Skip to content

Instantly share code, notes, and snippets.

@aaizemberg
Created October 8, 2019 18:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aaizemberg/6670bd21959e4dc61fbfc19f8896cb21 to your computer and use it in GitHub Desktop.
Save aaizemberg/6670bd21959e4dc61fbfc19f8896cb21 to your computer and use it in GitHub Desktop.
Plottable (bars) - minimal working example
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Plottable</title>
<link rel="stylesheet" href="./plottable.css" charset="utf-8">
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<script src="./plottable.js"></script>
</head>
<body>
<svg id="bars" height="500"></svg>
<script>
var data = [{ x: 1, y: 1 }, { x: 2, y: 3 },
{ x: 3, y: 2 }, { x: 4, y: 4 },
{ x: 5, y: 3 }, { x: 6, y: 5 }];
var xScale = new Plottable.Scales.Linear();
var yScale = new Plottable.Scales.Linear();
var plot = new Plottable.Plots.Bar()
.addDataset(new Plottable.Dataset(data))
.x(function(d) { return d.x; }, xScale)
.y(function(d) { return d.y; }, yScale)
.animated(true)
.renderTo("svg#bars");
window.addEventListener("resize", function() {
plot.redraw();
});
</script>
</body>
</html>
.plottable-colors-0 {
background-color: #5279c7; /* INDIGO */
}
.plottable-colors-1 {
background-color: #fd373e; /* CORAL_RED */
}
.plottable-colors-2 {
background-color: #63c261; /* FERN */
}
.plottable-colors-3 {
background-color: #fad419; /* BRIGHT_SUN */
}
.plottable-colors-4 {
background-color: #2c2b6f; /* JACARTA */
}
.plottable-colors-5 {
background-color: #ff7939; /* BURNING_ORANGE */
}
.plottable-colors-6 {
background-color: #db2e65; /* CERISE_RED */
}
.plottable-colors-7 {
background-color: #99ce50; /* CONIFER */
}
.plottable-colors-8 {
background-color: #962565; /* ROYAL_HEATH */
}
.plottable-colors-9 {
background-color: #06cccc; /* ROBINS_EGG_BLUE */
}
svg.plottable {
display : block; /* SVGs must be block elements for width/height calculations to work in Firefox. */
pointer-events: visibleFill;
}
.plottable .background-fill {
fill: none;
pointer-events: none;
}
.plottable .bounding-box {
/* Invisible pink bounding-box to allow for collision testing */
fill: pink;
visibility: hidden;
}
.plottable .label text {
font-family: "Helvetica Neue", sans-serif;
fill: #32313F;
}
.plottable .bar-label-text-area text {
font-family: "Helvetica Neue", sans-serif;
font-size: 12px;
font-style: italic;
}
.plottable .label-area text {
fill: #32313F;
font-family: "Helvetica Neue", sans-serif;
font-size: 14px;
}
.plottable .light-label text {
fill: white;
}
.plottable .dark-label text {
fill: #32313F;
}
.plottable .off-bar-label text {
fill: #32313F;
}
.plottable .stacked-bar-label text {
fill: #32313F;
font-style: normal;
}
.plottable .stacked-bar-plot .off-bar-label {
/* HACKHACK #2795: correct off-bar label logic to be implemented on StackedBar */
visibility: hidden !important;
}
.plottable .axis-label text {
font-size: 10px;
font-weight: bold;
letter-spacing: 1px;
line-height: normal;
text-transform: uppercase;
}
.plottable .title-label text {
font-size: 20px;
font-weight: bold;
}
.plottable .axis line.baseline {
stroke: #CCC;
stroke-width: 1px;
}
.plottable .axis line.tick-mark {
stroke: #CCC;
stroke-width: 1px;
}
.plottable .axis text {
fill: #32313F;
font-family: "Helvetica Neue", sans-serif;
font-size: 12px;
font-weight: 200;
line-height: normal;
}
.plottable .axis .annotation-circle {
fill: white;
stroke-width: 1px;
stroke: #CCC;
}
.plottable .axis .annotation-line {
stroke: #CCC;
stroke-width: 1px;
}
.plottable .axis .annotation-rect {
stroke: #CCC;
stroke-width: 1px;
fill: white;
}
.plottable .bar-plot .baseline {
stroke: #999;
}
.plottable .gridlines line {
stroke: #3C3C3C; /* hackhack: gridlines should be solid; see #820 */
opacity: 0.25;
stroke-width: 1px;
}
.plottable .selection-box-layer .selection-area {
fill: black;
fill-opacity: 0.03;
stroke: #CCC;
}
/* DragBoxLayer */
.plottable .drag-box-layer.x-resizable .drag-edge-lr {
cursor: ew-resize;
}
.plottable .drag-box-layer.y-resizable .drag-edge-tb {
cursor: ns-resize;
}
.plottable .drag-box-layer.x-resizable.y-resizable .drag-corner-tl {
cursor: nwse-resize;
}
.plottable .drag-box-layer.x-resizable.y-resizable .drag-corner-tr {
cursor: nesw-resize;
}
.plottable .drag-box-layer.x-resizable.y-resizable .drag-corner-bl {
cursor: nesw-resize;
}
.plottable .drag-box-layer.x-resizable.y-resizable .drag-corner-br {
cursor: nwse-resize;
}
.plottable .drag-box-layer.movable .selection-area {
cursor: move; /* IE fallback */
cursor: -moz-grab;
cursor: -webkit-grab;
cursor: grab;
}
.plottable .drag-box-layer.movable .selection-area:active {
cursor: -moz-grabbing;
cursor: -webkit-grabbing;
cursor: grabbing;
}
/* /DragBoxLayer */
.plottable .guide-line-layer line.guide-line {
stroke: #CCC;
stroke-width: 1px;
}
.plottable .drag-line-layer.enabled.vertical line.drag-edge {
cursor: ew-resize;
}
.plottable .drag-line-layer.enabled.horizontal line.drag-edge {
cursor: ns-resize;
}
.plottable .legend text {
fill: #32313F;
font-family: "Helvetica Neue", sans-serif;
font-size: 12px;
font-weight: bold;
line-height: normal;
}
.plottable .interpolated-color-legend rect.swatch-bounding-box {
fill: none;
stroke: #CCC;
stroke-width: 1px;
pointer-events: none;
}
.plottable .waterfall-plot line.connector {
stroke: #CCC;
stroke-width: 1px;
}
.plottable .pie-plot .arc.outline {
stroke-linejoin: round;
}
/*!
Plottable 2.7.0 (https://github.com/palantir/plottable)
Copyright 2014-2017 Palantir Technologies
Licensed under MIT (https://github.com/palantir/plottable/blob/master/LICENSE)
*/
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module unless amdModuleId is set
define(["d3"], function (a0) {
return (root['Plottable'] = factory(a0));
});
} else if (typeof exports === 'object') {
// Node. Does not work with strict CommonJS, but
// only CommonJS-like environments that support module.exports,
// like Node.
module.exports = factory(require("d3"));
} else {
root['Plottable'] = factory(root["d3"]);
}
}(this, function (d3) {
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var Plottable;
(function (Plottable) {
var Utils;
(function (Utils) {
/**
* Array-backed implementation of {EntityStore}
*/
var EntityArray = (function () {
function EntityArray() {
this._entities = [];
}
EntityArray.prototype.add = function (entity) {
this._entities.push(entity);
};
/**
* Iterates through array of of entities and computes the closest point using
* the standard Euclidean distance formula.
*/
EntityArray.prototype.entityNearest = function (queryPoint, filter) {
var closestDistanceSquared = Infinity;
var closestPointEntity;
this._entities.forEach(function (entity) {
if (filter !== undefined && filter(entity) === false) {
return;
}
var distanceSquared = Utils.Math.distanceSquared(entity.position, queryPoint);
if (distanceSquared < closestDistanceSquared) {
closestDistanceSquared = distanceSquared;
closestPointEntity = entity;
}
});
if (closestPointEntity === undefined) {
return undefined;
}
return closestPointEntity;
};
EntityArray.prototype.map = function (callback) {
return this._entities.map(function (entity) { return callback(entity); });
};
return EntityArray;
}());
Utils.EntityArray = EntityArray;
})(Utils = Plottable.Utils || (Plottable.Utils = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Utils;
(function (Utils) {
var Math;
(function (Math) {
var nativeMath = window.Math;
/**
* Checks if x is between a and b.
*
* @param {number} x The value to test if in range
* @param {number} a The beginning of the (inclusive) range
* @param {number} b The ending of the (inclusive) range
* @return {boolean} Whether x is in [a, b]
*/
function inRange(x, a, b) {
return (nativeMath.min(a, b) <= x && x <= nativeMath.max(a, b));
}
Math.inRange = inRange;
/**
* Clamps x to the range [min, max].
*
* @param {number} x The value to be clamped.
* @param {number} min The minimum value.
* @param {number} max The maximum value.
* @return {number} A clamped value in the range [min, max].
*/
function clamp(x, min, max) {
return nativeMath.min(nativeMath.max(min, x), max);
}
Math.clamp = clamp;
function max(array, firstArg, secondArg) {
var accessor = typeof (firstArg) === "function" ? firstArg : null;
var defaultValue = accessor == null ? firstArg : secondArg;
/* tslint:disable:ban */
var maxValue = accessor == null ? d3.max(array) : d3.max(array, accessor);
/* tslint:enable:ban */
return maxValue !== undefined ? maxValue : defaultValue;
}
Math.max = max;
function min(array, firstArg, secondArg) {
var accessor = typeof (firstArg) === "function" ? firstArg : null;
var defaultValue = accessor == null ? firstArg : secondArg;
/* tslint:disable:ban */
var minValue = accessor == null ? d3.min(array) : d3.min(array, accessor);
/* tslint:enable:ban */
return minValue !== undefined ? minValue : defaultValue;
}
Math.min = min;
/**
* Returns true **only** if x is NaN
*/
function isNaN(n) {
return n !== n;
}
Math.isNaN = isNaN;
/**
* Returns true if the argument is a number, which is not NaN
* Numbers represented as strings do not pass this function
*/
function isValidNumber(n) {
return typeof n === "number" && !Plottable.Utils.Math.isNaN(n) && isFinite(n);
}
Math.isValidNumber = isValidNumber;
/**
* Generates an array of consecutive, strictly increasing numbers
* in the range [start, stop) separeted by step
*/
function range(start, stop, step) {
if (step === void 0) { step = 1; }
if (step === 0) {
throw new Error("step cannot be 0");
}
var length = nativeMath.max(nativeMath.ceil((stop - start) / step), 0);
var range = [];
for (var i = 0; i < length; ++i) {
range[i] = start + step * i;
}
return range;
}
Math.range = range;
/**
* Returns the square of the distance between two points
*
* @param {Point} p1
* @param {Point} p2
* @return {number} dist(p1, p2)^2
*/
function distanceSquared(p1, p2) {
return nativeMath.pow(p2.y - p1.y, 2) + nativeMath.pow(p2.x - p1.x, 2);
}
Math.distanceSquared = distanceSquared;
function degreesToRadians(degree) {
return degree / 360 * nativeMath.PI * 2;
}
Math.degreesToRadians = degreesToRadians;
})(Math = Utils.Math || (Utils.Math = {}));
})(Utils = Plottable.Utils || (Plottable.Utils = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Utils;
(function (Utils) {
/**
* Shim for ES6 map.
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
*/
var Map = (function () {
function Map() {
if (typeof window.Map === "function") {
this._es6Map = new window.Map();
}
else {
this._keyValuePairs = [];
}
}
Map.prototype.set = function (key, value) {
if (Utils.Math.isNaN(key)) {
throw new Error("NaN may not be used as a key to the Map");
}
if (this._es6Map != null) {
this._es6Map.set(key, value);
return this;
}
for (var i = 0; i < this._keyValuePairs.length; i++) {
if (this._keyValuePairs[i].key === key) {
this._keyValuePairs[i].value = value;
return this;
}
}
this._keyValuePairs.push({ key: key, value: value });
return this;
};
Map.prototype.get = function (key) {
if (this._es6Map != null) {
return this._es6Map.get(key);
}
for (var i = 0; i < this._keyValuePairs.length; i++) {
if (this._keyValuePairs[i].key === key) {
return this._keyValuePairs[i].value;
}
}
return undefined;
};
Map.prototype.has = function (key) {
if (this._es6Map != null) {
return this._es6Map.has(key);
}
for (var i = 0; i < this._keyValuePairs.length; i++) {
if (this._keyValuePairs[i].key === key) {
return true;
}
}
return false;
};
Map.prototype.forEach = function (callbackFn, thisArg) {
var _this = this;
if (this._es6Map != null) {
var callbackWrapper = function (value, key) { return callbackFn.call(thisArg, value, key, _this); };
this._es6Map.forEach(callbackWrapper, thisArg);
return;
}
this._keyValuePairs.forEach(function (keyValuePair) {
callbackFn.call(thisArg, keyValuePair.value, keyValuePair.key, _this);
});
};
Map.prototype.delete = function (key) {
if (this._es6Map != null) {
return this._es6Map.delete(key);
}
for (var i = 0; i < this._keyValuePairs.length; i++) {
if (this._keyValuePairs[i].key === key) {
this._keyValuePairs.splice(i, 1);
return true;
}
}
return false;
};
return Map;
}());
Utils.Map = Map;
})(Utils = Plottable.Utils || (Plottable.Utils = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Utils;
(function (Utils) {
/**
* Shim for ES6 set.
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
*/
var Set = (function () {
function Set() {
if (typeof window.Set === "function") {
this._es6Set = new window.Set();
}
else {
this._values = [];
}
this.size = 0;
}
Set.prototype.add = function (value) {
if (this._es6Set != null) {
this._es6Set.add(value);
this.size = this._es6Set.size;
return this;
}
if (!this.has(value)) {
this._values.push(value);
this.size = this._values.length;
}
return this;
};
Set.prototype.delete = function (value) {
if (this._es6Set != null) {
var deleted = this._es6Set.delete(value);
this.size = this._es6Set.size;
return deleted;
}
var index = this._values.indexOf(value);
if (index !== -1) {
this._values.splice(index, 1);
this.size = this._values.length;
return true;
}
return false;
};
Set.prototype.has = function (value) {
if (this._es6Set != null) {
return this._es6Set.has(value);
}
return this._values.indexOf(value) !== -1;
};
Set.prototype.forEach = function (callback, thisArg) {
var _this = this;
if (this._es6Set != null) {
var callbackWrapper = function (value, value2) { return callback.call(thisArg, value, value2, _this); };
this._es6Set.forEach(callbackWrapper, thisArg);
return;
}
this._values.forEach(function (value) {
callback.call(thisArg, value, value, _this);
});
};
return Set;
}());
Utils.Set = Set;
})(Utils = Plottable.Utils || (Plottable.Utils = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Utils;
(function (Utils) {
var DOM;
(function (DOM) {
var nativeMath = window.Math;
/**
* Gets the bounding box of an element.
* @param {d3.Selection} element
* @returns {SVGRed} The bounding box.
*/
function elementBBox(element) {
var bbox;
// HACKHACK: Firefox won't correctly measure nodes with style "display: none" or their descendents (FF Bug 612118).
try {
bbox = element.node().getBBox();
}
catch (err) {
bbox = { x: 0, y: 0, width: 0, height: 0 };
}
return bbox;
}
DOM.elementBBox = elementBBox;
/**
* Screen refresh rate which is assumed to be 60fps
*/
DOM.SCREEN_REFRESH_RATE_MILLISECONDS = 1000 / 60;
/**
* Polyfill for `window.requestAnimationFrame`.
* If the function exists, then we use the function directly.
* Otherwise, we set a timeout on `SCREEN_REFRESH_RATE_MILLISECONDS` and then perform the function.
*
* @param {() => void} callback The callback to call in the next animation frame
*/
function requestAnimationFramePolyfill(callback) {
if (window.requestAnimationFrame != null) {
window.requestAnimationFrame(callback);
}
else {
setTimeout(callback, DOM.SCREEN_REFRESH_RATE_MILLISECONDS);
}
}
DOM.requestAnimationFramePolyfill = requestAnimationFramePolyfill;
/**
* Calculates the width of the element.
* The width includes the padding and the border on the element's left and right sides.
*
* @param {Element} element The element to query
* @returns {number} The width of the element.
*/
function elementWidth(element) {
var style = window.getComputedStyle(element);
return _parseStyleValue(style, "width")
+ _parseStyleValue(style, "padding-left")
+ _parseStyleValue(style, "padding-right")
+ _parseStyleValue(style, "border-left-width")
+ _parseStyleValue(style, "border-right-width");
}
DOM.elementWidth = elementWidth;
/**
* Calculates the height of the element.
* The height includes the padding the and the border on the element's top and bottom sides.
*
* @param {Element} element The element to query
* @returns {number} The height of the element
*/
function elementHeight(element) {
var style = window.getComputedStyle(element);
return _parseStyleValue(style, "height")
+ _parseStyleValue(style, "padding-top")
+ _parseStyleValue(style, "padding-bottom")
+ _parseStyleValue(style, "border-top-width")
+ _parseStyleValue(style, "border-bottom-width");
}
DOM.elementHeight = elementHeight;
function translate(selection, x, y) {
var transformMatrix = d3.transform(selection.attr("transform"));
if (x == null) {
return transformMatrix.translate;
}
y = (y == null) ? 0 : y;
transformMatrix.translate[0] = x;
transformMatrix.translate[1] = y;
selection.attr("transform", transformMatrix.toString());
return selection;
}
DOM.translate = translate;
/**
* Checks if the first ClientRect overlaps the second.
*
* @param {ClientRect} clientRectA The first ClientRect
* @param {ClientRect} clientRectB The second ClientRect
* @returns {boolean} If the ClientRects overlap each other.
*/
function clientRectsOverlap(clientRectA, clientRectB) {
if (nativeMath.floor(clientRectA.right) <= nativeMath.ceil(clientRectB.left)) {
return false;
}
if (nativeMath.ceil(clientRectA.left) >= nativeMath.floor(clientRectB.right)) {
return false;
}
if (nativeMath.floor(clientRectA.bottom) <= nativeMath.ceil(clientRectB.top)) {
return false;
}
if (nativeMath.ceil(clientRectA.top) >= nativeMath.floor(clientRectB.bottom)) {
return false;
}
return true;
}
DOM.clientRectsOverlap = clientRectsOverlap;
/**
* Returns true if and only if innerClientRect is inside outerClientRect.
*
* @param {ClientRect} innerClientRect The first ClientRect
* @param {ClientRect} outerClientRect The second ClientRect
* @returns {boolean} If and only if the innerClientRect is inside outerClientRect.
*/
function clientRectInside(innerClientRect, outerClientRect) {
return (nativeMath.floor(outerClientRect.left) <= nativeMath.ceil(innerClientRect.left) &&
nativeMath.floor(outerClientRect.top) <= nativeMath.ceil(innerClientRect.top) &&
nativeMath.floor(innerClientRect.right) <= nativeMath.ceil(outerClientRect.right) &&
nativeMath.floor(innerClientRect.bottom) <= nativeMath.ceil(outerClientRect.bottom));
}
DOM.clientRectInside = clientRectInside;
/**
* Retrieves the bounding svg of the input element
*
* @param {SVGElement} element The element to query
* @returns {SVGElement} The bounding svg
*/
function boundingSVG(element) {
var ownerSVG = element.ownerSVGElement;
if (ownerSVG != null) {
return ownerSVG;
}
if (element.nodeName.toLowerCase() === "svg") {
return element;
}
return null; // not in the DOM
}
DOM.boundingSVG = boundingSVG;
var _latestClipPathId = 0;
/**
* Generates a ClipPath ID that is unique for this instance of Plottable
*/
function generateUniqueClipPathId() {
return "plottableClipPath" + ++_latestClipPathId;
}
DOM.generateUniqueClipPathId = generateUniqueClipPathId;
/**
* Returns true if the supplied coordinates or Ranges intersect or are contained by bbox.
*
* @param {number | Range} xValOrRange The x coordinate or Range to test
* @param {number | Range} yValOrRange The y coordinate or Range to test
* @param {SVGRect} bbox The bbox
* @param {number} tolerance Amount by which to expand bbox, in each dimension, before
* testing intersection
*
* @returns {boolean} True if the supplied coordinates or Ranges intersect or are
* contained by bbox, false otherwise.
*/
function intersectsBBox(xValOrRange, yValOrRange, bbox, tolerance) {
if (tolerance === void 0) { tolerance = 0.5; }
var xRange = _parseRange(xValOrRange);
var yRange = _parseRange(yValOrRange);
// SVGRects are positioned with sub-pixel accuracy (the default unit
// for the x, y, height & width attributes), but user selections (e.g. via
// mouse events) usually have pixel accuracy. A tolerance of half-a-pixel
// seems appropriate.
return bbox.x + bbox.width >= xRange.min - tolerance &&
bbox.x <= xRange.max + tolerance &&
bbox.y + bbox.height >= yRange.min - tolerance &&
bbox.y <= yRange.max + tolerance;
}
DOM.intersectsBBox = intersectsBBox;
/**
* Create a Range from a number or an object with "min" and "max" defined.
*
* @param {any} input The object to parse
*
* @returns {Range} The generated Range
*/
function _parseRange(input) {
if (typeof (input) === "number") {
var value = input;
return { min: value, max: value };
}
var range = input;
if (range instanceof Object && "min" in range && "max" in range) {
return range;
}
throw new Error("input '" + input + "' can't be parsed as an Range");
}
function _parseStyleValue(style, property) {
var value = style.getPropertyValue(property);
var parsedValue = parseFloat(value);
return parsedValue || 0;
}
})(DOM = Utils.DOM || (Utils.DOM = {}));
})(Utils = Plottable.Utils || (Plottable.Utils = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Utils;
(function (Utils) {
var Color;
(function (Color) {
var nativeMath = window.Math;
/**
* Return contrast ratio between two colors
* Based on implementation from chroma.js by Gregor Aisch (gka) (licensed under BSD)
* chroma.js may be found here: https://github.com/gka/chroma.js
* License may be found here: https://github.com/gka/chroma.js/blob/master/LICENSE
* see http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef
*/
function contrast(a, b) {
var l1 = luminance(a) + 0.05;
var l2 = luminance(b) + 0.05;
return l1 > l2 ? l1 / l2 : l2 / l1;
}
Color.contrast = contrast;
/**
* Returns a brighter copy of this color. Each channel is multiplied by 0.7 ^ -factor.
* Channel values are capped at the maximum value of 255, and the minimum value of 30.
*/
function lightenColor(color, factor) {
var hsl = d3.hsl(color).brighter(factor);
return hsl.rgb().toString();
}
Color.lightenColor = lightenColor;
/**
* Gets the Hex Code of the color resulting by applying the className CSS class to the
* colorTester selection. Returns null if the tester is transparent.
*
* @param {d3.Selection<void>} colorTester The d3 selection to apply the CSS class to
* @param {string} className The name of the class to be applied
* @return {string} The hex code of the computed color
*/
function colorTest(colorTester, className) {
colorTester.classed(className, true);
// Use regex to get the text inside the rgb parentheses
var colorStyle = colorTester.style("background-color");
if (colorStyle === "transparent") {
return null;
}
var rgb = /\((.+)\)/.exec(colorStyle)[1]
.split(",")
.map(function (colorValue) {
var colorNumber = +colorValue;
var hexValue = colorNumber.toString(16);
return colorNumber < 16 ? "0" + hexValue : hexValue;
});
if (rgb.length === 4 && rgb[3] === "00") {
return null;
}
var hexCode = "#" + rgb.join("");
colorTester.classed(className, false);
return hexCode;
}
Color.colorTest = colorTest;
/**
* Return relative luminance (defined here: http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef)
* Based on implementation from chroma.js by Gregor Aisch (gka) (licensed under BSD)
* chroma.js may be found here: https://github.com/gka/chroma.js
* License may be found here: https://github.com/gka/chroma.js/blob/master/LICENSE
*/
function luminance(color) {
var rgb = d3.rgb(color);
var lum = function (x) {
x = x / 255;
return x <= 0.03928 ? x / 12.92 : nativeMath.pow((x + 0.055) / 1.055, 2.4);
};
var r = lum(rgb.r);
var g = lum(rgb.g);
var b = lum(rgb.b);
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}
})(Color = Utils.Color || (Utils.Color = {}));
})(Utils = Plottable.Utils || (Plottable.Utils = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Utils;
(function (Utils) {
var Array;
(function (Array) {
var nativeArray = window.Array;
/**
* Takes two arrays of numbers and adds them together
*
* @param {number[]} aList The first array of numbers
* @param {number[]} bList The second array of numbers
* @return {number[]} An array of numbers where x[i] = aList[i] + bList[i]
*/
function add(aList, bList) {
if (aList.length !== bList.length) {
throw new Error("attempted to add arrays of unequal length");
}
return aList.map(function (_, i) { return aList[i] + bList[i]; });
}
Array.add = add;
/**
* Take an array of values, and return the unique values.
* Will work iff ∀ a, b, a.toString() == b.toString() => a == b; will break on Object inputs
*
* @param {T[]} values The values to find uniqueness for
* @return {T[]} The unique values
*/
function uniq(arr) {
var seen = d3.set();
var result = [];
arr.forEach(function (x) {
if (!seen.has(String(x))) {
seen.add(String(x));
result.push(x);
}
});
return result;
}
Array.uniq = uniq;
/**
* @param {T[][]} a The 2D array that will have its elements joined together.
* @return {T[]} Every array in a, concatenated together in the order they appear.
*/
function flatten(a) {
return nativeArray.prototype.concat.apply([], a);
}
Array.flatten = flatten;
/**
* Creates an array of length `count`, filled with value or (if value is a function), value()
*
* @param {T | ((index?: number) => T)} value The value to fill the array with or a value generator (called with index as arg)
* @param {number} count The length of the array to generate
* @return {any[]}
*/
function createFilledArray(value, count) {
var out = [];
for (var i = 0; i < count; i++) {
out[i] = typeof (value) === "function" ? value(i) : value;
}
return out;
}
Array.createFilledArray = createFilledArray;
})(Array = Utils.Array || (Utils.Array = {}));
})(Utils = Plottable.Utils || (Plottable.Utils = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Utils;
(function (Utils) {
/**
* A set of callbacks which can be all invoked at once.
* Each callback exists at most once in the set (based on reference equality).
* All callbacks should have the same signature.
*/
var CallbackSet = (function (_super) {
__extends(CallbackSet, _super);
function CallbackSet() {
_super.apply(this, arguments);
}
CallbackSet.prototype.callCallbacks = function () {
var _this = this;
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i - 0] = arguments[_i];
}
this.forEach(function (callback) {
callback.apply(_this, args);
});
return this;
};
return CallbackSet;
}(Utils.Set));
Utils.CallbackSet = CallbackSet;
})(Utils = Plottable.Utils || (Plottable.Utils = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Utils;
(function (Utils) {
var Stacking;
(function (Stacking) {
var nativeMath = window.Math;
/**
* Computes the StackingResult (value and offset) for each data point in each Dataset.
*
* @param {Dataset[]} datasets The Datasets to be stacked on top of each other in the order of stacking
* @param {Accessor<any>} keyAccessor Accessor for the key of the data
* @param {Accessor<number>} valueAccessor Accessor for the value of the data
* @return {StackingResult} value and offset for each datapoint in each Dataset
*/
function stack(datasets, keyAccessor, valueAccessor) {
var positiveOffsets = d3.map();
var negativeOffsets = d3.map();
var datasetToKeyToStackedDatum = new Utils.Map();
datasets.forEach(function (dataset) {
var keyToStackedDatum = new Utils.Map();
dataset.data().forEach(function (datum, index) {
var key = normalizeKey(keyAccessor(datum, index, dataset));
var value = +valueAccessor(datum, index, dataset);
var offset;
var offsetMap = (value >= 0) ? positiveOffsets : negativeOffsets;
if (offsetMap.has(key)) {
offset = offsetMap.get(key);
offsetMap.set(key, offset + value);
}
else {
offset = 0;
offsetMap.set(key, value);
}
keyToStackedDatum.set(key, {
value: value,
offset: offset,
});
});
datasetToKeyToStackedDatum.set(dataset, keyToStackedDatum);
});
return datasetToKeyToStackedDatum;
}
Stacking.stack = stack;
/**
* Computes the maximum and minimum extents of each stack individually.
*
* @param {GenericStackingResult} stackingResult The value and offset information for each datapoint in each dataset
* @return { { maximumExtents: Utils.Map<D, number>, minimumExtents: Utils.Map<D, number> } } The maximum and minimum extents
* of each individual stack.
*/
function stackedExtents(stackingResult) {
var maximumExtents = new Utils.Map();
var minimumExtents = new Utils.Map();
stackingResult.forEach(function (stack) {
stack.forEach(function (datum, key) {
// correctly handle negative bar stacks
var maximalValue = Utils.Math.max([datum.offset + datum.value, datum.offset], datum.offset);
var minimalValue = Utils.Math.min([datum.offset + datum.value, datum.offset], datum.offset);
if (!maximumExtents.has(key)) {
maximumExtents.set(key, maximalValue);
}
else if (maximumExtents.get(key) < maximalValue) {
maximumExtents.set(key, maximalValue);
}
if (!minimumExtents.has(key)) {
minimumExtents.set(key, minimalValue);
}
else if (minimumExtents.get(key) > (minimalValue)) {
minimumExtents.set(key, minimalValue);
}
});
});
return { maximumExtents: maximumExtents, minimumExtents: minimumExtents };
}
Stacking.stackedExtents = stackedExtents;
/**
* Computes the total extent over all data points in all Datasets, taking stacking into consideration.
*
* @param {StackingResult} stackingResult The value and offset information for each datapoint in each dataset
* @param {Accessor<any>} keyAccessor Accessor for the key of the data existent in the stackingResult
* @param {Accessor<boolean>} filter A filter for data to be considered when computing the total extent
* @return {[number, number]} The total extent
*/
function stackedExtent(stackingResult, keyAccessor, filter) {
var extents = [];
stackingResult.forEach(function (stackedDatumMap, dataset) {
dataset.data().forEach(function (datum, index) {
if (filter != null && !filter(datum, index, dataset)) {
return;
}
var stackedDatum = stackedDatumMap.get(normalizeKey(keyAccessor(datum, index, dataset)));
extents.push(stackedDatum.value + stackedDatum.offset);
});
});
var maxStackExtent = Utils.Math.max(extents, 0);
var minStackExtent = Utils.Math.min(extents, 0);
return [nativeMath.min(minStackExtent, 0), nativeMath.max(0, maxStackExtent)];
}
Stacking.stackedExtent = stackedExtent;
/**
* Normalizes a key used for stacking
*
* @param {any} key The key to be normalized
* @return {string} The stringified key
*/
function normalizeKey(key) {
return String(key);
}
Stacking.normalizeKey = normalizeKey;
})(Stacking = Utils.Stacking || (Utils.Stacking = {}));
})(Utils = Plottable.Utils || (Plottable.Utils = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Utils;
(function (Utils) {
var Window;
(function (Window) {
/**
* Print a warning message to the console, if it is available.
*
* @param {string} The warnings to print
*/
function warn(warning) {
if (!Plottable.Configs.SHOW_WARNINGS) {
return;
}
/* tslint:disable:no-console */
if (window.console != null) {
if (window.console.warn != null) {
console.warn(warning);
}
else if (window.console.log != null) {
console.log(warning);
}
}
/* tslint:enable:no-console */
}
Window.warn = warn;
/**
* Is like setTimeout, but activates synchronously if time=0
* We special case 0 because of an observed issue where calling setTimeout causes visible flickering.
* We believe this is because when requestAnimationFrame calls into the paint function, as soon as that function finishes
* evaluating, the results are painted to the screen. As a result, if we want something to occur immediately but call setTimeout
* with time=0, then it is pushed to the call stack and rendered in the next frame, so the component that was rendered via
* setTimeout appears out-of-sync with the rest of the plot.
*/
function setTimeout(f, time) {
var args = [];
for (var _i = 2; _i < arguments.length; _i++) {
args[_i - 2] = arguments[_i];
}
if (time === 0) {
f(args);
return -1;
}
else {
return window.setTimeout(f, time, args);
}
}
Window.setTimeout = setTimeout;
/**
* Sends a deprecation warning to the console. The warning includes the name of the deprecated method,
* version number of the deprecation, and an optional message.
*
* To be used in the first line of a deprecated method.
*
* @param {string} callingMethod The name of the method being deprecated
* @param {string} version The version when the tagged method became obsolete
* @param {string?} message Optional message to be shown with the warning
*/
function deprecated(callingMethod, version, message) {
if (message === void 0) { message = ""; }
Utils.Window.warn("Method " + callingMethod + " has been deprecated in version " + version +
". Please refer to the release notes. " + message);
}
Window.deprecated = deprecated;
})(Window = Utils.Window || (Utils.Window = {}));
})(Utils = Plottable.Utils || (Plottable.Utils = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Utils;
(function (Utils) {
var ClientToSVGTranslator = (function () {
function ClientToSVGTranslator(svg) {
this._svg = svg;
this._measureRect = document.createElementNS(svg.namespaceURI, "rect");
this._measureRect.setAttribute("class", "measure-rect");
this._measureRect.setAttribute("style", "opacity: 0; visibility: hidden;");
this._measureRect.setAttribute("width", "1");
this._measureRect.setAttribute("height", "1");
this._svg.appendChild(this._measureRect);
}
/**
* Returns the ClientToSVGTranslator for the <svg> containing elem.
* If one already exists on that <svg>, it will be returned; otherwise, a new one will be created.
*/
ClientToSVGTranslator.getTranslator = function (elem) {
var svg = Utils.DOM.boundingSVG(elem);
var translator = svg[ClientToSVGTranslator._TRANSLATOR_KEY];
if (translator == null) {
translator = new ClientToSVGTranslator(svg);
svg[ClientToSVGTranslator._TRANSLATOR_KEY] = translator;
}
return translator;
};
/**
* Computes the position relative to the <svg> in svg-coordinate-space.
*/
ClientToSVGTranslator.prototype.computePosition = function (clientX, clientY) {
// get the origin
this._measureRect.setAttribute("x", "0");
this._measureRect.setAttribute("y", "0");
var mrBCR = this._measureRect.getBoundingClientRect();
var origin = { x: mrBCR.left, y: mrBCR.top };
// calculate the scale
var sampleDistance = 100;
this._measureRect.setAttribute("x", String(sampleDistance));
this._measureRect.setAttribute("y", String(sampleDistance));
mrBCR = this._measureRect.getBoundingClientRect();
var testPoint = { x: mrBCR.left, y: mrBCR.top };
// invalid measurements -- SVG might not be in the DOM
if (origin.x === testPoint.x || origin.y === testPoint.y) {
return null;
}
var scaleX = (testPoint.x - origin.x) / sampleDistance;
var scaleY = (testPoint.y - origin.y) / sampleDistance;
// get the true cursor position
this._measureRect.setAttribute("x", String((clientX - origin.x) / scaleX));
this._measureRect.setAttribute("y", String((clientY - origin.y) / scaleY));
mrBCR = this._measureRect.getBoundingClientRect();
var trueCursorPosition = { x: mrBCR.left, y: mrBCR.top };
var scaledPosition = {
x: (trueCursorPosition.x - origin.x) / scaleX,
y: (trueCursorPosition.y - origin.y) / scaleY,
};
return scaledPosition;
};
/**
* Checks whether event happened inside <svg> element.
*/
ClientToSVGTranslator.prototype.insideSVG = function (e) {
return Utils.DOM.boundingSVG(e.target) === this._svg;
};
ClientToSVGTranslator._TRANSLATOR_KEY = "__Plottable_ClientToSVGTranslator";
return ClientToSVGTranslator;
}());
Utils.ClientToSVGTranslator = ClientToSVGTranslator;
})(Utils = Plottable.Utils || (Plottable.Utils = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Configs;
(function (Configs) {
/**
* Specifies if Plottable should show warnings.
*/
Configs.SHOW_WARNINGS = true;
/**
* Specifies if Plottable should add <title> elements to text.
*/
Configs.ADD_TITLE_ELEMENTS = true;
})(Configs = Plottable.Configs || (Plottable.Configs = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
Plottable.version = "2.7.0";
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Dataset = (function () {
/**
* A Dataset contains an array of data and some metadata.
* Changes to the data or metadata will cause anything subscribed to the Dataset to update.
*
* @constructor
* @param {any[]} [data=[]] The data for this Dataset.
* @param {any} [metadata={}] An object containing additional information.
*/
function Dataset(data, metadata) {
if (data === void 0) { data = []; }
if (metadata === void 0) { metadata = {}; }
this._data = data;
this._metadata = metadata;
this._callbacks = new Plottable.Utils.CallbackSet();
}
/**
* Adds a callback to be called when the Dataset updates.
*
* @param {DatasetCallback} callback.
* @returns {Dataset} The calling Dataset.
*/
Dataset.prototype.onUpdate = function (callback) {
this._callbacks.add(callback);
return this;
};
/**
* Removes a callback that would be called when the Dataset updates.
*
* @param {DatasetCallback} callback
* @returns {Dataset} The calling Dataset.
*/
Dataset.prototype.offUpdate = function (callback) {
this._callbacks.delete(callback);
return this;
};
Dataset.prototype.data = function (data) {
if (data == null) {
return this._data;
}
else {
this._data = data;
this._callbacks.callCallbacks(this);
return this;
}
};
Dataset.prototype.metadata = function (metadata) {
if (metadata == null) {
return this._metadata;
}
else {
this._metadata = metadata;
this._callbacks.callCallbacks(this);
return this;
}
};
return Dataset;
}());
Plottable.Dataset = Dataset;
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var RenderPolicies;
(function (RenderPolicies) {
/**
* Renders Components immediately after they are enqueued.
* Useful for debugging, horrible for performance.
*/
var Immediate = (function () {
function Immediate() {
}
Immediate.prototype.render = function () {
Plottable.RenderController.flush();
};
return Immediate;
}());
RenderPolicies.Immediate = Immediate;
/**
* The default way to render, which only tries to render every frame
* (usually, 1/60th of a second).
*/
var AnimationFrame = (function () {
function AnimationFrame() {
}
AnimationFrame.prototype.render = function () {
Plottable.Utils.DOM.requestAnimationFramePolyfill(Plottable.RenderController.flush);
};
return AnimationFrame;
}());
RenderPolicies.AnimationFrame = AnimationFrame;
/**
* Renders with `setTimeout()`.
* Generally an inferior way to render compared to `requestAnimationFrame`,
* but useful for browsers that don't suppoort `requestAnimationFrame`.
*/
var Timeout = (function () {
function Timeout() {
this._timeoutMsec = Plottable.Utils.DOM.SCREEN_REFRESH_RATE_MILLISECONDS;
}
Timeout.prototype.render = function () {
setTimeout(Plottable.RenderController.flush, this._timeoutMsec);
};
return Timeout;
}());
RenderPolicies.Timeout = Timeout;
})(RenderPolicies = Plottable.RenderPolicies || (Plottable.RenderPolicies = {}));
})(Plottable || (Plottable = {}));
/**
* The RenderController is responsible for enqueueing and synchronizing
* layout and render calls for Components.
*
* Layout and render calls occur inside an animation callback
* (window.requestAnimationFrame if available).
*
* RenderController.flush() immediately lays out and renders all Components currently enqueued.
*
* To always have immediate rendering (useful for debugging), call
* ```typescript
* Plottable.RenderController.setRenderPolicy(
* new Plottable.RenderPolicies.Immediate()
* );
* ```
*/
var Plottable;
(function (Plottable) {
var RenderController;
(function (RenderController) {
var _componentsNeedingRender = new Plottable.Utils.Set();
var _componentsNeedingComputeLayout = new Plottable.Utils.Set();
var _animationRequested = false;
var _isCurrentlyFlushing = false;
var Policy;
(function (Policy) {
Policy.IMMEDIATE = "immediate";
Policy.ANIMATION_FRAME = "animationframe";
Policy.TIMEOUT = "timeout";
})(Policy = RenderController.Policy || (RenderController.Policy = {}));
var _renderPolicy = new Plottable.RenderPolicies.AnimationFrame();
function renderPolicy(renderPolicy) {
if (renderPolicy == null) {
return _renderPolicy;
}
switch (renderPolicy.toLowerCase()) {
case Policy.IMMEDIATE:
_renderPolicy = new Plottable.RenderPolicies.Immediate();
break;
case Policy.ANIMATION_FRAME:
_renderPolicy = new Plottable.RenderPolicies.AnimationFrame();
break;
case Policy.TIMEOUT:
_renderPolicy = new Plottable.RenderPolicies.Timeout();
break;
default:
Plottable.Utils.Window.warn("Unrecognized renderPolicy: " + renderPolicy);
}
}
RenderController.renderPolicy = renderPolicy;
/**
* Enqueues the Component for rendering.
*
* @param {Component} component
*/
function registerToRender(component) {
if (_isCurrentlyFlushing) {
Plottable.Utils.Window.warn("Registered to render while other components are flushing: request may be ignored");
}
_componentsNeedingRender.add(component);
requestRender();
}
RenderController.registerToRender = registerToRender;
/**
* Enqueues the Component for layout and rendering.
*
* @param {Component} component
*/
function registerToComputeLayout(component) {
_componentsNeedingComputeLayout.add(component);
_componentsNeedingRender.add(component);
requestRender();
}
RenderController.registerToComputeLayout = registerToComputeLayout;
function requestRender() {
// Only run or enqueue flush on first request.
if (!_animationRequested) {
_animationRequested = true;
_renderPolicy.render();
}
}
/**
* Renders all Components waiting to be rendered immediately
* instead of waiting until the next frame.
*
* Useful to call when debugging.
*/
function flush() {
if (_animationRequested) {
// Layout
_componentsNeedingComputeLayout.forEach(function (component) { return component.computeLayout(); });
// Top level render; Containers will put their children in the toRender queue
_componentsNeedingRender.forEach(function (component) { return component.render(); });
_isCurrentlyFlushing = true;
var failed_1 = new Plottable.Utils.Set();
_componentsNeedingRender.forEach(function (component) {
try {
component.renderImmediately();
}
catch (err) {
// throw error with timeout to avoid interrupting further renders
window.setTimeout(function () { throw err; }, 0);
failed_1.add(component);
}
});
_componentsNeedingComputeLayout = new Plottable.Utils.Set();
_componentsNeedingRender = failed_1;
_animationRequested = false;
_isCurrentlyFlushing = false;
}
}
RenderController.flush = flush;
})(RenderController = Plottable.RenderController || (Plottable.RenderController = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Formatters;
(function (Formatters) {
/**
* Creates a formatter for currency values.
*
* @param {number} [precision] The number of decimal places to show (default 2).
* @param {string} [symbol] The currency symbol to use (default "$").
* @param {boolean} [prefix] Whether to prepend or append the currency symbol (default true).
*
* @returns {Formatter} A formatter for currency values.
*/
function currency(precision, symbol, prefix) {
if (precision === void 0) { precision = 2; }
if (symbol === void 0) { symbol = "$"; }
if (prefix === void 0) { prefix = true; }
var fixedFormatter = Formatters.fixed(precision);
return function (d) {
var formattedValue = fixedFormatter(Math.abs(d));
if (formattedValue !== "") {
if (prefix) {
formattedValue = symbol + formattedValue;
}
else {
formattedValue += symbol;
}
if (d < 0) {
formattedValue = "-" + formattedValue;
}
}
return formattedValue;
};
}
Formatters.currency = currency;
/**
* Creates a formatter that displays exactly [precision] decimal places.
*
* @param {number} [precision] The number of decimal places to show (default 3).
*
* @returns {Formatter} A formatter that displays exactly [precision] decimal places.
*/
function fixed(precision) {
if (precision === void 0) { precision = 3; }
verifyPrecision(precision);
return function (d) { return d.toFixed(precision); };
}
Formatters.fixed = fixed;
/**
* Creates a formatter that formats numbers to show no more than
* [maxNumberOfDecimalPlaces] decimal places. All other values are stringified.
*
* @param {number} [maxNumberOfDecimalPlaces] The number of decimal places to show (default 3).
*
* @returns {Formatter} A formatter for general values.
*/
function general(maxNumberOfDecimalPlaces) {
if (maxNumberOfDecimalPlaces === void 0) { maxNumberOfDecimalPlaces = 3; }
verifyPrecision(maxNumberOfDecimalPlaces);
return function (d) {
if (typeof d === "number") {
var multiplier = Math.pow(10, maxNumberOfDecimalPlaces);
return String(Math.round(d * multiplier) / multiplier);
}
else {
return String(d);
}
};
}
Formatters.general = general;
/**
* Creates a formatter that stringifies its input.
*
* @returns {Formatter} A formatter that stringifies its input.
*/
function identity() {
return function (d) { return String(d); };
}
Formatters.identity = identity;
/**
* Creates a formatter for percentage values.
* Multiplies the input by 100 and appends "%".
*
* @param {number} [precision] The number of decimal places to show (default 0).
*
* @returns {Formatter} A formatter for percentage values.
*/
function percentage(precision) {
if (precision === void 0) { precision = 0; }
var fixedFormatter = Formatters.fixed(precision);
return function (d) {
var valToFormat = d * 100;
// Account for float imprecision
var valString = d.toString();
var integerPowerTen = Math.pow(10, valString.length - (valString.indexOf(".") + 1));
valToFormat = parseInt((valToFormat * integerPowerTen).toString(), 10) / integerPowerTen;
return fixedFormatter(valToFormat) + "%";
};
}
Formatters.percentage = percentage;
/**
* Creates a formatter for values that displays [numberOfSignificantFigures] significant figures
* and puts SI notation.
*
* @param {number} [numberOfSignificantFigures] The number of significant figures to show (default 3).
*
* @returns {Formatter} A formatter for SI values.
*/
function siSuffix(numberOfSignificantFigures) {
if (numberOfSignificantFigures === void 0) { numberOfSignificantFigures = 3; }
verifyPrecision(numberOfSignificantFigures);
return function (d) { return d3.format("." + numberOfSignificantFigures + "s")(d); };
}
Formatters.siSuffix = siSuffix;
/**
* Creates a formatter for values that displays abbreviated values
* and uses standard short scale suffixes
* - K - thousands - 10 ^ 3
* - M - millions - 10 ^ 6
* - B - billions - 10 ^ 9
* - T - trillions - 10 ^ 12
* - Q - quadrillions - 10 ^ 15
*
* Numbers with a magnitude outside of (10 ^ (-precision), 10 ^ 15) are shown using
* scientific notation to avoid creating extremely long decimal strings.
*
* @param {number} [precision] the number of decimal places to show (default 3)
* @returns {Formatter} A formatter with short scale formatting
*/
function shortScale(precision) {
if (precision === void 0) { precision = 3; }
verifyPrecision(precision);
var suffixes = "KMBTQ";
var exponentFormatter = d3.format("." + precision + "e");
var fixedFormatter = d3.format("." + precision + "f");
var max = Math.pow(10, (3 * (suffixes.length + 1)));
var min = Math.pow(10, -precision);
return function (num) {
var absNum = Math.abs(num);
if ((absNum < min || absNum >= max) && absNum !== 0) {
return exponentFormatter(num);
}
var idx = -1;
while (absNum >= Math.pow(1000, idx + 2) && idx < (suffixes.length - 1)) {
idx++;
}
var output = "";
if (idx === -1) {
output = fixedFormatter(num);
}
else {
output = fixedFormatter(num / Math.pow(1000, idx + 1)) + suffixes[idx];
}
// catch rounding by the underlying d3 formatter
if ((num > 0 && output.substr(0, 4) === "1000") || (num < 0 && output.substr(0, 5) === "-1000")) {
if (idx < suffixes.length - 1) {
idx++;
output = fixedFormatter(num / Math.pow(1000, idx + 1)) + suffixes[idx];
}
else {
output = exponentFormatter(num);
}
}
return output;
};
}
Formatters.shortScale = shortScale;
/**
* Creates a multi time formatter that displays dates.
*
* @returns {Formatter} A formatter for time/date values.
*/
function multiTime() {
// Formatter tiers going from shortest time scale to largest - these were taken from d3
// https://github.com/mbostock/d3/wiki/Time-Formatting#format_multi
var candidateFormats = [
{
specifier: ".%L",
predicate: function (d) { return d.getMilliseconds() !== 0; },
},
{
specifier: ":%S",
predicate: function (d) { return d.getSeconds() !== 0; },
},
{
specifier: "%I:%M",
predicate: function (d) { return d.getMinutes() !== 0; },
},
{
specifier: "%I %p",
predicate: function (d) { return d.getHours() !== 0; },
},
{
specifier: "%a %d",
predicate: function (d) { return d.getDay() !== 0 && d.getDate() !== 1; },
},
{
specifier: "%b %d",
predicate: function (d) { return d.getDate() !== 1; },
},
{
specifier: "%b",
predicate: function (d) { return d.getMonth() !== 0; },
},
];
return function (d) {
var acceptableFormats = candidateFormats.filter(function (candidate) { return candidate.predicate(d); });
var specifier = acceptableFormats.length > 0
? acceptableFormats[0].specifier
: "%Y";
return d3.time.format(specifier)(d);
};
}
Formatters.multiTime = multiTime;
/**
* Creates a time formatter that displays time/date using given specifier.
*
* List of directives can be found on: https://github.com/mbostock/d3/wiki/Time-Formatting#format
*
* @param {string} [specifier] The specifier for the formatter.
*
* @returns {Formatter} A formatter for time/date values.
*/
function time(specifier) {
return d3.time.format(specifier);
}
Formatters.time = time;
function verifyPrecision(precision) {
if (precision < 0 || precision > 20) {
throw new RangeError("Formatter precision must be between 0 and 20");
}
if (precision !== Math.floor(precision)) {
throw new RangeError("Formatter precision must be an integer");
}
}
})(Formatters = Plottable.Formatters || (Plottable.Formatters = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var SymbolFactories;
(function (SymbolFactories) {
function circle() {
return function (symbolSize) { return d3.svg.symbol().type("circle").size(Math.PI * Math.pow(symbolSize / 2, 2))(null); };
}
SymbolFactories.circle = circle;
function square() {
return function (symbolSize) { return d3.svg.symbol().type("square").size(Math.pow(symbolSize, 2))(null); };
}
SymbolFactories.square = square;
function cross() {
return function (symbolSize) { return d3.svg.symbol().type("cross").size((5 / 9) * Math.pow(symbolSize, 2))(null); };
}
SymbolFactories.cross = cross;
function diamond() {
return function (symbolSize) { return d3.svg.symbol().type("diamond").size(Math.tan(Math.PI / 6) * Math.pow(symbolSize, 2) / 2)(null); };
}
SymbolFactories.diamond = diamond;
function triangleUp() {
return function (symbolSize) { return d3.svg.symbol().type("triangle-up").size(Math.sqrt(3) * Math.pow(symbolSize / 2, 2))(null); };
}
SymbolFactories.triangleUp = triangleUp;
function triangleDown() {
return function (symbolSize) { return d3.svg.symbol().type("triangle-down").size(Math.sqrt(3) * Math.pow(symbolSize / 2, 2))(null); };
}
SymbolFactories.triangleDown = triangleDown;
})(SymbolFactories = Plottable.SymbolFactories || (Plottable.SymbolFactories = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Scales;
(function (Scales) {
/**
* Type guarded function to check if the scale implements the
* `TransformableScale` interface. Unfortunately, there is no way to do
* runtime interface typechecking, so we have to explicitly list all classes
* that implement the interface.
*/
function isTransformable(scale) {
return (scale instanceof Plottable.QuantitativeScale ||
scale instanceof Plottable.Scales.Category);
}
Scales.isTransformable = isTransformable;
})(Scales = Plottable.Scales || (Plottable.Scales = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Scale = (function () {
/**
* A Scale is a function (in the mathematical sense) that maps values from a domain to a range.
*
* @constructor
*/
function Scale() {
this._autoDomainAutomatically = true;
this._domainModificationInProgress = false;
this._callbacks = new Plottable.Utils.CallbackSet();
this._includedValuesProviders = new Plottable.Utils.Set();
}
/**
* Given an array of potential domain values, computes the extent of those values.
*
* @param {D[]} values
* @returns {D[]} The extent of the input values.
*/
Scale.prototype.extentOfValues = function (values) {
return []; // this should be overwritten
};
Scale.prototype._getAllIncludedValues = function () {
var _this = this;
var providerArray = [];
this._includedValuesProviders.forEach(function (provider) {
var extents = provider(_this);
providerArray = providerArray.concat(extents);
});
return providerArray;
};
Scale.prototype._getExtent = function () {
return []; // this should be overwritten
};
/**
* Adds a callback to be called when the Scale updates.
*
* @param {ScaleCallback} callback.
* @returns {Scale} The calling Scale.
*/
Scale.prototype.onUpdate = function (callback) {
this._callbacks.add(callback);
return this;
};
/**
* Removes a callback that would be called when the Scale updates.
*
* @param {ScaleCallback} callback.
* @returns {Scale} The calling Scale.
*/
Scale.prototype.offUpdate = function (callback) {
this._callbacks.delete(callback);
return this;
};
Scale.prototype._dispatchUpdate = function () {
this._callbacks.callCallbacks(this);
};
/**
* Sets the Scale's domain so that it spans the Extents of all its ExtentsProviders.
*
* @returns {Scale} The calling Scale.
*/
Scale.prototype.autoDomain = function () {
this._autoDomainAutomatically = true;
this._setDomain(this._getExtent());
return this;
};
Scale.prototype._autoDomainIfAutomaticMode = function () {
if (this._autoDomainAutomatically) {
this.autoDomain();
}
};
/**
* Computes the range value corresponding to a given domain value.
*
* @param {D} value
* @returns {R} The range value corresponding to the supplied domain value.
*/
Scale.prototype.scale = function (value) {
throw new Error("Subclasses should override scale");
};
Scale.prototype.domain = function (values) {
if (values == null) {
return this._getDomain();
}
else {
this._autoDomainAutomatically = false;
this._setDomain(values);
return this;
}
};
Scale.prototype._getDomain = function () {
throw new Error("Subclasses should override _getDomain");
};
Scale.prototype._setDomain = function (values) {
if (!this._domainModificationInProgress) {
this._domainModificationInProgress = true;
this._backingScaleDomain(values);
this._dispatchUpdate();
this._domainModificationInProgress = false;
}
};
Scale.prototype._backingScaleDomain = function (values) {
throw new Error("Subclasses should override _backingDomain");
};
Scale.prototype.range = function (values) {
if (values == null) {
return this._getRange();
}
else {
this._setRange(values);
return this;
}
};
Scale.prototype._getRange = function () {
throw new Error("Subclasses should override _getRange");
};
Scale.prototype._setRange = function (values) {
throw new Error("Subclasses should override _setRange");
};
/**
* Adds an IncludedValuesProvider to the Scale.
*
* @param {Scales.IncludedValuesProvider} provider
* @returns {Scale} The calling Scale.
*/
Scale.prototype.addIncludedValuesProvider = function (provider) {
this._includedValuesProviders.add(provider);
this._autoDomainIfAutomaticMode();
return this;
};
/**
* Removes the IncludedValuesProvider from the Scale.
*
* @param {Scales.IncludedValuesProvider} provider
* @returns {Scale} The calling Scale.
*/
Scale.prototype.removeIncludedValuesProvider = function (provider) {
this._includedValuesProviders.delete(provider);
this._autoDomainIfAutomaticMode();
return this;
};
return Scale;
}());
Plottable.Scale = Scale;
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var QuantitativeScale = (function (_super) {
__extends(QuantitativeScale, _super);
/**
* A QuantitativeScale is a Scale that maps number-like values to numbers.
* It is invertible and continuous.
*
* @constructor
*/
function QuantitativeScale() {
_super.call(this);
this._tickGenerator = function (scale) { return scale.defaultTicks(); };
this._padProportion = 0.05;
this._snappingDomainEnabled = true;
this._paddingExceptionsProviders = new Plottable.Utils.Set();
}
QuantitativeScale.prototype.autoDomain = function () {
this._domainMin = null;
this._domainMax = null;
_super.prototype.autoDomain.call(this);
return this;
};
QuantitativeScale.prototype._autoDomainIfAutomaticMode = function () {
if (this._domainMin != null && this._domainMax != null) {
this._setDomain([this._domainMin, this._domainMax]);
return;
}
var computedExtent = this._getExtent();
if (this._domainMin != null) {
var maxValue = computedExtent[1];
if (this._domainMin >= maxValue) {
maxValue = this._expandSingleValueDomain([this._domainMin, this._domainMin])[1];
}
this._setDomain([this._domainMin, maxValue]);
return;
}
if (this._domainMax != null) {
var minValue = computedExtent[0];
if (this._domainMax <= minValue) {
minValue = this._expandSingleValueDomain([this._domainMax, this._domainMax])[0];
}
this._setDomain([minValue, this._domainMax]);
return;
}
_super.prototype._autoDomainIfAutomaticMode.call(this);
};
QuantitativeScale.prototype._getExtent = function () {
var includedValues = this._getAllIncludedValues();
var extent = this._defaultExtent();
if (includedValues.length !== 0) {
var combinedExtent = [
Plottable.Utils.Math.min(includedValues, extent[0]),
Plottable.Utils.Math.max(includedValues, extent[1]),
];
extent = this._padDomain(combinedExtent);
}
if (this._domainMin != null) {
extent[0] = this._domainMin;
}
if (this._domainMax != null) {
extent[1] = this._domainMax;
}
return extent;
};
/**
* Adds a padding exception provider.
* If one end of the domain is set to an excepted value as a result of autoDomain()-ing,
* that end of the domain will not be padded.
*
* @param {Scales.PaddingExceptionProvider<D>} provider The provider function.
* @returns {QuantitativeScale} The calling QuantitativeScale.
*/
QuantitativeScale.prototype.addPaddingExceptionsProvider = function (provider) {
this._paddingExceptionsProviders.add(provider);
this._autoDomainIfAutomaticMode();
return this;
};
/**
* Removes the padding exception provider.
*
* @param {Scales.PaddingExceptionProvider<D>} provider The provider function.
* @returns {QuantitativeScale} The calling QuantitativeScale.
*/
QuantitativeScale.prototype.removePaddingExceptionsProvider = function (provider) {
this._paddingExceptionsProviders.delete(provider);
this._autoDomainIfAutomaticMode();
return this;
};
QuantitativeScale.prototype.padProportion = function (padProportion) {
if (padProportion == null) {
return this._padProportion;
}
if (padProportion < 0) {
throw new Error("padProportion must be non-negative");
}
this._padProportion = padProportion;
this._autoDomainIfAutomaticMode();
return this;
};
QuantitativeScale.prototype._padDomain = function (domain) {
var _this = this;
if (domain[0].valueOf() === domain[1].valueOf()) {
return this._expandSingleValueDomain(domain);
}
if (this._padProportion === 0) {
return domain;
}
var p = this._padProportion / 2;
var min = domain[0];
var max = domain[1];
var minExistsInExceptions = false;
var maxExistsInExceptions = false;
this._paddingExceptionsProviders.forEach(function (provider) {
var values = provider(_this);
values.forEach(function (value) {
if (value.valueOf() === min.valueOf()) {
minExistsInExceptions = true;
}
if (value.valueOf() === max.valueOf()) {
maxExistsInExceptions = true;
}
});
});
var originalDomain = this._backingScaleDomain();
this._backingScaleDomain(domain);
var newMin = minExistsInExceptions ? min : this.invert(this.scale(min) - (this.scale(max) - this.scale(min)) * p);
var newMax = maxExistsInExceptions ? max : this.invert(this.scale(max) + (this.scale(max) - this.scale(min)) * p);
this._backingScaleDomain(originalDomain);
if (this._snappingDomainEnabled) {
return this._niceDomain([newMin, newMax]);
}
return ([newMin, newMax]);
};
QuantitativeScale.prototype.snappingDomainEnabled = function (snappingDomainEnabled) {
if (snappingDomainEnabled == null) {
return this._snappingDomainEnabled;
}
this._snappingDomainEnabled = snappingDomainEnabled;
this._autoDomainIfAutomaticMode();
return this;
};
QuantitativeScale.prototype._expandSingleValueDomain = function (singleValueDomain) {
return singleValueDomain;
};
/**
* Computes the domain value corresponding to a supplied range value.
*
* @param {number} value: A value from the Scale's range.
* @returns {D} The domain value corresponding to the supplied range value.
*/
QuantitativeScale.prototype.invert = function (value) {
throw new Error("Subclasses should override invert");
};
QuantitativeScale.prototype.domain = function (values) {
if (values != null) {
this._domainMin = values[0];
this._domainMax = values[1];
}
return _super.prototype.domain.call(this, values);
};
QuantitativeScale.prototype.domainMin = function (domainMin) {
if (domainMin == null) {
return this.domain()[0];
}
this._domainMin = domainMin;
this._autoDomainIfAutomaticMode();
return this;
};
QuantitativeScale.prototype.domainMax = function (domainMax) {
if (domainMax == null) {
return this.domain()[1];
}
this._domainMax = domainMax;
this._autoDomainIfAutomaticMode();
return this;
};
QuantitativeScale.prototype.extentOfValues = function (values) {
// HACKHACK: TS1.4 doesn't consider numbers to be Number-like (valueOf() returning number), so D can't be typed correctly
var extent = d3.extent(values.filter(function (value) { return Plottable.Utils.Math.isValidNumber(+value); }));
if (extent[0] == null || extent[1] == null) {
return [];
}
else {
return extent;
}
};
QuantitativeScale.prototype.zoom = function (magnifyAmount, centerValue) {
var _this = this;
var magnifyTransform = function (rangeValue) { return _this.invert(Plottable.Interactions.zoomAt(rangeValue, magnifyAmount, centerValue)); };
this.domain(this.range().map(magnifyTransform));
};
QuantitativeScale.prototype.pan = function (translateAmount) {
var _this = this;
var translateTransform = function (rangeValue) { return _this.invert(rangeValue + translateAmount); };
this.domain(this.range().map(translateTransform));
};
QuantitativeScale.prototype.scaleTransformation = function (value) {
throw new Error("Subclasses should override scaleTransformation");
};
QuantitativeScale.prototype.invertedTransformation = function (value) {
throw new Error("Subclasses should override invertedTransformation");
};
QuantitativeScale.prototype.getTransformationDomain = function () {
throw new Error("Subclasses should override getTransformationDomain");
};
QuantitativeScale.prototype._setDomain = function (values) {
var isNaNOrInfinity = function (x) { return Plottable.Utils.Math.isNaN(x) || x === Infinity || x === -Infinity; };
if (isNaNOrInfinity(values[0]) || isNaNOrInfinity(values[1])) {
Plottable.Utils.Window.warn("Warning: QuantitativeScales cannot take NaN or Infinity as a domain value. Ignoring.");
return;
}
_super.prototype._setDomain.call(this, values);
};
/**
* Gets the array of tick values generated by the default algorithm.
*/
QuantitativeScale.prototype.defaultTicks = function () {
throw new Error("Subclasses should override _getDefaultTicks");
};
/**
* Gets an array of tick values spanning the domain.
*
* @returns {D[]}
*/
QuantitativeScale.prototype.ticks = function () {
return this._tickGenerator(this);
};
/**
* Given a domain, expands its domain onto "nice" values, e.g. whole
* numbers.
*/
QuantitativeScale.prototype._niceDomain = function (domain, count) {
throw new Error("Subclasses should override _niceDomain");
};
QuantitativeScale.prototype._defaultExtent = function () {
throw new Error("Subclasses should override _defaultExtent");
};
QuantitativeScale.prototype.tickGenerator = function (generator) {
if (generator == null) {
return this._tickGenerator;
}
else {
this._tickGenerator = generator;
return this;
}
};
QuantitativeScale._DEFAULT_NUM_TICKS = 10;
return QuantitativeScale;
}(Plottable.Scale));
Plottable.QuantitativeScale = QuantitativeScale;
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Scales;
(function (Scales) {
var Linear = (function (_super) {
__extends(Linear, _super);
/**
* @constructor
*/
function Linear() {
_super.call(this);
this._d3Scale = d3.scale.linear();
}
Linear.prototype._defaultExtent = function () {
return [0, 1];
};
Linear.prototype._expandSingleValueDomain = function (singleValueDomain) {
if (singleValueDomain[0] === singleValueDomain[1]) {
return [singleValueDomain[0] - 1, singleValueDomain[1] + 1];
}
return singleValueDomain;
};
Linear.prototype.scale = function (value) {
return this._d3Scale(value);
};
Linear.prototype.scaleTransformation = function (value) {
return this.scale(value);
};
Linear.prototype.invertedTransformation = function (value) {
return this.invert(value);
};
Linear.prototype.getTransformationDomain = function () {
return this.domain();
};
Linear.prototype._getDomain = function () {
return this._backingScaleDomain();
};
Linear.prototype._backingScaleDomain = function (values) {
if (values == null) {
return this._d3Scale.domain();
}
else {
this._d3Scale.domain(values);
return this;
}
};
Linear.prototype._getRange = function () {
return this._d3Scale.range();
};
Linear.prototype._setRange = function (values) {
this._d3Scale.range(values);
};
Linear.prototype.invert = function (value) {
return this._d3Scale.invert(value);
};
Linear.prototype.defaultTicks = function () {
return this._d3Scale.ticks(Scales.Linear._DEFAULT_NUM_TICKS);
};
Linear.prototype._niceDomain = function (domain, count) {
return this._d3Scale.copy().domain(domain).nice(count).domain();
};
return Linear;
}(Plottable.QuantitativeScale));
Scales.Linear = Linear;
})(Scales = Plottable.Scales || (Plottable.Scales = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Scales;
(function (Scales) {
var ModifiedLog = (function (_super) {
__extends(ModifiedLog, _super);
/**
* A ModifiedLog Scale acts as a regular log scale for large numbers.
* As it approaches 0, it gradually becomes linear.
* Consequently, a ModifiedLog Scale can process 0 and negative numbers.
*
* For x >= base, scale(x) = log(x).
*
* For 0 < x < base, scale(x) will become more and more
* linear as it approaches 0.
*
* At x == 0, scale(x) == 0.
*
* For negative values, scale(-x) = -scale(x).
*
* The range and domain for the scale should also be set, using the
* range() and domain() accessors, respectively.
*
* For `range`, provide a two-element array giving the minimum and
* maximum of values produced when scaling.
*
* For `domain` provide a two-element array giving the minimum and
* maximum of the values that will be scaled.
*
* @constructor
* @param {number} [base=10]
* The base of the log. Must be > 1.
*
*/
function ModifiedLog(base) {
if (base === void 0) { base = 10; }
_super.call(this);
this._d3Scale = d3.scale.linear();
this._base = base;
this._pivot = this._base;
this._setDomain(this._defaultExtent());
if (base <= 1) {
throw new Error("ModifiedLogScale: The base must be > 1");
}
}
/**
* Returns an adjusted log10 value for graphing purposes. The first
* adjustment is that negative values are changed to positive during
* the calculations, and then the answer is negated at the end. The
* second is that, for values less than 10, an increasingly large
* (0 to 1) scaling factor is added such that at 0 the value is
* adjusted to 1, resulting in a returned result of 0.
*/
ModifiedLog.prototype._adjustedLog = function (x) {
var negationFactor = x < 0 ? -1 : 1;
x *= negationFactor;
if (x < this._pivot) {
x += (this._pivot - x) / this._pivot;
}
x = Math.log(x) / Math.log(this._base);
x *= negationFactor;
return x;
};
ModifiedLog.prototype._invertedAdjustedLog = function (x) {
var negationFactor = x < 0 ? -1 : 1;
x *= negationFactor;
x = Math.pow(this._base, x);
if (x < this._pivot) {
x = (this._pivot * (x - 1)) / (this._pivot - 1);
}
x *= negationFactor;
return x;
};
ModifiedLog.prototype.scale = function (x) {
return this._d3Scale(this._adjustedLog(x));
};
ModifiedLog.prototype.invert = function (x) {
return this._invertedAdjustedLog(this._d3Scale.invert(x));
};
ModifiedLog.prototype.scaleTransformation = function (value) {
return this.scale(value);
};
ModifiedLog.prototype.invertedTransformation = function (value) {
return this.invert(value);
};
ModifiedLog.prototype.getTransformationDomain = function () {
return this.domain();
};
ModifiedLog.prototype._getDomain = function () {
return this._untransformedDomain;
};
ModifiedLog.prototype._setDomain = function (values) {
this._untransformedDomain = values;
var transformedDomain = [this._adjustedLog(values[0]), this._adjustedLog(values[1])];
_super.prototype._setDomain.call(this, transformedDomain);
};
ModifiedLog.prototype._backingScaleDomain = function (values) {
if (values == null) {
return this._d3Scale.domain();
}
else {
this._d3Scale.domain(values);
return this;
}
};
ModifiedLog.prototype.ticks = function () {
// Say your domain is [-100, 100] and your pivot is 10.
// then we're going to draw negative log ticks from -100 to -10,
// linear ticks from -10 to 10, and positive log ticks from 10 to 100.
var middle = function (x, y, z) { return [x, y, z].sort(function (a, b) { return a - b; })[1]; };
var min = Plottable.Utils.Math.min(this._untransformedDomain, 0);
var max = Plottable.Utils.Math.max(this._untransformedDomain, 0);
var negativeLower = min;
var negativeUpper = middle(min, max, -this._pivot);
var positiveLower = middle(min, max, this._pivot);
var positiveUpper = max;
var negativeLogTicks = this._logTicks(-negativeUpper, -negativeLower).map(function (x) { return -x; }).reverse();
var positiveLogTicks = this._logTicks(positiveLower, positiveUpper);
var linearMin = Math.max(min, -this._pivot);
var linearMax = Math.min(max, this._pivot);
var linearTicks = d3.scale.linear().domain([linearMin, linearMax]).ticks(this._howManyTicks(linearMin, linearMax));
var ticks = negativeLogTicks.concat(linearTicks).concat(positiveLogTicks);
// If you only have 1 tick, you can't tell how big the scale is.
if (ticks.length <= 1) {
ticks = d3.scale.linear().domain([min, max]).ticks(Scales.ModifiedLog._DEFAULT_NUM_TICKS);
}
return ticks;
};
/**
* Return an appropriate number of ticks from lower to upper.
*
* This will first try to fit as many powers of this.base as it can from
* lower to upper.
*
* If it still has ticks after that, it will generate ticks in "clusters",
* e.g. [20, 30, ... 90, 100] would be a cluster, [200, 300, ... 900, 1000]
* would be another cluster.
*
* This function will generate clusters as large as it can while not
* drastically exceeding its number of ticks.
*/
ModifiedLog.prototype._logTicks = function (lower, upper) {
var _this = this;
var nTicks = this._howManyTicks(lower, upper);
if (nTicks === 0) {
return [];
}
var startLogged = Math.floor(Math.log(lower) / Math.log(this._base));
var endLogged = Math.ceil(Math.log(upper) / Math.log(this._base));
var bases = d3.range(endLogged, startLogged, -Math.ceil((endLogged - startLogged) / nTicks));
var multiples = d3.range(this._base, 1, -(this._base - 1)).map(Math.floor);
var uniqMultiples = Plottable.Utils.Array.uniq(multiples);
var clusters = bases.map(function (b) { return uniqMultiples.map(function (x) { return Math.pow(_this._base, b - 1) * x; }); });
var flattened = Plottable.Utils.Array.flatten(clusters);
var filtered = flattened.filter(function (x) { return lower <= x && x <= upper; });
var sorted = filtered.sort(function (x, y) { return x - y; });
return sorted;
};
/**
* How many ticks does the range [lower, upper] deserve?
*
* e.g. if your domain was [10, 1000] and I asked _howManyTicks(10, 100),
* I would get 1/2 of the ticks. The range 10, 100 takes up 1/2 of the
* distance when plotted.
*/
ModifiedLog.prototype._howManyTicks = function (lower, upper) {
var adjustedMin = this._adjustedLog(Plottable.Utils.Math.min(this._untransformedDomain, 0));
var adjustedMax = this._adjustedLog(Plottable.Utils.Math.max(this._untransformedDomain, 0));
var adjustedLower = this._adjustedLog(lower);
var adjustedUpper = this._adjustedLog(upper);
var proportion = (adjustedUpper - adjustedLower) / (adjustedMax - adjustedMin);
var ticks = Math.ceil(proportion * Scales.ModifiedLog._DEFAULT_NUM_TICKS);
return ticks;
};
ModifiedLog.prototype._niceDomain = function (domain, count) {
return domain;
};
ModifiedLog.prototype._defaultExtent = function () {
return [0, this._base];
};
ModifiedLog.prototype._expandSingleValueDomain = function (singleValueDomain) {
if (singleValueDomain[0] === singleValueDomain[1]) {
var singleValue = singleValueDomain[0];
if (singleValue > 0) {
return [singleValue / this._base, singleValue * this._base];
}
else if (singleValue === 0) {
return [-this._base, this._base];
}
else {
return [singleValue * this._base, singleValue / this._base];
}
}
return singleValueDomain;
};
ModifiedLog.prototype._getRange = function () {
return this._d3Scale.range();
};
ModifiedLog.prototype._setRange = function (values) {
this._d3Scale.range(values);
};
ModifiedLog.prototype.defaultTicks = function () {
return this._d3Scale.ticks(Scales.ModifiedLog._DEFAULT_NUM_TICKS);
};
return ModifiedLog;
}(Plottable.QuantitativeScale));
Scales.ModifiedLog = ModifiedLog;
})(Scales = Plottable.Scales || (Plottable.Scales = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Scales;
(function (Scales) {
var TRANSFORMATION_SPACE = [0, 1];
var Category = (function (_super) {
__extends(Category, _super);
/**
* A Category Scale maps strings to numbers.
*
* @constructor
*/
function Category() {
_super.call(this);
this._range = [0, 1];
this._d3Scale = d3.scale.ordinal();
this._d3Scale.range(TRANSFORMATION_SPACE);
this._d3TransformationScale = d3.scale.linear();
this._d3TransformationScale.domain(TRANSFORMATION_SPACE);
var d3InnerPadding = 0.3;
this._innerPadding = Category._convertToPlottableInnerPadding(d3InnerPadding);
this._outerPadding = Category._convertToPlottableOuterPadding(0.5, d3InnerPadding);
}
Category.prototype.extentOfValues = function (values) {
return Plottable.Utils.Array.uniq(values);
};
Category.prototype._getExtent = function () {
return Plottable.Utils.Array.uniq(this._getAllIncludedValues());
};
Category.prototype.domain = function (values) {
return _super.prototype.domain.call(this, values);
};
Category.prototype.range = function (values) {
return _super.prototype.range.call(this, values);
};
Category._convertToPlottableInnerPadding = function (d3InnerPadding) {
return 1 / (1 - d3InnerPadding) - 1;
};
Category._convertToPlottableOuterPadding = function (d3OuterPadding, d3InnerPadding) {
return d3OuterPadding / (1 - d3InnerPadding);
};
Category.prototype._setBands = function () {
var d3InnerPadding = 1 - 1 / (1 + this.innerPadding());
var d3OuterPadding = this.outerPadding() / (1 + this.innerPadding());
this._d3Scale.rangeBands(TRANSFORMATION_SPACE, d3InnerPadding, d3OuterPadding);
};
/**
* Returns the width of the range band.
*
* @returns {number} The range band width
*/
Category.prototype.rangeBand = function () {
return this._rescaleBand(this._d3Scale.rangeBand());
};
/**
* Returns the step width of the scale.
*
* The step width is the pixel distance between adjacent values in the domain.
*
* @returns {number}
*/
Category.prototype.stepWidth = function () {
return this._rescaleBand(this._d3Scale.rangeBand() * (1 + this.innerPadding()));
};
Category.prototype.innerPadding = function (innerPadding) {
if (innerPadding == null) {
return this._innerPadding;
}
this._innerPadding = innerPadding;
this.range(this.range());
this._dispatchUpdate();
return this;
};
Category.prototype.outerPadding = function (outerPadding) {
if (outerPadding == null) {
return this._outerPadding;
}
this._outerPadding = outerPadding;
this.range(this.range());
this._dispatchUpdate();
return this;
};
Category.prototype.scale = function (value) {
// Determine the middle of the range band for the value
var untransformed = this._d3Scale(value) + this._d3Scale.rangeBand() / 2;
// Convert to screen space
return this._d3TransformationScale(untransformed);
};
Category.prototype.zoom = function (magnifyAmount, centerValue) {
var _this = this;
var magnifyTransform = function (rangeValue) {
return _this._d3TransformationScale.invert(Plottable.Interactions.zoomAt(rangeValue, magnifyAmount, centerValue));
};
this._d3TransformationScale.domain(this._d3TransformationScale.range().map(magnifyTransform));
this._dispatchUpdate();
};
Category.prototype.pan = function (translateAmount) {
var _this = this;
var translateTransform = function (rangeValue) {
return _this._d3TransformationScale.invert(rangeValue + translateAmount);
};
this._d3TransformationScale.domain(this._d3TransformationScale.range().map(translateTransform));
this._dispatchUpdate();
};
Category.prototype.scaleTransformation = function (value) {
return this._d3TransformationScale(value);
};
Category.prototype.invertedTransformation = function (value) {
return this._d3TransformationScale.invert(value);
};
Category.prototype.getTransformationDomain = function () {
return this._d3TransformationScale.domain();
};
Category.prototype._getDomain = function () {
return this._backingScaleDomain();
};
Category.prototype._backingScaleDomain = function (values) {
if (values == null) {
return this._d3Scale.domain();
}
else {
this._d3Scale.domain(values);
this._setBands();
return this;
}
};
Category.prototype._getRange = function () {
return this._range;
};
Category.prototype._setRange = function (values) {
this._range = values;
this._d3TransformationScale.range(values);
this._setBands();
};
/**
* Converts a width or height in *Transformation Space* into *Screen Space*.
*/
Category.prototype._rescaleBand = function (band) {
return Math.abs(this._d3TransformationScale(band) - this._d3TransformationScale(0));
};
return Category;
}(Plottable.Scale));
Scales.Category = Category;
})(Scales = Plottable.Scales || (Plottable.Scales = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Scales;
(function (Scales) {
var Color = (function (_super) {
__extends(Color, _super);
/**
* A Color Scale maps string values to color hex values expressed as a string.
*
* @constructor
* @param {string} [scaleType] One of "Category10"/"Category20"/"Category20b"/"Category20c".
* (see https://github.com/mbostock/d3/wiki/Ordinal-Scales#categorical-colors)
* If not supplied, reads the colors defined using CSS -- see plottable.css.
*/
function Color(scaleType) {
_super.call(this);
var scale;
switch (scaleType) {
case null:
case undefined:
if (Color._plottableColorCache == null) {
Color._plottableColorCache = Color._getPlottableColors();
}
scale = d3.scale.ordinal().range(Color._plottableColorCache);
break;
case "Category10":
case "category10":
case "10":
scale = d3.scale.category10();
break;
case "Category20":
case "category20":
case "20":
scale = d3.scale.category20();
break;
case "Category20b":
case "category20b":
case "20b":
scale = d3.scale.category20b();
break;
case "Category20c":
case "category20c":
case "20c":
scale = d3.scale.category20c();
break;
default:
throw new Error("Unsupported ColorScale type");
}
this._d3Scale = scale;
}
Color.prototype.extentOfValues = function (values) {
return Plottable.Utils.Array.uniq(values);
};
// Duplicated from OrdinalScale._getExtent - should be removed in #388
Color.prototype._getExtent = function () {
return Plottable.Utils.Array.uniq(this._getAllIncludedValues());
};
Color.invalidateColorCache = function () {
Color._plottableColorCache = null;
};
Color._getPlottableColors = function () {
var plottableDefaultColors = [];
var colorTester = d3.select("body").append("plottable-color-tester");
var defaultColorHex = Plottable.Utils.Color.colorTest(colorTester, "");
var i = 0;
var colorHex = Plottable.Utils.Color.colorTest(colorTester, "plottable-colors-0");
while (colorHex != null && i < this._MAXIMUM_COLORS_FROM_CSS) {
if (colorHex === defaultColorHex && colorHex === plottableDefaultColors[plottableDefaultColors.length - 1]) {
break;
}
plottableDefaultColors.push(colorHex);
i++;
colorHex = Plottable.Utils.Color.colorTest(colorTester, "plottable-colors-" + i);
}
colorTester.remove();
return plottableDefaultColors;
};
/**
* Returns the color-string corresponding to a given string.
* If there are not enough colors in the range(), a lightened version of an existing color will be used.
*
* @param {string} value
* @returns {string}
*/
Color.prototype.scale = function (value) {
var color = this._d3Scale(value);
var index = this.domain().indexOf(value);
var numLooped = Math.floor(index / this.range().length);
var modifyFactor = Math.log(numLooped * Color._LOOP_LIGHTEN_FACTOR + 1);
return Plottable.Utils.Color.lightenColor(color, modifyFactor);
};
Color.prototype._getDomain = function () {
return this._backingScaleDomain();
};
Color.prototype._backingScaleDomain = function (values) {
if (values == null) {
return this._d3Scale.domain();
}
else {
this._d3Scale.domain(values);
return this;
}
};
Color.prototype._getRange = function () {
return this._d3Scale.range();
};
Color.prototype._setRange = function (values) {
this._d3Scale.range(values);
};
Color._LOOP_LIGHTEN_FACTOR = 1.6;
// The maximum number of colors we are getting from CSS stylesheets
Color._MAXIMUM_COLORS_FROM_CSS = 256;
return Color;
}(Plottable.Scale));
Scales.Color = Color;
})(Scales = Plottable.Scales || (Plottable.Scales = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Scales;
(function (Scales) {
var Time = (function (_super) {
__extends(Time, _super);
/**
* A Time Scale maps Date objects to numbers.
*
* @constructor
*/
function Time() {
_super.call(this);
this._d3Scale = d3.time.scale();
this.autoDomain();
}
/**
* Returns an array of ticks values separated by the specified interval.
*
* @param {string} interval A string specifying the interval unit.
* @param {number?} [step] The number of multiples of the interval between consecutive ticks.
* @return {Date[]}
*/
Time.prototype.tickInterval = function (interval, step) {
// temporarily creats a time scale from our linear scale into a time scale so we can get access to its api
var tempScale = d3.time.scale();
var d3Interval = Time.timeIntervalToD3Time(interval);
tempScale.domain(this.domain());
tempScale.range(this.range());
return tempScale.ticks(d3Interval, step);
};
Time.prototype._setDomain = function (values) {
if (values[1] < values[0]) {
throw new Error("Scale.Time domain values must be in chronological order");
}
return _super.prototype._setDomain.call(this, values);
};
Time.prototype._defaultExtent = function () {
return [new Date("1970-01-01"), new Date("1970-01-02")];
};
Time.prototype._expandSingleValueDomain = function (singleValueDomain) {
var startTime = singleValueDomain[0].getTime();
var endTime = singleValueDomain[1].getTime();
if (startTime === endTime) {
var startDate = new Date(startTime);
startDate.setDate(startDate.getDate() - 1);
var endDate = new Date(endTime);
endDate.setDate(endDate.getDate() + 1);
return [startDate, endDate];
}
return singleValueDomain;
};
Time.prototype.scale = function (value) {
return this._d3Scale(value);
};
Time.prototype.scaleTransformation = function (value) {
return this.scale(new Date(value));
};
Time.prototype.invertedTransformation = function (value) {
return this.invert(value).getTime();
};
Time.prototype.getTransformationDomain = function () {
var dates = this.domain();
return [dates[0].valueOf(), dates[1].valueOf()];
};
Time.prototype._getDomain = function () {
return this._backingScaleDomain();
};
Time.prototype._backingScaleDomain = function (values) {
if (values == null) {
return this._d3Scale.domain();
}
else {
this._d3Scale.domain(values);
return this;
}
};
Time.prototype._getRange = function () {
return this._d3Scale.range();
};
Time.prototype._setRange = function (values) {
this._d3Scale.range(values);
};
Time.prototype.invert = function (value) {
return this._d3Scale.invert(value);
};
Time.prototype.defaultTicks = function () {
return this._d3Scale.ticks(Scales.Time._DEFAULT_NUM_TICKS);
};
Time.prototype._niceDomain = function (domain) {
return this._d3Scale.copy().domain(domain).nice().domain();
};
/**
* Transforms the Plottable TimeInterval string into a d3 time interval equivalent.
* If the provided TimeInterval is incorrect, the default is d3.time.year
*/
Time.timeIntervalToD3Time = function (timeInterval) {
switch (timeInterval) {
case Plottable.TimeInterval.second:
return d3.time.second;
case Plottable.TimeInterval.minute:
return d3.time.minute;
case Plottable.TimeInterval.hour:
return d3.time.hour;
case Plottable.TimeInterval.day:
return d3.time.day;
case Plottable.TimeInterval.week:
return d3.time.week;
case Plottable.TimeInterval.month:
return d3.time.month;
case Plottable.TimeInterval.year:
return d3.time.year;
default:
throw Error("TimeInterval specified does not exist: " + timeInterval);
}
};
return Time;
}(Plottable.QuantitativeScale));
Scales.Time = Time;
})(Scales = Plottable.Scales || (Plottable.Scales = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Scales;
(function (Scales) {
var InterpolatedColor = (function (_super) {
__extends(InterpolatedColor, _super);
/**
* An InterpolatedColor Scale maps numbers to color hex values, expressed as strings.
*
* @param {string} [scaleType="linear"] One of "linear"/"log"/"sqrt"/"pow".
*/
function InterpolatedColor(scaleType) {
if (scaleType === void 0) { scaleType = "linear"; }
_super.call(this);
switch (scaleType) {
case "linear":
this._colorScale = d3.scale.linear();
break;
case "log":
this._colorScale = d3.scale.log();
break;
case "sqrt":
this._colorScale = d3.scale.sqrt();
break;
case "pow":
this._colorScale = d3.scale.pow();
break;
}
if (this._colorScale == null) {
throw new Error("unknown QuantitativeScale scale type " + scaleType);
}
this.range(InterpolatedColor.REDS);
}
InterpolatedColor.prototype.extentOfValues = function (values) {
var extent = d3.extent(values);
if (extent[0] == null || extent[1] == null) {
return [];
}
else {
return extent;
}
};
/**
* Generates the converted QuantitativeScale.
*/
InterpolatedColor.prototype._d3InterpolatedScale = function () {
return this._colorScale.range([0, 1]).interpolate(this._interpolateColors());
};
/**
* Generates the d3 interpolator for colors.
*/
InterpolatedColor.prototype._interpolateColors = function () {
var colors = this._colorRange;
if (colors.length < 2) {
throw new Error("Color scale arrays must have at least two elements.");
}
;
return function (a, b) {
return function (t) {
// Clamp t parameter to [0,1]
t = Math.max(0, Math.min(1, t));
// Determine indices for colors
var tScaled = t * (colors.length - 1);
var i0 = Math.floor(tScaled);
var i1 = Math.ceil(tScaled);
var frac = (tScaled - i0);
// Interpolate in the L*a*b color space
return d3.interpolateLab(colors[i0], colors[i1])(frac);
};
};
};
InterpolatedColor.prototype._resetScale = function () {
this._d3Scale = this._d3InterpolatedScale();
this._autoDomainIfAutomaticMode();
this._dispatchUpdate();
};
InterpolatedColor.prototype.autoDomain = function () {
// InterpolatedColorScales do not pad
var includedValues = this._getAllIncludedValues();
if (includedValues.length > 0) {
this._setDomain([Plottable.Utils.Math.min(includedValues, 0), Plottable.Utils.Math.max(includedValues, 0)]);
}
return this;
};
InterpolatedColor.prototype.scale = function (value) {
return this._d3Scale(value);
};
InterpolatedColor.prototype._getDomain = function () {
return this._backingScaleDomain();
};
InterpolatedColor.prototype._backingScaleDomain = function (values) {
if (values == null) {
return this._d3Scale.domain();
}
else {
this._d3Scale.domain(values);
return this;
}
};
InterpolatedColor.prototype._getRange = function () {
return this._colorRange;
};
InterpolatedColor.prototype._setRange = function (range) {
this._colorRange = range;
this._resetScale();
};
InterpolatedColor.REDS = [
"#FFFFFF",
"#FFF6E1",
"#FEF4C0",
"#FED976",
"#FEB24C",
"#FD8D3C",
"#FC4E2A",
"#E31A1C",
"#B10026",
];
InterpolatedColor.BLUES = [
"#FFFFFF",
"#CCFFFF",
"#A5FFFD",
"#85F7FB",
"#6ED3EF",
"#55A7E0",
"#417FD0",
"#2545D3",
"#0B02E1",
];
InterpolatedColor.POSNEG = [
"#0B02E1",
"#2545D3",
"#417FD0",
"#55A7E0",
"#6ED3EF",
"#85F7FB",
"#A5FFFD",
"#CCFFFF",
"#FFFFFF",
"#FFF6E1",
"#FEF4C0",
"#FED976",
"#FEB24C",
"#FD8D3C",
"#FC4E2A",
"#E31A1C",
"#B10026",
];
return InterpolatedColor;
}(Plottable.Scale));
Scales.InterpolatedColor = InterpolatedColor;
})(Scales = Plottable.Scales || (Plottable.Scales = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Scales;
(function (Scales) {
var TickGenerators;
(function (TickGenerators) {
/**
* Creates a TickGenerator using the specified interval.
*
* Generates ticks at multiples of the interval while also including the domain boundaries.
*
* @param {number} interval
* @returns {TickGenerator}
*/
function intervalTickGenerator(interval) {
if (interval <= 0) {
throw new Error("interval must be positive number");
}
return function (s) {
var domain = s.domain();
var low = Math.min(domain[0], domain[1]);
var high = Math.max(domain[0], domain[1]);
var firstTick = Math.ceil(low / interval) * interval;
var numTicks = Math.floor((high - firstTick) / interval) + 1;
var lowTicks = low % interval === 0 ? [] : [low];
var middleTicks = Plottable.Utils.Math.range(0, numTicks).map(function (t) { return firstTick + t * interval; });
var highTicks = high % interval === 0 ? [] : [high];
return lowTicks.concat(middleTicks).concat(highTicks);
};
}
TickGenerators.intervalTickGenerator = intervalTickGenerator;
/**
* Creates a TickGenerator returns only integer tick values.
*
* @returns {TickGenerator}
*/
function integerTickGenerator() {
return function (s) {
var defaultTicks = s.defaultTicks();
return defaultTicks.filter(function (tick, i) { return (tick % 1 === 0) || (i === 0) || (i === defaultTicks.length - 1); });
};
}
TickGenerators.integerTickGenerator = integerTickGenerator;
})(TickGenerators = Scales.TickGenerators || (Scales.TickGenerators = {}));
})(Scales = Plottable.Scales || (Plottable.Scales = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Drawer = (function () {
/**
* A Drawer draws svg elements based on the input Dataset.
*
* @constructor
* @param {Dataset} dataset The dataset associated with this Drawer
*/
function Drawer(dataset) {
this._cachedSelectionValid = false;
this._dataset = dataset;
this._svgElementName = "path";
}
Drawer.prototype.renderArea = function (area) {
if (area == null) {
return this._renderArea;
}
this._renderArea = area;
this._cachedSelectionValid = false;
return this;
};
/**
* Removes the Drawer and its renderArea
*/
Drawer.prototype.remove = function () {
if (this.renderArea() != null) {
this.renderArea().remove();
}
};
/**
* Binds data to selection
*
* @param{any[]} data The data to be drawn
*/
Drawer.prototype._bindSelectionData = function (data) {
var dataElements = this.selection().data(data);
dataElements.enter().append(this._svgElementName);
dataElements.exit().remove();
this._applyDefaultAttributes(dataElements);
};
Drawer.prototype._applyDefaultAttributes = function (selection) {
if (this._className != null) {
selection.classed(this._className, true);
}
};
/**
* Draws data using one step
*
* @param{AppliedDrawStep} step The step, how data should be drawn.
*/
Drawer.prototype._drawStep = function (step) {
var selection = this.selection();
var colorAttributes = ["fill", "stroke"];
colorAttributes.forEach(function (colorAttribute) {
if (step.attrToAppliedProjector[colorAttribute] != null) {
selection.attr(colorAttribute, step.attrToAppliedProjector[colorAttribute]);
}
});
step.animator.animate(selection, step.attrToAppliedProjector);
if (this._className != null) {
this.selection().classed(this._className, true);
}
};
Drawer.prototype._appliedProjectors = function (attrToProjector) {
var _this = this;
var modifiedAttrToProjector = {};
Object.keys(attrToProjector).forEach(function (attr) {
modifiedAttrToProjector[attr] =
function (datum, index) { return attrToProjector[attr](datum, index, _this._dataset); };
});
return modifiedAttrToProjector;
};
/**
* Calculates the total time it takes to use the input drawSteps to draw the input data
*
* @param {any[]} data The data that would have been drawn
* @param {Drawers.DrawStep[]} drawSteps The DrawSteps to use
* @returns {number} The total time it takes to draw
*/
Drawer.prototype.totalDrawTime = function (data, drawSteps) {
var delay = 0;
drawSteps.forEach(function (drawStep, i) {
delay += drawStep.animator.totalTime(data.length);
});
return delay;
};
/**
* Draws the data into the renderArea using the spefic steps and metadata
*
* @param{any[]} data The data to be drawn
* @param{DrawStep[]} drawSteps The list of steps, which needs to be drawn
*/
Drawer.prototype.draw = function (data, drawSteps) {
var _this = this;
var appliedDrawSteps = drawSteps.map(function (dr) {
var attrToAppliedProjector = _this._appliedProjectors(dr.attrToProjector);
return {
attrToAppliedProjector: attrToAppliedProjector,
animator: dr.animator,
};
});
this._bindSelectionData(data);
this._cachedSelectionValid = false;
var delay = 0;
appliedDrawSteps.forEach(function (drawStep, i) {
Plottable.Utils.Window.setTimeout(function () { return _this._drawStep(drawStep); }, delay);
delay += drawStep.animator.totalTime(data.length);
});
return this;
};
Drawer.prototype.selection = function () {
if (!this._cachedSelectionValid) {
this._cachedSelection = this.renderArea().selectAll(this.selector());
this._cachedSelectionValid = true;
}
return this._cachedSelection;
};
/**
* Returns the CSS selector for this Drawer's visual elements.
*/
Drawer.prototype.selector = function () {
return this._svgElementName;
};
/**
* Returns the D3 selection corresponding to the datum with the specified index.
*/
Drawer.prototype.selectionForIndex = function (index) {
return d3.select(this.selection()[0][index]);
};
return Drawer;
}());
Plottable.Drawer = Drawer;
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Drawers;
(function (Drawers) {
var Line = (function (_super) {
__extends(Line, _super);
function Line(dataset) {
_super.call(this, dataset);
this._className = "line";
this._svgElementName = "path";
}
Line.prototype._applyDefaultAttributes = function (selection) {
_super.prototype._applyDefaultAttributes.call(this, selection);
selection.style("fill", "none");
};
Line.prototype.selectionForIndex = function (index) {
return d3.select(this.selection()[0][0]);
};
return Line;
}(Plottable.Drawer));
Drawers.Line = Line;
})(Drawers = Plottable.Drawers || (Plottable.Drawers = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Drawers;
(function (Drawers) {
var Area = (function (_super) {
__extends(Area, _super);
function Area(dataset) {
_super.call(this, dataset);
this._className = "area";
this._svgElementName = "path";
}
Area.prototype._applyDefaultAttributes = function (selection) {
_super.prototype._applyDefaultAttributes.call(this, selection);
selection.style("stroke", "none");
};
Area.prototype.selectionForIndex = function (index) {
return d3.select(this.selection()[0][0]);
};
return Area;
}(Plottable.Drawer));
Drawers.Area = Area;
})(Drawers = Plottable.Drawers || (Plottable.Drawers = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Drawers;
(function (Drawers) {
var Rectangle = (function (_super) {
__extends(Rectangle, _super);
function Rectangle(dataset) {
_super.call(this, dataset);
this._svgElementName = "rect";
}
return Rectangle;
}(Plottable.Drawer));
Drawers.Rectangle = Rectangle;
})(Drawers = Plottable.Drawers || (Plottable.Drawers = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Drawers;
(function (Drawers) {
var Arc = (function (_super) {
__extends(Arc, _super);
function Arc(dataset) {
_super.call(this, dataset);
this._className = "arc fill";
this._svgElementName = "path";
}
Arc.prototype._applyDefaultAttributes = function (selection) {
_super.prototype._applyDefaultAttributes.call(this, selection);
selection.style("stroke", "none");
};
return Arc;
}(Plottable.Drawer));
Drawers.Arc = Arc;
})(Drawers = Plottable.Drawers || (Plottable.Drawers = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Drawers;
(function (Drawers) {
var ArcOutline = (function (_super) {
__extends(ArcOutline, _super);
function ArcOutline(dataset) {
_super.call(this, dataset);
this._className = "arc outline";
this._svgElementName = "path";
}
ArcOutline.prototype._applyDefaultAttributes = function (selection) {
_super.prototype._applyDefaultAttributes.call(this, selection);
selection.style("fill", "none");
};
return ArcOutline;
}(Plottable.Drawer));
Drawers.ArcOutline = ArcOutline;
})(Drawers = Plottable.Drawers || (Plottable.Drawers = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Drawers;
(function (Drawers) {
var Symbol = (function (_super) {
__extends(Symbol, _super);
function Symbol(dataset) {
_super.call(this, dataset);
this._svgElementName = "path";
this._className = "symbol";
}
return Symbol;
}(Plottable.Drawer));
Drawers.Symbol = Symbol;
})(Drawers = Plottable.Drawers || (Plottable.Drawers = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Drawers;
(function (Drawers) {
var Segment = (function (_super) {
__extends(Segment, _super);
function Segment(dataset) {
_super.call(this, dataset);
this._svgElementName = "line";
}
return Segment;
}(Plottable.Drawer));
Drawers.Segment = Segment;
})(Drawers = Plottable.Drawers || (Plottable.Drawers = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Components;
(function (Components) {
var Alignment = (function () {
function Alignment() {
}
Alignment.TOP = "top";
Alignment.BOTTOM = "bottom";
Alignment.LEFT = "left";
Alignment.RIGHT = "right";
Alignment.CENTER = "center";
return Alignment;
}());
Components.Alignment = Alignment;
})(Components = Plottable.Components || (Plottable.Components = {}));
var Component = (function () {
function Component() {
this._clipPathEnabled = false;
this._origin = { x: 0, y: 0 }; // Origin of the coordinate space for the Component.
this._xAlignment = "left";
this._yAlignment = "top";
this._isSetup = false;
this._isAnchored = false;
this._boxes = [];
this._isTopLevelComponent = false;
this._cssClasses = new Plottable.Utils.Set();
this._destroyed = false;
this._onAnchorCallbacks = new Plottable.Utils.CallbackSet();
this._onDetachCallbacks = new Plottable.Utils.CallbackSet();
this._cssClasses.add("component");
}
/**
* Attaches the Component as a child of a given d3 Selection.
*
* @param {d3.Selection} selection.
* @returns {Component} The calling Component.
*/
Component.prototype.anchor = function (selection) {
if (this._destroyed) {
throw new Error("Can't reuse destroy()-ed Components!");
}
this._isTopLevelComponent = selection.node().nodeName.toLowerCase() === "svg";
if (this._isTopLevelComponent) {
// svg node gets the "plottable" CSS class
this._rootSVG = selection;
this._rootSVG.classed("plottable", true);
// visible overflow for firefox https://stackoverflow.com/questions/5926986/why-does-firefox-appear-to-truncate-embedded-svgs
this._rootSVG.style("overflow", "visible");
// HACKHACK: Safari fails to register events on the <svg> itself
var safariBacking = this._rootSVG.select("." + Component._SAFARI_EVENT_BACKING_CLASS);
if (safariBacking.empty()) {
this._rootSVG.append("rect").classed(Component._SAFARI_EVENT_BACKING_CLASS, true).attr({
x: 0,
y: 0,
width: "100%",
height: "100%",
}).style("opacity", 0);
}
}
if (this._element != null) {
// reattach existing element
selection.node().appendChild(this._element.node());
}
else {
this._element = selection.append("g");
this._setup();
}
this._isAnchored = true;
this._onAnchorCallbacks.callCallbacks(this);
return this;
};
/**
* Adds a callback to be called on anchoring the Component to the DOM.
* If the Component is already anchored, the callback is called immediately.
*
* @param {ComponentCallback} callback
* @return {Component}
*/
Component.prototype.onAnchor = function (callback) {
if (this._isAnchored) {
callback(this);
}
this._onAnchorCallbacks.add(callback);
return this;
};
/**
* Removes a callback that would be called on anchoring the Component to the DOM.
* The callback is identified by reference equality.
*
* @param {ComponentCallback} callback
* @return {Component}
*/
Component.prototype.offAnchor = function (callback) {
this._onAnchorCallbacks.delete(callback);
return this;
};
/**
* Creates additional elements as necessary for the Component to function.
* Called during anchor() if the Component's element has not been created yet.
* Override in subclasses to provide additional functionality.
*/
Component.prototype._setup = function () {
var _this = this;
if (this._isSetup) {
return;
}
this._cssClasses.forEach(function (cssClass) {
_this._element.classed(cssClass, true);
});
this._cssClasses = new Plottable.Utils.Set();
this._backgroundContainer = this._element.append("g").classed("background-container", true);
this._addBox("background-fill", this._backgroundContainer);
this._content = this._element.append("g").classed("content", true);
this._foregroundContainer = this._element.append("g").classed("foreground-container", true);
this._boxContainer = this._element.append("g").classed("box-container", true);
if (this._clipPathEnabled) {
this._generateClipPath();
}
;
this._boundingBox = this._addBox("bounding-box");
this._isSetup = true;
};
/**
* Given available space in pixels, returns the minimum width and height this Component will need.
*
* @param {number} availableWidth
* @param {number} availableHeight
* @returns {SpaceRequest}
*/
Component.prototype.requestedSpace = function (availableWidth, availableHeight) {
return {
minWidth: 0,
minHeight: 0,
};
};
/**
* Computes and sets the size, position, and alignment of the Component from the specified values.
* If no parameters are supplied and the Component is a root node,
* they are inferred from the size of the Component's element.
*
* @param {Point} [origin] Origin of the space offered to the Component.
* @param {number} [availableWidth] Available width in pixels.
* @param {number} [availableHeight] Available height in pixels.
* @returns {Component} The calling Component.
*/
Component.prototype.computeLayout = function (origin, availableWidth, availableHeight) {
var _this = this;
if (origin == null || availableWidth == null || availableHeight == null) {
if (this._element == null) {
throw new Error("anchor() must be called before computeLayout()");
}
else if (this._isTopLevelComponent) {
// we are the root node, retrieve height/width from root SVG
origin = { x: 0, y: 0 };
// Set width/height to 100% if not specified, to allow accurate size calculation
// see http://www.w3.org/TR/CSS21/visudet.html#block-replaced-width
// and http://www.w3.org/TR/CSS21/visudet.html#inline-replaced-height
if (this._rootSVG.attr("width") == null) {
this._rootSVG.attr("width", "100%");
}
if (this._rootSVG.attr("height") == null) {
this._rootSVG.attr("height", "100%");
}
var elem = this._rootSVG.node();
availableWidth = Plottable.Utils.DOM.elementWidth(elem);
availableHeight = Plottable.Utils.DOM.elementHeight(elem);
}
else {
throw new Error("null arguments cannot be passed to computeLayout() on a non-root node");
}
}
var size = this._sizeFromOffer(availableWidth, availableHeight);
this._width = size.width;
this._height = size.height;
var xAlignProportion = Component._xAlignToProportion[this._xAlignment];
var yAlignProportion = Component._yAlignToProportion[this._yAlignment];
this._origin = {
x: origin.x + (availableWidth - this.width()) * xAlignProportion,
y: origin.y + (availableHeight - this.height()) * yAlignProportion,
};
this._element.attr("transform", "translate(" + this._origin.x + "," + this._origin.y + ")");
this._boxes.forEach(function (b) { return b.attr("width", _this.width()).attr("height", _this.height()); });
if (this._resizeHandler != null) {
this._resizeHandler(size);
}
return this;
};
Component.prototype._sizeFromOffer = function (availableWidth, availableHeight) {
var requestedSpace = this.requestedSpace(availableWidth, availableHeight);
return {
width: this.fixedWidth() ? Math.min(availableWidth, requestedSpace.minWidth) : availableWidth,
height: this.fixedHeight() ? Math.min(availableHeight, requestedSpace.minHeight) : availableHeight,
};
};
/**
* Queues the Component for rendering.
*
* @returns {Component} The calling Component.
*/
Component.prototype.render = function () {
if (this._isAnchored && this._isSetup && this.width() >= 0 && this.height() >= 0) {
Plottable.RenderController.registerToRender(this);
}
return this;
};
Component.prototype._scheduleComputeLayout = function () {
if (this._isAnchored && this._isSetup) {
Plottable.RenderController.registerToComputeLayout(this);
}
};
/**
* Sets a callback that gets called when the component resizes. The size change
* is not guaranteed to be reflected by the DOM at the time the callback is fired.
*
* @param {IResizeHandler} [resizeHandler] Callback to be called when component resizes
*/
Component.prototype.onResize = function (resizeHandler) {
this._resizeHandler = resizeHandler;
return this;
};
/**
* Renders the Component without waiting for the next frame.
*/
Component.prototype.renderImmediately = function () {
if (this._clipPathEnabled) {
this._updateClipPath();
}
return this;
};
/**
* Causes the Component to re-layout and render.
*
* This function should be called when a CSS change has occured that could
* influence the layout of the Component, such as changing the font size.
*
* @returns {Component} The calling Component.
*/
Component.prototype.redraw = function () {
if (this._isAnchored && this._isSetup) {
if (this._isTopLevelComponent) {
this._scheduleComputeLayout();
}
else {
this.parent().redraw();
}
}
return this;
};
/**
* Renders the Component to a given <svg>.
*
* @param {String|d3.Selection} element A selector-string for the <svg>, or a d3 selection containing an <svg>.
* @returns {Component} The calling Component.
*/
Component.prototype.renderTo = function (element) {
this.detach();
if (element != null) {
var selection = void 0;
if (typeof (element) === "string") {
selection = d3.select(element);
}
else if (element instanceof Element) {
selection = d3.select(element);
}
else {
selection = element;
}
if (!selection.node() || selection.node().nodeName.toLowerCase() !== "svg") {
throw new Error("Plottable requires a valid SVG to renderTo");
}
this.anchor(selection);
}
if (this._element == null) {
throw new Error("If a Component has never been rendered before, then renderTo must be given a node to render to, " +
"or a d3.Selection, or a selector string");
}
this.computeLayout();
this.render();
// flush so that consumers can immediately attach to stuff we create in the DOM
Plottable.RenderController.flush();
return this;
};
Component.prototype.xAlignment = function (xAlignment) {
if (xAlignment == null) {
return this._xAlignment;
}
xAlignment = xAlignment.toLowerCase();
if (Component._xAlignToProportion[xAlignment] == null) {
throw new Error("Unsupported alignment: " + xAlignment);
}
this._xAlignment = xAlignment;
this.redraw();
return this;
};
Component.prototype.yAlignment = function (yAlignment) {
if (yAlignment == null) {
return this._yAlignment;
}
yAlignment = yAlignment.toLowerCase();
if (Component._yAlignToProportion[yAlignment] == null) {
throw new Error("Unsupported alignment: " + yAlignment);
}
this._yAlignment = yAlignment;
this.redraw();
return this;
};
Component.prototype._addBox = function (className, parentElement) {
if (this._element == null) {
throw new Error("Adding boxes before anchoring is currently disallowed");
}
parentElement = parentElement == null ? this._boxContainer : parentElement;
var box = parentElement.append("rect");
if (className != null) {
box.classed(className, true);
}
this._boxes.push(box);
if (this.width() != null && this.height() != null) {
box.attr("width", this.width()).attr("height", this.height());
}
return box;
};
Component.prototype._generateClipPath = function () {
// The clip path will prevent content from overflowing its Component space.
this._clipPathID = Plottable.Utils.DOM.generateUniqueClipPathId();
var clipPathParent = this._boxContainer.append("clipPath").attr("id", this._clipPathID);
this._addBox("clip-rect", clipPathParent);
this._updateClipPath();
};
Component.prototype._updateClipPath = function () {
// HACKHACK: IE <= 9 does not respect the HTML base element in SVG.
// They don't need the current URL in the clip path reference.
var prefix = /MSIE [5-9]/.test(navigator.userAgent) ? "" : document.location.href;
prefix = prefix.split("#")[0]; // To fix cases where an anchor tag was used
this._element.attr("clip-path", "url(\"" + prefix + "#" + this._clipPathID + "\")");
};
/**
* Checks if the Component has a given CSS class.
*
* @param {string} cssClass The CSS class to check for.
*/
Component.prototype.hasClass = function (cssClass) {
if (cssClass == null) {
return false;
}
if (this._element == null) {
return this._cssClasses.has(cssClass);
}
else {
return this._element.classed(cssClass);
}
};
/**
* Adds a given CSS class to the Component.
*
* @param {string} cssClass The CSS class to add.
* @returns {Component} The calling Component.
*/
Component.prototype.addClass = function (cssClass) {
if (cssClass == null) {
return this;
}
if (this._element == null) {
this._cssClasses.add(cssClass);
}
else {
this._element.classed(cssClass, true);
}
return this;
};
/**
* Removes a given CSS class from the Component.
*
* @param {string} cssClass The CSS class to remove.
* @returns {Component} The calling Component.
*/
Component.prototype.removeClass = function (cssClass) {
if (cssClass == null) {
return this;
}
if (this._element == null) {
this._cssClasses.delete(cssClass);
}
else {
this._element.classed(cssClass, false);
}
return this;
};
/**
* Checks if the Component has a fixed width or if it grows to fill available space.
* Returns false by default on the base Component class.
*/
Component.prototype.fixedWidth = function () {
return false;
};
/**
* Checks if the Component has a fixed height or if it grows to fill available space.
* Returns false by default on the base Component class.
*/
Component.prototype.fixedHeight = function () {
return false;
};
/**
* Detaches a Component from the DOM. The Component can be reused.
*
* This should only be used if you plan on reusing the calling Component. Otherwise, use destroy().
*
* @returns The calling Component.
*/
Component.prototype.detach = function () {
this.parent(null);
if (this._isAnchored) {
this._element.remove();
if (this._isTopLevelComponent) {
this._rootSVG.select("." + Component._SAFARI_EVENT_BACKING_CLASS).remove();
}
}
this._isAnchored = false;
this._onDetachCallbacks.callCallbacks(this);
return this;
};
/**
* Adds a callback to be called when the Component is detach()-ed.
*
* @param {ComponentCallback} callback
* @return {Component} The calling Component.
*/
Component.prototype.onDetach = function (callback) {
this._onDetachCallbacks.add(callback);
return this;
};
/**
* Removes a callback to be called when the Component is detach()-ed.
* The callback is identified by reference equality.
*
* @param {ComponentCallback} callback
* @return {Component} The calling Component.
*/
Component.prototype.offDetach = function (callback) {
this._onDetachCallbacks.delete(callback);
return this;
};
Component.prototype.parent = function (parent) {
if (parent === undefined) {
return this._parent;
}
if (parent !== null && !parent.has(this)) {
throw new Error("Passed invalid parent");
}
this._parent = parent;
return this;
};
/**
* @returns {Bounds} for the component in pixel space, where the topLeft
* represents the component's minimum x and y values and the bottomRight represents
* the component's maximum x and y values.
*/
Component.prototype.bounds = function () {
var topLeft = this.origin();
return {
topLeft: topLeft,
bottomRight: {
x: topLeft.x + this.width(),
y: topLeft.y + this.height()
},
};
};
/**
* Removes a Component from the DOM and disconnects all listeners.
*/
Component.prototype.destroy = function () {
this._destroyed = true;
this.detach();
};
/**
* Gets the width of the Component in pixels.
*/
Component.prototype.width = function () {
return this._width;
};
/**
* Gets the height of the Component in pixels.
*/
Component.prototype.height = function () {
return this._height;
};
/**
* Gets the origin of the Component relative to its parent.
*
* @return {Point}
*/
Component.prototype.origin = function () {
return {
x: this._origin.x,
y: this._origin.y,
};
};
/**
* Gets the origin of the Component relative to the root <svg>.
*
* @return {Point}
*/
Component.prototype.originToSVG = function () {
var origin = this.origin();
var ancestor = this.parent();
while (ancestor != null) {
var ancestorOrigin = ancestor.origin();
origin.x += ancestorOrigin.x;
origin.y += ancestorOrigin.y;
ancestor = ancestor.parent();
}
return origin;
};
/**
* Gets the Selection containing the <g> in front of the visual elements of the Component.
*
* Will return undefined if the Component has not been anchored.
*
* @return {d3.Selection}
*/
Component.prototype.foreground = function () {
return this._foregroundContainer;
};
/**
* Gets a Selection containing a <g> that holds the visual elements of the Component.
*
* Will return undefined if the Component has not been anchored.
*
* @return {d3.Selection} content selection for the Component
*/
Component.prototype.content = function () {
return this._content;
};
/**
* Gets the Selection containing the <g> behind the visual elements of the Component.
*
* Will return undefined if the Component has not been anchored.
*
* @return {d3.Selection} background selection for the Component
*/
Component.prototype.background = function () {
return this._backgroundContainer;
};
Component._xAlignToProportion = {
"left": 0,
"center": 0.5,
"right": 1,
};
Component._yAlignToProportion = {
"top": 0,
"center": 0.5,
"bottom": 1,
};
Component._SAFARI_EVENT_BACKING_CLASS = "safari-event-backing";
return Component;
}());
Plottable.Component = Component;
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
/*
* ComponentContainer class encapsulates Table and ComponentGroup's shared functionality.
* It will not do anything if instantiated directly.
*/
var ComponentContainer = (function (_super) {
__extends(ComponentContainer, _super);
function ComponentContainer() {
var _this = this;
_super.call(this);
this._detachCallback = function (component) { return _this.remove(component); };
}
ComponentContainer.prototype.anchor = function (selection) {
var _this = this;
_super.prototype.anchor.call(this, selection);
this._forEach(function (c) { return c.anchor(_this.content()); });
return this;
};
ComponentContainer.prototype.render = function () {
this._forEach(function (c) { return c.render(); });
return this;
};
/**
* Checks whether the specified Component is in the ComponentContainer.
*/
ComponentContainer.prototype.has = function (component) {
throw new Error("has() is not implemented on ComponentContainer");
};
ComponentContainer.prototype._adoptAndAnchor = function (component) {
component.parent(this);
component.onDetach(this._detachCallback);
if (this._isAnchored) {
component.anchor(this.content());
}
};
/**
* Removes the specified Component from the ComponentContainer.
*/
ComponentContainer.prototype.remove = function (component) {
if (this.has(component)) {
component.offDetach(this._detachCallback);
this._remove(component);
component.detach();
this.redraw();
}
return this;
};
/**
* Carry out the actual removal of a Component.
* Implementation dependent on the type of container.
*
* @return {boolean} true if the Component was successfully removed, false otherwise.
*/
ComponentContainer.prototype._remove = function (component) {
return false;
};
/**
* Invokes a callback on each Component in the ComponentContainer.
*/
ComponentContainer.prototype._forEach = function (callback) {
throw new Error("_forEach() is not implemented on ComponentContainer");
};
/**
* Destroys the ComponentContainer and all Components within it.
*/
ComponentContainer.prototype.destroy = function () {
_super.prototype.destroy.call(this);
this._forEach(function (c) { return c.destroy(); });
};
return ComponentContainer;
}(Plottable.Component));
Plottable.ComponentContainer = ComponentContainer;
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Components;
(function (Components) {
var Group = (function (_super) {
__extends(Group, _super);
/**
* Constructs a Group.
*
* A Group contains Components that will be rendered on top of each other.
* Components added later will be rendered above Components already in the Group.
*
* @constructor
* @param {Component[]} [components=[]] Components to be added to the Group.
*/
function Group(components) {
var _this = this;
if (components === void 0) { components = []; }
_super.call(this);
this._components = [];
this.addClass("component-group");
components.forEach(function (c) { return _this.append(c); });
}
Group.prototype._forEach = function (callback) {
this.components().forEach(callback);
};
/**
* Checks whether the specified Component is in the Group.
*/
Group.prototype.has = function (component) {
return this._components.indexOf(component) >= 0;
};
Group.prototype.requestedSpace = function (offeredWidth, offeredHeight) {
var requests = this._components.map(function (c) { return c.requestedSpace(offeredWidth, offeredHeight); });
return {
minWidth: Plottable.Utils.Math.max(requests, function (request) { return request.minWidth; }, 0),
minHeight: Plottable.Utils.Math.max(requests, function (request) { return request.minHeight; }, 0),
};
};
Group.prototype.computeLayout = function (origin, availableWidth, availableHeight) {
var _this = this;
_super.prototype.computeLayout.call(this, origin, availableWidth, availableHeight);
this._forEach(function (component) {
component.computeLayout({ x: 0, y: 0 }, _this.width(), _this.height());
});
return this;
};
Group.prototype._sizeFromOffer = function (availableWidth, availableHeight) {
return {
width: availableWidth,
height: availableHeight,
};
};
Group.prototype.fixedWidth = function () {
return this._components.every(function (c) { return c.fixedWidth(); });
};
Group.prototype.fixedHeight = function () {
return this._components.every(function (c) { return c.fixedHeight(); });
};
/**
* @return {Component[]} The Components in this Group.
*/
Group.prototype.components = function () {
return this._components.slice();
};
/**
* Adds a Component to this Group.
* The added Component will be rendered above Components already in the Group.
*/
Group.prototype.append = function (component) {
if (component != null && !this.has(component)) {
component.detach();
this._components.push(component);
this._adoptAndAnchor(component);
this.redraw();
}
return this;
};
Group.prototype._remove = function (component) {
var removeIndex = this._components.indexOf(component);
if (removeIndex >= 0) {
this._components.splice(removeIndex, 1);
return true;
}
return false;
};
return Group;
}(Plottable.ComponentContainer));
Components.Group = Group;
})(Components = Plottable.Components || (Plottable.Components = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Components;
(function (Components) {
var PlotGroup = (function (_super) {
__extends(PlotGroup, _super);
function PlotGroup() {
_super.apply(this, arguments);
}
PlotGroup.prototype.entityNearest = function (point) {
var closestPlotEntity;
var minDistSquared = Infinity;
this.components().forEach(function (plot) {
var candidatePlotEntity = plot.entityNearest(point);
if (candidatePlotEntity == null) {
return;
}
var distSquared = Plottable.Utils.Math.distanceSquared(candidatePlotEntity.position, point);
if (distSquared <= minDistSquared) {
minDistSquared = distSquared;
closestPlotEntity = candidatePlotEntity;
}
});
return closestPlotEntity;
};
/**
* Adds a Plot to this Plot Group.
* The added Plot will be rendered above Plots already in the Group.
*/
PlotGroup.prototype.append = function (plot) {
if (plot != null && !(plot instanceof Plottable.Plot)) {
throw new Error("Plot Group only accepts plots");
}
_super.prototype.append.call(this, plot);
return this;
};
return PlotGroup;
}(Components.Group));
Components.PlotGroup = PlotGroup;
})(Components = Plottable.Components || (Plottable.Components = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Axis = (function (_super) {
__extends(Axis, _super);
/**
* Constructs an Axis.
* An Axis is a visual representation of a Scale.
*
* @constructor
* @param {Scale} scale
* @param {AxisOrientation} orientation Orientation of this Axis.
*/
function Axis(scale, orientation) {
var _this = this;
_super.call(this);
this._endTickLength = 5;
this._innerTickLength = 5;
this._tickLabelPadding = 10;
this._margin = 15;
this._showEndTickLabels = false;
this._annotationsEnabled = false;
this._annotationTierCount = 1;
if (scale == null || orientation == null) {
throw new Error("Axis requires a scale and orientation");
}
this._scale = scale;
this.orientation(orientation);
this._setDefaultAlignment();
this.addClass("axis");
if (this._isHorizontal()) {
this.addClass("x-axis");
}
else {
this.addClass("y-axis");
}
this.formatter(Plottable.Formatters.identity());
this._rescaleCallback = function (newScale) { return _this._rescale(); };
this._scale.onUpdate(this._rescaleCallback);
this._annotatedTicks = [];
this._annotationFormatter = Plottable.Formatters.identity();
}
Axis.prototype.destroy = function () {
_super.prototype.destroy.call(this);
this._scale.offUpdate(this._rescaleCallback);
};
Axis.prototype._isHorizontal = function () {
return this._orientation === "top" || this._orientation === "bottom";
};
Axis.prototype._computeWidth = function () {
// to be overridden by subclass logic
return this._maxLabelTickLength();
};
Axis.prototype._computeHeight = function () {
// to be overridden by subclass logic
return this._maxLabelTickLength();
};
Axis.prototype.requestedSpace = function (offeredWidth, offeredHeight) {
var requestedWidth = 0;
var requestedHeight = 0;
if (this._isHorizontal()) {
requestedHeight = this._computeHeight() + this._margin;
if (this.annotationsEnabled()) {
var tierHeight = this._annotationMeasurer.measure().height + 2 * Axis._ANNOTATION_LABEL_PADDING;
requestedHeight += tierHeight * this.annotationTierCount();
}
}
else {
requestedWidth = this._computeWidth() + this._margin;
if (this.annotationsEnabled()) {
var tierHeight = this._annotationMeasurer.measure().height + 2 * Axis._ANNOTATION_LABEL_PADDING;
requestedWidth += tierHeight * this.annotationTierCount();
}
}
return {
minWidth: requestedWidth,
minHeight: requestedHeight,
};
};
Axis.prototype.fixedHeight = function () {
return this._isHorizontal();
};
Axis.prototype.fixedWidth = function () {
return !this._isHorizontal();
};
Axis.prototype._rescale = function () {
// default implementation; subclasses may call redraw() here
this.render();
};
Axis.prototype.computeLayout = function (origin, availableWidth, availableHeight) {
_super.prototype.computeLayout.call(this, origin, availableWidth, availableHeight);
if (this._isHorizontal()) {
this._scale.range([0, this.width()]);
}
else {
this._scale.range([this.height(), 0]);
}
return this;
};
Axis.prototype._setup = function () {
_super.prototype._setup.call(this);
this._tickMarkContainer = this.content().append("g")
.classed(Axis.TICK_MARK_CLASS + "-container", true);
this._tickLabelContainer = this.content().append("g")
.classed(Axis.TICK_LABEL_CLASS + "-container", true);
this._baseline = this.content().append("line").classed("baseline", true);
this._annotationContainer = this.content().append("g")
.classed("annotation-container", true);
this._annotationContainer.append("g").classed("annotation-line-container", true);
this._annotationContainer.append("g").classed("annotation-circle-container", true);
this._annotationContainer.append("g").classed("annotation-rect-container", true);
var annotationLabelContainer = this._annotationContainer.append("g").classed("annotation-label-container", true);
this._annotationMeasurer = new SVGTypewriter.CacheMeasurer(annotationLabelContainer);
this._annotationWriter = new SVGTypewriter.Writer(this._annotationMeasurer);
};
/*
* Function for generating tick values in data-space (as opposed to pixel values).
* To be implemented by subclasses.
*/
Axis.prototype._getTickValues = function () {
return [];
};
/**
* Render tick marks, baseline, and annotations. Should be super called by subclasses and then overridden to draw
* other relevant aspects of this Axis.
*/
Axis.prototype.renderImmediately = function () {
var tickMarkValues = this._getTickValues();
var tickMarks = this._tickMarkContainer.selectAll("." + Axis.TICK_MARK_CLASS).data(tickMarkValues);
tickMarks.enter().append("line").classed(Axis.TICK_MARK_CLASS, true);
tickMarks.attr(this._generateTickMarkAttrHash());
d3.select(tickMarks[0][0]).classed(Axis.END_TICK_MARK_CLASS, true)
.attr(this._generateTickMarkAttrHash(true));
d3.select(tickMarks[0][tickMarkValues.length - 1]).classed(Axis.END_TICK_MARK_CLASS, true)
.attr(this._generateTickMarkAttrHash(true));
tickMarks.exit().remove();
this._baseline.attr(this._generateBaselineAttrHash());
if (this.annotationsEnabled()) {
this._drawAnnotations();
}
else {
this._removeAnnotations();
}
return this;
};
Axis.prototype.annotatedTicks = function (annotatedTicks) {
if (annotatedTicks == null) {
return this._annotatedTicks;
}
this._annotatedTicks = annotatedTicks;
this.render();
return this;
};
Axis.prototype.annotationFormatter = function (annotationFormatter) {
if (annotationFormatter == null) {
return this._annotationFormatter;
}
this._annotationFormatter = annotationFormatter;
this.render();
return this;
};
Axis.prototype.annotationsEnabled = function (annotationsEnabled) {
if (annotationsEnabled == null) {
return this._annotationsEnabled;
}
this._annotationsEnabled = annotationsEnabled;
this.redraw();
return this;
};
Axis.prototype.annotationTierCount = function (annotationTierCount) {
if (annotationTierCount == null) {
return this._annotationTierCount;
}
if (annotationTierCount < 0) {
throw new Error("annotationTierCount cannot be negative");
}
this._annotationTierCount = annotationTierCount;
this.redraw();
return this;
};
Axis.prototype._drawAnnotations = function () {
var _this = this;
var labelPadding = Axis._ANNOTATION_LABEL_PADDING;
var measurements = new Plottable.Utils.Map();
var annotatedTicks = this._annotatedTicksToRender();
annotatedTicks.forEach(function (annotatedTick) {
var measurement = _this._annotationMeasurer.measure(_this.annotationFormatter()(annotatedTick));
var paddedMeasurement = { width: measurement.width + 2 * labelPadding, height: measurement.height + 2 * labelPadding };
measurements.set(annotatedTick, paddedMeasurement);
});
var tierHeight = this._annotationMeasurer.measure().height + 2 * labelPadding;
var annotationToTier = this._annotationToTier(measurements);
var hiddenAnnotations = new Plottable.Utils.Set();
var axisHeight = this._isHorizontal() ? this.height() : this.width();
var axisHeightWithoutMarginAndAnnotations = this._coreSize();
var numTiers = Math.min(this.annotationTierCount(), Math.floor((axisHeight - axisHeightWithoutMarginAndAnnotations) / tierHeight));
annotationToTier.forEach(function (tier, annotation) {
if (tier === -1 || tier >= numTiers) {
hiddenAnnotations.add(annotation);
}
});
var bindElements = function (selection, elementName, className) {
var elements = selection.selectAll("." + className).data(annotatedTicks);
elements.enter().append(elementName).classed(className, true);
elements.exit().remove();
return elements;
};
var offsetF = function (d) {
switch (_this.orientation()) {
case "bottom":
case "right":
return annotationToTier.get(d) * tierHeight + axisHeightWithoutMarginAndAnnotations;
case "top":
case "left":
return axisHeight - axisHeightWithoutMarginAndAnnotations - annotationToTier.get(d) * tierHeight;
}
};
var positionF = function (d) { return _this._scale.scale(d); };
var visibilityF = function (d) { return hiddenAnnotations.has(d) ? "hidden" : "visible"; };
var secondaryPosition;
switch (this.orientation()) {
case "bottom":
case "right":
secondaryPosition = 0;
break;
case "top":
secondaryPosition = this.height();
break;
case "left":
secondaryPosition = this.width();
break;
}
var isHorizontal = this._isHorizontal();
bindElements(this._annotationContainer.select(".annotation-line-container"), "line", Axis.ANNOTATION_LINE_CLASS)
.attr({
x1: isHorizontal ? positionF : secondaryPosition,
x2: isHorizontal ? positionF : offsetF,
y1: isHorizontal ? secondaryPosition : positionF,
y2: isHorizontal ? offsetF : positionF,
visibility: visibilityF,
});
bindElements(this._annotationContainer.select(".annotation-circle-container"), "circle", Axis.ANNOTATION_CIRCLE_CLASS)
.attr({
cx: isHorizontal ? positionF : secondaryPosition,
cy: isHorizontal ? secondaryPosition : positionF,
r: 3,
});
var rectangleOffsetF = function (d) {
switch (_this.orientation()) {
case "bottom":
case "right":
return offsetF(d);
case "top":
case "left":
return offsetF(d) - measurements.get(d).height;
}
};
bindElements(this._annotationContainer.select(".annotation-rect-container"), "rect", Axis.ANNOTATION_RECT_CLASS)
.attr({
x: isHorizontal ? positionF : rectangleOffsetF,
y: isHorizontal ? rectangleOffsetF : positionF,
width: isHorizontal ? function (d) { return measurements.get(d).width; } : function (d) { return measurements.get(d).height; },
height: isHorizontal ? function (d) { return measurements.get(d).height; } : function (d) { return measurements.get(d).width; },
visibility: visibilityF,
});
var annotationWriter = this._annotationWriter;
var annotationFormatter = this.annotationFormatter();
var annotationLabels = bindElements(this._annotationContainer.select(".annotation-label-container"), "g", Axis.ANNOTATION_LABEL_CLASS);
annotationLabels.selectAll(".text-container").remove();
annotationLabels.attr({
transform: function (d) {
var xTranslate = isHorizontal ? positionF(d) : rectangleOffsetF(d);
var yTranslate = isHorizontal ? rectangleOffsetF(d) : positionF(d);
return "translate(" + xTranslate + "," + yTranslate + ")";
},
visibility: visibilityF,
})
.each(function (annotationLabel) {
var writeOptions = {
selection: d3.select(this),
xAlign: "center",
yAlign: "center",
textRotation: isHorizontal ? 0 : 90,
};
annotationWriter.write(annotationFormatter(annotationLabel), isHorizontal ? measurements.get(annotationLabel).width : measurements.get(annotationLabel).height, isHorizontal ? measurements.get(annotationLabel).height : measurements.get(annotationLabel).width, writeOptions);
});
};
Axis.prototype._annotatedTicksToRender = function () {
var _this = this;
var scaleRange = this._scale.range();
return Plottable.Utils.Array.uniq(this.annotatedTicks().filter(function (tick) {
if (tick == null) {
return false;
}
return Plottable.Utils.Math.inRange(_this._scale.scale(tick), scaleRange[0], scaleRange[1]);
}));
};
/**
* Retrieves the size of the core pieces.
*
* The core pieces include the labels, the end tick marks, the inner tick marks, and the tick label padding.
*/
Axis.prototype._coreSize = function () {
var relevantDimension = this._isHorizontal() ? this.height() : this.width();
var axisHeightWithoutMargin = this._isHorizontal() ? this._computeHeight() : this._computeWidth();
return Math.min(axisHeightWithoutMargin, relevantDimension);
};
Axis.prototype._annotationTierHeight = function () {
return this._annotationMeasurer.measure().height + 2 * Axis._ANNOTATION_LABEL_PADDING;
};
Axis.prototype._annotationToTier = function (measurements) {
var _this = this;
var annotationTiers = [[]];
var annotationToTier = new Plottable.Utils.Map();
var dimension = this._isHorizontal() ? this.width() : this.height();
this._annotatedTicksToRender().forEach(function (annotatedTick) {
var position = _this._scale.scale(annotatedTick);
var length = measurements.get(annotatedTick).width;
if (position < 0 || position + length > dimension) {
annotationToTier.set(annotatedTick, -1);
return;
}
var tierHasCollision = function (testTier) { return annotationTiers[testTier].some(function (testTick) {
var testPosition = _this._scale.scale(testTick);
var testLength = measurements.get(testTick).width;
return position + length >= testPosition && position <= testPosition + testLength;
}); };
var tier = 0;
while (tierHasCollision(tier)) {
tier++;
if (annotationTiers.length === tier) {
annotationTiers.push([]);
}
}
annotationTiers[tier].push(annotatedTick);
annotationToTier.set(annotatedTick, tier);
});
return annotationToTier;
};
Axis.prototype._removeAnnotations = function () {
this._annotationContainer.selectAll(".annotation-line").remove();
this._annotationContainer.selectAll(".annotation-circle").remove();
this._annotationContainer.selectAll(".annotation-rect").remove();
this._annotationContainer.selectAll(".annotation-label").remove();
};
Axis.prototype._generateBaselineAttrHash = function () {
var baselineAttrHash = {
x1: 0,
y1: 0,
x2: 0,
y2: 0,
};
switch (this._orientation) {
case "bottom":
baselineAttrHash["x2"] = this.width();
break;
case "top":
baselineAttrHash["x2"] = this.width();
baselineAttrHash["y1"] = this.height();
baselineAttrHash["y2"] = this.height();
break;
case "left":
baselineAttrHash["x1"] = this.width();
baselineAttrHash["x2"] = this.width();
baselineAttrHash["y2"] = this.height();
break;
case "right":
baselineAttrHash["y2"] = this.height();
break;
}
return baselineAttrHash;
};
Axis.prototype._generateTickMarkAttrHash = function (isEndTickMark) {
var _this = this;
if (isEndTickMark === void 0) { isEndTickMark = false; }
var tickMarkAttrHash = {
x1: 0,
y1: 0,
x2: 0,
y2: 0,
};
var scalingFunction = function (d) { return _this._scale.scale(d); };
if (this._isHorizontal()) {
tickMarkAttrHash["x1"] = scalingFunction;
tickMarkAttrHash["x2"] = scalingFunction;
}
else {
tickMarkAttrHash["y1"] = scalingFunction;
tickMarkAttrHash["y2"] = scalingFunction;
}
var tickLength = isEndTickMark ? this._endTickLength : this._innerTickLength;
switch (this._orientation) {
case "bottom":
tickMarkAttrHash["y2"] = tickLength;
break;
case "top":
tickMarkAttrHash["y1"] = this.height();
tickMarkAttrHash["y2"] = this.height() - tickLength;
break;
case "left":
tickMarkAttrHash["x1"] = this.width();
tickMarkAttrHash["x2"] = this.width() - tickLength;
break;
case "right":
tickMarkAttrHash["x2"] = tickLength;
break;
}
return tickMarkAttrHash;
};
Axis.prototype._setDefaultAlignment = function () {
switch (this._orientation) {
case "bottom":
this.yAlignment("top");
break;
case "top":
this.yAlignment("bottom");
break;
case "left":
this.xAlignment("right");
break;
case "right":
this.xAlignment("left");
break;
}
};
Axis.prototype.formatter = function (formatter) {
if (formatter == null) {
return this._formatter;
}
this._formatter = formatter;
this.redraw();
return this;
};
Axis.prototype.innerTickLength = function (length) {
if (length == null) {
return this._innerTickLength;
}
else {
if (length < 0) {
throw new Error("inner tick length must be positive");
}
this._innerTickLength = length;
this.redraw();
return this;
}
};
Axis.prototype.endTickLength = function (length) {
if (length == null) {
return this._endTickLength;
}
else {
if (length < 0) {
throw new Error("end tick length must be positive");
}
this._endTickLength = length;
this.redraw();
return this;
}
};
/**
* Gets the maximum pixel length over all ticks on this axis.
* @returns {number}
*/
Axis.prototype._maxLabelTickLength = function () {
if (this.showEndTickLabels()) {
return Math.max(this.innerTickLength(), this.endTickLength());
}
else {
return this.innerTickLength();
}
};
Axis.prototype.tickLabelPadding = function (padding) {
if (padding == null) {
return this._tickLabelPadding;
}
else {
if (padding < 0) {
throw new Error("tick label padding must be positive");
}
this._tickLabelPadding = padding;
this.redraw();
return this;
}
};
Axis.prototype.margin = function (size) {
if (size == null) {
return this._margin;
}
else {
if (size < 0) {
throw new Error("margin size must be positive");
}
this._margin = size;
this.redraw();
return this;
}
};
Axis.prototype.orientation = function (orientation) {
if (orientation == null) {
return this._orientation;
}
else {
// ensure backwards compatibility for older versions that supply orientation in different cases
var newOrientationLC = orientation.toLowerCase();
if (newOrientationLC !== "top" &&
newOrientationLC !== "bottom" &&
newOrientationLC !== "left" &&
newOrientationLC !== "right") {
throw new Error("unsupported orientation");
}
this._orientation = newOrientationLC;
this.redraw();
return this;
}
};
Axis.prototype.showEndTickLabels = function (show) {
if (show == null) {
return this._showEndTickLabels;
}
this._showEndTickLabels = show;
this.render();
return this;
};
Axis.prototype._showAllTickMarks = function () {
this._tickMarkContainer.selectAll("." + Axis.TICK_MARK_CLASS)
.each(function () {
d3.select(this).style("visibility", "inherit");
});
};
Axis.prototype._showAllTickLabels = function () {
this._tickLabelContainer.selectAll("." + Axis.TICK_LABEL_CLASS)
.each(function () {
d3.select(this).style("visibility", "inherit");
});
};
/**
* Responsible for hiding any tick labels that break out of the bounding
* container.
*/
Axis.prototype._hideOverflowingTickLabels = function () {
var boundingBox = this._boundingBox.node().getBoundingClientRect();
var tickLabels = this._tickLabelContainer.selectAll("." + Axis.TICK_LABEL_CLASS);
if (tickLabels.empty()) {
return;
}
tickLabels.each(function (d, i) {
if (!Plottable.Utils.DOM.clientRectInside(this.getBoundingClientRect(), boundingBox)) {
d3.select(this).style("visibility", "hidden");
}
});
};
/**
* Hides the Tick Marks which have no corresponding Tick Labels
*/
Axis.prototype._hideTickMarksWithoutLabel = function () {
var visibleTickMarks = this._tickMarkContainer.selectAll("." + Axis.TICK_MARK_CLASS);
var visibleTickLabels = this._tickLabelContainer
.selectAll("." + Axis.TICK_LABEL_CLASS)
.filter(function (d, i) {
var visibility = d3.select(this).style("visibility");
return (visibility === "inherit") || (visibility === "visible");
});
var labelNumbersShown = [];
visibleTickLabels.each(function (labelNumber) { return labelNumbersShown.push(labelNumber); });
visibleTickMarks.each(function (e, i) {
if (labelNumbersShown.indexOf(e) === -1) {
d3.select(this).style("visibility", "hidden");
}
});
};
/**
* The css class applied to each end tick mark (the line on the end tick).
*/
Axis.END_TICK_MARK_CLASS = "end-tick-mark";
/**
* The css class applied to each tick mark (the line on the tick).
*/
Axis.TICK_MARK_CLASS = "tick-mark";
/**
* The css class applied to each tick label (the text associated with the tick).
*/
Axis.TICK_LABEL_CLASS = "tick-label";
/**
* The css class applied to each annotation line, which extends from the axis to the rect.
*/
Axis.ANNOTATION_LINE_CLASS = "annotation-line";
/**
* The css class applied to each annotation rect, which surrounds the annotation label.
*/
Axis.ANNOTATION_RECT_CLASS = "annotation-rect";
/**
* The css class applied to each annotation circle, which denotes which tick is being annotated.
*/
Axis.ANNOTATION_CIRCLE_CLASS = "annotation-circle";
/**
* The css class applied to each annotation label, which shows the formatted annotation text.
*/
Axis.ANNOTATION_LABEL_CLASS = "annotation-label";
Axis._ANNOTATION_LABEL_PADDING = 4;
return Axis;
}(Plottable.Component));
Plottable.Axis = Axis;
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var TimeInterval;
(function (TimeInterval) {
TimeInterval.second = "second";
TimeInterval.minute = "minute";
TimeInterval.hour = "hour";
TimeInterval.day = "day";
TimeInterval.week = "week";
TimeInterval.month = "month";
TimeInterval.year = "year";
})(TimeInterval = Plottable.TimeInterval || (Plottable.TimeInterval = {}));
;
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Axes;
(function (Axes) {
var Time = (function (_super) {
__extends(Time, _super);
/**
* Constructs a Time Axis.
*
* A Time Axis is a visual representation of a Time Scale.
*
* @constructor
* @param {Scales.Time} scale
* @param {AxisOrientation} orientation Orientation of this Time Axis. Time Axes can only have "top" or "bottom"
* orientations.
*/
function Time(scale, orientation) {
_super.call(this, scale, orientation);
this._tierLabelPositions = [];
this.addClass("time-axis");
this.tickLabelPadding(5);
this.axisConfigurations(Time._DEFAULT_TIME_AXIS_CONFIGURATIONS);
this.annotationFormatter(Plottable.Formatters.time("%a %b %d, %Y"));
}
Time.prototype.tierLabelPositions = function (newPositions) {
if (newPositions == null) {
return this._tierLabelPositions;
}
else {
if (!newPositions.every(function (pos) { return pos.toLowerCase() === "between" || pos.toLowerCase() === "center"; })) {
throw new Error("Unsupported position for tier labels");
}
this._tierLabelPositions = newPositions;
this.redraw();
return this;
}
};
Time.prototype.axisConfigurations = function (configurations) {
if (configurations == null) {
return this._possibleTimeAxisConfigurations;
}
this._possibleTimeAxisConfigurations = configurations;
this._numTiers = Plottable.Utils.Math.max(this._possibleTimeAxisConfigurations.map(function (config) { return config.length; }), 0);
if (this._isAnchored) {
this._setupDomElements();
}
var oldLabelPositions = this.tierLabelPositions();
var newLabelPositions = [];
for (var i = 0; i < this._numTiers; i++) {
newLabelPositions.push(oldLabelPositions[i] || "between");
}
this.tierLabelPositions(newLabelPositions);
this.redraw();
return this;
};
/**
* Gets the index of the most precise TimeAxisConfiguration that will fit in the current width.
*/
Time.prototype._getMostPreciseConfigurationIndex = function () {
var _this = this;
var mostPreciseIndex = this._possibleTimeAxisConfigurations.length;
this._possibleTimeAxisConfigurations.forEach(function (interval, index) {
if (index < mostPreciseIndex && interval.every(function (tier) {
return _this._checkTimeAxisTierConfigurationWidth(tier);
})) {
mostPreciseIndex = index;
}
});
if (mostPreciseIndex === this._possibleTimeAxisConfigurations.length) {
Plottable.Utils.Window.warn("zoomed out too far: could not find suitable interval to display labels");
--mostPreciseIndex;
}
return mostPreciseIndex;
};
Time.prototype.orientation = function (orientation) {
if (orientation && (orientation.toLowerCase() === "right" || orientation.toLowerCase() === "left")) {
throw new Error(orientation + " is not a supported orientation for TimeAxis - only horizontal orientations are supported");
}
return _super.prototype.orientation.call(this, orientation); // maintains getter-setter functionality
};
Time.prototype._computeHeight = function () {
var textHeight = this._measurer.measure().height;
this._tierHeights = [];
for (var i = 0; i < this._numTiers; i++) {
this._tierHeights.push(textHeight + this.tickLabelPadding() +
((this._tierLabelPositions[i]) === "between" ? 0 : this._maxLabelTickLength()));
}
return d3.sum(this._tierHeights);
};
Time.prototype._getIntervalLength = function (config) {
var startDate = this._scale.domain()[0];
var d3Interval = Plottable.Scales.Time.timeIntervalToD3Time(config.interval);
var endDate = d3Interval.offset(startDate, config.step);
if (endDate > this._scale.domain()[1]) {
// this offset is too large, so just return available width
return this.width();
}
// measure how much space one date can get
var stepLength = Math.abs(this._scale.scale(endDate) - this._scale.scale(startDate));
return stepLength;
};
Time.prototype._maxWidthForInterval = function (config) {
return this._measurer.measure(config.formatter(Time._LONG_DATE)).width;
};
/**
* Check if tier configuration fits in the current width.
*/
Time.prototype._checkTimeAxisTierConfigurationWidth = function (config) {
var worstWidth = this._maxWidthForInterval(config) + 2 * this.tickLabelPadding();
return Math.min(this._getIntervalLength(config), this.width()) >= worstWidth;
};
Time.prototype._sizeFromOffer = function (availableWidth, availableHeight) {
// Makes sure that the size it requires is a multiple of tier sizes, such that
// we have no leftover tiers
var size = _super.prototype._sizeFromOffer.call(this, availableWidth, availableHeight);
var tierHeights = this._tierHeights.reduce(function (prevValue, currValue, index, arr) {
return (prevValue + currValue > size.height) ? prevValue : (prevValue + currValue);
});
var nonCoreHeight = this.margin() + (this.annotationsEnabled() ? this.annotationTierCount() * this._annotationTierHeight() : 0);
size.height = Math.min(size.height, tierHeights + nonCoreHeight);
return size;
};
Time.prototype._setup = function () {
_super.prototype._setup.call(this);
this._setupDomElements();
};
Time.prototype._setupDomElements = function () {
this.content().selectAll("." + Time.TIME_AXIS_TIER_CLASS).remove();
this._tierLabelContainers = [];
this._tierMarkContainers = [];
this._tierBaselines = [];
this._tickLabelContainer.remove();
this._baseline.remove();
for (var i = 0; i < this._numTiers; ++i) {
var tierContainer = this.content().append("g").classed(Time.TIME_AXIS_TIER_CLASS, true);
this._tierLabelContainers.push(tierContainer.append("g").classed(Plottable.Axis.TICK_LABEL_CLASS + "-container", true));
this._tierMarkContainers.push(tierContainer.append("g").classed(Plottable.Axis.TICK_MARK_CLASS + "-container", true));
this._tierBaselines.push(tierContainer.append("line").classed("baseline", true));
}
this._measurer = new SVGTypewriter.CacheMeasurer(this._tierLabelContainers[0]);
};
Time.prototype._getTickIntervalValues = function (config) {
return this._scale.tickInterval(config.interval, config.step);
};
Time.prototype._getTickValues = function () {
var _this = this;
return this._possibleTimeAxisConfigurations[this._mostPreciseConfigIndex].reduce(function (ticks, config) { return ticks.concat(_this._getTickIntervalValues(config)); }, []);
};
Time.prototype._cleanTiers = function () {
for (var index = 0; index < this._tierLabelContainers.length; index++) {
this._tierLabelContainers[index].selectAll("." + Plottable.Axis.TICK_LABEL_CLASS).remove();
this._tierMarkContainers[index].selectAll("." + Plottable.Axis.TICK_MARK_CLASS).remove();
this._tierBaselines[index].style("visibility", "hidden");
}
};
Time.prototype._getTickValuesForConfiguration = function (config) {
var tickPos = this._scale.tickInterval(config.interval, config.step);
var domain = this._scale.domain();
var tickPosValues = tickPos.map(function (d) { return d.valueOf(); }); // can't indexOf with objects
if (tickPosValues.indexOf(domain[0].valueOf()) === -1) {
tickPos.unshift(domain[0]);
}
if (tickPosValues.indexOf(domain[1].valueOf()) === -1) {
tickPos.push(domain[1]);
}
return tickPos;
};
Time.prototype._renderTierLabels = function (container, config, index) {
var _this = this;
var tickPos = this._getTickValuesForConfiguration(config);
var labelPos = [];
if (this._tierLabelPositions[index] === "between" && config.step === 1) {
tickPos.map(function (datum, i) {
if (i + 1 >= tickPos.length) {
return;
}
labelPos.push(new Date((tickPos[i + 1].valueOf() - tickPos[i].valueOf()) / 2 + tickPos[i].valueOf()));
});
}
else {
labelPos = tickPos;
}
var tickLabels = container.selectAll("." + Plottable.Axis.TICK_LABEL_CLASS).data(labelPos, function (d) { return String(d.valueOf()); });
var tickLabelsEnter = tickLabels.enter().append("g").classed(Plottable.Axis.TICK_LABEL_CLASS, true);
tickLabelsEnter.append("text");
var xTranslate = (this._tierLabelPositions[index] === "center" || config.step === 1) ? 0 : this.tickLabelPadding();
var yTranslate;
if (this.orientation() === "bottom") {
yTranslate = d3.sum(this._tierHeights.slice(0, index + 1)) - this.tickLabelPadding();
}
else {
if (this._tierLabelPositions[index] === "center") {
yTranslate = this.height() - d3.sum(this._tierHeights.slice(0, index)) - this.tickLabelPadding() - this._maxLabelTickLength();
}
else {
yTranslate = this.height() - d3.sum(this._tierHeights.slice(0, index)) - this.tickLabelPadding();
}
}
var textSelection = tickLabels.selectAll("text");
if (textSelection.size() > 0) {
Plottable.Utils.DOM.translate(textSelection, xTranslate, yTranslate);
}
tickLabels.exit().remove();
tickLabels.attr("transform", function (d) { return "translate(" + _this._scale.scale(d) + ",0)"; });
var anchor = (this._tierLabelPositions[index] === "center" || config.step === 1) ? "middle" : "start";
tickLabels.selectAll("text").text(config.formatter).style("text-anchor", anchor);
};
Time.prototype._renderTickMarks = function (tickValues, index) {
var tickMarks = this._tierMarkContainers[index].selectAll("." + Plottable.Axis.TICK_MARK_CLASS).data(tickValues);
tickMarks.enter().append("line").classed(Plottable.Axis.TICK_MARK_CLASS, true);
var attr = this._generateTickMarkAttrHash();
var offset = this._tierHeights.slice(0, index).reduce(function (translate, height) { return translate + height; }, 0);
if (this.orientation() === "bottom") {
attr["y1"] = offset;
attr["y2"] = offset + (this._tierLabelPositions[index] === "center" ? this.innerTickLength() : this._tierHeights[index]);
}
else {
attr["y1"] = this.height() - offset;
attr["y2"] = this.height() - (offset + (this._tierLabelPositions[index] === "center" ?
this.innerTickLength() : this._tierHeights[index]));
}
tickMarks.attr(attr);
if (this.orientation() === "bottom") {
attr["y1"] = offset;
attr["y2"] = offset + (this._tierLabelPositions[index] === "center" ? this.endTickLength() : this._tierHeights[index]);
}
else {
attr["y1"] = this.height() - offset;
attr["y2"] = this.height() - (offset + (this._tierLabelPositions[index] === "center" ?
this.endTickLength() : this._tierHeights[index]));
}
d3.select(tickMarks[0][0]).attr(attr);
d3.select(tickMarks[0][tickMarks.size() - 1]).attr(attr);
// Add end-tick classes to first and last tick for CSS customization purposes
d3.select(tickMarks[0][0]).classed(Plottable.Axis.END_TICK_MARK_CLASS, true);
d3.select(tickMarks[0][tickMarks.size() - 1]).classed(Plottable.Axis.END_TICK_MARK_CLASS, true);
tickMarks.exit().remove();
};
Time.prototype._renderLabellessTickMarks = function (tickValues) {
var tickMarks = this._tickMarkContainer.selectAll("." + Plottable.Axis.TICK_MARK_CLASS).data(tickValues);
tickMarks.enter().append("line").classed(Plottable.Axis.TICK_MARK_CLASS, true);
var attr = this._generateTickMarkAttrHash();
attr["y2"] = (this.orientation() === "bottom") ? this.tickLabelPadding() : this.height() - this.tickLabelPadding();
tickMarks.attr(attr);
tickMarks.exit().remove();
};
Time.prototype._generateLabellessTicks = function () {
if (this._mostPreciseConfigIndex < 1) {
return [];
}
return this._getTickIntervalValues(this._possibleTimeAxisConfigurations[this._mostPreciseConfigIndex - 1][0]);
};
Time.prototype.renderImmediately = function () {
var _this = this;
this._mostPreciseConfigIndex = this._getMostPreciseConfigurationIndex();
var tierConfigs = this._possibleTimeAxisConfigurations[this._mostPreciseConfigIndex];
this._cleanTiers();
tierConfigs.forEach(function (config, i) {
return _this._renderTierLabels(_this._tierLabelContainers[i], config, i);
});
var tierTicks = tierConfigs.map(function (config, i) {
return _this._getTickValuesForConfiguration(config);
});
var baselineOffset = 0;
for (var i = 0; i < Math.max(tierConfigs.length, 1); ++i) {
var attr = this._generateBaselineAttrHash();
attr["y1"] += (this.orientation() === "bottom") ? baselineOffset : -baselineOffset;
attr["y2"] = attr["y1"];
this._tierBaselines[i].attr(attr).style("visibility", "inherit");
baselineOffset += this._tierHeights[i];
}
var labelLessTicks = [];
var domain = this._scale.domain();
var totalLength = this._scale.scale(domain[1]) - this._scale.scale(domain[0]);
if (this._getIntervalLength(tierConfigs[0]) * 1.5 >= totalLength) {
labelLessTicks = this._generateLabellessTicks();
}
this._renderLabellessTickMarks(labelLessTicks);
this._hideOverflowingTiers();
for (var i = 0; i < tierConfigs.length; ++i) {
this._renderTickMarks(tierTicks[i], i);
this._hideOverlappingAndCutOffLabels(i);
}
if (this.annotationsEnabled()) {
this._drawAnnotations();
}
else {
this._removeAnnotations();
}
return this;
};
Time.prototype._hideOverflowingTiers = function () {
var _this = this;
var availableHeight = this.height();
var usedHeight = 0;
this.content()
.selectAll("." + Time.TIME_AXIS_TIER_CLASS)
.attr("visibility", function (d, i) {
usedHeight += _this._tierHeights[i];
return usedHeight <= availableHeight ? "inherit" : "hidden";
});
};
Time.prototype._hideOverlappingAndCutOffLabels = function (index) {
var _this = this;
var boundingBox = this._boundingBox.node().getBoundingClientRect();
var isInsideBBox = function (tickBox) {
return (Math.floor(boundingBox.left) <= Math.ceil(tickBox.left) &&
Math.floor(boundingBox.top) <= Math.ceil(tickBox.top) &&
Math.floor(tickBox.right) <= Math.ceil(boundingBox.left + _this.width()) &&
Math.floor(tickBox.bottom) <= Math.ceil(boundingBox.top + _this.height()));
};
var visibleTickMarks = this._tierMarkContainers[index]
.selectAll("." + Plottable.Axis.TICK_MARK_CLASS)
.filter(function (d, i) {
var visibility = d3.select(this).style("visibility");
return visibility === "visible" || visibility === "inherit";
});
// We use the ClientRects because x1/x2 attributes are not comparable to ClientRects of labels
var visibleTickMarkRects = visibleTickMarks[0].map(function (mark) { return mark.getBoundingClientRect(); });
var visibleTickLabels = this._tierLabelContainers[index]
.selectAll("." + Plottable.Axis.TICK_LABEL_CLASS)
.filter(function (d, i) {
var visibility = d3.select(this).style("visibility");
return visibility === "visible" || visibility === "inherit";
});
var lastLabelClientRect;
visibleTickLabels.each(function (d, i) {
var clientRect = this.getBoundingClientRect();
var tickLabel = d3.select(this);
var leadingTickMark = visibleTickMarkRects[i];
var trailingTickMark = visibleTickMarkRects[i + 1];
var isOverlappingLastLabel = (lastLabelClientRect != null && Plottable.Utils.DOM.clientRectsOverlap(clientRect, lastLabelClientRect));
var isOverlappingLeadingTickMark = (leadingTickMark != null && Plottable.Utils.DOM.clientRectsOverlap(clientRect, leadingTickMark));
var isOverlappingTrailingTickMark = (trailingTickMark != null && Plottable.Utils.DOM.clientRectsOverlap(clientRect, trailingTickMark));
if (!isInsideBBox(clientRect) || isOverlappingLastLabel || isOverlappingLeadingTickMark || isOverlappingTrailingTickMark) {
tickLabel.style("visibility", "hidden");
}
else {
lastLabelClientRect = clientRect;
tickLabel.style("visibility", "inherit");
}
});
};
/**
* The CSS class applied to each Time Axis tier
*/
Time.TIME_AXIS_TIER_CLASS = "time-axis-tier";
Time._DEFAULT_TIME_AXIS_CONFIGURATIONS = [
[
{ interval: Plottable.TimeInterval.second, step: 1, formatter: Plottable.Formatters.time("%I:%M:%S %p") },
{ interval: Plottable.TimeInterval.day, step: 1, formatter: Plottable.Formatters.time("%B %e, %Y") },
],
[
{ interval: Plottable.TimeInterval.second, step: 5, formatter: Plottable.Formatters.time("%I:%M:%S %p") },
{ interval: Plottable.TimeInterval.day, step: 1, formatter: Plottable.Formatters.time("%B %e, %Y") },
],
[
{ interval: Plottable.TimeInterval.second, step: 10, formatter: Plottable.Formatters.time("%I:%M:%S %p") },
{ interval: Plottable.TimeInterval.day, step: 1, formatter: Plottable.Formatters.time("%B %e, %Y") },
],
[
{ interval: Plottable.TimeInterval.second, step: 15, formatter: Plottable.Formatters.time("%I:%M:%S %p") },
{ interval: Plottable.TimeInterval.day, step: 1, formatter: Plottable.Formatters.time("%B %e, %Y") },
],
[
{ interval: Plottable.TimeInterval.second, step: 30, formatter: Plottable.Formatters.time("%I:%M:%S %p") },
{ interval: Plottable.TimeInterval.day, step: 1, formatter: Plottable.Formatters.time("%B %e, %Y") },
],
[
{ interval: Plottable.TimeInterval.minute, step: 1, formatter: Plottable.Formatters.time("%I:%M %p") },
{ interval: Plottable.TimeInterval.day, step: 1, formatter: Plottable.Formatters.time("%B %e, %Y") },
],
[
{ interval: Plottable.TimeInterval.minute, step: 5, formatter: Plottable.Formatters.time("%I:%M %p") },
{ interval: Plottable.TimeInterval.day, step: 1, formatter: Plottable.Formatters.time("%B %e, %Y") },
],
[
{ interval: Plottable.TimeInterval.minute, step: 10, formatter: Plottable.Formatters.time("%I:%M %p") },
{ interval: Plottable.TimeInterval.day, step: 1, formatter: Plottable.Formatters.time("%B %e, %Y") },
],
[
{ interval: Plottable.TimeInterval.minute, step: 15, formatter: Plottable.Formatters.time("%I:%M %p") },
{ interval: Plottable.TimeInterval.day, step: 1, formatter: Plottable.Formatters.time("%B %e, %Y") },
],
[
{ interval: Plottable.TimeInterval.minute, step: 30, formatter: Plottable.Formatters.time("%I:%M %p") },
{ interval: Plottable.TimeInterval.day, step: 1, formatter: Plottable.Formatters.time("%B %e, %Y") },
],
[
{ interval: Plottable.TimeInterval.hour, step: 1, formatter: Plottable.Formatters.time("%I %p") },
{ interval: Plottable.TimeInterval.day, step: 1, formatter: Plottable.Formatters.time("%B %e, %Y") },
],
[
{ interval: Plottable.TimeInterval.hour, step: 3, formatter: Plottable.Formatters.time("%I %p") },
{ interval: Plottable.TimeInterval.day, step: 1, formatter: Plottable.Formatters.time("%B %e, %Y") },
],
[
{ interval: Plottable.TimeInterval.hour, step: 6, formatter: Plottable.Formatters.time("%I %p") },
{ interval: Plottable.TimeInterval.day, step: 1, formatter: Plottable.Formatters.time("%B %e, %Y") },
],
[
{ interval: Plottable.TimeInterval.hour, step: 12, formatter: Plottable.Formatters.time("%I %p") },
{ interval: Plottable.TimeInterval.day, step: 1, formatter: Plottable.Formatters.time("%B %e, %Y") },
],
[
{ interval: Plottable.TimeInterval.day, step: 1, formatter: Plottable.Formatters.time("%a %e") },
{ interval: Plottable.TimeInterval.month, step: 1, formatter: Plottable.Formatters.time("%B %Y") },
],
[
{ interval: Plottable.TimeInterval.day, step: 1, formatter: Plottable.Formatters.time("%e") },
{ interval: Plottable.TimeInterval.month, step: 1, formatter: Plottable.Formatters.time("%B %Y") },
],
[
{ interval: Plottable.TimeInterval.month, step: 1, formatter: Plottable.Formatters.time("%B") },
{ interval: Plottable.TimeInterval.year, step: 1, formatter: Plottable.Formatters.time("%Y") },
],
[
{ interval: Plottable.TimeInterval.month, step: 1, formatter: Plottable.Formatters.time("%b") },
{ interval: Plottable.TimeInterval.year, step: 1, formatter: Plottable.Formatters.time("%Y") },
],
[
{ interval: Plottable.TimeInterval.month, step: 3, formatter: Plottable.Formatters.time("%b") },
{ interval: Plottable.TimeInterval.year, step: 1, formatter: Plottable.Formatters.time("%Y") },
],
[
{ interval: Plottable.TimeInterval.month, step: 6, formatter: Plottable.Formatters.time("%b") },
{ interval: Plottable.TimeInterval.year, step: 1, formatter: Plottable.Formatters.time("%Y") },
],
[
{ interval: Plottable.TimeInterval.year, step: 1, formatter: Plottable.Formatters.time("%Y") },
],
[
{ interval: Plottable.TimeInterval.year, step: 1, formatter: Plottable.Formatters.time("%y") },
],
[
{ interval: Plottable.TimeInterval.year, step: 5, formatter: Plottable.Formatters.time("%Y") },
],
[
{ interval: Plottable.TimeInterval.year, step: 25, formatter: Plottable.Formatters.time("%Y") },
],
[
{ interval: Plottable.TimeInterval.year, step: 50, formatter: Plottable.Formatters.time("%Y") },
],
[
{ interval: Plottable.TimeInterval.year, step: 100, formatter: Plottable.Formatters.time("%Y") },
],
[
{ interval: Plottable.TimeInterval.year, step: 200, formatter: Plottable.Formatters.time("%Y") },
],
[
{ interval: Plottable.TimeInterval.year, step: 500, formatter: Plottable.Formatters.time("%Y") },
],
[
{ interval: Plottable.TimeInterval.year, step: 1000, formatter: Plottable.Formatters.time("%Y") },
],
];
Time._LONG_DATE = new Date(9999, 8, 29, 12, 59, 9999);
return Time;
}(Plottable.Axis));
Axes.Time = Time;
})(Axes = Plottable.Axes || (Plottable.Axes = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Axes;
(function (Axes) {
var Numeric = (function (_super) {
__extends(Numeric, _super);
/**
* Constructs a Numeric Axis.
*
* A Numeric Axis is a visual representation of a QuantitativeScale.
*
* @constructor
* @param {QuantitativeScale} scale
* @param {AxisOrientation} orientation Orientation of this Numeric Axis.
*/
function Numeric(scale, orientation) {
_super.call(this, scale, orientation);
this._tickLabelPositioning = "center";
this._usesTextWidthApproximation = false;
this.formatter(Plottable.Formatters.general());
}
Numeric.prototype._setup = function () {
_super.prototype._setup.call(this);
this._measurer = new SVGTypewriter.CacheMeasurer(this._tickLabelContainer, Plottable.Axis.TICK_LABEL_CLASS);
this._wrapper = new SVGTypewriter.Wrapper().maxLines(1);
};
Numeric.prototype._computeWidth = function () {
var maxTextWidth = this._usesTextWidthApproximation ? this._computeApproximateTextWidth() : this._computeExactTextWidth();
if (this._tickLabelPositioning === "center") {
return this._maxLabelTickLength() + this.tickLabelPadding() + maxTextWidth;
}
else {
return Math.max(this._maxLabelTickLength(), this.tickLabelPadding() + maxTextWidth);
}
};
Numeric.prototype._computeExactTextWidth = function () {
var _this = this;
var tickValues = this._getTickValues();
var textLengths = tickValues.map(function (v) {
var formattedValue = _this.formatter()(v);
return _this._measurer.measure(formattedValue).width;
});
return Plottable.Utils.Math.max(textLengths, 0);
};
Numeric.prototype._computeApproximateTextWidth = function () {
var _this = this;
var tickValues = this._getTickValues();
var mWidth = this._measurer.measure("M").width;
var textLengths = tickValues.map(function (v) {
var formattedValue = _this.formatter()(v);
return formattedValue.length * mWidth;
});
return Plottable.Utils.Math.max(textLengths, 0);
};
Numeric.prototype._computeHeight = function () {
var textHeight = this._measurer.measure().height;
if (this._tickLabelPositioning === "center") {
return this._maxLabelTickLength() + this.tickLabelPadding() + textHeight;
}
else {
return Math.max(this._maxLabelTickLength(), this.tickLabelPadding() + textHeight);
}
};
Numeric.prototype._getTickValues = function () {
var scale = this._scale;
var domain = scale.domain();
var min = domain[0] <= domain[1] ? domain[0] : domain[1];
var max = domain[0] >= domain[1] ? domain[0] : domain[1];
if (min === domain[0]) {
return scale.ticks().filter(function (i) { return i >= min && i <= max; });
}
else {
return scale.ticks().filter(function (i) { return i >= min && i <= max; }).reverse();
}
};
Numeric.prototype._rescale = function () {
if (!this._isSetup) {
return;
}
if (!this._isHorizontal()) {
var reComputedWidth = this._computeWidth();
if (reComputedWidth > this.width() || reComputedWidth < (this.width() - this.margin())) {
this.redraw();
return;
}
}
this.render();
};
Numeric.prototype.renderImmediately = function () {
var _this = this;
_super.prototype.renderImmediately.call(this);
var tickLabelAttrHash = {
x: 0,
y: 0,
dx: "0em",
dy: "0.3em",
};
var tickMarkLength = this._maxLabelTickLength();
var tickLabelPadding = this.tickLabelPadding();
var tickLabelTextAnchor = "middle";
var labelGroupTransformX = 0;
var labelGroupTransformY = 0;
var labelGroupShiftX = 0;
var labelGroupShiftY = 0;
if (this._isHorizontal()) {
switch (this._tickLabelPositioning) {
case "left":
tickLabelTextAnchor = "end";
labelGroupTransformX = -tickLabelPadding;
labelGroupShiftY = tickLabelPadding;
break;
case "center":
labelGroupShiftY = tickMarkLength + tickLabelPadding;
break;
case "right":
tickLabelTextAnchor = "start";
labelGroupTransformX = tickLabelPadding;
labelGroupShiftY = tickLabelPadding;
break;
}
}
else {
switch (this._tickLabelPositioning) {
case "top":
tickLabelAttrHash["dy"] = "-0.3em";
labelGroupShiftX = tickLabelPadding;
labelGroupTransformY = -tickLabelPadding;
break;
case "center":
labelGroupShiftX = tickMarkLength + tickLabelPadding;
break;
case "bottom":
tickLabelAttrHash["dy"] = "1em";
labelGroupShiftX = tickLabelPadding;
labelGroupTransformY = tickLabelPadding;
break;
}
}
var tickMarkAttrHash = this._generateTickMarkAttrHash();
switch (this.orientation()) {
case "bottom":
tickLabelAttrHash["x"] = tickMarkAttrHash["x1"];
tickLabelAttrHash["dy"] = "0.95em";
labelGroupTransformY = tickMarkAttrHash["y1"] + labelGroupShiftY;
break;
case "top":
tickLabelAttrHash["x"] = tickMarkAttrHash["x1"];
tickLabelAttrHash["dy"] = "-.25em";
labelGroupTransformY = tickMarkAttrHash["y1"] - labelGroupShiftY;
break;
case "left":
tickLabelTextAnchor = "end";
labelGroupTransformX = tickMarkAttrHash["x1"] - labelGroupShiftX;
tickLabelAttrHash["y"] = tickMarkAttrHash["y1"];
break;
case "right":
tickLabelTextAnchor = "start";
labelGroupTransformX = tickMarkAttrHash["x1"] + labelGroupShiftX;
tickLabelAttrHash["y"] = tickMarkAttrHash["y1"];
break;
}
var tickLabelValues = this._getTickValues();
var tickLabels = this._tickLabelContainer
.selectAll("." + Plottable.Axis.TICK_LABEL_CLASS)
.data(tickLabelValues);
tickLabels.enter().append("text").classed(Plottable.Axis.TICK_LABEL_CLASS, true);
tickLabels.exit().remove();
tickLabels.style("text-anchor", tickLabelTextAnchor)
.style("visibility", "inherit")
.attr(tickLabelAttrHash)
.text(function (s) { return _this.formatter()(s); });
var labelGroupTransform = "translate(" + labelGroupTransformX + ", " + labelGroupTransformY + ")";
this._tickLabelContainer.attr("transform", labelGroupTransform);
this._showAllTickMarks();
if (!this.showEndTickLabels()) {
this._hideEndTickLabels();
}
this._hideOverflowingTickLabels();
this._hideOverlappingTickLabels();
if (this._tickLabelPositioning === "bottom" ||
this._tickLabelPositioning === "top" ||
this._tickLabelPositioning === "left" ||
this._tickLabelPositioning === "right") {
this._hideTickMarksWithoutLabel();
}
return this;
};
Numeric.prototype.tickLabelPosition = function (position) {
if (position == null) {
return this._tickLabelPositioning;
}
else {
var positionLC = position.toLowerCase();
if (this._isHorizontal()) {
if (!(positionLC === "left" || positionLC === "center" || positionLC === "right")) {
throw new Error(positionLC + " is not a valid tick label position for a horizontal NumericAxis");
}
}
else {
if (!(positionLC === "top" || positionLC === "center" || positionLC === "bottom")) {
throw new Error(positionLC + " is not a valid tick label position for a vertical NumericAxis");
}
}
this._tickLabelPositioning = positionLC;
this.redraw();
return this;
}
};
Numeric.prototype.usesTextWidthApproximation = function (enable) {
if (enable == null) {
return this._usesTextWidthApproximation;
}
else {
this._usesTextWidthApproximation = enable;
return this;
}
};
Numeric.prototype._hideEndTickLabels = function () {
var boundingBox = this._boundingBox.node().getBoundingClientRect();
var tickLabels = this._tickLabelContainer.selectAll("." + Plottable.Axis.TICK_LABEL_CLASS);
if (tickLabels[0].length === 0) {
return;
}
var firstTickLabel = tickLabels[0][0];
if (!Plottable.Utils.DOM.clientRectInside(firstTickLabel.getBoundingClientRect(), boundingBox)) {
d3.select(firstTickLabel).style("visibility", "hidden");
}
var lastTickLabel = tickLabels[0][tickLabels[0].length - 1];
if (!Plottable.Utils.DOM.clientRectInside(lastTickLabel.getBoundingClientRect(), boundingBox)) {
d3.select(lastTickLabel).style("visibility", "hidden");
}
};
Numeric.prototype._hideOverlappingTickLabels = function () {
var visibleTickLabels = this._tickLabelContainer
.selectAll("." + Plottable.Axis.TICK_LABEL_CLASS)
.filter(function (d, i) {
var visibility = d3.select(this).style("visibility");
return (visibility === "inherit") || (visibility === "visible");
});
var visibleTickLabelRects = visibleTickLabels[0].map(function (label) { return label.getBoundingClientRect(); });
var interval = 1;
while (!this._hasOverlapWithInterval(interval, visibleTickLabelRects) && interval < visibleTickLabelRects.length) {
interval += 1;
}
visibleTickLabels.each(function (d, i) {
var tickLabel = d3.select(this);
if (i % interval !== 0) {
tickLabel.style("visibility", "hidden");
}
});
};
/**
* The method is responsible for evenly spacing the labels on the axis.
* @return test to see if taking every `interval` recrangle from `rects`
* will result in labels not overlapping
*
* For top, bottom, left, right positioning of the thicks, we want the padding
* between the labels to be 3x, such that the label will be `padding` distance
* from the tick and 2 * `padding` distance (or more) from the next tick
*
*/
Numeric.prototype._hasOverlapWithInterval = function (interval, rects) {
var padding = this.tickLabelPadding();
if (this._tickLabelPositioning === "bottom" ||
this._tickLabelPositioning === "top" ||
this._tickLabelPositioning === "left" ||
this._tickLabelPositioning === "right") {
padding *= 3;
}
for (var i = 0; i < rects.length - (interval); i += interval) {
var currRect = rects[i];
var nextRect = rects[i + interval];
if (this._isHorizontal()) {
if (currRect.right + padding >= nextRect.left) {
return false;
}
}
else {
if (currRect.top - padding <= nextRect.bottom) {
return false;
}
}
}
return true;
};
return Numeric;
}(Plottable.Axis));
Axes.Numeric = Numeric;
})(Axes = Plottable.Axes || (Plottable.Axes = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Axes;
(function (Axes) {
var Category = (function (_super) {
__extends(Category, _super);
/**
* Constructs a Category Axis.
*
* A Category Axis is a visual representation of a Category Scale.
*
* @constructor
* @param {Scales.Category} scale
* @param {AxisOrientation} [orientation="bottom"] Orientation of this Category Axis.
*/
function Category(scale, orientation) {
if (orientation === void 0) { orientation = "bottom"; }
_super.call(this, scale, orientation);
this._tickLabelAngle = 0;
this.addClass("category-axis");
}
Object.defineProperty(Category.prototype, "_wrapper", {
/**
* A Wrapper configured according to the other properties on this axis.
* @returns {SVGTypewriter.Wrapper}
*/
get: function () {
var wrapper = new SVGTypewriter.Wrapper();
if (this._tickLabelMaxLines != null) {
wrapper.maxLines(this._tickLabelMaxLines);
}
return wrapper;
},
enumerable: true,
configurable: true
});
Object.defineProperty(Category.prototype, "_writer", {
/**
* A Writer attached to this measurer and wrapper.
* @returns {SVGTypewriter.Writer}
*/
get: function () {
return new SVGTypewriter.Writer(this._measurer, this._wrapper);
},
enumerable: true,
configurable: true
});
Category.prototype._setup = function () {
_super.prototype._setup.call(this);
this._measurer = new SVGTypewriter.CacheMeasurer(this._tickLabelContainer);
};
Category.prototype._rescale = function () {
return this.redraw();
};
/**
* Compute space requirements for this Category Axis. Category Axes have two primary space requirements:
*
* 1) width/height needed by the tick lines (including annotations, padding, and margins).
* 2) width/height needed by the tick text.
*
* We requested space is the sum of the lines and text.
* @param offeredWidth
* @param offeredHeight
* @returns {any}
*/
Category.prototype.requestedSpace = function (offeredWidth, offeredHeight) {
var widthRequiredByTicks = this._isHorizontal() ? 0 : this._tickSpaceRequired() + this.margin();
var heightRequiredByTicks = this._isHorizontal() ? this._tickSpaceRequired() + this.margin() : 0;
if (this._scale.domain().length === 0) {
return {
minWidth: 0,
minHeight: 0,
};
}
if (this.annotationsEnabled()) {
var tierTotalHeight = this._annotationTierHeight() * this.annotationTierCount();
if (this._isHorizontal()) {
heightRequiredByTicks += tierTotalHeight;
}
else {
widthRequiredByTicks += tierTotalHeight;
}
}
var measureResult = this._measureTickLabels(offeredWidth, offeredHeight);
return {
minWidth: measureResult.usedWidth + widthRequiredByTicks,
minHeight: measureResult.usedHeight + heightRequiredByTicks,
};
};
Category.prototype._coreSize = function () {
var relevantDimension = this._isHorizontal() ? this.height() : this.width();
var relevantRequestedSpaceDimension = this._isHorizontal() ?
this.requestedSpace(this.width(), this.height()).minHeight :
this.requestedSpace(this.width(), this.height()).minWidth;
var marginAndAnnotationSize = this.margin() + this._annotationTierHeight();
var axisHeightWithoutMargin = relevantRequestedSpaceDimension - marginAndAnnotationSize;
return Math.min(axisHeightWithoutMargin, relevantDimension);
};
Category.prototype._getTickValues = function () {
return this.getDownsampleInfo().domain;
};
/**
* Take the scale and drop ticks at regular intervals such that the resultant ticks are all a reasonable minimum
* distance apart. Return the resultant ticks to render, as well as the new stepWidth between them.
*
* @param {Scales.Category} scale - The scale being downsampled. Defaults to this Axis' scale.
* @return {DownsampleInfo} an object holding the resultant domain and new stepWidth.
*/
Category.prototype.getDownsampleInfo = function (scale) {
if (scale === void 0) { scale = this._scale; }
var downsampleRatio = Math.ceil(Category._MINIMUM_WIDTH_PER_LABEL_PX / scale.stepWidth());
return {
domain: scale.domain().filter(function (d, i) { return i % downsampleRatio === 0; }),
stepWidth: downsampleRatio * scale.stepWidth(),
};
};
Category.prototype.tickLabelAngle = function (angle) {
if (angle == null) {
return this._tickLabelAngle;
}
if (angle !== 0 && angle !== 90 && angle !== -90) {
throw new Error("Angle " + angle + " not supported; only 0, 90, and -90 are valid values");
}
this._tickLabelAngle = angle;
this.redraw();
return this;
};
/**
* Set or get the tick label's max width on this axis. When set, tick labels will be truncated with ellipsis to be
* at most `tickLabelMaxWidth()` pixels wide. This ensures the axis doesn't grow to an undesirable width.
*
* Passing no arguments retrieves the value, while passing a number sets the value. Pass undefined to un-set the max
* width.
* @param maxWidth
* @returns {number | this}
*/
Category.prototype.tickLabelMaxWidth = function (maxWidth) {
// allow user to un-set tickLabelMaxWidth by passing in null or undefined explicitly
if (arguments.length === 0) {
return this._tickLabelMaxWidth;
}
this._tickLabelMaxWidth = maxWidth;
this.redraw();
return this;
};
/**
* Set or get the tick label's max number of wrapped lines on this axis. By default, a Category Axis will line-wrap
* long tick labels onto multiple lines in order to fit the width of the axis. When set, long tick labels will be
* rendered on at most `tickLabelMaxLines()` lines. This ensures the axis doesn't grow to an undesirable height.
*
* Passing no arguments retrieves the value, while passing a number sets the value. Pass undefined to un-set the
* max lines.
* @param maxLines
* @returns {number | this}
*/
Category.prototype.tickLabelMaxLines = function (maxLines) {
// allow user to un-set tickLabelMaxLines by passing in null or undefined explicitly
if (arguments.length === 0) {
return this._tickLabelMaxLines;
}
this._tickLabelMaxLines = maxLines;
this.redraw();
return this;
};
/**
* Return the space required by the ticks, padding included.
* @returns {number}
*/
Category.prototype._tickSpaceRequired = function () {
return this._maxLabelTickLength() + this.tickLabelPadding();
};
/**
* Write ticks to the DOM.
* @param {Plottable.Scales.Category} scale The scale this axis is representing.
* @param {d3.Selection} ticks The tick elements to write.
*/
Category.prototype._drawTicks = function (stepWidth, ticks) {
var self = this;
var xAlign;
var yAlign;
switch (this.tickLabelAngle()) {
case 0:
xAlign = { left: "right", right: "left", top: "center", bottom: "center" };
yAlign = { left: "center", right: "center", top: "bottom", bottom: "top" };
break;
case 90:
xAlign = { left: "center", right: "center", top: "right", bottom: "left" };
yAlign = { left: "top", right: "bottom", top: "center", bottom: "center" };
break;
case -90:
xAlign = { left: "center", right: "center", top: "left", bottom: "right" };
yAlign = { left: "bottom", right: "top", top: "center", bottom: "center" };
break;
}
ticks.each(function (d) {
var width = self._isHorizontal() ? stepWidth : self.width() - self._tickSpaceRequired();
var height = self._isHorizontal() ? self.height() - self._tickSpaceRequired() : stepWidth;
var writeOptions = {
selection: d3.select(this),
xAlign: xAlign[self.orientation()],
yAlign: yAlign[self.orientation()],
textRotation: self.tickLabelAngle(),
};
if (self._tickLabelMaxWidth != null) {
// for left-oriented axes, we must move the ticks by the amount we've cut off in order to keep the text
// aligned with the side of the ticks
if (self.orientation() === "left" && width > self._tickLabelMaxWidth) {
var cutOffWidth = width - self._tickLabelMaxWidth;
var newTransform = writeOptions.selection.attr("transform") + " translate(" + cutOffWidth + ", 0)";
writeOptions.selection.attr("transform", newTransform);
}
width = Math.min(width, self._tickLabelMaxWidth);
}
self._writer.write(self.formatter()(d), width, height, writeOptions);
});
};
/**
* Measures the size of the tick labels without making any (permanent) DOM changes.
*
* @param {number} axisWidth Width available for this axis.
* @param {number} axisHeight Height available for this axis.
* @param {Plottable.Scales.Category} scale The scale this axis is representing.
* @param {string[]} ticks The strings that will be printed on the ticks.
*/
Category.prototype._measureTickLabels = function (axisWidth, axisHeight) {
var _this = this;
var thisScale = this._scale;
// set up a test scale to simulate rendering ticks with the given width and height.
var scale = new Plottable.Scales.Category()
.domain(thisScale.domain())
.innerPadding(thisScale.innerPadding())
.outerPadding(thisScale.outerPadding())
.range([0, this._isHorizontal() ? axisWidth : axisHeight]);
var _a = this.getDownsampleInfo(scale), domain = _a.domain, stepWidth = _a.stepWidth;
// HACKHACK: https://github.com/palantir/svg-typewriter/issues/25
// the width (x-axis specific) available to a single tick label.
var width = axisWidth - this._tickSpaceRequired(); // default for left/right
if (this._isHorizontal()) {
width = stepWidth; // defaults to the band width
if (this._tickLabelAngle !== 0) {
width = axisHeight - this._tickSpaceRequired(); // use the axis height
}
// HACKHACK: Wrapper fails under negative circumstances
width = Math.max(width, 0);
}
// HACKHACK: https://github.com/palantir/svg-typewriter/issues/25
// the height (y-axis specific) available to a single tick label.
var height = stepWidth; // default for left/right
if (this._isHorizontal()) {
height = axisHeight - this._tickSpaceRequired();
if (this._tickLabelAngle !== 0) {
height = axisWidth - this._tickSpaceRequired();
}
// HACKHACK: Wrapper fails under negative circumstances
height = Math.max(height, 0);
}
if (this._tickLabelMaxWidth != null) {
width = Math.min(width, this._tickLabelMaxWidth);
}
var wrappingResults = domain.map(function (s) {
return _this._wrapper.wrap(_this.formatter()(s), _this._measurer, width, height);
});
// HACKHACK: https://github.com/palantir/svg-typewriter/issues/25
var widthFn = (this._isHorizontal() && this._tickLabelAngle === 0) ? d3.sum : Plottable.Utils.Math.max;
var heightFn = (this._isHorizontal() && this._tickLabelAngle === 0) ? Plottable.Utils.Math.max : d3.sum;
var usedWidth = widthFn(wrappingResults, function (t) { return _this._measurer.measure(t.wrappedText).width; }, 0);
var usedHeight = heightFn(wrappingResults, function (t) { return _this._measurer.measure(t.wrappedText).height; }, 0);
// If the tick labels are rotated, reverse usedWidth and usedHeight
// HACKHACK: https://github.com/palantir/svg-typewriter/issues/25
if (this._tickLabelAngle !== 0) {
_b = [usedHeight, usedWidth], usedWidth = _b[0], usedHeight = _b[1];
}
return {
usedWidth: usedWidth,
usedHeight: usedHeight,
};
var _b;
};
Category.prototype.renderImmediately = function () {
var _this = this;
_super.prototype.renderImmediately.call(this);
var catScale = this._scale;
var _a = this.getDownsampleInfo(), domain = _a.domain, stepWidth = _a.stepWidth;
var tickLabels = this._tickLabelContainer.selectAll("." + Plottable.Axis.TICK_LABEL_CLASS).data(domain, function (d) { return d; });
// Give each tick a stepWidth of space which will partition the entire axis evenly
var availableTextSpace = stepWidth;
if (this._isHorizontal() && this._tickLabelMaxWidth != null) {
availableTextSpace = Math.min(availableTextSpace, this._tickLabelMaxWidth);
}
var getTickLabelTransform = function (d, i) {
// scale(d) will give the center of the band, so subtract half of the text width to get the left (top-most)
// coordinate that the tick label should be transformed to.
var tickLabelEdge = catScale.scale(d) - availableTextSpace / 2;
var x = _this._isHorizontal() ? tickLabelEdge : 0;
var y = _this._isHorizontal() ? 0 : tickLabelEdge;
return "translate(" + x + "," + y + ")";
};
tickLabels.enter().append("g").classed(Plottable.Axis.TICK_LABEL_CLASS, true);
tickLabels.exit().remove();
tickLabels.attr("transform", getTickLabelTransform);
// erase all text first, then rewrite
tickLabels.text("");
this._drawTicks(stepWidth, tickLabels);
var xTranslate = this.orientation() === "right" ? this._tickSpaceRequired() : 0;
var yTranslate = this.orientation() === "bottom" ? this._tickSpaceRequired() : 0;
Plottable.Utils.DOM.translate(this._tickLabelContainer, xTranslate, yTranslate);
// hide ticks and labels that overflow the axis
this._showAllTickMarks();
this._showAllTickLabels();
this._hideOverflowingTickLabels();
this._hideTickMarksWithoutLabel();
return this;
};
Category.prototype.computeLayout = function (origin, availableWidth, availableHeight) {
// When anyone calls redraw(), computeLayout() will be called
// on everyone, including this. Since CSS or something might have
// affected the size of the characters, clear the cache.
this._measurer.reset();
_super.prototype.computeLayout.call(this, origin, availableWidth, availableHeight);
if (!this._isHorizontal()) {
this._scale.range([0, this.height()]);
}
return this;
};
/**
* How many pixels to give labels at minimum before downsampling takes effect.
*/
Category._MINIMUM_WIDTH_PER_LABEL_PX = 15;
return Category;
}(Plottable.Axis));
Axes.Category = Category;
})(Axes = Plottable.Axes || (Plottable.Axes = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Components;
(function (Components) {
var Label = (function (_super) {
__extends(Label, _super);
/**
* A Label is a Component that displays a single line of text.
*
* @constructor
* @param {string} [displayText=""] The text of the Label.
* @param {number} [angle=0] The angle of the Label in degrees (-90/0/90). 0 is horizontal.
*/
function Label(displayText, angle) {
if (displayText === void 0) { displayText = ""; }
if (angle === void 0) { angle = 0; }
_super.call(this);
this.addClass("label");
this.text(displayText);
this.angle(angle);
this.xAlignment("center").yAlignment("center");
this._padding = 0;
}
Label.prototype.requestedSpace = function (offeredWidth, offeredHeight) {
var desiredWH = this._measurer.measure(this._text);
var desiredWidth = (this.angle() === 0 ? desiredWH.width : desiredWH.height) + 2 * this.padding();
var desiredHeight = (this.angle() === 0 ? desiredWH.height : desiredWH.width) + 2 * this.padding();
return {
minWidth: desiredWidth,
minHeight: desiredHeight,
};
};
Label.prototype._setup = function () {
_super.prototype._setup.call(this);
this._textContainer = this.content().append("g");
this._measurer = new SVGTypewriter.CacheMeasurer(this._textContainer);
this._wrapper = new SVGTypewriter.Wrapper();
this._writer = new SVGTypewriter.Writer(this._measurer, this._wrapper);
this.text(this._text);
};
Label.prototype.text = function (displayText) {
if (displayText == null) {
return this._text;
}
else {
if (typeof displayText !== "string") {
throw new Error("Label.text() only takes strings as input");
}
this._text = displayText;
this.redraw();
return this;
}
};
Label.prototype.angle = function (angle) {
if (angle == null) {
return this._angle;
}
else {
angle %= 360;
if (angle > 180) {
angle -= 360;
}
else if (angle < -180) {
angle += 360;
}
if (angle === -90 || angle === 0 || angle === 90) {
this._angle = angle;
}
else {
throw new Error(angle + " is not a valid angle for Label");
}
this.redraw();
return this;
}
};
Label.prototype.padding = function (padAmount) {
if (padAmount == null) {
return this._padding;
}
else {
padAmount = +padAmount;
if (padAmount < 0) {
throw new Error(padAmount + " is not a valid padding value. Cannot be less than 0.");
}
this._padding = padAmount;
this.redraw();
return this;
}
};
Label.prototype.fixedWidth = function () {
return true;
};
Label.prototype.fixedHeight = function () {
return true;
};
Label.prototype.renderImmediately = function () {
_super.prototype.renderImmediately.call(this);
// HACKHACK SVGTypewriter.remove existing content - #21 on SVGTypewriter.
this._textContainer.selectAll("g").remove();
var textMeasurement = this._measurer.measure(this._text);
var heightPadding = Math.max(Math.min((this.height() - textMeasurement.height) / 2, this.padding()), 0);
var widthPadding = Math.max(Math.min((this.width() - textMeasurement.width) / 2, this.padding()), 0);
this._textContainer.attr("transform", "translate(" + widthPadding + "," + heightPadding + ")");
var writeWidth = this.width() - 2 * widthPadding;
var writeHeight = this.height() - 2 * heightPadding;
var writeOptions = {
selection: this._textContainer,
xAlign: this.xAlignment(),
yAlign: this.yAlignment(),
textRotation: this.angle(),
};
this._writer.write(this._text, writeWidth, writeHeight, writeOptions);
return this;
};
return Label;
}(Plottable.Component));
Components.Label = Label;
var TitleLabel = (function (_super) {
__extends(TitleLabel, _super);
/**
* @constructor
* @param {string} [text]
* @param {number} [angle] One of -90/0/90. 0 is horizontal.
*/
function TitleLabel(text, angle) {
_super.call(this, text, angle);
this.addClass(TitleLabel.TITLE_LABEL_CLASS);
}
TitleLabel.TITLE_LABEL_CLASS = "title-label";
return TitleLabel;
}(Label));
Components.TitleLabel = TitleLabel;
var AxisLabel = (function (_super) {
__extends(AxisLabel, _super);
/**
* @constructor
* @param {string} [text]
* @param {number} [angle] One of -90/0/90. 0 is horizontal.
*/
function AxisLabel(text, angle) {
_super.call(this, text, angle);
this.addClass(AxisLabel.AXIS_LABEL_CLASS);
}
AxisLabel.AXIS_LABEL_CLASS = "axis-label";
return AxisLabel;
}(Label));
Components.AxisLabel = AxisLabel;
})(Components = Plottable.Components || (Plottable.Components = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Components;
(function (Components) {
var Legend = (function (_super) {
__extends(Legend, _super);
/**
* The Legend consists of a series of entries, each with a color and label taken from the Color Scale.
*
* @constructor
* @param {Scale.Color} scale
*/
function Legend(colorScale) {
var _this = this;
_super.call(this);
this._padding = 5;
this.addClass("legend");
this.maxEntriesPerRow(1);
if (colorScale == null) {
throw new Error("Legend requires a colorScale");
}
this._colorScale = colorScale;
this._redrawCallback = function (scale) { return _this.redraw(); };
this._colorScale.onUpdate(this._redrawCallback);
this._formatter = Plottable.Formatters.identity();
this.xAlignment("right").yAlignment("top");
this.comparator(function (a, b) {
var formattedText = _this._colorScale.domain().slice().map(function (d) { return _this._formatter(d); });
return formattedText.indexOf(a) - formattedText.indexOf(b);
});
this._symbolFactoryAccessor = function () { return Plottable.SymbolFactories.circle(); };
this._symbolOpacityAccessor = function () { return 1; };
}
Legend.prototype._setup = function () {
_super.prototype._setup.call(this);
var fakeLegendRow = this.content().append("g").classed(Legend.LEGEND_ROW_CLASS, true);
var fakeLegendEntry = fakeLegendRow.append("g").classed(Legend.LEGEND_ENTRY_CLASS, true);
fakeLegendEntry.append("text");
this._measurer = new SVGTypewriter.Measurer(fakeLegendRow);
this._wrapper = new SVGTypewriter.Wrapper().maxLines(1);
this._writer = new SVGTypewriter.Writer(this._measurer, this._wrapper).addTitleElement(Plottable.Configs.ADD_TITLE_ELEMENTS);
};
Legend.prototype.formatter = function (formatter) {
if (formatter == null) {
return this._formatter;
}
this._formatter = formatter;
this.redraw();
return this;
};
Legend.prototype.maxEntriesPerRow = function (maxEntriesPerRow) {
if (maxEntriesPerRow == null) {
return this._maxEntriesPerRow;
}
else {
this._maxEntriesPerRow = maxEntriesPerRow;
this.redraw();
return this;
}
};
Legend.prototype.comparator = function (comparator) {
if (comparator == null) {
return this._comparator;
}
else {
this._comparator = comparator;
this.redraw();
return this;
}
};
Legend.prototype.colorScale = function (colorScale) {
if (colorScale != null) {
this._colorScale.offUpdate(this._redrawCallback);
this._colorScale = colorScale;
this._colorScale.onUpdate(this._redrawCallback);
this.redraw();
return this;
}
else {
return this._colorScale;
}
};
Legend.prototype.destroy = function () {
_super.prototype.destroy.call(this);
this._colorScale.offUpdate(this._redrawCallback);
};
Legend.prototype._calculateLayoutInfo = function (availableWidth, availableHeight) {
var _this = this;
var textHeight = this._measurer.measure().height;
var availableWidthForEntries = Math.max(0, (availableWidth - this._padding));
var entryNames = this._colorScale.domain().slice().sort(function (a, b) { return _this._comparator(_this._formatter(a), _this._formatter(b)); });
var entryLengths = d3.map();
var untruncatedEntryLengths = d3.map();
entryNames.forEach(function (entryName) {
var untruncatedEntryLength = textHeight + _this._measurer.measure(_this._formatter(entryName)).width + _this._padding;
var entryLength = Math.min(untruncatedEntryLength, availableWidthForEntries);
entryLengths.set(entryName, entryLength);
untruncatedEntryLengths.set(entryName, untruncatedEntryLength);
});
var rows = this._packRows(availableWidthForEntries, entryNames, entryLengths);
var rowsAvailable = Math.floor((availableHeight - 2 * this._padding) / textHeight);
if (rowsAvailable !== rowsAvailable) {
rowsAvailable = 0;
}
return {
textHeight: textHeight,
entryLengths: entryLengths,
untruncatedEntryLengths: untruncatedEntryLengths,
rows: rows,
numRowsToDraw: Math.max(Math.min(rowsAvailable, rows.length), 0),
};
};
Legend.prototype.requestedSpace = function (offeredWidth, offeredHeight) {
var estimatedLayout = this._calculateLayoutInfo(offeredWidth, offeredHeight);
var untruncatedRowLengths = estimatedLayout.rows.map(function (row) {
return d3.sum(row, function (entry) { return estimatedLayout.untruncatedEntryLengths.get(entry); });
});
var longestUntruncatedRowLength = Plottable.Utils.Math.max(untruncatedRowLengths, 0);
return {
minWidth: this._padding + longestUntruncatedRowLength,
minHeight: estimatedLayout.rows.length * estimatedLayout.textHeight + 2 * this._padding,
};
};
Legend.prototype._packRows = function (availableWidth, entries, entryLengths) {
var _this = this;
var rows = [];
var currentRow = [];
var spaceLeft = availableWidth;
entries.forEach(function (e) {
var entryLength = entryLengths.get(e);
if (entryLength > spaceLeft || currentRow.length === _this._maxEntriesPerRow) {
rows.push(currentRow);
currentRow = [];
spaceLeft = availableWidth;
}
currentRow.push(e);
spaceLeft -= entryLength;
});
if (currentRow.length !== 0) {
rows.push(currentRow);
}
return rows;
};
/**
* Gets the Entities (representing Legend entries) at a particular point.
* Returns an empty array if no Entities are present at that location.
*
* @param {Point} p
* @returns {Entity<Legend>[]}
*/
Legend.prototype.entitiesAt = function (p) {
if (!this._isSetup) {
return [];
}
var entities = [];
var layout = this._calculateLayoutInfo(this.width(), this.height());
var legendPadding = this._padding;
var legend = this;
this.content().selectAll("g." + Legend.LEGEND_ROW_CLASS).each(function (d, i) {
var lowY = i * layout.textHeight + legendPadding;
var highY = (i + 1) * layout.textHeight + legendPadding;
var symbolY = (lowY + highY) / 2;
var lowX = legendPadding;
var highX = legendPadding;
d3.select(this).selectAll("g." + Legend.LEGEND_ENTRY_CLASS).each(function (value) {
highX += layout.entryLengths.get(value);
var symbolX = lowX + layout.textHeight / 2;
if (highX >= p.x && lowX <= p.x &&
highY >= p.y && lowY <= p.y) {
var entrySelection = d3.select(this);
var datum = entrySelection.datum();
entities.push({
datum: datum,
position: { x: symbolX, y: symbolY },
selection: entrySelection,
component: legend,
});
}
lowX += layout.entryLengths.get(value);
});
});
return entities;
};
Legend.prototype.renderImmediately = function () {
var _this = this;
_super.prototype.renderImmediately.call(this);
var layout = this._calculateLayoutInfo(this.width(), this.height());
var rowsToDraw = layout.rows.slice(0, layout.numRowsToDraw);
var rows = this.content().selectAll("g." + Legend.LEGEND_ROW_CLASS).data(rowsToDraw);
rows.enter().append("g").classed(Legend.LEGEND_ROW_CLASS, true);
rows.exit().remove();
rows.attr("transform", function (d, i) { return "translate(0, " + (i * layout.textHeight + _this._padding) + ")"; });
var entries = rows.selectAll("g." + Legend.LEGEND_ENTRY_CLASS).data(function (d) { return d; });
var entriesEnter = entries.enter().append("g").classed(Legend.LEGEND_ENTRY_CLASS, true);
entriesEnter.append("path");
entriesEnter.append("g").classed("text-container", true);
entries.exit().remove();
var legendPadding = this._padding;
rows.each(function (values) {
var xShift = legendPadding;
var entriesInRow = d3.select(this).selectAll("g." + Legend.LEGEND_ENTRY_CLASS);
entriesInRow.attr("transform", function (value, i) {
var translateString = "translate(" + xShift + ", 0)";
xShift += layout.entryLengths.get(value);
return translateString;
});
});
entries.select("path").attr("d", function (d, i, j) { return _this.symbol()(d, j)(layout.textHeight * 0.6); })
.attr("transform", "translate(" + (layout.textHeight / 2) + "," + layout.textHeight / 2 + ")")
.attr("fill", function (value) { return _this._colorScale.scale(value); })
.attr("opacity", function (d, i, j) { return _this.symbolOpacity()(d, j); })
.classed(Legend.LEGEND_SYMBOL_CLASS, true);
var padding = this._padding;
var textContainers = entries.select("g.text-container");
textContainers.text(""); // clear out previous results
var self = this;
textContainers.attr("transform", "translate(" + layout.textHeight + ", 0)")
.each(function (value) {
var container = d3.select(this);
var maxTextLength = layout.entryLengths.get(value) - layout.textHeight - padding;
var writeOptions = {
selection: container,
xAlign: "left",
yAlign: "top",
textRotation: 0,
};
self._writer.write(self._formatter(value), maxTextLength, self.height(), writeOptions);
});
return this;
};
Legend.prototype.symbol = function (symbol) {
if (symbol == null) {
return this._symbolFactoryAccessor;
}
else {
this._symbolFactoryAccessor = symbol;
this.render();
return this;
}
};
Legend.prototype.symbolOpacity = function (symbolOpacity) {
if (symbolOpacity == null) {
return this._symbolOpacityAccessor;
}
else if (typeof symbolOpacity === "number") {
this._symbolOpacityAccessor = function () { return symbolOpacity; };
}
else {
this._symbolOpacityAccessor = symbolOpacity;
}
this.render();
return this;
};
Legend.prototype.fixedWidth = function () {
return true;
};
Legend.prototype.fixedHeight = function () {
return true;
};
/**
* The css class applied to each legend row
*/
Legend.LEGEND_ROW_CLASS = "legend-row";
/**
* The css class applied to each legend entry
*/
Legend.LEGEND_ENTRY_CLASS = "legend-entry";
/**
* The css class applied to each legend symbol
*/
Legend.LEGEND_SYMBOL_CLASS = "legend-symbol";
return Legend;
}(Plottable.Component));
Components.Legend = Legend;
})(Components = Plottable.Components || (Plottable.Components = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Components;
(function (Components) {
var InterpolatedColorLegend = (function (_super) {
__extends(InterpolatedColorLegend, _super);
/**
* Creates an InterpolatedColorLegend.
*
* The InterpolatedColorLegend consists of a sequence of swatches that show the
* associated InterpolatedColor Scale sampled at various points.
* Two labels show the maximum and minimum values of the InterpolatedColor Scale.
*
* @constructor
* @param {Scales.InterpolatedColor} interpolatedColorScale
*/
function InterpolatedColorLegend(interpolatedColorScale) {
var _this = this;
_super.call(this);
this._textPadding = 5;
if (interpolatedColorScale == null) {
throw new Error("InterpolatedColorLegend requires a interpolatedColorScale");
}
this._scale = interpolatedColorScale;
this._redrawCallback = function (scale) { return _this.redraw(); };
this._scale.onUpdate(this._redrawCallback);
this._formatter = Plottable.Formatters.general();
this._orientation = "horizontal";
this._expands = false;
this.addClass("legend");
this.addClass("interpolated-color-legend");
}
InterpolatedColorLegend.prototype.destroy = function () {
_super.prototype.destroy.call(this);
this._scale.offUpdate(this._redrawCallback);
};
InterpolatedColorLegend.prototype.formatter = function (formatter) {
if (formatter === undefined) {
return this._formatter;
}
this._formatter = formatter;
this.redraw();
return this;
};
InterpolatedColorLegend.prototype.expands = function (expands) {
if (expands == null) {
return this._expands;
}
this._expands = expands;
this.redraw();
return this;
};
InterpolatedColorLegend._ensureOrientation = function (orientation) {
orientation = orientation.toLowerCase();
if (orientation === "horizontal" || orientation === "left" || orientation === "right") {
return orientation;
}
else {
throw new Error("\"" + orientation + "\" is not a valid orientation for InterpolatedColorLegend");
}
};
InterpolatedColorLegend.prototype.orientation = function (orientation) {
if (orientation == null) {
return this._orientation;
}
else {
this._orientation = InterpolatedColorLegend._ensureOrientation(orientation);
this.redraw();
return this;
}
};
InterpolatedColorLegend.prototype.fixedWidth = function () {
return !this.expands() || this._isVertical();
};
InterpolatedColorLegend.prototype.fixedHeight = function () {
return !this.expands() || !this._isVertical();
};
InterpolatedColorLegend.prototype._generateTicks = function (numSwatches) {
if (numSwatches === void 0) { numSwatches = InterpolatedColorLegend._DEFAULT_NUM_SWATCHES; }
var domain = this._scale.domain();
if (numSwatches === 1) {
return [domain[0]];
}
var slope = (domain[1] - domain[0]) / (numSwatches - 1);
var ticks = [];
for (var i = 0; i < numSwatches; i++) {
ticks.push(domain[0] + slope * i);
}
return ticks;
};
InterpolatedColorLegend.prototype._setup = function () {
_super.prototype._setup.call(this);
this._swatchContainer = this.content().append("g").classed("swatch-container", true);
this._swatchBoundingBox = this.content().append("rect").classed("swatch-bounding-box", true);
this._lowerLabel = this.content().append("g").classed(InterpolatedColorLegend.LEGEND_LABEL_CLASS, true);
this._upperLabel = this.content().append("g").classed(InterpolatedColorLegend.LEGEND_LABEL_CLASS, true);
this._measurer = new SVGTypewriter.Measurer(this.content());
this._wrapper = new SVGTypewriter.Wrapper();
this._writer = new SVGTypewriter.Writer(this._measurer, this._wrapper);
};
InterpolatedColorLegend.prototype.requestedSpace = function (offeredWidth, offeredHeight) {
var _this = this;
var textHeight = this._measurer.measure().height;
var padding = textHeight;
var domain = this._scale.domain();
var labelWidths = domain.map(function (d) { return _this._measurer.measure(_this._formatter(d)).width; });
var desiredHeight;
var desiredWidth;
var numSwatches = InterpolatedColorLegend._DEFAULT_NUM_SWATCHES;
if (this._isVertical()) {
var longestWidth = Plottable.Utils.Math.max(labelWidths, 0);
desiredWidth = padding + textHeight + this._textPadding + longestWidth + this._textPadding;
desiredHeight = numSwatches * textHeight;
}
else {
desiredHeight = padding + textHeight + padding;
desiredWidth = this._textPadding + labelWidths[0] + numSwatches * textHeight
+ labelWidths[1] + this._textPadding;
}
return {
minWidth: desiredWidth,
minHeight: desiredHeight,
};
};
InterpolatedColorLegend.prototype._isVertical = function () {
return this._orientation !== "horizontal";
};
InterpolatedColorLegend.prototype.renderImmediately = function () {
var _this = this;
_super.prototype.renderImmediately.call(this);
var domain = this._scale.domain();
var text0 = this._formatter(domain[0]);
var text0Width = this._measurer.measure(text0).width;
var text1 = this._formatter(domain[1]);
var text1Width = this._measurer.measure(text1).width;
var textHeight = this._measurer.measure().height;
var textPadding = this._textPadding;
var upperLabelShift = { x: 0, y: 0 };
var lowerLabelShift = { x: 0, y: 0 };
var lowerWriteOptions = {
selection: this._lowerLabel,
xAlign: "center",
yAlign: "center",
textRotation: 0,
};
var upperWriteOptions = {
selection: this._upperLabel,
xAlign: "center",
yAlign: "center",
textRotation: 0,
};
var swatchWidth;
var swatchHeight;
var swatchX;
var swatchY;
var boundingBoxAttr = {
x: 0,
y: 0,
width: 0,
height: 0,
};
var padding;
var numSwatches;
if (this._isVertical()) {
numSwatches = Math.floor(this.height());
var longestTextWidth_1 = Math.max(text0Width, text1Width);
padding = (this.width() - longestTextWidth_1 - 2 * this._textPadding) / 2;
swatchWidth = Math.max(this.width() - padding - 2 * textPadding - longestTextWidth_1, 0);
swatchHeight = 1;
swatchY = function (d, i) { return _this.height() - (i + 1); };
upperWriteOptions.yAlign = "top";
upperLabelShift.y = 0;
lowerWriteOptions.yAlign = "bottom";
lowerLabelShift.y = 0;
if (this._orientation === "left") {
swatchX = function (d, i) { return textPadding + longestTextWidth_1 + textPadding; };
upperWriteOptions.xAlign = "right";
upperLabelShift.x = -(padding + swatchWidth + textPadding);
lowerWriteOptions.xAlign = "right";
lowerLabelShift.x = -(padding + swatchWidth + textPadding);
}
else {
swatchX = function (d, i) { return padding; };
upperWriteOptions.xAlign = "left";
upperLabelShift.x = padding + swatchWidth + textPadding;
lowerWriteOptions.xAlign = "left";
lowerLabelShift.x = padding + swatchWidth + textPadding;
}
boundingBoxAttr["width"] = swatchWidth;
boundingBoxAttr["height"] = numSwatches * swatchHeight;
}
else {
padding = Math.max(textPadding, (this.height() - textHeight) / 2);
numSwatches = Math.max(Math.floor(this.width() - textPadding * 4 - text0Width - text1Width), 0);
swatchWidth = 1;
swatchHeight = Math.max((this.height() - 2 * padding), 0);
swatchX = function (d, i) { return Math.floor(text0Width + 2 * textPadding) + i; };
swatchY = function (d, i) { return padding; };
upperWriteOptions.xAlign = "right";
upperLabelShift.x = -textPadding;
lowerWriteOptions.xAlign = "left";
lowerLabelShift.x = textPadding;
boundingBoxAttr["y"] = padding;
boundingBoxAttr["width"] = numSwatches * swatchWidth;
boundingBoxAttr["height"] = swatchHeight;
}
boundingBoxAttr["x"] = swatchX(null, 0); // position of the first swatch
this._upperLabel.text(""); // clear the upper label
this._writer.write(text1, this.width(), this.height(), upperWriteOptions);
var upperTranslateString = "translate(" + upperLabelShift.x + ", " + upperLabelShift.y + ")";
this._upperLabel.attr("transform", upperTranslateString);
this._lowerLabel.text(""); // clear the lower label
this._writer.write(text0, this.width(), this.height(), lowerWriteOptions);
var lowerTranslateString = "translate(" + lowerLabelShift.x + ", " + lowerLabelShift.y + ")";
this._lowerLabel.attr("transform", lowerTranslateString);
this._swatchBoundingBox.attr(boundingBoxAttr);
var ticks = this._generateTicks(numSwatches);
var swatches = this._swatchContainer.selectAll("rect.swatch").data(ticks);
var rects = swatches.enter().append("rect").classed("swatch", true);
swatches.exit().remove();
swatches.attr({
"fill": function (d, i) { return _this._scale.scale(d); },
"width": swatchWidth,
"height": swatchHeight,
"x": swatchX,
"y": swatchY,
"shape-rendering": "crispEdges",
});
if (Plottable.Configs.ADD_TITLE_ELEMENTS) {
rects.append("title").text(function (d) { return _this._formatter(d); });
}
return this;
};
InterpolatedColorLegend._DEFAULT_NUM_SWATCHES = 11;
/**
* The css class applied to the legend labels.
*/
InterpolatedColorLegend.LEGEND_LABEL_CLASS = "legend-label";
return InterpolatedColorLegend;
}(Plottable.Component));
Components.InterpolatedColorLegend = InterpolatedColorLegend;
})(Components = Plottable.Components || (Plottable.Components = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Components;
(function (Components) {
var Gridlines = (function (_super) {
__extends(Gridlines, _super);
/**
* @constructor
* @param {QuantitativeScale} xScale The scale to base the x gridlines on. Pass null if no gridlines are desired.
* @param {QuantitativeScale} yScale The scale to base the y gridlines on. Pass null if no gridlines are desired.
*/
function Gridlines(xScale, yScale) {
var _this = this;
if (xScale != null && !(Plottable.QuantitativeScale.prototype.isPrototypeOf(xScale))) {
throw new Error("xScale needs to inherit from Scale.QuantitativeScale");
}
if (yScale != null && !(Plottable.QuantitativeScale.prototype.isPrototypeOf(yScale))) {
throw new Error("yScale needs to inherit from Scale.QuantitativeScale");
}
_super.call(this);
this.addClass("gridlines");
this._xScale = xScale;
this._yScale = yScale;
this._renderCallback = function (scale) { return _this.render(); };
if (this._xScale) {
this._xScale.onUpdate(this._renderCallback);
}
if (this._yScale) {
this._yScale.onUpdate(this._renderCallback);
}
}
Gridlines.prototype.destroy = function () {
_super.prototype.destroy.call(this);
if (this._xScale) {
this._xScale.offUpdate(this._renderCallback);
}
if (this._yScale) {
this._yScale.offUpdate(this._renderCallback);
}
return this;
};
Gridlines.prototype._setup = function () {
_super.prototype._setup.call(this);
this._xLinesContainer = this.content().append("g").classed("x-gridlines", true);
this._yLinesContainer = this.content().append("g").classed("y-gridlines", true);
};
Gridlines.prototype.renderImmediately = function () {
_super.prototype.renderImmediately.call(this);
this._redrawXLines();
this._redrawYLines();
return this;
};
Gridlines.prototype.computeLayout = function (origin, availableWidth, availableHeight) {
_super.prototype.computeLayout.call(this, origin, availableWidth, availableHeight);
if (this._xScale != null) {
this._xScale.range([0, this.width()]);
}
if (this._yScale != null) {
this._yScale.range([this.height(), 0]);
}
return this;
};
Gridlines.prototype._redrawXLines = function () {
var _this = this;
if (this._xScale) {
var xTicks = this._xScale.ticks();
var getScaledXValue = function (tickVal) { return _this._xScale.scale(tickVal); };
var xLines = this._xLinesContainer.selectAll("line").data(xTicks);
xLines.enter().append("line");
xLines.attr("x1", getScaledXValue)
.attr("y1", 0)
.attr("x2", getScaledXValue)
.attr("y2", this.height())
.classed("zeroline", function (t) { return t === 0; });
xLines.exit().remove();
}
};
Gridlines.prototype._redrawYLines = function () {
var _this = this;
if (this._yScale) {
var yTicks = this._yScale.ticks();
var getScaledYValue = function (tickVal) { return _this._yScale.scale(tickVal); };
var yLines = this._yLinesContainer.selectAll("line").data(yTicks);
yLines.enter().append("line");
yLines.attr("x1", 0)
.attr("y1", getScaledYValue)
.attr("x2", this.width())
.attr("y2", getScaledYValue)
.classed("zeroline", function (t) { return t === 0; });
yLines.exit().remove();
}
};
return Gridlines;
}(Plottable.Component));
Components.Gridlines = Gridlines;
})(Components = Plottable.Components || (Plottable.Components = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Components;
(function (Components) {
var Table = (function (_super) {
__extends(Table, _super);
/**
* A Table combines Components in the form of a grid. A
* common case is combining a y-axis, x-axis, and the plotted data via
* ```typescript
* new Table([[yAxis, plot],
* [null, xAxis]]);
* ```
*
* @constructor
* @param {Component[][]} [rows=[]] A 2-D array of Components to be added to the Table.
* null can be used if a cell is empty.
*/
function Table(rows) {
var _this = this;
if (rows === void 0) { rows = []; }
_super.call(this);
this._rowPadding = 0;
this._columnPadding = 0;
this._rows = [];
this._rowWeights = [];
this._columnWeights = [];
this._nRows = 0;
this._nCols = 0;
this._calculatedLayout = null;
this.addClass("table");
rows.forEach(function (row, rowIndex) {
row.forEach(function (component, colIndex) {
if (component != null) {
_this.add(component, rowIndex, colIndex);
}
});
});
}
Table.prototype._forEach = function (callback) {
for (var r = 0; r < this._nRows; r++) {
for (var c = 0; c < this._nCols; c++) {
if (this._rows[r][c] != null) {
callback(this._rows[r][c]);
}
}
}
};
/**
* Checks whether the specified Component is in the Table.
*/
Table.prototype.has = function (component) {
for (var r = 0; r < this._nRows; r++) {
for (var c = 0; c < this._nCols; c++) {
if (this._rows[r][c] === component) {
return true;
}
}
}
return false;
};
/**
* Returns the Component at the specified row and column index.
*
* @param {number} rowIndex
* @param {number} columnIndex
* @returns {Component} The Component at the specified position, or null if no Component is there.
*/
Table.prototype.componentAt = function (rowIndex, columnIndex) {
if (rowIndex < 0 || rowIndex >= this._nRows || columnIndex < 0 || columnIndex >= this._nCols) {
return null;
}
return this._rows[rowIndex][columnIndex];
};
;
/**
* Adds a Component in the specified row and column position.
*
* For example, instead of calling `new Table([[a, b], [null, c]])`, you
* could call
* var table = new Plottable.Components.Table();
* table.add(a, 0, 0);
* table.add(b, 0, 1);
* table.add(c, 1, 1);
*
* @param {Component} component The Component to be added.
* @param {number} row
* @param {number} col
* @returns {Table} The calling Table.
*/
Table.prototype.add = function (component, row, col) {
if (component == null) {
throw Error("Cannot add null to a table cell");
}
if (!this.has(component)) {
var currentComponent = this._rows[row] && this._rows[row][col];
if (currentComponent != null) {
throw new Error("cell is occupied");
}
component.detach();
this._nRows = Math.max(row + 1, this._nRows);
this._nCols = Math.max(col + 1, this._nCols);
this._padTableToSize(this._nRows, this._nCols);
this._rows[row][col] = component;
this._adoptAndAnchor(component);
this.redraw();
}
return this;
};
Table.prototype._remove = function (component) {
for (var r = 0; r < this._nRows; r++) {
for (var c = 0; c < this._nCols; c++) {
if (this._rows[r][c] === component) {
this._rows[r][c] = null;
return true;
}
}
}
return false;
};
Table.prototype._iterateLayout = function (availableWidth, availableHeight, isFinalOffer) {
if (isFinalOffer === void 0) { isFinalOffer = false; }
/*
* Given availableWidth and availableHeight, figure out how to allocate it between rows and columns using an iterative algorithm.
*
* For both dimensions, keeps track of "guaranteedSpace", which the fixed-size components have requested, and
* "proportionalSpace", which is being given to proportionally-growing components according to the weights on the table.
* Here is how it works (example uses width but it is the same for height). First, columns are guaranteed no width, and
* the free width is allocated to columns based on their colWeights. Then, in determineGuarantees, every component is
* offered its column's width and may request some amount of it, which increases that column's guaranteed
* width. If there are some components that were not satisfied with the width they were offered, and there is free
* width that has not already been guaranteed, then the remaining width is allocated to the unsatisfied columns and the
* algorithm runs again. If all components are satisfied, then the remaining width is allocated as proportional space
* according to the colWeights.
*
* The guaranteed width for each column is monotonically increasing as the algorithm iterates. Since it is deterministic
* and monotonically increasing, if the freeWidth does not change during an iteration it implies that no further progress
* is possible, so the algorithm will not continue iterating on that dimension's account.
*
* If the algorithm runs more than 5 times, we stop and just use whatever we arrived at. It's not clear under what
* circumstances this will happen or if it will happen at all. A message will be printed to the console if this occurs.
*
*/
var rows = this._rows;
var cols = d3.transpose(this._rows);
var availableWidthAfterPadding = availableWidth - this._columnPadding * (this._nCols - 1);
var availableHeightAfterPadding = availableHeight - this._rowPadding * (this._nRows - 1);
var rowWeights = Table._calcComponentWeights(this._rowWeights, rows, function (c) { return (c == null) || c.fixedHeight(); });
var colWeights = Table._calcComponentWeights(this._columnWeights, cols, function (c) { return (c == null) || c.fixedWidth(); });
// To give the table a good starting position to iterate from, we give the fixed-width components half-weight
// so that they will get some initial space allocated to work with
var heuristicColWeights = colWeights.map(function (c) { return c === 0 ? 0.5 : c; });
var heuristicRowWeights = rowWeights.map(function (c) { return c === 0 ? 0.5 : c; });
var colProportionalSpace = Table._calcProportionalSpace(heuristicColWeights, availableWidthAfterPadding);
var rowProportionalSpace = Table._calcProportionalSpace(heuristicRowWeights, availableHeightAfterPadding);
var guaranteedWidths = Plottable.Utils.Array.createFilledArray(0, this._nCols);
var guaranteedHeights = Plottable.Utils.Array.createFilledArray(0, this._nRows);
var freeWidth;
var freeHeight;
var nIterations = 0;
var guarantees;
var wantsWidth;
var wantsHeight;
while (true) {
var offeredHeights = Plottable.Utils.Array.add(guaranteedHeights, rowProportionalSpace);
var offeredWidths = Plottable.Utils.Array.add(guaranteedWidths, colProportionalSpace);
guarantees = this._determineGuarantees(offeredWidths, offeredHeights, isFinalOffer);
guaranteedWidths = guarantees.guaranteedWidths;
guaranteedHeights = guarantees.guaranteedHeights;
wantsWidth = guarantees.wantsWidthArr.some(function (x) { return x; });
wantsHeight = guarantees.wantsHeightArr.some(function (x) { return x; });
var lastFreeWidth = freeWidth;
var lastFreeHeight = freeHeight;
freeWidth = availableWidthAfterPadding - d3.sum(guarantees.guaranteedWidths);
freeHeight = availableHeightAfterPadding - d3.sum(guarantees.guaranteedHeights);
var xWeights = void 0;
if (wantsWidth) {
xWeights = guarantees.wantsWidthArr.map(function (x) { return x ? 0.1 : 0; });
xWeights = Plottable.Utils.Array.add(xWeights, colWeights);
}
else {
xWeights = colWeights;
}
var yWeights = void 0;
if (wantsHeight) {
yWeights = guarantees.wantsHeightArr.map(function (x) { return x ? 0.1 : 0; });
yWeights = Plottable.Utils.Array.add(yWeights, rowWeights);
}
else {
yWeights = rowWeights;
}
colProportionalSpace = Table._calcProportionalSpace(xWeights, freeWidth);
rowProportionalSpace = Table._calcProportionalSpace(yWeights, freeHeight);
nIterations++;
var canImproveWidthAllocation = freeWidth > 0 && freeWidth !== lastFreeWidth;
var canImproveHeightAllocation = freeHeight > 0 && freeHeight !== lastFreeHeight;
if (!(canImproveWidthAllocation || canImproveHeightAllocation)) {
break;
}
if (nIterations > 5) {
break;
}
}
// Redo the proportional space one last time, to ensure we use the real weights not the wantsWidth/Height weights
freeWidth = availableWidthAfterPadding - d3.sum(guarantees.guaranteedWidths);
freeHeight = availableHeightAfterPadding - d3.sum(guarantees.guaranteedHeights);
colProportionalSpace = Table._calcProportionalSpace(colWeights, freeWidth);
rowProportionalSpace = Table._calcProportionalSpace(rowWeights, freeHeight);
return {
colProportionalSpace: colProportionalSpace,
rowProportionalSpace: rowProportionalSpace,
guaranteedWidths: guarantees.guaranteedWidths,
guaranteedHeights: guarantees.guaranteedHeights,
wantsWidth: wantsWidth,
wantsHeight: wantsHeight,
};
};
Table.prototype._determineGuarantees = function (offeredWidths, offeredHeights, isFinalOffer) {
if (isFinalOffer === void 0) { isFinalOffer = false; }
var requestedWidths = Plottable.Utils.Array.createFilledArray(0, this._nCols);
var requestedHeights = Plottable.Utils.Array.createFilledArray(0, this._nRows);
var columnNeedsWidth = Plottable.Utils.Array.createFilledArray(false, this._nCols);
var rowNeedsHeight = Plottable.Utils.Array.createFilledArray(false, this._nRows);
this._rows.forEach(function (row, rowIndex) {
row.forEach(function (component, colIndex) {
var spaceRequest;
if (component != null) {
spaceRequest = component.requestedSpace(offeredWidths[colIndex], offeredHeights[rowIndex]);
}
else {
spaceRequest = {
minWidth: 0,
minHeight: 0,
};
}
var columnWidth = isFinalOffer ? Math.min(spaceRequest.minWidth, offeredWidths[colIndex]) : spaceRequest.minWidth;
requestedWidths[colIndex] = Math.max(requestedWidths[colIndex], columnWidth);
var rowHeight = isFinalOffer ? Math.min(spaceRequest.minHeight, offeredHeights[rowIndex]) : spaceRequest.minHeight;
requestedHeights[rowIndex] = Math.max(requestedHeights[rowIndex], rowHeight);
var componentNeedsWidth = spaceRequest.minWidth > offeredWidths[colIndex];
columnNeedsWidth[colIndex] = columnNeedsWidth[colIndex] || componentNeedsWidth;
var componentNeedsHeight = spaceRequest.minHeight > offeredHeights[rowIndex];
rowNeedsHeight[rowIndex] = rowNeedsHeight[rowIndex] || componentNeedsHeight;
});
});
return {
guaranteedWidths: requestedWidths,
guaranteedHeights: requestedHeights,
wantsWidthArr: columnNeedsWidth,
wantsHeightArr: rowNeedsHeight,
};
};
Table.prototype.requestedSpace = function (offeredWidth, offeredHeight) {
this._calculatedLayout = this._iterateLayout(offeredWidth, offeredHeight);
return {
minWidth: d3.sum(this._calculatedLayout.guaranteedWidths),
minHeight: d3.sum(this._calculatedLayout.guaranteedHeights),
};
};
Table.prototype.computeLayout = function (origin, availableWidth, availableHeight) {
var _this = this;
_super.prototype.computeLayout.call(this, origin, availableWidth, availableHeight);
var lastLayoutWidth = d3.sum(this._calculatedLayout.guaranteedWidths);
var lastLayoutHeight = d3.sum(this._calculatedLayout.guaranteedHeights);
var layout = this._calculatedLayout;
if (lastLayoutWidth > this.width() || lastLayoutHeight > this.height()) {
layout = this._iterateLayout(this.width(), this.height(), true);
}
var childYOrigin = 0;
var rowHeights = Plottable.Utils.Array.add(layout.rowProportionalSpace, layout.guaranteedHeights);
var colWidths = Plottable.Utils.Array.add(layout.colProportionalSpace, layout.guaranteedWidths);
this._rows.forEach(function (row, rowIndex) {
var childXOrigin = 0;
row.forEach(function (component, colIndex) {
// recursively compute layout
if (component != null) {
component.computeLayout({ x: childXOrigin, y: childYOrigin }, colWidths[colIndex], rowHeights[rowIndex]);
}
childXOrigin += colWidths[colIndex] + _this._columnPadding;
});
childYOrigin += rowHeights[rowIndex] + _this._rowPadding;
});
return this;
};
Table.prototype.rowPadding = function (rowPadding) {
if (rowPadding == null) {
return this._rowPadding;
}
if (!Plottable.Utils.Math.isValidNumber(rowPadding) || rowPadding < 0) {
throw Error("rowPadding must be a non-negative finite value");
}
this._rowPadding = rowPadding;
this.redraw();
return this;
};
Table.prototype.columnPadding = function (columnPadding) {
if (columnPadding == null) {
return this._columnPadding;
}
if (!Plottable.Utils.Math.isValidNumber(columnPadding) || columnPadding < 0) {
throw Error("columnPadding must be a non-negative finite value");
}
this._columnPadding = columnPadding;
this.redraw();
return this;
};
Table.prototype.rowWeight = function (index, weight) {
if (weight == null) {
return this._rowWeights[index];
}
if (!Plottable.Utils.Math.isValidNumber(weight) || weight < 0) {
throw Error("rowWeight must be a non-negative finite value");
}
this._rowWeights[index] = weight;
this.redraw();
return this;
};
Table.prototype.columnWeight = function (index, weight) {
if (weight == null) {
return this._columnWeights[index];
}
if (!Plottable.Utils.Math.isValidNumber(weight) || weight < 0) {
throw Error("columnWeight must be a non-negative finite value");
}
this._columnWeights[index] = weight;
this.redraw();
return this;
};
Table.prototype.fixedWidth = function () {
var cols = d3.transpose(this._rows);
return Table._fixedSpace(cols, function (c) { return (c == null) || c.fixedWidth(); });
};
Table.prototype.fixedHeight = function () {
return Table._fixedSpace(this._rows, function (c) { return (c == null) || c.fixedHeight(); });
};
Table.prototype._padTableToSize = function (nRows, nCols) {
for (var i = 0; i < nRows; i++) {
if (this._rows[i] === undefined) {
this._rows[i] = [];
this._rowWeights[i] = null;
}
for (var j = 0; j < nCols; j++) {
if (this._rows[i][j] === undefined) {
this._rows[i][j] = null;
}
}
}
for (var j = 0; j < nCols; j++) {
if (this._columnWeights[j] === undefined) {
this._columnWeights[j] = null;
}
}
};
Table._calcComponentWeights = function (setWeights, componentGroups, fixityAccessor) {
// If the row/col weight was explicitly set, then return it outright
// If the weight was not explicitly set, then guess it using the heuristic that if all components are fixed-space
// then weight is 0, otherwise weight is 1
return setWeights.map(function (w, i) {
if (w != null) {
return w;
}
var fixities = componentGroups[i].map(fixityAccessor);
var allFixed = fixities.reduce(function (a, b) { return a && b; }, true);
return allFixed ? 0 : 1;
});
};
Table._calcProportionalSpace = function (weights, freeSpace) {
var weightSum = d3.sum(weights);
if (weightSum === 0) {
return Plottable.Utils.Array.createFilledArray(0, weights.length);
}
else {
return weights.map(function (w) { return freeSpace * w / weightSum; });
}
};
Table._fixedSpace = function (componentGroup, fixityAccessor) {
var all = function (bools) { return bools.reduce(function (a, b) { return a && b; }, true); };
var groupIsFixed = function (components) { return all(components.map(fixityAccessor)); };
return all(componentGroup.map(groupIsFixed));
};
return Table;
}(Plottable.ComponentContainer));
Components.Table = Table;
})(Components = Plottable.Components || (Plottable.Components = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Components;
(function (Components) {
(function (PropertyMode) {
PropertyMode[PropertyMode["VALUE"] = 0] = "VALUE";
PropertyMode[PropertyMode["PIXEL"] = 1] = "PIXEL";
})(Components.PropertyMode || (Components.PropertyMode = {}));
var PropertyMode = Components.PropertyMode;
;
var SelectionBoxLayer = (function (_super) {
__extends(SelectionBoxLayer, _super);
function SelectionBoxLayer() {
var _this = this;
_super.call(this);
this._boxVisible = false;
this._boxBounds = {
topLeft: { x: 0, y: 0 },
bottomRight: { x: 0, y: 0 },
};
this._xBoundsMode = PropertyMode.PIXEL;
this._yBoundsMode = PropertyMode.PIXEL;
this.addClass("selection-box-layer");
this._adjustBoundsCallback = function () {
_this.render();
};
this._clipPathEnabled = true;
this._xExtent = [undefined, undefined];
this._yExtent = [undefined, undefined];
}
SelectionBoxLayer.prototype._setup = function () {
_super.prototype._setup.call(this);
this._box = this.content().append("g").classed("selection-box", true).remove();
this._boxArea = this._box.append("rect").classed("selection-area", true);
};
SelectionBoxLayer.prototype._sizeFromOffer = function (availableWidth, availableHeight) {
return {
width: availableWidth,
height: availableHeight,
};
};
SelectionBoxLayer.prototype.bounds = function (newBounds) {
if (newBounds == null) {
return this._getBounds();
}
this._setBounds(newBounds);
this._xBoundsMode = PropertyMode.PIXEL;
this._yBoundsMode = PropertyMode.PIXEL;
this.render();
return this;
};
SelectionBoxLayer.prototype._setBounds = function (newBounds) {
var topLeft = {
x: Math.min(newBounds.topLeft.x, newBounds.bottomRight.x),
y: Math.min(newBounds.topLeft.y, newBounds.bottomRight.y),
};
var bottomRight = {
x: Math.max(newBounds.topLeft.x, newBounds.bottomRight.x),
y: Math.max(newBounds.topLeft.y, newBounds.bottomRight.y),
};
this._boxBounds = {
topLeft: topLeft,
bottomRight: bottomRight,
};
};
SelectionBoxLayer.prototype._getBounds = function () {
return {
topLeft: {
x: this._xBoundsMode === PropertyMode.PIXEL ?
this._boxBounds.topLeft.x :
(this._xScale == null ?
0 :
Math.min(this.xScale().scale(this.xExtent()[0]), this.xScale().scale(this.xExtent()[1]))),
y: this._yBoundsMode === PropertyMode.PIXEL ?
this._boxBounds.topLeft.y :
(this._yScale == null ?
0 :
Math.min(this.yScale().scale(this.yExtent()[0]), this.yScale().scale(this.yExtent()[1]))),
},
bottomRight: {
x: this._xBoundsMode === PropertyMode.PIXEL ?
this._boxBounds.bottomRight.x :
(this._xScale == null ?
0 :
Math.max(this.xScale().scale(this.xExtent()[0]), this.xScale().scale(this.xExtent()[1]))),
y: this._yBoundsMode === PropertyMode.PIXEL ?
this._boxBounds.bottomRight.y :
(this._yScale == null ?
0 :
Math.max(this.yScale().scale(this.yExtent()[0]), this.yScale().scale(this.yExtent()[1]))),
},
};
};
SelectionBoxLayer.prototype.renderImmediately = function () {
_super.prototype.renderImmediately.call(this);
if (this._boxVisible) {
var bounds = this.bounds();
var t = bounds.topLeft.y;
var b = bounds.bottomRight.y;
var l = bounds.topLeft.x;
var r = bounds.bottomRight.x;
if (!(Plottable.Utils.Math.isValidNumber(t) &&
Plottable.Utils.Math.isValidNumber(b) &&
Plottable.Utils.Math.isValidNumber(l) &&
Plottable.Utils.Math.isValidNumber(r))) {
throw new Error("bounds have not been properly set");
}
this._boxArea.attr({
x: l, y: t, width: r - l, height: b - t,
});
this.content().node().appendChild(this._box.node());
}
else {
this._box.remove();
}
return this;
};
SelectionBoxLayer.prototype.boxVisible = function (show) {
if (show == null) {
return this._boxVisible;
}
this._boxVisible = show;
this.render();
return this;
};
SelectionBoxLayer.prototype.fixedWidth = function () {
return true;
};
SelectionBoxLayer.prototype.fixedHeight = function () {
return true;
};
SelectionBoxLayer.prototype.xScale = function (xScale) {
if (xScale == null) {
return this._xScale;
}
if (this._xScale != null) {
this._xScale.offUpdate(this._adjustBoundsCallback);
}
this._xScale = xScale;
this._xBoundsMode = PropertyMode.VALUE;
this._xScale.onUpdate(this._adjustBoundsCallback);
this.render();
return this;
};
SelectionBoxLayer.prototype.yScale = function (yScale) {
if (yScale == null) {
return this._yScale;
}
if (this._yScale != null) {
this._yScale.offUpdate(this._adjustBoundsCallback);
}
this._yScale = yScale;
this._yBoundsMode = PropertyMode.VALUE;
this._yScale.onUpdate(this._adjustBoundsCallback);
this.render();
return this;
};
SelectionBoxLayer.prototype.xExtent = function (xExtent) {
// Explicit typing for Typescript 1.4
if (xExtent == null) {
return this._getXExtent();
}
this._setXExtent(xExtent);
this._xBoundsMode = PropertyMode.VALUE;
this.render();
return this;
};
SelectionBoxLayer.prototype._getXExtent = function () {
return this._xBoundsMode === PropertyMode.VALUE ?
this._xExtent :
(this._xScale == null ?
[undefined, undefined] :
[
this._xScale.invert(this._boxBounds.topLeft.x),
this._xScale.invert(this._boxBounds.bottomRight.x),
]);
};
SelectionBoxLayer.prototype._setXExtent = function (xExtent) {
this._xExtent = xExtent;
};
SelectionBoxLayer.prototype.yExtent = function (yExtent) {
// Explicit typing for Typescript 1.4
if (yExtent == null) {
return this._getYExtent();
}
this._setYExtent(yExtent);
this._yBoundsMode = PropertyMode.VALUE;
this.render();
return this;
};
SelectionBoxLayer.prototype._getYExtent = function () {
return this._yBoundsMode === PropertyMode.VALUE ?
this._yExtent :
(this._yScale == null ?
[undefined, undefined] :
[
this._yScale.invert(this._boxBounds.topLeft.y),
this._yScale.invert(this._boxBounds.bottomRight.y),
]);
};
SelectionBoxLayer.prototype._setYExtent = function (yExtent) {
this._yExtent = yExtent;
};
SelectionBoxLayer.prototype.destroy = function () {
_super.prototype.destroy.call(this);
if (this._xScale != null) {
this.xScale().offUpdate(this._adjustBoundsCallback);
}
if (this._yScale != null) {
this.yScale().offUpdate(this._adjustBoundsCallback);
}
};
return SelectionBoxLayer;
}(Plottable.Component));
Components.SelectionBoxLayer = SelectionBoxLayer;
})(Components = Plottable.Components || (Plottable.Components = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Components;
(function (Components) {
var PropertyMode;
(function (PropertyMode) {
PropertyMode[PropertyMode["VALUE"] = 0] = "VALUE";
PropertyMode[PropertyMode["PIXEL"] = 1] = "PIXEL";
})(PropertyMode || (PropertyMode = {}));
;
var GuideLineLayer = (function (_super) {
__extends(GuideLineLayer, _super);
function GuideLineLayer(orientation) {
var _this = this;
_super.call(this);
this._mode = PropertyMode.VALUE;
if (orientation !== GuideLineLayer.ORIENTATION_VERTICAL && orientation !== GuideLineLayer.ORIENTATION_HORIZONTAL) {
throw new Error(orientation + " is not a valid orientation for GuideLineLayer");
}
this._orientation = orientation;
this._clipPathEnabled = true;
this.addClass("guide-line-layer");
if (this._isVertical()) {
this.addClass("vertical");
}
else {
this.addClass("horizontal");
}
this._scaleUpdateCallback = function () {
_this._syncPixelPositionAndValue();
_this.render();
};
}
GuideLineLayer.prototype._setup = function () {
_super.prototype._setup.call(this);
this._guideLine = this.content().append("line").classed("guide-line", true);
};
GuideLineLayer.prototype._sizeFromOffer = function (availableWidth, availableHeight) {
return {
width: availableWidth,
height: availableHeight,
};
};
GuideLineLayer.prototype._isVertical = function () {
return this._orientation === GuideLineLayer.ORIENTATION_VERTICAL;
};
GuideLineLayer.prototype.fixedWidth = function () {
return true;
};
GuideLineLayer.prototype.fixedHeight = function () {
return true;
};
GuideLineLayer.prototype.computeLayout = function (origin, availableWidth, availableHeight) {
_super.prototype.computeLayout.call(this, origin, availableWidth, availableHeight);
if (this.scale() != null) {
if (this._isVertical()) {
this.scale().range([0, this.width()]);
}
else {
this.scale().range([this.height(), 0]);
}
}
return this;
};
GuideLineLayer.prototype.renderImmediately = function () {
_super.prototype.renderImmediately.call(this);
this._syncPixelPositionAndValue();
this._guideLine.attr({
x1: this._isVertical() ? this.pixelPosition() : 0,
y1: this._isVertical() ? 0 : this.pixelPosition(),
x2: this._isVertical() ? this.pixelPosition() : this.width(),
y2: this._isVertical() ? this.height() : this.pixelPosition(),
});
return this;
};
// sets pixelPosition() or value() based on the other, depending on which was the last one set
GuideLineLayer.prototype._syncPixelPositionAndValue = function () {
if (this.scale() == null) {
return;
}
if (this._mode === PropertyMode.VALUE && this.value() != null) {
this._pixelPosition = this.scale().scale(this.value());
}
else if (this._mode === PropertyMode.PIXEL && this.pixelPosition() != null) {
this._value = this.scale().invert(this.pixelPosition());
}
};
GuideLineLayer.prototype._setPixelPositionWithoutChangingMode = function (pixelPosition) {
this._pixelPosition = pixelPosition;
if (this.scale() != null) {
this._value = this.scale().invert(this.pixelPosition());
}
this.render();
};
GuideLineLayer.prototype.scale = function (scale) {
if (scale == null) {
return this._scale;
}
var previousScale = this._scale;
if (previousScale != null) {
previousScale.offUpdate(this._scaleUpdateCallback);
}
this._scale = scale;
this._scale.onUpdate(this._scaleUpdateCallback);
this._syncPixelPositionAndValue();
this.redraw();
return this;
};
GuideLineLayer.prototype.value = function (value) {
if (value == null) {
return this._value;
}
this._value = value;
this._mode = PropertyMode.VALUE;
this._syncPixelPositionAndValue();
this.render();
return this;
};
GuideLineLayer.prototype.pixelPosition = function (pixelPosition) {
if (pixelPosition == null) {
return this._pixelPosition;
}
if (!Plottable.Utils.Math.isValidNumber(pixelPosition)) {
throw new Error("pixelPosition must be a finite number");
}
this._pixelPosition = pixelPosition;
this._mode = PropertyMode.PIXEL;
this._syncPixelPositionAndValue();
this.render();
return this;
};
GuideLineLayer.prototype.destroy = function () {
_super.prototype.destroy.call(this);
if (this.scale() != null) {
this.scale().offUpdate(this._scaleUpdateCallback);
}
};
GuideLineLayer.ORIENTATION_VERTICAL = "vertical";
GuideLineLayer.ORIENTATION_HORIZONTAL = "horizontal";
return GuideLineLayer;
}(Plottable.Component));
Components.GuideLineLayer = GuideLineLayer;
})(Components = Plottable.Components || (Plottable.Components = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Plots;
(function (Plots) {
var Animator;
(function (Animator) {
Animator.MAIN = "main";
Animator.RESET = "reset";
})(Animator = Plots.Animator || (Plots.Animator = {}));
})(Plots = Plottable.Plots || (Plottable.Plots = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Plot = (function (_super) {
__extends(Plot, _super);
/**
* A Plot draws some visualization of the inputted Datasets.
*
* @constructor
*/
function Plot() {
var _this = this;
_super.call(this);
this._dataChanged = false;
this._animate = false;
this._animators = {};
this._clipPathEnabled = true;
this.addClass("plot");
this._datasetToDrawer = new Plottable.Utils.Map();
this._attrBindings = d3.map();
this._attrExtents = d3.map();
this._includedValuesProvider = function (scale) { return _this._includedValuesForScale(scale); };
this._renderCallback = function (scale) { return _this.render(); };
this._onDatasetUpdateCallback = function () { return _this._onDatasetUpdate(); };
this._propertyBindings = d3.map();
this._propertyExtents = d3.map();
var mainAnimator = new Plottable.Animators.Easing().maxTotalDuration(Plot._ANIMATION_MAX_DURATION);
this.animator(Plottable.Plots.Animator.MAIN, mainAnimator);
this.animator(Plottable.Plots.Animator.RESET, new Plottable.Animators.Null());
}
Plot.prototype.anchor = function (selection) {
_super.prototype.anchor.call(this, selection);
this._dataChanged = true;
this._cachedEntityStore = undefined;
this._updateExtents();
return this;
};
Plot.prototype._setup = function () {
var _this = this;
_super.prototype._setup.call(this);
this._renderArea = this.content().append("g").classed("render-area", true);
this.datasets().forEach(function (dataset) { return _this._createNodesForDataset(dataset); });
};
Plot.prototype.destroy = function () {
var _this = this;
_super.prototype.destroy.call(this);
this._scales().forEach(function (scale) { return scale.offUpdate(_this._renderCallback); });
this.datasets([]);
};
Plot.prototype._createNodesForDataset = function (dataset) {
var drawer = this._datasetToDrawer.get(dataset);
drawer.renderArea(this._renderArea.append("g"));
return drawer;
};
Plot.prototype._createDrawer = function (dataset) {
return new Plottable.Drawer(dataset);
};
Plot.prototype._getAnimator = function (key) {
if (this._animateOnNextRender()) {
return this._animators[key] || new Plottable.Animators.Null();
}
else {
return new Plottable.Animators.Null();
}
};
Plot.prototype._onDatasetUpdate = function () {
this._updateExtents();
this._dataChanged = true;
this._cachedEntityStore = undefined;
this.render();
};
Plot.prototype.attr = function (attr, attrValue, scale) {
if (attrValue == null) {
return this._attrBindings.get(attr);
}
this._bindAttr(attr, attrValue, scale);
this.render(); // queue a re-render upon changing projector
return this;
};
Plot.prototype._bindProperty = function (property, value, scale) {
var binding = this._propertyBindings.get(property);
var oldScale = binding != null ? binding.scale : null;
this._propertyBindings.set(property, { accessor: d3.functor(value), scale: scale });
this._updateExtentsForProperty(property);
if (oldScale != null) {
this._uninstallScaleForKey(oldScale, property);
}
if (scale != null) {
this._installScaleForKey(scale, property);
}
};
Plot.prototype._bindAttr = function (attr, value, scale) {
var binding = this._attrBindings.get(attr);
var oldScale = binding != null ? binding.scale : null;
this._attrBindings.set(attr, { accessor: d3.functor(value), scale: scale });
this._updateExtentsForAttr(attr);
if (oldScale != null) {
this._uninstallScaleForKey(oldScale, attr);
}
if (scale != null) {
this._installScaleForKey(scale, attr);
}
};
Plot.prototype._generateAttrToProjector = function () {
var h = {};
this._attrBindings.forEach(function (attr, binding) {
var accessor = binding.accessor;
var scale = binding.scale;
var fn = scale ? function (d, i, dataset) { return scale.scale(accessor(d, i, dataset)); } : accessor;
h[attr] = fn;
});
var propertyProjectors = this._propertyProjectors();
Object.keys(propertyProjectors).forEach(function (key) {
if (h[key] == null) {
h[key] = propertyProjectors[key];
}
});
return h;
};
Plot.prototype.renderImmediately = function () {
_super.prototype.renderImmediately.call(this);
if (this._isAnchored) {
this._paint();
this._dataChanged = false;
}
return this;
};
Plot.prototype.animated = function (willAnimate) {
if (willAnimate == null) {
return this._animate;
}
this._animate = willAnimate;
return this;
};
Plot.prototype.detach = function () {
_super.prototype.detach.call(this);
// make the domain resize
this._updateExtents();
return this;
};
/**
* @returns {Scale[]} A unique array of all scales currently used by the Plot.
*/
Plot.prototype._scales = function () {
var scales = [];
this._attrBindings.forEach(function (attr, binding) {
var scale = binding.scale;
if (scale != null && scales.indexOf(scale) === -1) {
scales.push(scale);
}
});
this._propertyBindings.forEach(function (property, binding) {
var scale = binding.scale;
if (scale != null && scales.indexOf(scale) === -1) {
scales.push(scale);
}
});
return scales;
};
/**
* Updates the extents associated with each attribute, then autodomains all scales the Plot uses.
*/
Plot.prototype._updateExtents = function () {
var _this = this;
this._attrBindings.forEach(function (attr) { return _this._updateExtentsForAttr(attr); });
this._propertyExtents.forEach(function (property) { return _this._updateExtentsForProperty(property); });
this._scales().forEach(function (scale) { return scale.addIncludedValuesProvider(_this._includedValuesProvider); });
};
Plot.prototype._updateExtentsForAttr = function (attr) {
// Filters should never be applied to attributes
this._updateExtentsForKey(attr, this._attrBindings, this._attrExtents, null);
};
Plot.prototype._updateExtentsForProperty = function (property) {
this._updateExtentsForKey(property, this._propertyBindings, this._propertyExtents, this._filterForProperty(property));
};
Plot.prototype._filterForProperty = function (property) {
return null;
};
Plot.prototype._updateExtentsForKey = function (key, bindings, extents, filter) {
var _this = this;
var accScaleBinding = bindings.get(key);
if (accScaleBinding == null || accScaleBinding.accessor == null) {
return;
}
extents.set(key, this.datasets().map(function (dataset) { return _this._computeExtent(dataset, accScaleBinding, filter); }));
};
Plot.prototype._computeExtent = function (dataset, accScaleBinding, filter) {
var accessor = accScaleBinding.accessor;
var scale = accScaleBinding.scale;
if (scale == null) {
return [];
}
var data = dataset.data();
if (filter != null) {
data = data.filter(function (d, i) { return filter(d, i, dataset); });
}
var appliedAccessor = function (d, i) { return accessor(d, i, dataset); };
var mappedData = data.map(appliedAccessor);
return scale.extentOfValues(mappedData);
};
/**
* Override in subclass to add special extents, such as included values
*/
Plot.prototype._extentsForProperty = function (property) {
return this._propertyExtents.get(property);
};
Plot.prototype._includedValuesForScale = function (scale) {
var _this = this;
if (!this._isAnchored) {
return [];
}
var includedValues = [];
this._attrBindings.forEach(function (attr, binding) {
if (binding.scale === scale) {
var extents = _this._attrExtents.get(attr);
if (extents != null) {
includedValues = includedValues.concat(d3.merge(extents));
}
}
});
this._propertyBindings.forEach(function (property, binding) {
if (binding.scale === scale) {
var extents = _this._extentsForProperty(property);
if (extents != null) {
includedValues = includedValues.concat(d3.merge(extents));
}
}
});
return includedValues;
};
Plot.prototype.animator = function (animatorKey, animator) {
if (animator === undefined) {
return this._animators[animatorKey];
}
else {
this._animators[animatorKey] = animator;
return this;
}
};
/**
* Adds a Dataset to the Plot.
*
* @param {Dataset} dataset
* @returns {Plot} The calling Plot.
*/
Plot.prototype.addDataset = function (dataset) {
this._addDataset(dataset);
this._onDatasetUpdate();
return this;
};
Plot.prototype._addDataset = function (dataset) {
this._removeDataset(dataset);
var drawer = this._createDrawer(dataset);
this._datasetToDrawer.set(dataset, drawer);
if (this._isSetup) {
this._createNodesForDataset(dataset);
}
dataset.onUpdate(this._onDatasetUpdateCallback);
return this;
};
/**
* Removes a Dataset from the Plot.
*
* @param {Dataset} dataset
* @returns {Plot} The calling Plot.
*/
Plot.prototype.removeDataset = function (dataset) {
this._removeDataset(dataset);
this._onDatasetUpdate();
return this;
};
Plot.prototype._removeDataset = function (dataset) {
if (this.datasets().indexOf(dataset) === -1) {
return this;
}
this._removeDatasetNodes(dataset);
dataset.offUpdate(this._onDatasetUpdateCallback);
this._datasetToDrawer.delete(dataset);
return this;
};
Plot.prototype._removeDatasetNodes = function (dataset) {
var drawer = this._datasetToDrawer.get(dataset);
drawer.remove();
};
Plot.prototype.datasets = function (datasets) {
var _this = this;
var currentDatasets = [];
this._datasetToDrawer.forEach(function (drawer, dataset) { return currentDatasets.push(dataset); });
if (datasets == null) {
return currentDatasets;
}
currentDatasets.forEach(function (dataset) { return _this._removeDataset(dataset); });
datasets.forEach(function (dataset) { return _this._addDataset(dataset); });
this._onDatasetUpdate();
return this;
};
Plot.prototype._getDrawersInOrder = function () {
var _this = this;
return this.datasets().map(function (dataset) { return _this._datasetToDrawer.get(dataset); });
};
Plot.prototype._generateDrawSteps = function () {
return [{ attrToProjector: this._generateAttrToProjector(), animator: new Plottable.Animators.Null() }];
};
Plot.prototype._additionalPaint = function (time) {
// no-op
};
/**
* _buildLightweightPlotEntities constucts {LightweightPlotEntity[]} from
* all the entities in the plot
* @param {Dataset[]} [datasets] - datasets comprising this plot
*/
Plot.prototype._buildLightweightPlotEntities = function (datasets) {
var _this = this;
var lightweightPlotEntities = [];
datasets.forEach(function (dataset, datasetIndex) {
var drawer = _this._datasetToDrawer.get(dataset);
var validDatumIndex = 0;
dataset.data().forEach(function (datum, datumIndex) {
var position = _this._pixelPoint(datum, datumIndex, dataset);
if (Plottable.Utils.Math.isNaN(position.x) || Plottable.Utils.Math.isNaN(position.y)) {
return;
}
lightweightPlotEntities.push({
datum: datum,
position: position,
index: datumIndex,
dataset: dataset,
datasetIndex: datasetIndex,
component: _this,
drawer: drawer,
validDatumIndex: validDatumIndex,
});
validDatumIndex++;
});
});
return lightweightPlotEntities;
};
Plot.prototype._getDataToDraw = function () {
var dataToDraw = new Plottable.Utils.Map();
this.datasets().forEach(function (dataset) { return dataToDraw.set(dataset, dataset.data()); });
return dataToDraw;
};
Plot.prototype._paint = function () {
var drawSteps = this._generateDrawSteps();
var dataToDraw = this._getDataToDraw();
var drawers = this._getDrawersInOrder();
this.datasets().forEach(function (ds, i) { return drawers[i].draw(dataToDraw.get(ds), drawSteps); });
var times = this.datasets().map(function (ds, i) { return drawers[i].totalDrawTime(dataToDraw.get(ds), drawSteps); });
var maxTime = Plottable.Utils.Math.max(times, 0);
this._additionalPaint(maxTime);
};
/**
* Retrieves Selections of this Plot for the specified Datasets.
*
* @param {Dataset[]} [datasets] The Datasets to retrieve the Selections for.
* If not provided, Selections will be retrieved for all Datasets on the Plot.
* @returns {d3.Selection}
*/
Plot.prototype.selections = function (datasets) {
var _this = this;
if (datasets === void 0) { datasets = this.datasets(); }
var selections = [];
datasets.forEach(function (dataset) {
var drawer = _this._datasetToDrawer.get(dataset);
if (drawer == null) {
return;
}
drawer.renderArea().selectAll(drawer.selector()).each(function () {
selections.push(this);
});
});
return d3.selectAll(selections);
};
/**
* Gets the Entities associated with the specified Datasets.
*
* @param {Dataset[]} datasets The Datasets to retrieve the Entities for.
* If not provided, returns defaults to all Datasets on the Plot.
* @return {Plots.PlotEntity[]}
*/
Plot.prototype.entities = function (datasets) {
var _this = this;
return this._getEntityStore(datasets).map(function (entity) { return _this._lightweightPlotEntityToPlotEntity(entity); });
};
/**
* _getEntityStore returns the store of all Entities associated with the specified dataset
*
* @param {Dataset[]} [datasets] - The datasets with which to construct the store. If no datasets
* are specified all datasets will be used.
*/
Plot.prototype._getEntityStore = function (datasets) {
var _this = this;
if (datasets !== undefined) {
var EntityStore_1 = new Plottable.Utils.EntityArray();
this._buildLightweightPlotEntities(datasets).forEach(function (entity) {
EntityStore_1.add(entity);
});
return EntityStore_1;
}
else if (this._cachedEntityStore === undefined) {
this._cachedEntityStore = new Plottable.Utils.EntityArray();
this._buildLightweightPlotEntities(this.datasets()).forEach(function (entity) {
_this._cachedEntityStore.add(entity);
});
}
return this._cachedEntityStore;
};
Plot.prototype._lightweightPlotEntityToPlotEntity = function (entity) {
var plotEntity = {
datum: entity.datum,
position: this._pixelPoint(entity.datum, entity.index, entity.dataset),
dataset: entity.dataset,
datasetIndex: entity.datasetIndex,
index: entity.index,
component: entity.component,
selection: entity.drawer.selectionForIndex(entity.validDatumIndex),
};
return plotEntity;
};
/**
* Gets the PlotEntities at a particular Point.
*
* Each plot type determines how to locate entities at or near the query
* point. For example, line and area charts will return the nearest entity,
* but bar charts will only return the entities that fully contain the query
* point.
*
* @param {Point} point The point to query.
* @returns {PlotEntity[]} The PlotEntities at the particular point
*/
Plot.prototype.entitiesAt = function (point) {
throw new Error("plots must implement entitiesAt");
};
/**
* Returns the {Plots.PlotEntity} nearest to the query point,
* or undefined if no {Plots.PlotEntity} can be found.
*
* @param {Point} queryPoint
* @param {bounds} Bounds The bounding box within which to search. By default, bounds is the bounds of
* the chart, relative to the parent.
* @returns {Plots.PlotEntity} The nearest PlotEntity, or undefined if no {Plots.PlotEntity} can be found.
*/
Plot.prototype.entityNearest = function (queryPoint, bounds) {
var _this = this;
if (bounds === void 0) { bounds = this.bounds(); }
var nearest = this._getEntityStore().entityNearest(queryPoint, function (entity) {
return _this._entityVisibleOnPlot(entity, bounds);
});
return nearest === undefined ? undefined : this._lightweightPlotEntityToPlotEntity(nearest);
};
Plot.prototype._entityVisibleOnPlot = function (entity, chartBounds) {
return !(entity.position.x < chartBounds.topLeft.x || entity.position.y < chartBounds.topLeft.y ||
entity.position.x > chartBounds.bottomRight.x || entity.position.y > chartBounds.bottomRight.y);
};
Plot.prototype._uninstallScaleForKey = function (scale, key) {
scale.offUpdate(this._renderCallback);
scale.removeIncludedValuesProvider(this._includedValuesProvider);
};
Plot.prototype._installScaleForKey = function (scale, key) {
scale.onUpdate(this._renderCallback);
scale.addIncludedValuesProvider(this._includedValuesProvider);
};
Plot.prototype._propertyProjectors = function () {
return {};
};
Plot._scaledAccessor = function (binding) {
return binding.scale == null ?
binding.accessor :
function (d, i, ds) { return binding.scale.scale(binding.accessor(d, i, ds)); };
};
Plot.prototype._pixelPoint = function (datum, index, dataset) {
return { x: 0, y: 0 };
};
Plot.prototype._animateOnNextRender = function () {
return this._animate && this._dataChanged;
};
Plot._ANIMATION_MAX_DURATION = 600;
return Plot;
}(Plottable.Component));
Plottable.Plot = Plot;
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Plots;
(function (Plots) {
var Pie = (function (_super) {
__extends(Pie, _super);
/**
* @constructor
*/
function Pie() {
var _this = this;
_super.call(this);
this._startAngle = 0;
this._endAngle = 2 * Math.PI;
this._labelFormatter = Plottable.Formatters.identity();
this._labelsEnabled = false;
this.innerRadius(0);
this.outerRadius(function () {
var pieCenter = _this._pieCenter();
return Math.min(Math.max(_this.width() - pieCenter.x, pieCenter.x), Math.max(_this.height() - pieCenter.y, pieCenter.y));
});
this.addClass("pie-plot");
this.attr("fill", function (d, i) { return String(i); }, new Plottable.Scales.Color());
this._strokeDrawers = new Plottable.Utils.Map();
}
Pie.prototype._setup = function () {
var _this = this;
_super.prototype._setup.call(this);
this._strokeDrawers.forEach(function (d) { return d.renderArea(_this._renderArea.append("g")); });
};
Pie.prototype.computeLayout = function (origin, availableWidth, availableHeight) {
_super.prototype.computeLayout.call(this, origin, availableWidth, availableHeight);
var pieCenter = this._pieCenter();
this._renderArea.attr("transform", "translate(" + pieCenter.x + "," + pieCenter.y + ")");
var radiusLimit = Math.min(Math.max(this.width() - pieCenter.x, pieCenter.x), Math.max(this.height() - pieCenter.y, pieCenter.y));
if (this.innerRadius().scale != null) {
this.innerRadius().scale.range([0, radiusLimit]);
}
if (this.outerRadius().scale != null) {
this.outerRadius().scale.range([0, radiusLimit]);
}
return this;
};
Pie.prototype.addDataset = function (dataset) {
_super.prototype.addDataset.call(this, dataset);
return this;
};
Pie.prototype._addDataset = function (dataset) {
if (this.datasets().length === 1) {
Plottable.Utils.Window.warn("Only one dataset is supported in Pie plots");
return this;
}
this._updatePieAngles();
var strokeDrawer = new Plottable.Drawers.ArcOutline(dataset);
if (this._isSetup) {
strokeDrawer.renderArea(this._renderArea.append("g"));
}
this._strokeDrawers.set(dataset, strokeDrawer);
_super.prototype._addDataset.call(this, dataset);
return this;
};
Pie.prototype.removeDataset = function (dataset) {
_super.prototype.removeDataset.call(this, dataset);
return this;
};
Pie.prototype._removeDatasetNodes = function (dataset) {
_super.prototype._removeDatasetNodes.call(this, dataset);
this._strokeDrawers.get(dataset).remove();
};
Pie.prototype._removeDataset = function (dataset) {
_super.prototype._removeDataset.call(this, dataset);
this._startAngles = [];
this._endAngles = [];
return this;
};
Pie.prototype.selections = function (datasets) {
var _this = this;
if (datasets === void 0) { datasets = this.datasets(); }
var allSelections = _super.prototype.selections.call(this, datasets)[0];
datasets.forEach(function (dataset) {
var drawer = _this._strokeDrawers.get(dataset);
if (drawer == null) {
return;
}
drawer.renderArea().selectAll(drawer.selector()).each(function () {
allSelections.push(this);
});
});
return d3.selectAll(allSelections);
};
Pie.prototype._onDatasetUpdate = function () {
_super.prototype._onDatasetUpdate.call(this);
this._updatePieAngles();
this.render();
};
Pie.prototype._createDrawer = function (dataset) {
return new Plottable.Drawers.Arc(dataset);
};
Pie.prototype.entities = function (datasets) {
var _this = this;
if (datasets === void 0) { datasets = this.datasets(); }
var entities = _super.prototype.entities.call(this, datasets);
entities.forEach(function (entity) {
entity.position.x += _this.width() / 2;
entity.position.y += _this.height() / 2;
var stroke = _this._strokeDrawers.get(entity.dataset).selectionForIndex(entity.index);
entity.selection[0].push(stroke[0][0]);
});
return entities;
};
Pie.prototype.sectorValue = function (sectorValue, scale) {
if (sectorValue == null) {
return this._propertyBindings.get(Pie._SECTOR_VALUE_KEY);
}
this._bindProperty(Pie._SECTOR_VALUE_KEY, sectorValue, scale);
this._updatePieAngles();
this.render();
return this;
};
Pie.prototype.innerRadius = function (innerRadius, scale) {
if (innerRadius == null) {
return this._propertyBindings.get(Pie._INNER_RADIUS_KEY);
}
this._bindProperty(Pie._INNER_RADIUS_KEY, innerRadius, scale);
this.render();
return this;
};
Pie.prototype.outerRadius = function (outerRadius, scale) {
if (outerRadius == null) {
return this._propertyBindings.get(Pie._OUTER_RADIUS_KEY);
}
this._bindProperty(Pie._OUTER_RADIUS_KEY, outerRadius, scale);
this.render();
return this;
};
Pie.prototype.startAngle = function (angle) {
if (angle == null) {
return this._startAngle;
}
else {
this._startAngle = angle;
this._updatePieAngles();
this.render();
return this;
}
};
Pie.prototype.endAngle = function (angle) {
if (angle == null) {
return this._endAngle;
}
else {
this._endAngle = angle;
this._updatePieAngles();
this.render();
return this;
}
};
Pie.prototype.labelsEnabled = function (enabled) {
if (enabled == null) {
return this._labelsEnabled;
}
else {
this._labelsEnabled = enabled;
this.render();
return this;
}
};
Pie.prototype.labelFormatter = function (formatter) {
if (formatter == null) {
return this._labelFormatter;
}
else {
this._labelFormatter = formatter;
this.render();
return this;
}
};
/*
* Gets the Entities at a particular Point.
*
* @param {Point} p
* @param {PlotEntity[]}
*/
Pie.prototype.entitiesAt = function (queryPoint) {
var center = { x: this.width() / 2, y: this.height() / 2 };
var adjustedQueryPoint = { x: queryPoint.x - center.x, y: queryPoint.y - center.y };
var index = this._sliceIndexForPoint(adjustedQueryPoint);
return index == null ? [] : [this.entities()[index]];
};
Pie.prototype._propertyProjectors = function () {
var _this = this;
var attrToProjector = _super.prototype._propertyProjectors.call(this);
var innerRadiusAccessor = Plottable.Plot._scaledAccessor(this.innerRadius());
var outerRadiusAccessor = Plottable.Plot._scaledAccessor(this.outerRadius());
attrToProjector["d"] = function (datum, index, ds) {
return d3.svg.arc().innerRadius(innerRadiusAccessor(datum, index, ds))
.outerRadius(outerRadiusAccessor(datum, index, ds))
.startAngle(_this._startAngles[index])
.endAngle(_this._endAngles[index])(datum, index);
};
return attrToProjector;
};
Pie.prototype._updatePieAngles = function () {
if (this.sectorValue() == null) {
return;
}
if (this.datasets().length === 0) {
return;
}
var sectorValueAccessor = Plottable.Plot._scaledAccessor(this.sectorValue());
var dataset = this.datasets()[0];
var data = this._getDataToDraw().get(dataset);
var pie = d3.layout.pie().sort(null).startAngle(this._startAngle).endAngle(this._endAngle)
.value(function (d, i) { return sectorValueAccessor(d, i, dataset); })(data);
this._startAngles = pie.map(function (slice) { return slice.startAngle; });
this._endAngles = pie.map(function (slice) { return slice.endAngle; });
};
Pie.prototype._pieCenter = function () {
var a = this._startAngle < this._endAngle ? this._startAngle : this._endAngle;
var b = this._startAngle < this._endAngle ? this._endAngle : this._startAngle;
var sinA = Math.sin(a);
var cosA = Math.cos(a);
var sinB = Math.sin(b);
var cosB = Math.cos(b);
var hTop;
var hBottom;
var wRight;
var wLeft;
/**
* The center of the pie is computed using the sine and cosine of the start angle and the end angle
* The sine indicates whether the start and end fall on the right half or the left half of the pie
* The cosine indicates whether the start and end fall on the top or the bottom half of the pie
* Different combinations provide the different heights and widths the pie needs from the center to the sides
*/
if (sinA >= 0 && sinB >= 0) {
if (cosA >= 0 && cosB >= 0) {
hTop = cosA;
hBottom = 0;
wLeft = 0;
wRight = sinB;
}
else if (cosA < 0 && cosB < 0) {
hTop = 0;
hBottom = -cosB;
wLeft = 0;
wRight = sinA;
}
else if (cosA >= 0 && cosB < 0) {
hTop = cosA;
hBottom = -cosB;
wLeft = 0;
wRight = sinA;
}
else if (cosA < 0 && cosB >= 0) {
hTop = 1;
hBottom = 1;
wLeft = 1;
wRight = Math.max(sinA, sinB);
}
}
else if (sinA >= 0 && sinB < 0) {
if (cosA >= 0 && cosB >= 0) {
hTop = Math.max(cosA, cosB);
hBottom = 1;
wLeft = 1;
wRight = 1;
}
else if (cosA < 0 && cosB < 0) {
hTop = 0;
hBottom = 1;
wLeft = -sinB;
wRight = sinA;
}
else if (cosA >= 0 && cosB < 0) {
hTop = cosA;
hBottom = 1;
wLeft = -sinB;
wRight = 1;
}
else if (cosA < 0 && cosB >= 0) {
hTop = cosB;
hBottom = 1;
wLeft = 1;
wRight = sinA;
}
}
else if (sinA < 0 && sinB >= 0) {
if (cosA >= 0 && cosB >= 0) {
hTop = 1;
hBottom = 0;
wLeft = -sinA;
wRight = sinB;
}
else if (cosA < 0 && cosB < 0) {
hTop = 1;
hBottom = Math.max(-cosA, -cosB);
wLeft = 1;
wRight = 1;
}
else if (cosA >= 0 && cosB < 0) {
hTop = 1;
hBottom = -cosB;
wLeft = -sinA;
wRight = 1;
}
else if (cosA < 0 && cosB >= 0) {
hTop = 1;
hBottom = -cosA;
wLeft = 1;
wRight = sinB;
}
}
else if (sinA < 0 && sinB < 0) {
if (cosA >= 0 && cosB >= 0) {
hTop = cosB;
hBottom = 0;
wLeft = -sinA;
wRight = 0;
}
else if (cosA < 0 && cosB < 0) {
hTop = 0;
hBottom = -cosA;
wLeft = -sinB;
wRight = 0;
}
else if (cosA >= 0 && cosB < 0) {
hTop = 1;
hBottom = 1;
wLeft = Math.max(cosA, -cosB);
wRight = 1;
}
else if (cosA < 0 && cosB >= 0) {
hTop = cosB;
hBottom = -cosA;
wLeft = 1;
wRight = 0;
}
}
return {
x: wLeft + wRight == 0 ? 0 : (wLeft / (wLeft + wRight)) * this.width(),
y: hTop + hBottom == 0 ? 0 : (hTop / (hTop + hBottom)) * this.height()
};
};
Pie.prototype._getDataToDraw = function () {
var dataToDraw = _super.prototype._getDataToDraw.call(this);
if (this.datasets().length === 0) {
return dataToDraw;
}
var sectorValueAccessor = Plottable.Plot._scaledAccessor(this.sectorValue());
var ds = this.datasets()[0];
var data = dataToDraw.get(ds);
var filteredData = data.filter(function (d, i) { return Pie._isValidData(sectorValueAccessor(d, i, ds)); });
dataToDraw.set(ds, filteredData);
return dataToDraw;
};
Pie._isValidData = function (value) {
return Plottable.Utils.Math.isValidNumber(value) && value >= 0;
};
Pie.prototype._pixelPoint = function (datum, index, dataset) {
var scaledValueAccessor = Plottable.Plot._scaledAccessor(this.sectorValue());
if (!Pie._isValidData(scaledValueAccessor(datum, index, dataset))) {
return { x: NaN, y: NaN };
}
var innerRadius = Plottable.Plot._scaledAccessor(this.innerRadius())(datum, index, dataset);
var outerRadius = Plottable.Plot._scaledAccessor(this.outerRadius())(datum, index, dataset);
var avgRadius = (innerRadius + outerRadius) / 2;
var pie = d3.layout.pie()
.sort(null)
.value(function (d, i) {
var value = scaledValueAccessor(d, i, dataset);
return Pie._isValidData(value) ? value : 0;
}).startAngle(this._startAngle).endAngle(this._endAngle)(dataset.data());
var startAngle = pie[index].startAngle;
var endAngle = pie[index].endAngle;
var avgAngle = (startAngle + endAngle) / 2;
return { x: avgRadius * Math.sin(avgAngle), y: -avgRadius * Math.cos(avgAngle) };
};
Pie.prototype._additionalPaint = function (time) {
var _this = this;
this._renderArea.select(".label-area").remove();
if (this._labelsEnabled) {
Plottable.Utils.Window.setTimeout(function () { return _this._drawLabels(); }, time);
}
var drawSteps = this._generateStrokeDrawSteps();
var dataToDraw = this._getDataToDraw();
this.datasets().forEach(function (dataset) { return _this._strokeDrawers.get(dataset).draw(dataToDraw.get(dataset), drawSteps); });
};
Pie.prototype._generateStrokeDrawSteps = function () {
var attrToProjector = this._generateAttrToProjector();
return [{ attrToProjector: attrToProjector, animator: new Plottable.Animators.Null() }];
};
Pie.prototype._sliceIndexForPoint = function (p) {
var pointRadius = Math.sqrt(Math.pow(p.x, 2) + Math.pow(p.y, 2));
var pointAngle = Math.acos(-p.y / pointRadius);
if (p.x < 0) {
pointAngle = Math.PI * 2 - pointAngle;
}
var index;
for (var i = 0; i < this._startAngles.length; i++) {
if (this._startAngles[i] < pointAngle && this._endAngles[i] > pointAngle) {
index = i;
break;
}
}
if (index !== undefined) {
var dataset = this.datasets()[0];
var datum = dataset.data()[index];
var innerRadius = this.innerRadius().accessor(datum, index, dataset);
var outerRadius = this.outerRadius().accessor(datum, index, dataset);
if (pointRadius > innerRadius && pointRadius < outerRadius) {
return index;
}
}
return null;
};
Pie.prototype._drawLabels = function () {
var _this = this;
var attrToProjector = this._generateAttrToProjector();
var labelArea = this._renderArea.append("g").classed("label-area", true);
var measurer = new SVGTypewriter.CacheMeasurer(labelArea);
var writer = new SVGTypewriter.Writer(measurer);
var dataset = this.datasets()[0];
var data = this._getDataToDraw().get(dataset);
data.forEach(function (datum, datumIndex) {
var value = _this.sectorValue().accessor(datum, datumIndex, dataset);
if (!Plottable.Utils.Math.isValidNumber(value)) {
return;
}
value = _this._labelFormatter(value);
var measurement = measurer.measure(value);
var theta = (_this._endAngles[datumIndex] + _this._startAngles[datumIndex]) / 2;
var outerRadius = _this.outerRadius().accessor(datum, datumIndex, dataset);
if (_this.outerRadius().scale) {
outerRadius = _this.outerRadius().scale.scale(outerRadius);
}
var innerRadius = _this.innerRadius().accessor(datum, datumIndex, dataset);
if (_this.innerRadius().scale) {
innerRadius = _this.innerRadius().scale.scale(innerRadius);
}
var labelRadius = (outerRadius + innerRadius) / 2;
var x = Math.sin(theta) * labelRadius - measurement.width / 2;
var y = -Math.cos(theta) * labelRadius - measurement.height / 2;
var corners = [
{ x: x, y: y },
{ x: x, y: y + measurement.height },
{ x: x + measurement.width, y: y },
{ x: x + measurement.width, y: y + measurement.height },
];
var showLabel = corners.every(function (corner) {
return Math.abs(corner.x) <= _this.width() / 2 && Math.abs(corner.y) <= _this.height() / 2;
});
if (showLabel) {
var sliceIndices = corners.map(function (corner) { return _this._sliceIndexForPoint(corner); });
showLabel = sliceIndices.every(function (index) { return index === datumIndex; });
}
var color = attrToProjector["fill"](datum, datumIndex, dataset);
var dark = Plottable.Utils.Color.contrast("white", color) * 1.6 < Plottable.Utils.Color.contrast("black", color);
var g = labelArea.append("g").attr("transform", "translate(" + x + "," + y + ")");
var className = dark ? "dark-label" : "light-label";
g.classed(className, true);
g.style("visibility", showLabel ? "inherit" : "hidden");
writer.write(value, measurement.width, measurement.height, {
selection: g,
xAlign: "center",
yAlign: "center",
textRotation: 0,
});
});
};
Pie._INNER_RADIUS_KEY = "inner-radius";
Pie._OUTER_RADIUS_KEY = "outer-radius";
Pie._SECTOR_VALUE_KEY = "sector-value";
return Pie;
}(Plottable.Plot));
Plots.Pie = Pie;
})(Plots = Plottable.Plots || (Plottable.Plots = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var XYPlot = (function (_super) {
__extends(XYPlot, _super);
/**
* An XYPlot is a Plot that displays data along two primary directions, X and Y.
*
* @constructor
* @param {Scale} xScale The x scale to use.
* @param {Scale} yScale The y scale to use.
*/
function XYPlot() {
var _this = this;
_super.call(this);
this._autoAdjustXScaleDomain = false;
this._autoAdjustYScaleDomain = false;
this._deferredRendering = false;
this._cachedDomainX = [null, null];
this._cachedDomainY = [null, null];
this.addClass("xy-plot");
this._adjustYDomainOnChangeFromXCallback = function (scale) { return _this._adjustYDomainOnChangeFromX(); };
this._adjustXDomainOnChangeFromYCallback = function (scale) { return _this._adjustXDomainOnChangeFromY(); };
var _deltaX = 0;
var _deltaY = 0;
var _scalingX = 1;
var _scalingY = 1;
var _lastSeenDomainX = [null, null];
var _lastSeenDomainY = [null, null];
var _timeoutReference = 0;
var _deferredRenderingTimeout = 500;
var _registerDeferredRendering = function () {
if (_this._renderArea == null) {
return;
}
_this._renderArea.attr("transform", "translate(" + _deltaX + ", " + _deltaY + ")" +
"scale(" + _scalingX + ", " + _scalingY + ")");
clearTimeout(_timeoutReference);
_timeoutReference = setTimeout(function () {
_this._cachedDomainX = _lastSeenDomainX;
_this._cachedDomainY = _lastSeenDomainY;
_deltaX = 0;
_deltaY = 0;
_scalingX = 1;
_scalingY = 1;
_this.render();
_this._renderArea.attr("transform", "translate(0, 0) scale(1, 1)");
}, _deferredRenderingTimeout);
};
var _lazyDomainChangeCallbackX = function (scale) {
if (!_this._isAnchored) {
return;
}
_lastSeenDomainX = scale.domain();
_scalingX = (scale.scale(_this._cachedDomainX[1]) - scale.scale(_this._cachedDomainX[0])) /
(scale.scale(_lastSeenDomainX[1]) - scale.scale(_lastSeenDomainX[0])) || 1;
_deltaX = scale.scale(_this._cachedDomainX[0]) - scale.scale(_lastSeenDomainX[0]) || 0;
_registerDeferredRendering();
};
var _lazyDomainChangeCallbackY = function (scale) {
if (!_this._isAnchored) {
return;
}
_lastSeenDomainY = scale.domain();
_scalingY = (scale.scale(_this._cachedDomainY[1]) - scale.scale(_this._cachedDomainY[0])) /
(scale.scale(_lastSeenDomainY[1]) - scale.scale(_lastSeenDomainY[0])) || 1;
_deltaY = scale.scale(_this._cachedDomainY[0]) - scale.scale(_lastSeenDomainY[0]) * _scalingY || 0;
_registerDeferredRendering();
};
this._renderCallback = function (scale) {
if (_this.deferredRendering() && _this.x() && _this.x().scale === scale) {
_lazyDomainChangeCallbackX(scale);
}
else if (_this.deferredRendering() && _this.y() && _this.y().scale === scale) {
_lazyDomainChangeCallbackY(scale);
}
else {
_this.render();
}
};
}
XYPlot.prototype.entityNearest = function (queryPoint) {
// by default, the entity index stores position information in the data space
// the default impelentation of the entityNearest must convert the chart bounding
// box as well as the query point to the data space before it can make a comparison
var invertedChartBounds = this._invertedBounds();
var invertedQueryPoint = this._invertPixelPoint(queryPoint);
return _super.prototype.entityNearest.call(this, invertedQueryPoint, invertedChartBounds);
};
XYPlot.prototype.deferredRendering = function (deferredRendering) {
if (deferredRendering == null) {
return this._deferredRendering;
}
if (deferredRendering && this._isAnchored) {
if (this.x() && this.x().scale) {
this._cachedDomainX = this.x().scale.domain();
}
if (this.y() && this.y().scale) {
this._cachedDomainY = this.y().scale.domain();
}
}
this._deferredRendering = deferredRendering;
return this;
};
XYPlot.prototype.x = function (x, xScale) {
if (x == null) {
return this._propertyBindings.get(XYPlot._X_KEY);
}
this._bindProperty(XYPlot._X_KEY, x, xScale);
var width = this.width();
if (xScale != null && width != null) {
xScale.range([0, width]);
}
if (this._autoAdjustYScaleDomain) {
this._updateYExtentsAndAutodomain();
}
this.render();
return this;
};
XYPlot.prototype.y = function (y, yScale) {
if (y == null) {
return this._propertyBindings.get(XYPlot._Y_KEY);
}
this._bindProperty(XYPlot._Y_KEY, y, yScale);
var height = this.height();
if (yScale != null && height != null) {
if (yScale instanceof Plottable.Scales.Category) {
yScale.range([0, height]);
}
else {
yScale.range([height, 0]);
}
}
if (this._autoAdjustXScaleDomain) {
this._updateXExtentsAndAutodomain();
}
this.render();
return this;
};
XYPlot.prototype._filterForProperty = function (property) {
if (property === "x" && this._autoAdjustXScaleDomain) {
return this._makeFilterByProperty("y");
}
else if (property === "y" && this._autoAdjustYScaleDomain) {
return this._makeFilterByProperty("x");
}
return null;
};
XYPlot.prototype._makeFilterByProperty = function (property) {
var binding = this._propertyBindings.get(property);
if (binding != null) {
var accessor_1 = binding.accessor;
var scale_1 = binding.scale;
if (scale_1 != null) {
return function (datum, index, dataset) {
var range = scale_1.range();
return Plottable.Utils.Math.inRange(scale_1.scale(accessor_1(datum, index, dataset)), range[0], range[1]);
};
}
}
return null;
};
XYPlot.prototype._uninstallScaleForKey = function (scale, key) {
_super.prototype._uninstallScaleForKey.call(this, scale, key);
var adjustCallback = key === XYPlot._X_KEY ? this._adjustYDomainOnChangeFromXCallback
: this._adjustXDomainOnChangeFromYCallback;
scale.offUpdate(adjustCallback);
};
XYPlot.prototype._installScaleForKey = function (scale, key) {
_super.prototype._installScaleForKey.call(this, scale, key);
var adjustCallback = key === XYPlot._X_KEY ? this._adjustYDomainOnChangeFromXCallback
: this._adjustXDomainOnChangeFromYCallback;
scale.onUpdate(adjustCallback);
};
XYPlot.prototype.destroy = function () {
_super.prototype.destroy.call(this);
if (this.x().scale) {
this.x().scale.offUpdate(this._adjustYDomainOnChangeFromXCallback);
}
if (this.y().scale) {
this.y().scale.offUpdate(this._adjustXDomainOnChangeFromYCallback);
}
return this;
};
XYPlot.prototype.autorangeMode = function (autorangeMode) {
if (autorangeMode == null) {
if (this._autoAdjustXScaleDomain) {
return "x";
}
if (this._autoAdjustYScaleDomain) {
return "y";
}
return "none";
}
switch (autorangeMode) {
case "x":
this._autoAdjustXScaleDomain = true;
this._autoAdjustYScaleDomain = false;
this._adjustXDomainOnChangeFromY();
break;
case "y":
this._autoAdjustXScaleDomain = false;
this._autoAdjustYScaleDomain = true;
this._adjustYDomainOnChangeFromX();
break;
case "none":
this._autoAdjustXScaleDomain = false;
this._autoAdjustYScaleDomain = false;
break;
default:
throw new Error("Invalid scale name '" + autorangeMode + "', must be 'x', 'y' or 'none'");
}
return this;
};
XYPlot.prototype.computeLayout = function (origin, availableWidth, availableHeight) {
_super.prototype.computeLayout.call(this, origin, availableWidth, availableHeight);
var xBinding = this.x();
var xScale = xBinding && xBinding.scale;
if (xScale != null) {
xScale.range([0, this.width()]);
}
var yBinding = this.y();
var yScale = yBinding && yBinding.scale;
if (yScale != null) {
if (yScale instanceof Plottable.Scales.Category) {
yScale.range([0, this.height()]);
}
else {
yScale.range([this.height(), 0]);
}
}
return this;
};
XYPlot.prototype._updateXExtentsAndAutodomain = function () {
this._updateExtentsForProperty("x");
var xScale = this.x().scale;
if (xScale != null) {
xScale.autoDomain();
}
};
XYPlot.prototype._updateYExtentsAndAutodomain = function () {
this._updateExtentsForProperty("y");
var yScale = this.y().scale;
if (yScale != null) {
yScale.autoDomain();
}
};
/**
* Adjusts the domains of both X and Y scales to show all data.
* This call does not override the autorange() behavior.
*
* @returns {XYPlot} The calling XYPlot.
*/
XYPlot.prototype.showAllData = function () {
this._updateXExtentsAndAutodomain();
this._updateYExtentsAndAutodomain();
return this;
};
XYPlot.prototype._adjustYDomainOnChangeFromX = function () {
if (!this._projectorsReady()) {
return;
}
if (this._autoAdjustYScaleDomain) {
this._updateYExtentsAndAutodomain();
}
};
XYPlot.prototype._adjustXDomainOnChangeFromY = function () {
if (!this._projectorsReady()) {
return;
}
if (this._autoAdjustXScaleDomain) {
this._updateXExtentsAndAutodomain();
}
};
XYPlot.prototype._buildLightweightPlotEntities = function (datasets) {
var _this = this;
return _super.prototype._buildLightweightPlotEntities.call(this, datasets).map(function (lightweightPlotEntity) {
lightweightPlotEntity.position = _this._invertPixelPoint(lightweightPlotEntity.position);
return lightweightPlotEntity;
});
};
XYPlot.prototype._projectorsReady = function () {
var xBinding = this.x();
var yBinding = this.y();
return xBinding != null &&
xBinding.accessor != null &&
yBinding != null &&
yBinding.accessor != null;
};
/**
* Returns the bounds of the plot in the Data space ensures that the topLeft
* and bottomRight points represent the minima and maxima of the Data space, respectively
@returns {Bounds}
*/
XYPlot.prototype._invertedBounds = function () {
var bounds = this.bounds();
var maybeTopLeft = this._invertPixelPoint(bounds.topLeft);
var maybeBottomRight = this._invertPixelPoint(bounds.bottomRight);
// Scale domains can map from lowest to highest or highest to lowest (eg [0, 1] or [1, 0]).
// What we're interested in is a domain space equivalent to the concept of topLeft
// and bottomRight, not a true mapping from point to domain. This is in keeping
// with our definition of {Bounds}, where the topLeft coordinate is minimal
// and the bottomRight is maximal.
return {
topLeft: {
x: Math.min(maybeTopLeft.x, maybeBottomRight.x),
y: Math.min(maybeTopLeft.y, maybeBottomRight.y)
},
bottomRight: {
x: Math.max(maybeBottomRight.x, maybeTopLeft.x),
y: Math.max(maybeBottomRight.y, maybeTopLeft.y)
}
};
};
/**
* _invertPixelPoint converts a point in pixel coordinates to a point in data coordinates
* @param {Point} point Representation of the point in pixel coordinates
* @return {Point} Returns the point represented in data coordinates
*/
XYPlot.prototype._invertPixelPoint = function (point) {
var xScale = this.x();
var yScale = this.y();
return { x: xScale.scale.invertedTransformation(point.x), y: yScale.scale.invertedTransformation(point.y) };
};
XYPlot.prototype._pixelPoint = function (datum, index, dataset) {
var xProjector = Plottable.Plot._scaledAccessor(this.x());
var yProjector = Plottable.Plot._scaledAccessor(this.y());
return { x: xProjector(datum, index, dataset), y: yProjector(datum, index, dataset) };
};
XYPlot.prototype._getDataToDraw = function () {
var _this = this;
var dataToDraw = _super.prototype._getDataToDraw.call(this);
var definedFunction = function (d, i, dataset) {
var positionX = Plottable.Plot._scaledAccessor(_this.x())(d, i, dataset);
var positionY = Plottable.Plot._scaledAccessor(_this.y())(d, i, dataset);
return Plottable.Utils.Math.isValidNumber(positionX) &&
Plottable.Utils.Math.isValidNumber(positionY);
};
this.datasets().forEach(function (dataset) {
dataToDraw.set(dataset, dataToDraw.get(dataset).filter(function (d, i) { return definedFunction(d, i, dataset); }));
});
return dataToDraw;
};
XYPlot._X_KEY = "x";
XYPlot._Y_KEY = "y";
return XYPlot;
}(Plottable.Plot));
Plottable.XYPlot = XYPlot;
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Plots;
(function (Plots) {
var Rectangle = (function (_super) {
__extends(Rectangle, _super);
/**
* A Rectangle Plot displays rectangles based on the data.
* The left and right edges of each rectangle can be set with x() and x2().
* If only x() is set the Rectangle Plot will attempt to compute the correct left and right edge positions.
* The top and bottom edges of each rectangle can be set with y() and y2().
* If only y() is set the Rectangle Plot will attempt to compute the correct top and bottom edge positions.
*
* @constructor
* @param {Scale.Scale} xScale
* @param {Scale.Scale} yScale
*/
function Rectangle() {
_super.call(this);
this._labelsEnabled = false;
this._label = null;
this.animator("rectangles", new Plottable.Animators.Null());
this.addClass("rectangle-plot");
this.attr("fill", new Plottable.Scales.Color().range()[0]);
}
Rectangle.prototype._createDrawer = function (dataset) {
return new Plottable.Drawers.Rectangle(dataset);
};
Rectangle.prototype._generateAttrToProjector = function () {
var _this = this;
var attrToProjector = _super.prototype._generateAttrToProjector.call(this);
// Copy each of the different projectors.
var xAttr = Plottable.Plot._scaledAccessor(this.x());
var x2Attr = attrToProjector[Rectangle._X2_KEY];
var yAttr = Plottable.Plot._scaledAccessor(this.y());
var y2Attr = attrToProjector[Rectangle._Y2_KEY];
var xScale = this.x().scale;
var yScale = this.y().scale;
if (x2Attr != null) {
attrToProjector["width"] = function (d, i, dataset) { return Math.abs(x2Attr(d, i, dataset) - xAttr(d, i, dataset)); };
attrToProjector["x"] = function (d, i, dataset) { return Math.min(x2Attr(d, i, dataset), xAttr(d, i, dataset)); };
}
else {
attrToProjector["width"] = function (d, i, dataset) { return _this._rectangleWidth(xScale); };
attrToProjector["x"] = function (d, i, dataset) { return xAttr(d, i, dataset) - 0.5 * attrToProjector["width"](d, i, dataset); };
}
if (y2Attr != null) {
attrToProjector["height"] = function (d, i, dataset) { return Math.abs(y2Attr(d, i, dataset) - yAttr(d, i, dataset)); };
attrToProjector["y"] = function (d, i, dataset) {
return Math.max(y2Attr(d, i, dataset), yAttr(d, i, dataset)) - attrToProjector["height"](d, i, dataset);
};
}
else {
attrToProjector["height"] = function (d, i, dataset) { return _this._rectangleWidth(yScale); };
attrToProjector["y"] = function (d, i, dataset) { return yAttr(d, i, dataset) - 0.5 * attrToProjector["height"](d, i, dataset); };
}
// Clean up the attributes projected onto the SVG elements
delete attrToProjector[Rectangle._X2_KEY];
delete attrToProjector[Rectangle._Y2_KEY];
return attrToProjector;
};
Rectangle.prototype._generateDrawSteps = function () {
return [{ attrToProjector: this._generateAttrToProjector(), animator: this._getAnimator("rectangles") }];
};
Rectangle.prototype._updateExtentsForProperty = function (property) {
_super.prototype._updateExtentsForProperty.call(this, property);
if (property === "x") {
_super.prototype._updateExtentsForProperty.call(this, "x2");
}
else if (property === "y") {
_super.prototype._updateExtentsForProperty.call(this, "y2");
}
};
Rectangle.prototype._filterForProperty = function (property) {
if (property === "x2") {
return _super.prototype._filterForProperty.call(this, "x");
}
else if (property === "y2") {
return _super.prototype._filterForProperty.call(this, "y");
}
return _super.prototype._filterForProperty.call(this, property);
};
Rectangle.prototype.x = function (x, xScale) {
if (x == null) {
return _super.prototype.x.call(this);
}
if (xScale == null) {
_super.prototype.x.call(this, x);
}
else {
_super.prototype.x.call(this, x, xScale);
}
if (xScale != null) {
var x2Binding = this.x2();
var x2 = x2Binding && x2Binding.accessor;
if (x2 != null) {
this._bindProperty(Rectangle._X2_KEY, x2, xScale);
}
}
// The x and y scales should render in bands with no padding for category scales
if (xScale instanceof Plottable.Scales.Category) {
xScale.innerPadding(0).outerPadding(0);
}
return this;
};
Rectangle.prototype.x2 = function (x2) {
if (x2 == null) {
return this._propertyBindings.get(Rectangle._X2_KEY);
}
var xBinding = this.x();
var xScale = xBinding && xBinding.scale;
this._bindProperty(Rectangle._X2_KEY, x2, xScale);
this.render();
return this;
};
Rectangle.prototype.y = function (y, yScale) {
if (y == null) {
return _super.prototype.y.call(this);
}
if (yScale == null) {
_super.prototype.y.call(this, y);
}
else {
_super.prototype.y.call(this, y, yScale);
}
if (yScale != null) {
var y2Binding = this.y2();
var y2 = y2Binding && y2Binding.accessor;
if (y2 != null) {
this._bindProperty(Rectangle._Y2_KEY, y2, yScale);
}
}
// The x and y scales should render in bands with no padding for category scales
if (yScale instanceof Plottable.Scales.Category) {
yScale.innerPadding(0).outerPadding(0);
}
return this;
};
Rectangle.prototype.y2 = function (y2) {
if (y2 == null) {
return this._propertyBindings.get(Rectangle._Y2_KEY);
}
var yBinding = this.y();
var yScale = yBinding && yBinding.scale;
this._bindProperty(Rectangle._Y2_KEY, y2, yScale);
this.render();
return this;
};
/**
* Gets the PlotEntities at a particular Point.
*
* @param {Point} point The point to query.
* @returns {PlotEntity[]} The PlotEntities at the particular point
*/
Rectangle.prototype.entitiesAt = function (point) {
var attrToProjector = this._generateAttrToProjector();
return this.entities().filter(function (entity) {
var datum = entity.datum;
var index = entity.index;
var dataset = entity.dataset;
var x = attrToProjector["x"](datum, index, dataset);
var y = attrToProjector["y"](datum, index, dataset);
var width = attrToProjector["width"](datum, index, dataset);
var height = attrToProjector["height"](datum, index, dataset);
return x <= point.x && point.x <= x + width && y <= point.y && point.y <= y + height;
});
};
Rectangle.prototype.entitiesIn = function (xRangeOrBounds, yRange) {
var dataXRange;
var dataYRange;
if (yRange == null) {
var bounds = xRangeOrBounds;
dataXRange = { min: bounds.topLeft.x, max: bounds.bottomRight.x };
dataYRange = { min: bounds.topLeft.y, max: bounds.bottomRight.y };
}
else {
dataXRange = xRangeOrBounds;
dataYRange = yRange;
}
return this._entitiesIntersecting(dataXRange, dataYRange);
};
Rectangle.prototype._entityBBox = function (datum, index, dataset, attrToProjector) {
return {
x: attrToProjector["x"](datum, index, dataset),
y: attrToProjector["y"](datum, index, dataset),
width: attrToProjector["width"](datum, index, dataset),
height: attrToProjector["height"](datum, index, dataset),
};
};
Rectangle.prototype._entitiesIntersecting = function (xValOrRange, yValOrRange) {
var _this = this;
var intersected = [];
var attrToProjector = this._generateAttrToProjector();
this.entities().forEach(function (entity) {
if (Plottable.Utils.DOM.intersectsBBox(xValOrRange, yValOrRange, _this._entityBBox(entity.datum, entity.index, entity.dataset, attrToProjector))) {
intersected.push(entity);
}
});
return intersected;
};
Rectangle.prototype.label = function (label) {
if (label == null) {
return this._label;
}
this._label = label;
this.render();
return this;
};
Rectangle.prototype.labelsEnabled = function (enabled) {
if (enabled == null) {
return this._labelsEnabled;
}
else {
this._labelsEnabled = enabled;
this.render();
return this;
}
};
Rectangle.prototype._propertyProjectors = function () {
var attrToProjector = _super.prototype._propertyProjectors.call(this);
if (this.x2() != null) {
attrToProjector["x2"] = Plottable.Plot._scaledAccessor(this.x2());
}
if (this.y2() != null) {
attrToProjector["y2"] = Plottable.Plot._scaledAccessor(this.y2());
}
return attrToProjector;
};
Rectangle.prototype._pixelPoint = function (datum, index, dataset) {
var attrToProjector = this._generateAttrToProjector();
var rectX = attrToProjector["x"](datum, index, dataset);
var rectY = attrToProjector["y"](datum, index, dataset);
var rectWidth = attrToProjector["width"](datum, index, dataset);
var rectHeight = attrToProjector["height"](datum, index, dataset);
var x = rectX + rectWidth / 2;
var y = rectY + rectHeight / 2;
return { x: x, y: y };
};
Rectangle.prototype._rectangleWidth = function (scale) {
if (scale instanceof Plottable.Scales.Category) {
return scale.rangeBand();
}
else {
var accessor_2 = scale === this.x().scale ? this.x().accessor : this.y().accessor;
var accessorData = d3.set(Plottable.Utils.Array.flatten(this.datasets().map(function (dataset) {
return dataset.data().map(function (d, i) { return accessor_2(d, i, dataset).valueOf(); });
}))).values().map(function (value) { return +value; });
// Get the absolute difference between min and max
var min = Plottable.Utils.Math.min(accessorData, 0);
var max = Plottable.Utils.Math.max(accessorData, 0);
var scaledMin = scale.scale(min);
var scaledMax = scale.scale(max);
return (scaledMax - scaledMin) / Math.abs(max - min);
}
};
Rectangle.prototype._getDataToDraw = function () {
var dataToDraw = new Plottable.Utils.Map();
var attrToProjector = this._generateAttrToProjector();
this.datasets().forEach(function (dataset) {
var data = dataset.data().filter(function (d, i) { return Plottable.Utils.Math.isValidNumber(attrToProjector["x"](d, i, dataset)) &&
Plottable.Utils.Math.isValidNumber(attrToProjector["y"](d, i, dataset)) &&
Plottable.Utils.Math.isValidNumber(attrToProjector["width"](d, i, dataset)) &&
Plottable.Utils.Math.isValidNumber(attrToProjector["height"](d, i, dataset)); });
dataToDraw.set(dataset, data);
});
return dataToDraw;
};
Rectangle.prototype._additionalPaint = function (time) {
var _this = this;
this._renderArea.selectAll(".label-area").remove();
if (this._labelsEnabled && this.label() != null) {
Plottable.Utils.Window.setTimeout(function () { return _this._drawLabels(); }, time);
}
};
Rectangle.prototype._drawLabels = function () {
var _this = this;
var dataToDraw = this._getDataToDraw();
this.datasets().forEach(function (dataset, i) { return _this._drawLabel(dataToDraw, dataset, i); });
};
Rectangle.prototype._drawLabel = function (dataToDraw, dataset, datasetIndex) {
var _this = this;
var attrToProjector = this._generateAttrToProjector();
var labelArea = this._renderArea.append("g").classed("label-area", true);
var measurer = new SVGTypewriter.CacheMeasurer(labelArea);
var writer = new SVGTypewriter.Writer(measurer);
var xRange = this.x().scale.range();
var yRange = this.y().scale.range();
var xMin = Math.min.apply(null, xRange);
var xMax = Math.max.apply(null, xRange);
var yMin = Math.min.apply(null, yRange);
var yMax = Math.max.apply(null, yRange);
var data = dataToDraw.get(dataset);
data.forEach(function (datum, datumIndex) {
var label = "" + _this.label()(datum, datumIndex, dataset);
var measurement = measurer.measure(label);
var x = attrToProjector["x"](datum, datumIndex, dataset);
var y = attrToProjector["y"](datum, datumIndex, dataset);
var width = attrToProjector["width"](datum, datumIndex, dataset);
var height = attrToProjector["height"](datum, datumIndex, dataset);
if (measurement.height <= height && measurement.width <= width) {
var horizontalOffset = (width - measurement.width) / 2;
var verticalOffset = (height - measurement.height) / 2;
x += horizontalOffset;
y += verticalOffset;
var xLabelRange = { min: x, max: x + measurement.width };
var yLabelRange = { min: y, max: y + measurement.height };
if (xLabelRange.min < xMin || xLabelRange.max > xMax || yLabelRange.min < yMin || yLabelRange.max > yMax) {
return;
}
if (_this._overlayLabel(xLabelRange, yLabelRange, datumIndex, datasetIndex, dataToDraw)) {
return;
}
var color = attrToProjector["fill"](datum, datumIndex, dataset);
var dark = Plottable.Utils.Color.contrast("white", color) * 1.6 < Plottable.Utils.Color.contrast("black", color);
var g = labelArea.append("g").attr("transform", "translate(" + x + "," + y + ")");
var className = dark ? "dark-label" : "light-label";
g.classed(className, true);
writer.write(label, measurement.width, measurement.height, {
selection: g,
xAlign: "center",
yAlign: "center",
textRotation: 0,
});
}
});
};
Rectangle.prototype._overlayLabel = function (labelXRange, labelYRange, datumIndex, datasetIndex, dataToDraw) {
var attrToProjector = this._generateAttrToProjector();
var datasets = this.datasets();
for (var i = datasetIndex; i < datasets.length; i++) {
var dataset = datasets[i];
var data = dataToDraw.get(dataset);
for (var j = (i === datasetIndex ? datumIndex + 1 : 0); j < data.length; j++) {
if (Plottable.Utils.DOM.intersectsBBox(labelXRange, labelYRange, this._entityBBox(data[j], j, dataset, attrToProjector))) {
return true;
}
}
}
return false;
};
Rectangle._X2_KEY = "x2";
Rectangle._Y2_KEY = "y2";
return Rectangle;
}(Plottable.XYPlot));
Plots.Rectangle = Rectangle;
})(Plots = Plottable.Plots || (Plottable.Plots = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Plots;
(function (Plots) {
var Scatter = (function (_super) {
__extends(Scatter, _super);
/**
* A Scatter Plot draws a symbol at each data point.
*
* @constructor
*/
function Scatter() {
_super.call(this);
this.addClass("scatter-plot");
var animator = new Plottable.Animators.Easing();
animator.startDelay(5);
animator.stepDuration(250);
animator.maxTotalDuration(Plottable.Plot._ANIMATION_MAX_DURATION);
this.animator(Plots.Animator.MAIN, animator);
this.attr("opacity", 0.6);
this.attr("fill", new Plottable.Scales.Color().range()[0]);
this.size(6);
var circleSymbolFactory = Plottable.SymbolFactories.circle();
this.symbol(function () { return circleSymbolFactory; });
}
Scatter.prototype._buildLightweightPlotEntities = function (datasets) {
var _this = this;
var lightweightPlotEntities = _super.prototype._buildLightweightPlotEntities.call(this, datasets);
return lightweightPlotEntities.map(function (lightweightPlotEntity) {
var diameter = Plottable.Plot._scaledAccessor(_this.size())(lightweightPlotEntity.datum, lightweightPlotEntity.index, lightweightPlotEntity.dataset);
// convert diameter into data space to be on the same scale as the scatter point position
lightweightPlotEntity.diameter = _this._invertedPixelSize({ x: diameter, y: diameter });
return lightweightPlotEntity;
});
};
Scatter.prototype._createDrawer = function (dataset) {
return new Plottable.Drawers.Symbol(dataset);
};
Scatter.prototype.size = function (size, scale) {
if (size == null) {
return this._propertyBindings.get(Scatter._SIZE_KEY);
}
this._bindProperty(Scatter._SIZE_KEY, size, scale);
this.render();
return this;
};
Scatter.prototype.symbol = function (symbol) {
if (symbol == null) {
return this._propertyBindings.get(Scatter._SYMBOL_KEY);
}
this._propertyBindings.set(Scatter._SYMBOL_KEY, { accessor: symbol });
this.render();
return this;
};
Scatter.prototype._generateDrawSteps = function () {
var drawSteps = [];
if (this._animateOnNextRender()) {
var resetAttrToProjector = this._generateAttrToProjector();
var symbolProjector_1 = Plottable.Plot._scaledAccessor(this.symbol());
resetAttrToProjector["d"] = function (datum, index, dataset) { return symbolProjector_1(datum, index, dataset)(0); };
drawSteps.push({ attrToProjector: resetAttrToProjector, animator: this._getAnimator(Plots.Animator.RESET) });
}
drawSteps.push({ attrToProjector: this._generateAttrToProjector(), animator: this._getAnimator(Plots.Animator.MAIN) });
return drawSteps;
};
Scatter.prototype._entityVisibleOnPlot = function (entity, bounds) {
var xRange = { min: bounds.topLeft.x, max: bounds.bottomRight.x };
var yRange = { min: bounds.topLeft.y, max: bounds.bottomRight.y };
var translatedBbox = {
x: entity.position.x - entity.diameter.x,
y: entity.position.y - entity.diameter.y,
width: entity.diameter.x,
height: entity.diameter.y,
};
return Plottable.Utils.DOM.intersectsBBox(xRange, yRange, translatedBbox);
};
Scatter.prototype._propertyProjectors = function () {
var propertyToProjectors = _super.prototype._propertyProjectors.call(this);
var xProjector = Plottable.Plot._scaledAccessor(this.x());
var yProjector = Plottable.Plot._scaledAccessor(this.y());
var sizeProjector = Plottable.Plot._scaledAccessor(this.size());
propertyToProjectors["transform"] = function (datum, index, dataset) {
return "translate(" + xProjector(datum, index, dataset) + "," + yProjector(datum, index, dataset) + ")";
};
var symbolProjector = Plottable.Plot._scaledAccessor(this.symbol());
propertyToProjectors["d"] = function (datum, index, dataset) {
return symbolProjector(datum, index, dataset)(sizeProjector(datum, index, dataset));
};
return propertyToProjectors;
};
Scatter.prototype.entitiesIn = function (xRangeOrBounds, yRange) {
var dataXRange;
var dataYRange;
if (yRange == null) {
var bounds = xRangeOrBounds;
dataXRange = { min: bounds.topLeft.x, max: bounds.bottomRight.x };
dataYRange = { min: bounds.topLeft.y, max: bounds.bottomRight.y };
}
else {
dataXRange = xRangeOrBounds;
dataYRange = yRange;
}
var xProjector = Plottable.Plot._scaledAccessor(this.x());
var yProjector = Plottable.Plot._scaledAccessor(this.y());
return this.entities().filter(function (entity) {
var datum = entity.datum;
var index = entity.index;
var dataset = entity.dataset;
var x = xProjector(datum, index, dataset);
var y = yProjector(datum, index, dataset);
return dataXRange.min <= x && x <= dataXRange.max && dataYRange.min <= y && y <= dataYRange.max;
});
};
/**
* Gets the Entities at a particular Point.
*
* @param {Point} p
* @returns {PlotEntity[]}
*/
Scatter.prototype.entitiesAt = function (p) {
var xProjector = Plottable.Plot._scaledAccessor(this.x());
var yProjector = Plottable.Plot._scaledAccessor(this.y());
var sizeProjector = Plottable.Plot._scaledAccessor(this.size());
return this.entities().filter(function (entity) {
var datum = entity.datum;
var index = entity.index;
var dataset = entity.dataset;
var x = xProjector(datum, index, dataset);
var y = yProjector(datum, index, dataset);
var size = sizeProjector(datum, index, dataset);
return x - size / 2 <= p.x && p.x <= x + size / 2 && y - size / 2 <= p.y && p.y <= y + size / 2;
});
};
/**
* _invertedPixelSize returns the size of the object in data space
* @param {Point} [point] The size of the object in pixel space. X corresponds to
* the width of the object, and Y corresponds to the height of the object
* @return {Point} Returns the size of the object in data space. X corresponds to
* the width of the object in data space, and Y corresponds to the height of the
* object in data space.
*/
Scatter.prototype._invertedPixelSize = function (point) {
var invertedOrigin = this._invertPixelPoint(this.origin());
var invertedSize = this._invertPixelPoint({ x: point.x, y: point.y });
return {
x: Math.abs(invertedSize.x - invertedOrigin.x),
y: Math.abs(invertedSize.y - invertedOrigin.y)
};
};
Scatter._SIZE_KEY = "size";
Scatter._SYMBOL_KEY = "symbol";
return Scatter;
}(Plottable.XYPlot));
Plots.Scatter = Scatter;
})(Plots = Plottable.Plots || (Plottable.Plots = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Plots;
(function (Plots) {
var Bar = (function (_super) {
__extends(Bar, _super);
/**
* A Bar Plot draws bars growing out from a baseline to some value
*
* @constructor
* @param {string} [orientation="vertical"] One of "vertical"/"horizontal".
*/
function Bar(orientation) {
var _this = this;
if (orientation === void 0) { orientation = Bar.ORIENTATION_VERTICAL; }
_super.call(this);
this._labelFormatter = Plottable.Formatters.identity();
this._labelsEnabled = false;
this._hideBarsIfAnyAreTooWide = true;
this._barPixelWidth = 0;
this.addClass("bar-plot");
if (orientation !== Bar.ORIENTATION_VERTICAL && orientation !== Bar.ORIENTATION_HORIZONTAL) {
throw new Error(orientation + " is not a valid orientation for Plots.Bar");
}
this._isVertical = orientation === Bar.ORIENTATION_VERTICAL;
this.animator("baseline", new Plottable.Animators.Null());
this.attr("fill", new Plottable.Scales.Color().range()[0]);
this.attr("width", function () { return _this._barPixelWidth; });
this._labelConfig = new Plottable.Utils.Map();
this._baselineValueProvider = function () { return [_this.baselineValue()]; };
this._updateBarPixelWidthCallback = function () { return _this._updateBarPixelWidth(); };
}
Bar.prototype.x = function (x, xScale) {
if (x == null) {
return _super.prototype.x.call(this);
}
if (xScale == null) {
_super.prototype.x.call(this, x);
}
else {
_super.prototype.x.call(this, x, xScale);
xScale.onUpdate(this._updateBarPixelWidthCallback);
}
this._updateValueScale();
return this;
};
Bar.prototype.y = function (y, yScale) {
if (y == null) {
return _super.prototype.y.call(this);
}
if (yScale == null) {
_super.prototype.y.call(this, y);
}
else {
_super.prototype.y.call(this, y, yScale);
yScale.onUpdate(this._updateBarPixelWidthCallback);
}
this._updateValueScale();
return this;
};
/**
* Gets the orientation of the plot
*
* @return "vertical" | "horizontal"
*/
Bar.prototype.orientation = function () {
return this._isVertical ? Bar.ORIENTATION_VERTICAL : Bar.ORIENTATION_HORIZONTAL;
};
Bar.prototype.render = function () {
this._updateBarPixelWidth();
this._updateExtents();
_super.prototype.render.call(this);
return this;
};
Bar.prototype._createDrawer = function (dataset) {
return new Plottable.Drawers.Rectangle(dataset);
};
Bar.prototype._setup = function () {
_super.prototype._setup.call(this);
this._baseline = this._renderArea.append("line").classed("baseline", true);
};
Bar.prototype.baselineValue = function (value) {
if (value == null) {
if (this._baselineValue != null) {
return this._baselineValue;
}
if (!this._projectorsReady()) {
return 0;
}
var valueScale = this._isVertical ? this.y().scale : this.x().scale;
if (!valueScale) {
return 0;
}
if (valueScale instanceof Plottable.Scales.Time) {
return new Date(0);
}
return 0;
}
this._baselineValue = value;
this._updateValueScale();
this.render();
return this;
};
Bar.prototype.addDataset = function (dataset) {
_super.prototype.addDataset.call(this, dataset);
this._updateBarPixelWidth();
return this;
};
Bar.prototype._addDataset = function (dataset) {
dataset.onUpdate(this._updateBarPixelWidthCallback);
_super.prototype._addDataset.call(this, dataset);
return this;
};
Bar.prototype.removeDataset = function (dataset) {
dataset.offUpdate(this._updateBarPixelWidthCallback);
_super.prototype.removeDataset.call(this, dataset);
this._updateBarPixelWidth();
return this;
};
Bar.prototype._removeDataset = function (dataset) {
dataset.offUpdate(this._updateBarPixelWidthCallback);
_super.prototype._removeDataset.call(this, dataset);
return this;
};
Bar.prototype.datasets = function (datasets) {
if (datasets == null) {
return _super.prototype.datasets.call(this);
}
_super.prototype.datasets.call(this, datasets);
this._updateBarPixelWidth();
return this;
};
Bar.prototype.labelsEnabled = function (enabled) {
if (enabled == null) {
return this._labelsEnabled;
}
else {
this._labelsEnabled = enabled;
this.render();
return this;
}
};
Bar.prototype.labelFormatter = function (formatter) {
if (formatter == null) {
return this._labelFormatter;
}
else {
this._labelFormatter = formatter;
this.render();
return this;
}
};
Bar.prototype._createNodesForDataset = function (dataset) {
var drawer = _super.prototype._createNodesForDataset.call(this, dataset);
drawer.renderArea().classed(Bar._BAR_AREA_CLASS, true);
var labelArea = this._renderArea.append("g").classed(Bar._LABEL_AREA_CLASS, true);
var measurer = new SVGTypewriter.CacheMeasurer(labelArea);
var writer = new SVGTypewriter.Writer(measurer);
this._labelConfig.set(dataset, { labelArea: labelArea, measurer: measurer, writer: writer });
return drawer;
};
Bar.prototype._removeDatasetNodes = function (dataset) {
_super.prototype._removeDatasetNodes.call(this, dataset);
var labelConfig = this._labelConfig.get(dataset);
if (labelConfig != null) {
labelConfig.labelArea.remove();
this._labelConfig.delete(dataset);
}
};
/**
* Returns the PlotEntity nearest to the query point according to the following algorithm:
* - If the query point is inside a bar, returns the PlotEntity for that bar.
* - Otherwise, gets the nearest PlotEntity by the primary direction (X for vertical, Y for horizontal),
* breaking ties with the secondary direction.
* Returns undefined if no PlotEntity can be found.
*
* @param {Point} queryPoint
* @returns {PlotEntity} The nearest PlotEntity, or undefined if no PlotEntity can be found.
*/
Bar.prototype.entityNearest = function (queryPoint) {
var _this = this;
var minPrimaryDist = Infinity;
var minSecondaryDist = Infinity;
var queryPtPrimary = this._isVertical ? queryPoint.x : queryPoint.y;
var queryPtSecondary = this._isVertical ? queryPoint.y : queryPoint.x;
// SVGRects are positioned with sub-pixel accuracy (the default unit
// for the x, y, height & width attributes), but user selections (e.g. via
// mouse events) usually have pixel accuracy. We add a tolerance of 0.5 pixels.
var tolerance = 0.5;
var chartBounds = this.bounds();
var closest;
this.entities().forEach(function (entity) {
if (!_this._entityVisibleOnPlot(entity, chartBounds)) {
return;
}
var primaryDist = 0;
var secondaryDist = 0;
var plotPt = entity.position;
// if we're inside a bar, distance in both directions should stay 0
var barBBox = Plottable.Utils.DOM.elementBBox(entity.selection);
if (!Plottable.Utils.DOM.intersectsBBox(queryPoint.x, queryPoint.y, barBBox, tolerance)) {
var plotPtPrimary = _this._isVertical ? plotPt.x : plotPt.y;
primaryDist = Math.abs(queryPtPrimary - plotPtPrimary);
// compute this bar's min and max along the secondary axis
var barMinSecondary = _this._isVertical ? barBBox.y : barBBox.x;
var barMaxSecondary = barMinSecondary + (_this._isVertical ? barBBox.height : barBBox.width);
if (queryPtSecondary >= barMinSecondary - tolerance && queryPtSecondary <= barMaxSecondary + tolerance) {
// if we're within a bar's secondary axis span, it is closest in that direction
secondaryDist = 0;
}
else {
var plotPtSecondary = _this._isVertical ? plotPt.y : plotPt.x;
secondaryDist = Math.abs(queryPtSecondary - plotPtSecondary);
}
}
// if we find a closer bar, record its distance and start new closest lists
if (primaryDist < minPrimaryDist
|| primaryDist === minPrimaryDist && secondaryDist < minSecondaryDist) {
closest = entity;
minPrimaryDist = primaryDist;
minSecondaryDist = secondaryDist;
}
});
return closest;
};
Bar.prototype._entityVisibleOnPlot = function (entity, bounds) {
var chartWidth = bounds.bottomRight.x - bounds.topLeft.x;
var chartHeight = bounds.bottomRight.y - bounds.topLeft.y;
var xRange = { min: 0, max: chartWidth };
var yRange = { min: 0, max: chartHeight };
var attrToProjector = this._generateAttrToProjector();
var datum = entity.datum, index = entity.index, dataset = entity.dataset;
var barBBox = {
x: attrToProjector["x"](datum, index, dataset),
y: attrToProjector["y"](datum, index, dataset),
width: attrToProjector["width"](datum, index, dataset),
height: attrToProjector["height"](datum, index, dataset),
};
return Plottable.Utils.DOM.intersectsBBox(xRange, yRange, barBBox);
};
/**
* Gets the Entities at a particular Point.
*
* @param {Point} p
* @returns {PlotEntity[]}
*/
Bar.prototype.entitiesAt = function (p) {
return this._entitiesIntersecting(p.x, p.y);
};
Bar.prototype.entitiesIn = function (xRangeOrBounds, yRange) {
var dataXRange;
var dataYRange;
if (yRange == null) {
var bounds = xRangeOrBounds;
dataXRange = { min: bounds.topLeft.x, max: bounds.bottomRight.x };
dataYRange = { min: bounds.topLeft.y, max: bounds.bottomRight.y };
}
else {
dataXRange = xRangeOrBounds;
dataYRange = yRange;
}
return this._entitiesIntersecting(dataXRange, dataYRange);
};
Bar.prototype._entitiesIntersecting = function (xValOrRange, yValOrRange) {
var intersected = [];
this.entities().forEach(function (entity) {
if (Plottable.Utils.DOM.intersectsBBox(xValOrRange, yValOrRange, Plottable.Utils.DOM.elementBBox(entity.selection))) {
intersected.push(entity);
}
});
return intersected;
};
Bar.prototype._updateValueScale = function () {
if (!this._projectorsReady()) {
return;
}
var valueScale = this._isVertical ? this.y().scale : this.x().scale;
if (valueScale instanceof Plottable.QuantitativeScale) {
var qscale = valueScale;
qscale.addPaddingExceptionsProvider(this._baselineValueProvider);
qscale.addIncludedValuesProvider(this._baselineValueProvider);
}
};
Bar.prototype._additionalPaint = function (time) {
var _this = this;
var primaryScale = this._isVertical ? this.y().scale : this.x().scale;
var scaledBaseline = primaryScale.scale(this.baselineValue());
var baselineAttr = {
"x1": this._isVertical ? 0 : scaledBaseline,
"y1": this._isVertical ? scaledBaseline : 0,
"x2": this._isVertical ? this.width() : scaledBaseline,
"y2": this._isVertical ? scaledBaseline : this.height(),
};
this._getAnimator("baseline").animate(this._baseline, baselineAttr);
this.datasets().forEach(function (dataset) { return _this._labelConfig.get(dataset).labelArea.selectAll("g").remove(); });
if (this._labelsEnabled) {
Plottable.Utils.Window.setTimeout(function () { return _this._drawLabels(); }, time);
}
};
/**
* Makes sure the extent takes into account the widths of the bars
*/
Bar.prototype._extentsForProperty = function (property) {
var _this = this;
var extents = _super.prototype._extentsForProperty.call(this, property);
var accScaleBinding;
if (property === "x" && this._isVertical) {
accScaleBinding = this.x();
}
else if (property === "y" && !this._isVertical) {
accScaleBinding = this.y();
}
else {
return extents;
}
if (!(accScaleBinding && accScaleBinding.scale && accScaleBinding.scale instanceof Plottable.QuantitativeScale)) {
return extents;
}
var scale = accScaleBinding.scale;
// To account for inverted domains
extents = extents.map(function (extent) { return d3.extent([
scale.invert(scale.scale(extent[0]) - _this._barPixelWidth / 2),
scale.invert(scale.scale(extent[0]) + _this._barPixelWidth / 2),
scale.invert(scale.scale(extent[1]) - _this._barPixelWidth / 2),
scale.invert(scale.scale(extent[1]) + _this._barPixelWidth / 2),
]); });
return extents;
};
Bar.prototype._drawLabels = function () {
var _this = this;
var dataToDraw = this._getDataToDraw();
var labelsTooWide = false;
this.datasets().forEach(function (dataset) { return labelsTooWide = labelsTooWide || _this._drawLabel(dataToDraw.get(dataset), dataset); });
if (this._hideBarsIfAnyAreTooWide && labelsTooWide) {
this.datasets().forEach(function (dataset) { return _this._labelConfig.get(dataset).labelArea.selectAll("g").remove(); });
}
};
Bar.prototype._drawLabel = function (data, dataset) {
var _this = this;
var attrToProjector = this._generateAttrToProjector();
var labelConfig = this._labelConfig.get(dataset);
var labelArea = labelConfig.labelArea;
var measurer = labelConfig.measurer;
var writer = labelConfig.writer;
var drawLabel = function (d, i) {
var valueAccessor = _this._isVertical ? _this.y().accessor : _this.x().accessor;
var value = valueAccessor(d, i, dataset);
var valueScale = _this._isVertical ? _this.y().scale : _this.x().scale;
var scaledValue = valueScale != null ? valueScale.scale(value) : value;
var scaledBaseline = valueScale != null ? valueScale.scale(_this.baselineValue()) : _this.baselineValue();
var barWidth = attrToProjector["width"](d, i, dataset);
var barHeight = attrToProjector["height"](d, i, dataset);
var text = _this._labelFormatter(valueAccessor(d, i, dataset));
var measurement = measurer.measure(text);
var xAlignment = "center";
var yAlignment = "center";
var labelContainerOrigin = {
x: attrToProjector["x"](d, i, dataset),
y: attrToProjector["y"](d, i, dataset),
};
var containerWidth = barWidth;
var containerHeight = barHeight;
var labelOrigin = {
x: labelContainerOrigin.x,
y: labelContainerOrigin.y,
};
var showLabelOnBar;
if (_this._isVertical) {
labelOrigin.x += containerWidth / 2 - measurement.width / 2;
var barY = attrToProjector["y"](d, i, dataset);
var effectiveBarHeight = barHeight;
if (barY + barHeight > _this.height()) {
effectiveBarHeight = _this.height() - barY;
}
else if (barY < 0) {
effectiveBarHeight = barY + barHeight;
}
var offset = Bar._LABEL_VERTICAL_PADDING;
showLabelOnBar = measurement.height + 2 * offset <= effectiveBarHeight;
if (showLabelOnBar) {
if (scaledValue < scaledBaseline) {
labelContainerOrigin.y += offset;
yAlignment = "top";
labelOrigin.y += offset;
}
else {
labelContainerOrigin.y -= offset;
yAlignment = "bottom";
labelOrigin.y += containerHeight - offset - measurement.height;
}
}
else {
containerHeight = barHeight + offset + measurement.height;
if (scaledValue <= scaledBaseline) {
labelContainerOrigin.y -= offset + measurement.height;
yAlignment = "top";
labelOrigin.y -= offset + measurement.height;
}
else {
yAlignment = "bottom";
labelOrigin.y += barHeight + offset;
}
}
}
else {
labelOrigin.y += containerHeight / 2 - measurement.height / 2;
var barX = attrToProjector["x"](d, i, dataset);
var effectiveBarWidth = barWidth;
if (barX + barWidth > _this.width()) {
effectiveBarWidth = _this.width() - barX;
}
else if (barX < 0) {
effectiveBarWidth = barX + barWidth;
}
var offset = Bar._LABEL_HORIZONTAL_PADDING;
showLabelOnBar = measurement.width + 2 * offset <= effectiveBarWidth;
if (showLabelOnBar) {
if (scaledValue < scaledBaseline) {
labelContainerOrigin.x += offset;
xAlignment = "left";
labelOrigin.x += offset;
}
else {
labelContainerOrigin.x -= offset;
xAlignment = "right";
labelOrigin.x += containerWidth - offset - measurement.width;
}
}
else {
containerWidth = barWidth + offset + measurement.width;
if (scaledValue < scaledBaseline) {
labelContainerOrigin.x -= offset + measurement.width;
xAlignment = "left";
labelOrigin.x -= offset + measurement.width;
}
else {
xAlignment = "right";
labelOrigin.x += barWidth + offset;
}
}
}
var labelContainer = labelArea.append("g").attr("transform", "translate(" + labelContainerOrigin.x + ", " + labelContainerOrigin.y + ")");
if (showLabelOnBar) {
labelContainer.classed("on-bar-label", true);
var color = attrToProjector["fill"](d, i, dataset);
var dark = Plottable.Utils.Color.contrast("white", color) * 1.6 < Plottable.Utils.Color.contrast("black", color);
labelContainer.classed(dark ? "dark-label" : "light-label", true);
}
else {
labelContainer.classed("off-bar-label", true);
}
var hideLabel = labelOrigin.x < 0 ||
labelOrigin.y < 0 ||
labelOrigin.x + measurement.width > _this.width() ||
labelOrigin.y + measurement.height > _this.height();
labelContainer.style("visibility", hideLabel ? "hidden" : "inherit");
var writeOptions = {
selection: labelContainer,
xAlign: xAlignment,
yAlign: yAlignment,
textRotation: 0,
};
writer.write(text, containerWidth, containerHeight, writeOptions);
var tooWide = _this._isVertical ? barWidth < measurement.width : barHeight < measurement.height;
return tooWide;
};
var labelTooWide = data.map(drawLabel);
return labelTooWide.some(function (d) { return d; });
};
Bar.prototype._generateDrawSteps = function () {
var drawSteps = [];
if (this._animateOnNextRender()) {
var resetAttrToProjector = this._generateAttrToProjector();
var primaryScale = this._isVertical ? this.y().scale : this.x().scale;
var scaledBaseline_1 = primaryScale.scale(this.baselineValue());
var positionAttr = this._isVertical ? "y" : "x";
var dimensionAttr = this._isVertical ? "height" : "width";
resetAttrToProjector[positionAttr] = function () { return scaledBaseline_1; };
resetAttrToProjector[dimensionAttr] = function () { return 0; };
drawSteps.push({ attrToProjector: resetAttrToProjector, animator: this._getAnimator(Plots.Animator.RESET) });
}
drawSteps.push({ attrToProjector: this._generateAttrToProjector(), animator: this._getAnimator(Plots.Animator.MAIN) });
return drawSteps;
};
Bar.prototype._generateAttrToProjector = function () {
// Primary scale/direction: the "length" of the bars
// Secondary scale/direction: the "width" of the bars
var attrToProjector = _super.prototype._generateAttrToProjector.call(this);
var primaryScale = this._isVertical ? this.y().scale : this.x().scale;
var primaryAttr = this._isVertical ? "y" : "x";
var secondaryAttr = this._isVertical ? "x" : "y";
var scaledBaseline = primaryScale.scale(this.baselineValue());
var positionF = this._isVertical ? Plottable.Plot._scaledAccessor(this.x()) : Plottable.Plot._scaledAccessor(this.y());
var widthF = attrToProjector["width"];
var originalPositionFn = this._isVertical ? Plottable.Plot._scaledAccessor(this.y()) : Plottable.Plot._scaledAccessor(this.x());
var heightF = function (d, i, dataset) {
return Math.abs(scaledBaseline - originalPositionFn(d, i, dataset));
};
attrToProjector["width"] = this._isVertical ? widthF : heightF;
attrToProjector["height"] = this._isVertical ? heightF : widthF;
attrToProjector[secondaryAttr] = function (d, i, dataset) {
return positionF(d, i, dataset) - widthF(d, i, dataset) / 2;
};
attrToProjector[primaryAttr] = function (d, i, dataset) {
var originalPos = originalPositionFn(d, i, dataset);
// If it is past the baseline, it should start at the baselin then width/height
// carries it over. If it's not past the baseline, leave it at original position and
// then width/height carries it to baseline
return (originalPos > scaledBaseline) ? scaledBaseline : originalPos;
};
return attrToProjector;
};
/**
* Computes the barPixelWidth of all the bars in the plot.
*
* If the position scale of the plot is a CategoryScale and in bands mode, then the rangeBands function will be used.
* If the position scale of the plot is a QuantitativeScale, then the bar width is equal to the smallest distance between
* two adjacent data points, padded for visualisation.
*/
Bar.prototype._getBarPixelWidth = function () {
if (!this._projectorsReady()) {
return 0;
}
var barPixelWidth;
var barScale = this._isVertical ? this.x().scale : this.y().scale;
if (barScale instanceof Plottable.Scales.Category) {
barPixelWidth = barScale.rangeBand();
}
else {
var barAccessor_1 = this._isVertical ? this.x().accessor : this.y().accessor;
var numberBarAccessorData = d3.set(Plottable.Utils.Array.flatten(this.datasets().map(function (dataset) {
return dataset.data().map(function (d, i) { return barAccessor_1(d, i, dataset); })
.filter(function (d) { return d != null; })
.map(function (d) { return d.valueOf(); });
}))).values().map(function (value) { return +value; });
numberBarAccessorData.sort(function (a, b) { return a - b; });
var scaledData = numberBarAccessorData.map(function (datum) { return barScale.scale(datum); });
var barAccessorDataPairs = d3.pairs(scaledData);
var barWidthDimension = this._isVertical ? this.width() : this.height();
barPixelWidth = Plottable.Utils.Math.min(barAccessorDataPairs, function (pair, i) {
return Math.abs(pair[1] - pair[0]);
}, barWidthDimension * Bar._SINGLE_BAR_DIMENSION_RATIO);
barPixelWidth *= Bar._BAR_WIDTH_RATIO;
}
return barPixelWidth;
};
Bar.prototype._updateBarPixelWidth = function () {
this._barPixelWidth = this._getBarPixelWidth();
};
Bar.prototype.entities = function (datasets) {
if (datasets === void 0) { datasets = this.datasets(); }
if (!this._projectorsReady()) {
return [];
}
var entities = _super.prototype.entities.call(this, datasets);
return entities;
};
Bar.prototype._pixelPoint = function (datum, index, dataset) {
var attrToProjector = this._generateAttrToProjector();
var rectX = attrToProjector["x"](datum, index, dataset);
var rectY = attrToProjector["y"](datum, index, dataset);
var rectWidth = attrToProjector["width"](datum, index, dataset);
var rectHeight = attrToProjector["height"](datum, index, dataset);
var x;
var y;
var originalPosition = (this._isVertical ? Plottable.Plot._scaledAccessor(this.y()) : Plottable.Plot._scaledAccessor(this.x()))(datum, index, dataset);
var scaledBaseline = (this._isVertical ? this.y().scale : this.x().scale).scale(this.baselineValue());
if (this._isVertical) {
x = rectX + rectWidth / 2;
y = originalPosition <= scaledBaseline ? rectY : rectY + rectHeight;
}
else {
x = originalPosition >= scaledBaseline ? rectX + rectWidth : rectX;
y = rectY + rectHeight / 2;
}
return { x: x, y: y };
};
Bar.prototype._uninstallScaleForKey = function (scale, key) {
scale.offUpdate(this._updateBarPixelWidthCallback);
_super.prototype._uninstallScaleForKey.call(this, scale, key);
};
Bar.prototype._getDataToDraw = function () {
var dataToDraw = new Plottable.Utils.Map();
var attrToProjector = this._generateAttrToProjector();
this.datasets().forEach(function (dataset) {
var data = dataset.data().filter(function (d, i) { return Plottable.Utils.Math.isValidNumber(attrToProjector["x"](d, i, dataset)) &&
Plottable.Utils.Math.isValidNumber(attrToProjector["y"](d, i, dataset)) &&
Plottable.Utils.Math.isValidNumber(attrToProjector["width"](d, i, dataset)) &&
Plottable.Utils.Math.isValidNumber(attrToProjector["height"](d, i, dataset)); });
dataToDraw.set(dataset, data);
});
return dataToDraw;
};
Bar.ORIENTATION_VERTICAL = "vertical";
Bar.ORIENTATION_HORIZONTAL = "horizontal";
Bar._BAR_WIDTH_RATIO = 0.95;
Bar._SINGLE_BAR_DIMENSION_RATIO = 0.4;
Bar._BAR_AREA_CLASS = "bar-area";
Bar._LABEL_VERTICAL_PADDING = 5;
Bar._LABEL_HORIZONTAL_PADDING = 5;
Bar._LABEL_AREA_CLASS = "bar-label-text-area";
return Bar;
}(Plottable.XYPlot));
Plots.Bar = Bar;
})(Plots = Plottable.Plots || (Plottable.Plots = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Plots;
(function (Plots) {
var Line = (function (_super) {
__extends(Line, _super);
/**
* A Line Plot draws line segments starting from the first data point to the next.
*
* @constructor
*/
function Line() {
_super.call(this);
this._interpolator = "linear";
this._autorangeSmooth = false;
this._croppedRenderingEnabled = true;
this._downsamplingEnabled = false;
this.addClass("line-plot");
var animator = new Plottable.Animators.Easing();
animator.stepDuration(Plottable.Plot._ANIMATION_MAX_DURATION);
animator.easingMode("exp-in-out");
animator.maxTotalDuration(Plottable.Plot._ANIMATION_MAX_DURATION);
this.animator(Plots.Animator.MAIN, animator);
this.attr("stroke", new Plottable.Scales.Color().range()[0]);
this.attr("stroke-width", "2px");
}
Line.prototype.x = function (x, xScale) {
if (x == null) {
return _super.prototype.x.call(this);
}
else {
if (xScale == null) {
_super.prototype.x.call(this, x);
}
else {
_super.prototype.x.call(this, x, xScale);
}
this._setScaleSnapping();
return this;
}
};
Line.prototype.y = function (y, yScale) {
if (y == null) {
return _super.prototype.y.call(this);
}
else {
_super.prototype.y.call(this, y, yScale);
this._setScaleSnapping();
return this;
}
};
Line.prototype.autorangeMode = function (autorangeMode) {
if (autorangeMode == null) {
return _super.prototype.autorangeMode.call(this);
}
_super.prototype.autorangeMode.call(this, autorangeMode);
this._setScaleSnapping();
return this;
};
Line.prototype.autorangeSmooth = function (autorangeSmooth) {
if (autorangeSmooth == null) {
return this._autorangeSmooth;
}
this._autorangeSmooth = autorangeSmooth;
this._setScaleSnapping();
return this;
};
Line.prototype._setScaleSnapping = function () {
if (this.autorangeMode() === "x" && this.x() && this.x().scale && this.x().scale instanceof Plottable.QuantitativeScale) {
this.x().scale.snappingDomainEnabled(!this.autorangeSmooth());
}
if (this.autorangeMode() === "y" && this.y() && this.y().scale && this.y().scale instanceof Plottable.QuantitativeScale) {
this.y().scale.snappingDomainEnabled(!this.autorangeSmooth());
}
};
Line.prototype.interpolator = function (interpolator) {
if (interpolator == null) {
return this._interpolator;
}
this._interpolator = interpolator;
this.render();
return this;
};
Line.prototype.downsamplingEnabled = function (downsampling) {
if (downsampling == null) {
return this._downsamplingEnabled;
}
this._downsamplingEnabled = downsampling;
return this;
};
Line.prototype.croppedRenderingEnabled = function (croppedRendering) {
if (croppedRendering == null) {
return this._croppedRenderingEnabled;
}
this._croppedRenderingEnabled = croppedRendering;
this.render();
return this;
};
Line.prototype._createDrawer = function (dataset) {
return new Plottable.Drawers.Line(dataset);
};
Line.prototype._extentsForProperty = function (property) {
var extents = _super.prototype._extentsForProperty.call(this, property);
if (!this._autorangeSmooth) {
return extents;
}
if (this.autorangeMode() !== property) {
return extents;
}
if (this.autorangeMode() !== "x" && this.autorangeMode() !== "y") {
return extents;
}
var edgeIntersectionPoints = this._getEdgeIntersectionPoints();
var includedValues;
if (this.autorangeMode() === "y") {
includedValues = edgeIntersectionPoints.left.concat(edgeIntersectionPoints.right).map(function (point) { return point.y; });
}
else {
includedValues = edgeIntersectionPoints.top.concat(edgeIntersectionPoints.bottom).map(function (point) { return point.x; });
}
return extents.map(function (extent) { return d3.extent(d3.merge([extent, includedValues])); });
};
Line.prototype._getEdgeIntersectionPoints = function () {
var _this = this;
if (!(this.y().scale instanceof Plottable.QuantitativeScale && this.x().scale instanceof Plottable.QuantitativeScale)) {
return {
left: [],
right: [],
top: [],
bottom: [],
};
}
var yScale = this.y().scale;
var xScale = this.x().scale;
var intersectionPoints = {
left: [],
right: [],
top: [],
bottom: [],
};
var leftX = xScale.scale(xScale.domain()[0]);
var rightX = xScale.scale(xScale.domain()[1]);
var bottomY = yScale.scale(yScale.domain()[0]);
var topY = yScale.scale(yScale.domain()[1]);
this.datasets().forEach(function (dataset) {
var data = dataset.data();
var x1, x2, y1, y2;
var prevX, prevY, currX, currY;
for (var i = 1; i < data.length; i++) {
prevX = currX || xScale.scale(_this.x().accessor(data[i - 1], i - 1, dataset));
prevY = currY || yScale.scale(_this.y().accessor(data[i - 1], i - 1, dataset));
currX = xScale.scale(_this.x().accessor(data[i], i, dataset));
currY = yScale.scale(_this.y().accessor(data[i], i, dataset));
// If values crossed left edge
if ((prevX < leftX) === (leftX <= currX)) {
x1 = leftX - prevX;
x2 = currX - prevX;
y2 = currY - prevY;
y1 = x1 * y2 / x2;
intersectionPoints.left.push({
x: leftX,
y: yScale.invert(prevY + y1),
});
}
// If values crossed right edge
if ((prevX < rightX) === (rightX <= currX)) {
x1 = rightX - prevX;
x2 = currX - prevX;
y2 = currY - prevY;
y1 = x1 * y2 / x2;
intersectionPoints.right.push({
x: rightX,
y: yScale.invert(prevY + y1),
});
}
// If values crossed upper edge
if ((prevY < topY) === (topY <= currY)) {
x2 = currX - prevX;
y1 = topY - prevY;
y2 = currY - prevY;
x1 = y1 * x2 / y2;
intersectionPoints.top.push({
x: xScale.invert(prevX + x1),
y: topY,
});
}
// If values crossed lower edge
if ((prevY < bottomY) === (bottomY <= currY)) {
x2 = currX - prevX;
y1 = bottomY - prevY;
y2 = currY - prevY;
x1 = y1 * x2 / y2;
intersectionPoints.bottom.push({
x: xScale.invert(prevX + x1),
y: bottomY,
});
}
}
;
});
return intersectionPoints;
};
Line.prototype._getResetYFunction = function () {
// gets the y-value generator for the animation start point
var yDomain = this.y().scale.domain();
var domainMax = Math.max(yDomain[0], yDomain[1]);
var domainMin = Math.min(yDomain[0], yDomain[1]);
// start from zero, or the closest domain value to zero
// avoids lines zooming on from offscreen.
var startValue = (domainMax < 0 && domainMax) || (domainMin > 0 && domainMin) || 0;
var scaledStartValue = this.y().scale.scale(startValue);
return function (d, i, dataset) { return scaledStartValue; };
};
Line.prototype._generateDrawSteps = function () {
var drawSteps = [];
if (this._animateOnNextRender()) {
var attrToProjector = this._generateAttrToProjector();
attrToProjector["d"] = this._constructLineProjector(Plottable.Plot._scaledAccessor(this.x()), this._getResetYFunction());
drawSteps.push({ attrToProjector: attrToProjector, animator: this._getAnimator(Plots.Animator.RESET) });
}
drawSteps.push({ attrToProjector: this._generateAttrToProjector(), animator: this._getAnimator(Plots.Animator.MAIN) });
return drawSteps;
};
Line.prototype._generateAttrToProjector = function () {
var attrToProjector = _super.prototype._generateAttrToProjector.call(this);
Object.keys(attrToProjector).forEach(function (attribute) {
if (attribute === "d") {
return;
}
var projector = attrToProjector[attribute];
attrToProjector[attribute] = function (data, i, dataset) {
return data.length > 0 ? projector(data[0], i, dataset) : null;
};
});
return attrToProjector;
};
Line.prototype.entitiesAt = function (point) {
var entity = this.entityNearestByXThenY(point);
if (entity != null) {
return [entity];
}
else {
return [];
}
};
Line.prototype.entitiesIn = function (xRangeOrBounds, yRange) {
var dataXRange;
var dataYRange;
if (yRange == null) {
var bounds = xRangeOrBounds;
dataXRange = { min: bounds.topLeft.x, max: bounds.bottomRight.x };
dataYRange = { min: bounds.topLeft.y, max: bounds.bottomRight.y };
}
else {
dataXRange = xRangeOrBounds;
dataYRange = yRange;
}
var xProjector = Plottable.Plot._scaledAccessor(this.x());
var yProjector = Plottable.Plot._scaledAccessor(this.y());
return this.entities().filter(function (entity) {
var datum = entity.datum, index = entity.index, dataset = entity.dataset;
var x = xProjector(datum, index, dataset);
var y = yProjector(datum, index, dataset);
return dataXRange.min <= x && x <= dataXRange.max && dataYRange.min <= y && y <= dataYRange.max;
});
};
/**
* Returns the PlotEntity nearest to the query point by X then by Y, or undefined if no PlotEntity can be found.
*
* @param {Point} queryPoint
* @returns {PlotEntity} The nearest PlotEntity, or undefined if no PlotEntity can be found.
*/
Line.prototype.entityNearestByXThenY = function (queryPoint) {
var _this = this;
var minXDist = Infinity;
var minYDist = Infinity;
var closest;
var chartBounds = this.bounds();
this.entities().forEach(function (entity) {
if (!_this._entityVisibleOnPlot(entity, chartBounds)) {
return;
}
var xDist = Math.abs(queryPoint.x - entity.position.x);
var yDist = Math.abs(queryPoint.y - entity.position.y);
if (xDist < minXDist || xDist === minXDist && yDist < minYDist) {
closest = entity;
minXDist = xDist;
minYDist = yDist;
}
});
return closest;
};
Line.prototype._propertyProjectors = function () {
var propertyToProjectors = _super.prototype._propertyProjectors.call(this);
propertyToProjectors["d"] = this._constructLineProjector(Plottable.Plot._scaledAccessor(this.x()), Plottable.Plot._scaledAccessor(this.y()));
return propertyToProjectors;
};
Line.prototype._constructLineProjector = function (xProjector, yProjector) {
var _this = this;
var definedProjector = function (d, i, dataset) {
var positionX = Plottable.Plot._scaledAccessor(_this.x())(d, i, dataset);
var positionY = Plottable.Plot._scaledAccessor(_this.y())(d, i, dataset);
return positionX != null && !Plottable.Utils.Math.isNaN(positionX) &&
positionY != null && !Plottable.Utils.Math.isNaN(positionY);
};
return function (datum, index, dataset) {
return d3.svg.line()
.x(function (innerDatum, innerIndex) { return xProjector(innerDatum, innerIndex, dataset); })
.y(function (innerDatum, innerIndex) { return yProjector(innerDatum, innerIndex, dataset); })
.interpolate(_this.interpolator())
.defined(function (innerDatum, innerIndex) { return definedProjector(innerDatum, innerIndex, dataset); })(datum);
};
};
Line.prototype._getDataToDraw = function () {
var _this = this;
var dataToDraw = new Plottable.Utils.Map();
this.datasets().forEach(function (dataset) {
var data = dataset.data();
if (!_this._croppedRenderingEnabled && !_this._downsamplingEnabled) {
dataToDraw.set(dataset, [data]);
return;
}
var filteredDataIndices = data.map(function (d, i) { return i; });
if (_this._croppedRenderingEnabled) {
filteredDataIndices = _this._filterCroppedRendering(dataset, filteredDataIndices);
}
if (_this._downsamplingEnabled) {
filteredDataIndices = _this._filterDownsampling(dataset, filteredDataIndices);
}
dataToDraw.set(dataset, [filteredDataIndices.map(function (d, i) { return data[d]; })]);
});
return dataToDraw;
};
Line.prototype._filterCroppedRendering = function (dataset, indices) {
var _this = this;
var xProjector = Plottable.Plot._scaledAccessor(this.x());
var yProjector = Plottable.Plot._scaledAccessor(this.y());
var data = dataset.data();
var filteredDataIndices = [];
var pointInViewport = function (x, y) {
return Plottable.Utils.Math.inRange(x, 0, _this.width()) &&
Plottable.Utils.Math.inRange(y, 0, _this.height());
};
for (var i = 0; i < indices.length; i++) {
var currXPoint = xProjector(data[indices[i]], indices[i], dataset);
var currYPoint = yProjector(data[indices[i]], indices[i], dataset);
var shouldShow = pointInViewport(currXPoint, currYPoint);
if (!shouldShow && indices[i - 1] != null && data[indices[i - 1]] != null) {
var prevXPoint = xProjector(data[indices[i - 1]], indices[i - 1], dataset);
var prevYPoint = yProjector(data[indices[i - 1]], indices[i - 1], dataset);
shouldShow = shouldShow || pointInViewport(prevXPoint, prevYPoint);
}
if (!shouldShow && indices[i + 1] != null && data[indices[i + 1]] != null) {
var nextXPoint = xProjector(data[indices[i + 1]], indices[i + 1], dataset);
var nextYPoint = yProjector(data[indices[i + 1]], indices[i + 1], dataset);
shouldShow = shouldShow || pointInViewport(nextXPoint, nextYPoint);
}
if (shouldShow) {
filteredDataIndices.push(indices[i]);
}
}
return filteredDataIndices;
};
Line.prototype._filterDownsampling = function (dataset, indices) {
if (indices.length === 0) {
return [];
}
var data = dataset.data();
var scaledXAccessor = Plottable.Plot._scaledAccessor(this.x());
var scaledYAccessor = Plottable.Plot._scaledAccessor(this.y());
var filteredIndices = [indices[0]];
var indexOnCurrentSlope = function (i, currentSlope) {
var p1x = scaledXAccessor(data[indices[i]], indices[i], dataset);
var p1y = scaledYAccessor(data[indices[i]], indices[i], dataset);
var p2x = scaledXAccessor(data[indices[i + 1]], indices[i + 1], dataset);
var p2y = scaledYAccessor(data[indices[i + 1]], indices[i + 1], dataset);
if (currentSlope === Infinity) {
return Math.floor(p1x) === Math.floor(p2x);
}
else {
var expectedP2y = p1y + (p2x - p1x) * currentSlope;
return Math.floor(p2y) === Math.floor(expectedP2y);
}
};
for (var i = 0; i < indices.length - 1;) {
var indexFirst = indices[i];
var p1x = scaledXAccessor(data[indices[i]], indices[i], dataset);
var p1y = scaledYAccessor(data[indices[i]], indices[i], dataset);
var p2x = scaledXAccessor(data[indices[i + 1]], indices[i + 1], dataset);
var p2y = scaledYAccessor(data[indices[i + 1]], indices[i + 1], dataset);
var currentSlope = (Math.floor(p1x) === Math.floor(p2x)) ? Infinity : (p2y - p1y) / (p2x - p1x);
var indexMin = indices[i];
var minScaledValue = (currentSlope === Infinity) ? p1y : p1x;
var indexMax = indexMin;
var maxScaledValue = minScaledValue;
var firstIndexOnCurrentSlope = true;
while (i < indices.length - 1 && (firstIndexOnCurrentSlope || indexOnCurrentSlope(i, currentSlope))) {
i++;
firstIndexOnCurrentSlope = false;
var currScaledValue = currentSlope === Infinity ? scaledYAccessor(data[indices[i]], indices[i], dataset) :
scaledXAccessor(data[indices[i]], indices[i], dataset);
if (currScaledValue > maxScaledValue) {
maxScaledValue = currScaledValue;
indexMax = indices[i];
}
if (currScaledValue < minScaledValue) {
minScaledValue = currScaledValue;
indexMin = indices[i];
}
}
var indexLast = indices[i];
if (indexMin !== indexFirst) {
filteredIndices.push(indexMin);
}
if (indexMax !== indexMin && indexMax !== indexFirst) {
filteredIndices.push(indexMax);
}
if (indexLast !== indexFirst && indexLast !== indexMin && indexLast !== indexMax) {
filteredIndices.push(indexLast);
}
}
return filteredIndices;
};
return Line;
}(Plottable.XYPlot));
Plots.Line = Line;
})(Plots = Plottable.Plots || (Plottable.Plots = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Plots;
(function (Plots) {
var Area = (function (_super) {
__extends(Area, _super);
/**
* An Area Plot draws a filled region (area) between Y and Y0.
*
* @constructor
*/
function Area() {
_super.call(this);
this.addClass("area-plot");
this.y0(0); // default
this.attr("fill-opacity", 0.25);
this.attr("fill", new Plottable.Scales.Color().range()[0]);
this._lineDrawers = new Plottable.Utils.Map();
}
Area.prototype._setup = function () {
var _this = this;
_super.prototype._setup.call(this);
this._lineDrawers.forEach(function (d) { return d.renderArea(_this._renderArea.append("g")); });
};
Area.prototype.y = function (y, yScale) {
if (y == null) {
return _super.prototype.y.call(this);
}
if (yScale == null) {
_super.prototype.y.call(this, y);
}
else {
_super.prototype.y.call(this, y, yScale);
}
if (yScale != null) {
var y0 = this.y0().accessor;
if (y0 != null) {
this._bindProperty(Area._Y0_KEY, y0, yScale);
}
this._updateYScale();
}
return this;
};
Area.prototype.y0 = function (y0) {
if (y0 == null) {
return this._propertyBindings.get(Area._Y0_KEY);
}
var yBinding = this.y();
var yScale = yBinding && yBinding.scale;
this._bindProperty(Area._Y0_KEY, y0, yScale);
this._updateYScale();
this.render();
return this;
};
Area.prototype._onDatasetUpdate = function () {
_super.prototype._onDatasetUpdate.call(this);
this._updateYScale();
};
Area.prototype.addDataset = function (dataset) {
_super.prototype.addDataset.call(this, dataset);
return this;
};
Area.prototype._addDataset = function (dataset) {
var lineDrawer = new Plottable.Drawers.Line(dataset);
if (this._isSetup) {
lineDrawer.renderArea(this._renderArea.append("g"));
}
this._lineDrawers.set(dataset, lineDrawer);
_super.prototype._addDataset.call(this, dataset);
return this;
};
Area.prototype._removeDatasetNodes = function (dataset) {
_super.prototype._removeDatasetNodes.call(this, dataset);
this._lineDrawers.get(dataset).remove();
};
Area.prototype._additionalPaint = function () {
var _this = this;
var drawSteps = this._generateLineDrawSteps();
var dataToDraw = this._getDataToDraw();
this.datasets().forEach(function (dataset) { return _this._lineDrawers.get(dataset).draw(dataToDraw.get(dataset), drawSteps); });
};
Area.prototype._generateLineDrawSteps = function () {
var drawSteps = [];
if (this._animateOnNextRender()) {
var attrToProjector = this._generateLineAttrToProjector();
attrToProjector["d"] = this._constructLineProjector(Plottable.Plot._scaledAccessor(this.x()), this._getResetYFunction());
drawSteps.push({ attrToProjector: attrToProjector, animator: this._getAnimator(Plots.Animator.RESET) });
}
drawSteps.push({ attrToProjector: this._generateLineAttrToProjector(), animator: this._getAnimator(Plots.Animator.MAIN) });
return drawSteps;
};
Area.prototype._generateLineAttrToProjector = function () {
var lineAttrToProjector = this._generateAttrToProjector();
lineAttrToProjector["d"] = this._constructLineProjector(Plottable.Plot._scaledAccessor(this.x()), Plottable.Plot._scaledAccessor(this.y()));
return lineAttrToProjector;
};
Area.prototype._createDrawer = function (dataset) {
return new Plottable.Drawers.Area(dataset);
};
Area.prototype._generateDrawSteps = function () {
var drawSteps = [];
if (this._animateOnNextRender()) {
var attrToProjector = this._generateAttrToProjector();
attrToProjector["d"] = this._constructAreaProjector(Plottable.Plot._scaledAccessor(this.x()), this._getResetYFunction(), Plottable.Plot._scaledAccessor(this.y0()));
drawSteps.push({ attrToProjector: attrToProjector, animator: this._getAnimator(Plots.Animator.RESET) });
}
drawSteps.push({ attrToProjector: this._generateAttrToProjector(), animator: this._getAnimator(Plots.Animator.MAIN) });
return drawSteps;
};
Area.prototype._updateYScale = function () {
var extents = this._propertyExtents.get("y0");
var extent = Plottable.Utils.Array.flatten(extents);
var uniqExtentVals = Plottable.Utils.Array.uniq(extent);
var constantBaseline = uniqExtentVals.length === 1 ? uniqExtentVals[0] : null;
var yBinding = this.y();
var yScale = (yBinding && yBinding.scale);
if (yScale == null) {
return;
}
if (this._constantBaselineValueProvider != null) {
yScale.removePaddingExceptionsProvider(this._constantBaselineValueProvider);
this._constantBaselineValueProvider = null;
}
if (constantBaseline != null) {
this._constantBaselineValueProvider = function () { return [constantBaseline]; };
yScale.addPaddingExceptionsProvider(this._constantBaselineValueProvider);
}
};
Area.prototype._getResetYFunction = function () {
return Plottable.Plot._scaledAccessor(this.y0());
};
Area.prototype._propertyProjectors = function () {
var propertyToProjectors = _super.prototype._propertyProjectors.call(this);
propertyToProjectors["d"] = this._constructAreaProjector(Plottable.Plot._scaledAccessor(this.x()), Plottable.Plot._scaledAccessor(this.y()), Plottable.Plot._scaledAccessor(this.y0()));
return propertyToProjectors;
};
Area.prototype.selections = function (datasets) {
var _this = this;
if (datasets === void 0) { datasets = this.datasets(); }
var allSelections = _super.prototype.selections.call(this, datasets)[0];
var lineDrawers = datasets.map(function (dataset) { return _this._lineDrawers.get(dataset); })
.filter(function (drawer) { return drawer != null; });
lineDrawers.forEach(function (ld, i) { return allSelections.push(ld.selectionForIndex(i).node()); });
return d3.selectAll(allSelections);
};
Area.prototype._constructAreaProjector = function (xProjector, yProjector, y0Projector) {
var _this = this;
var definedProjector = function (d, i, dataset) {
var positionX = Plottable.Plot._scaledAccessor(_this.x())(d, i, dataset);
var positionY = Plottable.Plot._scaledAccessor(_this.y())(d, i, dataset);
return Plottable.Utils.Math.isValidNumber(positionX) && Plottable.Utils.Math.isValidNumber(positionY);
};
return function (datum, index, dataset) {
var areaGenerator = d3.svg.area()
.x(function (innerDatum, innerIndex) { return xProjector(innerDatum, innerIndex, dataset); })
.y1(function (innerDatum, innerIndex) { return yProjector(innerDatum, innerIndex, dataset); })
.y0(function (innerDatum, innerIndex) { return y0Projector(innerDatum, innerIndex, dataset); })
.interpolate(_this.interpolator())
.defined(function (innerDatum, innerIndex) { return definedProjector(innerDatum, innerIndex, dataset); });
return areaGenerator(datum);
};
};
Area._Y0_KEY = "y0";
return Area;
}(Plots.Line));
Plots.Area = Area;
})(Plots = Plottable.Plots || (Plottable.Plots = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Plots;
(function (Plots) {
var ClusteredBar = (function (_super) {
__extends(ClusteredBar, _super);
/**
* A ClusteredBar Plot groups bars across Datasets based on the primary value of the bars.
* On a vertical ClusteredBar Plot, the bars with the same X value are grouped.
* On a horizontal ClusteredBar Plot, the bars with the same Y value are grouped.
*
* @constructor
* @param {string} [orientation="vertical"] One of "vertical"/"horizontal".
*/
function ClusteredBar(orientation) {
if (orientation === void 0) { orientation = Plots.Bar.ORIENTATION_VERTICAL; }
_super.call(this, orientation);
this._clusterOffsets = new Plottable.Utils.Map();
}
ClusteredBar.prototype._generateAttrToProjector = function () {
var _this = this;
var attrToProjector = _super.prototype._generateAttrToProjector.call(this);
// the width is constant, so set the inner scale range to that
var innerScale = this._makeInnerScale();
var innerWidthF = function (d, i) { return innerScale.rangeBand(); };
attrToProjector["width"] = this._isVertical ? innerWidthF : attrToProjector["width"];
attrToProjector["height"] = !this._isVertical ? innerWidthF : attrToProjector["height"];
var xAttr = attrToProjector["x"];
var yAttr = attrToProjector["y"];
attrToProjector["x"] = this._isVertical ?
function (d, i, ds) { return xAttr(d, i, ds) + _this._clusterOffsets.get(ds); } :
function (d, i, ds) { return xAttr(d, i, ds); };
attrToProjector["y"] = this._isVertical ?
function (d, i, ds) { return yAttr(d, i, ds); } :
function (d, i, ds) { return yAttr(d, i, ds) + _this._clusterOffsets.get(ds); };
return attrToProjector;
};
ClusteredBar.prototype._updateClusterPosition = function () {
var _this = this;
var innerScale = this._makeInnerScale();
this.datasets().forEach(function (d, i) { return _this._clusterOffsets.set(d, innerScale.scale(String(i)) - innerScale.rangeBand() / 2); });
};
ClusteredBar.prototype._makeInnerScale = function () {
var innerScale = new Plottable.Scales.Category();
innerScale.domain(this.datasets().map(function (d, i) { return String(i); }));
var widthProjector = Plottable.Plot._scaledAccessor(this.attr("width"));
innerScale.range([0, widthProjector(null, 0, null)]);
return innerScale;
};
ClusteredBar.prototype._getDataToDraw = function () {
this._updateClusterPosition();
return _super.prototype._getDataToDraw.call(this);
};
return ClusteredBar;
}(Plots.Bar));
Plots.ClusteredBar = ClusteredBar;
})(Plots = Plottable.Plots || (Plottable.Plots = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Plots;
(function (Plots) {
var StackedArea = (function (_super) {
__extends(StackedArea, _super);
/**
* @constructor
*/
function StackedArea() {
var _this = this;
_super.call(this);
this._baselineValue = 0;
this.addClass("stacked-area-plot");
this.attr("fill-opacity", 1);
this._stackingResult = new Plottable.Utils.Map();
this._stackedExtent = [];
this._baselineValueProvider = function () { return [_this._baselineValue]; };
this.croppedRenderingEnabled(false);
}
StackedArea.prototype.croppedRenderingEnabled = function (croppedRendering) {
if (croppedRendering == null) {
return _super.prototype.croppedRenderingEnabled.call(this);
}
if (croppedRendering === true) {
// HACKHACK #3032: cropped rendering doesn't currently work correctly on StackedArea
Plottable.Utils.Window.warn("Warning: Stacked Area Plot does not support cropped rendering.");
return this;
}
return _super.prototype.croppedRenderingEnabled.call(this, croppedRendering);
};
StackedArea.prototype._getAnimator = function (key) {
return new Plottable.Animators.Null();
};
StackedArea.prototype._setup = function () {
_super.prototype._setup.call(this);
this._baseline = this._renderArea.append("line").classed("baseline", true);
};
StackedArea.prototype.x = function (x, xScale) {
if (x == null) {
return _super.prototype.x.call(this);
}
if (xScale == null) {
_super.prototype.x.call(this, x);
}
else {
_super.prototype.x.call(this, x, xScale);
}
this._updateStackExtentsAndOffsets();
return this;
};
StackedArea.prototype.y = function (y, yScale) {
if (y == null) {
return _super.prototype.y.call(this);
}
if (yScale == null) {
_super.prototype.y.call(this, y);
}
else {
_super.prototype.y.call(this, y, yScale);
}
this._updateStackExtentsAndOffsets();
return this;
};
StackedArea.prototype.downsamplingEnabled = function (downsampling) {
if (downsampling == null) {
return _super.prototype.downsamplingEnabled.call(this);
}
Plottable.Utils.Window.warn("Warning: Stacked Area Plot does not support downsampling");
return this;
};
StackedArea.prototype._additionalPaint = function () {
var scaledBaseline = this.y().scale.scale(this._baselineValue);
var baselineAttr = {
"x1": 0,
"y1": scaledBaseline,
"x2": this.width(),
"y2": scaledBaseline,
};
this._getAnimator("baseline").animate(this._baseline, baselineAttr);
};
StackedArea.prototype._updateYScale = function () {
var yBinding = this.y();
var scale = (yBinding && yBinding.scale);
if (scale == null) {
return;
}
scale.addPaddingExceptionsProvider(this._baselineValueProvider);
scale.addIncludedValuesProvider(this._baselineValueProvider);
};
StackedArea.prototype._onDatasetUpdate = function () {
this._updateStackExtentsAndOffsets();
_super.prototype._onDatasetUpdate.call(this);
return this;
};
StackedArea.prototype._updateExtentsForProperty = function (property) {
_super.prototype._updateExtentsForProperty.call(this, property);
if ((property === "x" || property === "y") && this._projectorsReady()) {
this._updateStackExtentsAndOffsets();
}
};
StackedArea.prototype._extentsForProperty = function (attr) {
var primaryAttr = "y";
if (attr === primaryAttr) {
return [this._stackedExtent];
}
else {
return _super.prototype._extentsForProperty.call(this, attr);
}
};
StackedArea.prototype._updateStackExtentsAndOffsets = function () {
if (!this._projectorsReady()) {
return;
}
var datasets = this.datasets();
var keyAccessor = this.x().accessor;
var valueAccessor = this.y().accessor;
var filter = this._filterForProperty("y");
this._checkSameDomain(datasets, keyAccessor);
this._stackingResult = Plottable.Utils.Stacking.stack(datasets, keyAccessor, valueAccessor);
this._stackedExtent = Plottable.Utils.Stacking.stackedExtent(this._stackingResult, keyAccessor, filter);
};
StackedArea.prototype._checkSameDomain = function (datasets, keyAccessor) {
var keySets = datasets.map(function (dataset) {
return d3.set(dataset.data().map(function (datum, i) { return keyAccessor(datum, i, dataset).toString(); })).values();
});
var domainKeys = StackedArea._domainKeys(datasets, keyAccessor);
if (keySets.some(function (keySet) { return keySet.length !== domainKeys.length; })) {
Plottable.Utils.Window.warn("the domains across the datasets are not the same. Plot may produce unintended behavior.");
}
};
/**
* Given an array of Datasets and the accessor function for the key, computes the
* set reunion (no duplicates) of the domain of each Dataset. The keys are stringified
* before being returned.
*
* @param {Dataset[]} datasets The Datasets for which we extract the domain keys
* @param {Accessor<any>} keyAccessor The accessor for the key of the data
* @return {string[]} An array of stringified keys
*/
StackedArea._domainKeys = function (datasets, keyAccessor) {
var domainKeys = d3.set();
datasets.forEach(function (dataset) {
dataset.data().forEach(function (datum, index) {
domainKeys.add(keyAccessor(datum, index, dataset));
});
});
return domainKeys.values();
};
StackedArea.prototype._propertyProjectors = function () {
var _this = this;
var propertyToProjectors = _super.prototype._propertyProjectors.call(this);
var yAccessor = this.y().accessor;
var xAccessor = this.x().accessor;
var normalizedXAccessor = function (datum, index, dataset) {
return Plottable.Utils.Stacking.normalizeKey(xAccessor(datum, index, dataset));
};
var stackYProjector = function (d, i, dataset) {
return _this.y().scale.scale(+yAccessor(d, i, dataset) + _this._stackingResult.get(dataset).get(normalizedXAccessor(d, i, dataset)).offset);
};
var stackY0Projector = function (d, i, dataset) {
return _this.y().scale.scale(_this._stackingResult.get(dataset).get(normalizedXAccessor(d, i, dataset)).offset);
};
propertyToProjectors["d"] = this._constructAreaProjector(Plottable.Plot._scaledAccessor(this.x()), stackYProjector, stackY0Projector);
return propertyToProjectors;
};
StackedArea.prototype._pixelPoint = function (datum, index, dataset) {
var pixelPoint = _super.prototype._pixelPoint.call(this, datum, index, dataset);
var xValue = this.x().accessor(datum, index, dataset);
var yValue = this.y().accessor(datum, index, dataset);
var scaledYValue = this.y().scale.scale(+yValue + this._stackingResult.get(dataset).get(Plottable.Utils.Stacking.normalizeKey(xValue)).offset);
return { x: pixelPoint.x, y: scaledYValue };
};
return StackedArea;
}(Plots.Area));
Plots.StackedArea = StackedArea;
})(Plots = Plottable.Plots || (Plottable.Plots = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Plots;
(function (Plots) {
var StackedBar = (function (_super) {
__extends(StackedBar, _super);
/**
* A StackedBar Plot stacks bars across Datasets based on the primary value of the bars.
* On a vertical StackedBar Plot, the bars with the same X value are stacked.
* On a horizontal StackedBar Plot, the bars with the same Y value are stacked.
*
* @constructor
* @param {Scale} xScale
* @param {Scale} yScale
* @param {string} [orientation="vertical"] One of "vertical"/"horizontal".
*/
function StackedBar(orientation) {
if (orientation === void 0) { orientation = Plots.Bar.ORIENTATION_VERTICAL; }
_super.call(this, orientation);
this.addClass("stacked-bar-plot");
this._stackingResult = new Plottable.Utils.Map();
this._stackedExtent = [];
}
StackedBar.prototype.x = function (x, xScale) {
if (x == null) {
return _super.prototype.x.call(this);
}
if (xScale == null) {
_super.prototype.x.call(this, x);
}
else {
_super.prototype.x.call(this, x, xScale);
}
this._updateStackExtentsAndOffsets();
return this;
};
StackedBar.prototype.y = function (y, yScale) {
if (y == null) {
return _super.prototype.y.call(this);
}
if (yScale == null) {
_super.prototype.y.call(this, y);
}
else {
_super.prototype.y.call(this, y, yScale);
}
this._updateStackExtentsAndOffsets();
return this;
};
StackedBar.prototype._setup = function () {
_super.prototype._setup.call(this);
this._labelArea = this._renderArea.append("g").classed(Plots.Bar._LABEL_AREA_CLASS, true);
this._measurer = new SVGTypewriter.CacheMeasurer(this._labelArea);
this._writer = new SVGTypewriter.Writer(this._measurer);
};
StackedBar.prototype._drawLabels = function () {
var _this = this;
_super.prototype._drawLabels.call(this);
// remove all current labels before redrawing
this._labelArea.selectAll("g").remove();
var baselineValue = +this.baselineValue();
var primaryScale = this._isVertical ? this.x().scale : this.y().scale;
var secondaryScale = this._isVertical ? this.y().scale : this.x().scale;
var _a = Plottable.Utils.Stacking.stackedExtents(this._stackingResult), maximumExtents = _a.maximumExtents, minimumExtents = _a.minimumExtents;
var barWidth = this._getBarPixelWidth();
var drawLabel = function (text, measurement, labelPosition) {
var x = labelPosition.x, y = labelPosition.y;
var height = measurement.height, width = measurement.width;
var tooWide = _this._isVertical ? (width > barWidth) : (height > barWidth);
var hideLabel = x < 0
|| y < 0
|| x + width > _this.width()
|| y + height > _this.height()
|| tooWide;
if (!hideLabel) {
var labelContainer = _this._labelArea.append("g").attr("transform", "translate(" + x + ", " + y + ")");
labelContainer.classed("stacked-bar-label", true);
var writeOptions = {
selection: labelContainer,
xAlign: "center",
yAlign: "center",
textRotation: 0,
};
_this._writer.write(text, measurement.width, measurement.height, writeOptions);
}
};
maximumExtents.forEach(function (maximum, axisValue) {
if (maximum !== baselineValue) {
// only draw sums for values not at the baseline
var text = _this.labelFormatter()(maximum);
var measurement = _this._measurer.measure(text);
var primaryTextMeasurement = _this._isVertical ? measurement.width : measurement.height;
var secondaryTextMeasurement = _this._isVertical ? measurement.height : measurement.width;
var x = _this._isVertical
? primaryScale.scale(axisValue) - primaryTextMeasurement / 2
: secondaryScale.scale(maximum) + StackedBar._STACKED_BAR_LABEL_PADDING;
var y = _this._isVertical
? secondaryScale.scale(maximum) - secondaryTextMeasurement - StackedBar._STACKED_BAR_LABEL_PADDING
: primaryScale.scale(axisValue) - primaryTextMeasurement / 2;
drawLabel(text, measurement, { x: x, y: y });
}
});
minimumExtents.forEach(function (minimum, axisValue) {
if (minimum !== baselineValue) {
var text = _this.labelFormatter()(minimum);
var measurement = _this._measurer.measure(text);
var primaryTextMeasurement = _this._isVertical ? measurement.width : measurement.height;
var secondaryTextMeasurement = _this._isVertical ? measurement.height : measurement.width;
var x = _this._isVertical
? primaryScale.scale(axisValue) - primaryTextMeasurement / 2
: secondaryScale.scale(minimum) - secondaryTextMeasurement - StackedBar._STACKED_BAR_LABEL_PADDING;
var y = _this._isVertical
? secondaryScale.scale(minimum) + StackedBar._STACKED_BAR_LABEL_PADDING
: primaryScale.scale(axisValue) - primaryTextMeasurement / 2;
drawLabel(text, measurement, { x: x, y: y });
}
});
};
StackedBar.prototype._generateAttrToProjector = function () {
var _this = this;
var attrToProjector = _super.prototype._generateAttrToProjector.call(this);
var valueAttr = this._isVertical ? "y" : "x";
var keyAttr = this._isVertical ? "x" : "y";
var primaryScale = this._isVertical ? this.y().scale : this.x().scale;
var primaryAccessor = this._propertyBindings.get(valueAttr).accessor;
var keyAccessor = this._propertyBindings.get(keyAttr).accessor;
var normalizedKeyAccessor = function (datum, index, dataset) {
return Plottable.Utils.Stacking.normalizeKey(keyAccessor(datum, index, dataset));
};
var getStart = function (d, i, dataset) {
return primaryScale.scale(_this._stackingResult.get(dataset).get(normalizedKeyAccessor(d, i, dataset)).offset);
};
var getEnd = function (d, i, dataset) {
return primaryScale.scale(+primaryAccessor(d, i, dataset) +
_this._stackingResult.get(dataset).get(normalizedKeyAccessor(d, i, dataset)).offset);
};
var heightF = function (d, i, dataset) {
return Math.abs(getEnd(d, i, dataset) - getStart(d, i, dataset));
};
attrToProjector[this._isVertical ? "height" : "width"] = heightF;
var attrFunction = function (d, i, dataset) {
return +primaryAccessor(d, i, dataset) < 0 ? getStart(d, i, dataset) : getEnd(d, i, dataset);
};
attrToProjector[valueAttr] = function (d, i, dataset) {
return _this._isVertical ? attrFunction(d, i, dataset) : attrFunction(d, i, dataset) - heightF(d, i, dataset);
};
return attrToProjector;
};
StackedBar.prototype._onDatasetUpdate = function () {
this._updateStackExtentsAndOffsets();
_super.prototype._onDatasetUpdate.call(this);
return this;
};
StackedBar.prototype._updateExtentsForProperty = function (property) {
_super.prototype._updateExtentsForProperty.call(this, property);
if ((property === "x" || property === "y") && this._projectorsReady()) {
this._updateStackExtentsAndOffsets();
}
};
StackedBar.prototype._extentsForProperty = function (attr) {
var primaryAttr = this._isVertical ? "y" : "x";
if (attr === primaryAttr) {
return [this._stackedExtent];
}
else {
return _super.prototype._extentsForProperty.call(this, attr);
}
};
StackedBar.prototype._updateStackExtentsAndOffsets = function () {
if (!this._projectorsReady()) {
return;
}
var datasets = this.datasets();
var keyAccessor = this._isVertical ? this.x().accessor : this.y().accessor;
var valueAccessor = this._isVertical ? this.y().accessor : this.x().accessor;
var filter = this._filterForProperty(this._isVertical ? "y" : "x");
this._stackingResult = Plottable.Utils.Stacking.stack(datasets, keyAccessor, valueAccessor);
this._stackedExtent = Plottable.Utils.Stacking.stackedExtent(this._stackingResult, keyAccessor, filter);
};
StackedBar._STACKED_BAR_LABEL_PADDING = 5;
return StackedBar;
}(Plots.Bar));
Plots.StackedBar = StackedBar;
})(Plots = Plottable.Plots || (Plottable.Plots = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Plots;
(function (Plots) {
var Segment = (function (_super) {
__extends(Segment, _super);
/**
* A Segment Plot displays line segments based on the data.
*
* @constructor
*/
function Segment() {
_super.call(this);
this.addClass("segment-plot");
this.attr("stroke", new Plottable.Scales.Color().range()[0]);
this.attr("stroke-width", "2px");
}
Segment.prototype._createDrawer = function (dataset) {
return new Plottable.Drawers.Segment(dataset);
};
Segment.prototype._generateDrawSteps = function () {
return [{ attrToProjector: this._generateAttrToProjector(), animator: new Plottable.Animators.Null() }];
};
Segment.prototype._updateExtentsForProperty = function (property) {
_super.prototype._updateExtentsForProperty.call(this, property);
if (property === "x") {
_super.prototype._updateExtentsForProperty.call(this, "x2");
}
else if (property === "y") {
_super.prototype._updateExtentsForProperty.call(this, "y2");
}
};
Segment.prototype._filterForProperty = function (property) {
if (property === "x2") {
return _super.prototype._filterForProperty.call(this, "x");
}
else if (property === "y2") {
return _super.prototype._filterForProperty.call(this, "y");
}
return _super.prototype._filterForProperty.call(this, property);
};
Segment.prototype.x = function (x, xScale) {
if (x == null) {
return _super.prototype.x.call(this);
}
if (xScale == null) {
_super.prototype.x.call(this, x);
}
else {
_super.prototype.x.call(this, x, xScale);
var x2Binding = this.x2();
var x2 = x2Binding && x2Binding.accessor;
if (x2 != null) {
this._bindProperty(Segment._X2_KEY, x2, xScale);
}
}
return this;
};
Segment.prototype.x2 = function (x2) {
if (x2 == null) {
return this._propertyBindings.get(Segment._X2_KEY);
}
var xBinding = this.x();
var xScale = xBinding && xBinding.scale;
this._bindProperty(Segment._X2_KEY, x2, xScale);
this.render();
return this;
};
Segment.prototype.y = function (y, yScale) {
if (y == null) {
return _super.prototype.y.call(this);
}
if (yScale == null) {
_super.prototype.y.call(this, y);
}
else {
_super.prototype.y.call(this, y, yScale);
var y2Binding = this.y2();
var y2 = y2Binding && y2Binding.accessor;
if (y2 != null) {
this._bindProperty(Segment._Y2_KEY, y2, yScale);
}
}
return this;
};
Segment.prototype.y2 = function (y2) {
if (y2 == null) {
return this._propertyBindings.get(Segment._Y2_KEY);
}
var yBinding = this.y();
var yScale = yBinding && yBinding.scale;
this._bindProperty(Segment._Y2_KEY, y2, yScale);
this.render();
return this;
};
Segment.prototype._propertyProjectors = function () {
var attrToProjector = _super.prototype._propertyProjectors.call(this);
attrToProjector["x1"] = Plottable.Plot._scaledAccessor(this.x());
attrToProjector["x2"] = this.x2() == null ? Plottable.Plot._scaledAccessor(this.x()) : Plottable.Plot._scaledAccessor(this.x2());
attrToProjector["y1"] = Plottable.Plot._scaledAccessor(this.y());
attrToProjector["y2"] = this.y2() == null ? Plottable.Plot._scaledAccessor(this.y()) : Plottable.Plot._scaledAccessor(this.y2());
return attrToProjector;
};
Segment.prototype.entitiesAt = function (point) {
var entity = this.entityNearest(point);
if (entity != null) {
return [entity];
}
else {
return [];
}
};
Segment.prototype.entitiesIn = function (xRangeOrBounds, yRange) {
var dataXRange;
var dataYRange;
if (yRange == null) {
var bounds = xRangeOrBounds;
dataXRange = { min: bounds.topLeft.x, max: bounds.bottomRight.x };
dataYRange = { min: bounds.topLeft.y, max: bounds.bottomRight.y };
}
else {
dataXRange = xRangeOrBounds;
dataYRange = yRange;
}
return this._entitiesIntersecting(dataXRange, dataYRange);
};
Segment.prototype._entitiesIntersecting = function (xRange, yRange) {
var _this = this;
var intersected = [];
var attrToProjector = this._generateAttrToProjector();
this.entities().forEach(function (entity) {
if (_this._lineIntersectsBox(entity, xRange, yRange, attrToProjector)) {
intersected.push(entity);
}
});
return intersected;
};
Segment.prototype._lineIntersectsBox = function (entity, xRange, yRange, attrToProjector) {
var _this = this;
var x1 = attrToProjector["x1"](entity.datum, entity.index, entity.dataset);
var x2 = attrToProjector["x2"](entity.datum, entity.index, entity.dataset);
var y1 = attrToProjector["y1"](entity.datum, entity.index, entity.dataset);
var y2 = attrToProjector["y2"](entity.datum, entity.index, entity.dataset);
// check if any of end points of the segment is inside the box
if ((xRange.min <= x1 && x1 <= xRange.max && yRange.min <= y1 && y1 <= yRange.max) ||
(xRange.min <= x2 && x2 <= xRange.max && yRange.min <= y2 && y2 <= yRange.max)) {
return true;
}
var startPoint = { x: x1, y: y1 };
var endPoint = { x: x2, y: y2 };
var corners = [
{ x: xRange.min, y: yRange.min },
{ x: xRange.min, y: yRange.max },
{ x: xRange.max, y: yRange.max },
{ x: xRange.max, y: yRange.min },
];
var intersections = corners.filter(function (point, index) {
if (index !== 0) {
// return true if border formed by conecting current corner and previous corner intersects with the segment
return _this._lineIntersectsSegment(startPoint, endPoint, point, corners[index - 1]) &&
_this._lineIntersectsSegment(point, corners[index - 1], startPoint, endPoint);
}
return false;
});
return intersections.length > 0;
};
Segment.prototype._lineIntersectsSegment = function (point1, point2, point3, point4) {
/* tslint:disable no-shadowed-variable */
var calcOrientation = function (point1, point2, point) {
return (point2.x - point1.x) * (point.y - point2.y) - (point2.y - point1.y) * (point.x - point2.x);
};
/* tslint:enable no-shadowed-variable */
// point3 and point4 are on different sides of line formed by point1 and point2
return calcOrientation(point1, point2, point3) * calcOrientation(point1, point2, point4) < 0;
};
Segment._X2_KEY = "x2";
Segment._Y2_KEY = "y2";
return Segment;
}(Plottable.XYPlot));
Plots.Segment = Segment;
})(Plots = Plottable.Plots || (Plottable.Plots = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Plots;
(function (Plots) {
var Waterfall = (function (_super) {
__extends(Waterfall, _super);
function Waterfall() {
_super.call(this);
this._connectorsEnabled = false;
this.addClass("waterfall-plot");
}
Waterfall.prototype.connectorsEnabled = function (enabled) {
if (enabled == null) {
return this._connectorsEnabled;
}
this._connectorsEnabled = enabled;
return this;
};
Waterfall.prototype.total = function (total) {
if (total == null) {
return this._propertyBindings.get(Waterfall._TOTAL_KEY);
}
this._bindProperty(Waterfall._TOTAL_KEY, total, null);
return this;
};
Waterfall.prototype._additionalPaint = function (time) {
var _this = this;
this._connectorArea.selectAll("line").remove();
if (this._connectorsEnabled) {
Plottable.Utils.Window.setTimeout(function () { return _this._drawConnectors(); }, time);
}
};
Waterfall.prototype._createNodesForDataset = function (dataset) {
var drawer = _super.prototype._createNodesForDataset.call(this, dataset);
this._connectorArea = this._renderArea.append("g").classed(Waterfall._CONNECTOR_AREA_CLASS, true);
return drawer;
};
Waterfall.prototype._extentsForProperty = function (attr) {
var primaryAttr = "y";
if (attr === primaryAttr) {
return [this._extent];
}
else {
return _super.prototype._extentsForProperty.call(this, attr);
}
};
Waterfall.prototype._generateAttrToProjector = function () {
var _this = this;
var attrToProjector = _super.prototype._generateAttrToProjector.call(this);
var yScale = this.y().scale;
var totalAccessor = Plottable.Plot._scaledAccessor(this.total());
var yAttr = this.attr("y");
if (yAttr == null) {
attrToProjector["y"] = function (d, i, dataset) {
var currentValue = _this.y().accessor(d, i, dataset);
var isTotal = totalAccessor(d, i, dataset);
if (isTotal) {
return Math.min(yScale.scale(currentValue), yScale.scale(0));
}
else {
var currentSubtotal = _this._subtotals[i];
if (i === 0) {
if (currentValue < 0) {
return yScale.scale(currentSubtotal - currentValue);
}
else {
return yScale.scale(currentSubtotal);
}
}
var priorSubtotal = _this._subtotals[i - 1];
if (currentSubtotal > priorSubtotal) {
return yScale.scale(currentSubtotal);
}
else {
return yScale.scale(priorSubtotal);
}
}
};
}
var heightAttr = this.attr("height");
if (heightAttr == null) {
attrToProjector["height"] = function (d, i, dataset) {
var isTotal = totalAccessor(d, i, dataset);
var currentValue = _this.y().accessor(d, i, dataset);
if (isTotal) {
return Math.abs(yScale.scale(currentValue) - yScale.scale(0));
}
else {
var currentSubtotal = _this._subtotals[i];
if (i === 0) {
return Math.abs(yScale.scale(currentSubtotal) - yScale.scale(currentSubtotal - currentValue));
}
else {
var priorSubtotal = _this._subtotals[i - 1];
return Math.abs(yScale.scale(currentSubtotal) - yScale.scale(priorSubtotal));
}
}
};
}
attrToProjector["class"] = function (d, i, dataset) {
var baseClass = "";
if (_this.attr("class") != null) {
baseClass = _this.attr("class").accessor(d, i, dataset) + " ";
}
var isTotal = totalAccessor(d, i, dataset);
if (isTotal) {
return baseClass + Waterfall._BAR_TOTAL_CLASS;
}
else {
var delta = _this.y().accessor(d, i, dataset);
return baseClass + (delta > 0 ? Waterfall._BAR_GROWTH_CLASS : Waterfall._BAR_DECLINE_CLASS);
}
};
return attrToProjector;
};
Waterfall.prototype._onDatasetUpdate = function () {
this._updateSubtotals();
_super.prototype._onDatasetUpdate.call(this);
return this;
};
Waterfall.prototype._calculateSubtotalsAndExtent = function (dataset) {
var _this = this;
var min = Number.MAX_VALUE;
var max = Number.MIN_VALUE;
var total = 0;
var hasStarted = false;
dataset.data().forEach(function (datum, index) {
var currentValue = _this.y().accessor(datum, index, dataset);
var isTotal = _this.total().accessor(datum, index, dataset);
if (!isTotal || index === 0) {
total += currentValue;
}
_this._subtotals.push(total);
if (total < min) {
min = total;
}
if (total > max) {
max = total;
}
if (isTotal) {
if (currentValue < min) {
min = currentValue;
}
if (currentValue > max) {
max = currentValue;
}
}
if (!hasStarted && isTotal) {
var startTotal = currentValue - total;
for (var i = 0; i < _this._subtotals.length; i++) {
_this._subtotals[i] += startTotal;
}
hasStarted = true;
total += startTotal;
min += startTotal;
max += startTotal;
}
});
this._extent = [min, max];
};
Waterfall.prototype._drawConnectors = function () {
var attrToProjector = this._generateAttrToProjector();
var dataset = this.datasets()[0];
for (var datumIndex = 1; datumIndex < dataset.data().length; datumIndex++) {
var prevIndex = datumIndex - 1;
var datum = dataset.data()[datumIndex];
var prevDatum = dataset.data()[prevIndex];
var x = attrToProjector["x"](prevDatum, prevIndex, dataset);
var x2 = attrToProjector["x"](datum, datumIndex, dataset) + attrToProjector["width"](datum, datumIndex, dataset);
var y = attrToProjector["y"](datum, datumIndex, dataset);
if ((this._subtotals[datumIndex] > 0 && this._subtotals[datumIndex] > this._subtotals[prevIndex]) ||
(this._subtotals[datumIndex] < 0 && this._subtotals[datumIndex] >= this._subtotals[prevIndex])) {
y = attrToProjector["y"](datum, datumIndex, dataset) + attrToProjector["height"](datum, datumIndex, dataset);
}
this._connectorArea.append("line").classed(Waterfall._CONNECTOR_CLASS, true)
.attr("x1", x).attr("x2", x2).attr("y1", y).attr("y2", y);
}
};
Waterfall.prototype._updateSubtotals = function () {
var datasets = this.datasets();
if (datasets.length > 0) {
var dataset = datasets[datasets.length - 1];
this._subtotals = new Array();
this._calculateSubtotalsAndExtent(dataset);
}
};
Waterfall._BAR_DECLINE_CLASS = "waterfall-decline";
Waterfall._BAR_GROWTH_CLASS = "waterfall-growth";
Waterfall._BAR_TOTAL_CLASS = "waterfall-total";
Waterfall._CONNECTOR_CLASS = "connector";
Waterfall._CONNECTOR_AREA_CLASS = "connector-area";
Waterfall._TOTAL_KEY = "total";
return Waterfall;
}(Plots.Bar));
Plots.Waterfall = Waterfall;
})(Plots = Plottable.Plots || (Plottable.Plots = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Animators;
(function (Animators) {
/**
* An animator implementation with no animation. The attributes are
* immediately set on the selection.
*/
var Null = (function () {
function Null() {
}
Null.prototype.totalTime = function (selection) {
return 0;
};
Null.prototype.animate = function (selection, attrToAppliedProjector) {
return selection.attr(attrToAppliedProjector);
};
return Null;
}());
Animators.Null = Null;
})(Animators = Plottable.Animators || (Plottable.Animators = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Animators;
(function (Animators) {
/**
* An Animator with easing and configurable durations and delays.
*/
var Easing = (function () {
/**
* Constructs the default animator
*
* @constructor
*/
function Easing() {
this._startDelay = Easing._DEFAULT_START_DELAY_MILLISECONDS;
this._stepDuration = Easing._DEFAULT_STEP_DURATION_MILLISECONDS;
this._stepDelay = Easing._DEFAULT_ITERATIVE_DELAY_MILLISECONDS;
this._maxTotalDuration = Easing._DEFAULT_MAX_TOTAL_DURATION_MILLISECONDS;
this._easingMode = Easing._DEFAULT_EASING_MODE;
}
Easing.prototype.totalTime = function (numberOfSteps) {
var adjustedIterativeDelay = this._getAdjustedIterativeDelay(numberOfSteps);
return this.startDelay() + adjustedIterativeDelay * (Math.max(numberOfSteps - 1, 0)) + this.stepDuration();
};
Easing.prototype.animate = function (selection, attrToAppliedProjector) {
var _this = this;
var numberOfSteps = selection[0].length;
var adjustedIterativeDelay = this._getAdjustedIterativeDelay(numberOfSteps);
return selection.transition()
.ease(this.easingMode())
.duration(this.stepDuration())
.delay(function (d, i) { return _this.startDelay() + adjustedIterativeDelay * i; })
.attr(attrToAppliedProjector);
};
Easing.prototype.startDelay = function (startDelay) {
if (startDelay == null) {
return this._startDelay;
}
else {
this._startDelay = startDelay;
return this;
}
};
Easing.prototype.stepDuration = function (stepDuration) {
if (stepDuration == null) {
return Math.min(this._stepDuration, this._maxTotalDuration);
}
else {
this._stepDuration = stepDuration;
return this;
}
};
Easing.prototype.stepDelay = function (stepDelay) {
if (stepDelay == null) {
return this._stepDelay;
}
else {
this._stepDelay = stepDelay;
return this;
}
};
Easing.prototype.maxTotalDuration = function (maxTotalDuration) {
if (maxTotalDuration == null) {
return this._maxTotalDuration;
}
else {
this._maxTotalDuration = maxTotalDuration;
return this;
}
};
Easing.prototype.easingMode = function (easingMode) {
if (easingMode == null) {
return this._easingMode;
}
else {
this._easingMode = easingMode;
return this;
}
};
/**
* Adjust the iterative delay, such that it takes into account the maxTotalDuration constraint
*/
Easing.prototype._getAdjustedIterativeDelay = function (numberOfSteps) {
var stepStartTimeInterval = this.maxTotalDuration() - this.stepDuration();
stepStartTimeInterval = Math.max(stepStartTimeInterval, 0);
var maxPossibleIterativeDelay = stepStartTimeInterval / Math.max(numberOfSteps - 1, 1);
return Math.min(this.stepDelay(), maxPossibleIterativeDelay);
};
/**
* The default starting delay of the animation in milliseconds
*/
Easing._DEFAULT_START_DELAY_MILLISECONDS = 0;
/**
* The default duration of one animation step in milliseconds
*/
Easing._DEFAULT_STEP_DURATION_MILLISECONDS = 300;
/**
* The default maximum start delay between each step of an animation
*/
Easing._DEFAULT_ITERATIVE_DELAY_MILLISECONDS = 15;
/**
* The default maximum total animation duration
*/
Easing._DEFAULT_MAX_TOTAL_DURATION_MILLISECONDS = Infinity;
/**
* The default easing of the animation
*/
Easing._DEFAULT_EASING_MODE = "exp-out";
return Easing;
}());
Animators.Easing = Easing;
})(Animators = Plottable.Animators || (Plottable.Animators = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Dispatcher = (function () {
function Dispatcher() {
this._eventToProcessingFunction = {};
this._eventNameToCallbackSet = {};
this._connected = false;
}
Dispatcher.prototype._hasNoCallbacks = function () {
var eventNames = Object.keys(this._eventNameToCallbackSet);
for (var i = 0; i < eventNames.length; i++) {
if (this._eventNameToCallbackSet[eventNames[i]].size !== 0) {
return false;
}
}
return true;
};
Dispatcher.prototype._connect = function () {
var _this = this;
if (this._connected) {
return;
}
Object.keys(this._eventToProcessingFunction).forEach(function (event) {
var processingFunction = _this._eventToProcessingFunction[event];
document.addEventListener(event, processingFunction);
});
this._connected = true;
};
Dispatcher.prototype._disconnect = function () {
var _this = this;
if (this._connected && this._hasNoCallbacks()) {
Object.keys(this._eventToProcessingFunction).forEach(function (event) {
var processingFunction = _this._eventToProcessingFunction[event];
document.removeEventListener(event, processingFunction);
});
this._connected = false;
}
};
Dispatcher.prototype._addCallbackForEvent = function (eventName, callback) {
if (this._eventNameToCallbackSet[eventName] == null) {
this._eventNameToCallbackSet[eventName] = new Plottable.Utils.CallbackSet();
}
this._eventNameToCallbackSet[eventName].add(callback);
this._connect();
};
Dispatcher.prototype._removeCallbackForEvent = function (eventName, callback) {
if (this._eventNameToCallbackSet[eventName] != null) {
this._eventNameToCallbackSet[eventName].delete(callback);
}
this._disconnect();
};
Dispatcher.prototype._callCallbacksForEvent = function (eventName) {
var args = [];
for (var _i = 1; _i < arguments.length; _i++) {
args[_i - 1] = arguments[_i];
}
var callbackSet = this._eventNameToCallbackSet[eventName];
if (callbackSet != null) {
callbackSet.callCallbacks.apply(callbackSet, args);
}
};
return Dispatcher;
}());
Plottable.Dispatcher = Dispatcher;
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Dispatchers;
(function (Dispatchers) {
var Mouse = (function (_super) {
__extends(Mouse, _super);
/**
* This constructor not be invoked directly.
*
* @constructor
* @param {SVGElement} svg The root <svg> to attach to.
*/
function Mouse(svg) {
var _this = this;
_super.call(this);
this._translator = Plottable.Utils.ClientToSVGTranslator.getTranslator(svg);
this._lastMousePosition = { x: -1, y: -1 };
var processMoveCallback = function (e) { return _this._measureAndDispatch(e, Mouse._MOUSEMOVE_EVENT_NAME, "page"); };
this._eventToProcessingFunction[Mouse._MOUSEOVER_EVENT_NAME] = processMoveCallback;
this._eventToProcessingFunction[Mouse._MOUSEMOVE_EVENT_NAME] = processMoveCallback;
this._eventToProcessingFunction[Mouse._MOUSEOUT_EVENT_NAME] = processMoveCallback;
this._eventToProcessingFunction[Mouse._MOUSEDOWN_EVENT_NAME] =
function (e) { return _this._measureAndDispatch(e, Mouse._MOUSEDOWN_EVENT_NAME); };
this._eventToProcessingFunction[Mouse._MOUSEUP_EVENT_NAME] =
function (e) { return _this._measureAndDispatch(e, Mouse._MOUSEUP_EVENT_NAME, "page"); };
this._eventToProcessingFunction[Mouse._WHEEL_EVENT_NAME] =
function (e) { return _this._measureAndDispatch(e, Mouse._WHEEL_EVENT_NAME); };
this._eventToProcessingFunction[Mouse._DBLCLICK_EVENT_NAME] =
function (e) { return _this._measureAndDispatch(e, Mouse._DBLCLICK_EVENT_NAME); };
}
/**
* Get a Mouse Dispatcher for the <svg> containing elem.
* If one already exists on that <svg>, it will be returned; otherwise, a new one will be created.
*
* @param {SVGElement} elem
* @return {Dispatchers.Mouse}
*/
Mouse.getDispatcher = function (elem) {
var svg = Plottable.Utils.DOM.boundingSVG(elem);
var dispatcher = svg[Mouse._DISPATCHER_KEY];
if (dispatcher == null) {
dispatcher = new Mouse(svg);
svg[Mouse._DISPATCHER_KEY] = dispatcher;
}
return dispatcher;
};
/**
* Registers a callback to be called when the mouse position changes.
*
* @param {MouseCallback} callback
* @return {Dispatchers.Mouse} The calling Mouse Dispatcher.
*/
Mouse.prototype.onMouseMove = function (callback) {
this._addCallbackForEvent(Mouse._MOUSEMOVE_EVENT_NAME, callback);
return this;
};
/**
* Removes a callback that would be called when the mouse position changes.
*
* @param {MouseCallback} callback
* @return {Dispatchers.Mouse} The calling Mouse Dispatcher.
*/
Mouse.prototype.offMouseMove = function (callback) {
this._removeCallbackForEvent(Mouse._MOUSEMOVE_EVENT_NAME, callback);
return this;
};
/**
* Registers a callback to be called when a mousedown occurs.
*
* @param {MouseCallback} callback
* @return {Dispatchers.Mouse} The calling Mouse Dispatcher.
*/
Mouse.prototype.onMouseDown = function (callback) {
this._addCallbackForEvent(Mouse._MOUSEDOWN_EVENT_NAME, callback);
return this;
};
/**
* Removes a callback that would be called when a mousedown occurs.
*
* @param {MouseCallback} callback
* @return {Dispatchers.Mouse} The calling Mouse Dispatcher.
*/
Mouse.prototype.offMouseDown = function (callback) {
this._removeCallbackForEvent(Mouse._MOUSEDOWN_EVENT_NAME, callback);
return this;
};
/**
* Registers a callback to be called when a mouseup occurs.
*
* @param {MouseCallback} callback
* @return {Dispatchers.Mouse} The calling Mouse Dispatcher.
*/
Mouse.prototype.onMouseUp = function (callback) {
this._addCallbackForEvent(Mouse._MOUSEUP_EVENT_NAME, callback);
return this;
};
/**
* Removes a callback that would be called when a mouseup occurs.
*
* @param {MouseCallback} callback
* @return {Dispatchers.Mouse} The calling Mouse Dispatcher.
*/
Mouse.prototype.offMouseUp = function (callback) {
this._removeCallbackForEvent(Mouse._MOUSEUP_EVENT_NAME, callback);
return this;
};
/**
* Registers a callback to be called when a wheel event occurs.
*
* @param {MouseCallback} callback
* @return {Dispatchers.Mouse} The calling Mouse Dispatcher.
*/
Mouse.prototype.onWheel = function (callback) {
this._addCallbackForEvent(Mouse._WHEEL_EVENT_NAME, callback);
return this;
};
/**
* Removes a callback that would be called when a wheel event occurs.
*
* @param {MouseCallback} callback
* @return {Dispatchers.Mouse} The calling Mouse Dispatcher.
*/
Mouse.prototype.offWheel = function (callback) {
this._removeCallbackForEvent(Mouse._WHEEL_EVENT_NAME, callback);
return this;
};
/**
* Registers a callback to be called when a dblClick occurs.
*
* @param {MouseCallback} callback
* @return {Dispatchers.Mouse} The calling Mouse Dispatcher.
*/
Mouse.prototype.onDblClick = function (callback) {
this._addCallbackForEvent(Mouse._DBLCLICK_EVENT_NAME, callback);
return this;
};
/**
* Removes a callback that would be called when a dblClick occurs.
*
* @param {MouseCallback} callback
* @return {Dispatchers.Mouse} The calling Mouse Dispatcher.
*/
Mouse.prototype.offDblClick = function (callback) {
this._removeCallbackForEvent(Mouse._DBLCLICK_EVENT_NAME, callback);
return this;
};
/**
* Computes the mouse position from the given event, and if successful
* calls all the callbacks in the provided callbackSet.
*/
Mouse.prototype._measureAndDispatch = function (event, eventName, scope) {
if (scope === void 0) { scope = "element"; }
if (scope !== "page" && scope !== "element") {
throw new Error("Invalid scope '" + scope + "', must be 'element' or 'page'");
}
if (scope === "page" || this.eventInsideSVG(event)) {
var newMousePosition = this._translator.computePosition(event.clientX, event.clientY);
if (newMousePosition != null) {
this._lastMousePosition = newMousePosition;
this._callCallbacksForEvent(eventName, this.lastMousePosition(), event);
}
}
};
Mouse.prototype.eventInsideSVG = function (event) {
return this._translator.insideSVG(event);
};
/**
* Returns the last computed mouse position in <svg> coordinate space.
*
* @return {Point}
*/
Mouse.prototype.lastMousePosition = function () {
return this._lastMousePosition;
};
Mouse._DISPATCHER_KEY = "__Plottable_Dispatcher_Mouse";
Mouse._MOUSEOVER_EVENT_NAME = "mouseover";
Mouse._MOUSEMOVE_EVENT_NAME = "mousemove";
Mouse._MOUSEOUT_EVENT_NAME = "mouseout";
Mouse._MOUSEDOWN_EVENT_NAME = "mousedown";
Mouse._MOUSEUP_EVENT_NAME = "mouseup";
Mouse._WHEEL_EVENT_NAME = "wheel";
Mouse._DBLCLICK_EVENT_NAME = "dblclick";
return Mouse;
}(Plottable.Dispatcher));
Dispatchers.Mouse = Mouse;
})(Dispatchers = Plottable.Dispatchers || (Plottable.Dispatchers = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Dispatchers;
(function (Dispatchers) {
var Touch = (function (_super) {
__extends(Touch, _super);
/**
* This constructor should not be invoked directly.
*
* @constructor
* @param {SVGElement} svg The root <svg> to attach to.
*/
function Touch(svg) {
var _this = this;
_super.call(this);
this._translator = Plottable.Utils.ClientToSVGTranslator.getTranslator(svg);
this._eventToProcessingFunction[Touch._TOUCHSTART_EVENT_NAME] =
function (e) { return _this._measureAndDispatch(e, Touch._TOUCHSTART_EVENT_NAME, "page"); };
this._eventToProcessingFunction[Touch._TOUCHMOVE_EVENT_NAME] =
function (e) { return _this._measureAndDispatch(e, Touch._TOUCHMOVE_EVENT_NAME, "page"); };
this._eventToProcessingFunction[Touch._TOUCHEND_EVENT_NAME] =
function (e) { return _this._measureAndDispatch(e, Touch._TOUCHEND_EVENT_NAME, "page"); };
this._eventToProcessingFunction[Touch._TOUCHCANCEL_EVENT_NAME] =
function (e) { return _this._measureAndDispatch(e, Touch._TOUCHCANCEL_EVENT_NAME, "page"); };
}
/**
* Gets a Touch Dispatcher for the <svg> containing elem.
* If one already exists on that <svg>, it will be returned; otherwise, a new one will be created.
*
* @param {SVGElement} elem
* @return {Dispatchers.Touch}
*/
Touch.getDispatcher = function (elem) {
var svg = Plottable.Utils.DOM.boundingSVG(elem);
var dispatcher = svg[Touch._DISPATCHER_KEY];
if (dispatcher == null) {
dispatcher = new Touch(svg);
svg[Touch._DISPATCHER_KEY] = dispatcher;
}
return dispatcher;
};
/**
* Registers a callback to be called when a touch starts.
*
* @param {TouchCallback} callback
* @return {Dispatchers.Touch} The calling Touch Dispatcher.
*/
Touch.prototype.onTouchStart = function (callback) {
this._addCallbackForEvent(Touch._TOUCHSTART_EVENT_NAME, callback);
return this;
};
/**
* Removes a callback that would be called when a touch starts.
*
* @param {TouchCallback} callback
* @return {Dispatchers.Touch} The calling Touch Dispatcher.
*/
Touch.prototype.offTouchStart = function (callback) {
this._removeCallbackForEvent(Touch._TOUCHSTART_EVENT_NAME, callback);
return this;
};
/**
* Registers a callback to be called when the touch position changes.
*
* @param {TouchCallback} callback
* @return {Dispatchers.Touch} The calling Touch Dispatcher.
*/
Touch.prototype.onTouchMove = function (callback) {
this._addCallbackForEvent(Touch._TOUCHMOVE_EVENT_NAME, callback);
return this;
};
/**
* Removes a callback that would be called when the touch position changes.
*
* @param {TouchCallback} callback
* @return {Dispatchers.Touch} The calling Touch Dispatcher.
*/
Touch.prototype.offTouchMove = function (callback) {
this._removeCallbackForEvent(Touch._TOUCHMOVE_EVENT_NAME, callback);
return this;
};
/**
* Registers a callback to be called when a touch ends.
*
* @param {TouchCallback} callback
* @return {Dispatchers.Touch} The calling Touch Dispatcher.
*/
Touch.prototype.onTouchEnd = function (callback) {
this._addCallbackForEvent(Touch._TOUCHEND_EVENT_NAME, callback);
return this;
};
/**
* Removes a callback that would be called when a touch ends.
*
* @param {TouchCallback} callback
* @return {Dispatchers.Touch} The calling Touch Dispatcher.
*/
Touch.prototype.offTouchEnd = function (callback) {
this._removeCallbackForEvent(Touch._TOUCHEND_EVENT_NAME, callback);
return this;
};
/**
* Registers a callback to be called when a touch is cancelled.
*
* @param {TouchCallback} callback
* @return {Dispatchers.Touch} The calling Touch Dispatcher.
*/
Touch.prototype.onTouchCancel = function (callback) {
this._addCallbackForEvent(Touch._TOUCHCANCEL_EVENT_NAME, callback);
return this;
};
/**
* Removes a callback that would be called when a touch is cancelled.
*
* @param {TouchCallback} callback
* @return {Dispatchers.Touch} The calling Touch Dispatcher.
*/
Touch.prototype.offTouchCancel = function (callback) {
this._removeCallbackForEvent(Touch._TOUCHCANCEL_EVENT_NAME, callback);
return this;
};
/**
* Computes the Touch position from the given event, and if successful
* calls all the callbacks in the provided callbackSet.
*/
Touch.prototype._measureAndDispatch = function (event, eventName, scope) {
if (scope === void 0) { scope = "element"; }
if (scope !== "page" && scope !== "element") {
throw new Error("Invalid scope '" + scope + "', must be 'element' or 'page'");
}
if (scope === "element" && !this.eventInsideSVG(event)) {
return;
}
var touches = event.changedTouches;
var touchPositions = {};
var touchIdentifiers = [];
for (var i = 0; i < touches.length; i++) {
var touch = touches[i];
var touchID = touch.identifier;
var newTouchPosition = this._translator.computePosition(touch.clientX, touch.clientY);
if (newTouchPosition != null) {
touchPositions[touchID] = newTouchPosition;
touchIdentifiers.push(touchID);
}
}
;
if (touchIdentifiers.length > 0) {
this._callCallbacksForEvent(eventName, touchIdentifiers, touchPositions, event);
}
};
Touch.prototype.eventInsideSVG = function (event) {
return this._translator.insideSVG(event);
};
Touch._DISPATCHER_KEY = "__Plottable_Dispatcher_Touch";
Touch._TOUCHSTART_EVENT_NAME = "touchstart";
Touch._TOUCHMOVE_EVENT_NAME = "touchmove";
Touch._TOUCHEND_EVENT_NAME = "touchend";
Touch._TOUCHCANCEL_EVENT_NAME = "touchcancel";
return Touch;
}(Plottable.Dispatcher));
Dispatchers.Touch = Touch;
})(Dispatchers = Plottable.Dispatchers || (Plottable.Dispatchers = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Dispatchers;
(function (Dispatchers) {
var Key = (function (_super) {
__extends(Key, _super);
/**
* This constructor should not be invoked directly.
*
* @constructor
*/
function Key() {
var _this = this;
_super.call(this);
this._eventToProcessingFunction[Key._KEYDOWN_EVENT_NAME] = function (e) { return _this._processKeydown(e); };
this._eventToProcessingFunction[Key._KEYUP_EVENT_NAME] = function (e) { return _this._processKeyup(e); };
}
/**
* Gets a Key Dispatcher. If one already exists it will be returned;
* otherwise, a new one will be created.
*
* @return {Dispatchers.Key}
*/
Key.getDispatcher = function () {
var dispatcher = document[Key._DISPATCHER_KEY];
if (dispatcher == null) {
dispatcher = new Key();
document[Key._DISPATCHER_KEY] = dispatcher;
}
return dispatcher;
};
Key.prototype._processKeydown = function (event) {
this._callCallbacksForEvent(Key._KEYDOWN_EVENT_NAME, event.keyCode, event);
};
Key.prototype._processKeyup = function (event) {
this._callCallbacksForEvent(Key._KEYUP_EVENT_NAME, event.keyCode, event);
};
/**
* Registers a callback to be called whenever a key is pressed.
*
* @param {KeyCallback} callback
* @return {Dispatchers.Key} The calling Key Dispatcher.
*/
Key.prototype.onKeyDown = function (callback) {
this._addCallbackForEvent(Key._KEYDOWN_EVENT_NAME, callback);
return this;
};
/**
* Removes the callback to be called whenever a key is pressed.
*
* @param {KeyCallback} callback
* @return {Dispatchers.Key} The calling Key Dispatcher.
*/
Key.prototype.offKeyDown = function (callback) {
this._removeCallbackForEvent(Key._KEYDOWN_EVENT_NAME, callback);
return this;
};
/** Registers a callback to be called whenever a key is released.
*
* @param {KeyCallback} callback
* @return {Dispatchers.Key} The calling Key Dispatcher.
*/
Key.prototype.onKeyUp = function (callback) {
this._addCallbackForEvent(Key._KEYUP_EVENT_NAME, callback);
return this;
};
/**
* Removes the callback to be called whenever a key is released.
*
* @param {KeyCallback} callback
* @return {Dispatchers.Key} The calling Key Dispatcher.
*/
Key.prototype.offKeyUp = function (callback) {
this._removeCallbackForEvent(Key._KEYUP_EVENT_NAME, callback);
return this;
};
Key._DISPATCHER_KEY = "__Plottable_Dispatcher_Key";
Key._KEYDOWN_EVENT_NAME = "keydown";
Key._KEYUP_EVENT_NAME = "keyup";
return Key;
}(Plottable.Dispatcher));
Dispatchers.Key = Key;
})(Dispatchers = Plottable.Dispatchers || (Plottable.Dispatchers = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Interaction = (function () {
function Interaction() {
var _this = this;
this._anchorCallback = function (component) { return _this._anchor(component); };
this._enabled = true;
}
Interaction.prototype._anchor = function (component) {
this._isAnchored = true;
};
Interaction.prototype._unanchor = function () {
this._isAnchored = false;
};
/**
* Attaches this Interaction to a Component.
* If the Interaction was already attached to a Component, it first detaches itself from the old Component.
*
* @param {Component} component
* @returns {Interaction} The calling Interaction.
*/
Interaction.prototype.attachTo = function (component) {
this._disconnect();
this._componentAttachedTo = component;
this._connect();
return this;
};
Interaction.prototype._connect = function () {
if (this.enabled() && this._componentAttachedTo != null && !this._isAnchored) {
this._componentAttachedTo.onAnchor(this._anchorCallback);
}
};
/**
* Detaches this Interaction from the Component.
* This Interaction can be reused.
*
* @param {Component} component
* @returns {Interaction} The calling Interaction.
*/
Interaction.prototype.detachFrom = function (component) {
this._disconnect();
this._componentAttachedTo = null;
return this;
};
Interaction.prototype._disconnect = function () {
if (this._isAnchored) {
this._unanchor();
}
if (this._componentAttachedTo != null) {
this._componentAttachedTo.offAnchor(this._anchorCallback);
}
};
Interaction.prototype.enabled = function (enabled) {
if (enabled == null) {
return this._enabled;
}
this._enabled = enabled;
if (this._enabled) {
this._connect();
}
else {
this._disconnect();
}
return this;
};
/**
* Translates an <svg>-coordinate-space point to Component-space coordinates.
*
* @param {Point} p A Point in <svg>-space coordinates.
* @return {Point} The same location in Component-space coordinates.
*/
Interaction.prototype._translateToComponentSpace = function (p) {
var origin = this._componentAttachedTo.originToSVG();
return {
x: p.x - origin.x,
y: p.y - origin.y,
};
};
/**
* Checks whether a Component-coordinate-space Point is inside the Component.
*
* @param {Point} p A Point in Compoennt-space coordinates.
* @return {boolean} Whether or not the point is inside the Component.
*/
Interaction.prototype._isInsideComponent = function (p) {
return 0 <= p.x && 0 <= p.y
&& p.x <= this._componentAttachedTo.width()
&& p.y <= this._componentAttachedTo.height();
};
return Interaction;
}());
Plottable.Interaction = Interaction;
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Interactions;
(function (Interactions) {
var Click = (function (_super) {
__extends(Click, _super);
function Click() {
var _this = this;
_super.apply(this, arguments);
this._clickedDown = false;
this._onClickCallbacks = new Plottable.Utils.CallbackSet();
this._mouseDownCallback = function (p, event) { return _this._handleClickDown(p, event); };
this._mouseUpCallback = function (p, event) { return _this._handleClickUp(p, event); };
this._touchStartCallback = function (ids, idToPoint, event) { return _this._handleClickDown(idToPoint[ids[0]], event); };
this._touchEndCallback = function (ids, idToPoint, event) { return _this._handleClickUp(idToPoint[ids[0]], event); };
this._touchCancelCallback = function (ids, idToPoint) { return _this._clickedDown = false; };
}
Click.prototype._anchor = function (component) {
_super.prototype._anchor.call(this, component);
this._mouseDispatcher = Plottable.Dispatchers.Mouse.getDispatcher(component.content().node());
this._mouseDispatcher.onMouseDown(this._mouseDownCallback);
this._mouseDispatcher.onMouseUp(this._mouseUpCallback);
this._touchDispatcher = Plottable.Dispatchers.Touch.getDispatcher(component.content().node());
this._touchDispatcher.onTouchStart(this._touchStartCallback);
this._touchDispatcher.onTouchEnd(this._touchEndCallback);
this._touchDispatcher.onTouchCancel(this._touchCancelCallback);
};
Click.prototype._unanchor = function () {
_super.prototype._unanchor.call(this);
this._mouseDispatcher.offMouseDown(this._mouseDownCallback);
this._mouseDispatcher.offMouseUp(this._mouseUpCallback);
this._mouseDispatcher = null;
this._touchDispatcher.offTouchStart(this._touchStartCallback);
this._touchDispatcher.offTouchEnd(this._touchEndCallback);
this._touchDispatcher.offTouchCancel(this._touchCancelCallback);
this._touchDispatcher = null;
};
Click.prototype._handleClickDown = function (p, e) {
var translatedPoint = this._translateToComponentSpace(p);
if (this._isInsideComponent(translatedPoint)) {
this._clickedDown = true;
}
};
Click.prototype._handleClickUp = function (p, e) {
var translatedPoint = this._translateToComponentSpace(p);
if (this._clickedDown && this._isInsideComponent(translatedPoint)) {
this._onClickCallbacks.callCallbacks(translatedPoint, e);
}
this._clickedDown = false;
};
/**
* Adds a callback to be called when the Component is clicked.
*
* @param {ClickCallback} callback
* @return {Interactions.Click} The calling Click Interaction.
*/
Click.prototype.onClick = function (callback) {
this._onClickCallbacks.add(callback);
return this;
};
/**
* Removes a callback that would be called when the Component is clicked.
*
* @param {ClickCallback} callback
* @return {Interactions.Click} The calling Click Interaction.
*/
Click.prototype.offClick = function (callback) {
this._onClickCallbacks.delete(callback);
return this;
};
return Click;
}(Plottable.Interaction));
Interactions.Click = Click;
})(Interactions = Plottable.Interactions || (Plottable.Interactions = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Interactions;
(function (Interactions) {
var ClickState;
(function (ClickState) {
ClickState[ClickState["NotClicked"] = 0] = "NotClicked";
ClickState[ClickState["SingleClicked"] = 1] = "SingleClicked";
ClickState[ClickState["DoubleClicked"] = 2] = "DoubleClicked";
})(ClickState || (ClickState = {}));
;
var DoubleClick = (function (_super) {
__extends(DoubleClick, _super);
function DoubleClick() {
var _this = this;
_super.apply(this, arguments);
this._clickState = ClickState.NotClicked;
this._clickedDown = false;
this._onDoubleClickCallbacks = new Plottable.Utils.CallbackSet();
this._mouseDownCallback = function (p) { return _this._handleClickDown(p); };
this._mouseUpCallback = function (p) { return _this._handleClickUp(p); };
this._dblClickCallback = function (p) { return _this._handleDblClick(); };
this._touchStartCallback = function (ids, idToPoint) { return _this._handleClickDown(idToPoint[ids[0]]); };
this._touchEndCallback = function (ids, idToPoint) { return _this._handleClickUp(idToPoint[ids[0]]); };
this._touchCancelCallback = function (ids, idToPoint) { return _this._handleClickCancel(); };
}
DoubleClick.prototype._anchor = function (component) {
_super.prototype._anchor.call(this, component);
this._mouseDispatcher = Plottable.Dispatchers.Mouse.getDispatcher(component.content().node());
this._mouseDispatcher.onMouseDown(this._mouseDownCallback);
this._mouseDispatcher.onMouseUp(this._mouseUpCallback);
this._mouseDispatcher.onDblClick(this._dblClickCallback);
this._touchDispatcher = Plottable.Dispatchers.Touch.getDispatcher(component.content().node());
this._touchDispatcher.onTouchStart(this._touchStartCallback);
this._touchDispatcher.onTouchEnd(this._touchEndCallback);
this._touchDispatcher.onTouchCancel(this._touchCancelCallback);
};
DoubleClick.prototype._unanchor = function () {
_super.prototype._unanchor.call(this);
this._mouseDispatcher.offMouseDown(this._mouseDownCallback);
this._mouseDispatcher.offMouseUp(this._mouseUpCallback);
this._mouseDispatcher.offDblClick(this._dblClickCallback);
this._mouseDispatcher = null;
this._touchDispatcher.offTouchStart(this._touchStartCallback);
this._touchDispatcher.offTouchEnd(this._touchEndCallback);
this._touchDispatcher.offTouchCancel(this._touchCancelCallback);
this._touchDispatcher = null;
};
DoubleClick.prototype._handleClickDown = function (p) {
var translatedP = this._translateToComponentSpace(p);
if (this._isInsideComponent(translatedP)) {
if (!(this._clickState === ClickState.SingleClicked) || !DoubleClick._pointsEqual(translatedP, this._clickedPoint)) {
this._clickState = ClickState.NotClicked;
}
this._clickedPoint = translatedP;
this._clickedDown = true;
}
};
DoubleClick.prototype._handleClickUp = function (p) {
var translatedP = this._translateToComponentSpace(p);
if (this._clickedDown && DoubleClick._pointsEqual(translatedP, this._clickedPoint)) {
this._clickState = this._clickState === ClickState.NotClicked ? ClickState.SingleClicked : ClickState.DoubleClicked;
}
else {
this._clickState = ClickState.NotClicked;
}
this._clickedDown = false;
};
DoubleClick.prototype._handleDblClick = function () {
if (this._clickState === ClickState.DoubleClicked) {
this._onDoubleClickCallbacks.callCallbacks(this._clickedPoint);
this._clickState = ClickState.NotClicked;
}
};
DoubleClick.prototype._handleClickCancel = function () {
this._clickState = ClickState.NotClicked;
this._clickedDown = false;
};
DoubleClick._pointsEqual = function (p1, p2) {
return p1.x === p2.x && p1.y === p2.y;
};
/**
* Adds a callback to be called when the Component is double-clicked.
*
* @param {ClickCallback} callback
* @return {Interactions.DoubleClick} The calling DoubleClick Interaction.
*/
DoubleClick.prototype.onDoubleClick = function (callback) {
this._onDoubleClickCallbacks.add(callback);
return this;
};
/**
* Removes a callback that would be called when the Component is double-clicked.
*
* @param {ClickCallback} callback
* @return {Interactions.DoubleClick} The calling DoubleClick Interaction.
*/
DoubleClick.prototype.offDoubleClick = function (callback) {
this._onDoubleClickCallbacks.delete(callback);
return this;
};
return DoubleClick;
}(Plottable.Interaction));
Interactions.DoubleClick = DoubleClick;
})(Interactions = Plottable.Interactions || (Plottable.Interactions = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Interactions;
(function (Interactions) {
var Key = (function (_super) {
__extends(Key, _super);
function Key() {
var _this = this;
_super.apply(this, arguments);
this._keyPressCallbacks = {};
this._keyReleaseCallbacks = {};
this._mouseMoveCallback = function (point) { return false; }; // HACKHACK: registering a listener
this._downedKeys = new Plottable.Utils.Set();
this._keyDownCallback = function (keyCode, event) { return _this._handleKeyDownEvent(keyCode, event); };
this._keyUpCallback = function (keyCode) { return _this._handleKeyUpEvent(keyCode); };
}
Key.prototype._anchor = function (component) {
_super.prototype._anchor.call(this, component);
this._positionDispatcher = Plottable.Dispatchers.Mouse.getDispatcher(this._componentAttachedTo._element.node());
this._positionDispatcher.onMouseMove(this._mouseMoveCallback);
this._keyDispatcher = Plottable.Dispatchers.Key.getDispatcher();
this._keyDispatcher.onKeyDown(this._keyDownCallback);
this._keyDispatcher.onKeyUp(this._keyUpCallback);
};
Key.prototype._unanchor = function () {
_super.prototype._unanchor.call(this);
this._positionDispatcher.offMouseMove(this._mouseMoveCallback);
this._positionDispatcher = null;
this._keyDispatcher.offKeyDown(this._keyDownCallback);
this._keyDispatcher.offKeyUp(this._keyUpCallback);
this._keyDispatcher = null;
};
Key.prototype._handleKeyDownEvent = function (keyCode, event) {
var p = this._translateToComponentSpace(this._positionDispatcher.lastMousePosition());
if (this._isInsideComponent(p) && !event.repeat) {
if (this._keyPressCallbacks[keyCode]) {
this._keyPressCallbacks[keyCode].callCallbacks(keyCode);
}
this._downedKeys.add(keyCode);
}
};
Key.prototype._handleKeyUpEvent = function (keyCode) {
if (this._downedKeys.has(keyCode) && this._keyReleaseCallbacks[keyCode]) {
this._keyReleaseCallbacks[keyCode].callCallbacks(keyCode);
}
this._downedKeys.delete(keyCode);
};
/**
* Adds a callback to be called when the key with the given keyCode is
* pressed and the user is moused over the Component.
*
* @param {number} keyCode
* @param {KeyCallback} callback
* @returns {Interactions.Key} The calling Key Interaction.
*/
Key.prototype.onKeyPress = function (keyCode, callback) {
if (!this._keyPressCallbacks[keyCode]) {
this._keyPressCallbacks[keyCode] = new Plottable.Utils.CallbackSet();
}
this._keyPressCallbacks[keyCode].add(callback);
return this;
};
/**
* Removes a callback that would be called when the key with the given keyCode is
* pressed and the user is moused over the Component.
*
* @param {number} keyCode
* @param {KeyCallback} callback
* @returns {Interactions.Key} The calling Key Interaction.
*/
Key.prototype.offKeyPress = function (keyCode, callback) {
this._keyPressCallbacks[keyCode].delete(callback);
if (this._keyPressCallbacks[keyCode].size === 0) {
delete this._keyPressCallbacks[keyCode];
}
return this;
};
/**
* Adds a callback to be called when the key with the given keyCode is
* released if the key was pressed with the mouse inside of the Component.
*
* @param {number} keyCode
* @param {KeyCallback} callback
* @returns {Interactions.Key} The calling Key Interaction.
*/
Key.prototype.onKeyRelease = function (keyCode, callback) {
if (!this._keyReleaseCallbacks[keyCode]) {
this._keyReleaseCallbacks[keyCode] = new Plottable.Utils.CallbackSet();
}
this._keyReleaseCallbacks[keyCode].add(callback);
return this;
};
/**
* Removes a callback that would be called when the key with the given keyCode is
* released if the key was pressed with the mouse inside of the Component.
*
* @param {number} keyCode
* @param {KeyCallback} callback
* @returns {Interactions.Key} The calling Key Interaction.
*/
Key.prototype.offKeyRelease = function (keyCode, callback) {
this._keyReleaseCallbacks[keyCode].delete(callback);
if (this._keyReleaseCallbacks[keyCode].size === 0) {
delete this._keyReleaseCallbacks[keyCode];
}
return this;
};
return Key;
}(Plottable.Interaction));
Interactions.Key = Key;
})(Interactions = Plottable.Interactions || (Plottable.Interactions = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Interactions;
(function (Interactions) {
var Pointer = (function (_super) {
__extends(Pointer, _super);
function Pointer() {
var _this = this;
_super.apply(this, arguments);
this._overComponent = false;
this._pointerEnterCallbacks = new Plottable.Utils.CallbackSet();
this._pointerMoveCallbacks = new Plottable.Utils.CallbackSet();
this._pointerExitCallbacks = new Plottable.Utils.CallbackSet();
this._mouseMoveCallback = function (p, e) { return _this._handleMouseEvent(p, e); };
this._touchStartCallback = function (ids, idToPoint, e) { return _this._handleTouchEvent(idToPoint[ids[0]], e); };
}
Pointer.prototype._anchor = function (component) {
_super.prototype._anchor.call(this, component);
this._mouseDispatcher = Plottable.Dispatchers.Mouse.getDispatcher(this._componentAttachedTo.content().node());
this._mouseDispatcher.onMouseMove(this._mouseMoveCallback);
this._touchDispatcher = Plottable.Dispatchers.Touch.getDispatcher(this._componentAttachedTo.content().node());
this._touchDispatcher.onTouchStart(this._touchStartCallback);
};
Pointer.prototype._unanchor = function () {
_super.prototype._unanchor.call(this);
this._mouseDispatcher.offMouseMove(this._mouseMoveCallback);
this._mouseDispatcher = null;
this._touchDispatcher.offTouchStart(this._touchStartCallback);
this._touchDispatcher = null;
};
Pointer.prototype._handleMouseEvent = function (p, e) {
var insideSVG = this._mouseDispatcher.eventInsideSVG(e);
this._handlePointerEvent(p, insideSVG);
};
Pointer.prototype._handleTouchEvent = function (p, e) {
var insideSVG = this._touchDispatcher.eventInsideSVG(e);
this._handlePointerEvent(p, insideSVG);
};
Pointer.prototype._handlePointerEvent = function (p, insideSVG) {
var translatedP = this._translateToComponentSpace(p);
var overComponent = this._isInsideComponent(translatedP);
if (overComponent && insideSVG) {
if (!this._overComponent) {
this._pointerEnterCallbacks.callCallbacks(translatedP);
}
this._pointerMoveCallbacks.callCallbacks(translatedP);
}
else if (this._overComponent) {
this._pointerExitCallbacks.callCallbacks(translatedP);
}
this._overComponent = overComponent && insideSVG;
};
/**
* Adds a callback to be called when the pointer enters the Component.
*
* @param {PointerCallback} callback
* @return {Interactions.Pointer} The calling Pointer Interaction.
*/
Pointer.prototype.onPointerEnter = function (callback) {
this._pointerEnterCallbacks.add(callback);
return this;
};
/**
* Removes a callback that would be called when the pointer enters the Component.
*
* @param {PointerCallback} callback
* @return {Interactions.Pointer} The calling Pointer Interaction.
*/
Pointer.prototype.offPointerEnter = function (callback) {
this._pointerEnterCallbacks.delete(callback);
return this;
};
/**
* Adds a callback to be called when the pointer moves within the Component.
*
* @param {PointerCallback} callback
* @return {Interactions.Pointer} The calling Pointer Interaction.
*/
Pointer.prototype.onPointerMove = function (callback) {
this._pointerMoveCallbacks.add(callback);
return this;
};
/**
* Removes a callback that would be called when the pointer moves within the Component.
*
* @param {PointerCallback} callback
* @return {Interactions.Pointer} The calling Pointer Interaction.
*/
Pointer.prototype.offPointerMove = function (callback) {
this._pointerMoveCallbacks.delete(callback);
return this;
};
/**
* Adds a callback to be called when the pointer exits the Component.
*
* @param {PointerCallback} callback
* @return {Interactions.Pointer} The calling Pointer Interaction.
*/
Pointer.prototype.onPointerExit = function (callback) {
this._pointerExitCallbacks.add(callback);
return this;
};
/**
* Removes a callback that would be called when the pointer exits the Component.
*
* @param {PointerCallback} callback
* @return {Interactions.Pointer} The calling Pointer Interaction.
*/
Pointer.prototype.offPointerExit = function (callback) {
this._pointerExitCallbacks.delete(callback);
return this;
};
return Pointer;
}(Plottable.Interaction));
Interactions.Pointer = Pointer;
})(Interactions = Plottable.Interactions || (Plottable.Interactions = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Interactions;
(function (Interactions) {
/**
* Performs a zoom transformation of the `value` argument scaled by the
* `zoom` argument about the point defined by the `center` argument.
*/
function zoomAt(value, zoom, center) {
return center - (center - value) * zoom;
}
Interactions.zoomAt = zoomAt;
var PanZoom = (function (_super) {
__extends(PanZoom, _super);
/**
* A PanZoom Interaction updates the domains of an x-scale and/or a y-scale
* in response to the user panning or zooming.
*
* @constructor
* @param {TransformableScale} [xScale] The x-scale to update on panning/zooming.
* @param {TransformableScale} [yScale] The y-scale to update on panning/zooming.
*/
function PanZoom(xScale, yScale) {
var _this = this;
_super.call(this);
this._wheelCallback = function (p, e) { return _this._handleWheelEvent(p, e); };
this._touchStartCallback = function (ids, idToPoint, e) { return _this._handleTouchStart(ids, idToPoint, e); };
this._touchMoveCallback = function (ids, idToPoint, e) { return _this._handlePinch(ids, idToPoint, e); };
this._touchEndCallback = function (ids, idToPoint, e) { return _this._handleTouchEnd(ids, idToPoint, e); };
this._touchCancelCallback = function (ids, idToPoint, e) { return _this._handleTouchEnd(ids, idToPoint, e); };
this._panEndCallbacks = new Plottable.Utils.CallbackSet();
this._zoomEndCallbacks = new Plottable.Utils.CallbackSet();
this._xScales = new Plottable.Utils.Set();
this._yScales = new Plottable.Utils.Set();
this._dragInteraction = new Interactions.Drag();
this._setupDragInteraction();
this._touchIds = d3.map();
this._minDomainExtents = new Plottable.Utils.Map();
this._maxDomainExtents = new Plottable.Utils.Map();
this._minDomainValues = new Plottable.Utils.Map();
this._maxDomainValues = new Plottable.Utils.Map();
if (xScale != null) {
this.addXScale(xScale);
}
if (yScale != null) {
this.addYScale(yScale);
}
}
/**
* Pans the chart by a specified amount
*
* @param {Plottable.Point} [translateAmount] The amount by which to translate the x and y scales.
*/
PanZoom.prototype.pan = function (translateAmount) {
var _this = this;
this.xScales().forEach(function (xScale) {
xScale.pan(_this._constrainedTranslation(xScale, translateAmount.x));
});
this.yScales().forEach(function (yScale) {
yScale.pan(_this._constrainedTranslation(yScale, translateAmount.y));
});
};
/**
* Zooms the chart by a specified amount around a specific point
*
* @param {number} [maginfyAmount] The percentage by which to zoom the x and y scale.
* A value of 0.9 zooms in by 10%. A value of 1.1 zooms out by 10%. A value of 1 has
* no effect.
* @param {Plottable.Point} [centerValue] The center in pixels around which to zoom.
* By default, `centerValue` is the center of the x and y range of each scale.
*/
PanZoom.prototype.zoom = function (zoomAmount, centerValue) {
this.xScales().forEach(function (xScale) {
var range = xScale.range();
var xCenter = centerValue === undefined
? (range[1] - range[0]) / 2
: centerValue.x;
xScale.zoom(zoomAmount, xCenter);
});
this.yScales().forEach(function (yScale) {
var range = yScale.range();
var yCenter = centerValue === undefined
? (range[1] - range[0]) / 2
: centerValue.y;
yScale.zoom(zoomAmount, yCenter);
});
};
PanZoom.prototype._anchor = function (component) {
_super.prototype._anchor.call(this, component);
this._dragInteraction.attachTo(component);
this._mouseDispatcher = Plottable.Dispatchers.Mouse.getDispatcher(this._componentAttachedTo.content().node());
this._mouseDispatcher.onWheel(this._wheelCallback);
this._touchDispatcher = Plottable.Dispatchers.Touch.getDispatcher(this._componentAttachedTo.content().node());
this._touchDispatcher.onTouchStart(this._touchStartCallback);
this._touchDispatcher.onTouchMove(this._touchMoveCallback);
this._touchDispatcher.onTouchEnd(this._touchEndCallback);
this._touchDispatcher.onTouchCancel(this._touchCancelCallback);
};
PanZoom.prototype._unanchor = function () {
_super.prototype._unanchor.call(this);
this._mouseDispatcher.offWheel(this._wheelCallback);
this._mouseDispatcher = null;
this._touchDispatcher.offTouchStart(this._touchStartCallback);
this._touchDispatcher.offTouchMove(this._touchMoveCallback);
this._touchDispatcher.offTouchEnd(this._touchEndCallback);
this._touchDispatcher.offTouchCancel(this._touchCancelCallback);
this._touchDispatcher = null;
this._dragInteraction.detachFrom(this._componentAttachedTo);
};
PanZoom.prototype._handleTouchStart = function (ids, idToPoint, e) {
for (var i = 0; i < ids.length && this._touchIds.size() < 2; i++) {
var id = ids[i];
this._touchIds.set(id.toString(), this._translateToComponentSpace(idToPoint[id]));
}
};
PanZoom.prototype._handlePinch = function (ids, idToPoint, e) {
var _this = this;
if (this._touchIds.size() < 2) {
return;
}
var oldPoints = this._touchIds.values();
if (!this._isInsideComponent(this._translateToComponentSpace(oldPoints[0])) ||
!this._isInsideComponent(this._translateToComponentSpace(oldPoints[1]))) {
return;
}
var oldCornerDistance = PanZoom._pointDistance(oldPoints[0], oldPoints[1]);
if (oldCornerDistance === 0) {
return;
}
ids.forEach(function (id) {
if (_this._touchIds.has(id.toString())) {
_this._touchIds.set(id.toString(), _this._translateToComponentSpace(idToPoint[id]));
}
});
var points = this._touchIds.values();
var newCornerDistance = PanZoom._pointDistance(points[0], points[1]);
if (newCornerDistance === 0) {
return;
}
var magnifyAmount = oldCornerDistance / newCornerDistance;
var normalizedPointDiffs = points.map(function (point, i) {
return { x: (point.x - oldPoints[i].x) / magnifyAmount, y: (point.y - oldPoints[i].y) / magnifyAmount };
});
var oldCenterPoint = PanZoom.centerPoint(oldPoints[0], oldPoints[1]);
var centerX = oldCenterPoint.x;
var centerY = oldCenterPoint.y;
this.xScales().forEach(function (xScale) {
var constrained = _this._constrainedZoom(xScale, magnifyAmount, centerX);
centerX = constrained.centerPoint;
magnifyAmount = constrained.zoomAmount;
});
this.yScales().forEach(function (yScale) {
var constrained = _this._constrainedZoom(yScale, magnifyAmount, centerY);
centerY = constrained.centerPoint;
magnifyAmount = constrained.zoomAmount;
});
var constrainedPoints = oldPoints.map(function (oldPoint, i) {
return {
x: normalizedPointDiffs[i].x * magnifyAmount + oldPoint.x,
y: normalizedPointDiffs[i].y * magnifyAmount + oldPoint.y,
};
});
var translateAmount = {
x: centerX - ((constrainedPoints[0].x + constrainedPoints[1].x) / 2),
y: centerY - ((constrainedPoints[0].y + constrainedPoints[1].y) / 2)
};
this.zoom(magnifyAmount, { x: centerX, y: centerY });
this.pan(translateAmount);
};
PanZoom.centerPoint = function (point1, point2) {
var leftX = Math.min(point1.x, point2.x);
var rightX = Math.max(point1.x, point2.x);
var topY = Math.min(point1.y, point2.y);
var bottomY = Math.max(point1.y, point2.y);
return { x: (leftX + rightX) / 2, y: (bottomY + topY) / 2 };
};
PanZoom._pointDistance = function (point1, point2) {
var leftX = Math.min(point1.x, point2.x);
var rightX = Math.max(point1.x, point2.x);
var topY = Math.min(point1.y, point2.y);
var bottomY = Math.max(point1.y, point2.y);
return Math.sqrt(Math.pow(rightX - leftX, 2) + Math.pow(bottomY - topY, 2));
};
PanZoom.prototype._handleTouchEnd = function (ids, idToPoint, e) {
var _this = this;
ids.forEach(function (id) {
_this._touchIds.remove(id.toString());
});
if (this._touchIds.size() > 0) {
this._zoomEndCallbacks.callCallbacks();
}
};
PanZoom.prototype._handleWheelEvent = function (p, e) {
var _this = this;
var translatedP = this._translateToComponentSpace(p);
if (this._isInsideComponent(translatedP)) {
e.preventDefault();
var deltaPixelAmount = e.deltaY * (e.deltaMode ? PanZoom._PIXELS_PER_LINE : 1);
var zoomAmount_1 = Math.pow(2, deltaPixelAmount * .002);
var centerX_1 = translatedP.x;
var centerY_1 = translatedP.y;
this.xScales().forEach(function (xScale) {
var constrained = _this._constrainedZoom(xScale, zoomAmount_1, centerX_1);
centerX_1 = constrained.centerPoint;
zoomAmount_1 = constrained.zoomAmount;
});
this.yScales().forEach(function (yScale) {
var constrained = _this._constrainedZoom(yScale, zoomAmount_1, centerY_1);
centerY_1 = constrained.centerPoint;
zoomAmount_1 = constrained.zoomAmount;
});
this.zoom(zoomAmount_1, { x: centerX_1, y: centerY_1 });
this._zoomEndCallbacks.callCallbacks();
}
};
PanZoom.prototype._constrainedZoom = function (scale, zoomAmount, centerPoint) {
zoomAmount = this._constrainZoomExtents(scale, zoomAmount);
return this._constrainZoomValues(scale, zoomAmount, centerPoint);
};
PanZoom.prototype._constrainZoomExtents = function (scale, zoomAmount) {
var extentIncreasing = zoomAmount > 1;
var boundingDomainExtent = extentIncreasing ? this.maxDomainExtent(scale) : this.minDomainExtent(scale);
if (boundingDomainExtent == null) {
return zoomAmount;
}
var _a = scale.getTransformationDomain(), scaleDomainMin = _a[0], scaleDomainMax = _a[1];
var domainExtent = Math.abs(scaleDomainMax - scaleDomainMin);
var compareF = extentIncreasing ? Math.min : Math.max;
return compareF(zoomAmount, boundingDomainExtent / domainExtent);
};
PanZoom.prototype._constrainZoomValues = function (scale, zoomAmount, centerPoint) {
// when zooming in, we don't have to worry about overflowing domain
if (zoomAmount <= 1) {
return { centerPoint: centerPoint, zoomAmount: zoomAmount };
}
var minDomain = this.minDomainValue(scale);
var maxDomain = this.maxDomainValue(scale);
// if no constraints set, we're done
if (minDomain == null && maxDomain == null) {
return { centerPoint: centerPoint, zoomAmount: zoomAmount };
}
var _a = scale.getTransformationDomain(), scaleDomainMin = _a[0], scaleDomainMax = _a[1];
if (maxDomain != null) {
// compute max range point if zoom applied
var maxRange = scale.scaleTransformation(maxDomain);
var currentMaxRange = scale.scaleTransformation(scaleDomainMax);
var testMaxRange = zoomAt(currentMaxRange, zoomAmount, centerPoint);
// move the center point to prevent max overflow, if necessary
if (testMaxRange > maxRange) {
centerPoint = this._getZoomCenterForTarget(currentMaxRange, zoomAmount, maxRange);
}
}
if (minDomain != null) {
// compute min range point if zoom applied
var minRange = scale.scaleTransformation(minDomain);
var currentMinRange = scale.scaleTransformation(scaleDomainMin);
var testMinRange = zoomAt(currentMinRange, zoomAmount, centerPoint);
// move the center point to prevent min overflow, if necessary
if (testMinRange < minRange) {
centerPoint = this._getZoomCenterForTarget(currentMinRange, zoomAmount, minRange);
}
}
// add fallback to prevent overflowing both min and max
if (maxDomain != null && maxDomain != null) {
var maxRange = scale.scaleTransformation(maxDomain);
var currentMaxRange = scale.scaleTransformation(scaleDomainMax);
var testMaxRange = zoomAt(currentMaxRange, zoomAmount, centerPoint);
var minRange = scale.scaleTransformation(minDomain);
var currentMinRange = scale.scaleTransformation(scaleDomainMin);
var testMinRange = zoomAt(currentMinRange, zoomAmount, centerPoint);
// If we overflow both, use some algebra to solve for centerPoint and
// zoomAmount that will make the domain match the min/max exactly.
// Algebra brought to you by Wolfram Alpha.
if (testMaxRange > maxRange || testMinRange < minRange) {
var denominator = (currentMaxRange - currentMinRange + minRange - maxRange);
if (denominator === 0) {
// In this case the domains already match, so just return no-op values.
centerPoint = (currentMaxRange + currentMinRange) / 2;
zoomAmount = 1;
}
else {
centerPoint = (currentMaxRange * minRange - currentMinRange * maxRange) / denominator;
zoomAmount = (maxRange - minRange) / (currentMaxRange - currentMinRange);
}
}
}
return { centerPoint: centerPoint, zoomAmount: zoomAmount };
};
/**
* Returns the `center` value to be used with `zoomAt` that will produce the
* `target` value given the same `value` and `zoom` arguments. Algebra
* brought to you by Wolfram Alpha.
*/
PanZoom.prototype._getZoomCenterForTarget = function (value, zoom, target) {
return (value * zoom - target) / (zoom - 1);
};
PanZoom.prototype._setupDragInteraction = function () {
var _this = this;
this._dragInteraction.constrainedToComponent(false);
var lastDragPoint;
this._dragInteraction.onDragStart(function () { return lastDragPoint = null; });
this._dragInteraction.onDrag(function (startPoint, endPoint) {
if (_this._touchIds.size() >= 2) {
return;
}
var translateAmount = {
x: (lastDragPoint == null ? startPoint.x : lastDragPoint.x) - endPoint.x,
y: (lastDragPoint == null ? startPoint.y : lastDragPoint.y) - endPoint.y
};
_this.pan(translateAmount);
lastDragPoint = endPoint;
});
this._dragInteraction.onDragEnd(function () { return _this._panEndCallbacks.callCallbacks(); });
};
/**
* Returns a new translation value that respects domain min/max value
* constraints.
*/
PanZoom.prototype._constrainedTranslation = function (scale, translation) {
var _a = scale.getTransformationDomain(), scaleDomainMin = _a[0], scaleDomainMax = _a[1];
if (translation > 0) {
var bound = this.maxDomainValue(scale);
if (bound != null) {
var currentMaxRange = scale.scaleTransformation(scaleDomainMax);
var maxRange = scale.scaleTransformation(bound);
translation = Math.min(currentMaxRange + translation, maxRange) - currentMaxRange;
}
}
else {
var bound = this.minDomainValue(scale);
if (bound != null) {
var currentMinRange = scale.scaleTransformation(scaleDomainMin);
var minRange = scale.scaleTransformation(bound);
translation = Math.max(currentMinRange + translation, minRange) - currentMinRange;
}
}
return translation;
};
PanZoom.prototype._nonLinearScaleWithExtents = function (scale) {
return this.minDomainExtent(scale) != null && this.maxDomainExtent(scale) != null &&
!(scale instanceof Plottable.Scales.Linear) && !(scale instanceof Plottable.Scales.Time);
};
PanZoom.prototype.xScales = function (xScales) {
var _this = this;
if (xScales == null) {
var scales_1 = [];
this._xScales.forEach(function (xScale) {
scales_1.push(xScale);
});
return scales_1;
}
this._xScales = new Plottable.Utils.Set();
xScales.forEach(function (xScale) {
_this.addXScale(xScale);
});
return this;
};
PanZoom.prototype.yScales = function (yScales) {
var _this = this;
if (yScales == null) {
var scales_2 = [];
this._yScales.forEach(function (yScale) {
scales_2.push(yScale);
});
return scales_2;
}
this._yScales = new Plottable.Utils.Set();
yScales.forEach(function (yScale) {
_this.addYScale(yScale);
});
return this;
};
/**
* Adds an x scale to this PanZoom Interaction
*
* @param {TransformableScale} An x scale to add
* @returns {Interactions.PanZoom} The calling PanZoom Interaction.
*/
PanZoom.prototype.addXScale = function (xScale) {
this._xScales.add(xScale);
return this;
};
/**
* Removes an x scale from this PanZoom Interaction
*
* @param {TransformableScale} An x scale to remove
* @returns {Interactions.PanZoom} The calling PanZoom Interaction.
*/
PanZoom.prototype.removeXScale = function (xScale) {
this._xScales.delete(xScale);
this._minDomainExtents.delete(xScale);
this._maxDomainExtents.delete(xScale);
this._minDomainValues.delete(xScale);
this._maxDomainValues.delete(xScale);
return this;
};
/**
* Adds a y scale to this PanZoom Interaction
*
* @param {TransformableScale} A y scale to add
* @returns {Interactions.PanZoom} The calling PanZoom Interaction.
*/
PanZoom.prototype.addYScale = function (yScale) {
this._yScales.add(yScale);
return this;
};
/**
* Removes a y scale from this PanZoom Interaction
*
* @param {TransformableScale} A y scale to remove
* @returns {Interactions.PanZoom} The calling PanZoom Interaction.
*/
PanZoom.prototype.removeYScale = function (yScale) {
this._yScales.delete(yScale);
this._minDomainExtents.delete(yScale);
this._maxDomainExtents.delete(yScale);
this._minDomainValues.delete(yScale);
this._maxDomainValues.delete(yScale);
return this;
};
PanZoom.prototype.minDomainExtent = function (scale, minDomainExtent) {
if (minDomainExtent == null) {
return this._minDomainExtents.get(scale);
}
if (minDomainExtent.valueOf() < 0) {
throw new Error("extent must be non-negative");
}
var maxExtentForScale = this.maxDomainExtent(scale);
if (maxExtentForScale != null && maxExtentForScale.valueOf() < minDomainExtent.valueOf()) {
throw new Error("minDomainExtent must be smaller than maxDomainExtent for the same Scale");
}
if (this._nonLinearScaleWithExtents(scale)) {
Plottable.Utils.Window.warn("Panning and zooming with extents on a nonlinear scale may have unintended behavior.");
}
this._minDomainExtents.set(scale, minDomainExtent);
return this;
};
PanZoom.prototype.maxDomainExtent = function (scale, maxDomainExtent) {
if (maxDomainExtent == null) {
return this._maxDomainExtents.get(scale);
}
if (maxDomainExtent.valueOf() <= 0) {
throw new Error("extent must be positive");
}
var minExtentForScale = this.minDomainExtent(scale);
if (minExtentForScale != null && maxDomainExtent.valueOf() < minExtentForScale.valueOf()) {
throw new Error("maxDomainExtent must be larger than minDomainExtent for the same Scale");
}
if (this._nonLinearScaleWithExtents(scale)) {
Plottable.Utils.Window.warn("Panning and zooming with extents on a nonlinear scale may have unintended behavior.");
}
this._maxDomainExtents.set(scale, maxDomainExtent);
return this;
};
PanZoom.prototype.minDomainValue = function (scale, minDomainValue) {
if (minDomainValue == null) {
return this._minDomainValues.get(scale);
}
var maxValueForScale = this.maxDomainValue(scale);
if (maxValueForScale != null && maxValueForScale.valueOf() <= minDomainValue.valueOf()) {
throw new Error("minDomainValue must be smaller than maxDomainValue for the same Scale");
}
this._minDomainValues.set(scale, minDomainValue);
return this;
};
PanZoom.prototype.maxDomainValue = function (scale, maxDomainValue) {
if (maxDomainValue == null) {
return this._maxDomainValues.get(scale);
}
var minValueForScale = this.minDomainValue(scale);
if (minValueForScale != null && maxDomainValue <= minValueForScale) {
throw new Error("maxDomainValue must be larger than minDomainValue for the same Scale");
}
this._maxDomainValues.set(scale, maxDomainValue);
return this;
};
/**
* Uses the current domain of the scale to apply a minimum and maximum
* domain value for that scale.
*
* This constrains the pan/zoom interaction to show no more than the domain
* of the scale.
*/
PanZoom.prototype.setMinMaxDomainValuesTo = function (scale) {
this._minDomainValues.delete(scale);
this._maxDomainValues.delete(scale);
var _a = scale.getTransformationDomain(), domainMin = _a[0], domainMax = _a[1];
this.minDomainValue(scale, domainMin);
this.maxDomainValue(scale, domainMax);
};
/**
* Adds a callback to be called when panning ends.
*
* @param {PanCallback} callback
* @returns {this} The calling PanZoom Interaction.
*/
PanZoom.prototype.onPanEnd = function (callback) {
this._panEndCallbacks.add(callback);
return this;
};
/**
* Removes a callback that would be called when panning ends.
*
* @param {PanCallback} callback
* @returns {this} The calling PanZoom Interaction.
*/
PanZoom.prototype.offPanEnd = function (callback) {
this._panEndCallbacks.delete(callback);
return this;
};
/**
* Adds a callback to be called when zooming ends.
*
* @param {ZoomCallback} callback
* @returns {this} The calling PanZoom Interaction.
*/
PanZoom.prototype.onZoomEnd = function (callback) {
this._zoomEndCallbacks.add(callback);
return this;
};
/**
* Removes a callback that would be called when zooming ends.
*
* @param {ZoomCallback} callback
* @returns {this} The calling PanZoom Interaction.
*/
PanZoom.prototype.offZoomEnd = function (callback) {
this._zoomEndCallbacks.delete(callback);
return this;
};
/**
* The number of pixels occupied in a line.
*/
PanZoom._PIXELS_PER_LINE = 120;
return PanZoom;
}(Plottable.Interaction));
Interactions.PanZoom = PanZoom;
})(Interactions = Plottable.Interactions || (Plottable.Interactions = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Interactions;
(function (Interactions) {
var Drag = (function (_super) {
__extends(Drag, _super);
function Drag() {
var _this = this;
_super.apply(this, arguments);
this._dragging = false;
this._constrainedToComponent = true;
this._dragStartCallbacks = new Plottable.Utils.CallbackSet();
this._dragCallbacks = new Plottable.Utils.CallbackSet();
this._dragEndCallbacks = new Plottable.Utils.CallbackSet();
this._mouseDownCallback = function (p, e) { return _this._startDrag(p, e); };
this._mouseMoveCallback = function (p, e) { return _this._doDrag(p, e); };
this._mouseUpCallback = function (p, e) { return _this._endDrag(p, e); };
this._touchStartCallback = function (ids, idToPoint, e) { return _this._startDrag(idToPoint[ids[0]], e); };
this._touchMoveCallback = function (ids, idToPoint, e) { return _this._doDrag(idToPoint[ids[0]], e); };
this._touchEndCallback = function (ids, idToPoint, e) { return _this._endDrag(idToPoint[ids[0]], e); };
}
Drag.prototype._anchor = function (component) {
_super.prototype._anchor.call(this, component);
this._mouseDispatcher = Plottable.Dispatchers.Mouse.getDispatcher(this._componentAttachedTo.content().node());
this._mouseDispatcher.onMouseDown(this._mouseDownCallback);
this._mouseDispatcher.onMouseMove(this._mouseMoveCallback);
this._mouseDispatcher.onMouseUp(this._mouseUpCallback);
this._touchDispatcher = Plottable.Dispatchers.Touch.getDispatcher(this._componentAttachedTo.content().node());
this._touchDispatcher.onTouchStart(this._touchStartCallback);
this._touchDispatcher.onTouchMove(this._touchMoveCallback);
this._touchDispatcher.onTouchEnd(this._touchEndCallback);
};
Drag.prototype._unanchor = function () {
_super.prototype._unanchor.call(this);
this._mouseDispatcher.offMouseDown(this._mouseDownCallback);
this._mouseDispatcher.offMouseMove(this._mouseMoveCallback);
this._mouseDispatcher.offMouseUp(this._mouseUpCallback);
this._mouseDispatcher = null;
this._touchDispatcher.offTouchStart(this._touchStartCallback);
this._touchDispatcher.offTouchMove(this._touchMoveCallback);
this._touchDispatcher.offTouchEnd(this._touchEndCallback);
this._touchDispatcher = null;
};
Drag.prototype._translateAndConstrain = function (p) {
var translatedP = this._translateToComponentSpace(p);
if (!this._constrainedToComponent) {
return translatedP;
}
return {
x: Plottable.Utils.Math.clamp(translatedP.x, 0, this._componentAttachedTo.width()),
y: Plottable.Utils.Math.clamp(translatedP.y, 0, this._componentAttachedTo.height()),
};
};
Drag.prototype._startDrag = function (point, event) {
if (event instanceof MouseEvent && event.button !== 0) {
return;
}
var translatedP = this._translateToComponentSpace(point);
if (this._isInsideComponent(translatedP)) {
event.preventDefault();
this._dragging = true;
this._dragOrigin = translatedP;
this._dragStartCallbacks.callCallbacks(this._dragOrigin);
}
};
Drag.prototype._doDrag = function (point, event) {
if (this._dragging) {
this._dragCallbacks.callCallbacks(this._dragOrigin, this._translateAndConstrain(point));
}
};
Drag.prototype._endDrag = function (point, event) {
if (event instanceof MouseEvent && event.button !== 0) {
return;
}
if (this._dragging) {
this._dragging = false;
this._dragEndCallbacks.callCallbacks(this._dragOrigin, this._translateAndConstrain(point));
}
};
Drag.prototype.constrainedToComponent = function (constrainedToComponent) {
if (constrainedToComponent == null) {
return this._constrainedToComponent;
}
this._constrainedToComponent = constrainedToComponent;
return this;
};
/**
* Adds a callback to be called when dragging starts.
*
* @param {DragCallback} callback
* @returns {Drag} The calling Drag Interaction.
*/
Drag.prototype.onDragStart = function (callback) {
this._dragStartCallbacks.add(callback);
return this;
};
/**
* Removes a callback that would be called when dragging starts.
*
* @param {DragCallback} callback
* @returns {Drag} The calling Drag Interaction.
*/
Drag.prototype.offDragStart = function (callback) {
this._dragStartCallbacks.delete(callback);
return this;
};
/**
* Adds a callback to be called during dragging.
*
* @param {DragCallback} callback
* @returns {Drag} The calling Drag Interaction.
*/
Drag.prototype.onDrag = function (callback) {
this._dragCallbacks.add(callback);
return this;
};
/**
* Removes a callback that would be called during dragging.
*
* @param {DragCallback} callback
* @returns {Drag} The calling Drag Interaction.
*/
Drag.prototype.offDrag = function (callback) {
this._dragCallbacks.delete(callback);
return this;
};
/**
* Adds a callback to be called when dragging ends.
*
* @param {DragCallback} callback
* @returns {Drag} The calling Drag Interaction.
*/
Drag.prototype.onDragEnd = function (callback) {
this._dragEndCallbacks.add(callback);
return this;
};
/**
* Removes a callback that would be called when dragging ends.
*
* @param {DragCallback} callback
* @returns {Drag} The calling Drag Interaction.
*/
Drag.prototype.offDragEnd = function (callback) {
this._dragEndCallbacks.delete(callback);
return this;
};
return Drag;
}(Plottable.Interaction));
Interactions.Drag = Drag;
})(Interactions = Plottable.Interactions || (Plottable.Interactions = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Components;
(function (Components) {
var DragBoxLayer = (function (_super) {
__extends(DragBoxLayer, _super);
/**
* Constructs a DragBoxLayer.
*
* A DragBoxLayer is a SelectionBoxLayer with a built-in Drag Interaction.
* A drag gesture will set the Bounds of the box.
* If resizing is enabled using resizable(true), the edges of box can be repositioned.
*
* @constructor
*/
function DragBoxLayer() {
_super.call(this);
this._detectionRadius = 3;
this._resizable = false;
this._movable = false;
this._hasCorners = true;
this.addClass("drag-box-layer");
this._dragInteraction = new Plottable.Interactions.Drag();
this._setUpCallbacks();
this._dragInteraction.attachTo(this);
this._dragStartCallbacks = new Plottable.Utils.CallbackSet();
this._dragCallbacks = new Plottable.Utils.CallbackSet();
this._dragEndCallbacks = new Plottable.Utils.CallbackSet();
}
DragBoxLayer.prototype._setUpCallbacks = function () {
var _this = this;
var resizingEdges;
var topLeft;
var bottomRight;
var lastEndPoint;
var DRAG_MODES = {
newBox: 0,
resize: 1,
move: 2,
};
var mode = DRAG_MODES.newBox;
var onDragStartCallback = function (startPoint) {
resizingEdges = _this._getResizingEdges(startPoint);
var bounds = _this.bounds();
var isInsideBox = bounds.topLeft.x <= startPoint.x && startPoint.x <= bounds.bottomRight.x &&
bounds.topLeft.y <= startPoint.y && startPoint.y <= bounds.bottomRight.y;
if (_this.boxVisible() && (resizingEdges.top || resizingEdges.bottom || resizingEdges.left || resizingEdges.right)) {
mode = DRAG_MODES.resize;
}
else if (_this.boxVisible() && _this.movable() && isInsideBox) {
mode = DRAG_MODES.move;
}
else {
mode = DRAG_MODES.newBox;
_this._setBounds({
topLeft: startPoint,
bottomRight: startPoint,
});
if (_this._xBoundsMode === Components.PropertyMode.VALUE && _this.xScale() != null) {
_this._setXExtent([_this.xScale().invert(startPoint.x), _this.xScale().invert(startPoint.x)]);
}
if (_this._yBoundsMode === Components.PropertyMode.VALUE && _this.yScale() != null) {
_this._setYExtent([_this.yScale().invert(startPoint.y), _this.yScale().invert(startPoint.y)]);
}
_this.render();
}
_this.boxVisible(true);
bounds = _this.bounds();
// copy points so changes to topLeft and bottomRight don't mutate bounds
topLeft = { x: bounds.topLeft.x, y: bounds.topLeft.y };
bottomRight = { x: bounds.bottomRight.x, y: bounds.bottomRight.y };
lastEndPoint = startPoint;
_this._dragStartCallbacks.callCallbacks(bounds);
};
var onDragCallback = function (startPoint, endPoint) {
switch (mode) {
case DRAG_MODES.newBox:
bottomRight.x = endPoint.x;
bottomRight.y = endPoint.y;
break;
case DRAG_MODES.resize:
if (resizingEdges.bottom) {
bottomRight.y = endPoint.y;
}
else if (resizingEdges.top) {
topLeft.y = endPoint.y;
}
if (resizingEdges.right) {
bottomRight.x = endPoint.x;
}
else if (resizingEdges.left) {
topLeft.x = endPoint.x;
}
break;
case DRAG_MODES.move:
var dx = endPoint.x - lastEndPoint.x;
var dy = endPoint.y - lastEndPoint.y;
topLeft.x += dx;
topLeft.y += dy;
bottomRight.x += dx;
bottomRight.y += dy;
lastEndPoint = endPoint;
break;
}
_this._setBounds({
topLeft: topLeft,
bottomRight: bottomRight,
});
if (_this._xBoundsMode === Components.PropertyMode.VALUE && _this.xScale() != null) {
_this._setXExtent([_this.xScale().invert(topLeft.x), _this.xScale().invert(bottomRight.x)]);
}
if (_this._yBoundsMode === Components.PropertyMode.VALUE && _this.yScale() != null) {
_this._setYExtent([_this.yScale().invert(topLeft.y), _this.yScale().invert(bottomRight.y)]);
}
_this.render();
_this._dragCallbacks.callCallbacks(_this.bounds());
};
var onDragEndCallback = function (startPoint, endPoint) {
if (mode === DRAG_MODES.newBox && startPoint.x === endPoint.x && startPoint.y === endPoint.y) {
_this.boxVisible(false);
}
_this._dragEndCallbacks.callCallbacks(_this.bounds());
};
this._dragInteraction.onDragStart(onDragStartCallback);
this._dragInteraction.onDrag(onDragCallback);
this._dragInteraction.onDragEnd(onDragEndCallback);
this._disconnectInteraction = function () {
_this._dragInteraction.offDragStart(onDragStartCallback);
_this._dragInteraction.offDrag(onDragCallback);
_this._dragInteraction.offDragEnd(onDragEndCallback);
_this._dragInteraction.detachFrom(_this);
};
};
DragBoxLayer.prototype._setup = function () {
var _this = this;
_super.prototype._setup.call(this);
var createLine = function () { return _this._box.append("line").style({
"opacity": 0,
"stroke": "pink",
"pointer-events": "visibleStroke",
}); };
this._detectionEdgeT = createLine().classed("drag-edge-tb", true);
this._detectionEdgeB = createLine().classed("drag-edge-tb", true);
this._detectionEdgeL = createLine().classed("drag-edge-lr", true);
this._detectionEdgeR = createLine().classed("drag-edge-lr", true);
if (this._hasCorners) {
var createCorner = function () { return _this._box.append("circle")
.style({
"opacity": 0,
"fill": "pink",
"pointer-events": "visibleFill",
}); };
this._detectionCornerTL = createCorner().classed("drag-corner-tl", true);
this._detectionCornerTR = createCorner().classed("drag-corner-tr", true);
this._detectionCornerBL = createCorner().classed("drag-corner-bl", true);
this._detectionCornerBR = createCorner().classed("drag-corner-br", true);
}
};
DragBoxLayer.prototype._getResizingEdges = function (p) {
var edges = {
top: false,
bottom: false,
left: false,
right: false,
};
if (!this.resizable()) {
return edges;
}
var bounds = this.bounds();
var t = bounds.topLeft.y;
var b = bounds.bottomRight.y;
var l = bounds.topLeft.x;
var r = bounds.bottomRight.x;
var rad = this._detectionRadius;
if (l - rad <= p.x && p.x <= r + rad) {
edges.top = (t - rad <= p.y && p.y <= t + rad);
edges.bottom = (b - rad <= p.y && p.y <= b + rad);
}
if (t - rad <= p.y && p.y <= b + rad) {
edges.left = (l - rad <= p.x && p.x <= l + rad);
edges.right = (r - rad <= p.x && p.x <= r + rad);
}
return edges;
};
DragBoxLayer.prototype.renderImmediately = function () {
_super.prototype.renderImmediately.call(this);
if (this.boxVisible()) {
var bounds = this.bounds();
var t = bounds.topLeft.y;
var b = bounds.bottomRight.y;
var l = bounds.topLeft.x;
var r = bounds.bottomRight.x;
this._detectionEdgeT.attr({
x1: l, y1: t, x2: r, y2: t,
"stroke-width": this._detectionRadius * 2,
});
this._detectionEdgeB.attr({
x1: l, y1: b, x2: r, y2: b,
"stroke-width": this._detectionRadius * 2,
});
this._detectionEdgeL.attr({
x1: l, y1: t, x2: l, y2: b,
"stroke-width": this._detectionRadius * 2,
});
this._detectionEdgeR.attr({
x1: r, y1: t, x2: r, y2: b,
"stroke-width": this._detectionRadius * 2,
});
if (this._hasCorners) {
this._detectionCornerTL.attr({ cx: l, cy: t, r: this._detectionRadius });
this._detectionCornerTR.attr({ cx: r, cy: t, r: this._detectionRadius });
this._detectionCornerBL.attr({ cx: l, cy: b, r: this._detectionRadius });
this._detectionCornerBR.attr({ cx: r, cy: b, r: this._detectionRadius });
}
}
return this;
};
DragBoxLayer.prototype.detectionRadius = function (r) {
if (r == null) {
return this._detectionRadius;
}
if (r < 0) {
throw new Error("detection radius cannot be negative.");
}
this._detectionRadius = r;
this.render();
return this;
};
DragBoxLayer.prototype.resizable = function (canResize) {
if (canResize == null) {
return this._resizable;
}
this._resizable = canResize;
this._setResizableClasses(canResize);
return this;
};
// Sets resizable classes. Overridden by subclasses that only resize in one dimension.
DragBoxLayer.prototype._setResizableClasses = function (canResize) {
if (canResize && this.enabled()) {
this.addClass("x-resizable");
this.addClass("y-resizable");
}
else {
this.removeClass("x-resizable");
this.removeClass("y-resizable");
}
};
DragBoxLayer.prototype.movable = function (movable) {
if (movable == null) {
return this._movable;
}
this._movable = movable;
this._setMovableClass();
return this;
};
DragBoxLayer.prototype._setMovableClass = function () {
if (this.movable() && this.enabled()) {
this.addClass("movable");
}
else {
this.removeClass("movable");
}
};
/**
* Sets the callback to be called when dragging starts.
*
* @param {DragBoxCallback} callback
* @returns {DragBoxLayer} The calling DragBoxLayer.
*/
DragBoxLayer.prototype.onDragStart = function (callback) {
this._dragStartCallbacks.add(callback);
return this;
};
/**
* Removes a callback to be called when dragging starts.
*
* @param {DragBoxCallback} callback
* @returns {DragBoxLayer} The calling DragBoxLayer.
*/
DragBoxLayer.prototype.offDragStart = function (callback) {
this._dragStartCallbacks.delete(callback);
return this;
};
/**
* Sets a callback to be called during dragging.
*
* @param {DragBoxCallback} callback
* @returns {DragBoxLayer} The calling DragBoxLayer.
*/
DragBoxLayer.prototype.onDrag = function (callback) {
this._dragCallbacks.add(callback);
return this;
};
/**
* Removes a callback to be called during dragging.
*
* @param {DragBoxCallback} callback
* @returns {DragBoxLayer} The calling DragBoxLayer.
*/
DragBoxLayer.prototype.offDrag = function (callback) {
this._dragCallbacks.delete(callback);
return this;
};
/**
* Sets a callback to be called when dragging ends.
*
* @param {DragBoxCallback} callback
* @returns {DragBoxLayer} The calling DragBoxLayer.
*/
DragBoxLayer.prototype.onDragEnd = function (callback) {
this._dragEndCallbacks.add(callback);
return this;
};
/**
* Removes a callback to be called when dragging ends.
*
* @param {DragBoxCallback} callback
* @returns {DragBoxLayer} The calling DragBoxLayer.
*/
DragBoxLayer.prototype.offDragEnd = function (callback) {
this._dragEndCallbacks.delete(callback);
return this;
};
/**
* Gets the internal Interactions.Drag of the DragBoxLayer.
*/
DragBoxLayer.prototype.dragInteraction = function () {
return this._dragInteraction;
};
DragBoxLayer.prototype.enabled = function (enabled) {
if (enabled == null) {
return this._dragInteraction.enabled();
}
this._dragInteraction.enabled(enabled);
this._setResizableClasses(this.resizable());
this._setMovableClass();
return this;
};
DragBoxLayer.prototype.destroy = function () {
var _this = this;
_super.prototype.destroy.call(this);
this._dragStartCallbacks.forEach(function (callback) { return _this._dragCallbacks.delete(callback); });
this._dragCallbacks.forEach(function (callback) { return _this._dragCallbacks.delete(callback); });
this._dragEndCallbacks.forEach(function (callback) { return _this._dragEndCallbacks.delete(callback); });
this._disconnectInteraction();
};
DragBoxLayer.prototype.detach = function () {
this._resetState();
this._dragInteraction.detachFrom(this);
_super.prototype.detach.call(this);
return this;
};
DragBoxLayer.prototype.anchor = function (selection) {
this._dragInteraction.attachTo(this);
_super.prototype.anchor.call(this, selection);
return this;
};
DragBoxLayer.prototype._resetState = function () {
this.bounds({
topLeft: { x: 0, y: 0 },
bottomRight: { x: 0, y: 0 },
});
};
return DragBoxLayer;
}(Components.SelectionBoxLayer));
Components.DragBoxLayer = DragBoxLayer;
})(Components = Plottable.Components || (Plottable.Components = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Components;
(function (Components) {
var XDragBoxLayer = (function (_super) {
__extends(XDragBoxLayer, _super);
/**
* An XDragBoxLayer is a DragBoxLayer whose size can only be set in the X-direction.
* The y-values of the bounds() are always set to 0 and the height() of the XDragBoxLayer.
*
* @constructor
*/
function XDragBoxLayer() {
_super.call(this);
this.addClass("x-drag-box-layer");
this._hasCorners = false;
}
XDragBoxLayer.prototype.computeLayout = function (origin, availableWidth, availableHeight) {
_super.prototype.computeLayout.call(this, origin, availableWidth, availableHeight);
// set correct bounds when width/height changes
this._setBounds(this.bounds());
return this;
};
XDragBoxLayer.prototype._setBounds = function (newBounds) {
_super.prototype._setBounds.call(this, {
topLeft: { x: newBounds.topLeft.x, y: 0 },
bottomRight: { x: newBounds.bottomRight.x, y: this.height() },
});
};
XDragBoxLayer.prototype._setResizableClasses = function (canResize) {
if (canResize && this.enabled()) {
this.addClass("x-resizable");
}
else {
this.removeClass("x-resizable");
}
};
XDragBoxLayer.prototype.yScale = function (yScale) {
if (yScale == null) {
return _super.prototype.yScale.call(this);
}
throw new Error("yScales cannot be set on an XDragBoxLayer");
};
XDragBoxLayer.prototype.yExtent = function (yExtent) {
if (yExtent == null) {
return _super.prototype.yExtent.call(this);
}
throw new Error("XDragBoxLayer has no yExtent");
};
return XDragBoxLayer;
}(Components.DragBoxLayer));
Components.XDragBoxLayer = XDragBoxLayer;
})(Components = Plottable.Components || (Plottable.Components = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Components;
(function (Components) {
var YDragBoxLayer = (function (_super) {
__extends(YDragBoxLayer, _super);
/**
* A YDragBoxLayer is a DragBoxLayer whose size can only be set in the Y-direction.
* The x-values of the bounds() are always set to 0 and the width() of the YDragBoxLayer.
*
* @constructor
*/
function YDragBoxLayer() {
_super.call(this);
this.addClass("y-drag-box-layer");
this._hasCorners = false;
}
YDragBoxLayer.prototype.computeLayout = function (origin, availableWidth, availableHeight) {
_super.prototype.computeLayout.call(this, origin, availableWidth, availableHeight);
// set correct bounds when width/height changes
this._setBounds(this.bounds());
return this;
};
YDragBoxLayer.prototype._setBounds = function (newBounds) {
_super.prototype._setBounds.call(this, {
topLeft: { x: 0, y: newBounds.topLeft.y },
bottomRight: { x: this.width(), y: newBounds.bottomRight.y },
});
};
YDragBoxLayer.prototype._setResizableClasses = function (canResize) {
if (canResize && this.enabled()) {
this.addClass("y-resizable");
}
else {
this.removeClass("y-resizable");
}
};
YDragBoxLayer.prototype.xScale = function (xScale) {
if (xScale == null) {
return _super.prototype.xScale.call(this);
}
throw new Error("xScales cannot be set on an YDragBoxLayer");
};
YDragBoxLayer.prototype.xExtent = function (xExtent) {
if (xExtent == null) {
return _super.prototype.xExtent.call(this);
}
throw new Error("YDragBoxLayer has no xExtent");
};
return YDragBoxLayer;
}(Components.DragBoxLayer));
Components.YDragBoxLayer = YDragBoxLayer;
})(Components = Plottable.Components || (Plottable.Components = {}));
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
;
})(Plottable || (Plottable = {}));
var Plottable;
(function (Plottable) {
var Components;
(function (Components) {
var DragLineLayer = (function (_super) {
__extends(DragLineLayer, _super);
function DragLineLayer(orientation) {
var _this = this;
_super.call(this, orientation);
this._detectionRadius = 3;
this._enabled = true;
this.addClass("drag-line-layer");
this.addClass("enabled");
this._dragInteraction = new Plottable.Interactions.Drag();
this._dragInteraction.attachTo(this);
var onLine = function (p) {
return (_this._isVertical() &&
_this.pixelPosition() - _this.detectionRadius() <= p.x &&
p.x <= _this.pixelPosition() + _this.detectionRadius()) ||
(!_this._isVertical() &&
_this.pixelPosition() - _this.detectionRadius() <= p.y &&
p.y <= _this.pixelPosition() + _this.detectionRadius());
};
var dragging = false;
var interactionDragStartCallback = function (start) {
if (onLine(start)) {
dragging = true;
_this._dragStartCallbacks.callCallbacks(_this);
}
};
this._dragInteraction.onDragStart(interactionDragStartCallback);
var interactionDragCallback = function (start, end) {
if (dragging) {
_this._setPixelPositionWithoutChangingMode(_this._isVertical() ? end.x : end.y);
_this._dragCallbacks.callCallbacks(_this);
}
};
this._dragInteraction.onDrag(interactionDragCallback);
var interactionDragEndCallback = function (start, end) {
if (dragging) {
dragging = false;
_this._dragEndCallbacks.callCallbacks(_this);
}
};
this._dragInteraction.onDragEnd(interactionDragEndCallback);
this._disconnectInteraction = function () {
_this._dragInteraction.offDragStart(interactionDragStartCallback);
_this._dragInteraction.offDrag(interactionDragCallback);
_this._dragInteraction.offDragEnd(interactionDragEndCallback);
_this._dragInteraction.detachFrom(_this);
};
this._dragStartCallbacks = new Plottable.Utils.CallbackSet();
this._dragCallbacks = new Plottable.Utils.CallbackSet();
this._dragEndCallbacks = new Plottable.Utils.CallbackSet();
}
DragLineLayer.prototype._setup = function () {
_super.prototype._setup.call(this);
this._detectionEdge = this.content().append("line").style({
"opacity": 0,
"stroke": "pink",
"pointer-events": "visibleStroke",
}).classed("drag-edge", true);
};
DragLineLayer.prototype.renderImmediately = function () {
_super.prototype.renderImmediately.call(this);
this._detectionEdge.attr({
x1: this._isVertical() ? this.pixelPosition() : 0,
y1: this._isVertical() ? 0 : this.pixelPosition(),
x2: this._isVertical() ? this.pixelPosition() : this.width(),
y2: this._isVertical() ? this.height() : this.pixelPosition(),
"stroke-width": this._detectionRadius * 2,
});
return this;
};
DragLineLayer.prototype.detectionRadius = function (detectionRadius) {
if (detectionRadius == null) {
return this._detectionRadius;
}
if (detectionRadius < 0) {
throw new Error("detection radius cannot be negative.");
}
this._detectionRadius = detectionRadius;
this.render();
return this;
};
DragLineLayer.prototype.enabled = function (enabled) {
if (enabled == null) {
return this._enabled;
}
this._enabled = enabled;
if (enabled) {
this.addClass("enabled");
}
else {
this.removeClass("enabled");
}
this._dragInteraction.enabled(enabled);
return this;
};
/**
* Sets the callback to be called when dragging starts.
* The callback will be passed the calling DragLineLayer.
*
* @param {DragLineCallback<D>} callback
* @returns {DragLineLayer<D>} The calling DragLineLayer.
*/
DragLineLayer.prototype.onDragStart = function (callback) {
this._dragStartCallbacks.add(callback);
return this;
};
/**
* Removes a callback that would be called when dragging starts.
*
* @param {DragLineCallback<D>} callback
* @returns {DragLineLayer<D>} The calling DragLineLayer.
*/
DragLineLayer.prototype.offDragStart = function (callback) {
this._dragStartCallbacks.delete(callback);
return this;
};
/**
* Sets a callback to be called during dragging.
* The callback will be passed the calling DragLineLayer.
*
* @param {DragLineCallback<D>} callback
* @returns {DragLineLayer<D>} The calling DragLineLayer.
*/
DragLineLayer.prototype.onDrag = function (callback) {
this._dragCallbacks.add(callback);
return this;
};
/**
* Removes a callback that would be called during dragging.
*
* @param {DragLineCallback<D>} callback
* @returns {DragLineLayer<D>} The calling DragLineLayer.
*/
DragLineLayer.prototype.offDrag = function (callback) {
this._dragCallbacks.delete(callback);
return this;
};
/**
* Sets a callback to be called when dragging ends.
* The callback will be passed the calling DragLineLayer.
*
* @param {DragLineCallback<D>} callback
* @returns {DragLineLayer<D>} The calling DragLineLayer.
*/
DragLineLayer.prototype.onDragEnd = function (callback) {
this._dragEndCallbacks.add(callback);
return this;
};
/**
* Removes a callback that would be called when dragging ends.
*
* @param {DragLineCallback<D>} callback
* @returns {DragLineLayer<D>} The calling DragLineLayer.
*/
DragLineLayer.prototype.offDragEnd = function (callback) {
this._dragEndCallbacks.delete(callback);
return this;
};
DragLineLayer.prototype.destroy = function () {
var _this = this;
_super.prototype.destroy.call(this);
this._dragStartCallbacks.forEach(function (callback) { return _this._dragStartCallbacks.delete(callback); });
this._dragCallbacks.forEach(function (callback) { return _this._dragCallbacks.delete(callback); });
this._dragEndCallbacks.forEach(function (callback) { return _this._dragEndCallbacks.delete(callback); });
this._disconnectInteraction();
};
return DragLineLayer;
}(Components.GuideLineLayer));
Components.DragLineLayer = DragLineLayer;
})(Components = Plottable.Components || (Plottable.Components = {}));
})(Plottable || (Plottable = {}));
return Plottable;
}));
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.SVGTypewriter = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
(function (global){
/**
* Copyright 2017-present Palantir Technologies, Inc. All rights reserved.
* Licensed under the MIT License (the "License"); you may obtain a copy of the
* license at https://github.com/palantir/svg-typewriter/blob/develop/LICENSE
*/
"use strict";
var d3 = (typeof window !== "undefined" ? window['d3'] : typeof global !== "undefined" ? global['d3'] : null);
var BaseAnimator = (function () {
function BaseAnimator() {
this.duration(BaseAnimator.DEFAULT_DURATION_MILLISECONDS);
this.delay(0);
this.easing(BaseAnimator.DEFAULT_EASING);
this.moveX(0);
this.moveY(0);
}
BaseAnimator.prototype.animate = function (selection) {
var xForm = d3.transform("");
xForm.translate = [this.moveX(), this.moveY()];
selection.attr("transform", xForm.toString());
xForm.translate = [0, 0];
return this._animate(selection, { transform: xForm.toString() });
};
BaseAnimator.prototype._animate = function (selection, attr) {
return selection.transition()
.ease(this.easing())
.duration(this.duration())
.delay(this.delay())
.attr(attr);
};
BaseAnimator.prototype.duration = function (duration) {
if (duration == null) {
return this._duration;
}
else {
this._duration = duration;
return this;
}
};
BaseAnimator.prototype.moveX = function (shift) {
if (shift == null) {
return this._moveX;
}
else {
this._moveX = shift;
return this;
}
};
BaseAnimator.prototype.moveY = function (shift) {
if (shift == null) {
return this._moveY;
}
else {
this._moveY = shift;
return this;
}
};
BaseAnimator.prototype.delay = function (delay) {
if (delay == null) {
return this._delay;
}
else {
this._delay = delay;
return this;
}
};
BaseAnimator.prototype.easing = function (easing) {
if (easing == null) {
return this._easing;
}
else {
this._easing = easing;
return this;
}
};
return BaseAnimator;
}());
/**
* The default duration of the animation in milliseconds
*/
BaseAnimator.DEFAULT_DURATION_MILLISECONDS = 300;
/**
* The default easing of the animation
*/
BaseAnimator.DEFAULT_EASING = "exp-out";
exports.BaseAnimator = BaseAnimator;
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}],2:[function(require,module,exports){
/**
* Copyright 2017-present Palantir Technologies, Inc. All rights reserved.
* Licensed under the MIT License (the "License"); you may obtain a copy of the
* license at https://github.com/palantir/svg-typewriter/blob/develop/LICENSE
*/
"use strict";
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
__export(require("./baseAnimator"));
__export(require("./opacityAnimator"));
__export(require("./unveilAnimator"));
},{"./baseAnimator":1,"./opacityAnimator":3,"./unveilAnimator":4}],3:[function(require,module,exports){
/**
* Copyright 2017-present Palantir Technologies, Inc. All rights reserved.
* Licensed under the MIT License (the "License"); you may obtain a copy of the
* license at https://github.com/palantir/svg-typewriter/blob/develop/LICENSE
*/
"use strict";
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var baseAnimator_1 = require("./baseAnimator");
var OpacityAnimator = (function (_super) {
__extends(OpacityAnimator, _super);
function OpacityAnimator() {
return _super.apply(this, arguments) || this;
}
OpacityAnimator.prototype.animate = function (selection) {
var area = selection.select(".text-area");
area.attr("opacity", 0);
var attr = {
opacity: 1,
};
this._animate(area, attr);
return _super.prototype.animate.call(this, selection);
};
return OpacityAnimator;
}(baseAnimator_1.BaseAnimator));
exports.OpacityAnimator = OpacityAnimator;
},{"./baseAnimator":1}],4:[function(require,module,exports){
/**
* Copyright 2017-present Palantir Technologies, Inc. All rights reserved.
* Licensed under the MIT License (the "License"); you may obtain a copy of the
* license at https://github.com/palantir/svg-typewriter/blob/develop/LICENSE
*/
"use strict";
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var Utils = require("../utils");
var baseAnimator_1 = require("./baseAnimator");
var UnveilAnimator = (function (_super) {
__extends(UnveilAnimator, _super);
function UnveilAnimator() {
var _this = _super.call(this) || this;
_this.direction("bottom");
return _this;
}
UnveilAnimator.prototype.direction = function (direction) {
if (direction == null) {
return this._direction;
}
else {
if (UnveilAnimator.SupportedDirections.indexOf(direction) === -1) {
throw new Error("unsupported direction - " + direction);
}
this._direction = direction;
return this;
}
};
UnveilAnimator.prototype.animate = function (selection) {
var attr = Utils.DOM.getBBox(selection);
var mask = selection.select(".clip-rect");
mask.attr("width", 0);
mask.attr("height", 0);
switch (this._direction) {
case "top":
mask.attr("y", attr.y + attr.height);
mask.attr("x", attr.x);
mask.attr("width", attr.width);
break;
case "bottom":
mask.attr("y", attr.y);
mask.attr("x", attr.x);
mask.attr("width", attr.width);
break;
case "left":
mask.attr("y", attr.y);
mask.attr("x", attr.x);
mask.attr("height", attr.height);
break;
case "right":
mask.attr("y", attr.y);
mask.attr("x", attr.x + attr.width);
mask.attr("height", attr.height);
break;
default:
break;
}
this._animate(mask, attr);
return _super.prototype.animate.call(this, selection);
};
return UnveilAnimator;
}(baseAnimator_1.BaseAnimator));
UnveilAnimator.SupportedDirections = ["top", "bottom", "left", "right"];
exports.UnveilAnimator = UnveilAnimator;
},{"../utils":14,"./baseAnimator":1}],5:[function(require,module,exports){
/**
* Copyright 2017-present Palantir Technologies, Inc. All rights reserved.
* Licensed under the MIT License (the "License"); you may obtain a copy of the
* license at https://github.com/palantir/svg-typewriter/blob/develop/LICENSE
*/
"use strict";
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
__export(require("./animators"));
__export(require("./measurers"));
__export(require("./utils"));
__export(require("./wrappers"));
__export(require("./writers"));
},{"./animators":2,"./measurers":10,"./utils":14,"./wrappers":18,"./writers":21}],6:[function(require,module,exports){
/**
* Copyright 2017-present Palantir Technologies, Inc. All rights reserved.
* Licensed under the MIT License (the "License"); you may obtain a copy of the
* license at https://github.com/palantir/svg-typewriter/blob/develop/LICENSE
*/
"use strict";
var Utils = require("../utils");
;
var AbstractMeasurer = (function () {
function AbstractMeasurer(area, className) {
this.textMeasurer = this.getTextMeasurer(area, className);
}
AbstractMeasurer.prototype.measure = function (text) {
if (text === void 0) { text = AbstractMeasurer.HEIGHT_TEXT; }
return this.textMeasurer(text);
};
AbstractMeasurer.prototype.checkSelectionIsText = function (d) {
return d[0][0].tagName === "text" || !d.select("text").empty();
};
AbstractMeasurer.prototype.getTextMeasurer = function (area, className) {
var _this = this;
if (!this.checkSelectionIsText(area)) {
var textElement_1 = area.append("text");
if (className) {
textElement_1.classed(className, true);
}
textElement_1.remove();
return function (text) {
area.node().appendChild(textElement_1.node());
var areaDimension = _this.measureBBox(textElement_1, text);
textElement_1.remove();
return areaDimension;
};
}
else {
var parentNode_1 = area.node().parentNode;
var textSelection_1;
if (area[0][0].tagName === "text") {
textSelection_1 = area;
}
else {
textSelection_1 = area.select("text");
}
area.remove();
return function (text) {
parentNode_1.appendChild(area.node());
var areaDimension = _this.measureBBox(textSelection_1, text);
area.remove();
return areaDimension;
};
}
};
AbstractMeasurer.prototype.measureBBox = function (d, text) {
d.text(text);
var bb = Utils.DOM.getBBox(d);
return { width: bb.width, height: bb.height };
};
return AbstractMeasurer;
}());
AbstractMeasurer.HEIGHT_TEXT = "bqpdl";
exports.AbstractMeasurer = AbstractMeasurer;
},{"../utils":14}],7:[function(require,module,exports){
/**
* Copyright 2017-present Palantir Technologies, Inc. All rights reserved.
* Licensed under the MIT License (the "License"); you may obtain a copy of the
* license at https://github.com/palantir/svg-typewriter/blob/develop/LICENSE
*/
"use strict";
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var Utils = require("../utils");
var characterMeasurer_1 = require("./characterMeasurer");
var CacheCharacterMeasurer = (function (_super) {
__extends(CacheCharacterMeasurer, _super);
function CacheCharacterMeasurer(area, className, useGuards) {
var _this = _super.call(this, area, className, useGuards) || this;
_this.cache = new Utils.Cache(function (c) {
return _this._measureCharacterNotFromCache(c);
});
return _this;
}
CacheCharacterMeasurer.prototype._measureCharacterNotFromCache = function (c) {
return _super.prototype._measureCharacter.call(this, c);
};
CacheCharacterMeasurer.prototype._measureCharacter = function (c) {
return this.cache.get(c);
};
CacheCharacterMeasurer.prototype.reset = function () {
this.cache.clear();
};
return CacheCharacterMeasurer;
}(characterMeasurer_1.CharacterMeasurer));
exports.CacheCharacterMeasurer = CacheCharacterMeasurer;
},{"../utils":14,"./characterMeasurer":9}],8:[function(require,module,exports){
/**
* Copyright 2017-present Palantir Technologies, Inc. All rights reserved.
* Licensed under the MIT License (the "License"); you may obtain a copy of the
* license at https://github.com/palantir/svg-typewriter/blob/develop/LICENSE
*/
"use strict";
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var Utils = require("../utils");
var cacheCharacterMeasurer_1 = require("./cacheCharacterMeasurer");
var CacheMeasurer = (function (_super) {
__extends(CacheMeasurer, _super);
function CacheMeasurer(area, className) {
var _this = _super.call(this, area, className) || this;
_this.dimCache = new Utils.Cache(function (s) {
return _this._measureNotFromCache(s);
});
return _this;
}
CacheMeasurer.prototype._measureNotFromCache = function (s) {
return _super.prototype.measure.call(this, s);
};
CacheMeasurer.prototype.measure = function (s) {
return this.dimCache.get(s);
};
CacheMeasurer.prototype.reset = function () {
this.dimCache.clear();
_super.prototype.reset.call(this);
};
return CacheMeasurer;
}(cacheCharacterMeasurer_1.CacheCharacterMeasurer));
exports.CacheMeasurer = CacheMeasurer;
},{"../utils":14,"./cacheCharacterMeasurer":7}],9:[function(require,module,exports){
(function (global){
/**
* Copyright 2017-present Palantir Technologies, Inc. All rights reserved.
* Licensed under the MIT License (the "License"); you may obtain a copy of the
* license at https://github.com/palantir/svg-typewriter/blob/develop/LICENSE
*/
"use strict";
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var d3 = (typeof window !== "undefined" ? window['d3'] : typeof global !== "undefined" ? global['d3'] : null);
var measurer_1 = require("./measurer");
var CharacterMeasurer = (function (_super) {
__extends(CharacterMeasurer, _super);
function CharacterMeasurer() {
return _super.apply(this, arguments) || this;
}
CharacterMeasurer.prototype._measureCharacter = function (c) {
return _super.prototype._measureLine.call(this, c);
};
CharacterMeasurer.prototype._measureLine = function (line) {
var _this = this;
var charactersDimensions = line.split("").map(function (c) { return _this._measureCharacter(c); });
return {
height: d3.max(charactersDimensions, function (dim) { return dim.height; }),
width: d3.sum(charactersDimensions, function (dim) { return dim.width; }),
};
};
return CharacterMeasurer;
}(measurer_1.Measurer));
exports.CharacterMeasurer = CharacterMeasurer;
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"./measurer":11}],10:[function(require,module,exports){
/**
* Copyright 2017-present Palantir Technologies, Inc. All rights reserved.
* Licensed under the MIT License (the "License"); you may obtain a copy of the
* license at https://github.com/palantir/svg-typewriter/blob/develop/LICENSE
*/
"use strict";
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
__export(require("./abstractMeasurer"));
__export(require("./cacheCharacterMeasurer"));
__export(require("./cacheMeasurer"));
__export(require("./characterMeasurer"));
__export(require("./measurer"));
},{"./abstractMeasurer":6,"./cacheCharacterMeasurer":7,"./cacheMeasurer":8,"./characterMeasurer":9,"./measurer":11}],11:[function(require,module,exports){
(function (global){
/**
* Copyright 2017-present Palantir Technologies, Inc. All rights reserved.
* Licensed under the MIT License (the "License"); you may obtain a copy of the
* license at https://github.com/palantir/svg-typewriter/blob/develop/LICENSE
*/
"use strict";
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var d3 = (typeof window !== "undefined" ? window['d3'] : typeof global !== "undefined" ? global['d3'] : null);
var abstractMeasurer_1 = require("./abstractMeasurer");
var Measurer = (function (_super) {
__extends(Measurer, _super);
function Measurer(area, className, useGuards) {
if (className === void 0) { className = null; }
if (useGuards === void 0) { useGuards = false; }
var _this = _super.call(this, area, className) || this;
_this.useGuards = useGuards;
return _this;
}
// Guards assures same line height and width of whitespaces on both ends.
Measurer.prototype._addGuards = function (text) {
return abstractMeasurer_1.AbstractMeasurer.HEIGHT_TEXT + text + abstractMeasurer_1.AbstractMeasurer.HEIGHT_TEXT;
};
Measurer.prototype._measureLine = function (line, forceGuards) {
if (forceGuards === void 0) { forceGuards = false; }
var useGuards = this.useGuards || forceGuards || /^[\t ]$/.test(line);
var measuredLine = useGuards ? this._addGuards(line) : line;
var measuredLineDimensions = _super.prototype.measure.call(this, measuredLine);
measuredLineDimensions.width -= useGuards ? (2 * this.getGuardWidth()) : 0;
return measuredLineDimensions;
};
Measurer.prototype.measure = function (text) {
var _this = this;
if (text === void 0) { text = abstractMeasurer_1.AbstractMeasurer.HEIGHT_TEXT; }
if (text.trim() === "") {
return { width: 0, height: 0 };
}
var linesDimensions = text.trim().split("\n").map(function (line) { return _this._measureLine(line); });
return {
height: d3.sum(linesDimensions, function (dim) { return dim.height; }),
width: d3.max(linesDimensions, function (dim) { return dim.width; }),
};
};
Measurer.prototype.getGuardWidth = function () {
if (this.guardWidth == null) {
this.guardWidth = _super.prototype.measure.call(this).width;
}
return this.guardWidth;
};
return Measurer;
}(abstractMeasurer_1.AbstractMeasurer));
exports.Measurer = Measurer;
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"./abstractMeasurer":6}],12:[function(require,module,exports){
(function (global){
/**
* Copyright 2017-present Palantir Technologies, Inc. All rights reserved.
* Licensed under the MIT License (the "License"); you may obtain a copy of the
* license at https://github.com/palantir/svg-typewriter/blob/develop/LICENSE
*/
"use strict";
var d3 = (typeof window !== "undefined" ? window['d3'] : typeof global !== "undefined" ? global['d3'] : null);
var Cache = (function () {
/**
* @constructor
*
* @param {string} compute The function whose results will be cached.
*/
function Cache(compute) {
this.cache = d3.map();
this.compute = compute;
}
/**
* Attempt to look up k in the cache, computing the result if it isn't
* found.
*
* @param {string} k The key to look up in the cache.
* @return {T} The value associated with k; the result of compute(k).
*/
Cache.prototype.get = function (k) {
if (!this.cache.has(k)) {
this.cache.set(k, this.compute(k));
}
return this.cache.get(k);
};
/**
* Reset the cache empty.
*
* @return {Cache<T>} The calling Cache.
*/
Cache.prototype.clear = function () {
this.cache = d3.map();
return this;
};
return Cache;
}());
exports.Cache = Cache;
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}],13:[function(require,module,exports){
(function (global){
/**
* Copyright 2017-present Palantir Technologies, Inc. All rights reserved.
* Licensed under the MIT License (the "License"); you may obtain a copy of the
* license at https://github.com/palantir/svg-typewriter/blob/develop/LICENSE
*/
"use strict";
var d3 = (typeof window !== "undefined" ? window['d3'] : typeof global !== "undefined" ? global['d3'] : null);
var DOM = (function () {
function DOM() {
}
DOM.transform = function (s, x, y) {
var xform = d3.transform(s.attr("transform"));
if (x == null) {
return xform.translate;
}
else {
y = (y == null) ? 0 : y;
xform.translate[0] = x;
xform.translate[1] = y;
s.attr("transform", xform.toString());
return s;
}
};
DOM.getBBox = function (element) {
var bbox;
try {
bbox = element.node().getBBox();
}
catch (err) {
bbox = {
height: 0,
width: 0,
x: 0,
y: 0,
};
}
return bbox;
};
return DOM;
}());
exports.DOM = DOM;
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}],14:[function(require,module,exports){
/**
* Copyright 2017-present Palantir Technologies, Inc. All rights reserved.
* Licensed under the MIT License (the "License"); you may obtain a copy of the
* license at https://github.com/palantir/svg-typewriter/blob/develop/LICENSE
*/
"use strict";
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
__export(require("./cache"));
__export(require("./dom"));
__export(require("./methods"));
__export(require("./stringMethods"));
__export(require("./tokenizer"));
},{"./cache":12,"./dom":13,"./methods":15,"./stringMethods":16,"./tokenizer":17}],15:[function(require,module,exports){
/**
* Copyright 2017-present Palantir Technologies, Inc. All rights reserved.
* Licensed under the MIT License (the "License"); you may obtain a copy of the
* license at https://github.com/palantir/svg-typewriter/blob/develop/LICENSE
*/
"use strict";
var Methods = (function () {
function Methods() {
}
/**
* Check if two arrays are equal by strict equality.
*/
Methods.arrayEq = function (a, b) {
// Technically, null and undefined are arrays too
if (a == null || b == null) {
return a === b;
}
if (a.length !== b.length) {
return false;
}
for (var i = 0; i < a.length; i++) {
if (a[i] !== b[i]) {
return false;
}
}
return true;
};
/**
* @param {any} a Object to check against b for equality.
* @param {any} b Object to check against a for equality.
*
* @returns {boolean} whether or not two objects share the same keys, and
* values associated with those keys. Values will be compared
* with ===.
*/
Methods.objEq = function (a, b) {
if (a == null || b == null) {
return a === b;
}
var keysA = Object.keys(a).sort();
var keysB = Object.keys(b).sort();
var valuesA = keysA.map(function (k) { return a[k]; });
var valuesB = keysB.map(function (k) { return b[k]; });
return Methods.arrayEq(keysA, keysB) && Methods.arrayEq(valuesA, valuesB);
};
Methods.strictEq = function (a, b) {
return a === b;
};
return Methods;
}());
exports.Methods = Methods;
},{}],16:[function(require,module,exports){
/**
* Copyright 2017-present Palantir Technologies, Inc. All rights reserved.
* Licensed under the MIT License (the "License"); you may obtain a copy of the
* license at https://github.com/palantir/svg-typewriter/blob/develop/LICENSE
*/
"use strict";
var StringMethods = (function () {
function StringMethods() {
}
/**
* Treat all sequences of consecutive spaces as a single " ".
*/
StringMethods.combineWhitespace = function (str) {
return str.replace(/[ \t]+/g, " ");
};
StringMethods.isNotEmptyString = function (str) {
return str && str.trim() !== "";
};
StringMethods.trimStart = function (str, splitter) {
if (!str) {
return str;
}
var chars = str.split("");
var reduceFunction = splitter ? function (s) { return s.split(splitter).some(StringMethods.isNotEmptyString); }
: StringMethods.isNotEmptyString;
return chars.reduce(function (s, c) { return reduceFunction(s + c) ? s + c : s; }, "");
};
StringMethods.trimEnd = function (str, c) {
if (!str) {
return str;
}
var reversedChars = str.split("");
reversedChars.reverse();
reversedChars = StringMethods.trimStart(reversedChars.join(""), c).split("");
reversedChars.reverse();
return reversedChars.join("");
};
return StringMethods;
}());
exports.StringMethods = StringMethods;
},{}],17:[function(require,module,exports){
/**
* Copyright 2017-present Palantir Technologies, Inc. All rights reserved.
* Licensed under the MIT License (the "License"); you may obtain a copy of the
* license at https://github.com/palantir/svg-typewriter/blob/develop/LICENSE
*/
"use strict";
var Tokenizer = (function () {
function Tokenizer() {
this.WordDividerRegExp = new RegExp("\\W");
this.WhitespaceRegExp = new RegExp("\\s");
}
Tokenizer.prototype.tokenize = function (line) {
var _this = this;
return line.split("").reduce(function (tokens, c) {
return tokens.slice(0, -1).concat(_this.shouldCreateNewToken(tokens[tokens.length - 1], c));
}, [""]);
};
Tokenizer.prototype.shouldCreateNewToken = function (token, newCharacter) {
if (!token) {
return [newCharacter];
}
var lastCharacter = token[token.length - 1];
if (this.WhitespaceRegExp.test(lastCharacter) && this.WhitespaceRegExp.test(newCharacter)) {
return [token + newCharacter];
}
else if (this.WhitespaceRegExp.test(lastCharacter) || this.WhitespaceRegExp.test(newCharacter)) {
return [token, newCharacter];
}
else if (!(this.WordDividerRegExp.test(lastCharacter) || this.WordDividerRegExp.test(newCharacter))) {
return [token + newCharacter];
}
else if (lastCharacter === newCharacter) {
return [token + newCharacter];
}
else {
return [token, newCharacter];
}
};
return Tokenizer;
}());
exports.Tokenizer = Tokenizer;
},{}],18:[function(require,module,exports){
/**
* Copyright 2017-present Palantir Technologies, Inc. All rights reserved.
* Licensed under the MIT License (the "License"); you may obtain a copy of the
* license at https://github.com/palantir/svg-typewriter/blob/develop/LICENSE
*/
"use strict";
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
__export(require("./singleLineWrapper"));
__export(require("./wrapper"));
},{"./singleLineWrapper":19,"./wrapper":20}],19:[function(require,module,exports){
/**
* Copyright 2017-present Palantir Technologies, Inc. All rights reserved.
* Licensed under the MIT License (the "License"); you may obtain a copy of the
* license at https://github.com/palantir/svg-typewriter/blob/develop/LICENSE
*/
"use strict";
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var wrapper_1 = require("./wrapper");
var SingleLineWrapper = (function (_super) {
__extends(SingleLineWrapper, _super);
function SingleLineWrapper() {
return _super.apply(this, arguments) || this;
}
SingleLineWrapper.prototype.wrap = function (text, measurer, width, height) {
var _this = this;
if (height === void 0) { height = Infinity; }
var lines = text.split("\n");
if (lines.length > 1) {
throw new Error("SingleLineWrapper is designed to work only on single line");
}
var wrapFN = function (w) { return _super.prototype.wrap.call(_this, text, measurer, w, height); };
var result = wrapFN(width);
if (result.noLines < 2) {
return result;
}
var left = 0;
var right = width;
for (var i = 0; i < SingleLineWrapper.NO_WRAP_ITERATIONS && right > left; ++i) {
var currentWidth = (right + left) / 2;
var currentResult = wrapFN(currentWidth);
if (this.areSameResults(result, currentResult)) {
right = currentWidth;
result = currentResult;
}
else {
left = currentWidth;
}
}
return result;
};
SingleLineWrapper.prototype.areSameResults = function (one, two) {
return one.noLines === two.noLines && one.truncatedText === two.truncatedText;
};
return SingleLineWrapper;
}(wrapper_1.Wrapper));
SingleLineWrapper.NO_WRAP_ITERATIONS = 5;
exports.SingleLineWrapper = SingleLineWrapper;
},{"./wrapper":20}],20:[function(require,module,exports){
/**
* Copyright 2017-present Palantir Technologies, Inc. All rights reserved.
* Licensed under the MIT License (the "License"); you may obtain a copy of the
* license at https://github.com/palantir/svg-typewriter/blob/develop/LICENSE
*/
"use strict";
var Utils = require("../utils");
var Wrapper = (function () {
function Wrapper() {
this.maxLines(Infinity);
this.textTrimming("ellipsis");
this.allowBreakingWords(true);
this._tokenizer = new Utils.Tokenizer();
this._breakingCharacter = "-";
}
Wrapper.prototype.maxLines = function (noLines) {
if (noLines == null) {
return this._maxLines;
}
else {
this._maxLines = noLines;
return this;
}
};
Wrapper.prototype.textTrimming = function (option) {
if (option == null) {
return this._textTrimming;
}
else {
if (option !== "ellipsis" && option !== "none") {
throw new Error(option + " - unsupported text trimming option.");
}
this._textTrimming = option;
return this;
}
};
Wrapper.prototype.allowBreakingWords = function (allow) {
if (allow == null) {
return this._allowBreakingWords;
}
else {
this._allowBreakingWords = allow;
return this;
}
};
Wrapper.prototype.wrap = function (text, measurer, width, height) {
var _this = this;
if (height === void 0) { height = Infinity; }
var initialWrappingResult = {
noBrokeWords: 0,
noLines: 0,
originalText: text,
truncatedText: "",
wrappedText: "",
};
var state = {
availableLines: Math.min(Math.floor(height / measurer.measure().height), this._maxLines),
availableWidth: width,
canFitText: true,
currentLine: "",
wrapping: initialWrappingResult,
};
var lines = text.split("\n");
return lines.reduce(function (s, line, i) {
return _this.breakLineToFitWidth(s, line, i !== lines.length - 1, measurer);
}, state).wrapping;
};
Wrapper.prototype.breakLineToFitWidth = function (state, line, hasNextLine, measurer) {
var _this = this;
if (!state.canFitText && state.wrapping.truncatedText !== "") {
state.wrapping.truncatedText += "\n";
}
var tokens = this._tokenizer.tokenize(line);
state = tokens.reduce(function (s, token) {
return _this.wrapNextToken(token, s, measurer);
}, state);
var wrappedText = Utils.StringMethods.trimEnd(state.currentLine);
state.wrapping.noLines += +(wrappedText !== "");
if (state.wrapping.noLines === state.availableLines && this._textTrimming !== "none" && hasNextLine) {
var ellipsisResult = this.addEllipsis(wrappedText, state.availableWidth, measurer);
state.wrapping.wrappedText += ellipsisResult.wrappedToken;
state.wrapping.truncatedText += ellipsisResult.remainingToken;
state.canFitText = false;
}
else {
state.wrapping.wrappedText += wrappedText;
}
state.currentLine = "\n";
return state;
};
Wrapper.prototype.canFitToken = function (token, width, measurer) {
var _this = this;
var possibleBreaks = this._allowBreakingWords ?
token.split("").map(function (c, i) { return (i !== token.length - 1) ? c + _this._breakingCharacter : c; })
: [token];
return (measurer.measure(token).width <= width) || possibleBreaks.every(function (c) { return measurer.measure(c).width <= width; });
};
Wrapper.prototype.addEllipsis = function (line, width, measurer) {
if (this._textTrimming === "none") {
return {
remainingToken: "",
wrappedToken: line,
};
}
var truncatedLine = line.substring(0).trim();
var lineWidth = measurer.measure(truncatedLine).width;
var ellipsesWidth = measurer.measure("...").width;
var prefix = (line.length > 0 && line[0] === "\n") ? "\n" : "";
if (width <= ellipsesWidth) {
var periodWidth = ellipsesWidth / 3;
var numPeriodsThatFit = Math.floor(width / periodWidth);
return {
remainingToken: line,
wrappedToken: prefix + "...".substr(0, numPeriodsThatFit),
};
}
while (lineWidth + ellipsesWidth > width) {
truncatedLine = Utils.StringMethods.trimEnd(truncatedLine.substr(0, truncatedLine.length - 1));
lineWidth = measurer.measure(truncatedLine).width;
}
return {
remainingToken: Utils.StringMethods.trimEnd(line.substring(truncatedLine.length), "-").trim(),
wrappedToken: prefix + truncatedLine + "...",
};
};
Wrapper.prototype.wrapNextToken = function (token, state, measurer) {
if (!state.canFitText ||
state.availableLines === state.wrapping.noLines ||
!this.canFitToken(token, state.availableWidth, measurer)) {
return this.finishWrapping(token, state, measurer);
}
var remainingToken = token;
while (remainingToken) {
var result = this.breakTokenToFitInWidth(remainingToken, state.currentLine, state.availableWidth, measurer);
state.currentLine = result.line;
remainingToken = result.remainingToken;
if (remainingToken != null) {
state.wrapping.noBrokeWords += +result.breakWord;
++state.wrapping.noLines;
if (state.availableLines === state.wrapping.noLines) {
var ellipsisResult = this.addEllipsis(state.currentLine, state.availableWidth, measurer);
state.wrapping.wrappedText += ellipsisResult.wrappedToken;
state.wrapping.truncatedText += ellipsisResult.remainingToken + remainingToken;
state.currentLine = "\n";
return state;
}
else {
state.wrapping.wrappedText += Utils.StringMethods.trimEnd(state.currentLine);
state.currentLine = "\n";
}
}
}
return state;
};
Wrapper.prototype.finishWrapping = function (token, state, measurer) {
// Token is really long, but we have a space to put part of the word.
if (state.canFitText &&
state.availableLines !== state.wrapping.noLines &&
this._allowBreakingWords &&
this._textTrimming !== "none") {
var res = this.addEllipsis(state.currentLine + token, state.availableWidth, measurer);
state.wrapping.wrappedText += res.wrappedToken;
state.wrapping.truncatedText += res.remainingToken;
state.wrapping.noBrokeWords += +(res.remainingToken.length < token.length);
state.wrapping.noLines += +(res.wrappedToken.length > 0);
state.currentLine = "";
}
else {
state.wrapping.truncatedText += token;
}
state.canFitText = false;
return state;
};
/**
* Breaks single token to fit current line.
* If token contains only whitespaces then they will not be populated to next line.
*/
Wrapper.prototype.breakTokenToFitInWidth = function (token, line, availableWidth, measurer, breakingCharacter) {
if (breakingCharacter === void 0) { breakingCharacter = this._breakingCharacter; }
if (measurer.measure(line + token).width <= availableWidth) {
return {
breakWord: false,
line: line + token,
remainingToken: null,
};
}
if (token.trim() === "") {
return {
breakWord: false,
line: line,
remainingToken: "",
};
}
if (!this._allowBreakingWords) {
return {
breakWord: false,
line: line,
remainingToken: token,
};
}
var fitTokenLength = 0;
while (fitTokenLength < token.length) {
if (measurer.measure(line + token.substring(0, fitTokenLength + 1) + breakingCharacter).width <= availableWidth) {
++fitTokenLength;
}
else {
break;
}
}
var suffix = "";
if (fitTokenLength > 0) {
suffix = breakingCharacter;
}
return {
breakWord: fitTokenLength > 0,
line: line + token.substring(0, fitTokenLength) + suffix,
remainingToken: token.substring(fitTokenLength),
};
};
return Wrapper;
}());
exports.Wrapper = Wrapper;
},{"../utils":14}],21:[function(require,module,exports){
/**
* Copyright 2017-present Palantir Technologies, Inc. All rights reserved.
* Licensed under the MIT License (the "License"); you may obtain a copy of the
* license at https://github.com/palantir/svg-typewriter/blob/develop/LICENSE
*/
"use strict";
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
__export(require("./writer"));
},{"./writer":22}],22:[function(require,module,exports){
(function (global){
/**
* Copyright 2017-present Palantir Technologies, Inc. All rights reserved.
* Licensed under the MIT License (the "License"); you may obtain a copy of the
* license at https://github.com/palantir/svg-typewriter/blob/develop/LICENSE
*/
"use strict";
var d3 = (typeof window !== "undefined" ? window['d3'] : typeof global !== "undefined" ? global['d3'] : null);
var Utils = require("../utils");
var Writer = (function () {
function Writer(measurer, wrapper) {
this._writerID = Writer.nextID++;
this._elementID = 0;
this.measurer(measurer);
if (wrapper) {
this.wrapper(wrapper);
}
this.addTitleElement(false);
}
Writer.prototype.measurer = function (newMeasurer) {
this._measurer = newMeasurer;
return this;
};
Writer.prototype.wrapper = function (newWrapper) {
this._wrapper = newWrapper;
return this;
};
Writer.prototype.addTitleElement = function (add) {
this._addTitleElement = add;
return this;
};
Writer.prototype.write = function (text, width, height, options) {
if (Writer.SupportedRotation.indexOf(options.textRotation) === -1) {
throw new Error("unsupported rotation - " + options.textRotation);
}
var orientHorizontally = Math.abs(Math.abs(options.textRotation) - 90) > 45;
var primaryDimension = orientHorizontally ? width : height;
var secondaryDimension = orientHorizontally ? height : width;
var textContainer = options.selection.append("g").classed("text-container", true);
if (this._addTitleElement) {
textContainer.append("title").text(text);
}
var normalizedText = Utils.StringMethods.combineWhitespace(text);
var textArea = textContainer.append("g").classed("text-area", true);
var wrappedText = this._wrapper ?
this._wrapper.wrap(normalizedText, this._measurer, primaryDimension, secondaryDimension).wrappedText : normalizedText;
this.writeText(wrappedText, textArea, primaryDimension, secondaryDimension, options.xAlign, options.yAlign);
var xForm = d3.transform("");
var xForm2 = d3.transform("");
xForm.rotate = options.textRotation;
switch (options.textRotation) {
case 90:
xForm.translate = [width, 0];
xForm2.rotate = -90;
xForm2.translate = [0, 200];
break;
case -90:
xForm.translate = [0, height];
xForm2.rotate = 90;
xForm2.translate = [width, 0];
break;
case 180:
xForm.translate = [width, height];
xForm2.translate = [width, height];
xForm2.rotate = 180;
break;
default:
break;
}
textArea.attr("transform", xForm.toString());
this.addClipPath(textContainer, xForm2);
if (options.animator) {
options.animator.animate(textContainer);
}
};
Writer.prototype.writeLine = function (line, g, width, xAlign, yOffset) {
var textEl = g.append("text");
textEl.text(line);
var xOffset = width * Writer.XOffsetFactor[xAlign];
var anchor = Writer.AnchorConverter[xAlign];
textEl.attr("text-anchor", anchor).classed("text-line", true);
Utils.DOM.transform(textEl, xOffset, yOffset).attr("y", "-0.25em");
};
Writer.prototype.writeText = function (text, writingArea, width, height, xAlign, yAlign) {
var _this = this;
var lines = text.split("\n");
var lineHeight = this._measurer.measure().height;
var yOffset = Writer.YOffsetFactor[yAlign] * (height - lines.length * lineHeight);
lines.forEach(function (line, i) {
_this.writeLine(line, writingArea, width, xAlign, (i + 1) * lineHeight + yOffset);
});
};
Writer.prototype.addClipPath = function (selection, _transform) {
var elementID = this._elementID++;
var prefix = /MSIE [5-9]/.test(navigator.userAgent) ? "" : document.location.href;
prefix = prefix.split("#")[0]; // To fix cases where an anchor tag was used
var clipPathID = "clipPath" + this._writerID + "_" + elementID;
selection.select(".text-area").attr("clip-path", "url(\"" + prefix + "#" + clipPathID + "\")");
var clipPathParent = selection.append("clipPath").attr("id", clipPathID);
var bboxAttrs = Utils.DOM.getBBox(selection.select(".text-area"));
var box = clipPathParent.append("rect");
box.classed("clip-rect", true).attr({
height: bboxAttrs.height,
width: bboxAttrs.width,
x: bboxAttrs.x,
y: bboxAttrs.y,
});
};
return Writer;
}());
Writer.nextID = 0;
Writer.SupportedRotation = [-90, 0, 180, 90];
Writer.AnchorConverter = {
center: "middle",
left: "start",
right: "end",
};
Writer.XOffsetFactor = {
center: 0.5,
left: 0,
right: 1,
};
Writer.YOffsetFactor = {
bottom: 1,
center: 0.5,
top: 0,
};
exports.Writer = Writer;
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"../utils":14}]},{},[5])(5)
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment