diff --git a/Gruntfile.js b/Gruntfile.js index 3c20186122..278e1bdc4c 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -180,7 +180,9 @@ module.exports = function(grunt) { options: { configuration: grunt.file.readJSON("tslint.json") }, - files: ["src/**/*.ts", "test/**/*.ts"] + all: { + src: ["src/**/*.ts", "test/**/*.ts"] + } }, jshint: { files: ['Gruntfile.js', 'quicktests/**/*.js'], @@ -205,6 +207,11 @@ module.exports = function(grunt) { "eqnull": true } }, + parallelize: { + tslint: { + all: 4 + } + }, watch: { "options": { livereload: true @@ -338,7 +345,7 @@ module.exports = function(grunt) { grunt.registerTask("dist-compile", [ "dev-compile", "blanket_mocha", - "tslint", + "parallelize:tslint", "ts:verify_d_ts", "uglify", "compress" @@ -348,7 +355,7 @@ module.exports = function(grunt) { grunt.registerTask("launch", ["connect", "dev-compile", "watch"]); grunt.registerTask("test-sauce", ["connect", "saucelabs-mocha"]); - grunt.registerTask("test", ["dev-compile", "blanket_mocha", "tslint", "jshint", "ts:verify_d_ts"]); + grunt.registerTask("test", ["dev-compile", "blanket_mocha", "parallelize:tslint", "jshint", "ts:verify_d_ts"]); // Disable saucelabs for external pull requests. Check if we can see the SAUCE_USERNAME var travisTests = ["test"]; if (process.env.SAUCE_USERNAME) { diff --git a/bower.json b/bower.json index 5c7c7108f7..c87704538c 100644 --- a/bower.json +++ b/bower.json @@ -1,7 +1,7 @@ { "name": "plottable", "description": "A library for creating charts out of D3", - "version": "0.33.1", + "version": "0.34.0", "main": ["plottable.js", "plottable.css"], "license": "MIT", "ignore": [ diff --git a/package.json b/package.json index 69c554ac4c..fb5215585b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "plottable.js", "description": "A library for creating charts out of D3", - "version": "0.33.1", + "version": "0.34.0", "repository": { "type": "git", "url": "https://github.com/palantir/plottable.git" @@ -30,6 +30,7 @@ "grunt-contrib-compress": "~0.9.1", "grunt-contrib-uglify": "~0.4.0", "grunt-shell": "0.7.0", - "grunt-contrib-jshint": "^0.10.0" + "grunt-contrib-jshint": "^0.10.0", + "grunt-parallelize": "1.0.0" } } diff --git a/plottable-dev.d.ts b/plottable-dev.d.ts new file mode 100644 index 0000000000..aa0bc139e6 --- /dev/null +++ b/plottable-dev.d.ts @@ -0,0 +1,3609 @@ + +declare module Plottable { + module _Util { + module Methods { + /** + * 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: number, a: number, b: number): boolean; + /** Print a warning message to the console, if it is available. + * + * @param {string} The warnings to print + */ + function warn(warning: string): void; + /** + * 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 addArrays(alist: number[], blist: number[]): number[]; + /** + * Takes two sets and returns the intersection + * + * Due to the fact that D3.Sets store strings internally, return type is always a string set + * + * @param {D3.Set} set1 The first set + * @param {D3.Set} set2 The second set + * @return {D3.Set} A set that contains elements that appear in both set1 and set2 + */ + function intersection(set1: D3.Set, set2: D3.Set): D3.Set; + /** + * Take an accessor object (may be a string to be made into a key, or a value, or a color code) + * and "activate" it by turning it into a function in (datum, index, metadata) + */ + function accessorize(accessor: any): _IAccessor; + /** + * Takes two sets and returns the union + * + * Due to the fact that D3.Sets store strings internally, return type is always a string set + * + * @param {D3.Set} set1 The first set + * @param {D3.Set} set2 The second set + * @return {D3.Set} A set that contains elements that appear in either set1 or set2 + */ + function union(set1: D3.Set, set2: D3.Set): D3.Set; + /** + * Populates a map from an array of keys and a transformation function. + * + * @param {string[]} keys The array of keys. + * @param {(string) => T} transform A transformation function to apply to the keys. + * @return {D3.Map} A map mapping keys to their transformed values. + */ + function populateMap(keys: string[], transform: (key: string) => T): D3.Map; + /** + * Take an accessor object, activate it, and partially apply it to a Plot's datasource's metadata + */ + function _applyAccessor(accessor: _IAccessor, plot: Abstract.Plot): (d: any, i: number) => any; + /** + * 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: T[]): T[]; + /** + * Creates an array of length `count`, filled with value or (if value is a function), value() + * + * @param {any} value The value to fill the array with, or, if a function, a generator for values (called with index as arg) + * @param {number} count The length of the array to generate + * @return {any[]} + */ + function createFilledArray(value: T, count: number): T[]; + function createFilledArray(func: (index?: number) => T, count: number): T[]; + /** + * @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: T[][]): T[]; + /** + * Check if two arrays are equal by strict equality. + */ + function arrayEq(a: T[], b: T[]): boolean; + /** + * @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 ===. + */ + function objEq(a: any, b: any): boolean; + function max(arr: number[], default_val?: number): number; + function max(arr: T[], acc: (x: T) => number, default_val?: number): number; + function min(arr: number[], default_val?: number): number; + function min(arr: T[], acc: (x: T) => number, default_val?: number): number; + } + } +} + + +declare module Plottable { + module _Util { + module OpenSource { + /** + * Returns the sortedIndex for inserting a value into an array. + * Takes a number and an array of numbers OR an array of objects and an accessor that returns a number. + * @param {number} value: The numerical value to insert + * @param {any[]} arr: Array to find insertion index, can be number[] or any[] (if accessor provided) + * @param {_IAccessor} accessor: If provided, this function is called on members of arr to determine insertion index + * @returns {number} The insertion index. + * The behavior is undefined for arrays that are unsorted + * If there are multiple valid insertion indices that maintain sorted order (e.g. addign 1 to [1,1,1,1,1]) then + * the behavior must satisfy that the array is sorted post-insertion, but is otherwise unspecified. + * This is a modified version of Underscore.js's implementation of sortedIndex. + * Underscore.js is released under the MIT License: + * Copyright (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative + * Reporters & Editors + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + function sortedIndex(val: number, arr: number[]): number; + function sortedIndex(val: number, arr: any[], accessor: _IAccessor): number; + } + } +} + + +declare module Plottable { + module _Util { + class IDCounter { + increment(id: any): number; + decrement(id: any): number; + get(id: any): number; + } + } +} + + +declare module Plottable { + module _Util { + /** + * An associative array that can be keyed by anything (inc objects). + * Uses pointer equality checks which is why this works. + * This power has a price: everything is linear time since it is actually backed by an array... + */ + class StrictEqualityAssociativeArray { + /** + * Set a new key/value pair in the store. + * + * @param {any} key Key to set in the store + * @param {any} value Value to set in the store + * @return {boolean} True if key already in store, false otherwise + */ + set(key: any, value: any): boolean; + /** + * Get a value from the store, given a key. + * + * @param {any} key Key associated with value to retrieve + * @return {any} Value if found, undefined otherwise + */ + get(key: any): any; + /** + * Test whether store has a value associated with given key. + * + * Will return true if there is a key/value entry, + * even if the value is explicitly `undefined`. + * + * @param {any} key Key to test for presence of an entry + * @return {boolean} Whether there was a matching entry for that key + */ + has(key: any): boolean; + /** + * Return an array of the values in the key-value store + * + * @return {any[]} The values in the store + */ + values(): any[]; + /** + * Return an array of keys in the key-value store + * + * @return {any[]} The keys in the store + */ + keys(): any[]; + /** + * Execute a callback for each entry in the array. + * + * @param {(key: any, val?: any, index?: number) => any} callback The callback to eecute + * @return {any[]} The results of mapping the callback over the entries + */ + map(cb: (key?: any, val?: any, index?: number) => any): any[]; + /** + * Delete a key from the key-value store. Return whether the key was present. + * + * @param {any} The key to remove + * @return {boolean} Whether a matching entry was found and removed + */ + delete(key: any): boolean; + } + } +} + + +declare module Plottable { + module _Util { + class Cache { + /** + * @constructor + * + * @param {string} compute The function whose results will be cached. + * @param {string} [canonicalKey] If present, when clear() is called, + * this key will be re-computed. If its result hasn't been changed, + * the cache will not be cleared. + * @param {(v: T, w: T) => boolean} [valueEq] + * Used to determine if the value of canonicalKey has changed. + * If omitted, defaults to === comparision. + */ + constructor(compute: (k: string) => T, canonicalKey?: string, valueEq?: (v: T, w: T) => boolean); + /** + * 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). + */ + get(k: string): T; + /** + * Reset the cache empty. + * + * If canonicalKey was provided at construction, compute(canonicalKey) + * will be re-run. If the result matches what is already in the cache, + * it will not clear the cache. + * + * @return {Cache} The calling Cache. + */ + clear(): Cache; + } + } +} + + +declare module Plottable { + module _Util { + module Text { + var HEIGHT_TEXT: string; + interface Dimensions { + width: number; + height: number; + } + interface TextMeasurer { + (s: string): Dimensions; + } + /** + * Returns a quasi-pure function of typesignature (t: string) => Dimensions which measures height and width of text + * in the given text selection + * + * @param {D3.Selection} selection: A temporary text selection that the string will be placed into for measurement. + * Will be removed on function creation and appended only for measurement. + * @returns {Dimensions} width and height of the text + */ + function getTextMeasurer(selection: D3.Selection): TextMeasurer; + /** + * This class will measure text by measuring each character individually, + * then adding up the dimensions. It will also cache the dimensions of each + * letter. + */ + class CachingCharacterMeasurer { + /** + * @param {string} s The string to be measured. + * @return {Dimensions} The width and height of the measured text. + */ + measure: TextMeasurer; + /** + * @param {D3.Selection} textSelection The element that will have text inserted into + * it in order to measure text. The styles present for text in + * this element will to the text being measured. + */ + constructor(textSelection: D3.Selection); + /** + * Clear the cache, if it seems that the text has changed size. + */ + clear(): CachingCharacterMeasurer; + } + /** + * Gets a truncated version of a sting that fits in the available space, given the element in which to draw the text + * + * @param {string} text: The string to be truncated + * @param {number} availableWidth: The available width, in pixels + * @param {D3.Selection} element: The text element used to measure the text + * @returns {string} text - the shortened text + */ + function getTruncatedText(text: string, availableWidth: number, measurer: TextMeasurer): string; + /** + * Takes a line, a width to fit it in, and a text measurer. Will attempt to add ellipses to the end of the line, + * shortening the line as required to ensure that it fits within width. + */ + function addEllipsesToLine(line: string, width: number, measureText: TextMeasurer): string; + function writeLineHorizontally(line: string, g: D3.Selection, width: number, height: number, xAlign?: string, yAlign?: string): { + width: number; + height: number; + }; + function writeLineVertically(line: string, g: D3.Selection, width: number, height: number, xAlign?: string, yAlign?: string, rotation?: string): { + width: number; + height: number; + }; + interface IWriteTextResult { + textFits: boolean; + usedWidth: number; + usedHeight: number; + } + interface IWriteOptions { + g: D3.Selection; + xAlign: string; + yAlign: string; + } + /** + * @param {write} [IWriteOptions] If supplied, the text will be written + * To the given g. Will align the text vertically if it seems like + * that is appropriate. + * Returns an IWriteTextResult with info on whether the text fit, and how much width/height was used. + */ + function writeText(text: string, width: number, height: number, tm: TextMeasurer, horizontally?: boolean, write?: IWriteOptions): IWriteTextResult; + } + } +} + + +declare module Plottable { + module _Util { + module WordWrap { + interface IWrappedText { + originalText: string; + lines: string[]; + textFits: boolean; + } + /** + * Takes a block of text, a width and height to fit it in, and a 2-d text measurement function. + * Wraps words and fits as much of the text as possible into the given width and height. + */ + function breakTextToFitRect(text: string, width: number, height: number, measureText: Text.TextMeasurer): IWrappedText; + /** + * Determines if it is possible to fit a given text within width without breaking any of the words. + * Simple algorithm, split the text up into tokens, and make sure that the widest token doesn't exceed + * allowed width. + */ + function canWrapWithoutBreakingWords(text: string, width: number, widthMeasure: (s: string) => number): boolean; + } + } +} + +declare module Plottable { + module _Util { + module DOM { + /** + * Gets the bounding box of an element. + * @param {D3.Selection} element + * @returns {SVGRed} The bounding box. + */ + function getBBox(element: D3.Selection): SVGRect; + var POLYFILL_TIMEOUT_MSEC: number; + function requestAnimationFramePolyfill(fn: () => any): void; + function isSelectionRemovedFromSVG(selection: D3.Selection): boolean; + function getElementWidth(elem: HTMLScriptElement): number; + function getElementHeight(elem: HTMLScriptElement): number; + function getSVGPixelWidth(svg: D3.Selection): number; + function translate(s: D3.Selection, x?: number, y?: number): any; + function boxesOverlap(boxA: ClientRect, boxB: ClientRect): boolean; + } + } +} + + +declare module Plottable { + interface Formatter { + (d: any): string; + } + var MILLISECONDS_IN_ONE_DAY: number; + module 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). + * @param {boolean} [onlyShowUnchanged] Whether to return a value if value changes after formatting (default true). + * + * @returns {Formatter} A formatter for currency values. + */ + function currency(precision?: number, symbol?: string, prefix?: boolean, onlyShowUnchanged?: boolean): (d: any) => string; + /** + * Creates a formatter that displays exactly [precision] decimal places. + * + * @param {number} [precision] The number of decimal places to show (default 3). + * @param {boolean} [onlyShowUnchanged] Whether to return a value if value changes after formatting (default true). + * + * @returns {Formatter} A formatter that displays exactly [precision] decimal places. + */ + function fixed(precision?: number, onlyShowUnchanged?: boolean): (d: any) => string; + /** + * Creates a formatter that formats numbers to show no more than + * [precision] decimal places. All other values are stringified. + * + * @param {number} [precision] The number of decimal places to show (default 3). + * @param {boolean} [onlyShowUnchanged] Whether to return a value if value changes after formatting (default true). + * + * @returns {Formatter} A formatter for general values. + */ + function general(precision?: number, onlyShowUnchanged?: boolean): (d: any) => string; + /** + * Creates a formatter that stringifies its input. + * + * @returns {Formatter} A formatter that stringifies its input. + */ + function identity(): (d: any) => string; + /** + * 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). + * @param {boolean} [onlyShowUnchanged] Whether to return a value if value changes after formatting (default true). + * + * @returns {Formatter} A formatter for percentage values. + */ + function percentage(precision?: number, onlyShowUnchanged?: boolean): (d: any) => string; + /** + * Creates a formatter for values that displays [precision] significant figures + * and puts SI notation. + * + * @param {number} [precision] The number of significant figures to show (default 3). + * + * @returns {Formatter} A formatter for SI values. + */ + function siSuffix(precision?: number): (d: any) => string; + /** + * Creates a formatter that displays dates. + * + * @returns {Formatter} A formatter for time/date values. + */ + function time(): (d: any) => string; + /** + * Creates a formatter for relative dates. + * + * @param {number} baseValue The start date (as epoch time) used in computing relative dates (default 0) + * @param {number} increment The unit used in calculating relative date values (default MILLISECONDS_IN_ONE_DAY) + * @param {string} label The label to append to the formatted string (default "") + * + * @returns {Formatter} A formatter for time/date values. + */ + function relativeDate(baseValue?: number, increment?: number, label?: string): (d: any) => string; + } +} + + +declare module Plottable { + module TickGenerators { + /** + * Creates a tick generator using specific interval. + * + * It generates ticks at multiples of the interval including the domain boundaries + * + * @param {number} [interval] The interval between two ticks. + * + * @returns {TickGenerator} A tick generator using specific interval. + */ + function intervalTickGenerator(interval: number): (s: Abstract.QuantitativeScale) => number[]; + } +} + + +declare module Plottable { + var version: string; +} + + +declare module Plottable { + module Core { + /** + * Colors we use as defaults on a number of graphs. + */ + class Colors { + static CORAL_RED: string; + static INDIGO: string; + static ROBINS_EGG_BLUE: string; + static FERN: string; + static BURNING_ORANGE: string; + static ROYAL_HEATH: string; + static CONIFER: string; + static CERISE_RED: string; + static BRIGHT_SUN: string; + static JACARTA: string; + static PLOTTABLE_COLORS: string[]; + } + } +} + + +declare module Plottable { + module Abstract { + /** + * A class most other Plottable classes inherit from, in order to have a + * unique ID. + */ + class PlottableObject { + _plottableID: number; + } + } +} + + +declare module Plottable { + module Core { + /** + * This interface represents anything in Plottable which can have a listener attached. + * Listeners attach by referencing the Listenable's broadcaster, and calling registerListener + * on it. + * + * e.g.: + * listenable: Plottable.IListenable; + * listenable.broadcaster.registerListener(callbackToCallOnBroadcast) + */ + interface IListenable { + broadcaster: Broadcaster; + } + /** + * This interface represents the callback that should be passed to the Broadcaster on a Listenable. + * + * The callback will be called with the attached Listenable as the first object, and optional arguments + * as the subsequent arguments. + * + * The Listenable is passed as the first argument so that it is easy for the callback to reference the + * current state of the Listenable in the resolution logic. + */ + interface IBroadcasterCallback { + (listenable: IListenable, ...args: any[]): any; + } + /** + * The Broadcaster class is owned by an IListenable. Third parties can register and deregister listeners + * from the broadcaster. When the broadcaster.broadcast method is activated, all registered callbacks are + * called. The registered callbacks are called with the registered Listenable that the broadcaster is attached + * to, along with optional arguments passed to the `broadcast` method. + * + * The listeners are called synchronously. + */ + class Broadcaster extends Abstract.PlottableObject { + listenable: IListenable; + /** + * Constructs a broadcaster, taking the Listenable that the broadcaster will be attached to. + * + * @constructor + * @param {IListenable} listenable The Listenable-object that this broadcaster is attached to. + */ + constructor(listenable: IListenable); + /** + * Registers a callback to be called when the broadcast method is called. Also takes a key which + * is used to support deregistering the same callback later, by passing in the same key. + * If there is already a callback associated with that key, then the callback will be replaced. + * + * @param key The key associated with the callback. Key uniqueness is determined by deep equality. + * @param {IBroadcasterCallback} callback A callback to be called when the Scale's domain changes. + * @returns {Broadcaster} this object + */ + registerListener(key: any, callback: IBroadcasterCallback): Broadcaster; + /** + * Call all listening callbacks, optionally with arguments passed through. + * + * @param ...args A variable number of optional arguments + * @returns {Broadcaster} this object + */ + broadcast(...args: any[]): Broadcaster; + /** + * Deregisters the callback associated with a key. + * + * @param key The key to deregister. + * @returns {Broadcaster} this object + */ + deregisterListener(key: any): Broadcaster; + /** + * Deregisters all listeners and callbacks associated with the broadcaster. + * + * @returns {Broadcaster} this object + */ + deregisterAllListeners(): void; + } + } +} + + +declare module Plottable { + class Dataset extends Abstract.PlottableObject implements Core.IListenable { + broadcaster: any; + /** + * Constructs a new set. + * + * A Dataset is mostly just a wrapper around an any[], Dataset is the + * data you're going to plot. + * + * @constructor + * @param {any[]} data The data for this DataSource (default = []). + * @param {any} metadata An object containing additional information (default = {}). + */ + constructor(data?: any[], metadata?: any); + /** + * Gets the data. + * + * @returns {DataSource|any[]} The calling DataSource, or the current data. + */ + data(): any[]; + /** + * Sets the data. + * + * @param {any[]} data The new data. + * @returns {Dataset} The calling Dataset. + */ + data(data: any[]): Dataset; + /** + * Get the metadata. + * + * @returns {any} the current + * metadata. + */ + metadata(): any; + /** + * Set the metadata. + * + * @param {any} metadata The new metadata. + * @returns {Dataset} The calling Dataset. + */ + metadata(metadata: any): Dataset; + _getExtent(accessor: _IAccessor, typeCoercer: (d: any) => any): any[]; + } +} + + +declare module Plottable { + module Core { + module RenderController { + module RenderPolicy { + /** + * A policy to render components. + */ + interface IRenderPolicy { + render(): any; + } + /** + * Never queue anything, render everything immediately. Useful for + * debugging, horrible for performance. + */ + class Immediate implements IRenderPolicy { + render(): void; + } + /** + * The default way to render, which only tries to render every frame + * (usually, 1/60th of a second). + */ + class AnimationFrame implements IRenderPolicy { + render(): void; + } + /** + * Renders with `setTimeout`. This is generally an inferior way to render + * compared to `requestAnimationFrame`, but it's still there if you want + * it. + */ + class Timeout implements IRenderPolicy { + _timeoutMsec: number; + render(): void; + } + } + } + } +} + + +declare module Plottable { + module Core { + /** + * The RenderController is responsible for enqueueing and synchronizing + * layout and render calls for Plottable components. + * + * Layouts and renders occur inside an animation callback + * (window.requestAnimationFrame if available). + * + * If you require immediate rendering, call RenderController.flush() to + * perform enqueued layout and rendering serially. + * + * If you want to always have immediate rendering (useful for debugging), + * call + * ```typescript + * Plottable.Core.RenderController.setRenderPolicy( + * new Plottable.Core.RenderController.RenderPolicy.Immediate() + * ); + * ``` + */ + module RenderController { + var _renderPolicy: RenderPolicy.IRenderPolicy; + function setRenderPolicy(policy: string): void; + function setRenderPolicy(policy: RenderPolicy.IRenderPolicy): void; + /** + * If the RenderController is enabled, we enqueue the component for + * render. Otherwise, it is rendered immediately. + * + * @param {Abstract.Component} component Any Plottable component. + */ + function registerToRender(c: Abstract.Component): void; + /** + * If the RenderController is enabled, we enqueue the component for + * layout and render. Otherwise, it is rendered immediately. + * + * @param {Abstract.Component} component Any Plottable component. + */ + function registerToComputeLayout(c: Abstract.Component): void; + /** + * Render everything that is waiting to be rendered right now, instead of + * waiting until the next frame. + * + * Useful to call when debugging. + */ + function flush(): void; + } + } +} + + +declare module Plottable { + module Core { + /** + * The ResizeBroadcaster will broadcast a notification to any registered + * components when the window is resized. + * + * The broadcaster and single event listener are lazily constructed. + * + * Upon resize, the _resized flag will be set to true until after the next + * flush of the RenderController. This is used, for example, to disable + * animations during resize. + */ + module ResizeBroadcaster { + /** + * Checks if the window has been resized and the RenderController + * has not yet been flushed. + * + * @returns {boolean} If the window has been resized/RenderController + * has not yet been flushed. + */ + function resizing(): boolean; + /** + * Sets that it is not resizing anymore. Good if it stubbornly thinks + * it is still resizing, or for cancelling the effects of resizing + * prematurely. + */ + function clearResizing(): void; + /** + * Registers a component. + * + * When the window is resized, ._invalidateLayout() is invoked on the + * component, which will enqueue the component for layout and rendering + * with the RenderController. + * + * @param {Component} component Any Plottable component. + */ + function register(c: Abstract.Component): void; + /** + * Deregisters the components. + * + * The component will no longer receive updates on window resize. + * + * @param {Component} component Any Plottable component. + */ + function deregister(c: Abstract.Component): void; + } + } +} + +declare module Plottable { + interface IDataset { + data: any[]; + metadata: IMetadata; + } + interface IMetadata { + cssClass?: string; + color?: string; + } + interface _IAccessor { + (datum: any, index?: number, metadata?: any): any; + } + /** + * A function to map across the data in a DataSource. For example, if your + * data looked like `{foo: 5, bar: 6}`, then a popular function might be + * `function(d) { return d.foo; }`. + * + * Index, if used, will be the index of the datum in the array. + */ + interface IAppliedAccessor { + (datum: any, index: number): any; + } + interface _IProjector { + accessor: _IAccessor; + scale?: Abstract.Scale; + attribute: string; + } + /** + * A mapping from attributes ("x", "fill", etc.) to the functions that get + * that information out of the data. + * + * So if my data looks like `{foo: 5, bar: 6}` and I want the radius to scale + * with both `foo` and `bar`, an entry in this type might be `{"r": + * function(d) { return foo + bar; }`. + */ + interface IAttributeToProjector { + [attrToSet: string]: IAppliedAccessor; + } + /** + * A simple bounding box. + */ + interface SelectionArea { + xMin: number; + xMax: number; + yMin: number; + yMax: number; + } + interface _ISpaceRequest { + width: number; + height: number; + wantsWidth: boolean; + wantsHeight: boolean; + } + interface _IPixelArea { + xMin: number; + xMax: number; + yMin: number; + yMax: number; + } + /** + * The range of your current data. For example, [1, 2, 6, -5] has the IExtent + * `{min: -5, max: 6}`. + * + * The point of this type is to hopefully replace the less-elegant `[min, + * max]` extents produced by d3. + */ + interface IExtent { + min: number; + max: number; + } + /** + * A simple location on the screen. + */ + interface Point { + x: number; + y: number; + } + /** + * A key that is also coupled with a dataset and a drawer. + */ + interface DatasetDrawerKey { + dataset: Dataset; + drawer: Abstract._Drawer; + key: string; + } +} + + +declare module Plottable { + class Domainer { + /** + * Constructs a new Domainer. + * + * @constructor + * @param {(extents: any[][]) => any[]} combineExtents + * If present, this function will be used by the Domainer to merge + * all the extents that are present on a scale. + * + * A plot may draw multiple things relative to a scale, e.g. + * different stocks over time. The plot computes their extents, + * which are a [min, max] pair. combineExtents is responsible for + * merging them all into one [min, max] pair. It defaults to taking + * the min of the first elements and the max of the second arguments. + */ + constructor(combineExtents?: (extents: any[][]) => any[]); + /** + * @param {any[][]} extents The list of extents to be reduced to a single + * extent. + * @param {QuantitativeScale} scale + * Since nice() must do different things depending on Linear, Log, + * or Time scale, the scale must be passed in for nice() to work. + * @returns {any[]} The domain, as a merging of all exents, as a [min, max] + * pair. + */ + computeDomain(extents: any[][], scale: Abstract.QuantitativeScale): any[]; + /** + * Sets the Domainer to pad by a given ratio. + * + * @param {number} padProportion Proportionally how much bigger the + * new domain should be (0.05 = 5% larger). + * + * A domainer will pad equal visual amounts on each side. + * On a linear scale, this means both sides are padded the same + * amount: [10, 20] will be padded to [5, 25]. + * On a log scale, the top will be padded more than the bottom, so + * [10, 100] will be padded to [1, 1000]. + * + * @returns {Domainer} The calling Domainer. + */ + pad(padProportion?: number): Domainer; + /** + * Adds a padding exception, a value that will not be padded at either end of the domain. + * + * Eg, if a padding exception is added at x=0, then [0, 100] will pad to [0, 105] instead of [-2.5, 102.5]. + * If a key is provided, it will be registered under that key with standard map semantics. (Overwrite / remove by key) + * If a key is not provided, it will be added with set semantics (Can be removed by value) + * + * @param {any} exception The padding exception to add. + * @param {string} key The key to register the exception under. + * @returns {Domainer} The calling domainer + */ + addPaddingException(exception: any, key?: string): Domainer; + /** + * Removes a padding exception, allowing the domain to pad out that value again. + * + * If a string is provided, it is assumed to be a key and the exception associated with that key is removed. + * If a non-string is provdied, it is assumed to be an unkeyed exception and that exception is removed. + * + * @param {any} keyOrException The key for the value to remove, or the value to remove + * @return {Domainer} The calling domainer + */ + removePaddingException(keyOrException: any): Domainer; + /** + * Adds an included value, a value that must be included inside the domain. + * + * Eg, if a value exception is added at x=0, then [50, 100] will expand to [0, 100] rather than [50, 100]. + * If a key is provided, it will be registered under that key with standard map semantics. (Overwrite / remove by key) + * If a key is not provided, it will be added with set semantics (Can be removed by value) + * + * @param {any} value The included value to add. + * @param {string} key The key to register the value under. + * @returns {Domainer} The calling domainer + */ + addIncludedValue(value: any, key?: string): Domainer; + /** + * Remove an included value, allowing the domain to not include that value gain again. + * + * If a string is provided, it is assumed to be a key and the value associated with that key is removed. + * If a non-string is provdied, it is assumed to be an unkeyed value and that value is removed. + * + * @param {any} keyOrException The key for the value to remove, or the value to remove + * @return {Domainer} The calling domainer + */ + removeIncludedValue(valueOrKey: any): Domainer; + /** + * Extends the scale's domain so it starts and ends with "nice" values. + * + * @param {number} count The number of ticks that should fit inside the new domain. + * @return {Domainer} The calling Domainer. + */ + nice(count?: number): Domainer; + } +} + + +declare module Plottable { + module Abstract { + class Scale extends PlottableObject implements Core.IListenable { + _d3Scale: D3.Scale.Scale; + _autoDomainAutomatically: boolean; + broadcaster: any; + _rendererAttrID2Extent: { + [x: string]: D[]; + }; + _typeCoercer: (d: any) => any; + /** + * Constructs a new Scale. + * + * A Scale is a wrapper around a D3.Scale.Scale. A Scale is really just a + * function. Scales have a domain (input), a range (output), and a function + * from domain to range. + * + * @constructor + * @param {D3.Scale.Scale} scale The D3 scale backing the Scale. + */ + constructor(scale: D3.Scale.Scale); + _getAllExtents(): D[][]; + _getExtent(): D[]; + /** + * Modifies the domain on the scale so that it includes the extent of all + * perspectives it depends on. This will normally happen automatically, but + * if you set domain explicitly with `plot.domain(x)`, you will need to + * call this function if you want the domain to neccessarily include all + * the data. + * + * Extent: The [min, max] pair for a Scale.Quantitative, all covered + * strings for a Scale.Ordinal. + * + * Perspective: A combination of a Dataset and an Accessor that + * represents a view in to the data. + * + * @returns {Scale} The calling Scale. + */ + autoDomain(): Scale; + _autoDomainIfAutomaticMode(): void; + /** + * Computes the range value corresponding to a given domain value. In other + * words, apply the function to value. + * + * @param {R} value A domain value to be scaled. + * @returns {R} The range value corresponding to the supplied domain value. + */ + scale(value: D): R; + /** + * Gets the domain. + * + * @returns {D[]} The current domain. + */ + domain(): D[]; + /** + * Sets the domain. + * + * @param {D[]} values If provided, the new value for the domain. On + * a QuantitativeScale, this is a [min, max] pair, or a [max, min] pair to + * make the function decreasing. On Scale.Ordinal, this is an array of all + * input values. + * @returns {Scale} The calling Scale. + */ + domain(values: D[]): Scale; + _getDomain(): any[]; + _setDomain(values: D[]): void; + /** + * Gets the range. + * + * In the case of having a numeric range, it will be a [min, max] pair. In + * the case of string range (e.g. Scale.InterpolatedColor), it will be a + * list of all possible outputs. + * + * @returns {R[]} The current range. + */ + range(): R[]; + /** + * Sets the range. + * + * In the case of having a numeric range, it will be a [min, max] pair. In + * the case of string range (e.g. Scale.InterpolatedColor), it will be a + * list of all possible outputs. + * + * @param {R[]} values If provided, the new values for the range. + * @returns {Scale} The calling Scale. + */ + range(values: R[]): Scale; + /** + * Constructs a copy of the Scale with the same domain and range but without + * any registered listeners. + * + * @returns {Scale} A copy of the calling Scale. + */ + copy(): Scale; + /** + * When a renderer determines that the extent of a projector has changed, + * it will call this function. This function should ensure that + * the scale has a domain at least large enough to include extent. + * + * @param {number} rendererID A unique indentifier of the renderer sending + * the new extent. + * @param {string} attr The attribute being projected, e.g. "x", "y0", "r" + * @param {D[]} extent The new extent to be included in the scale. + */ + _updateExtent(plotProvidedKey: string, attr: string, extent: D[]): Scale; + _removeExtent(plotProvidedKey: string, attr: string): Scale; + } + } +} + + +declare module Plottable { + module Abstract { + interface TickGenerator { + (scale: QuantitativeScale): D[]; + } + class QuantitativeScale extends Scale { + _d3Scale: D3.Scale.QuantitativeScale; + _numTicks: number; + _PADDING_FOR_IDENTICAL_DOMAIN: number; + _userSetDomainer: boolean; + _domainer: Domainer; + _typeCoercer: (d: any) => number; + _tickGenerator: TickGenerator; + /** + * Constructs a new QuantitativeScale. + * + * A QuantitativeScale is a Scale that maps anys to numbers. It + * is invertible and continuous. + * + * @constructor + * @param {D3.Scale.QuantitativeScale} scale The D3 QuantitativeScale + * backing the QuantitativeScale. + */ + constructor(scale: D3.Scale.QuantitativeScale); + _getExtent(): D[]; + /** + * Retrieves 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. + */ + invert(value: number): D; + /** + * Creates a copy of the QuantitativeScale with the same domain and range but without any registered listeners. + * + * @returns {QuantitativeScale} A copy of the calling QuantitativeScale. + */ + copy(): QuantitativeScale; + domain(): D[]; + domain(values: D[]): QuantitativeScale; + _setDomain(values: D[]): void; + /** + * Sets or gets the QuantitativeScale's output interpolator + * + * @param {D3.Transition.Interpolate} [factory] The output interpolator to use. + * @returns {D3.Transition.Interpolate|QuantitativeScale} The current output interpolator, or the calling QuantitativeScale. + */ + interpolate(): D3.Transition.Interpolate; + interpolate(factory: D3.Transition.Interpolate): QuantitativeScale; + /** + * Sets the range of the QuantitativeScale and sets the interpolator to d3.interpolateRound. + * + * @param {number[]} values The new range value for the range. + */ + rangeRound(values: number[]): QuantitativeScale; + /** + * Gets ticks generated by the default algorithm. + */ + getDefaultTicks(): D[]; + /** + * Gets the clamp status of the QuantitativeScale (whether to cut off values outside the ouput range). + * + * @returns {boolean} The current clamp status. + */ + clamp(): boolean; + /** + * Sets the clamp status of the QuantitativeScale (whether to cut off values outside the ouput range). + * + * @param {boolean} clamp Whether or not to clamp the QuantitativeScale. + * @returns {QuantitativeScale} The calling QuantitativeScale. + */ + clamp(clamp: boolean): QuantitativeScale; + /** + * Gets a set of tick values spanning the domain. + * + * @returns {any[]} The generated ticks. + */ + ticks(): any[]; + /** + * Gets the default number of ticks. + * + * @returns {number} The default number of ticks. + */ + numTicks(): number; + /** + * Sets the default number of ticks to generate. + * + * @param {number} count The new default number of ticks. + * @returns {Scale} The calling Scale. + */ + numTicks(count: number): QuantitativeScale; + /** + * Given a domain, expands its domain onto "nice" values, e.g. whole + * numbers. + */ + _niceDomain(domain: any[], count?: number): any[]; + /** + * Gets a Domainer of a scale. A Domainer is responsible for combining + * multiple extents into a single domain. + * + * @return {Domainer} The scale's current domainer. + */ + domainer(): Domainer; + /** + * Sets a Domainer of a scale. A Domainer is responsible for combining + * multiple extents into a single domain. + * + * When you set domainer, we assume that you know what you want the domain + * to look like better that we do. Ensuring that the domain is padded, + * includes 0, etc., will be the responsability of the new domainer. + * + * @param {Domainer} domainer If provided, the new domainer. + * @return {QuanitativeScale} The calling QuantitativeScale. + */ + domainer(domainer: Domainer): QuantitativeScale; + _defaultExtent(): any[]; + /** + * Gets the tick generator of the QuantitativeScale. + * + * @returns {TickGenerator} The current tick generator. + */ + tickGenerator(): TickGenerator; + /** + * Sets a tick generator + * + * @param {TickGenerator} generator, the new tick generator. + * @return {QuanitativeScale} The calling QuantitativeScale. + */ + tickGenerator(generator: TickGenerator): QuantitativeScale; + } + } +} + + +declare module Plottable { + module Scale { + class Linear extends Abstract.QuantitativeScale { + /** + * Constructs a new LinearScale. + * + * This scale maps from domain to range with a simple `mx + b` formula. + * + * @constructor + * @param {D3.Scale.LinearScale} [scale] The D3 LinearScale backing the + * LinearScale. If not supplied, uses a default scale. + */ + constructor(); + constructor(scale: D3.Scale.LinearScale); + /** + * Constructs a copy of the Scale.Linear with the same domain and range but + * without any registered listeners. + * + * @returns {Linear} A copy of the calling Scale.Linear. + */ + copy(): Linear; + } + } +} + + +declare module Plottable { + module Scale { + class Log extends Abstract.QuantitativeScale { + /** + * Constructs a new Scale.Log. + * + * Warning: Log is deprecated; if possible, use ModifiedLog. Log scales are + * very unstable due to the fact that they can't handle 0 or negative + * numbers. The only time when you would want to use a Log scale over a + * ModifiedLog scale is if you're plotting very small data, such as all + * data < 1. + * + * @constructor + * @param {D3.Scale.LogScale} [scale] The D3 Scale.Log backing the Scale.Log. If not supplied, uses a default scale. + */ + constructor(); + constructor(scale: D3.Scale.LogScale); + /** + * Creates a copy of the Scale.Log with the same domain and range but without any registered listeners. + * + * @returns {Log} A copy of the calling Log. + */ + copy(): Log; + _defaultExtent(): number[]; + } + } +} + + +declare module Plottable { + module Scale { + class ModifiedLog extends Abstract.QuantitativeScale { + /** + * Creates a new Scale.ModifiedLog. + * + * A ModifiedLog scale acts as a regular log scale for large numbers. + * As it approaches 0, it gradually becomes linear. This means that the + * scale won't freak out if you give it 0 or a negative number, where an + * ordinary Log scale would. + * + * However, it does mean that scale will be effectively linear as values + * approach 0. If you want very small values on a log scale, you should use + * an ordinary Scale.Log instead. + * + * @constructor + * @param {number} [base] + * The base of the log. Defaults to 10, and must be > 1. + * + * For base <= x, 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). + */ + constructor(base?: number); + scale(x: number): number; + invert(x: number): number; + _getDomain(): number[]; + _setDomain(values: number[]): void; + ticks(count?: number): number[]; + copy(): ModifiedLog; + _niceDomain(domain: any[], count?: number): any[]; + /** + * Gets whether or not to return tick values other than powers of base. + * + * This defaults to false, so you'll normally only see ticks like + * [10, 100, 1000]. If you turn it on, you might see ticks values + * like [10, 50, 100, 500, 1000]. + * @returns {boolean} the current setting. + */ + showIntermediateTicks(): boolean; + /** + * Sets whether or not to return ticks values other than powers or base. + * + * @param {boolean} show If provided, the desired setting. + * @returns {ModifiedLog} The calling ModifiedLog. + */ + showIntermediateTicks(show: boolean): ModifiedLog; + } + } +} + + +declare module Plottable { + module Scale { + class Ordinal extends Abstract.Scale { + _d3Scale: D3.Scale.OrdinalScale; + _typeCoercer: (d: any) => any; + /** + * Creates an OrdinalScale. + * + * An OrdinalScale maps strings to numbers. A common use is to map the + * labels of a bar plot (strings) to their pixel locations (numbers). + * + * @constructor + */ + constructor(scale?: D3.Scale.OrdinalScale); + _getExtent(): string[]; + domain(): string[]; + domain(values: string[]): Ordinal; + _setDomain(values: string[]): void; + range(): number[]; + range(values: number[]): Ordinal; + /** + * Returns the width of the range band. Only valid when rangeType is set to "bands". + * + * @returns {number} The range band width or 0 if rangeType isn't "bands". + */ + rangeBand(): number; + innerPadding(): number; + fullBandStartAndWidth(v: string): number[]; + /** + * Get the range type. + * + * @returns {string} The current range type. + */ + rangeType(): string; + /** + * Set the range type. + * + * @param {string} rangeType If provided, either "points" or "bands" indicating the + * d3 method used to generate range bounds. + * @param {number} [outerPadding] If provided, the padding outside the range, + * proportional to the range step. + * @param {number} [innerPadding] If provided, the padding between bands in the range, + * proportional to the range step. This parameter is only used in + * "bands" type ranges. + * @returns {Ordinal} The calling Ordinal. + */ + rangeType(rangeType: string, outerPadding?: number, innerPadding?: number): Ordinal; + copy(): Ordinal; + } + } +} + + +declare module Plottable { + module Scale { + class Color extends Abstract.Scale { + /** + * Constructs a ColorScale. + * + * @constructor + * @param {string} [scaleType] the type of color scale to create + * (Category10/Category20/Category20b/Category20c). + * See https://github.com/mbostock/d3/wiki/Ordinal-Scales#categorical-colors + */ + constructor(scaleType?: string); + _getExtent(): string[]; + } + } +} + + +declare module Plottable { + module Scale { + class Time extends Abstract.QuantitativeScale { + _typeCoercer: (d: any) => any; + /** + * Constructs a TimeScale. + * + * A TimeScale maps Date objects to numbers. + * + * @constructor + * @param {D3.Scale.Time} scale The D3 LinearScale backing the Scale.Time. If not supplied, uses a default scale. + */ + constructor(); + constructor(scale: D3.Scale.LinearScale); + _tickInterval(interval: D3.Time.Interval, step?: number): any[]; + _setDomain(values: any[]): void; + copy(): Time; + _defaultExtent(): any[]; + } + } +} + + +declare module Plottable { + module Scale { + /** + * This class implements a color scale that takes quantitive input and + * interpolates between a list of color values. It returns a hex string + * representing the interpolated color. + * + * By default it generates a linear scale internally. + */ + class InterpolatedColor extends Abstract.Scale { + /** + * Constructs an InterpolatedColorScale. + * + * An InterpolatedColorScale maps numbers evenly to color strings. + * + * @constructor + * @param {string|string[]} colorRange the type of color scale to + * create. Default is "reds". @see {@link colorRange} for further + * options. + * @param {string} scaleType the type of underlying scale to use + * (linear/pow/log/sqrt). Default is "linear". @see {@link scaleType} + * for further options. + */ + constructor(colorRange?: any, scaleType?: string); + /** + * Gets the color range. + * + * @returns {string[]} the current color values for the range as strings. + */ + colorRange(): string[]; + /** + * Sets the color range. + * + * @param {string|string[]} [colorRange]. If provided and if colorRange is one of + * (reds/blues/posneg), uses the built-in color groups. If colorRange is an + * array of strings with at least 2 values (e.g. ["#FF00FF", "red", + * "dodgerblue"], the resulting scale will interpolate between the color + * values across the domain. + * @returns {InterpolatedColor} The calling InterpolatedColor. + */ + colorRange(colorRange: any): InterpolatedColor; + /** + * Gets the internal scale type. + * + * @returns {string} The current scale type. + */ + scaleType(): string; + /** + * Sets the internal scale type. + * + * @param {string} scaleType If provided, the type of d3 scale to use internally. (linear/log/sqrt/pow). + * @returns {InterpolatedColor} The calling InterpolatedColor. + */ + scaleType(scaleType: string): InterpolatedColor; + autoDomain(): InterpolatedColor; + } + } +} + + +declare module Plottable { + module _Util { + class ScaleDomainCoordinator { + /** + * Constructs a ScaleDomainCoordinator. + * + * @constructor + * @param {Scale[]} scales A list of scales whose domains should be linked. + */ + constructor(scales: Abstract.Scale[]); + rescale(scale: Abstract.Scale): void; + } + } +} + + +declare module Plottable { + module Abstract { + class _Drawer { + _renderArea: D3.Selection; + key: string; + /** + * Constructs a Drawer + * + * @constructor + * @param{string} key The key associated with this Drawer + */ + constructor(key: string); + /** + * Removes the Drawer and its renderArea + */ + remove(): void; + /** + * Draws the data into the renderArea using the attrHash for attributes + * + * @param{any[]} data The data to be drawn + * @param{attrHash} IAttributeToProjector The list of attributes to set on the data + */ + draw(data: any[], attrToProjector: IAttributeToProjector, animator?: Animator.Null): void; + } + } +} + + +declare module Plottable { + module _Drawer { + class Arc extends Abstract._Drawer { + draw(data: any[], attrToProjector: IAttributeToProjector, animator?: Animator.Null): void; + } + } +} + + +declare module Plottable { + module _Drawer { + class Area extends Abstract._Drawer { + draw(data: any[], attrToProjector: IAttributeToProjector): void; + } + } +} + + +declare module Plottable { + module _Drawer { + class Rect extends Abstract._Drawer { + draw(data: any[], attrToProjector: IAttributeToProjector, animator?: Animator.Null): void; + } + } +} + + +declare module Plottable { + module Abstract { + class Component extends PlottableObject { + static AUTORESIZE_BY_DEFAULT: boolean; + _element: D3.Selection; + _content: D3.Selection; + _backgroundContainer: D3.Selection; + _foregroundContainer: D3.Selection; + clipPathEnabled: boolean; + _parent: ComponentContainer; + _xAlignProportion: number; + _yAlignProportion: number; + _fixedHeightFlag: boolean; + _fixedWidthFlag: boolean; + _isSetup: boolean; + _isAnchored: boolean; + /** + * Attaches the Component as a child of a given a DOM element. Usually only directly invoked on root-level Components. + * + * @param {D3.Selection} element A D3 selection consisting of the element to anchor under. + */ + _anchor(element: D3.Selection): void; + /** + * 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. + */ + _setup(): void; + _requestedSpace(availableWidth: number, availableHeight: number): _ISpaceRequest; + /** + * Computes the size, position, and alignment 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 {number} xOrigin x-coordinate of the origin of the component + * @param {number} yOrigin y-coordinate of the origin of the component + * @param {number} availableWidth available width for the component to render in + * @param {number} availableHeight available height for the component to render in + */ + _computeLayout(xOrigin?: number, yOrigin?: number, availableWidth?: number, availableHeight?: number): void; + _render(): void; + _scheduleComputeLayout(): void; + _doRender(): void; + _invalidateLayout(): void; + /** + * Renders the Component into a given DOM element. The element must be as . + * + * @param {String|D3.Selection} element A D3 selection or a selector for getting the element to render into. + * @returns {Component} The calling component. + */ + renderTo(selector: String): Component; + renderTo(element: D3.Selection): Component; + /** + * Causes the Component to recompute layout and redraw. If passed arguments, will resize the root SVG it lives in. + * + * This function should be called when CSS changes could influence the size + * of the components, e.g. changing the font size. + * + * @param {number} [availableWidth] - the width of the container element + * @param {number} [availableHeight] - the height of the container element + * @returns {Component} The calling component. + */ + resize(width?: number, height?: number): Component; + /** + * Enables or disables resize on window resizes. + * + * If enabled, window resizes will enqueue this component for a re-layout + * and re-render. Animations are disabled during window resizes when auto- + * resize is enabled. + * + * @param {boolean} flag Enable (true) or disable (false) auto-resize. + * @returns {Component} The calling component. + */ + autoResize(flag: boolean): Component; + /** + * Sets the x alignment of the Component. This will be used if the + * Component is given more space than it needs. + * + * For example, you may want to make a Legend postition itself it the top + * right, so you would call `legend.xAlign("right")` and + * `legend.yAlign("top")`. + * + * @param {string} alignment The x alignment of the Component (one of ["left", "center", "right"]). + * @returns {Component} The calling Component. + */ + xAlign(alignment: string): Component; + /** + * Sets the y alignment of the Component. This will be used if the + * Component is given more space than it needs. + * + * For example, you may want to make a Legend postition itself it the top + * right, so you would call `legend.xAlign("right")` and + * `legend.yAlign("top")`. + * + * @param {string} alignment The x alignment of the Component (one of ["top", "center", "bottom"]). + * @returns {Component} The calling Component. + */ + yAlign(alignment: string): Component; + /** + * Sets the x offset of the Component. This will be used if the Component + * is given more space than it needs. + * + * @param {number} offset The desired x offset, in pixels, from the left + * side of the container. + * @returns {Component} The calling Component. + */ + xOffset(offset: number): Component; + /** + * Sets the y offset of the Component. This will be used if the Component + * is given more space than it needs. + * + * @param {number} offset The desired y offset, in pixels, from the top + * side of the container. + * @returns {Component} The calling Component. + */ + yOffset(offset: number): Component; + /** + * Attaches an Interaction to the Component, so that the Interaction will listen for events on the Component. + * + * @param {Interaction} interaction The Interaction to attach to the Component. + * @returns {Component} The calling Component. + */ + registerInteraction(interaction: Interaction): Component; + /** + * Adds/removes a given CSS class to/from the Component, or checks if the Component has a particular CSS class. + * + * @param {string} cssClass The CSS class to add/remove/check for. + * @param {boolean} addClass Whether to add or remove the CSS class. If not supplied, checks for the CSS class. + * @returns {boolean|Component} Whether the Component has the given CSS class, or the calling Component (if addClass is supplied). + */ + classed(cssClass: string): boolean; + classed(cssClass: string, addClass: boolean): Component; + /** + * Checks if the Component has a fixed width or false if it grows to fill available space. + * Returns false by default on the base Component class. + * + * @returns {boolean} Whether the component has a fixed width. + */ + _isFixedWidth(): boolean; + /** + * Checks if the Component has a fixed height or false if it grows to fill available space. + * Returns false by default on the base Component class. + * + * @returns {boolean} Whether the component has a fixed height. + */ + _isFixedHeight(): boolean; + /** + * Merges this Component with another Component, returning a + * ComponentGroup. This is used to layer Components on top of each other. + * + * There are four cases: + * Component + Component: Returns a ComponentGroup with both components inside it. + * ComponentGroup + Component: Returns the ComponentGroup with the Component appended. + * Component + ComponentGroup: Returns the ComponentGroup with the Component prepended. + * ComponentGroup + ComponentGroup: Returns a new ComponentGroup with two ComponentGroups inside it. + * + * @param {Component} c The component to merge in. + * @returns {ComponentGroup} The relevant ComponentGroup out of the above four cases. + */ + merge(c: Component): Component.Group; + /** + * Detaches a Component from the DOM. The component can be reused. + * + * This should only be used if you plan on reusing the calling + * Components. Otherwise, use remove(). + * + * @returns The calling Component. + */ + detach(): Component; + /** + * Removes a Component from the DOM and disconnects it from everything it's + * listening to (effectively destroying it). + */ + remove(): void; + /** + * Return the width of the component + * + * @return {number} width of the component + */ + width(): number; + /** + * Return the height of the component + * + * @return {number} height of the component + */ + height(): number; + } + } +} + + +declare module Plottable { + module Abstract { + class ComponentContainer extends Component { + _components: Component[]; + _anchor(element: D3.Selection): void; + _render(): void; + _removeComponent(c: Component): void; + _addComponent(c: Component, prepend?: boolean): boolean; + /** + * Returns a list of components in the ComponentContainer. + * + * @returns {Component[]} the contained Components + */ + components(): Component[]; + /** + * Returns true iff the ComponentContainer is empty. + * + * @returns {boolean} Whether the calling ComponentContainer is empty. + */ + empty(): boolean; + /** + * Detaches all components contained in the ComponentContainer, and + * empties the ComponentContainer. + * + * @returns {ComponentContainer} The calling ComponentContainer + */ + detachAll(): ComponentContainer; + remove(): void; + } + } +} + + +declare module Plottable { + module Component { + class Group extends Abstract.ComponentContainer { + /** + * Constructs a GroupComponent. + * + * A GroupComponent is a set of Components that will be rendered on top of + * each other. When you call Component.merge(Component), it creates and + * returns a GroupComponent. + * + * @constructor + * @param {Component[]} components The Components in the Group (default = []). + */ + constructor(components?: Abstract.Component[]); + _requestedSpace(offeredWidth: number, offeredHeight: number): _ISpaceRequest; + merge(c: Abstract.Component): Group; + _computeLayout(xOrigin?: number, yOrigin?: number, availableWidth?: number, availableHeight?: number): Group; + _isFixedWidth(): boolean; + _isFixedHeight(): boolean; + } + } +} + + +declare module Plottable { + module Abstract { + class Axis extends Component { + /** + * The css class applied to each end tick mark (the line on the end tick). + */ + static END_TICK_MARK_CLASS: string; + /** + * The css class applied to each tick mark (the line on the tick). + */ + static TICK_MARK_CLASS: string; + /** + * The css class applied to each tick label (the text associated with the tick). + */ + static TICK_LABEL_CLASS: string; + _tickMarkContainer: D3.Selection; + _tickLabelContainer: D3.Selection; + _baseline: D3.Selection; + _scale: Scale; + _formatter: Formatter; + _orientation: string; + _computedWidth: number; + _computedHeight: number; + /** + * Constructs an axis. An axis is a wrapper around a scale for rendering. + * + * @constructor + * @param {Scale} scale The scale for this axis to render. + * @param {string} orientation One of ["top", "left", "bottom", "right"]; + * on which side the axis will appear. On most axes, this is either "left" + * or "bottom". + * @param {Formatter} Data is passed through this formatter before being + * displayed. + */ + constructor(scale: Scale, orientation: string, formatter?: (d: any) => string); + remove(): void; + _isHorizontal(): boolean; + _computeWidth(): number; + _computeHeight(): number; + _requestedSpace(offeredWidth: number, offeredHeight: number): _ISpaceRequest; + _isFixedHeight(): boolean; + _isFixedWidth(): boolean; + _rescale(): void; + _computeLayout(xOffset?: number, yOffset?: number, availableWidth?: number, availableHeight?: number): void; + _setup(): void; + _getTickValues(): any[]; + _doRender(): void; + _generateBaselineAttrHash(): { + x1: number; + y1: number; + x2: number; + y2: number; + }; + _generateTickMarkAttrHash(isEndTickMark?: boolean): { + x1: any; + y1: any; + x2: any; + y2: any; + }; + _invalidateLayout(): void; + /** + * Gets the current formatter on the axis. Data is passed through the + * formatter before being displayed. + * + * @returns {Formatter} The calling Axis, or the current + * Formatter. + */ + formatter(): Formatter; + /** + * Sets the current formatter on the axis. Data is passed through the + * formatter before being displayed. + * + * @param {Formatter} formatter If provided, data will be passed though `formatter(data)`. + * @returns {Axis} The calling Axis. + */ + formatter(formatter: Formatter): Axis; + /** + * Gets the current tick mark length. + * + * @returns {number} the current tick mark length. + */ + tickLength(): number; + /** + * Sets the current tick mark length. + * + * @param {number} length If provided, length of each tick. + * @returns {Axis} The calling Axis. + */ + tickLength(length: number): Axis; + /** + * Gets the current end tick mark length. + * + * @returns {number} The current end tick mark length. + */ + endTickLength(): number; + /** + * Sets the end tick mark length. + * + * @param {number} length If provided, the length of the end ticks. + * @returns {BaseAxis} The calling Axis. + */ + endTickLength(length: number): Axis; + _maxLabelTickLength(): number; + /** + * Gets the padding between each tick mark and its associated label. + * + * @returns {number} the current padding. + * length. + */ + tickLabelPadding(): number; + /** + * Sets the padding between each tick mark and its associated label. + * + * @param {number} padding If provided, the desired padding. + * @returns {Axis} The calling Axis. + */ + tickLabelPadding(padding: number): Axis; + /** + * Gets the size of the gutter (the extra space between the tick + * labels and the outer edge of the axis). + * + * @returns {number} the current gutter. + * length. + */ + gutter(): number; + /** + * Sets the size of the gutter (the extra space between the tick + * labels and the outer edge of the axis). + * + * @param {number} size If provided, the desired gutter. + * @returns {Axis} The calling Axis. + */ + gutter(size: number): Axis; + /** + * Gets the orientation of the Axis. + * + * @returns {number} the current orientation. + */ + orient(): string; + /** + * Sets the orientation of the Axis. + * + * @param {number} newOrientation If provided, the desired orientation + * (top/bottom/left/right). + * @returns {Axis} The calling Axis. + */ + orient(newOrientation: string): Axis; + /** + * Gets whether the Axis is currently set to show the first and last + * tick labels. + * + * @returns {boolean} whether or not the last + * tick labels are showing. + */ + showEndTickLabels(): boolean; + /** + * Sets whether the Axis is currently set to show the first and last tick + * labels. + * + * @param {boolean} show Whether or not to show the first and last + * labels. + * @returns {Axis} The calling Axis. + */ + showEndTickLabels(show: boolean): Axis; + _hideEndTickLabels(): void; + _hideOverlappingTickLabels(): void; + } + } +} + + +declare module Plottable { + module Axis { + interface _ITimeInterval { + timeUnit: D3.Time.Interval; + step: number; + formatString: string; + } + class Time extends Abstract.Axis { + _majorTickLabels: D3.Selection; + _minorTickLabels: D3.Selection; + _scale: Scale.Time; + static _minorIntervals: _ITimeInterval[]; + static _majorIntervals: _ITimeInterval[]; + /** + * Constructs a TimeAxis. + * + * A TimeAxis is used for rendering a TimeScale. + * + * @constructor + * @param {TimeScale} scale The scale to base the Axis on. + * @param {string} orientation The orientation of the Axis (top/bottom) + */ + constructor(scale: Scale.Time, orientation: string); + _computeHeight(): number; + _setup(): void; + _getTickIntervalValues(interval: _ITimeInterval): any[]; + _getTickValues(): any[]; + _measureTextHeight(container: D3.Selection): number; + _doRender(): Time; + } + } +} + + +declare module Plottable { + module Axis { + class Numeric extends Abstract.Axis { + _scale: Abstract.QuantitativeScale; + /** + * Constructs a NumericAxis. + * + * Just as an CategoryAxis is for rendering an OrdinalScale, a NumericAxis + * is for rendering a QuantitativeScale. + * + * @constructor + * @param {QuantitativeScale} scale The QuantitativeScale to base the axis on. + * @param {string} orientation The orientation of the QuantitativeScale (top/bottom/left/right) + * @param {Formatter} formatter A function to format tick labels (default Formatters.general(3, false)). + */ + constructor(scale: Abstract.QuantitativeScale, orientation: string, formatter?: (d: any) => string); + _setup(): void; + _computeWidth(): number; + _computeHeight(): number; + _getTickValues(): any[]; + _rescale(): void; + _doRender(): void; + /** + * Gets the tick label position relative to the tick marks. + * + * @returns {string} The current tick label position. + */ + tickLabelPosition(): string; + /** + * Sets the tick label position relative to the tick marks. + * + * @param {string} position If provided, the relative position of the tick label. + * [top/center/bottom] for a vertical NumericAxis, + * [left/center/right] for a horizontal NumericAxis. + * Defaults to center. + * @returns {Numeric} The calling Axis.Numeric. + */ + tickLabelPosition(position: string): Numeric; + /** + * Gets whether or not the tick labels at the end of the graph are + * displayed when partially cut off. + * + * @param {string} orientation Where on the scale to change tick labels. + * On a "top" or "bottom" axis, this can be "left" or + * "right". On a "left" or "right" axis, this can be "top" + * or "bottom". + * @returns {boolean} The current setting. + */ + showEndTickLabel(orientation: string): boolean; + /** + * Sets whether or not the tick labels at the end of the graph are + * displayed when partially cut off. + * + * @param {string} orientation If provided, where on the scale to change tick labels. + * On a "top" or "bottom" axis, this can be "left" or + * "right". On a "left" or "right" axis, this can be "top" + * or "bottom". + * @param {boolean} show Whether or not the given tick should be + * displayed. + * @returns {Numeric} The calling NumericAxis. + */ + showEndTickLabel(orientation: string, show: boolean): Numeric; + } + } +} + + +declare module Plottable { + module Axis { + class Category extends Abstract.Axis { + _scale: Scale.Ordinal; + /** + * Constructs a CategoryAxis. + * + * A CategoryAxis takes an OrdinalScale and includes word-wrapping + * algorithms and advanced layout logic to try to display the scale as + * efficiently as possible. + * + * @constructor + * @param {OrdinalScale} scale The scale to base the Axis on. + * @param {string} orientation The orientation of the Axis (top/bottom/left/right) (default = "bottom"). + * @param {Formatter} formatter The Formatter for the Axis (default Formatters.identity()) + */ + constructor(scale: Scale.Ordinal, orientation?: string, formatter?: (d: any) => string); + _setup(): void; + _rescale(): void; + _requestedSpace(offeredWidth: number, offeredHeight: number): _ISpaceRequest; + _getTickValues(): string[]; + _doRender(): Category; + _computeLayout(xOrigin?: number, yOrigin?: number, availableWidth?: number, availableHeight?: number): void; + } + } +} + + +declare module Plottable { + module Component { + class Label extends Abstract.Component { + /** + * Creates a Label. + * + * A label is component that renders just text. The most common use of + * labels is to create a title or axis labels. + * + * @constructor + * @param {string} displayText The text of the Label (default = ""). + * @param {string} orientation The orientation of the Label (horizontal/vertical-left/vertical-right) (default = "horizontal"). + */ + constructor(displayText?: string, orientation?: string); + /** + * Sets the horizontal side the label will go to given the label is given more space that it needs + * + * @param {string} alignment The new setting, one of `["left", "center", + * "right"]`. Defaults to `"center"`. + * @returns {Label} The calling Label. + */ + xAlign(alignment: string): Label; + /** + * Sets the vertical side the label will go to given the label is given more space that it needs + * + * @param {string} alignment The new setting, one of `["top", "center", + * "bottom"]`. Defaults to `"center"`. + * @returns {Label} The calling Label. + */ + yAlign(alignment: string): Label; + _requestedSpace(offeredWidth: number, offeredHeight: number): _ISpaceRequest; + _setup(): void; + /** + * Gets the current text on the Label. + * + * @returns {string} the text on the label. + */ + text(): string; + /** + * Sets the current text on the Label. + * + * @param {string} displayText If provided, the new text for the Label. + * @returns {Label} The calling Label. + */ + text(displayText: string): Label; + /** + * Gets the orientation of the Label. + * + * @returns {string} the current orientation. + */ + orient(): string; + /** + * Sets the orientation of the Label. + * + * @param {string} newOrientation If provided, the desired orientation + * (horizontal/vertical-left/vertical-right). + * @returns {Label} The calling Label. + */ + orient(newOrientation: string): Label; + _doRender(): void; + _computeLayout(xOffset?: number, yOffset?: number, availableWidth?: number, availableHeight?: number): Label; + } + class TitleLabel extends Label { + /** + * Creates a TitleLabel, a type of label made for rendering titles. + * + * @constructor + */ + constructor(text?: string, orientation?: string); + } + class AxisLabel extends Label { + /** + * Creates a AxisLabel, a type of label made for rendering axis labels. + * + * @constructor + */ + constructor(text?: string, orientation?: string); + } + } +} + + +declare module Plottable { + module Component { + interface ToggleCallback { + (datum: string, newState: boolean): any; + } + interface HoverCallback { + (datum?: string): any; + } + class Legend extends Abstract.Component { + /** + * The css class applied to each legend row + */ + static SUBELEMENT_CLASS: string; + /** + * Constructs a Legend. + * + * A legend consists of a series of legend rows, each with a color and label taken from the `colorScale`. + * The rows will be displayed in the order of the `colorScale` domain. + * This legend also allows interactions, through the functions `toggleCallback` and `hoverCallback` + * Setting a callback will also put classes on the individual rows. + * + * @constructor + * @param {ColorScale} colorScale + */ + constructor(colorScale?: Scale.Color); + remove(): void; + /** + * Gets the toggle callback from the Legend. + * + * This callback is associated with toggle events, which trigger when a legend row is clicked. + * Internally, this will change the state of of the row from "toggled-on" to "toggled-off" and vice versa. + * Setting a callback will also set a class to each individual legend row as "toggled-on" or "toggled-off". + * Call with argument of null to remove the callback. This will also remove the above classes to legend rows. + * + * @returns {ToggleCallback} The current toggle callback. + */ + toggleCallback(): ToggleCallback; + /** + * Assigns a toggle callback to the Legend. + * + * This callback is associated with toggle events, which trigger when a legend row is clicked. + * Internally, this will change the state of of the row from "toggled-on" to "toggled-off" and vice versa. + * Setting a callback will also set a class to each individual legend row as "toggled-on" or "toggled-off". + * Call with argument of null to remove the callback. This will also remove the above classes to legend rows. + * + * @param {ToggleCallback} callback The new callback function. + * @returns {Legend} The calling Legend. + */ + toggleCallback(callback: ToggleCallback): Legend; + /** + * Gets the hover callback from the Legend. + * + * This callback is associated with hover events, which trigger when the mouse enters or leaves a legend row + * Setting a callback will also set the class "hover" to all legend row, + * as well as the class "focus" to the legend row being hovered over. + * Call with argument of null to remove the callback. This will also remove the above classes to legend rows. + * + * @returns {HoverCallback} The new current hover callback. + */ + hoverCallback(): HoverCallback; + /** + * Assigns a hover callback to the Legend. + * + * This callback is associated with hover events, which trigger when the mouse enters or leaves a legend row + * Setting a callback will also set the class "hover" to all legend row, + * as well as the class "focus" to the legend row being hovered over. + * Call with argument of null to remove the callback. This will also remove the above classes to legend rows. + * + * @param {HoverCallback} callback If provided, the new callback function. + * @returns {Legend} The calling Legend. + */ + hoverCallback(callback: HoverCallback): Legend; + /** + * Gets the current color scale from the Legend. + * + * @returns {ColorScale} The current color scale. + */ + scale(): Scale.Color; + /** + * Assigns a new color scale to the Legend. + * + * @param {Scale.Color} scale If provided, the new scale. + * @returns {Legend} The calling Legend. + */ + scale(scale: Scale.Color): Legend; + _computeLayout(xOrigin?: number, yOrigin?: number, availableWidth?: number, availableHeight?: number): void; + _requestedSpace(offeredWidth: number, offeredHeight: number): _ISpaceRequest; + _doRender(): void; + } + } +} + + +declare module Plottable { + module Component { + class HorizontalLegend extends Abstract.Component { + /** + * The css class applied to each legend row + */ + static LEGEND_ROW_CLASS: string; + /** + * The css class applied to each legend entry + */ + static LEGEND_ENTRY_CLASS: string; + /** + * Creates a Horizontal Legend. + * + * The legend consists of a series of legend entries, each with a color and label taken from the `colorScale`. + * The entries will be displayed in the order of the `colorScale` domain. + * + * @constructor + * @param {Scale.Color} colorScale + */ + constructor(colorScale: Scale.Color); + remove(): void; + _requestedSpace(offeredWidth: number, offeredHeight: number): _ISpaceRequest; + _doRender(): void; + } + } +} + + +declare module Plottable { + module Component { + class Gridlines extends Abstract.Component { + /** + * Creates a set of Gridlines. + * @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. + */ + constructor(xScale: Abstract.QuantitativeScale, yScale: Abstract.QuantitativeScale); + remove(): Gridlines; + _setup(): void; + _doRender(): void; + } + } +} + + +declare module Plottable { + module Component { + interface _IterateLayoutResult { + colProportionalSpace: number[]; + rowProportionalSpace: number[]; + guaranteedWidths: number[]; + guaranteedHeights: number[]; + wantsWidth: boolean; + wantsHeight: boolean; + } + class Table extends Abstract.ComponentContainer { + /** + * Constructs a Table. + * + * A Table is used to combine multiple 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 the Components to place in the table. + * null can be used if a cell is empty. (default = []) + */ + constructor(rows?: Abstract.Component[][]); + /** + * Adds a Component in the specified cell. The cell must be unoccupied. + * + * For example, instead of calling `new Table([[a, b], [null, c]])`, you + * could call + * ```typescript + * var table = new Table(); + * table.addComponent(0, 0, a); + * table.addComponent(0, 1, b); + * table.addComponent(1, 1, c); + * ``` + * + * @param {number} row The row in which to add the Component. + * @param {number} col The column in which to add the Component. + * @param {Component} component The Component to be added. + * @returns {Table} The calling Table. + */ + addComponent(row: number, col: number, component: Abstract.Component): Table; + _removeComponent(component: Abstract.Component): void; + _requestedSpace(offeredWidth: number, offeredHeight: number): _ISpaceRequest; + _computeLayout(xOffset?: number, yOffset?: number, availableWidth?: number, availableHeight?: number): void; + /** + * Sets the row and column padding on the Table. + * + * @param {number} rowPadding The padding above and below each row, in pixels. + * @param {number} colPadding the padding to the left and right of each column, in pixels. + * @returns {Table} The calling Table. + */ + padding(rowPadding: number, colPadding: number): Table; + /** + * Sets the layout weight of a particular row. + * Space is allocated to rows based on their weight. Rows with higher weights receive proportionally more space. + * + * A common case would be to have one graph take up 2/3rds of the space, + * and the other graph take up 1/3rd. + * + * @param {number} index The index of the row. + * @param {number} weight The weight to be set on the row. + * @returns {Table} The calling Table. + */ + rowWeight(index: number, weight: number): Table; + /** + * Sets the layout weight of a particular column. + * Space is allocated to columns based on their weight. Columns with higher weights receive proportionally more space. + * + * A common case would be to have one graph take up 2/3rds of the space, + * and the other graph take up 1/3rd. + * + * @param {number} index The index of the column. + * @param {number} weight The weight to be set on the column. + * @returns {Table} The calling Table. + */ + colWeight(index: number, weight: number): Table; + _isFixedWidth(): boolean; + _isFixedHeight(): boolean; + } + } +} + + +declare module Plottable { + module Abstract { + class Plot extends Component { + _dataset: Dataset; + _dataChanged: boolean; + _renderArea: D3.Selection; + _animate: boolean; + _animators: Animator.IPlotAnimatorMap; + _ANIMATION_DURATION: number; + _projectors: { + [x: string]: _IProjector; + }; + /** + * Constructs a Plot. + * + * Plots render data. Common example include Plot.Scatter, Plot.Bar, and Plot.Line. + * + * A bare Plot has a DataSource and any number of projectors, which take + * data and "project" it onto the Plot, such as "x", "y", "fill", "r". + * + * @constructor + * @param {any[]|Dataset} [dataset] If provided, the data or Dataset to be associated with this Plot. + */ + constructor(); + constructor(data: any[]); + constructor(dataset: Dataset); + _anchor(element: D3.Selection): void; + remove(): void; + /** + * Gets the Plot's Dataset. + * + * @returns {Dataset} The current Dataset. + */ + dataset(): Dataset; + /** + * Sets the Plot's Dataset. + * + * @param {Dataset} dataset If provided, the Dataset the Plot should use. + * @returns {Plot} The calling Plot. + */ + dataset(dataset: Dataset): Plot; + _onDatasetUpdate(): void; + /** + * Sets an attribute of every data point. + * + * Here's a common use case: + * ```typescript + * plot.attr("r", function(d) { return d.foo; }); + * ``` + * This will set the radius of each datum `d` to be `d.foo`. + * + * @param {string} attrToSet The attribute to set across each data + * point. Popular examples include "x", "y", "r". Scales that inherit from + * Plot define their meaning. + * + * @param {Function|string|any} accessor Function to apply to each element + * of the dataSource. If a Function, use `accessor(d, i)`. If a string, + * `d[accessor]` is used. If anything else, use `accessor` as a constant + * across all data points. + * + * @param {Abstract.Scale} scale If provided, the result of the accessor + * is passed through the scale, such as `scale.scale(accessor(d, i))`. + * + * @returns {Plot} The calling Plot. + */ + attr(attrToSet: string, accessor: any, scale?: Scale): Plot; + /** + * Identical to plot.attr + */ + project(attrToSet: string, accessor: any, scale?: Scale): Plot; + _generateAttrToProjector(): IAttributeToProjector; + _doRender(): void; + _paint(): void; + _setup(): void; + /** + * Enables or disables animation. + * + * @param {boolean} enabled Whether or not to animate. + */ + animate(enabled: boolean): Plot; + detach(): Plot; + /** + * This function makes sure that all of the scales in this._projectors + * have an extent that includes all the data that is projected onto them. + */ + _updateScaleExtents(): void; + _updateScaleExtent(attr: string): void; + /** + * Applies attributes to the selection. + * + * If animation is enabled and a valid animator's key is specified, the + * attributes are applied with the animator. Otherwise, they are applied + * immediately to the selection. + * + * The animation will not animate during auto-resize renders. + * + * @param {D3.Selection} selection The selection of elements to update. + * @param {string} animatorKey The key for the animator. + * @param {IAttributeToProjector} attrToProjector The set of attributes to set on the selection. + * @returns {D3.Selection} The resulting selection (potentially after the transition) + */ + _applyAnimatedAttributes(selection: any, animatorKey: string, attrToProjector: IAttributeToProjector): any; + /** + * Get the animator associated with the specified Animator key. + * + * @return {IPlotAnimator} The Animator for the specified key. + */ + animator(animatorKey: string): Animator.IPlotAnimator; + /** + * Set the animator associated with the specified Animator key. + * + * @param {string} animatorKey The key for the Animator. + * @param {IPlotAnimator} animator An Animator to be assigned to + * the specified key. + * @returns {Plot} The calling Plot. + */ + animator(animatorKey: string, animator: Animator.IPlotAnimator): Plot; + } + } +} + + +declare module Plottable { + module Plot { + class Pie extends Abstract.Plot { + _key2DatasetDrawerKey: D3.Map; + _datasetKeysInOrder: string[]; + /** + * Constructs a PiePlot. + * + * @constructor + */ + constructor(); + _setup(): void; + _computeLayout(xOffset?: number, yOffset?: number, availableWidth?: number, availableHeight?: number): void; + /** + * Adds a dataset to this plot. Only one dataset can be added to a PiePlot. + * + * A key is automatically generated if not supplied. + * + * @param {string} [key] The key of the dataset. + * @param {any[]|Dataset} dataset dataset to add. + * @returns {Pie} The calling PiePlot. + */ + addDataset(key: string, dataset: Dataset): Pie; + addDataset(key: string, dataset: any[]): Pie; + addDataset(dataset: Dataset): Pie; + addDataset(dataset: any[]): Pie; + _addDataset(key: string, dataset: Dataset): void; + /** + * Removes a dataset + * + * @param {string} key The key of the dataset + * @returns {Pie} The calling PiePlot. + */ + removeDataset(key: string): Pie; + _generateAttrToProjector(): IAttributeToProjector; + _getAnimator(drawer: Abstract._Drawer, index: number): Animator.IPlotAnimator; + _getDrawer(key: string): Abstract._Drawer; + _getDatasetsInOrder(): Dataset[]; + _getDrawersInOrder(): Abstract._Drawer[]; + _updateScaleExtent(attr: string): void; + _paint(): void; + } + } +} + + +declare module Plottable { + module Abstract { + class XYPlot extends Plot { + _xScale: Scale; + _yScale: Scale; + /** + * Constructs an XYPlot. + * + * An XYPlot is a plot from drawing 2-dimensional data. Common examples + * include Scale.Line and Scale.Bar. + * + * @constructor + * @param {any[]|Dataset} [dataset] The data or Dataset to be associated with this Renderer. + * @param {Scale} xScale The x scale to use. + * @param {Scale} yScale The y scale to use. + */ + constructor(dataset: any, xScale: Scale, yScale: Scale); + /** + * @param {string} attrToSet One of ["x", "y"] which determines the point's + * x and y position in the Plot. + */ + project(attrToSet: string, accessor: any, scale?: Scale): XYPlot; + _computeLayout(xOffset?: number, yOffset?: number, availableWidth?: number, availableHeight?: number): void; + _updateXDomainer(): void; + _updateYDomainer(): void; + } + } +} + + +declare module Plottable { + module Abstract { + class NewStylePlot extends XYPlot { + _key2DatasetDrawerKey: D3.Map; + _datasetKeysInOrder: string[]; + /** + * Constructs a NewStylePlot. + * + * Plots render data. Common example include Plot.Scatter, Plot.Bar, and Plot.Line. + * + * A bare Plot has a DataSource and any number of projectors, which take + * data and "project" it onto the Plot, such as "x", "y", "fill", "r". + * + * @constructor + * @param [Scale] xScale The x scale to use + * @param [Scale] yScale The y scale to use + */ + constructor(xScale?: Scale, yScale?: Scale); + _setup(): void; + remove(): void; + /** + * Adds a dataset to this plot. Identify this dataset with a key. + * + * A key is automatically generated if not supplied. + * + * @param {string} [key] The key of the dataset. + * @param {any[]|Dataset} dataset dataset to add. + * @returns {NewStylePlot} The calling NewStylePlot. + */ + addDataset(key: string, dataset: Dataset): NewStylePlot; + addDataset(key: string, dataset: any[]): NewStylePlot; + addDataset(dataset: Dataset): NewStylePlot; + addDataset(dataset: any[]): NewStylePlot; + _addDataset(key: string, dataset: Dataset): void; + _getDrawer(key: string): _Drawer; + _getAnimator(drawer: _Drawer, index: number): Animator.IPlotAnimator; + _updateScaleExtent(attr: string): void; + /** + * Gets the dataset order by key + * + * @returns {string[]} A string array of the keys in order + */ + datasetOrder(): string[]; + /** + * Sets the dataset order by key + * + * @param {string[]} order If provided, a string array which represents the order of the keys. + * This must be a permutation of existing keys. + * + * @returns {NewStylePlot} The calling NewStylePlot. + */ + datasetOrder(order: string[]): NewStylePlot; + /** + * Removes a dataset + * + * @param {string} key The key of the dataset + * @return {NewStylePlot} The calling NewStylePlot. + */ + removeDataset(key: string): NewStylePlot; + _getDatasetsInOrder(): Dataset[]; + _getDrawersInOrder(): _Drawer[]; + _paint(): void; + } + } +} + + +declare module Plottable { + module Plot { + class Scatter extends Abstract.XYPlot { + _animators: Animator.IPlotAnimatorMap; + /** + * Constructs a ScatterPlot. + * + * @constructor + * @param {IDataset | any} dataset The dataset to render. + * @param {Scale} xScale The x scale to use. + * @param {Scale} yScale The y scale to use. + */ + constructor(dataset: any, xScale: Abstract.Scale, yScale: Abstract.Scale); + /** + * @param {string} attrToSet One of ["x", "y", "cx", "cy", "r", + * "fill"]. "cx" and "cy" are aliases for "x" and "y". "r" is the datum's + * radius, and "fill" is the CSS color of the datum. + */ + project(attrToSet: string, accessor: any, scale?: Abstract.Scale): Scatter; + _paint(): void; + } + } +} + + +declare module Plottable { + module Plot { + class Grid extends Abstract.XYPlot { + _colorScale: Abstract.Scale; + _xScale: Scale.Ordinal; + _yScale: Scale.Ordinal; + _animators: Animator.IPlotAnimatorMap; + /** + * Constructs a GridPlot. + * + * A GridPlot is used to shade a grid of data. Each datum is a cell on the + * grid, and the datum can control what color it is. + * + * @constructor + * @param {IDataset | any} dataset The dataset to render. + * @param {Scale.Ordinal} xScale The x scale to use. + * @param {Scale.Ordinal} yScale The y scale to use. + * @param {Scale.Color|Scale.InterpolatedColor} colorScale The color scale + * to use for each grid cell. + */ + constructor(dataset: any, xScale: Scale.Ordinal, yScale: Scale.Ordinal, colorScale: Abstract.Scale); + /** + * @param {string} attrToSet One of ["x", "y", "fill"]. If "fill" is used, + * the data should return a valid CSS color. + */ + project(attrToSet: string, accessor: any, scale?: Abstract.Scale): Grid; + _paint(): void; + } + } +} + + +declare module Plottable { + module Abstract { + class BarPlot extends XYPlot { + _bars: D3.UpdateSelection; + _baseline: D3.Selection; + _baselineValue: number; + _barAlignmentFactor: number; + static _BarAlignmentToFactor: { + [x: string]: number; + }; + _isVertical: boolean; + _animators: Animator.IPlotAnimatorMap; + /** + * Constructs an AbstractBarPlot. + * + * @constructor + * @param {IDataset | any} dataset The dataset to render. + * @param {Scale} xScale The x scale to use. + * @param {Scale} yScale The y scale to use. + */ + constructor(dataset: any, xScale: Scale, yScale: Scale); + _setup(): void; + _paint(): void; + /** + * Sets the baseline for the bars to the specified value. + * + * The baseline is the line that the bars are drawn from, defaulting to 0. + * + * @param {number} value The value to position the baseline at. + * @returns {AbstractBarPlot} The calling AbstractBarPlot. + */ + baseline(value: number): BarPlot; + /** + * Sets the bar alignment relative to the independent axis. + * VerticalBarPlot supports "left", "center", "right" + * HorizontalBarPlot supports "top", "center", "bottom" + * + * @param {string} alignment The desired alignment. + * @returns {AbstractBarPlot} The calling AbstractBarPlot. + */ + barAlignment(alignment: string): BarPlot; + /** + * Selects the bar under the given pixel position (if [xValOrExtent] + * and [yValOrExtent] are {number}s), under a given line (if only one + * of [xValOrExtent] or [yValOrExtent] are {IExtent}s) or are under a + * 2D area (if [xValOrExtent] and [yValOrExtent] are both {IExtent}s). + * + * @param {any} xValOrExtent The pixel x position, or range of x values. + * @param {any} yValOrExtent The pixel y position, or range of y values. + * @param {boolean} [select] Whether or not to select the bar (by classing it "selected"); + * @returns {D3.Selection} The selected bar, or null if no bar was selected. + */ + selectBar(xValOrExtent: IExtent, yValOrExtent: IExtent, select?: boolean): D3.Selection; + selectBar(xValOrExtent: number, yValOrExtent: IExtent, select?: boolean): D3.Selection; + selectBar(xValOrExtent: IExtent, yValOrExtent: number, select?: boolean): D3.Selection; + selectBar(xValOrExtent: number, yValOrExtent: number, select?: boolean): D3.Selection; + /** + * Deselects all bars. + * @returns {AbstractBarPlot} The calling AbstractBarPlot. + */ + deselectAll(): BarPlot; + _updateDomainer(scale: Scale): void; + _updateYDomainer(): void; + _updateXDomainer(): void; + _generateAttrToProjector(): IAttributeToProjector; + } + } +} + + +declare module Plottable { + module Plot { + /** + * A VerticalBarPlot draws bars vertically. + * Key projected attributes: + * - "width" - the horizontal width of a bar. + * - if an ordinal scale is attached, this defaults to ordinalScale.rangeBand() + * - if a quantitative scale is attached, this defaults to 10 + * - "x" - the horizontal position of a bar + * - "y" - the vertical height of a bar + */ + class VerticalBar extends Abstract.BarPlot { + static _BarAlignmentToFactor: { + [x: string]: number; + }; + /** + * Constructs a VerticalBarPlot. + * + * @constructor + * @param {IDataset | any} dataset The dataset to render. + * @param {Scale} xScale The x scale to use. + * @param {QuantitativeScale} yScale The y scale to use. + */ + constructor(dataset: any, xScale: Abstract.Scale, yScale: Abstract.QuantitativeScale); + _updateYDomainer(): void; + } + } +} + + +declare module Plottable { + module Plot { + /** + * A HorizontalBarPlot draws bars horizontally. + * Key projected attributes: + * - "width" - the vertical height of a bar (since the bar is rotated horizontally) + * - if an ordinal scale is attached, this defaults to ordinalScale.rangeBand() + * - if a quantitative scale is attached, this defaults to 10 + * - "x" - the horizontal length of a bar + * - "y" - the vertical position of a bar + */ + class HorizontalBar extends Abstract.BarPlot { + static _BarAlignmentToFactor: { + [x: string]: number; + }; + /** + * Constructs a HorizontalBarPlot. + * + * @constructor + * @param {IDataset | any} dataset The dataset to render. + * @param {QuantitativeScale} xScale The x scale to use. + * @param {Scale} yScale The y scale to use. + */ + constructor(dataset: any, xScale: Abstract.QuantitativeScale, yScale: Abstract.Scale); + _updateXDomainer(): void; + _generateAttrToProjector(): IAttributeToProjector; + } + } +} + + +declare module Plottable { + module Plot { + class Line extends Abstract.XYPlot { + _yScale: Abstract.QuantitativeScale; + _animators: Animator.IPlotAnimatorMap; + /** + * Constructs a LinePlot. + * + * @constructor + * @param {any | IDataset} dataset The dataset to render. + * @param {QuantitativeScale} xScale The x scale to use. + * @param {QuantitativeScale} yScale The y scale to use. + */ + constructor(dataset: any, xScale: Abstract.QuantitativeScale, yScale: Abstract.QuantitativeScale); + _setup(): void; + _appendPath(): void; + _getResetYFunction(): (d: any, i: number) => number; + _generateAttrToProjector(): IAttributeToProjector; + _paint(): void; + _wholeDatumAttributes(): string[]; + } + } +} + + +declare module Plottable { + module Plot { + /** + * An AreaPlot draws a filled region (area) between the plot's projected "y" and projected "y0" values. + */ + class Area extends Line { + /** + * Constructs an AreaPlot. + * + * @constructor + * @param {IDataset | any} dataset The dataset to render. + * @param {QuantitativeScale} xScale The x scale to use. + * @param {QuantitativeScale} yScale The y scale to use. + */ + constructor(dataset: any, xScale: Abstract.QuantitativeScale, yScale: Abstract.QuantitativeScale); + _appendPath(): void; + _onDatasetUpdate(): void; + _updateYDomainer(): void; + project(attrToSet: string, accessor: any, scale?: Abstract.Scale): Area; + _getResetYFunction(): IAppliedAccessor; + _paint(): void; + _wholeDatumAttributes(): string[]; + } + } +} + + +declare module Plottable { + module Abstract { + class NewStyleBarPlot extends NewStylePlot { + static _barAlignmentToFactor: { + [x: string]: number; + }; + _baseline: D3.Selection; + _baselineValue: number; + _barAlignmentFactor: number; + _isVertical: boolean; + _animators: Animator.IPlotAnimatorMap; + /** + * Constructs a NewStyleBarPlot. + * + * @constructor + * @param {Scale} xScale The x scale to use. + * @param {Scale} yScale The y scale to use. + */ + constructor(xScale: Scale, yScale: Scale); + _getDrawer(key: string): _Drawer.Rect; + _setup(): void; + _paint(): void; + /** + * Sets the baseline for the bars to the specified value. + * + * The baseline is the line that the bars are drawn from, defaulting to 0. + * + * @param {number} value The value to position the baseline at. + * @returns {NewStyleBarPlot} The calling NewStyleBarPlot. + */ + baseline(value: number): any; + _updateDomainer(scale: Scale): any; + _generateAttrToProjector(): IAttributeToProjector; + _updateXDomainer(): any; + _updateYDomainer(): any; + } + } +} + + +declare module Plottable { + module Plot { + class ClusteredBar extends Abstract.NewStyleBarPlot { + /** + * Creates a ClusteredBarPlot. + * + * A ClusteredBarPlot is a plot that plots several bar plots next to each + * other. For example, when plotting life expectancy across each country, + * you would want each country to have a "male" and "female" bar. + * + * @constructor + * @param {Scale} xScale The x scale to use. + * @param {Scale} yScale The y scale to use. + */ + constructor(xScale: Abstract.Scale, yScale: Abstract.Scale, isVertical?: boolean); + _generateAttrToProjector(): IAttributeToProjector; + _paint(): void; + } + } +} + + +declare module Plottable { + module Abstract { + class Stacked extends NewStylePlot { + _isVertical: boolean; + _onDatasetUpdate(): void; + _updateScaleExtents(): void; + } + } +} + + +declare module Plottable { + module Plot { + class StackedArea extends Abstract.Stacked { + _baseline: D3.Selection; + _baselineValue: number; + /** + * Constructs a StackedArea plot. + * + * @constructor + * @param {QuantitativeScale} xScale The x scale to use. + * @param {QuantitativeScale} yScale The y scale to use. + */ + constructor(xScale: Abstract.QuantitativeScale, yScale: Abstract.QuantitativeScale); + _getDrawer(key: string): _Drawer.Area; + _setup(): void; + _paint(): void; + _updateYDomainer(): void; + _onDatasetUpdate(): void; + _generateAttrToProjector(): IAttributeToProjector; + } + } +} + + +declare module Plottable { + module Plot { + class StackedBar extends Abstract.Stacked { + _baselineValue: number; + _baseline: D3.Selection; + _barAlignmentFactor: number; + /** + * Constructs a StackedBar plot. + * A StackedBarPlot is a plot that plots several bar plots stacking on top of each + * other. + * @constructor + * @param {Scale} xScale the x scale of the plot. + * @param {Scale} yScale the y scale of the plot. + * @param {boolean} isVertical if the plot if vertical. + */ + constructor(xScale?: Abstract.Scale, yScale?: Abstract.Scale, isVertical?: boolean); + _setup(): void; + _getAnimator(drawer: Abstract._Drawer, index: number): Animator.Rect; + _getDrawer(key: string): any; + _generateAttrToProjector(): any; + _paint(): void; + baseline(value: number): any; + _updateDomainer(scale: Abstract.Scale): any; + _updateXDomainer(): any; + _updateYDomainer(): any; + } + } +} + + +declare module Plottable { + module Animator { + interface IPlotAnimator { + /** + * Applies the supplied attributes to a D3.Selection with some animation. + * + * @param {D3.Selection} selection The update selection or transition selection that we wish to animate. + * @param {IAttributeToProjector} attrToProjector The set of + * IAccessors that we will use to set attributes on the selection. + * @return {D3.Selection} Animators should return the selection or + * transition object so that plots may chain the transitions between + * animators. + */ + animate(selection: any, attrToProjector: IAttributeToProjector): D3.Selection; + } + interface IPlotAnimatorMap { + [animatorKey: string]: IPlotAnimator; + } + } +} + + +declare module Plottable { + module Animator { + /** + * An animator implementation with no animation. The attributes are + * immediately set on the selection. + */ + class Null implements IPlotAnimator { + animate(selection: any, attrToProjector: IAttributeToProjector): D3.Selection; + } + } +} + + +declare module Plottable { + module Animator { + /** + * The base animator implementation with easing, duration, and delay. + */ + class Base implements IPlotAnimator { + /** + * The default duration of the animation in milliseconds + */ + static DEFAULT_DURATION_MILLISECONDS: number; + /** + * The default starting delay of the animation in milliseconds + */ + static DEFAULT_DELAY_MILLISECONDS: number; + /** + * The default easing of the animation + */ + static DEFAULT_EASING: string; + /** + * Constructs the default animator + * + * @constructor + */ + constructor(); + animate(selection: any, attrToProjector: IAttributeToProjector): D3.Selection; + /** + * Gets the duration of the animation in milliseconds. + * + * @returns {number} The current duration. + */ + duration(): number; + /** + * Sets the duration of the animation in milliseconds. + * + * @param {number} duration The duration in milliseconds. + * @returns {Default} The calling Default Animator. + */ + duration(duration: number): Base; + /** + * Gets the delay of the animation in milliseconds. + * + * @returns {number} The current delay. + */ + delay(): number; + /** + * Sets the delay of the animation in milliseconds. + * + * @param {number} delay The delay in milliseconds. + * @returns {Default} The calling Default Animator. + */ + delay(delay: number): Base; + /** + * Gets the current easing of the animation. + * + * @returns {string} the current easing mode. + */ + easing(): string; + /** + * Sets the easing mode of the animation. + * + * @param {string} easing The desired easing mode. + * @returns {Default} The calling Default Animator. + */ + easing(easing: string): Base; + } + } +} + + +declare module Plottable { + module Animator { + /** + * An animator that delays the animation of the attributes using the index + * of the selection data. + * + * The delay between animations can be configured with the .delay getter/setter. + */ + class IterativeDelay extends Base { + /** + * The start delay between each start of an animation + */ + static DEFAULT_ITERATIVE_DELAY_MILLISECONDS: number; + /** + * Constructs an animator with a start delay between each selection animation + * + * @constructor + */ + constructor(); + animate(selection: any, attrToProjector: IAttributeToProjector): D3.Selection; + /** + * Gets the start delay between animations in milliseconds. + * + * @returns {number} The current iterative delay. + */ + iterativeDelay(): number; + /** + * Sets the start delay between animations in milliseconds. + * + * @param {number} iterDelay The iterative delay in milliseconds. + * @returns {IterativeDelay} The calling IterativeDelay Animator. + */ + iterativeDelay(iterDelay: number): IterativeDelay; + } + } +} + + +declare module Plottable { + module Animator { + /** + * The default animator implementation with easing, duration, and delay. + */ + class Rect extends Base { + static ANIMATED_ATTRIBUTES: string[]; + isVertical: boolean; + isReverse: boolean; + constructor(isVertical?: boolean, isReverse?: boolean); + animate(selection: any, attrToProjector: IAttributeToProjector): any; + _startMovingProjector(attrToProjector: IAttributeToProjector): IAppliedAccessor; + } + } +} + + +declare module Plottable { + module Core { + /** + * A function to be called when an event occurs. The argument is the d3 event + * generated by the event. + */ + interface IKeyEventListenerCallback { + (e: D3.D3Event): any; + } + /** + * A module for listening to keypresses on the document. + */ + module KeyEventListener { + /** + * Turns on key listening. + */ + function initialize(): void; + /** + * When a key event occurs with the key corresponding te keyCod, call cb. + * + * @param {number} keyCode The javascript key code to call cb on. + * @param {IKeyEventListener} cb Will be called when keyCode key event + * occurs. + */ + function addCallback(keyCode: number, cb: IKeyEventListenerCallback): void; + } + } +} + + +declare module Plottable { + module Abstract { + class Interaction extends PlottableObject { + /** + * It maintains a 'hitBox' which is where all event listeners are + * attached. Due to cross- browser weirdness, the hitbox needs to be an + * opaque but invisible rectangle. TODO: We should give the interaction + * "foreground" and "background" elements where it can draw things, + * e.g. crosshairs. + */ + _hitBox: D3.Selection; + _componentToListenTo: Component; + _anchor(component: Component, hitBox: D3.Selection): void; + } + } +} + + +declare module Plottable { + module Interaction { + class Click extends Abstract.Interaction { + _anchor(component: Abstract.Component, hitBox: D3.Selection): void; + _listenTo(): string; + /** + * Sets a callback to be called when a click is received. + * + * @param {(p: Point) => any} cb Callback that takes the pixel position of the click event. + */ + callback(cb: (p: Point) => any): Click; + } + class DoubleClick extends Click { + _listenTo(): string; + } + } +} + + +declare module Plottable { + module Interaction { + class Key extends Abstract.Interaction { + /** + * Creates a KeyInteraction. + * + * KeyInteraction listens to key events that occur while the component is + * moused over. + * + * @constructor + * @param {number} keyCode The key code to listen for. + */ + constructor(keyCode: number); + _anchor(component: Abstract.Component, hitBox: D3.Selection): void; + /** + * Sets a callback to be called when the designated key is pressed and the + * user is moused over the component. + * + * @param {() => any} cb Callback to be called. + * @returns The calling Key. + */ + callback(cb: () => any): Key; + } + } +} + + +declare module Plottable { + module Interaction { + class PanZoom extends Abstract.Interaction { + _xScale: Abstract.QuantitativeScale; + _yScale: Abstract.QuantitativeScale; + /** + * Creates a PanZoomInteraction. + * + * The allows you to move around and zoom in on a plot, interactively. It + * does so by changing the xScale and yScales' domains repeatedly. + * + * @constructor + * @param {QuantitativeScale} [xScale] The X scale to update on panning/zooming. + * @param {QuantitativeScale} [yScale] The Y scale to update on panning/zooming. + */ + constructor(xScale?: Abstract.QuantitativeScale, yScale?: Abstract.QuantitativeScale); + /** + * Sets the scales back to their original domains. + */ + resetZoom(): void; + _anchor(component: Abstract.Component, hitBox: D3.Selection): void; + } + } +} + + +declare module Plottable { + module Interaction { + class BarHover extends Abstract.Interaction { + _componentToListenTo: Abstract.BarPlot; + _anchor(barPlot: Abstract.BarPlot, hitBox: D3.Selection): void; + /** + * Gets the current hover mode. + * + * @return {string} The current hover mode. + */ + hoverMode(): string; + /** + * Sets the hover mode for the interaction. There are two modes: + * - "point": Selects the bar under the mouse cursor (default). + * - "line" : Selects any bar that would be hit by a line extending + * in the same direction as the bar and passing through + * the cursor. + * + * @param {string} mode If provided, the desired hover mode. + * @return {BarHover} The calling BarHover. + */ + hoverMode(mode: string): BarHover; + /** + * Attaches an callback to be called when the user mouses over a bar. + * + * @param {(datum: any, bar: D3.Selection) => any} callback The callback to be called. + * The callback will be passed the data from the hovered-over bar. + * @return {BarHover} The calling BarHover. + */ + onHover(callback: (datum: any, bar: D3.Selection) => any): BarHover; + /** + * Attaches a callback to be called when the user mouses off of a bar. + * + * @param {(datum: any, bar: D3.Selection) => any} callback The callback to be called. + * The callback will be passed the data from the last-hovered bar. + * @return {BarHover} The calling BarHover. + */ + onUnhover(callback: (datum: any, bar: D3.Selection) => any): BarHover; + } + } +} + + +declare module Plottable { + module Interaction { + class Drag extends Abstract.Interaction { + _origin: number[]; + _location: number[]; + /** + * Constructs a Drag. A Drag will signal its callbacks on mouse drag. + */ + constructor(); + /** + * Gets the callback that is called when dragging starts. + * + * @returns {(startLocation: Point) => void} The callback called when dragging starts. + */ + dragstart(): (startLocation: Point) => void; + /** + * Sets the callback to be called when dragging starts. + * + * @param {(startLocation: Point) => any} cb If provided, the function to be called. Takes in a Point in pixels. + * @returns {Drag} The calling Drag. + */ + dragstart(cb: (startLocation: Point) => any): Drag; + /** + * Gets the callback that is called during dragging. + * + * @returns {(startLocation: Point, endLocation: Point) => void} The callback called during dragging. + */ + drag(): (startLocation: Point, endLocation: Point) => void; + /** + * Adds a callback to be called during dragging. + * + * @param {(startLocation: Point, endLocation: Point) => any} cb If provided, the function to be called. Takes in Points in pixels. + * @returns {Drag} The calling Drag. + */ + drag(cb: (startLocation: Point, endLocation: Point) => any): Drag; + /** + * Gets the callback that is called when dragging ends. + * + * @returns {(startLocation: Point, endLocation: Point) => void} The callback called when dragging ends. + */ + dragend(): (startLocation: Point, endLocation: Point) => void; + /** + * Adds a callback to be called when the dragging ends. + * + * @param {(startLocation: Point, endLocation: Point) => any} cb If provided, the function to be called. Takes in Points in pixels. + * @returns {Drag} The calling Drag. + */ + dragend(cb: (startLocation: Point, endLocation: Point) => any): Drag; + _dragstart(): void; + _doDragstart(): void; + _drag(): void; + _doDrag(): void; + _dragend(): void; + _doDragend(): void; + _anchor(component: Abstract.Component, hitBox: D3.Selection): Drag; + /** + * Sets up so that the xScale and yScale that are passed have their + * domains automatically changed as you zoom. + * + * @param {QuantitativeScale} xScale The scale along the x-axis. + * @param {QuantitativeScale} yScale The scale along the y-axis. + * @returns {Drag} The calling Drag. + */ + setupZoomCallback(xScale?: Abstract.QuantitativeScale, yScale?: Abstract.QuantitativeScale): Drag; + } + } +} + + +declare module Plottable { + module Interaction { + /** + * A DragBox is an interaction that automatically draws a box across the + * element you attach it to when you drag. + */ + class DragBox extends Drag { + /** + * The DOM element of the box that is drawn. When no box is drawn, it is + * null. + */ + dragBox: D3.Selection; + /** + * Whether or not dragBox has been rendered in a visible area. + */ + boxIsDrawn: boolean; + _dragstart(): void; + /** + * Clears the highlighted drag-selection box drawn by the DragBox. + * + * @returns {DragBox} The calling DragBox. + */ + clearBox(): DragBox; + /** + * Set where the box is draw explicitly. + * + * @param {number} x0 Left. + * @param {number} x1 Right. + * @param {number} y0 Top. + * @param {number} y1 Bottom. + * + * @returns {DragBox} The calling DragBox. + */ + setBox(x0: number, x1: number, y0: number, y1: number): DragBox; + _anchor(component: Abstract.Component, hitBox: D3.Selection): DragBox; + } + } +} + + +declare module Plottable { + module Interaction { + class XDragBox extends DragBox { + _drag(): void; + setBox(x0: number, x1: number): XDragBox; + } + } +} + + +declare module Plottable { + module Interaction { + class XYDragBox extends DragBox { + _drag(): void; + } + } +} + + +declare module Plottable { + module Interaction { + class YDragBox extends DragBox { + _drag(): void; + setBox(y0: number, y1: number): YDragBox; + } + } +} + + +declare module Plottable { + module Abstract { + class Dispatcher extends PlottableObject { + _target: D3.Selection; + _event2Callback: { + [x: string]: () => any; + }; + /** + * Constructs a Dispatcher with the specified target. + * + * @param {D3.Selection} target The selection to listen for events on. + */ + constructor(target: D3.Selection); + /** + * Gets the target of the Dispatcher. + * + * @returns {D3.Selection} The Dispatcher's current target. + */ + target(): D3.Selection; + /** + * Sets the target of the Dispatcher. + * + * @param {D3.Selection} target The element to listen for updates on. + * @returns {Dispatcher} The calling Dispatcher. + */ + target(targetElement: D3.Selection): Dispatcher; + /** + * Attaches the Dispatcher's listeners to the Dispatcher's target element. + * + * @returns {Dispatcher} The calling Dispatcher. + */ + connect(): Dispatcher; + /** + * Detaches the Dispatcher's listeners from the Dispatchers' target element. + * + * @returns {Dispatcher} The calling Dispatcher. + */ + disconnect(): Dispatcher; + } + } +} + + +declare module Plottable { + module Dispatcher { + class Mouse extends Abstract.Dispatcher { + /** + * Constructs a Mouse Dispatcher with the specified target. + * + * @param {D3.Selection} target The selection to listen for events on. + */ + constructor(target: D3.Selection); + /** + * Gets the current callback to be called on mouseover. + * + * @return {(location: Point) => any} The current mouseover callback. + */ + mouseover(): (location: Point) => any; + /** + * Attaches a callback to be called on mouseover. + * + * @param {(location: Point) => any} callback A function that takes the pixel position of the mouse event. + * Pass in null to remove the callback. + * @return {Mouse} The calling Mouse Handler. + */ + mouseover(callback: (location: Point) => any): Mouse; + /** + * Gets the current callback to be called on mousemove. + * + * @return {(location: Point) => any} The current mousemove callback. + */ + mousemove(): (location: Point) => any; + /** + * Attaches a callback to be called on mousemove. + * + * @param {(location: Point) => any} callback A function that takes the pixel position of the mouse event. + * Pass in null to remove the callback. + * @return {Mouse} The calling Mouse Handler. + */ + mousemove(callback: (location: Point) => any): Mouse; + /** + * Gets the current callback to be called on mouseout. + * + * @return {(location: Point) => any} The current mouseout callback. + */ + mouseout(): (location: Point) => any; + /** + * Attaches a callback to be called on mouseout. + * + * @param {(location: Point) => any} callback A function that takes the pixel position of the mouse event. + * Pass in null to remove the callback. + * @return {Mouse} The calling Mouse Handler. + */ + mouseout(callback: (location: Point) => any): Mouse; + } + } +} diff --git a/plottable.css b/plottable.css index 2d03aff277..daf7ff55d2 100644 --- a/plottable.css +++ b/plottable.css @@ -14,6 +14,19 @@ svg.plottable { fill: #32313F; } +.plottable .bar-label-text-area text { + font-family: "Helvetica Neue", sans-serif; + font-size: 14px; +} + +.plottable .light-label text { + fill: white; +} + +.plottable .dark-label text { + fill: #32313F; +} + .plottable .axis-label text { font-size: 10px; font-weight: bold; diff --git a/plottable.d.ts b/plottable.d.ts index bc4bdb61cc..585b43e58e 100644 --- a/plottable.d.ts +++ b/plottable.d.ts @@ -98,10 +98,40 @@ declare module Plottable { * with ===. */ function objEq(a: any, b: any): boolean; - function max(arr: number[], default_val?: number): number; - function max(arr: T[], acc: (x: T) => number, default_val?: number): number; - function min(arr: number[], default_val?: number): number; - function min(arr: T[], acc: (x: T) => number, default_val?: number): number; + /** + * Computes the max value from the array. + * + * If type is not comparable then t will be converted to a comparable before computing max. + */ + function max(arr: C[], default_val: C): C; + function max(arr: T[], acc: (x?: T, i?: number) => C, default_val: C): C; + /** + * Computes the min value from the array. + * + * If type is not comparable then t will be converted to a comparable before computing min. + */ + function min(arr: C[], default_val: C): C; + function min(arr: T[], acc: (x?: T, i?: number) => C, default_val: C): C; + /** + * Creates shallow copy of map. + * @param {{ [key: string]: any }} oldMap Map to copy + * + * @returns {[{ [key: string]: any }} coppied map. + */ + function copyMap(oldMap: { + [x: string]: T; + }): { + [x: string]: T; + }; + function range(start: number, stop: number, step?: number): number[]; + /** 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: Function, time: number, ...args: any[]): number; } } } @@ -397,6 +427,22 @@ declare module Plottable { } +declare module Plottable { + module _Util { + module Color { + /** + * 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: string, b: string): number; + } + } +} + + declare module Plottable { interface Formatter { (d: any): string; @@ -781,14 +827,6 @@ declare module Plottable { } declare module Plottable { - interface DatasetInterface { - data: any[]; - metadata: Metadata; - } - interface Metadata { - cssClass?: string; - color?: string; - } interface _Accessor { (datum: any, index?: number, metadata?: any): any; } @@ -1085,6 +1123,7 @@ declare module Plottable { _userSetDomainer: boolean; _domainer: Domainer; _typeCoercer: (d: any) => number; + _tickGenerator: TickGenerators.TickGenerator; /** * Constructs a new QuantitativeScale. * @@ -1127,6 +1166,10 @@ declare module Plottable { * @param {number[]} values The new range value for the range. */ rangeRound(values: number[]): AbstractQuantitative; + /** + * Gets ticks generated by the default algorithm. + */ + getDefaultTicks(): D[]; /** * Gets the clamp status of the QuantitativeScale (whether to cut off values outside the ouput range). * @@ -1143,12 +1186,9 @@ declare module Plottable { /** * Gets a set of tick values spanning the domain. * - * @param {number} [count] The approximate number of ticks to generate. - * If not supplied, the number specified by - * numTicks() is used instead. * @returns {any[]} The generated ticks. */ - ticks(count?: number): any[]; + ticks(): any[]; /** * Gets the default number of ticks. * @@ -1187,6 +1227,19 @@ declare module Plottable { */ domainer(domainer: Domainer): AbstractQuantitative; _defaultExtent(): any[]; + /** + * Gets the tick generator of the AbstractQuantitative. + * + * @returns {TickGenerator} The current tick generator. + */ + tickGenerator(): TickGenerators.TickGenerator; + /** + * Sets a tick generator + * + * @param {TickGenerator} generator, the new tick generator. + * @return {AbstractQuantitative} The calling AbstractQuantitative. + */ + tickGenerator(generator: TickGenerators.TickGenerator): AbstractQuantitative; } } } @@ -1474,11 +1527,48 @@ declare module Plottable { } +declare module Plottable { + module Scale { + module TickGenerators { + interface TickGenerator { + (scale: AbstractQuantitative): D[]; + } + /** + * Creates a tick generator using the specified interval. + * + * Generates ticks at multiples of the interval while also including the domain boundaries. + * + * @param {number} interval The interval between two ticks (not including the end ticks). + * + * @returns {TickGenerator} A tick generator using the specified interval. + */ + function intervalTickGenerator(interval: number): TickGenerator; + } + } +} + + declare module Plottable { module _Drawer { + /** + * A step for the drawer to draw. + * + * Specifies how AttributeToProjector needs to be animated. + */ + interface DrawStep { + attrToProjector: AttributeToProjector; + animator: Animator.PlotAnimator; + } class AbstractDrawer { _renderArea: D3.Selection; + _className: string; key: string; + /** + * Sets the class, which needs to be applied to bound elements. + * + * @param{string} className The class name to be applied. + */ + setClass(className: string): AbstractDrawer; /** * Constructs a Drawer * @@ -1486,17 +1576,31 @@ declare module Plottable { * @param{string} key The key associated with this Drawer */ constructor(key: string); + setup(area: D3.Selection): void; /** * Removes the Drawer and its renderArea */ remove(): void; /** - * Draws the data into the renderArea using the attrHash for attributes + * Enter new data to render area and creates binding * * @param{any[]} data The data to be drawn - * @param{attrHash} AttributeToProjector The list of attributes to set on the data */ - draw(data: any[], attrToProjector: AttributeToProjector, animator?: Animator.Null): void; + _enterData(data: any[]): void; + /** + * Draws data using one step + * + * @param{DataStep} step The step, how data should be drawn. + */ + _drawStep(step: DrawStep): void; + _numberOfAnimationIterations(data: any[]): number; + /** + * Draws the data into the renderArea using the spefic steps + * + * @param{any[]} data The data to be drawn + * @param{DrawStep[]} drawSteps The list of steps, which needs to be drawn + */ + draw(data: any[], drawSteps: DrawStep[]): number; } } } @@ -1504,8 +1608,28 @@ declare module Plottable { declare module Plottable { module _Drawer { - class Arc extends AbstractDrawer { - draw(data: any[], attrToProjector: AttributeToProjector, animator?: Animator.Null): void; + class Line extends AbstractDrawer { + _enterData(data: any[]): void; + setup(area: D3.Selection): void; + _numberOfAnimationIterations(data: any[]): number; + _drawStep(step: DrawStep): void; + } + } +} + + +declare module Plottable { + module _Drawer { + class Area extends Line { + _enterData(data: any[]): void; + /** + * Sets the value determining if line should be drawn. + * + * @param{boolean} draw The value determing if line should be drawn. + */ + drawLine(draw: boolean): Area; + setup(area: D3.Selection): void; + _drawStep(step: DrawStep): void; } } } @@ -1513,8 +1637,17 @@ declare module Plottable { declare module Plottable { module _Drawer { - class Area extends AbstractDrawer { - draw(data: any[], attrToProjector: AttributeToProjector): void; + class Element extends AbstractDrawer { + _svgElement: string; + /** + * Sets the svg element, which needs to be bind to data + * + * @param{string} tag The svg element to be bind + */ + svgElement(tag: string): Element; + _getDrawSelection(): D3.Selection; + _drawStep(step: DrawStep): void; + _enterData(data: any[]): void; } } } @@ -1522,8 +1655,24 @@ declare module Plottable { declare module Plottable { module _Drawer { - class Rect extends AbstractDrawer { - draw(data: any[], attrToProjector: AttributeToProjector, animator?: Animator.Null): void; + class Rect extends Element { + _someLabelsTooWide: boolean; + _isVertical: boolean; + constructor(key: string, isVertical: boolean); + setup(area: D3.Selection): void; + removeLabels(): void; + drawText(data: any[], attrToProjector: AttributeToProjector): void; + } + } +} + + +declare module Plottable { + module _Drawer { + class Arc extends Element { + constructor(key: string); + _drawStep(step: DrawStep): void; + draw(data: any[], drawSteps: DrawStep[]): number; } } } @@ -2374,8 +2523,22 @@ declare module Plottable { * Sets the layout weight of a particular row. * Space is allocated to rows based on their weight. Rows with higher weights receive proportionally more space. * - * A common case would be to have one graph take up 2/3rds of the space, - * and the other graph take up 1/3rd. + * A common case would be to have one row take up 2/3rds of the space, + * and the other row take up 1/3rd. + * + * Example: + * + * ```JavaScript + * plot = new Plottable.Component.Table([ + * [row1], + * [row2] + * ]); + * + * // assign twice as much space to the first row + * plot + * .rowWeight(0, 2) + * .rowWeight(1, 1) + * ``` * * @param {number} index The index of the row. * @param {number} weight The weight to be set on the row. @@ -2386,8 +2549,7 @@ declare module Plottable { * Sets the layout weight of a particular column. * Space is allocated to columns based on their weight. Columns with higher weights receive proportionally more space. * - * A common case would be to have one graph take up 2/3rds of the space, - * and the other graph take up 1/3rd. + * Please see `rowWeight` docs for an example. * * @param {number} index The index of the column. * @param {number} weight The weight to be set on the column. @@ -2414,6 +2576,7 @@ declare module Plottable { _animate: boolean; _animators: Animator.PlotAnimatorMap; _ANIMATION_DURATION: number; + _animateOnNextRender: boolean; /** * Constructs a Plot. * @@ -2444,7 +2607,7 @@ declare module Plottable { addDataset(dataset: any[]): AbstractPlot; _addDataset(key: string, dataset: Dataset): void; _getDrawer(key: string): _Drawer.AbstractDrawer; - _getAnimator(drawer: _Drawer.AbstractDrawer, index: number): Animator.PlotAnimator; + _getAnimator(key: string): Animator.PlotAnimator; _onDatasetUpdate(): void; /** * Sets an attribute of every data point. @@ -2489,21 +2652,6 @@ declare module Plottable { */ _updateScaleExtents(): void; _updateScaleExtent(attr: string): void; - /** - * Applies attributes to the selection. - * - * If animation is enabled and a valid animator's key is specified, the - * attributes are applied with the animator. Otherwise, they are applied - * immediately to the selection. - * - * The animation will not animate during auto-resize renders. - * - * @param {D3.Selection} selection The selection of elements to update. - * @param {string} animatorKey The key for the animator. - * @param {AttributeToProjector} attrToProjector The set of attributes to set on the selection. - * @returns {D3.Selection} The resulting selection (potentially after the transition) - */ - _applyAnimatedAttributes(selection: any, animatorKey: string, attrToProjector: AttributeToProjector): any; /** * Get the animator associated with the specified Animator key. * @@ -2558,7 +2706,9 @@ declare module Plottable { _removeDataset(key: string): AbstractPlot; datasets(): Dataset[]; _getDrawersInOrder(): _Drawer.AbstractDrawer[]; - _paint(): void; + _generateDrawSteps(): _Drawer.DrawStep[]; + _additionalPaint(time: number): void; + _getDataToDraw(): D3.Map; } } } @@ -2577,7 +2727,6 @@ declare module Plottable { _addDataset(key: string, dataset: Dataset): void; _generateAttrToProjector(): AttributeToProjector; _getDrawer(key: string): _Drawer.AbstractDrawer; - _paint(): void; } } } @@ -2615,13 +2764,11 @@ declare module Plottable { declare module Plottable { module Plot { - class Scatter extends AbstractXYPlot { - _animators: Animator.PlotAnimatorMap; + class Scatter extends AbstractXYPlot implements Interaction.Hoverable { /** * Constructs a ScatterPlot. * * @constructor - * @param {DatasetInterface | any} dataset The dataset to render. * @param {Scale} xScale The x scale to use. * @param {Scale} yScale The y scale to use. */ @@ -2632,8 +2779,16 @@ declare module Plottable { * radius, and "fill" is the CSS color of the datum. */ project(attrToSet: string, accessor: any, scale?: Scale.AbstractScale): Scatter; + _getDrawer(key: string): _Drawer.Element; _generateAttrToProjector(): AttributeToProjector; - _paint(): void; + _generateDrawSteps(): _Drawer.DrawStep[]; + _getClosestStruckPoint(p: Point, range: number): { + selection: D3.Selection; + data: any[]; + }; + _hoverOverComponent(p: Point): void; + _hoverOutComponent(p: Point): void; + _doHover(p: Point): Interaction.HoverData; } } } @@ -2660,12 +2815,14 @@ declare module Plottable { */ constructor(xScale: Scale.Ordinal, yScale: Scale.Ordinal, colorScale: Scale.AbstractScale); _addDataset(key: string, dataset: Dataset): void; + _getDrawer(key: string): _Drawer.Element; /** * @param {string} attrToSet One of ["x", "y", "fill"]. If "fill" is used, * the data should return a valid CSS color. */ project(attrToSet: string, accessor: any, scale?: Scale.AbstractScale): Grid; - _paint(): void; + _generateAttrToProjector(): AttributeToProjector; + _generateDrawSteps(): _Drawer.DrawStep[]; } } } @@ -2673,7 +2830,7 @@ declare module Plottable { declare module Plottable { module Plot { - class AbstractBarPlot extends AbstractXYPlot { + class AbstractBarPlot extends AbstractXYPlot implements Interaction.Hoverable { static _BarAlignmentToFactor: { [x: string]: number; }; @@ -2681,7 +2838,6 @@ declare module Plottable { _baselineValue: number; _barAlignmentFactor: number; _isVertical: boolean; - _animators: Animator.PlotAnimatorMap; /** * Constructs a BarPlot. * @@ -2692,7 +2848,6 @@ declare module Plottable { constructor(xScale: Scale.AbstractScale, yScale: Scale.AbstractScale); _getDrawer(key: string): _Drawer.Rect; _setup(): void; - _paint(): void; /** * Sets the baseline for the bars to the specified value. * @@ -2711,6 +2866,32 @@ declare module Plottable { * @returns {AbstractBarPlot} The calling AbstractBarPlot. */ barAlignment(alignment: string): AbstractBarPlot; + /** + * Get whether bar labels are enabled. + * + * @returns {boolean} Whether bars should display labels or not. + */ + barLabelsEnabled(): boolean; + /** + * Set whether bar labels are enabled. + * @param {boolean} Whether bars should display labels or not. + * + * @returns {AbstractBarPlot} The calling plot. + */ + barLabelsEnabled(enabled: boolean): AbstractBarPlot; + /** + * Get the formatter for bar labels. + * + * @returns {Formatter} The formatting function for bar labels. + */ + barLabelFormatter(): Formatter; + /** + * Change the formatting function for bar labels. + * @param {Formatter} The formatting function for bar labels. + * + * @returns {AbstractBarPlot} The calling plot. + */ + barLabelFormatter(formatter: Formatter): AbstractBarPlot; /** * Selects the bar under the given pixel position (if [xValOrExtent] * and [yValOrExtent] are {number}s), under a given line (if only one @@ -2734,7 +2915,30 @@ declare module Plottable { _updateDomainer(scale: Scale.AbstractScale): void; _updateYDomainer(): void; _updateXDomainer(): void; + _additionalPaint(time: number): void; + _drawLabels(): void; + _generateDrawSteps(): _Drawer.DrawStep[]; _generateAttrToProjector(): AttributeToProjector; + /** + * Gets the current hover mode. + * + * @return {string} The current hover mode. + */ + hoverMode(): string; + /** + * Sets the hover mode for hover interactions. There are two modes: + * - "point": Selects the bar under the mouse cursor (default). + * - "line" : Selects any bar that would be hit by a line extending + * in the same direction as the bar and passing through + * the cursor. + * + * @param {string} mode The desired hover mode. + * @return {AbstractBarPlot} The calling Bar Plot. + */ + hoverMode(mode: String): AbstractBarPlot; + _hoverOverComponent(p: Point): void; + _hoverOutComponent(p: Point): void; + _doHover(p: Point): Interaction.HoverData; } } } @@ -2759,7 +2963,6 @@ declare module Plottable { * Constructs a VerticalBarPlot. * * @constructor - * @param {DatasetInterface | any} dataset The dataset to render. * @param {Scale} xScale The x scale to use. * @param {QuantitativeScale} yScale The y scale to use. */ @@ -2804,20 +3007,19 @@ declare module Plottable { module Plot { class Line extends AbstractXYPlot { _yScale: Scale.AbstractQuantitative; - _animators: Animator.PlotAnimatorMap; /** * Constructs a LinePlot. * * @constructor - * @param {any | DatasetInterface} dataset The dataset to render. * @param {QuantitativeScale} xScale The x scale to use. * @param {QuantitativeScale} yScale The y scale to use. */ constructor(xScale: Scale.AbstractQuantitative, yScale: Scale.AbstractQuantitative); + _rejectNullsAndNaNs(d: any, i: number, projector: AppliedAccessor): boolean; + _getDrawer(key: string): _Drawer.Line; _getResetYFunction(): (d: any, i: number) => number; + _generateDrawSteps(): _Drawer.DrawStep[]; _generateAttrToProjector(): AttributeToProjector; - _rejectNullsAndNaNs(d: any, i: number, projector: AppliedAccessor): boolean; - _paint(): void; _wholeDatumAttributes(): string[]; } } @@ -2834,16 +3036,15 @@ declare module Plottable { * Constructs an AreaPlot. * * @constructor - * @param {DatasetInterface | any} dataset The dataset to render. * @param {QuantitativeScale} xScale The x scale to use. * @param {QuantitativeScale} yScale The y scale to use. */ constructor(xScale: Scale.AbstractQuantitative, yScale: Scale.AbstractQuantitative); _onDatasetUpdate(): void; + _getDrawer(key: string): _Drawer.Area; _updateYDomainer(): void; project(attrToSet: string, accessor: any, scale?: Scale.AbstractScale): Area; _getResetYFunction(): AppliedAccessor; - _paint(): void; _wholeDatumAttributes(): string[]; } } @@ -2866,7 +3067,7 @@ declare module Plottable { */ constructor(xScale: Scale.AbstractScale, yScale: Scale.AbstractScale, isVertical?: boolean); _generateAttrToProjector(): AttributeToProjector; - _paint(): void; + _getDataToDraw(): D3.Map; } } } @@ -2874,10 +3075,32 @@ declare module Plottable { declare module Plottable { module Plot { + interface StackedDatum { + key: any; + value: number; + offset?: number; + } class AbstractStacked extends AbstractXYPlot { _isVertical: boolean; + project(attrToSet: string, accessor: any, scale?: Scale.AbstractScale): AbstractStacked; _onDatasetUpdate(): void; + _updateStackOffsets(): void; + _updateStackExtents(): void; + /** + * Feeds the data through d3's stack layout function which will calculate + * the stack offsets and use the the function declared in .out to set the offsets on the data. + */ + _stack(dataArray: D3.Map[]): D3.Map[]; + /** + * After the stack offsets have been determined on each separate dataset, the offsets need + * to be determined correctly on the overall datasets + */ + _setDatasetStackOffsets(positiveDataMapArray: D3.Map[], negativeDataMapArray: D3.Map[]): void; + _getDomainKeys(): string[]; + _generateDefaultMapArray(): D3.Map[]; _updateScaleExtents(): void; + _keyAccessor(): AppliedAccessor; + _valueAccessor(): AppliedAccessor; } } } @@ -2898,7 +3121,8 @@ declare module Plottable { constructor(xScale: Scale.AbstractQuantitative, yScale: Scale.AbstractQuantitative); _getDrawer(key: string): _Drawer.Area; _setup(): void; - _paint(): void; + _updateStackOffsets(): void; + _additionalPaint(): void; _updateYDomainer(): void; _onDatasetUpdate(): void; _generateAttrToProjector(): AttributeToProjector; @@ -2909,7 +3133,7 @@ declare module Plottable { declare module Plottable { module Plot { - class StackedBar extends AbstractStacked { + class StackedBar extends AbstractBarPlot { _baselineValue: number; _baseline: D3.Selection; _barAlignmentFactor: number; @@ -2923,15 +3147,20 @@ declare module Plottable { * @param {boolean} isVertical if the plot if vertical. */ constructor(xScale?: Scale.AbstractScale, yScale?: Scale.AbstractScale, isVertical?: boolean); - _setup(): void; - _getAnimator(drawer: _Drawer.AbstractDrawer, index: number): Animator.MovingRect; - _getDrawer(key: string): any; + _getAnimator(key: string): Animator.PlotAnimator; _generateAttrToProjector(): any; - _paint(): void; - baseline(value: number): any; - _updateDomainer(scale: Scale.AbstractScale): any; - _updateXDomainer(): any; - _updateYDomainer(): any; + _generateDrawSteps(): _Drawer.DrawStep[]; + project(attrToSet: string, accessor: any, scale?: Scale.AbstractScale): StackedBar; + _onDatasetUpdate(): StackedBar; + _updateStackOffsets(): void; + _updateStackExtents(): void; + _stack(dataArray: D3.Map[]): D3.Map[]; + _setDatasetStackOffsets(positiveDataMapArray: D3.Map[], negativeDataMapArray: D3.Map[]): void; + _getDomainKeys(): any; + _generateDefaultMapArray(): D3.Map[]; + _updateScaleExtents(): void; + _keyAccessor(): AppliedAccessor; + _valueAccessor(): AppliedAccessor; } } } @@ -2951,6 +3180,12 @@ declare module Plottable { * animators. */ animate(selection: any, attrToProjector: AttributeToProjector): any; + /** + * Given the number of elements, return the total time the animation requires + * @param number numberofIterations The number of elements that will be drawn + * @returns {any} The time required for the animation + */ + getTiming(numberOfIterations: number): number; } interface PlotAnimatorMap { [animatorKey: string]: PlotAnimator; @@ -2966,7 +3201,8 @@ declare module Plottable { * immediately set on the selection. */ class Null implements PlotAnimator { - animate(selection: any, attrToProjector: AttributeToProjector): D3.Selection; + getTiming(selection: any): number; + animate(selection: any, attrToProjector: AttributeToProjector): any; } } } @@ -2976,6 +3212,15 @@ declare module Plottable { module Animator { /** * The base animator implementation with easing, duration, and delay. + * + * The maximum delay between animations can be configured with maxIterativeDelay. + * + * The maximum total animation duration can be configured with maxTotalDuration. + * maxTotalDuration does not set actual total animation duration. + * + * The actual interval delay is calculated by following formula: + * min(maxIterativeDelay(), + * max(maxTotalDuration() - duration(), 0) / ) */ class Base implements PlotAnimator { /** @@ -2986,6 +3231,14 @@ declare module Plottable { * The default starting delay of the animation in milliseconds */ static DEFAULT_DELAY_MILLISECONDS: number; + /** + * The default maximum start delay between each start of an animation + */ + static DEFAULT_MAX_ITERATIVE_DELAY_MILLISECONDS: number; + /** + * The default maximum total animation duration + */ + static DEFAULT_MAX_TOTAL_DURATION_MILLISECONDS: number; /** * The default easing of the animation */ @@ -2996,7 +3249,8 @@ declare module Plottable { * @constructor */ constructor(); - animate(selection: any, attrToProjector: AttributeToProjector): D3.Transition.Transition; + getTiming(numberOfIterations: number): number; + animate(selection: any, attrToProjector: AttributeToProjector): any; /** * Gets the duration of the animation in milliseconds. * @@ -3036,42 +3290,6 @@ declare module Plottable { * @returns {Default} The calling Default Animator. */ easing(easing: string): Base; - } - } -} - - -declare module Plottable { - module Animator { - /** - * An animator that delays the animation of the attributes using the index - * of the selection data. - * - * The maximum delay between animations can be configured with maxIterativeDelay. - * - * The maximum total animation duration can be configured with maxTotalDuration. - * maxTotalDuration does not set actual total animation duration. - * - * The actual interval delay is calculated by following formula: - * min(maxIterativeDelay(), - * max(totalDurationLimit() - duration(), 0) / ) - */ - class IterativeDelay extends Base { - /** - * The default maximum start delay between each start of an animation - */ - static DEFAULT_MAX_ITERATIVE_DELAY_MILLISECONDS: number; - /** - * The default maximum total animation duration - */ - static DEFAULT_MAX_TOTAL_DURATION_MILLISECONDS: number; - /** - * Constructs an animator with a start delay between each selection animation - * - * @constructor - */ - constructor(); - animate(selection: any, attrToProjector: AttributeToProjector): D3.Transition.Transition; /** * Gets the maximum start delay between animations in milliseconds. * @@ -3082,9 +3300,9 @@ declare module Plottable { * Sets the maximum start delay between animations in milliseconds. * * @param {number} maxIterDelay The maximum iterative delay in milliseconds. - * @returns {IterativeDelay} The calling IterativeDelay Animator. + * @returns {Base} The calling Base Animator. */ - maxIterativeDelay(maxIterDelay: number): IterativeDelay; + maxIterativeDelay(maxIterDelay: number): Base; /** * Gets the maximum total animation duration in milliseconds. * @@ -3095,9 +3313,9 @@ declare module Plottable { * Sets the maximum total animation duration in miliseconds. * * @param {number} maxDuration The maximum total animation duration in milliseconds. - * @returns {IterativeDelay} The calling IterativeDelay Animator. + * @returns {Base} The calling Base Animator. */ - maxTotalDuration(maxDuration: number): IterativeDelay; + maxTotalDuration(maxDuration: number): Base; } } } @@ -3113,7 +3331,7 @@ declare module Plottable { isVertical: boolean; isReverse: boolean; constructor(isVertical?: boolean, isReverse?: boolean); - animate(selection: any, attrToProjector: AttributeToProjector): D3.Transition.Transition; + animate(selection: any, attrToProjector: AttributeToProjector): any; _startMovingProjector(attrToProjector: AttributeToProjector): AppliedAccessor; } } @@ -3445,6 +3663,65 @@ declare module Plottable { } +declare module Plottable { + module Interaction { + interface HoverData { + data: any[]; + selection: D3.Selection; + } + interface Hoverable extends Component.AbstractComponent { + /** + * Called when the user first mouses over the Component. + * + * @param {Point} The cursor's position relative to the Component's origin. + */ + _hoverOverComponent(p: Point): void; + /** + * Called when the user mouses out of the Component. + * + * @param {Point} The cursor's position relative to the Component's origin. + */ + _hoverOutComponent(p: Point): void; + /** + * Returns the HoverData associated with the given position, and performs + * any visual changes associated with hovering inside a Component. + * + * @param {Point} The cursor's position relative to the Component's origin. + * @return {HoverData} The HoverData associated with the given position. + */ + _doHover(p: Point): HoverData; + } + class Hover extends AbstractInteraction { + _componentToListenTo: Hoverable; + _anchor(component: Hoverable, hitBox: D3.Selection): void; + /** + * Attaches an callback to be called when the user mouses over an element. + * + * @param {(hoverData: HoverData) => any} callback The callback to be called. + * The callback will be passed data for newly hovered-over elements. + * @return {Interaction.Hover} The calling Interaction.Hover. + */ + onHoverOver(callback: (hoverData: HoverData) => any): Hover; + /** + * Attaches a callback to be called when the user mouses off of an element. + * + * @param {(hoverData: HoverData) => any} callback The callback to be called. + * The callback will be passed data from the hovered-out elements. + * @return {Interaction.Hover} The calling Interaction.Hover. + */ + onHoverOut(callback: (hoverData: HoverData) => any): Hover; + /** + * Retrieves the HoverData associated with the elements the user is currently hovering over. + * + * @return {HoverData} The data and selection corresponding to the elements + * the user is currently hovering over. + */ + getCurrentHoverData(): HoverData; + } + } +} + + declare module Plottable { module Dispatcher { class AbstractDispatcher extends Core.PlottableObject { diff --git a/plottable.js b/plottable.js index e05fe0c1f0..daa4b812a4 100644 --- a/plottable.js +++ b/plottable.js @@ -1,5 +1,5 @@ /*! -Plottable 0.33.1 (https://github.com/palantir/plottable) +Plottable 0.34.0 (https://github.com/palantir/plottable) Copyright 2014 Palantir Technologies Licensed under MIT (https://github.com/palantir/plottable/blob/master/LICENSE) */ @@ -208,10 +208,8 @@ var Plottable; } Methods.objEq = objEq; function max(arr, one, two) { - if (one === void 0) { one = 0; } - if (two === void 0) { two = 0; } if (arr.length === 0) { - if (typeof (one) === "number") { + if (typeof (one) !== "function") { return one; } else { @@ -225,10 +223,8 @@ var Plottable; } Methods.max = max; function min(arr, one, two) { - if (one === void 0) { one = 0; } - if (two === void 0) { two = 0; } if (arr.length === 0) { - if (typeof (one) === "number") { + if (typeof (one) !== "function") { return one; } else { @@ -241,6 +237,52 @@ var Plottable; /* tslint:enable:ban */ } Methods.min = min; + /** + * Creates shallow copy of map. + * @param {{ [key: string]: any }} oldMap Map to copy + * + * @returns {[{ [key: string]: any }} coppied map. + */ + function copyMap(oldMap) { + var newMap = {}; + d3.keys(oldMap).forEach(function (key) { return newMap[key] = oldMap[key]; }); + return newMap; + } + Methods.copyMap = copyMap; + function range(start, stop, step) { + if (step === void 0) { step = 1; } + if (step === 0) { + throw new Error("step cannot be 0"); + } + var length = Math.max(Math.ceil((stop - start) / step), 0); + var range = []; + for (var i = 0; i < length; ++i) { + range[i] = start + step * i; + } + return range; + } + Methods.range = range; + /** 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); + } + } + Methods.setTimeout = setTimeout; })(_Util.Methods || (_Util.Methods = {})); var Methods = _Util.Methods; })(Plottable._Util || (Plottable._Util = {})); @@ -532,7 +574,7 @@ var Plottable; var whs = s.trim().split("").map(tm); return { width: d3.sum(whs, function (wh) { return wh.width; }), - height: _Util.Methods.max(whs, function (wh) { return wh.height; }) + height: _Util.Methods.max(whs, function (wh) { return wh.height; }, 0) }; }; } @@ -558,7 +600,7 @@ var Plottable; }); return { width: d3.sum(whs, function (x) { return x.width; }), - height: _Util.Methods.max(whs, function (x) { return x.height; }) + height: _Util.Methods.max(whs, function (x) { return x.height; }, 0) }; } else { @@ -680,7 +722,7 @@ var Plottable; xForm.translate = [isRight ? width : 0, isRight ? 0 : height]; innerG.attr("transform", xForm.toString()); innerG.classed("rotated-" + rotation, true); - return wh; + return { width: wh.height, height: wh.width }; } Text.writeLineVertically = writeLineVertically; function writeTextHorizontally(brokenText, g, width, height, xAlign, yAlign) { @@ -747,8 +789,10 @@ var Plottable; if (write == null) { var widthFn = orientHorizontally ? _Util.Methods.max : d3.sum; var heightFn = orientHorizontally ? d3.sum : _Util.Methods.max; - usedWidth = widthFn(wrappedText.lines, function (line) { return tm(line).width; }); - usedHeight = heightFn(wrappedText.lines, function (line) { return tm(line).height; }); + var heightAcc = function (line) { return orientHorizontally ? tm(line).height : tm(line).width; }; + var widthAcc = function (line) { return orientHorizontally ? tm(line).width : tm(line).height; }; + usedWidth = widthFn(wrappedText.lines, widthAcc, 0); + usedHeight = heightFn(wrappedText.lines, heightAcc, 0); } else { var innerG = write.g.append("g").classed("writeText-inner-g", true); // unleash your inner G @@ -824,7 +868,7 @@ var Plottable; function canWrapWithoutBreakingWords(text, width, widthMeasure) { var tokens = tokenize(text); var widths = tokens.map(widthMeasure); - var maxWidth = _Util.Methods.max(widths); + var maxWidth = _Util.Methods.max(widths, 0); return maxWidth <= width; } WordWrap.canWrapWithoutBreakingWords = canWrapWithoutBreakingWords; @@ -1078,6 +1122,47 @@ var Plottable; var _Util = Plottable._Util; })(Plottable || (Plottable = {})); +/// +var Plottable; +(function (Plottable) { + (function (_Util) { + (function (Color) { + /** + * 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 : Math.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; + } + /** + * 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; + })(_Util.Color || (_Util.Color = {})); + var Color = _Util.Color; + })(Plottable._Util || (Plottable._Util = {})); + var _Util = Plottable._Util; +})(Plottable || (Plottable = {})); + /// var Plottable; (function (Plottable) { @@ -1310,7 +1395,7 @@ var Plottable; /// var Plottable; (function (Plottable) { - Plottable.version = "0.33.1"; + Plottable.version = "0.34.0"; })(Plottable || (Plottable = {})); /// @@ -1854,7 +1939,7 @@ var Plottable; domain = scale._defaultExtent(); } else { - domain = [Plottable._Util.Methods.min(extents, function (e) { return e[0]; }), Plottable._Util.Methods.max(extents, function (e) { return e[1]; })]; + domain = [Plottable._Util.Methods.min(extents, function (e) { return e[0]; }, 0), Plottable._Util.Methods.max(extents, function (e) { return e[1]; }, 0)]; } domain = this.includeDomain(domain); domain = this.padDomain(scale, domain); @@ -2181,6 +2266,7 @@ var Plottable; this._userSetDomainer = false; this._domainer = new Plottable.Domainer(); this._typeCoercer = function (d) { return +d; }; + this._tickGenerator = function (scale) { return scale.getDefaultTicks(); }; } AbstractQuantitative.prototype._getExtent = function () { return this._domainer.computeDomain(this._getAllExtents(), this); @@ -2229,6 +2315,12 @@ var Plottable; this._d3Scale.rangeRound(values); return this; }; + /** + * Gets ticks generated by the default algorithm. + */ + AbstractQuantitative.prototype.getDefaultTicks = function () { + return this._d3Scale.ticks(this.numTicks()); + }; AbstractQuantitative.prototype.clamp = function (clamp) { if (clamp == null) { return this._d3Scale.clamp(); @@ -2239,14 +2331,10 @@ var Plottable; /** * Gets a set of tick values spanning the domain. * - * @param {number} [count] The approximate number of ticks to generate. - * If not supplied, the number specified by - * numTicks() is used instead. * @returns {any[]} The generated ticks. */ - AbstractQuantitative.prototype.ticks = function (count) { - if (count === void 0) { count = this.numTicks(); } - return this._d3Scale.ticks(count); + AbstractQuantitative.prototype.ticks = function () { + return this._tickGenerator(this); }; AbstractQuantitative.prototype.numTicks = function (count) { if (count == null) { @@ -2276,6 +2364,15 @@ var Plottable; AbstractQuantitative.prototype._defaultExtent = function () { return [0, 1]; }; + AbstractQuantitative.prototype.tickGenerator = function (generator) { + if (generator == null) { + return this._tickGenerator; + } + else { + this._tickGenerator = generator; + return this; + } + }; return AbstractQuantitative; })(Scale.AbstractScale); Scale.AbstractQuantitative = AbstractQuantitative; @@ -2450,8 +2547,8 @@ var Plottable; // 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._Util.Methods.min(this.untransformedDomain); - var max = Plottable._Util.Methods.max(this.untransformedDomain); + var min = Plottable._Util.Methods.min(this.untransformedDomain, 0); + var max = Plottable._Util.Methods.max(this.untransformedDomain, 0); var negativeLower = min; var negativeUpper = middle(min, max, -this.pivot); var positiveLower = middle(min, max, this.pivot); @@ -2505,8 +2602,8 @@ var Plottable; * distance when plotted. */ ModifiedLog.prototype.howManyTicks = function (lower, upper) { - var adjustedMin = this.adjustedLog(Plottable._Util.Methods.min(this.untransformedDomain)); - var adjustedMax = this.adjustedLog(Plottable._Util.Methods.max(this.untransformedDomain)); + var adjustedMin = this.adjustedLog(Plottable._Util.Methods.min(this.untransformedDomain, 0)); + var adjustedMax = this.adjustedLog(Plottable._Util.Methods.max(this.untransformedDomain, 0)); var adjustedLower = this.adjustedLog(lower); var adjustedUpper = this.adjustedLog(upper); var proportion = (adjustedUpper - adjustedLower) / (adjustedMax - adjustedMin); @@ -2889,7 +2986,7 @@ var Plottable; // unlike other QuantitativeScales, interpolatedColorScale ignores its domainer var extents = this._getAllExtents(); if (extents.length > 0) { - this._setDomain([Plottable._Util.Methods.min(extents, function (x) { return x[0]; }), Plottable._Util.Methods.max(extents, function (x) { return x[1]; })]); + this._setDomain([Plottable._Util.Methods.min(extents, function (x) { return x[0]; }, 0), Plottable._Util.Methods.max(extents, function (x) { return x[1]; }, 0)]); } return this; }; @@ -2983,6 +3080,43 @@ var Plottable; var _Util = Plottable._Util; })(Plottable || (Plottable = {})); +/// +var Plottable; +(function (Plottable) { + (function (Scale) { + (function (TickGenerators) { + /** + * Creates a tick generator using the specified interval. + * + * Generates ticks at multiples of the interval while also including the domain boundaries. + * + * @param {number} interval The interval between two ticks (not including the end ticks). + * + * @returns {TickGenerator} A tick generator using the specified interval. + */ + 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._Util.Methods.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; + })(Scale.TickGenerators || (Scale.TickGenerators = {})); + var TickGenerators = Scale.TickGenerators; + })(Plottable.Scale || (Plottable.Scale = {})); + var Scale = Plottable.Scale; +})(Plottable || (Plottable = {})); + /// var Plottable; (function (Plottable) { @@ -2997,6 +3131,18 @@ var Plottable; function AbstractDrawer(key) { this.key = key; } + /** + * Sets the class, which needs to be applied to bound elements. + * + * @param{string} className The class name to be applied. + */ + AbstractDrawer.prototype.setClass = function (className) { + this._className = className; + return this; + }; + AbstractDrawer.prototype.setup = function (area) { + this._renderArea = area; + }; /** * Removes the Drawer and its renderArea */ @@ -3006,15 +3152,41 @@ var Plottable; } }; /** - * Draws the data into the renderArea using the attrHash for attributes + * Enter new data to render area and creates binding * * @param{any[]} data The data to be drawn - * @param{attrHash} AttributeToProjector The list of attributes to set on the data */ - AbstractDrawer.prototype.draw = function (data, attrToProjector, animator) { - if (animator === void 0) { animator = new Plottable.Animator.Null(); } + AbstractDrawer.prototype._enterData = function (data) { + // no-op + }; + /** + * Draws data using one step + * + * @param{DataStep} step The step, how data should be drawn. + */ + AbstractDrawer.prototype._drawStep = function (step) { // no-op }; + AbstractDrawer.prototype._numberOfAnimationIterations = function (data) { + return data.length; + }; + /** + * Draws the data into the renderArea using the spefic steps + * + * @param{any[]} data The data to be drawn + * @param{DrawStep[]} drawSteps The list of steps, which needs to be drawn + */ + AbstractDrawer.prototype.draw = function (data, drawSteps) { + var _this = this; + this._enterData(data); + var numberOfIterations = this._numberOfAnimationIterations(data); + var delay = 0; + drawSteps.forEach(function (drawStep, i) { + Plottable._Util.Methods.setTimeout(function () { return _this._drawStep(drawStep); }, delay); + delay += drawStep.animator.getTiming(numberOfIterations); + }); + return delay; + }; return AbstractDrawer; })(); _Drawer.AbstractDrawer = AbstractDrawer; @@ -3032,23 +3204,49 @@ var __extends = this.__extends || function (d, b) { var Plottable; (function (Plottable) { (function (_Drawer) { - var Arc = (function (_super) { - __extends(Arc, _super); - function Arc() { + var Line = (function (_super) { + __extends(Line, _super); + function Line() { _super.apply(this, arguments); } - Arc.prototype.draw = function (data, attrToProjector, animator) { - if (animator === void 0) { animator = new Plottable.Animator.Null(); } - var svgElement = "path"; - var dataElements = this._renderArea.selectAll(svgElement).data(data); - dataElements.enter().append(svgElement); - dataElements.classed("arc", true); - animator.animate(dataElements, attrToProjector); - dataElements.exit().remove(); + Line.prototype._enterData = function (data) { + _super.prototype._enterData.call(this, data); + this.pathSelection.datum(data); }; - return Arc; + Line.prototype.setup = function (area) { + area.append("path").classed("line", true); + _super.prototype.setup.call(this, area); + this.pathSelection = this._renderArea.select(".line"); + }; + Line.prototype.createLine = function (xFunction, yFunction, definedFunction) { + if (!definedFunction) { + definedFunction = function () { return true; }; + } + return d3.svg.line().x(xFunction).y(yFunction).defined(definedFunction); + }; + Line.prototype._numberOfAnimationIterations = function (data) { + return 1; + }; + Line.prototype._drawStep = function (step) { + var baseTime = _super.prototype._drawStep.call(this, step); + var attrToProjector = Plottable._Util.Methods.copyMap(step.attrToProjector); + var xFunction = attrToProjector["x"]; + var yFunction = attrToProjector["y"]; + var definedFunction = attrToProjector["defined"]; + delete attrToProjector["x"]; + delete attrToProjector["y"]; + attrToProjector["d"] = this.createLine(xFunction, yFunction, attrToProjector["defined"]); + if (attrToProjector["defined"]) { + delete attrToProjector["defined"]; + } + if (attrToProjector["fill"]) { + this.pathSelection.attr("fill", attrToProjector["fill"]); // so colors don't animate + } + step.animator.animate(this.pathSelection, attrToProjector); + }; + return Line; })(_Drawer.AbstractDrawer); - _Drawer.Arc = Arc; + _Drawer.Line = Line; })(Plottable._Drawer || (Plottable._Drawer = {})); var _Drawer = Plottable._Drawer; })(Plottable || (Plottable = {})); @@ -3067,16 +3265,68 @@ var Plottable; __extends(Area, _super); function Area() { _super.apply(this, arguments); + this._drawLine = true; } - Area.prototype.draw = function (data, attrToProjector) { - var svgElement = "path"; - var dataElements = this._renderArea.selectAll(svgElement).data([data]); - dataElements.enter().append(svgElement); - dataElements.attr(attrToProjector).classed("area", true); - dataElements.exit().remove(); + Area.prototype._enterData = function (data) { + if (this._drawLine) { + _super.prototype._enterData.call(this, data); + } + else { + _Drawer.AbstractDrawer.prototype._enterData.call(this, data); + } + this.areaSelection.datum(data); + }; + /** + * Sets the value determining if line should be drawn. + * + * @param{boolean} draw The value determing if line should be drawn. + */ + Area.prototype.drawLine = function (draw) { + this._drawLine = draw; + return this; + }; + Area.prototype.setup = function (area) { + area.append("path").classed("area", true); + if (this._drawLine) { + _super.prototype.setup.call(this, area); + } + else { + _Drawer.AbstractDrawer.prototype.setup.call(this, area); + } + this.areaSelection = this._renderArea.select(".area"); + }; + Area.prototype.createArea = function (xFunction, y0Function, y1Function, definedFunction) { + if (!definedFunction) { + definedFunction = function () { return true; }; + } + return d3.svg.area().x(xFunction).y0(y0Function).y1(y1Function).defined(definedFunction); + }; + Area.prototype._drawStep = function (step) { + if (this._drawLine) { + _super.prototype._drawStep.call(this, step); + } + else { + _Drawer.AbstractDrawer.prototype._drawStep.call(this, step); + } + var attrToProjector = Plottable._Util.Methods.copyMap(step.attrToProjector); + var xFunction = attrToProjector["x"]; + var y0Function = attrToProjector["y0"]; + var y1Function = attrToProjector["y"]; + var definedFunction = attrToProjector["defined"]; + delete attrToProjector["x"]; + delete attrToProjector["y0"]; + delete attrToProjector["y"]; + attrToProjector["d"] = this.createArea(xFunction, y0Function, y1Function, attrToProjector["defined"]); + if (attrToProjector["defined"]) { + delete attrToProjector["defined"]; + } + if (attrToProjector["fill"]) { + this.areaSelection.attr("fill", attrToProjector["fill"]); // so colors don't animate + } + step.animator.animate(this.areaSelection, attrToProjector); }; return Area; - })(_Drawer.AbstractDrawer); + })(_Drawer.Line); _Drawer.Area = Area; })(Plottable._Drawer || (Plottable._Drawer = {})); var _Drawer = Plottable._Drawer; @@ -3092,26 +3342,179 @@ var __extends = this.__extends || function (d, b) { var Plottable; (function (Plottable) { (function (_Drawer) { - var Rect = (function (_super) { - __extends(Rect, _super); - function Rect() { + var Element = (function (_super) { + __extends(Element, _super); + function Element() { _super.apply(this, arguments); } - Rect.prototype.draw = function (data, attrToProjector, animator) { - if (animator === void 0) { animator = new Plottable.Animator.Null(); } - var svgElement = "rect"; - var dataElements = this._renderArea.selectAll(svgElement).data(data); - dataElements.enter().append(svgElement); - animator.animate(dataElements, attrToProjector); + /** + * Sets the svg element, which needs to be bind to data + * + * @param{string} tag The svg element to be bind + */ + Element.prototype.svgElement = function (tag) { + this._svgElement = tag; + return this; + }; + Element.prototype._getDrawSelection = function () { + return this._renderArea.selectAll(this._svgElement); + }; + Element.prototype._drawStep = function (step) { + _super.prototype._drawStep.call(this, step); + var drawSelection = this._getDrawSelection(); + if (step.attrToProjector["fill"]) { + drawSelection.attr("fill", step.attrToProjector["fill"]); // so colors don't animate + } + step.animator.animate(drawSelection, step.attrToProjector); + }; + Element.prototype._enterData = function (data) { + _super.prototype._enterData.call(this, data); + var dataElements = this._getDrawSelection().data(data); + dataElements.enter().append(this._svgElement); + if (this._className != null) { + dataElements.classed(this._className, true); + } dataElements.exit().remove(); }; - return Rect; + return Element; })(_Drawer.AbstractDrawer); + _Drawer.Element = Element; + })(Plottable._Drawer || (Plottable._Drawer = {})); + var _Drawer = Plottable._Drawer; +})(Plottable || (Plottable = {})); + +/// +var __extends = this.__extends || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + __.prototype = b.prototype; + d.prototype = new __(); +}; +var Plottable; +(function (Plottable) { + (function (_Drawer) { + var LABEL_VERTICAL_PADDING = 5; + var LABEL_HORIZONTAL_PADDING = 5; + var Rect = (function (_super) { + __extends(Rect, _super); + function Rect(key, isVertical) { + _super.call(this, key); + this._someLabelsTooWide = false; + this.svgElement("rect"); + this._isVertical = isVertical; + } + Rect.prototype.setup = function (area) { + // need to put the bars in a seperate container so we can ensure that they don't cover labels + _super.prototype.setup.call(this, area.append("g").classed("bar-area", true)); + this.textArea = area.append("g").classed("bar-label-text-area", true); + this.measurer = new Plottable._Util.Text.CachingCharacterMeasurer(this.textArea.append("text")).measure; + }; + Rect.prototype.removeLabels = function () { + this.textArea.selectAll("g").remove(); + }; + Rect.prototype.drawText = function (data, attrToProjector) { + var _this = this; + var labelTooWide = data.map(function (d, i) { + var text = attrToProjector["label"](d, i).toString(); + var w = attrToProjector["width"](d, i); + var h = attrToProjector["height"](d, i); + var x = attrToProjector["x"](d, i); + var y = attrToProjector["y"](d, i); + var positive = attrToProjector["positive"](d, i); + var measurement = _this.measurer(text); + var color = attrToProjector["fill"](d, i); + var dark = Plottable._Util.Color.contrast("white", color) * 1.6 < Plottable._Util.Color.contrast("black", color); + var primary = _this._isVertical ? h : w; + var primarySpace = _this._isVertical ? measurement.height : measurement.width; + var secondaryAttrTextSpace = _this._isVertical ? measurement.width : measurement.height; + var secondaryAttrAvailableSpace = _this._isVertical ? w : h; + var tooWide = secondaryAttrTextSpace + 2 * LABEL_HORIZONTAL_PADDING > secondaryAttrAvailableSpace; + if (measurement.height <= h && measurement.width <= w) { + var offset = Math.min((primary - primarySpace) / 2, LABEL_VERTICAL_PADDING); + if (!positive) { + offset = offset * -1; + } + if (_this._isVertical) { + y += offset; + } + else { + x += offset; + } + var g = _this.textArea.append("g").attr("transform", "translate(" + x + "," + y + ")"); + var className = dark ? "dark-label" : "light-label"; + g.classed(className, true); + var xAlign; + var yAlign; + if (_this._isVertical) { + xAlign = "center"; + yAlign = positive ? "top" : "bottom"; + } + else { + xAlign = positive ? "left" : "right"; + yAlign = "center"; + } + Plottable._Util.Text.writeLineHorizontally(text, g, w, h, xAlign, yAlign); + } + return tooWide; + }); + this._someLabelsTooWide = labelTooWide.some(function (d) { return d; }); + }; + return Rect; + })(_Drawer.Element); _Drawer.Rect = Rect; })(Plottable._Drawer || (Plottable._Drawer = {})); var _Drawer = Plottable._Drawer; })(Plottable || (Plottable = {})); +/// +var __extends = this.__extends || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + __.prototype = b.prototype; + d.prototype = new __(); +}; +var Plottable; +(function (Plottable) { + (function (_Drawer) { + var Arc = (function (_super) { + __extends(Arc, _super); + function Arc(key) { + _super.call(this, key); + this._svgElement = "path"; + } + Arc.prototype.createArc = function (innerRadiusF, outerRadiusF) { + return d3.svg.arc().innerRadius(innerRadiusF).outerRadius(outerRadiusF); + }; + Arc.prototype.retargetProjectors = function (attrToProjector) { + var retargetedAttrToProjector = {}; + d3.entries(attrToProjector).forEach(function (entry) { + retargetedAttrToProjector[entry.key] = function (d, i) { return entry.value(d.data, i); }; + }); + return retargetedAttrToProjector; + }; + Arc.prototype._drawStep = function (step) { + var attrToProjector = Plottable._Util.Methods.copyMap(step.attrToProjector); + attrToProjector = this.retargetProjectors(attrToProjector); + var innerRadiusF = attrToProjector["inner-radius"]; + var outerRadiusF = attrToProjector["outer-radius"]; + delete attrToProjector["inner-radius"]; + delete attrToProjector["outer-radius"]; + attrToProjector["d"] = this.createArc(innerRadiusF, outerRadiusF); + return _super.prototype._drawStep.call(this, { attrToProjector: attrToProjector, animator: step.animator }); + }; + Arc.prototype.draw = function (data, drawSteps) { + var valueAccessor = drawSteps[0].attrToProjector["value"]; + var pie = d3.layout.pie().sort(null).value(valueAccessor)(data); + drawSteps.forEach(function (s) { return delete s.attrToProjector["value"]; }); + return _super.prototype.draw.call(this, pie, drawSteps); + }; + return Arc; + })(_Drawer.Element); + _Drawer.Arc = Arc; + })(Plottable._Drawer || (Plottable._Drawer = {})); + var _Drawer = Plottable._Drawer; +})(Plottable || (Plottable = {})); + /// var __extends = this.__extends || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; @@ -3719,8 +4122,8 @@ var Plottable; Group.prototype._requestedSpace = function (offeredWidth, offeredHeight) { var requests = this._components.map(function (c) { return c._requestedSpace(offeredWidth, offeredHeight); }); return { - width: Plottable._Util.Methods.max(requests, function (request) { return request.width; }), - height: Plottable._Util.Methods.max(requests, function (request) { return request.height; }), + width: Plottable._Util.Methods.max(requests, function (request) { return request.width; }, 0), + height: Plottable._Util.Methods.max(requests, function (request) { return request.height; }, 0), wantsWidth: requests.map(function (r) { return r.wantsWidth; }).some(function (x) { return x; }), wantsHeight: requests.map(function (r) { return r.wantsHeight; }).some(function (x) { return x; }) }; @@ -4408,7 +4811,7 @@ var Plottable; var formattedValue = _this._formatter(v); return _this.measurer(formattedValue).width; }); - var maxTextLength = Plottable._Util.Methods.max(textLengths); + var maxTextLength = Plottable._Util.Methods.max(textLengths, 0); if (this.tickLabelPositioning === "center") { this._computedWidth = this._maxLabelTickLength() + this.tickLabelPadding() + maxTextLength; } @@ -4711,8 +5114,8 @@ var Plottable; var heightFn = this._isHorizontal() ? Plottable._Util.Methods.max : d3.sum; return { textFits: textWriteResults.every(function (t) { return t.textFits; }), - usedWidth: widthFn(textWriteResults, function (t) { return t.usedWidth; }), - usedHeight: heightFn(textWriteResults, function (t) { return t.usedHeight; }) + usedWidth: widthFn(textWriteResults, function (t) { return t.usedWidth; }, 0), + usedHeight: heightFn(textWriteResults, function (t) { return t.usedHeight; }, 0) }; }; Category.prototype._doRender = function () { @@ -5005,7 +5408,7 @@ var Plottable; var rowsICanFit = Math.min(totalNumRows, Math.floor((offeredHeight - 2 * Legend.MARGIN) / textHeight)); var fakeLegendEl = this._content.append("g").classed(Legend.SUBELEMENT_CLASS, true); var measure = Plottable._Util.Text.getTextMeasurer(fakeLegendEl.append("text")); - var maxWidth = Plottable._Util.Methods.max(this.colorScale.domain(), function (d) { return measure(d).width; }); + var maxWidth = Plottable._Util.Methods.max(this.colorScale.domain(), function (d) { return measure(d).width; }, 0); fakeLegendEl.remove(); maxWidth = maxWidth === undefined ? 0 : maxWidth; var desiredWidth = rowsICanFit === 0 ? 0 : maxWidth + textHeight + 2 * Legend.MARGIN; @@ -5195,7 +5598,7 @@ var Plottable; var rowLengths = estimatedLayout.rows.map(function (row) { return d3.sum(row, function (entry) { return estimatedLayout.entryLengths.get(entry); }); }); - var longestRowLength = Plottable._Util.Methods.max(rowLengths); + var longestRowLength = Plottable._Util.Methods.max(rowLengths, 0); longestRowLength = longestRowLength === undefined ? 0 : longestRowLength; // HACKHACK: #843 var desiredWidth = this.padding + longestRowLength; var acceptableHeight = estimatedLayout.numRowsToDraw * estimatedLayout.textHeight + 2 * this.padding; @@ -5607,8 +6010,22 @@ var Plottable; * Sets the layout weight of a particular row. * Space is allocated to rows based on their weight. Rows with higher weights receive proportionally more space. * - * A common case would be to have one graph take up 2/3rds of the space, - * and the other graph take up 1/3rd. + * A common case would be to have one row take up 2/3rds of the space, + * and the other row take up 1/3rd. + * + * Example: + * + * ```JavaScript + * plot = new Plottable.Component.Table([ + * [row1], + * [row2] + * ]); + * + * // assign twice as much space to the first row + * plot + * .rowWeight(0, 2) + * .rowWeight(1, 1) + * ``` * * @param {number} index The index of the row. * @param {number} weight The weight to be set on the row. @@ -5623,8 +6040,7 @@ var Plottable; * Sets the layout weight of a particular column. * Space is allocated to columns based on their weight. Columns with higher weights receive proportionally more space. * - * A common case would be to have one graph take up 2/3rds of the space, - * and the other graph take up 1/3rd. + * Please see `rowWeight` docs for an example. * * @param {number} index The index of the column. * @param {number} weight The weight to be set on the column. @@ -5724,7 +6140,7 @@ var Plottable; this._animate = false; this._animators = {}; this._ANIMATION_DURATION = 250; // milliseconds - this.animateOnNextRender = true; + this._animateOnNextRender = true; this.clipPathEnabled = true; this.classed("plot", true); this._key2DatasetDrawerKey = d3.map(); @@ -5733,7 +6149,7 @@ var Plottable; } AbstractPlot.prototype._anchor = function (element) { _super.prototype._anchor.call(this, element); - this.animateOnNextRender = true; + this._animateOnNextRender = true; this._dataChanged = true; this._updateScaleExtents(); }; @@ -5742,7 +6158,7 @@ var Plottable; _super.prototype._setup.call(this); this._renderArea = this._content.append("g").classed("render-area", true); // HACKHACK on 591 - this._getDrawersInOrder().forEach(function (d) { return d._renderArea = _this._renderArea.append("g"); }); + this._getDrawersInOrder().forEach(function (d) { return d.setup(_this._renderArea.append("g")); }); }; AbstractPlot.prototype.remove = function () { var _this = this; @@ -5781,7 +6197,7 @@ var Plottable; this._datasetKeysInOrder.push(key); this._key2DatasetDrawerKey.set(key, ddk); if (this._isSetup) { - drawer._renderArea = this._renderArea.append("g"); + drawer.setup(this._renderArea.append("g")); } dataset.broadcaster.registerListener(this, function () { return _this._onDatasetUpdate(); }); this._onDatasetUpdate(); @@ -5789,12 +6205,17 @@ var Plottable; AbstractPlot.prototype._getDrawer = function (key) { return new Plottable._Drawer.AbstractDrawer(key); }; - AbstractPlot.prototype._getAnimator = function (drawer, index) { - return new Plottable.Animator.Null(); + AbstractPlot.prototype._getAnimator = function (key) { + if (this._animate && this._animateOnNextRender) { + return this._animators[key] || new Plottable.Animator.Null(); + } + else { + return new Plottable.Animator.Null(); + } }; AbstractPlot.prototype._onDatasetUpdate = function () { this._updateScaleExtents(); - this.animateOnNextRender = true; + this._animateOnNextRender = true; this._dataChanged = true; this._render(); }; @@ -5861,9 +6282,9 @@ var Plottable; }; AbstractPlot.prototype._doRender = function () { if (this._isAnchored) { - this._paint(); + this.paint(); this._dataChanged = false; - this.animateOnNextRender = false; + this._animateOnNextRender = false; } }; /** @@ -5905,28 +6326,6 @@ var Plottable; }); } }; - /** - * Applies attributes to the selection. - * - * If animation is enabled and a valid animator's key is specified, the - * attributes are applied with the animator. Otherwise, they are applied - * immediately to the selection. - * - * The animation will not animate during auto-resize renders. - * - * @param {D3.Selection} selection The selection of elements to update. - * @param {string} animatorKey The key for the animator. - * @param {AttributeToProjector} attrToProjector The set of attributes to set on the selection. - * @returns {D3.Selection} The resulting selection (potentially after the transition) - */ - AbstractPlot.prototype._applyAnimatedAttributes = function (selection, animatorKey, attrToProjector) { - if (this._animate && this.animateOnNextRender && this._animators[animatorKey]) { - return this._animators[animatorKey].animate(selection, attrToProjector); - } - else { - return selection.attr(attrToProjector); - } - }; AbstractPlot.prototype.animator = function (animatorKey, animator) { if (animator === undefined) { return this._animators[animatorKey]; @@ -5994,14 +6393,27 @@ var Plottable; var _this = this; return this._datasetKeysInOrder.map(function (k) { return _this._key2DatasetDrawerKey.get(k).drawer; }); }; - AbstractPlot.prototype._paint = function () { + AbstractPlot.prototype._generateDrawSteps = function () { + return [{ attrToProjector: this._generateAttrToProjector(), animator: new Plottable.Animator.Null() }]; + }; + AbstractPlot.prototype._additionalPaint = function (time) { + // no-op + }; + AbstractPlot.prototype._getDataToDraw = function () { var _this = this; - var attrHash = this._generateAttrToProjector(); - var datasets = this.datasets(); - this._getDrawersInOrder().forEach(function (d, i) { - var animator = _this._animate ? _this._getAnimator(d, i) : new Plottable.Animator.Null(); - d.draw(datasets[i].data(), attrHash, animator); + var datasets = d3.map(); + this._datasetKeysInOrder.forEach(function (key) { + datasets.set(key, _this._key2DatasetDrawerKey.get(key).dataset.data()); }); + return datasets; + }; + AbstractPlot.prototype.paint = function () { + var drawSteps = this._generateDrawSteps(); + var dataToDraw = this._getDataToDraw(); + var drawers = this._getDrawersInOrder(); + var times = this._datasetKeysInOrder.map(function (k, i) { return drawers[i].draw(dataToDraw.get(k), drawSteps); }); + var maxTime = Plottable._Util.Methods.max(times, 0); + this._additionalPaint(maxTime); }; return AbstractPlot; })(Plottable.Component.AbstractComponent); @@ -6053,47 +6465,19 @@ var Plottable; _super.prototype._addDataset.call(this, key, dataset); }; Pie.prototype._generateAttrToProjector = function () { - var attrToProjector = this.retargetProjectors(_super.prototype._generateAttrToProjector.call(this)); - var innerRadiusF = attrToProjector["inner-radius"] || d3.functor(0); - var outerRadiusF = attrToProjector["outer-radius"] || d3.functor(Math.min(this.width(), this.height()) / 2); - attrToProjector["d"] = d3.svg.arc().innerRadius(innerRadiusF).outerRadius(outerRadiusF); - delete attrToProjector["inner-radius"]; - delete attrToProjector["outer-radius"]; + var attrToProjector = _super.prototype._generateAttrToProjector.call(this); + attrToProjector["inner-radius"] = attrToProjector["inner-radius"] || d3.functor(0); + attrToProjector["outer-radius"] = attrToProjector["outer-radius"] || d3.functor(Math.min(this.width(), this.height()) / 2); if (attrToProjector["fill"] == null) { attrToProjector["fill"] = function (d, i) { return Pie.DEFAULT_COLOR_SCALE.scale(String(i)); }; } - delete attrToProjector["value"]; + var defaultAccessor = function (d) { return d.value; }; + var valueProjector = this._projectors["value"]; + attrToProjector["value"] = valueProjector ? valueProjector.accessor : defaultAccessor; return attrToProjector; }; - /** - * Since the data goes through a pie function, which returns an array of ArcDescriptors, - * projectors will need to be retargeted so they point to the data portion of each arc descriptor. - */ - Pie.prototype.retargetProjectors = function (attrToProjector) { - var retargetedAttrToProjector = {}; - d3.entries(attrToProjector).forEach(function (entry) { - retargetedAttrToProjector[entry.key] = function (d, i) { return entry.value(d.data, i); }; - }); - return retargetedAttrToProjector; - }; Pie.prototype._getDrawer = function (key) { - return new Plottable._Drawer.Arc(key); - }; - Pie.prototype._paint = function () { - var _this = this; - var attrHash = this._generateAttrToProjector(); - var datasets = this.datasets(); - this._getDrawersInOrder().forEach(function (d, i) { - var animator = _this._animate ? _this._getAnimator(d, i) : new Plottable.Animator.Null(); - var pieData = _this.pie(datasets[i].data()); - d.draw(pieData, attrHash, animator); - }); - }; - Pie.prototype.pie = function (d) { - var defaultAccessor = function (d) { return d.value; }; - var valueProjector = this._projectors["value"]; - var valueAccessor = valueProjector ? valueProjector.accessor : defaultAccessor; - return d3.layout.pie().sort(null).value(valueAccessor)(d); + return new Plottable._Drawer.Arc(key).setClass("arc"); }; Pie.DEFAULT_COLOR_SCALE = new Plottable.Scale.Color(); return Pie; @@ -6197,20 +6581,18 @@ var Plottable; * Constructs a ScatterPlot. * * @constructor - * @param {DatasetInterface | any} dataset The dataset to render. * @param {Scale} xScale The x scale to use. * @param {Scale} yScale The y scale to use. */ function Scatter(xScale, yScale) { _super.call(this, xScale, yScale); - this._animators = { - "circles-reset": new Plottable.Animator.Null(), - "circles": new Plottable.Animator.IterativeDelay().duration(250).delay(5) - }; + this.closeDetectionRadius = 5; this.classed("scatter-plot", true); this.project("r", 3); // default this.project("opacity", 0.6); // default this.project("fill", function () { return Plottable.Core.Colors.INDIGO; }); // default + this._animators["circles-reset"] = new Plottable.Animator.Null(); + this._animators["circles"] = new Plottable.Animator.Base().duration(250).delay(5); } /** * @param {string} attrToSet One of ["x", "y", "cx", "cy", "r", @@ -6223,6 +6605,9 @@ var Plottable; _super.prototype.project.call(this, attrToSet, accessor, scale); return this; }; + Scatter.prototype._getDrawer = function (key) { + return new Plottable._Drawer.Element(key).svgElement("circle"); + }; Scatter.prototype._generateAttrToProjector = function () { var attrToProjector = _super.prototype._generateAttrToProjector.call(this); attrToProjector["cx"] = attrToProjector["x"]; @@ -6231,24 +6616,59 @@ var Plottable; delete attrToProjector["y"]; return attrToProjector; }; - // HACKHACK #1106 - should use drawers for paint logic - Scatter.prototype._paint = function () { - var _this = this; + Scatter.prototype._generateDrawSteps = function () { + var drawSteps = []; + if (this._dataChanged) { + var resetAttrToProjector = this._generateAttrToProjector(); + resetAttrToProjector["r"] = function () { return 0; }; + drawSteps.push({ attrToProjector: resetAttrToProjector, animator: this._getAnimator("circles-reset") }); + } + drawSteps.push({ attrToProjector: this._generateAttrToProjector(), animator: this._getAnimator("circles") }); + return drawSteps; + }; + Scatter.prototype._getClosestStruckPoint = function (p, range) { + var drawers = this._getDrawersInOrder(); var attrToProjector = this._generateAttrToProjector(); - var datasets = this.datasets(); - this._getDrawersInOrder().forEach(function (d, i) { - var dataset = datasets[i]; - var circles = d._renderArea.selectAll("circle").data(dataset.data()); - circles.enter().append("circle"); - if (_this._dataChanged) { - var rFunction = attrToProjector["r"]; - attrToProjector["r"] = function () { return 0; }; - _this._applyAnimatedAttributes(circles, "circles-reset", attrToProjector); - attrToProjector["r"] = rFunction; - } - _this._applyAnimatedAttributes(circles, "circles", attrToProjector); - circles.exit().remove(); + var getDistSq = function (d, i) { + var dx = attrToProjector["cx"](d, i) - p.x; + var dy = attrToProjector["cy"](d, i) - p.y; + return (dx * dx + dy * dy); + }; + var overAPoint = false; + var closestElement; + var minDistSq = range * range; + drawers.forEach(function (drawer) { + drawer._getDrawSelection().each(function (d, i) { + var distSq = getDistSq(d, i); + var r = attrToProjector["r"](d, i); + if (distSq < r * r) { + if (!overAPoint || distSq < minDistSq) { + closestElement = this; + minDistSq = distSq; + } + overAPoint = true; + } + else if (!overAPoint && distSq < minDistSq) { + closestElement = this; + minDistSq = distSq; + } + }); }); + var closestSelection = d3.select(closestElement); + return { + selection: closestElement ? closestSelection : null, + data: closestElement ? closestSelection.data() : null + }; + }; + //===== Hover logic ===== + Scatter.prototype._hoverOverComponent = function (p) { + // no-op + }; + Scatter.prototype._hoverOutComponent = function (p) { + // no-op + }; + Scatter.prototype._doHover = function (p) { + return this._getClosestStruckPoint(p, this.closeDetectionRadius); }; return Scatter; })(Plot.AbstractXYPlot); @@ -6292,6 +6712,7 @@ var Plottable; this._yScale.rangeType("bands", 0, 0); this._colorScale = colorScale; this.project("fill", "value", colorScale); // default + this._animators["cells"] = new Plottable.Animator.Null(); } Grid.prototype._addDataset = function (key, dataset) { if (this._datasetKeysInOrder.length === 1) { @@ -6300,6 +6721,9 @@ var Plottable; } _super.prototype._addDataset.call(this, key, dataset); }; + Grid.prototype._getDrawer = function (key) { + return new Plottable._Drawer.Element(key).svgElement("rect"); + }; /** * @param {string} attrToSet One of ["x", "y", "fill"]. If "fill" is used, * the data should return a valid CSS color. @@ -6311,17 +6735,16 @@ var Plottable; } return this; }; - Grid.prototype._paint = function () { - var dataset = this.datasets()[0]; - var cells = this._renderArea.selectAll("rect").data(dataset.data()); - cells.enter().append("rect"); + Grid.prototype._generateAttrToProjector = function () { + var attrToProjector = _super.prototype._generateAttrToProjector.call(this); var xStep = this._xScale.rangeBand(); var yStep = this._yScale.rangeBand(); - var attrToProjector = this._generateAttrToProjector(); attrToProjector["width"] = function () { return xStep; }; attrToProjector["height"] = function () { return yStep; }; - this._applyAnimatedAttributes(cells, "cells", attrToProjector); - cells.exit().remove(); + return attrToProjector; + }; + Grid.prototype._generateDrawSteps = function () { + return [{ attrToProjector: this._generateAttrToProjector(), animator: this._getAnimator("cells") }]; }; return Grid; })(Plot.AbstractXYPlot); @@ -6352,58 +6775,25 @@ var Plottable; function AbstractBarPlot(xScale, yScale) { _super.call(this, xScale, yScale); this._baselineValue = 0; - this._barAlignmentFactor = 0; - this._animators = { - "bars-reset": new Plottable.Animator.Null(), - "bars": new Plottable.Animator.IterativeDelay(), - "baseline": new Plottable.Animator.Null() - }; + this._barAlignmentFactor = 0.5; + this._barLabelFormatter = Plottable.Formatters.identity(); + this._barLabelsEnabled = false; + this._hoverMode = "point"; + this.hideBarsIfAnyAreTooWide = true; this.classed("bar-plot", true); this.project("fill", function () { return Plottable.Core.Colors.INDIGO; }); - // super() doesn't set baseline + this._animators["bars-reset"] = new Plottable.Animator.Null(); + this._animators["bars"] = new Plottable.Animator.Base(); + this._animators["baseline"] = new Plottable.Animator.Null(); this.baseline(this._baselineValue); } AbstractBarPlot.prototype._getDrawer = function (key) { - return new Plottable._Drawer.Rect(key); + return new Plottable._Drawer.Rect(key, this._isVertical); }; AbstractBarPlot.prototype._setup = function () { _super.prototype._setup.call(this); this._baseline = this._renderArea.append("line").classed("baseline", true); }; - // HACKHACK #1106 - should use drawers for paint logic - AbstractBarPlot.prototype._paint = function () { - var _this = this; - var attrToProjector = this._generateAttrToProjector(); - var datasets = this.datasets(); - var primaryScale = this._isVertical ? this._yScale : this._xScale; - var scaledBaseline = primaryScale.scale(this._baselineValue); - var positionAttr = this._isVertical ? "y" : "x"; - var dimensionAttr = this._isVertical ? "height" : "width"; - this._getDrawersInOrder().forEach(function (d, i) { - var dataset = datasets[i]; - var bars = d._renderArea.selectAll("rect").data(dataset.data()); - bars.enter().append("rect"); - if (_this._dataChanged && _this._animate) { - var resetAttrToProjector = _this._generateAttrToProjector(); - resetAttrToProjector[positionAttr] = function () { return scaledBaseline; }; - resetAttrToProjector[dimensionAttr] = function () { return 0; }; - _this._applyAnimatedAttributes(bars, "bars-reset", resetAttrToProjector); - } - var attrToProjector = _this._generateAttrToProjector(); - if (attrToProjector["fill"]) { - bars.attr("fill", attrToProjector["fill"]); // so colors don't animate - } - _this._applyAnimatedAttributes(bars, "bars", attrToProjector); - bars.exit().remove(); - }); - 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._applyAnimatedAttributes(this._baseline, "baseline", baselineAttr); - }; /** * Sets the baseline for the bars to the specified value. * @@ -6448,6 +6838,26 @@ var Plottable; throw new Error("input '" + input + "' can't be parsed as an Extent"); } }; + AbstractBarPlot.prototype.barLabelsEnabled = function (enabled) { + if (enabled === undefined) { + return this._barLabelsEnabled; + } + else { + this._barLabelsEnabled = enabled; + this._render(); + return this; + } + }; + AbstractBarPlot.prototype.barLabelFormatter = function (formatter) { + if (formatter == null) { + return this._barLabelFormatter; + } + else { + this._barLabelFormatter = formatter; + this._render(); + return this; + } + }; AbstractBarPlot.prototype.selectBar = function (xValOrExtent, yValOrExtent, select) { if (select === void 0) { select = true; } if (!this._isSetup) { @@ -6521,6 +6931,47 @@ var Plottable; _super.prototype._updateXDomainer.call(this); } }; + AbstractBarPlot.prototype._additionalPaint = function (time) { + var _this = this; + var primaryScale = this._isVertical ? this._yScale : this._xScale; + 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); + var drawers = this._getDrawersInOrder(); + drawers.forEach(function (d) { return d.removeLabels(); }); + if (this._barLabelsEnabled) { + Plottable._Util.Methods.setTimeout(function () { return _this._drawLabels(); }, time); + } + }; + AbstractBarPlot.prototype._drawLabels = function () { + var drawers = this._getDrawersInOrder(); + var attrToProjector = this._generateAttrToProjector(); + var dataToDraw = this._getDataToDraw(); + this._datasetKeysInOrder.forEach(function (k, i) { return drawers[i].drawText(dataToDraw.get(k), attrToProjector); }); + if (this.hideBarsIfAnyAreTooWide && drawers.some(function (d) { return d._someLabelsTooWide; })) { + drawers.forEach(function (d) { return d.removeLabels(); }); + } + }; + AbstractBarPlot.prototype._generateDrawSteps = function () { + var drawSteps = []; + if (this._dataChanged && this._animate) { + var resetAttrToProjector = this._generateAttrToProjector(); + var primaryScale = this._isVertical ? this._yScale : this._xScale; + var scaledBaseline = primaryScale.scale(this._baselineValue); + var positionAttr = this._isVertical ? "y" : "x"; + var dimensionAttr = this._isVertical ? "height" : "width"; + resetAttrToProjector[positionAttr] = function () { return scaledBaseline; }; + resetAttrToProjector[dimensionAttr] = function () { return 0; }; + drawSteps.push({ attrToProjector: resetAttrToProjector, animator: this._getAnimator("bars-reset") }); + } + drawSteps.push({ attrToProjector: this._generateAttrToProjector(), animator: this._getAnimator("bars") }); + return drawSteps; + }; AbstractBarPlot.prototype._generateAttrToProjector = function () { var _this = this; // Primary scale/direction: the "length" of the bars @@ -6556,8 +7007,65 @@ var Plottable; attrToProjector["height"] = function (d, i) { return Math.abs(scaledBaseline - originalPositionFn(d, i)); }; + var primaryAccessor = this._projectors[primaryAttr].accessor; + if (this.barLabelsEnabled && this.barLabelFormatter) { + attrToProjector["label"] = function (d, i) { + return _this._barLabelFormatter(primaryAccessor(d, i)); + }; + attrToProjector["positive"] = function (d, i) { return originalPositionFn(d, i) <= scaledBaseline; }; + } return attrToProjector; }; + AbstractBarPlot.prototype.hoverMode = function (mode) { + if (mode == null) { + return this._hoverMode; + } + var modeLC = mode.toLowerCase(); + if (modeLC !== "point" && modeLC !== "line") { + throw new Error(mode + " is not a valid hover mode"); + } + this._hoverMode = modeLC; + return this; + }; + AbstractBarPlot.prototype.clearHoverSelection = function () { + this._getDrawersInOrder().forEach(function (d, i) { + d._renderArea.selectAll("rect").classed("not-hovered hovered", false); + }); + }; + //===== Hover logic ===== + AbstractBarPlot.prototype._hoverOverComponent = function (p) { + // no-op + }; + AbstractBarPlot.prototype._hoverOutComponent = function (p) { + this.clearHoverSelection(); + }; + AbstractBarPlot.prototype._doHover = function (p) { + var xPositionOrExtent = p.x; + var yPositionOrExtent = p.y; + if (this._hoverMode === "line") { + var maxExtent = { min: -Infinity, max: Infinity }; + if (this._isVertical) { + yPositionOrExtent = maxExtent; + } + else { + xPositionOrExtent = maxExtent; + } + } + var selectedBars = this.selectBar(xPositionOrExtent, yPositionOrExtent, false); + if (selectedBars) { + this._getDrawersInOrder().forEach(function (d, i) { + d._renderArea.selectAll("rect").classed({ "hovered": false, "not-hovered": true }); + }); + selectedBars.classed({ "hovered": true, "not-hovered": false }); + } + else { + this.clearHoverSelection(); + } + return { + data: selectedBars ? selectedBars.data() : null, + selection: selectedBars + }; + }; AbstractBarPlot._BarAlignmentToFactor = {}; AbstractBarPlot.DEFAULT_WIDTH = 10; return AbstractBarPlot; @@ -6592,7 +7100,6 @@ var Plottable; * Constructs a VerticalBarPlot. * * @constructor - * @param {DatasetInterface | any} dataset The dataset to render. * @param {Scale} xScale The x scale to use. * @param {QuantitativeScale} yScale The y scale to use. */ @@ -6678,20 +7185,24 @@ var Plottable; * Constructs a LinePlot. * * @constructor - * @param {any | DatasetInterface} dataset The dataset to render. * @param {QuantitativeScale} xScale The x scale to use. * @param {QuantitativeScale} yScale The y scale to use. */ function Line(xScale, yScale) { _super.call(this, xScale, yScale); - this._animators = { - "line-reset": new Plottable.Animator.Null(), - "line": new Plottable.Animator.Base().duration(600).easing("exp-in-out") - }; this.classed("line-plot", true); this.project("stroke", function () { return Plottable.Core.Colors.INDIGO; }); // default this.project("stroke-width", function () { return "2px"; }); // default + this._animators["reset"] = new Plottable.Animator.Null(); + this._animators["main"] = new Plottable.Animator.Base().duration(600).easing("exp-in-out"); } + Line.prototype._rejectNullsAndNaNs = function (d, i, projector) { + var value = projector(d, i); + return value != null && value === value; + }; + Line.prototype._getDrawer = function (key) { + return new Plottable._Drawer.Line(key); + }; Line.prototype._getResetYFunction = function () { // gets the y-value generator for the animation start point var yDomain = this._yScale.domain(); @@ -6703,7 +7214,18 @@ var Plottable; var scaledStartValue = this._yScale.scale(startValue); return function (d, i) { return scaledStartValue; }; }; + Line.prototype._generateDrawSteps = function () { + var drawSteps = []; + if (this._dataChanged) { + var attrToProjector = this._generateAttrToProjector(); + attrToProjector["y"] = this._getResetYFunction(); + drawSteps.push({ attrToProjector: attrToProjector, animator: this._getAnimator("reset") }); + } + drawSteps.push({ attrToProjector: this._generateAttrToProjector(), animator: this._getAnimator("main") }); + return drawSteps; + }; Line.prototype._generateAttrToProjector = function () { + var _this = this; var attrToProjector = _super.prototype._generateAttrToProjector.call(this); var wholeDatumAttributes = this._wholeDatumAttributes(); var isSingleDatumAttr = function (attr) { return wholeDatumAttributes.indexOf(attr) === -1; }; @@ -6712,40 +7234,10 @@ var Plottable; var projector = attrToProjector[attribute]; attrToProjector[attribute] = function (data, i) { return data.length > 0 ? projector(data[0], i) : null; }; }); - return attrToProjector; - }; - Line.prototype._rejectNullsAndNaNs = function (d, i, projector) { - var value = projector(d, i); - return value != null && value === value; - }; - // HACKHACK #1106 - should use drawers for paint logic - Line.prototype._paint = function () { - var _this = this; - var attrToProjector = this._generateAttrToProjector(); var xFunction = attrToProjector["x"]; var yFunction = attrToProjector["y"]; - delete attrToProjector["x"]; - delete attrToProjector["y"]; - var line = d3.svg.line().x(xFunction).defined(function (d, i) { return _this._rejectNullsAndNaNs(d, i, xFunction) && _this._rejectNullsAndNaNs(d, i, yFunction); }); - attrToProjector["d"] = line; - var datasets = this.datasets(); - this._getDrawersInOrder().forEach(function (d, i) { - var dataset = datasets[i]; - var linePath; - if (d._renderArea.select(".line").node()) { - linePath = d._renderArea.select(".line"); - } - else { - linePath = d._renderArea.append("path").classed("line", true); - } - linePath.datum(dataset.data()); - if (_this._dataChanged) { - line.y(_this._getResetYFunction()); - _this._applyAnimatedAttributes(linePath, "line-reset", attrToProjector); - } - line.y(yFunction); - _this._applyAnimatedAttributes(linePath, "line", attrToProjector); - }); + attrToProjector["defined"] = function (d, i) { return _this._rejectNullsAndNaNs(d, i, xFunction) && _this._rejectNullsAndNaNs(d, i, yFunction); }; + return attrToProjector; }; Line.prototype._wholeDatumAttributes = function () { return ["x", "y"]; @@ -6776,7 +7268,6 @@ var Plottable; * Constructs an AreaPlot. * * @constructor - * @param {DatasetInterface | any} dataset The dataset to render. * @param {QuantitativeScale} xScale The x scale to use. * @param {QuantitativeScale} yScale The y scale to use. */ @@ -6787,8 +7278,8 @@ var Plottable; this.project("fill", function () { return Plottable.Core.Colors.INDIGO; }); // default this.project("fill-opacity", function () { return 0.25; }); // default this.project("stroke", function () { return Plottable.Core.Colors.INDIGO; }); // default - this._animators["area-reset"] = new Plottable.Animator.Null(); - this._animators["area"] = new Plottable.Animator.Base().duration(600).easing("exp-in-out"); + this._animators["reset"] = new Plottable.Animator.Null(); + this._animators["main"] = new Plottable.Animator.Base().duration(600).easing("exp-in-out"); } Area.prototype._onDatasetUpdate = function () { _super.prototype._onDatasetUpdate.call(this); @@ -6796,6 +7287,9 @@ var Plottable; this._updateYDomainer(); } }; + Area.prototype._getDrawer = function (key) { + return new Plottable._Drawer.Area(key); + }; Area.prototype._updateYDomainer = function () { var _this = this; _super.prototype._updateYDomainer.call(this); @@ -6831,39 +7325,6 @@ var Plottable; Area.prototype._getResetYFunction = function () { return this._generateAttrToProjector()["y0"]; }; - // HACKHACK #1106 - should use drawers for paint logic - Area.prototype._paint = function () { - var _this = this; - _super.prototype._paint.call(this); - var attrToProjector = this._generateAttrToProjector(); - var xFunction = attrToProjector["x"]; - var y0Function = attrToProjector["y0"]; - var yFunction = attrToProjector["y"]; - delete attrToProjector["x"]; - delete attrToProjector["y0"]; - delete attrToProjector["y"]; - var area = d3.svg.area().x(xFunction).y0(y0Function).defined(function (d, i) { return _this._rejectNullsAndNaNs(d, i, xFunction) && _this._rejectNullsAndNaNs(d, i, yFunction); }); - attrToProjector["d"] = area; - var datasets = this.datasets(); - this._getDrawersInOrder().forEach(function (d, i) { - var dataset = datasets[i]; - var areaPath; - if (d._renderArea.select(".area").node()) { - areaPath = d._renderArea.select(".area"); - } - else { - // Make sure to insert the area before the line - areaPath = d._renderArea.insert("path", ".line").classed("area", true); - } - areaPath.datum(dataset.data()); - if (_this._dataChanged) { - area.y1(_this._getResetYFunction()); - _this._applyAnimatedAttributes(areaPath, "area-reset", attrToProjector); - } - area.y1(yFunction); - _this._applyAnimatedAttributes(areaPath, "area", attrToProjector); - }); - }; Area.prototype._wholeDatumAttributes = function () { var wholeDatumAttributes = _super.prototype._wholeDatumAttributes.call(this); wholeDatumAttributes.push("y0"); @@ -6920,36 +7381,22 @@ var Plottable; attrToProjector["y"] = this._isVertical ? attrToProjector["y"] : positionF; return attrToProjector; }; - ClusteredBar.prototype.cluster = function (accessor) { + ClusteredBar.prototype._getDataToDraw = function () { var _this = this; + var accessor = this._isVertical ? this._projectors["x"].accessor : this._projectors["y"].accessor; this.innerScale.domain(this._datasetKeysInOrder); - var clusters = {}; + var clusters = d3.map(); this._datasetKeysInOrder.forEach(function (key) { var data = _this._key2DatasetDrawerKey.get(key).dataset.data(); - clusters[key] = data.map(function (d, i) { + clusters.set(key, data.map(function (d, i) { var val = accessor(d, i); var primaryScale = _this._isVertical ? _this._xScale : _this._yScale; d["_PLOTTABLE_PROTECTED_FIELD_POSITION"] = primaryScale.scale(val) + _this.innerScale.scale(key); return d; - }); + })); }); return clusters; }; - ClusteredBar.prototype._paint = function () { - var attrHash = this._generateAttrToProjector(); - var accessor = this._isVertical ? this._projectors["x"].accessor : this._projectors["y"].accessor; - var clusteredData = this.cluster(accessor); - this._getDrawersInOrder().forEach(function (d) { return d.draw(clusteredData[d.key], attrHash); }); - var primaryScale = this._isVertical ? this._yScale : this._xScale; - 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._applyAnimatedAttributes(this._baseline, "baseline", baselineAttr); - }; return ClusteredBar; })(Plot.AbstractBarPlot); Plot.ClusteredBar = ClusteredBar; @@ -6973,16 +7420,23 @@ var Plottable; _super.apply(this, arguments); this.stackedExtent = [0, 0]; } + AbstractStacked.prototype.project = function (attrToSet, accessor, scale) { + _super.prototype.project.call(this, attrToSet, accessor, scale); + if (this._projectors["x"] && this._projectors["y"] && (attrToSet === "x" || attrToSet === "y")) { + this._updateStackOffsets(); + } + return this; + }; AbstractStacked.prototype._onDatasetUpdate = function () { _super.prototype._onDatasetUpdate.call(this); // HACKHACK Caused since onDataSource is called before projectors are set up. Should be fixed by #803 if (this._datasetKeysInOrder && this._projectors["x"] && this._projectors["y"]) { - this.updateStackOffsets(); + this._updateStackOffsets(); } }; - AbstractStacked.prototype.updateStackOffsets = function () { - var dataMapArray = this.generateDefaultMapArray(); - var domainKeys = this.getDomainKeys(); + AbstractStacked.prototype._updateStackOffsets = function () { + var dataMapArray = this._generateDefaultMapArray(); + var domainKeys = this._getDomainKeys(); var positiveDataMapArray = dataMapArray.map(function (dataMap) { return Plottable._Util.Methods.populateMap(domainKeys, function (domainKey) { return { key: domainKey, value: Math.max(0, dataMap.get(domainKey).value) }; @@ -6993,43 +7447,47 @@ var Plottable; return { key: domainKey, value: Math.min(dataMap.get(domainKey).value, 0) }; }); }); - this.setDatasetStackOffsets(this.stack(positiveDataMapArray), this.stack(negativeDataMapArray)); - this.updateStackExtents(); + this._setDatasetStackOffsets(this._stack(positiveDataMapArray), this._stack(negativeDataMapArray)); + this._updateStackExtents(); }; - AbstractStacked.prototype.updateStackExtents = function () { + AbstractStacked.prototype._updateStackExtents = function () { var datasets = this.datasets(); - var valueAccessor = this.valueAccessor(); + var valueAccessor = this._valueAccessor(); var maxStackExtent = Plottable._Util.Methods.max(datasets, function (dataset) { return Plottable._Util.Methods.max(dataset.data(), function (datum) { - return valueAccessor(datum) + datum["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"]; - }); - }); + return +valueAccessor(datum) + datum["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"]; + }, 0); + }, 0); var minStackExtent = Plottable._Util.Methods.min(datasets, function (dataset) { return Plottable._Util.Methods.min(dataset.data(), function (datum) { - return valueAccessor(datum) + datum["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"]; - }); - }); + return +valueAccessor(datum) + datum["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"]; + }, 0); + }, 0); this.stackedExtent = [Math.min(minStackExtent, 0), Math.max(0, maxStackExtent)]; }; /** * Feeds the data through d3's stack layout function which will calculate * the stack offsets and use the the function declared in .out to set the offsets on the data. */ - AbstractStacked.prototype.stack = function (dataArray) { + AbstractStacked.prototype._stack = function (dataArray) { var _this = this; + // HACKHACK d3's stack layout logic crashes on 0-length dataArray https://github.com/mbostock/d3/issues/2004 + if (dataArray.length === 0) { + return dataArray; + } var outFunction = function (d, y0, y) { d.offset = y0; }; - d3.layout.stack().x(function (d) { return d.key; }).y(function (d) { return d.value; }).values(function (d) { return _this.getDomainKeys().map(function (domainKey) { return d.get(domainKey); }); }).out(outFunction)(dataArray); + d3.layout.stack().x(function (d) { return d.key; }).y(function (d) { return +d.value; }).values(function (d) { return _this._getDomainKeys().map(function (domainKey) { return d.get(domainKey); }); }).out(outFunction)(dataArray); return dataArray; }; /** * After the stack offsets have been determined on each separate dataset, the offsets need * to be determined correctly on the overall datasets */ - AbstractStacked.prototype.setDatasetStackOffsets = function (positiveDataMapArray, negativeDataMapArray) { - var keyAccessor = this.keyAccessor(); - var valueAccessor = this.valueAccessor(); + AbstractStacked.prototype._setDatasetStackOffsets = function (positiveDataMapArray, negativeDataMapArray) { + var keyAccessor = this._keyAccessor(); + var valueAccessor = this._valueAccessor(); this.datasets().forEach(function (dataset, datasetIndex) { var positiveDataMap = positiveDataMapArray[datasetIndex]; var negativeDataMap = negativeDataMapArray[datasetIndex]; @@ -7047,8 +7505,8 @@ var Plottable; }); }); }; - AbstractStacked.prototype.getDomainKeys = function () { - var keyAccessor = this.keyAccessor(); + AbstractStacked.prototype._getDomainKeys = function () { + var keyAccessor = this._keyAccessor(); var domainKeys = d3.set(); var datasets = this.datasets(); datasets.forEach(function (dataset) { @@ -7058,11 +7516,11 @@ var Plottable; }); return domainKeys.values(); }; - AbstractStacked.prototype.generateDefaultMapArray = function () { - var keyAccessor = this.keyAccessor(); - var valueAccessor = this.valueAccessor(); + AbstractStacked.prototype._generateDefaultMapArray = function () { + var keyAccessor = this._keyAccessor(); + var valueAccessor = this._valueAccessor(); var datasets = this.datasets(); - var domainKeys = this.getDomainKeys(); + var domainKeys = this._getDomainKeys(); var dataMapArray = datasets.map(function () { return Plottable._Util.Methods.populateMap(domainKeys, function (domainKey) { return { key: domainKey, value: 0 }; @@ -7090,10 +7548,10 @@ var Plottable; primaryScale._removeExtent(this._plottableID.toString(), "_PLOTTABLE_PROTECTED_FIELD_STACK_EXTENT"); } }; - AbstractStacked.prototype.keyAccessor = function () { + AbstractStacked.prototype._keyAccessor = function () { return this._isVertical ? this._projectors["x"].accessor : this._projectors["y"].accessor; }; - AbstractStacked.prototype.valueAccessor = function () { + AbstractStacked.prototype._valueAccessor = function () { return this._isVertical ? this._projectors["y"].accessor : this._projectors["x"].accessor; }; return AbstractStacked; @@ -7130,14 +7588,22 @@ var Plottable; this._isVertical = true; } StackedArea.prototype._getDrawer = function (key) { - return new Plottable._Drawer.Area(key); + return new Plottable._Drawer.Area(key).drawLine(false); }; StackedArea.prototype._setup = function () { _super.prototype._setup.call(this); this._baseline = this._renderArea.append("line").classed("baseline", true); }; - StackedArea.prototype._paint = function () { - _super.prototype._paint.call(this); + StackedArea.prototype._updateStackOffsets = function () { + var domainKeys = this._getDomainKeys(); + var keyAccessor = this._isVertical ? this._projectors["x"].accessor : this._projectors["y"].accessor; + var keySets = this.datasets().map(function (dataset) { return d3.set(dataset.data().map(function (datum, i) { return keyAccessor(datum, i).toString(); })).values(); }); + if (keySets.some(function (keySet) { return keySet.length !== domainKeys.length; })) { + Plottable._Util.Methods.warn("the domains across the datasets are not the same. Plot may produce unintended behavior."); + } + _super.prototype._updateStackOffsets.call(this); + }; + StackedArea.prototype._additionalPaint = function () { var scaledBaseline = this._yScale.scale(this._baselineValue); var baselineAttr = { "x1": 0, @@ -7145,7 +7611,7 @@ var Plottable; "x2": this.width(), "y2": scaledBaseline }; - this._applyAnimatedAttributes(this._baseline, "baseline", baselineAttr); + this._getAnimator("baseline").animate(this._baseline, baselineAttr); }; StackedArea.prototype._updateYDomainer = function () { _super.prototype._updateYDomainer.call(this); @@ -7163,13 +7629,9 @@ var Plottable; StackedArea.prototype._generateAttrToProjector = function () { var _this = this; var attrToProjector = _super.prototype._generateAttrToProjector.call(this); - var xFunction = attrToProjector["x"]; - var yFunction = function (d) { return _this._yScale.scale(d.y + d["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"]); }; - var y0Function = function (d) { return _this._yScale.scale(d["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"]); }; - delete attrToProjector["x"]; - delete attrToProjector["y0"]; - delete attrToProjector["y"]; - attrToProjector["d"] = d3.svg.area().x(xFunction).y0(y0Function).y1(yFunction); + var yAccessor = this._projectors["y"].accessor; + attrToProjector["y"] = function (d) { return _this._yScale.scale(+yAccessor(d) + d["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"]); }; + attrToProjector["y0"] = function (d) { return _this._yScale.scale(d["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"]); }; // Align fill with first index var fillProjector = attrToProjector["fill"]; attrToProjector["fill"] = function (d, i) { return (d && d[0]) ? fillProjector(d[0], i) : null; }; @@ -7207,23 +7669,24 @@ var Plottable; if (isVertical === void 0) { isVertical = true; } this._isVertical = isVertical; // Has to be set before super() this._baselineValue = 0; - this._barAlignmentFactor = 0.5; _super.call(this, xScale, yScale); this.classed("bar-plot", true); this.project("fill", function () { return Plottable.Core.Colors.INDIGO; }); this.baseline(this._baselineValue); this._isVertical = isVertical; } - StackedBar.prototype._setup = function () { - Plot.AbstractBarPlot.prototype._setup.call(this); - }; - StackedBar.prototype._getAnimator = function (drawer, index) { - var primaryScale = this._isVertical ? this._yScale : this._xScale; - var scaledBaseline = primaryScale.scale(this._baselineValue); - return new Plottable.Animator.MovingRect(scaledBaseline, this._isVertical); - }; - StackedBar.prototype._getDrawer = function (key) { - return Plot.AbstractBarPlot.prototype._getDrawer.apply(this, [key]); + StackedBar.prototype._getAnimator = function (key) { + if (this._animate && this._animateOnNextRender) { + if (this._animators[key]) { + return this._animators[key]; + } + else if (key === "stacked-bar") { + var primaryScale = this._isVertical ? this._yScale : this._xScale; + var scaledBaseline = primaryScale.scale(this._baselineValue); + return new Plottable.Animator.MovingRect(scaledBaseline, this._isVertical); + } + } + return new Plottable.Animator.Null(); }; StackedBar.prototype._generateAttrToProjector = function () { var _this = this; @@ -7232,41 +7695,58 @@ var Plottable; var primaryScale = this._isVertical ? this._yScale : this._xScale; var primaryAccessor = this._projectors[primaryAttr].accessor; var getStart = function (d) { return primaryScale.scale(d["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"]); }; - var getEnd = function (d) { return primaryScale.scale(primaryAccessor(d) + d["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"]); }; + var getEnd = function (d) { return primaryScale.scale(+primaryAccessor(d) + d["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"]); }; var heightF = function (d) { return Math.abs(getEnd(d) - getStart(d)); }; var widthF = attrToProjector["width"]; attrToProjector["height"] = this._isVertical ? heightF : widthF; attrToProjector["width"] = this._isVertical ? widthF : heightF; - var attrFunction = function (d) { return primaryAccessor(d) < 0 ? getStart(d) : getEnd(d); }; + var attrFunction = function (d) { return +primaryAccessor(d) < 0 ? getStart(d) : getEnd(d); }; attrToProjector[primaryAttr] = function (d) { return _this._isVertical ? attrFunction(d) : attrFunction(d) - heightF(d); }; return attrToProjector; }; - StackedBar.prototype._paint = function () { - _super.prototype._paint.call(this); - var primaryScale = this._isVertical ? this._yScale : this._xScale; - 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._baseline.attr(baselineAttr); + StackedBar.prototype._generateDrawSteps = function () { + return [{ attrToProjector: this._generateAttrToProjector(), animator: this._getAnimator("stacked-bar") }]; + }; + StackedBar.prototype.project = function (attrToSet, accessor, scale) { + _super.prototype.project.call(this, attrToSet, accessor, scale); + Plot.AbstractStacked.prototype.project.apply(this, [attrToSet, accessor, scale]); + return this; + }; + StackedBar.prototype._onDatasetUpdate = function () { + _super.prototype._onDatasetUpdate.call(this); + Plot.AbstractStacked.prototype._onDatasetUpdate.apply(this); + return this; + }; + //===== Stack logic from AbstractStackedPlot ===== + StackedBar.prototype._updateStackOffsets = function () { + Plot.AbstractStacked.prototype._updateStackOffsets.call(this); + }; + StackedBar.prototype._updateStackExtents = function () { + Plot.AbstractStacked.prototype._updateStackExtents.call(this); + }; + StackedBar.prototype._stack = function (dataArray) { + return Plot.AbstractStacked.prototype._stack.call(this, dataArray); + }; + StackedBar.prototype._setDatasetStackOffsets = function (positiveDataMapArray, negativeDataMapArray) { + Plot.AbstractStacked.prototype._setDatasetStackOffsets.call(this, positiveDataMapArray, negativeDataMapArray); + }; + StackedBar.prototype._getDomainKeys = function () { + return Plot.AbstractStacked.prototype._getDomainKeys.call(this); }; - StackedBar.prototype.baseline = function (value) { - return Plot.AbstractBarPlot.prototype.baseline.apply(this, [value]); + StackedBar.prototype._generateDefaultMapArray = function () { + return Plot.AbstractStacked.prototype._generateDefaultMapArray.call(this); }; - StackedBar.prototype._updateDomainer = function (scale) { - return Plot.AbstractBarPlot.prototype._updateDomainer.apply(this, [scale]); + StackedBar.prototype._updateScaleExtents = function () { + Plot.AbstractStacked.prototype._updateScaleExtents.call(this); }; - StackedBar.prototype._updateXDomainer = function () { - return Plot.AbstractBarPlot.prototype._updateXDomainer.apply(this); + StackedBar.prototype._keyAccessor = function () { + return Plot.AbstractStacked.prototype._keyAccessor.call(this); }; - StackedBar.prototype._updateYDomainer = function () { - return Plot.AbstractBarPlot.prototype._updateYDomainer.apply(this); + StackedBar.prototype._valueAccessor = function () { + return Plot.AbstractStacked.prototype._valueAccessor.call(this); }; return StackedBar; - })(Plot.AbstractStacked); + })(Plot.AbstractBarPlot); Plot.StackedBar = StackedBar; })(Plottable.Plot || (Plottable.Plot = {})); var Plot = Plottable.Plot; @@ -7285,6 +7765,9 @@ var Plottable; var Null = (function () { function Null() { } + Null.prototype.getTiming = function (selection) { + return 0; + }; Null.prototype.animate = function (selection, attrToProjector) { return selection.attr(attrToProjector); }; @@ -7301,6 +7784,15 @@ var Plottable; (function (Animator) { /** * The base animator implementation with easing, duration, and delay. + * + * The maximum delay between animations can be configured with maxIterativeDelay. + * + * The maximum total animation duration can be configured with maxTotalDuration. + * maxTotalDuration does not set actual total animation duration. + * + * The actual interval delay is calculated by following formula: + * min(maxIterativeDelay(), + * max(maxTotalDuration() - duration(), 0) / ) */ var Base = (function () { /** @@ -7312,12 +7804,24 @@ var Plottable; this._duration = Base.DEFAULT_DURATION_MILLISECONDS; this._delay = Base.DEFAULT_DELAY_MILLISECONDS; this._easing = Base.DEFAULT_EASING; + this._maxIterativeDelay = Base.DEFAULT_MAX_ITERATIVE_DELAY_MILLISECONDS; + this._maxTotalDuration = Base.DEFAULT_MAX_TOTAL_DURATION_MILLISECONDS; } + Base.prototype.getTiming = function (numberOfIterations) { + var maxDelayForLastIteration = Math.max(this.maxTotalDuration() - this.duration(), 0); + var adjustedIterativeDelay = Math.min(this.maxIterativeDelay(), maxDelayForLastIteration / Math.max(numberOfIterations - 1, 1)); + var time = adjustedIterativeDelay * numberOfIterations + this.delay() + this.duration(); + return time; + }; Base.prototype.animate = function (selection, attrToProjector) { - return selection.transition().ease(this.easing()).duration(this.duration()).delay(this.delay()).attr(attrToProjector); + var _this = this; + var numberOfIterations = selection[0].length; + var maxDelayForLastIteration = Math.max(this.maxTotalDuration() - this.duration(), 0); + var adjustedIterativeDelay = Math.min(this.maxIterativeDelay(), maxDelayForLastIteration / Math.max(numberOfIterations - 1, 1)); + return selection.transition().ease(this.easing()).duration(this.duration()).delay(function (d, i) { return _this.delay() + adjustedIterativeDelay * i; }).attr(attrToProjector); }; Base.prototype.duration = function (duration) { - if (duration === undefined) { + if (duration == null) { return this._duration; } else { @@ -7326,7 +7830,7 @@ var Plottable; } }; Base.prototype.delay = function (delay) { - if (delay === undefined) { + if (delay == null) { return this._delay; } else { @@ -7335,7 +7839,7 @@ var Plottable; } }; Base.prototype.easing = function (easing) { - if (easing === undefined) { + if (easing == null) { return this._easing; } else { @@ -7343,69 +7847,8 @@ var Plottable; return this; } }; - /** - * The default duration of the animation in milliseconds - */ - Base.DEFAULT_DURATION_MILLISECONDS = 300; - /** - * The default starting delay of the animation in milliseconds - */ - Base.DEFAULT_DELAY_MILLISECONDS = 0; - /** - * The default easing of the animation - */ - Base.DEFAULT_EASING = "exp-out"; - return Base; - })(); - Animator.Base = Base; - })(Plottable.Animator || (Plottable.Animator = {})); - var Animator = Plottable.Animator; -})(Plottable || (Plottable = {})); - -/// -var __extends = this.__extends || function (d, b) { - for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; - function __() { this.constructor = d; } - __.prototype = b.prototype; - d.prototype = new __(); -}; -var Plottable; -(function (Plottable) { - (function (Animator) { - /** - * An animator that delays the animation of the attributes using the index - * of the selection data. - * - * The maximum delay between animations can be configured with maxIterativeDelay. - * - * The maximum total animation duration can be configured with maxTotalDuration. - * maxTotalDuration does not set actual total animation duration. - * - * The actual interval delay is calculated by following formula: - * min(maxIterativeDelay(), - * max(totalDurationLimit() - duration(), 0) / ) - */ - var IterativeDelay = (function (_super) { - __extends(IterativeDelay, _super); - /** - * Constructs an animator with a start delay between each selection animation - * - * @constructor - */ - function IterativeDelay() { - _super.call(this); - this._maxIterativeDelay = IterativeDelay.DEFAULT_MAX_ITERATIVE_DELAY_MILLISECONDS; - this._maxTotalDuration = IterativeDelay.DEFAULT_MAX_TOTAL_DURATION_MILLISECONDS; - } - IterativeDelay.prototype.animate = function (selection, attrToProjector) { - var _this = this; - var numberOfIterations = selection[0].length; - var maxDelayForLastIteration = Math.max(this.maxTotalDuration() - this.duration(), 0); - var adjustedIterativeDelay = Math.min(this.maxIterativeDelay(), maxDelayForLastIteration / numberOfIterations); - return selection.transition().ease(this.easing()).duration(this.duration()).delay(function (d, i) { return _this.delay() + adjustedIterativeDelay * i; }).attr(attrToProjector); - }; - IterativeDelay.prototype.maxIterativeDelay = function (maxIterDelay) { - if (maxIterDelay === undefined) { + Base.prototype.maxIterativeDelay = function (maxIterDelay) { + if (maxIterDelay == null) { return this._maxIterativeDelay; } else { @@ -7413,7 +7856,7 @@ var Plottable; return this; } }; - IterativeDelay.prototype.maxTotalDuration = function (maxDuration) { + Base.prototype.maxTotalDuration = function (maxDuration) { if (maxDuration == null) { return this._maxTotalDuration; } @@ -7422,17 +7865,29 @@ var Plottable; return this; } }; + /** + * The default duration of the animation in milliseconds + */ + Base.DEFAULT_DURATION_MILLISECONDS = 300; + /** + * The default starting delay of the animation in milliseconds + */ + Base.DEFAULT_DELAY_MILLISECONDS = 0; /** * The default maximum start delay between each start of an animation */ - IterativeDelay.DEFAULT_MAX_ITERATIVE_DELAY_MILLISECONDS = 15; + Base.DEFAULT_MAX_ITERATIVE_DELAY_MILLISECONDS = 15; /** * The default maximum total animation duration */ - IterativeDelay.DEFAULT_MAX_TOTAL_DURATION_MILLISECONDS = 600; - return IterativeDelay; - })(Animator.Base); - Animator.IterativeDelay = IterativeDelay; + Base.DEFAULT_MAX_TOTAL_DURATION_MILLISECONDS = 600; + /** + * The default easing of the animation + */ + Base.DEFAULT_EASING = "exp-out"; + return Base; + })(); + Animator.Base = Base; })(Plottable.Animator || (Plottable.Animator = {})); var Animator = Plottable.Animator; })(Plottable || (Plottable = {})); @@ -7807,6 +8262,7 @@ var Plottable; BarHover.prototype._anchor = function (barPlot, hitBox) { var _this = this; _super.prototype._anchor.call(this, barPlot, hitBox); + Plottable._Util.Methods.warn("Interaction.BarHover is deprecated; please use Interaction.Hover instead"); this.plotIsVertical = this._componentToListenTo._isVertical; this.dispatcher = new Plottable.Dispatcher.Mouse(this._hitBox); this.dispatcher.mousemove(function (p) { @@ -8206,6 +8662,122 @@ var Plottable; var Interaction = Plottable.Interaction; })(Plottable || (Plottable = {})); +/// +var __extends = this.__extends || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + __.prototype = b.prototype; + d.prototype = new __(); +}; +var Plottable; +(function (Plottable) { + (function (Interaction) { + var Hover = (function (_super) { + __extends(Hover, _super); + function Hover() { + _super.apply(this, arguments); + this.currentHoverData = { + data: null, + selection: null + }; + } + Hover.prototype._anchor = function (component, hitBox) { + var _this = this; + _super.prototype._anchor.call(this, component, hitBox); + this.dispatcher = new Plottable.Dispatcher.Mouse(this._hitBox); + this.dispatcher.mouseover(function (p) { + _this._componentToListenTo._hoverOverComponent(p); + _this.handleHoverOver(p); + }); + this.dispatcher.mouseout(function (p) { + _this._componentToListenTo._hoverOutComponent(p); + _this.safeHoverOut(_this.currentHoverData); + _this.currentHoverData = { + data: null, + selection: null + }; + }); + this.dispatcher.mousemove(function (p) { return _this.handleHoverOver(p); }); + this.dispatcher.connect(); + }; + /** + * Returns a HoverData consisting of all data and selections in a but not in b. + */ + Hover.diffHoverData = function (a, b) { + if (a.data == null || b.data == null) { + return a; + } + var notInB = function (d) { return b.data.indexOf(d) === -1; }; + var diffData = a.data.filter(notInB); + if (diffData.length === 0) { + return { + data: null, + selection: null + }; + } + var diffSelection = a.selection.filter(notInB); + return { + data: diffData, + selection: diffSelection + }; + }; + Hover.prototype.handleHoverOver = function (p) { + var lastHoverData = this.currentHoverData; + var newHoverData = this._componentToListenTo._doHover(p); + var outData = Hover.diffHoverData(lastHoverData, newHoverData); + this.safeHoverOut(outData); + var overData = Hover.diffHoverData(newHoverData, lastHoverData); + this.safeHoverOver(overData); + this.currentHoverData = newHoverData; + }; + Hover.prototype.safeHoverOut = function (outData) { + if (this.hoverOutCallback && outData.data) { + this.hoverOutCallback(outData); + } + }; + Hover.prototype.safeHoverOver = function (overData) { + if (this.hoverOverCallback && overData.data) { + this.hoverOverCallback(overData); + } + }; + /** + * Attaches an callback to be called when the user mouses over an element. + * + * @param {(hoverData: HoverData) => any} callback The callback to be called. + * The callback will be passed data for newly hovered-over elements. + * @return {Interaction.Hover} The calling Interaction.Hover. + */ + Hover.prototype.onHoverOver = function (callback) { + this.hoverOverCallback = callback; + return this; + }; + /** + * Attaches a callback to be called when the user mouses off of an element. + * + * @param {(hoverData: HoverData) => any} callback The callback to be called. + * The callback will be passed data from the hovered-out elements. + * @return {Interaction.Hover} The calling Interaction.Hover. + */ + Hover.prototype.onHoverOut = function (callback) { + this.hoverOutCallback = callback; + return this; + }; + /** + * Retrieves the HoverData associated with the elements the user is currently hovering over. + * + * @return {HoverData} The data and selection corresponding to the elements + * the user is currently hovering over. + */ + Hover.prototype.getCurrentHoverData = function () { + return this.currentHoverData; + }; + return Hover; + })(Interaction.AbstractInteraction); + Interaction.Hover = Hover; + })(Plottable.Interaction || (Plottable.Interaction = {})); + var Interaction = Plottable.Interaction; +})(Plottable || (Plottable = {})); + /// var __extends = this.__extends || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; diff --git a/plottable.min.js b/plottable.min.js index 5db2e42a6d..e13c654393 100644 --- a/plottable.min.js +++ b/plottable.min.js @@ -1,5 +1,5 @@ -var Plottable;!function(a){!function(a){!function(a){function b(a,b,c){return Math.min(b,c)<=a&&a<=Math.max(b,c)}function c(a){null!=window.console&&(null!=window.console.warn?console.warn(a):null!=window.console.log&&console.log(a))}function d(a,b){if(a.length!==b.length)throw new Error("attempted to add arrays of unequal length");return a.map(function(c,d){return a[d]+b[d]})}function e(a,b){var c=d3.set();return a.forEach(function(a){b.has(a)&&c.add(a)}),c}function f(a){return"function"==typeof a?a:"string"==typeof a&&"#"!==a[0]?function(b){return b[a]}:function(){return a}}function g(a,b){var c=f(a);return function(a,d){var e=b.datasets(),f=e.length>0?e[0]:null,g=f?f.metadata():null;return c(a,d,g)}}function h(a,b){var c=d3.set();return a.forEach(function(a){return c.add(a)}),b.forEach(function(a){return c.add(a)}),c}function i(a,b){var c=d3.map();return a.forEach(function(a,d){c.set(a,b(a,d))}),c}function j(a){var b=d3.set(),c=[];return a.forEach(function(a){b.has(a)||(b.add(a),c.push(a))}),c}function k(a,b){for(var c=[],d=0;b>d;d++)c[d]="function"==typeof a?a(d):a;return c}function l(a){return Array.prototype.concat.apply([],a)}function m(a,b){if(null==a||null==b)return a===b;if(a.length!==b.length)return!1;for(var c=0;cd;){var f=d+e>>>1,g=null==c?b[f]:c(b[f]);a>g?d=f+1:e=f}return d}a.sortedIndex=b}(a.OpenSource||(a.OpenSource={}));a.OpenSource}(a._Util||(a._Util={}));a._Util}(Plottable||(Plottable={}));var Plottable;!function(a){!function(a){var b=function(){function a(){this.counter={}}return a.prototype.setDefault=function(a){null==this.counter[a]&&(this.counter[a]=0)},a.prototype.increment=function(a){return this.setDefault(a),++this.counter[a]},a.prototype.decrement=function(a){return this.setDefault(a),--this.counter[a]},a.prototype.get=function(a){return this.setDefault(a),this.counter[a]},a}();a.IDCounter=b}(a._Util||(a._Util={}));a._Util}(Plottable||(Plottable={}));var Plottable;!function(a){!function(a){var b=function(){function a(){this.keyValuePairs=[]}return a.prototype.set=function(a,b){if(a!==a)throw new Error("NaN may not be used as a key to the StrictEqualityAssociativeArray");for(var c=0;cb){var h=e("."),i=Math.floor(b/h);return"...".substr(0,i)}for(;f+g>b;)d=d.substr(0,d.length-1).trim(),f=e(d);if(e(d+"...")>b)throw new Error("addEllipsesToLine failed :(");return d+"..."}function i(b,c,d,e,f,g){void 0===f&&(f="left"),void 0===g&&(g="top");var h={left:0,center:.5,right:1},i={top:0,center:.5,bottom:1};if(void 0===h[f]||void 0===i[g])throw new Error("unrecognized alignment x:"+f+", y:"+g);var j=c.append("g"),k=j.append("text");k.text(b);var l=a.DOM.getBBox(k),m=l.height,n=l.width;if(n>d||m>e)return a.Methods.warn("Insufficient space to fit text: "+b),k.text(""),{width:0,height:0};var o={left:"start",center:"middle",right:"end"},p=o[f],q=d*h[f],r=e*i[g],s=.85-i[g];return k.attr("text-anchor",p).attr("y",s+"em"),a.DOM.translate(j,q,r),{width:n,height:m}}function j(a,b,c,d,e,f,g){if(void 0===e&&(e="left"),void 0===f&&(f="top"),void 0===g&&(g="right"),"right"!==g&&"left"!==g)throw new Error("unrecognized rotation: "+g);var h="right"===g,j={left:"bottom",right:"top",center:"center",top:"left",bottom:"right"},k={left:"top",right:"bottom",center:"center",top:"right",bottom:"left"},l=h?j:k,m=b.append("g"),n=i(a,m,d,c,l[f],l[e]),o=d3.transform("");return o.rotate="right"===g?90:-90,o.translate=[h?c:0,h?0:d],m.attr("transform",o.toString()),m.classed("rotated-"+g,!0),n}function k(d,e,f,g,h,j){void 0===h&&(h="left"),void 0===j&&(j="top");var k=c(e.append("text"))(b.HEIGHT_TEXT).height,l=0,m=e.append("g");d.forEach(function(b,c){var d=m.append("g");a.DOM.translate(d,0,c*k);var e=i(b,d,f,k,h,j);e.width>l&&(l=e.width)});var n=k*d.length,o=g-n,p={center:.5,top:0,bottom:1};return a.DOM.translate(m,0,o*p[j]),{width:l,height:n}}function l(d,e,f,g,h,i,k){void 0===h&&(h="left"),void 0===i&&(i="top"),void 0===k&&(k="left");var l=c(e.append("text"))(b.HEIGHT_TEXT).height,m=0,n=e.append("g");d.forEach(function(b,c){var d=n.append("g");a.DOM.translate(d,c*l,0);var e=j(b,d,l,g,h,i,k);e.height>m&&(m=e.height)});var o=l*d.length,p=f-o,q={center:.5,left:0,right:1};return a.DOM.translate(n,p*q[h],0),{width:o,height:m}}function m(b,c,d,e,f,g){if(void 0===f&&(f="horizontal"),-1===["left","right","horizontal"].indexOf(f))throw new Error("Unrecognized orientation to writeText: "+f);var h="horizontal"===f,i=h?c:d,j=h?d:c,m=a.WordWrap.breakTextToFitRect(b,i,j,e);if(0===m.lines.length)return{textFits:m.textFits,usedWidth:0,usedHeight:0};var n,o;if(null==g){var p=h?a.Methods.max:d3.sum,q=h?d3.sum:a.Methods.max;n=p(m.lines,function(a){return e(a).width}),o=q(m.lines,function(a){return e(a).height})}else{var r=g.g.append("g").classed("writeText-inner-g",!0),s=h?k:l,t=s.call(this,m.lines,r,c,d,g.xAlign,g.yAlign,f);n=t.width,o=t.height}return{textFits:m.textFits,usedWidth:n,usedHeight:o}}b.HEIGHT_TEXT="bqpdl",b.getTextMeasurer=c;var n="a",o=function(){function b(b){var g=this;this.cache=new a.Cache(c(b),n,a.Methods.objEq),this.measure=d(e(f(function(a){return g.cache.get(a)})))}return b.prototype.clear=function(){return this.cache.clear(),this},b}();b.CachingCharacterMeasurer=o,b.getTruncatedText=g,b.addEllipsesToLine=h,b.writeLineHorizontally=i,b.writeLineVertically=j,b.writeText=m}(a.Text||(a.Text={}));a.Text}(a._Util||(a._Util={}));a._Util}(Plottable||(Plottable={}));var Plottable;!function(a){!function(a){!function(b){function c(b,c,e,f){var g=function(a){return f(a).width},h=d(b,c,g),i=f("hello world").height,j=Math.floor(e/i),k=j>=h.length;return k||(h=h.splice(0,j),j>0&&(h[j-1]=a.Text.addEllipsesToLine(h[j-1],c,f))),{originalText:b,lines:h,textFits:k}}function d(a,b,c){for(var d=[],e=a.split("\n"),g=0,h=e.length;h>g;g++){var i=e[g];null!==i?d=d.concat(f(i,b,c)):d.push("")}return d}function e(b,c,d){var e=h(b),f=e.map(d),g=a.Methods.max(f);return c>=g}function f(a,b,c){for(var d,e=[],f=h(a),i="",j=0;d||je;e++){var g=a[e];""===c||j(c[0],g,d)?c+=g:(b.push(c),c=g),d=g}return c&&b.push(c),b}function i(a){return null==a?!0:""===a.trim()}function j(a,b,c){return m.test(a)&&m.test(b)?!0:m.test(a)||m.test(b)?!1:l.test(c)||k.test(b)?!1:!0}var k=/[{\[]/,l=/[!"%),-.:;?\]}]/,m=/^\s+$/;b.breakTextToFitRect=c,b.canWrapWithoutBreakingWords=e}(a.WordWrap||(a.WordWrap={}));a.WordWrap}(a._Util||(a._Util={}));a._Util}(Plottable||(Plottable={}));var Plottable;!function(a){!function(a){!function(a){function b(a){var b;try{b=a.node().getBBox()}catch(c){b={x:0,y:0,width:0,height:0}}return b}function c(b){null!=window.requestAnimationFrame?window.requestAnimationFrame(b):setTimeout(b,a.POLYFILL_TIMEOUT_MSEC)}function d(a,b){var c=a.getPropertyValue(b),d=parseFloat(c);return d!==d?0:d}function e(a){for(var b=a.node();null!==b&&"svg"!==b.nodeName;)b=b.parentNode;return null==b}function f(a){var b=window.getComputedStyle(a);return d(b,"width")+d(b,"padding-left")+d(b,"padding-right")+d(b,"border-left-width")+d(b,"border-right-width")}function g(a){var b=window.getComputedStyle(a);return d(b,"height")+d(b,"padding-top")+d(b,"padding-bottom")+d(b,"border-top-width")+d(b,"border-bottom-width")}function h(a){var b=a.node().clientWidth;if(0===b){var c=a.attr("width");if(-1!==c.indexOf("%")){for(var d=a.node().parentNode;null!=d&&0===d.clientWidth;)d=d.parentNode;if(null==d)throw new Error("Could not compute width of element");b=d.clientWidth*parseFloat(c)/100}else b=parseFloat(c)}return b}function i(a,b,c){var d=d3.transform(a.attr("transform"));return null==b?d.translate:(c=null==c?0:c,d.translate[0]=b,d.translate[1]=c,a.attr("transform",d.toString()),a)}function j(a,b){return a.rightb.right?!1:a.bottomb.bottom?!1:!0}a.getBBox=b,a.POLYFILL_TIMEOUT_MSEC=1e3/60,a.requestAnimationFramePolyfill=c,a.isSelectionRemovedFromSVG=e,a.getElementWidth=f,a.getElementHeight=g,a.getSVGPixelWidth=h,a.translate=i,a.boxesOverlap=j}(a.DOM||(a.DOM={}));a.DOM}(a._Util||(a._Util={}));a._Util}(Plottable||(Plottable={}));var Plottable;!function(a){a.MILLISECONDS_IN_ONE_DAY=864e5,function(b){function c(a,c,d,e){void 0===a&&(a=2),void 0===c&&(c="$"),void 0===d&&(d=!0),void 0===e&&(e=!0);var f=b.fixed(a);return function(a){var b=f(Math.abs(a));return e&&l(Math.abs(a),b)?"":(""!==b&&(d?b=c+b:b+=c,0>a&&(b="-"+b)),b)}}function d(a,b){return void 0===a&&(a=3),void 0===b&&(b=!0),k(a),function(c){var d=c.toFixed(a);return b&&l(c,d)?"":d}}function e(a,b){return void 0===a&&(a=3),void 0===b&&(b=!0),k(a),function(c){if("number"==typeof c){var d=Math.pow(10,a),e=String(Math.round(c*d)/d);return b&&l(c,e)?"":e}return String(c)}}function f(){return function(a){return String(a)}}function g(a,c){void 0===a&&(a=0),void 0===c&&(c=!0);var d=b.fixed(a,c);return function(a){var b=100*a,e=a.toString(),f=Math.pow(10,e.length-(e.indexOf(".")+1));b=parseInt((b*f).toString(),10)/f;var g=d(b);return c&&l(b,g)?"":(""!==g&&(g+="%"),g)}}function h(a){return void 0===a&&(a=3),k(a),function(b){return d3.format("."+a+"s")(b)}}function i(){var a=8,b={};return b[0]={format:".%L",filter:function(a){return 0!==a.getMilliseconds()}},b[1]={format:":%S",filter:function(a){return 0!==a.getSeconds()}},b[2]={format:"%I:%M",filter:function(a){return 0!==a.getMinutes()}},b[3]={format:"%I %p",filter:function(a){return 0!==a.getHours()}},b[4]={format:"%a %d",filter:function(a){return 0!==a.getDay()&&1!==a.getDate()}},b[5]={format:"%b %d",filter:function(a){return 1!==a.getDate()}},b[6]={format:"%b",filter:function(a){return 0!==a.getMonth()}},b[7]={format:"%Y",filter:function(){return!0}},function(c){for(var d=0;a>d;d++)if(b[d].filter(c))return d3.time.format(b[d].format)(c)}}function j(b,c,d){return void 0===b&&(b=0),void 0===c&&(c=a.MILLISECONDS_IN_ONE_DAY),void 0===d&&(d=""),function(a){var e=Math.round((a.valueOf()-b)/c);return e.toString()+d}}function k(a){if(0>a||a>20)throw new RangeError("Formatter precision must be between 0 and 20")}function l(a,b){return a!==parseFloat(b)}b.currency=c,b.fixed=d,b.general=e,b.identity=f,b.percentage=g,b.siSuffix=h,b.time=i,b.relativeDate=j}(a.Formatters||(a.Formatters={}));a.Formatters}(Plottable||(Plottable={}));var Plottable;!function(a){a.version="0.33.1"}(Plottable||(Plottable={}));var Plottable;!function(a){!function(a){var b=function(){function a(){}return a.CORAL_RED="#fd373e",a.INDIGO="#5279c7",a.ROBINS_EGG_BLUE="#06cccc",a.FERN="#63c261",a.BURNING_ORANGE="#ff7939",a.ROYAL_HEATH="#962565",a.CONIFER="#99ce50",a.CERISE_RED="#db2e65",a.BRIGHT_SUN="#fad419",a.JACARTA="#2c2b6f",a.PLOTTABLE_COLORS=[a.INDIGO,a.CORAL_RED,a.FERN,a.BRIGHT_SUN,a.JACARTA,a.BURNING_ORANGE,a.CERISE_RED,a.CONIFER,a.ROYAL_HEATH,a.ROBINS_EGG_BLUE],a}();a.Colors=b}(a.Core||(a.Core={}));a.Core}(Plottable||(Plottable={}));var Plottable;!function(a){!function(a){var b=function(){function a(){this._plottableID=a.nextID++}return a.nextID=0,a}();a.PlottableObject=b}(a.Core||(a.Core={}));a.Core}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(c){b.call(this),this.key2callback=new a._Util.StrictEqualityAssociativeArray,this.listenable=c}return __extends(c,b),c.prototype.registerListener=function(a,b){return this.key2callback.set(a,b),this},c.prototype.broadcast=function(){for(var a=this,b=[],c=0;c0){var f=d.valueOf();return d instanceof Date?[f-b.ONE_DAY,f+b.ONE_DAY]:[f-b.PADDING_FOR_IDENTICAL_DOMAIN,f+b.PADDING_FOR_IDENTICAL_DOMAIN]}if(a.domain()[0]===a.domain()[1])return c;var g=this.padProportion/2,h=a.invert(a.scale(d)-(a.scale(e)-a.scale(d))*g),i=a.invert(a.scale(e)+(a.scale(e)-a.scale(d))*g),j=this.paddingExceptions.values().concat(this.unregisteredPaddingExceptions.values()),k=d3.set(j);return k.has(d)&&(h=d),k.has(e)&&(i=e),[h,i]},b.prototype.niceDomain=function(a,b){return this.doNice?a._niceDomain(b,this.niceCount):b},b.prototype.includeDomain=function(a){var b=this.includedValues.values().concat(this.unregisteredIncludedValues.values());return b.reduce(function(a,b){return[Math.min(a[0],b),Math.max(a[1],b)]},a)},b.PADDING_FOR_IDENTICAL_DOMAIN=1,b.ONE_DAY=864e5,b}();a.Domainer=b}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(c){b.call(this),this._autoDomainAutomatically=!0,this.broadcaster=new a.Core.Broadcaster(this),this._rendererAttrID2Extent={},this._typeCoercer=function(a){return a},this._d3Scale=c}return __extends(c,b),c.prototype._getAllExtents=function(){return d3.values(this._rendererAttrID2Extent)},c.prototype._getExtent=function(){return[]},c.prototype.autoDomain=function(){return this._autoDomainAutomatically=!0,this._setDomain(this._getExtent()),this},c.prototype._autoDomainIfAutomaticMode=function(){this._autoDomainAutomatically&&this.autoDomain()},c.prototype.scale=function(a){return this._d3Scale(a)},c.prototype.domain=function(a){return null==a?this._getDomain():(this._autoDomainAutomatically=!1,this._setDomain(a),this)},c.prototype._getDomain=function(){return this._d3Scale.domain()},c.prototype._setDomain=function(a){this._d3Scale.domain(a),this.broadcaster.broadcast()},c.prototype.range=function(a){return null==a?this._d3Scale.range():(this._d3Scale.range(a),this)},c.prototype.copy=function(){return new c(this._d3Scale.copy())},c.prototype._updateExtent=function(a,b,c){return this._rendererAttrID2Extent[a+b]=c,this._autoDomainIfAutomaticMode(),this},c.prototype._removeExtent=function(a,b){return delete this._rendererAttrID2Extent[a+b],this._autoDomainIfAutomaticMode(),this},c}(a.Core.PlottableObject);b.AbstractScale=c}(a.Scale||(a.Scale={}));a.Scale}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(c){b.call(this,c),this._numTicks=10,this._PADDING_FOR_IDENTICAL_DOMAIN=1,this._userSetDomainer=!1,this._domainer=new a.Domainer,this._typeCoercer=function(a){return+a}}return __extends(c,b),c.prototype._getExtent=function(){return this._domainer.computeDomain(this._getAllExtents(),this)},c.prototype.invert=function(a){return this._d3Scale.invert(a)},c.prototype.copy=function(){return new c(this._d3Scale.copy())},c.prototype.domain=function(a){return b.prototype.domain.call(this,a)},c.prototype._setDomain=function(c){var d=function(a){return a!==a||1/0===a||a===-1/0};return d(c[0])||d(c[1])?void a._Util.Methods.warn("Warning: QuantitativeScales cannot take NaN or Infinity as a domain value. Ignoring."):void b.prototype._setDomain.call(this,c)},c.prototype.interpolate=function(a){return null==a?this._d3Scale.interpolate():(this._d3Scale.interpolate(a),this)},c.prototype.rangeRound=function(a){return this._d3Scale.rangeRound(a),this},c.prototype.clamp=function(a){return null==a?this._d3Scale.clamp():(this._d3Scale.clamp(a),this)},c.prototype.ticks=function(a){return void 0===a&&(a=this.numTicks()),this._d3Scale.ticks(a)},c.prototype.numTicks=function(a){return null==a?this._numTicks:(this._numTicks=a,this)},c.prototype._niceDomain=function(a,b){return this._d3Scale.copy().domain(a).nice(b).domain()},c.prototype.domainer=function(a){return null==a?this._domainer:(this._domainer=a,this._userSetDomainer=!0,this._autoDomainIfAutomaticMode(),this)},c.prototype._defaultExtent=function(){return[0,1]},c}(b.AbstractScale);b.AbstractQuantitative=c}(a.Scale||(a.Scale={}));a.Scale}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(a){var b=function(a){function b(b){a.call(this,null==b?d3.scale.linear():b)}return __extends(b,a),b.prototype.copy=function(){return new b(this._d3Scale.copy())},b}(a.AbstractQuantitative);a.Linear=b}(a.Scale||(a.Scale={}));a.Scale}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(d){b.call(this,null==d?d3.scale.log():d),c.warned||(c.warned=!0,a._Util.Methods.warn("Plottable.Scale.Log is deprecated. If possible, use Plottable.Scale.ModifiedLog instead."))}return __extends(c,b),c.prototype.copy=function(){return new c(this._d3Scale.copy())},c.prototype._defaultExtent=function(){return[1,10]},c.warned=!1,c}(b.AbstractQuantitative);b.Log=c}(a.Scale||(a.Scale={}));a.Scale}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(a){if(void 0===a&&(a=10),b.call(this,d3.scale.linear()),this._showIntermediateTicks=!1,this.base=a,this.pivot=this.base,this.untransformedDomain=this._defaultExtent(),this._numTicks=10,1>=a)throw new Error("ModifiedLogScale: The base must be > 1")}return __extends(c,b),c.prototype.adjustedLog=function(a){var b=0>a?-1:1;return a*=b,aa?-1:1;return a*=b,a=Math.pow(this.base,a),a=d&&e>=a}),m=j.concat(l).concat(k);return m.length<=1&&(m=d3.scale.linear().domain([d,e]).ticks(b)),m},c.prototype.logTicks=function(b,c){var d=this,e=this.howManyTicks(b,c);if(0===e)return[];var f=Math.floor(Math.log(b)/Math.log(this.base)),g=Math.ceil(Math.log(c)/Math.log(this.base)),h=d3.range(g,f,-Math.ceil((g-f)/e)),i=this._showIntermediateTicks?Math.floor(e/h.length):1,j=d3.range(this.base,1,-(this.base-1)/i).map(Math.floor),k=a._Util.Methods.uniq(j),l=h.map(function(a){return k.map(function(b){return Math.pow(d.base,a-1)*b})}),m=a._Util.Methods.flatten(l),n=m.filter(function(a){return a>=b&&c>=a}),o=n.sort(function(a,b){return a-b});return o},c.prototype.howManyTicks=function(b,c){var d=this.adjustedLog(a._Util.Methods.min(this.untransformedDomain)),e=this.adjustedLog(a._Util.Methods.max(this.untransformedDomain)),f=this.adjustedLog(b),g=this.adjustedLog(c),h=(g-f)/(e-d),i=Math.ceil(h*this._numTicks);return i},c.prototype.copy=function(){return new c(this.base)},c.prototype._niceDomain=function(a){return a},c.prototype.showIntermediateTicks=function(a){return null==a?this._showIntermediateTicks:void(this._showIntermediateTicks=a)},c}(b.AbstractQuantitative);b.ModifiedLog=c}(a.Scale||(a.Scale={}));a.Scale}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(a){if(b.call(this,null==a?d3.scale.ordinal():a),this._range=[0,1],this._rangeType="bands",this._innerPadding=.3,this._outerPadding=.5,this._typeCoercer=function(a){return null!=a&&a.toString?a.toString():a},this._innerPadding>this._outerPadding)throw new Error("outerPadding must be >= innerPadding so cat axis bands work out reasonably")}return __extends(c,b),c.prototype._getExtent=function(){var b=this._getAllExtents();return a._Util.Methods.uniq(a._Util.Methods.flatten(b))},c.prototype.domain=function(a){return b.prototype.domain.call(this,a)},c.prototype._setDomain=function(a){b.prototype._setDomain.call(this,a),this.range(this.range())},c.prototype.range=function(a){return null==a?this._range:(this._range=a,"points"===this._rangeType?this._d3Scale.rangePoints(a,2*this._outerPadding):"bands"===this._rangeType&&this._d3Scale.rangeBands(a,this._innerPadding,this._outerPadding),this)},c.prototype.rangeBand=function(){return this._d3Scale.rangeBand()},c.prototype.innerPadding=function(){var a=this.domain();if(a.length<2)return 0;var b=Math.abs(this.scale(a[1])-this.scale(a[0]));return b-this.rangeBand()},c.prototype.fullBandStartAndWidth=function(a){var b=this.scale(a)-this.innerPadding()/2,c=this.rangeBand()+this.innerPadding();return[b,c]},c.prototype.rangeType=function(a,b,c){if(null==a)return this._rangeType;if("points"!==a&&"bands"!==a)throw new Error("Unsupported range type: "+a);return this._rangeType=a,null!=b&&(this._outerPadding=b),null!=c&&(this._innerPadding=c),this.range(this.range()),this.broadcaster.broadcast(),this},c.prototype.copy=function(){return new c(this._d3Scale.copy())},c}(b.AbstractScale);b.Ordinal=c}(a.Scale||(a.Scale={}));a.Scale}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(c){var d;switch(c){case null:case void 0:d=d3.scale.ordinal().range(a.Core.Colors.PLOTTABLE_COLORS);break;case"Category10":case"category10":case"10":d=d3.scale.category10();break;case"Category20":case"category20":case"20":d=d3.scale.category20();break;case"Category20b":case"category20b":case"20b":d=d3.scale.category20b();break;case"Category20c":case"category20c":case"20c":d=d3.scale.category20c();break;default:throw new Error("Unsupported ColorScale type") -}b.call(this,d)}return __extends(c,b),c.prototype._getExtent=function(){var b=this._getAllExtents(),c=[];return b.forEach(function(a){c=c.concat(a)}),a._Util.Methods.uniq(c)},c}(b.AbstractScale);b.Color=c}(a.Scale||(a.Scale={}));a.Scale}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(a){b.call(this,null==a?d3.time.scale():a),this._typeCoercer=function(a){return a&&a._isAMomentObject||a instanceof Date?a:new Date(a)}}return __extends(c,b),c.prototype._tickInterval=function(a,b){var c=d3.time.scale();return c.domain(this.domain()),c.range(this.range()),c.ticks(a.range,b)},c.prototype._setDomain=function(a){return a=a.map(this._typeCoercer),b.prototype._setDomain.call(this,a)},c.prototype.copy=function(){return new c(this._d3Scale.copy())},c.prototype._defaultExtent=function(){var b=(new Date).valueOf(),c=b-a.MILLISECONDS_IN_ONE_DAY;return[c,b]},c}(b.AbstractQuantitative);b.Time=c}(a.Scale||(a.Scale={}));a.Scale}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(a,d){void 0===a&&(a="reds"),void 0===d&&(d="linear"),this._colorRange=this._resolveColorValues(a),this._scaleType=d,b.call(this,c.getD3InterpolatedScale(this._colorRange,this._scaleType))}return __extends(c,b),c.getD3InterpolatedScale=function(a,b){var d;switch(b){case"linear":d=d3.scale.linear();break;case"log":d=d3.scale.log();break;case"sqrt":d=d3.scale.sqrt();break;case"pow":d=d3.scale.pow()}if(null==d)throw new Error("unknown Quantitative scale type "+b);return d.range([0,1]).interpolate(c.interpolateColors(a))},c.interpolateColors=function(a){if(a.length<2)throw new Error("Color scale arrays must have at least two elements.");return function(){return function(b){b=Math.max(0,Math.min(1,b));var c=b*(a.length-1),d=Math.floor(c),e=Math.ceil(c),f=c-d;return d3.interpolateLab(a[d],a[e])(f)}}},c.prototype.colorRange=function(a){return null==a?this._colorRange:(this._colorRange=this._resolveColorValues(a),this._resetScale(),this)},c.prototype.scaleType=function(a){return null==a?this._scaleType:(this._scaleType=a,this._resetScale(),this)},c.prototype._resetScale=function(){this._d3Scale=c.getD3InterpolatedScale(this._colorRange,this._scaleType),this._autoDomainIfAutomaticMode(),this.broadcaster.broadcast()},c.prototype._resolveColorValues=function(a){return a instanceof Array?a:null!=c.COLOR_SCALES[a]?c.COLOR_SCALES[a]:c.COLOR_SCALES.reds},c.prototype.autoDomain=function(){var b=this._getAllExtents();return b.length>0&&this._setDomain([a._Util.Methods.min(b,function(a){return a[0]}),a._Util.Methods.max(b,function(a){return a[1]})]),this},c.COLOR_SCALES={reds:["#FFFFFF","#FFF6E1","#FEF4C0","#FED976","#FEB24C","#FD8D3C","#FC4E2A","#E31A1C","#B10026"],blues:["#FFFFFF","#CCFFFF","#A5FFFD","#85F7FB","#6ED3EF","#55A7E0","#417FD0","#2545D3","#0B02E1"],posneg:["#0B02E1","#2545D3","#417FD0","#55A7E0","#6ED3EF","#85F7FB","#A5FFFD","#CCFFFF","#FFFFFF","#FFF6E1","#FEF4C0","#FED976","#FEB24C","#FD8D3C","#FC4E2A","#E31A1C","#B10026"]},c}(b.AbstractScale);b.InterpolatedColor=c}(a.Scale||(a.Scale={}));a.Scale}(Plottable||(Plottable={}));var Plottable;!function(a){!function(a){var b=function(){function a(a){var b=this;if(this.rescaleInProgress=!1,null==a)throw new Error("ScaleDomainCoordinator requires scales to coordinate");this.scales=a,this.scales.forEach(function(a){return a.broadcaster.registerListener(b,function(a){return b.rescale(a)})})}return a.prototype.rescale=function(a){if(!this.rescaleInProgress){this.rescaleInProgress=!0;var b=a.domain();this.scales.forEach(function(a){return a.domain(b)}),this.rescaleInProgress=!1}},a}();a.ScaleDomainCoordinator=b}(a._Util||(a._Util={}));a._Util}(Plottable||(Plottable={}));var Plottable;!function(a){!function(b){var c=function(){function b(a){this.key=a}return b.prototype.remove=function(){null!=this._renderArea&&this._renderArea.remove()},b.prototype.draw=function(b,c,d){void 0===d&&(d=new a.Animator.Null)},b}();b.AbstractDrawer=c}(a._Drawer||(a._Drawer={}));a._Drawer}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(){b.apply(this,arguments)}return __extends(c,b),c.prototype.draw=function(b,c,d){void 0===d&&(d=new a.Animator.Null);var e="path",f=this._renderArea.selectAll(e).data(b);f.enter().append(e),f.classed("arc",!0),d.animate(f,c),f.exit().remove()},c}(b.AbstractDrawer);b.Arc=c}(a._Drawer||(a._Drawer={}));a._Drawer}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(a){var b=function(a){function b(){a.apply(this,arguments)}return __extends(b,a),b.prototype.draw=function(a,b){var c="path",d=this._renderArea.selectAll(c).data([a]);d.enter().append(c),d.attr(b).classed("area",!0),d.exit().remove()},b}(a.AbstractDrawer);a.Area=b}(a._Drawer||(a._Drawer={}));a._Drawer}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(){b.apply(this,arguments)}return __extends(c,b),c.prototype.draw=function(b,c,d){void 0===d&&(d=new a.Animator.Null);var e="rect",f=this._renderArea.selectAll(e).data(b);f.enter().append(e),d.animate(f,c),f.exit().remove()},c}(b.AbstractDrawer);b.Rect=c}(a._Drawer||(a._Drawer={}));a._Drawer}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(){b.apply(this,arguments),this.clipPathEnabled=!1,this._xAlignProportion=0,this._yAlignProportion=0,this._fixedHeightFlag=!1,this._fixedWidthFlag=!1,this._isSetup=!1,this._isAnchored=!1,this.interactionsToRegister=[],this.boxes=[],this.isTopLevelComponent=!1,this._width=0,this._height=0,this._xOffset=0,this._yOffset=0,this.cssClasses=["component"],this.removed=!1}return __extends(c,b),c.prototype._anchor=function(a){if(this.removed)throw new Error("Can't reuse remove()-ed components!");"svg"===a.node().nodeName&&(this.rootSVG=a,this.rootSVG.classed("plottable",!0),this.rootSVG.style("overflow","visible"),this.isTopLevelComponent=!0),null!=this._element?a.node().appendChild(this._element.node()):(this._element=a.append("g"),this._setup()),this._isAnchored=!0},c.prototype._setup=function(){var a=this;this._isSetup||(this.cssClasses.forEach(function(b){a._element.classed(b,!0)}),this.cssClasses=null,this._backgroundContainer=this._element.append("g").classed("background-container",!0),this._content=this._element.append("g").classed("content",!0),this._foregroundContainer=this._element.append("g").classed("foreground-container",!0),this.boxContainer=this._element.append("g").classed("box-container",!0),this.clipPathEnabled&&this.generateClipPath(),this.addBox("bounding-box"),this.interactionsToRegister.forEach(function(b){return a.registerInteraction(b)}),this.interactionsToRegister=null,this.isTopLevelComponent&&this.autoResize(c.AUTORESIZE_BY_DEFAULT),this._isSetup=!0)},c.prototype._requestedSpace=function(){return{width:0,height:0,wantsWidth:!1,wantsHeight:!1}},c.prototype._computeLayout=function(b,c,d,e){var f=this;if(null==b||null==c||null==d||null==e){if(null==this._element)throw new Error("anchor must be called before computeLayout");if(!this.isTopLevelComponent)throw new Error("null arguments cannot be passed to _computeLayout() on a non-root node");b=0,c=0,null==this.rootSVG.attr("width")&&this.rootSVG.attr("width","100%"),null==this.rootSVG.attr("height")&&this.rootSVG.attr("height","100%");var g=this.rootSVG.node();d=a._Util.DOM.getElementWidth(g),e=a._Util.DOM.getElementHeight(g)}this.xOrigin=b,this.yOrigin=c;var h=this._requestedSpace(d,e);this._width=this._isFixedWidth()?Math.min(d,h.width):d,this._height=this._isFixedHeight()?Math.min(e,h.height):e;var i=this.xOrigin+this._xOffset,j=this.yOrigin+this._yOffset;i+=(d-this.width())*this._xAlignProportion,j+=(e-h.height)*this._yAlignProportion,this._element.attr("transform","translate("+i+","+j+")"),this.boxes.forEach(function(a){return a.attr("width",f.width()).attr("height",f.height())})},c.prototype._render=function(){this._isAnchored&&this._isSetup&&a.Core.RenderController.registerToRender(this)},c.prototype._scheduleComputeLayout=function(){this._isAnchored&&this._isSetup&&a.Core.RenderController.registerToComputeLayout(this)},c.prototype._doRender=function(){},c.prototype._invalidateLayout=function(){this._isAnchored&&this._isSetup&&(this.isTopLevelComponent?this._scheduleComputeLayout():this._parent._invalidateLayout())},c.prototype.renderTo=function(b){if(null!=b){var c;if(c="function"==typeof b.node?b:d3.select(b),!c.node()||"svg"!==c.node().nodeName)throw new Error("Plottable requires a valid SVG to renderTo");this._anchor(c)}if(null==this._element)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");return this._computeLayout(),this._render(),a.Core.RenderController.flush(),this},c.prototype.resize=function(a,b){if(!this.isTopLevelComponent)throw new Error("Cannot resize on non top-level component");return null!=a&&null!=b&&this._isAnchored&&this.rootSVG.attr({width:a,height:b}),this._invalidateLayout(),this},c.prototype.autoResize=function(b){return b?a.Core.ResizeBroadcaster.register(this):a.Core.ResizeBroadcaster.deregister(this),this},c.prototype.xAlign=function(a){if(a=a.toLowerCase(),"left"===a)this._xAlignProportion=0;else if("center"===a)this._xAlignProportion=.5;else{if("right"!==a)throw new Error("Unsupported alignment");this._xAlignProportion=1}return this._invalidateLayout(),this},c.prototype.yAlign=function(a){if(a=a.toLowerCase(),"top"===a)this._yAlignProportion=0;else if("center"===a)this._yAlignProportion=.5;else{if("bottom"!==a)throw new Error("Unsupported alignment");this._yAlignProportion=1}return this._invalidateLayout(),this},c.prototype.xOffset=function(a){return this._xOffset=a,this._invalidateLayout(),this},c.prototype.yOffset=function(a){return this._yOffset=a,this._invalidateLayout(),this},c.prototype.addBox=function(a,b){if(null==this._element)throw new Error("Adding boxes before anchoring is currently disallowed");var b=null==b?this.boxContainer:b,c=b.append("rect");return null!=a&&c.classed(a,!0),this.boxes.push(c),null!=this.width()&&null!=this.height()&&c.attr("width",this.width()).attr("height",this.height()),c},c.prototype.generateClipPath=function(){var a=/MSIE [5-9]/.test(navigator.userAgent)?"":document.location.href;this._element.attr("clip-path","url("+a+"#clipPath"+this._plottableID+")");var b=this.boxContainer.append("clipPath").attr("id","clipPath"+this._plottableID);this.addBox("clip-rect",b)},c.prototype.registerInteraction=function(a){return this._element?(this.hitBox||(this.hitBox=this.addBox("hit-box"),this.hitBox.style("fill","#ffffff").style("opacity",0)),a._anchor(this,this.hitBox)):this.interactionsToRegister.push(a),this},c.prototype.classed=function(a,b){if(null==b)return null==a?!1:null==this._element?-1!==this.cssClasses.indexOf(a):this._element.classed(a);if(null==a)return this;if(null==this._element){var c=this.cssClasses.indexOf(a);b&&-1===c?this.cssClasses.push(a):b||-1===c||this.cssClasses.splice(c,1)}else this._element.classed(a,b);return this},c.prototype._isFixedWidth=function(){return this._fixedWidthFlag},c.prototype._isFixedHeight=function(){return this._fixedHeightFlag},c.prototype.merge=function(b){var c;if(this._isSetup||this._isAnchored)throw new Error("Can't presently merge a component that's already been anchored");return a.Component.Group.prototype.isPrototypeOf(b)?(c=b,c._addComponent(this,!0),c):c=new a.Component.Group([this,b])},c.prototype.detach=function(){return this._isAnchored&&this._element.remove(),null!=this._parent&&this._parent._removeComponent(this),this._isAnchored=!1,this._parent=null,this},c.prototype.remove=function(){this.removed=!0,this.detach(),a.Core.ResizeBroadcaster.deregister(this)},c.prototype.width=function(){return this._width},c.prototype.height=function(){return this._height},c.AUTORESIZE_BY_DEFAULT=!0,c}(a.Core.PlottableObject);b.AbstractComponent=c}(a.Component||(a.Component={}));a.Component}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(a){var b=function(a){function b(){a.apply(this,arguments),this._components=[]}return __extends(b,a),b.prototype._anchor=function(b){var c=this;a.prototype._anchor.call(this,b),this._components.forEach(function(a){return a._anchor(c._content)})},b.prototype._render=function(){this._components.forEach(function(a){return a._render()})},b.prototype._removeComponent=function(a){var b=this._components.indexOf(a);b>=0&&(this._components.splice(b,1),this._invalidateLayout())},b.prototype._addComponent=function(a,b){return void 0===b&&(b=!1),!a||this._components.indexOf(a)>=0?!1:(b?this._components.unshift(a):this._components.push(a),a._parent=this,this._isAnchored&&a._anchor(this._content),this._invalidateLayout(),!0)},b.prototype.components=function(){return this._components.slice()},b.prototype.empty=function(){return 0===this._components.length},b.prototype.detachAll=function(){return this._components.slice().forEach(function(a){return a.detach()}),this},b.prototype.remove=function(){a.prototype.remove.call(this),this._components.slice().forEach(function(a){return a.remove()})},b}(a.AbstractComponent);a.AbstractComponentContainer=b}(a.Component||(a.Component={}));a.Component}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(a){var c=this;void 0===a&&(a=[]),b.call(this),this.classed("component-group",!0),a.forEach(function(a){return c._addComponent(a)})}return __extends(c,b),c.prototype._requestedSpace=function(b,c){var d=this._components.map(function(a){return a._requestedSpace(b,c)});return{width:a._Util.Methods.max(d,function(a){return a.width}),height:a._Util.Methods.max(d,function(a){return a.height}),wantsWidth:d.map(function(a){return a.wantsWidth}).some(function(a){return a}),wantsHeight:d.map(function(a){return a.wantsHeight}).some(function(a){return a})}},c.prototype.merge=function(a){return this._addComponent(a),this},c.prototype._computeLayout=function(a,c,d,e){var f=this;return b.prototype._computeLayout.call(this,a,c,d,e),this._components.forEach(function(a){a._computeLayout(0,0,f.width(),f.height())}),this},c.prototype._isFixedWidth=function(){return this._components.every(function(a){return a._isFixedWidth()})},c.prototype._isFixedHeight=function(){return this._components.every(function(a){return a._isFixedHeight()})},c}(b.AbstractComponentContainer);b.Group=c}(a.Component||(a.Component={}));a.Component}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(c,d,e){var f=this;if(void 0===e&&(e=a.Formatters.identity()),b.call(this),this._endTickLength=5,this._tickLength=5,this._tickLabelPadding=10,this._gutter=15,this._showEndTickLabels=!1,null==c||null==d)throw new Error("Axis requires a scale and orientation");this._scale=c,this.orient(d),this._setDefaultAlignment(),this.classed("axis",!0),this._isHorizontal()?this.classed("x-axis",!0):this.classed("y-axis",!0),this.formatter(e),this._scale.broadcaster.registerListener(this,function(){return f._rescale()})}return __extends(c,b),c.prototype.remove=function(){b.prototype.remove.call(this),this._scale.broadcaster.deregisterListener(this)},c.prototype._isHorizontal=function(){return"top"===this._orientation||"bottom"===this._orientation},c.prototype._computeWidth=function(){return this._computedWidth=this._maxLabelTickLength(),this._computedWidth},c.prototype._computeHeight=function(){return this._computedHeight=this._maxLabelTickLength(),this._computedHeight},c.prototype._requestedSpace=function(a,b){var c=0,d=0;return this._isHorizontal()?(null==this._computedHeight&&this._computeHeight(),d=this._computedHeight+this._gutter):(null==this._computedWidth&&this._computeWidth(),c=this._computedWidth+this._gutter),{width:c,height:d,wantsWidth:!this._isHorizontal()&&c>a,wantsHeight:this._isHorizontal()&&d>b}},c.prototype._isFixedHeight=function(){return this._isHorizontal()},c.prototype._isFixedWidth=function(){return!this._isHorizontal()},c.prototype._rescale=function(){this._render()},c.prototype._computeLayout=function(a,c,d,e){b.prototype._computeLayout.call(this,a,c,d,e),this._scale.range(this._isHorizontal()?[0,this.width()]:[this.height(),0])},c.prototype._setup=function(){b.prototype._setup.call(this),this._tickMarkContainer=this._content.append("g").classed(c.TICK_MARK_CLASS+"-container",!0),this._tickLabelContainer=this._content.append("g").classed(c.TICK_LABEL_CLASS+"-container",!0),this._baseline=this._content.append("line").classed("baseline",!0)},c.prototype._getTickValues=function(){return[]},c.prototype._doRender=function(){var a=this._getTickValues(),b=this._tickMarkContainer.selectAll("."+c.TICK_MARK_CLASS).data(a);b.enter().append("line").classed(c.TICK_MARK_CLASS,!0),b.attr(this._generateTickMarkAttrHash()),d3.select(b[0][0]).classed(c.END_TICK_MARK_CLASS,!0).attr(this._generateTickMarkAttrHash(!0)),d3.select(b[0][a.length-1]).classed(c.END_TICK_MARK_CLASS,!0).attr(this._generateTickMarkAttrHash(!0)),b.exit().remove(),this._baseline.attr(this._generateBaselineAttrHash())},c.prototype._generateBaselineAttrHash=function(){var a={x1:0,y1:0,x2:0,y2:0};switch(this._orientation){case"bottom":a.x2=this.width();break;case"top":a.x2=this.width(),a.y1=this.height(),a.y2=this.height();break;case"left":a.x1=this.width(),a.x2=this.width(),a.y2=this.height();break;case"right":a.y2=this.height()}return a},c.prototype._generateTickMarkAttrHash=function(a){var b=this;void 0===a&&(a=!1);var c={x1:0,y1:0,x2:0,y2:0},d=function(a){return b._scale.scale(a)};this._isHorizontal()?(c.x1=d,c.x2=d):(c.y1=d,c.y2=d);var e=a?this._endTickLength:this._tickLength;switch(this._orientation){case"bottom":c.y2=e;break;case"top":c.y1=this.height(),c.y2=this.height()-e;break;case"left":c.x1=this.width(),c.x2=this.width()-e;break;case"right":c.x2=e}return c},c.prototype._invalidateLayout=function(){this._computedWidth=null,this._computedHeight=null,b.prototype._invalidateLayout.call(this)},c.prototype._setDefaultAlignment=function(){switch(this._orientation){case"bottom":this.yAlign("top");break;case"top":this.yAlign("bottom");break;case"left":this.xAlign("right");break;case"right":this.xAlign("left")}},c.prototype.formatter=function(a){return void 0===a?this._formatter:(this._formatter=a,this._invalidateLayout(),this)},c.prototype.tickLength=function(a){if(null==a)return this._tickLength;if(0>a)throw new Error("tick length must be positive");return this._tickLength=a,this._invalidateLayout(),this},c.prototype.endTickLength=function(a){if(null==a)return this._endTickLength;if(0>a)throw new Error("end tick length must be positive");return this._endTickLength=a,this._invalidateLayout(),this},c.prototype._maxLabelTickLength=function(){return this.showEndTickLabels()?Math.max(this.tickLength(),this.endTickLength()):this.tickLength()},c.prototype.tickLabelPadding=function(a){if(null==a)return this._tickLabelPadding;if(0>a)throw new Error("tick label padding must be positive");return this._tickLabelPadding=a,this._invalidateLayout(),this},c.prototype.gutter=function(a){if(null==a)return this._gutter;if(0>a)throw new Error("gutter size must be positive");return this._gutter=a,this._invalidateLayout(),this},c.prototype.orient=function(a){if(null==a)return this._orientation;var b=a.toLowerCase();if("top"!==b&&"bottom"!==b&&"left"!==b&&"right"!==b)throw new Error("unsupported orientation");return this._orientation=b,this._invalidateLayout(),this},c.prototype.showEndTickLabels=function(a){return null==a?this._showEndTickLabels:(this._showEndTickLabels=a,this._render(),this)},c.prototype._hideEndTickLabels=function(){var a=this,b=this._element.select(".bounding-box")[0][0].getBoundingClientRect(),d=function(c){return Math.floor(b.left)<=Math.ceil(c.left)&&Math.floor(b.top)<=Math.ceil(c.top)&&Math.floor(c.right)<=Math.ceil(b.left+a.width())&&Math.floor(c.bottom)<=Math.ceil(b.top+a.height())},e=this._tickLabelContainer.selectAll("."+c.TICK_LABEL_CLASS);if(0!==e[0].length){var f=e[0][0];d(f.getBoundingClientRect())||d3.select(f).style("visibility","hidden");var g=e[0][e[0].length-1];d(g.getBoundingClientRect())||d3.select(g).style("visibility","hidden")}},c.prototype._hideOverlappingTickLabels=function(){var b,d=this._tickLabelContainer.selectAll("."+c.TICK_LABEL_CLASS).filter(function(){return"visible"===d3.select(this).style("visibility")});d.each(function(){var c=this.getBoundingClientRect(),d=d3.select(this);null!=b&&a._Util.DOM.boxesOverlap(c,b)?d.style("visibility","hidden"):(b=c,d.style("visibility","visible"))})},c.END_TICK_MARK_CLASS="end-tick-mark",c.TICK_MARK_CLASS="tick-mark",c.TICK_LABEL_CLASS="tick-label",c}(a.Component.AbstractComponent);b.AbstractAxis=c}(a.Axis||(a.Axis={}));a.Axis}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(c){function d(a,b){if(b=b.toLowerCase(),"top"!==b&&"bottom"!==b)throw new Error("unsupported orientation: "+b);c.call(this,a,b),this.classed("time-axis",!0),this.tickLabelPadding(5)}return __extends(d,c),d.prototype._computeHeight=function(){if(null!==this._computedHeight)return this._computedHeight;var a=this._measureTextHeight(this._majorTickLabels)+this._measureTextHeight(this._minorTickLabels);return this.tickLength(a),this.endTickLength(a),this._computedHeight=this._maxLabelTickLength()+2*this.tickLabelPadding(),this._computedHeight},d.prototype.calculateWorstWidth=function(a,b){var c=new Date(9999,8,29,12,59,9999);return this.measurer(d3.time.format(b)(c)).width},d.prototype.getIntervalLength=function(a){var b=this._scale.domain()[0],c=a.timeUnit.offset(b,a.step);if(c>this._scale.domain()[1])return this.width();var d=Math.abs(this._scale.scale(c)-this._scale.scale(b));return d},d.prototype.isEnoughSpace=function(a,b){var c=this.calculateWorstWidth(a,b.formatString)+2*this.tickLabelPadding(),d=Math.min(this.getIntervalLength(b),this.width());return d>c},d.prototype._setup=function(){c.prototype._setup.call(this),this._majorTickLabels=this._content.append("g").classed(b.AbstractAxis.TICK_LABEL_CLASS,!0),this._minorTickLabels=this._content.append("g").classed(b.AbstractAxis.TICK_LABEL_CLASS,!0),this.measurer=a._Util.Text.getTextMeasurer(this._majorTickLabels.append("text"))},d.prototype.getTickLevel=function(){for(var b=0;b=d._minorIntervals.length&&(a._Util.Methods.warn("zoomed out too far: could not find suitable interval to display labels"),b=d._minorIntervals.length-1),b},d.prototype._getTickIntervalValues=function(a){return this._scale._tickInterval(a.timeUnit,a.step)},d.prototype._getTickValues=function(){var a=this.getTickLevel(),b=this._getTickIntervalValues(d._minorIntervals[a]),c=this._getTickIntervalValues(d._majorIntervals[a]);return b.concat(c)},d.prototype._measureTextHeight=function(c){var d=c.append("g").classed(b.AbstractAxis.TICK_LABEL_CLASS,!0),e=this.measurer(a._Util.Text.HEIGHT_TEXT).height;return d.remove(),e},d.prototype.renderTickLabels=function(c,d,e){var f=this;c.selectAll("."+b.AbstractAxis.TICK_LABEL_CLASS).remove();var g=this._scale._tickInterval(d.timeUnit,d.step);g.splice(0,0,this._scale.domain()[0]),g.push(this._scale.domain()[1]);var h=1===d.step,i=[];h?g.map(function(a,b){b+1>=g.length||i.push(new Date((g[b+1].valueOf()-g[b].valueOf())/2+g[b].valueOf()))}):i=g,i=i.filter(function(a){return f.canFitLabelFilter(c,a,d3.time.format(d.formatString)(a),h)});var j=c.selectAll("."+b.AbstractAxis.TICK_LABEL_CLASS).data(i,function(a){return a.valueOf()}),k=j.enter().append("g").classed(b.AbstractAxis.TICK_LABEL_CLASS,!0);k.append("text");var l=h?0:this.tickLabelPadding(),m="bottom"===this._orientation?this._maxLabelTickLength()/2*e:this.height()-this._maxLabelTickLength()/2*e+2*this.tickLabelPadding(),n=j.selectAll("text");n.size()>0&&a._Util.DOM.translate(n,l,m),j.exit().remove(),j.attr("transform",function(a){return"translate("+f._scale.scale(a)+",0)"});var o=h?"middle":"start";j.selectAll("text").text(function(a){return d3.time.format(d.formatString)(a)}).style("text-anchor",o)},d.prototype.canFitLabelFilter=function(a,b,c,d){var e,f,g=this.measurer(c).width+this.tickLabelPadding();return d?(e=this._scale.scale(b)+g/2,f=this._scale.scale(b)-g/2):(e=this._scale.scale(b)+g,f=this._scale.scale(b)),e0},d.prototype.adjustTickLength=function(a,c){var d=this._getTickIntervalValues(c),e=this._tickMarkContainer.selectAll("."+b.AbstractAxis.TICK_MARK_CLASS).filter(function(a){return d.map(function(a){return a.valueOf()}).indexOf(a.valueOf())>=0});"top"===this._orientation&&(a=this.height()-a),e.attr("y2",a)},d.prototype.generateLabellessTicks=function(a){if(!(0>a)){var c=this._getTickIntervalValues(d._minorIntervals[a]),e=this._getTickValues().concat(c),f=this._tickMarkContainer.selectAll("."+b.AbstractAxis.TICK_MARK_CLASS).data(e);f.enter().append("line").classed(b.AbstractAxis.TICK_MARK_CLASS,!0),f.attr(this._generateTickMarkAttrHash()),f.exit().remove(),this.adjustTickLength(this.tickLabelPadding(),d._minorIntervals[a])}},d.prototype._doRender=function(){c.prototype._doRender.call(this);var a=this.getTickLevel();this.renderTickLabels(this._minorTickLabels,d._minorIntervals[a],1),this.renderTickLabels(this._majorTickLabels,d._majorIntervals[a],2);var b=this._scale.domain(),e=this._scale.scale(b[1])-this._scale.scale(b[0]);return 1.5*this.getIntervalLength(d._minorIntervals[a])>=e&&this.generateLabellessTicks(a-1),this.adjustTickLength(this._maxLabelTickLength()/2,d._minorIntervals[a]),this.adjustTickLength(this._maxLabelTickLength(),d._majorIntervals[a]),this},d._minorIntervals=[{timeUnit:d3.time.second,step:1,formatString:"%I:%M:%S %p"},{timeUnit:d3.time.second,step:5,formatString:"%I:%M:%S %p"},{timeUnit:d3.time.second,step:10,formatString:"%I:%M:%S %p"},{timeUnit:d3.time.second,step:15,formatString:"%I:%M:%S %p"},{timeUnit:d3.time.second,step:30,formatString:"%I:%M:%S %p"},{timeUnit:d3.time.minute,step:1,formatString:"%I:%M %p"},{timeUnit:d3.time.minute,step:5,formatString:"%I:%M %p"},{timeUnit:d3.time.minute,step:10,formatString:"%I:%M %p"},{timeUnit:d3.time.minute,step:15,formatString:"%I:%M %p"},{timeUnit:d3.time.minute,step:30,formatString:"%I:%M %p"},{timeUnit:d3.time.hour,step:1,formatString:"%I %p"},{timeUnit:d3.time.hour,step:3,formatString:"%I %p"},{timeUnit:d3.time.hour,step:6,formatString:"%I %p"},{timeUnit:d3.time.hour,step:12,formatString:"%I %p"},{timeUnit:d3.time.day,step:1,formatString:"%a %e"},{timeUnit:d3.time.day,step:1,formatString:"%e"},{timeUnit:d3.time.month,step:1,formatString:"%B"},{timeUnit:d3.time.month,step:1,formatString:"%b"},{timeUnit:d3.time.month,step:3,formatString:"%B"},{timeUnit:d3.time.month,step:6,formatString:"%B"},{timeUnit:d3.time.year,step:1,formatString:"%Y"},{timeUnit:d3.time.year,step:1,formatString:"%y"},{timeUnit:d3.time.year,step:5,formatString:"%Y"},{timeUnit:d3.time.year,step:25,formatString:"%Y"},{timeUnit:d3.time.year,step:50,formatString:"%Y"},{timeUnit:d3.time.year,step:100,formatString:"%Y"},{timeUnit:d3.time.year,step:200,formatString:"%Y"},{timeUnit:d3.time.year,step:500,formatString:"%Y"},{timeUnit:d3.time.year,step:1e3,formatString:"%Y"}],d._majorIntervals=[{timeUnit:d3.time.day,step:1,formatString:"%B %e, %Y"},{timeUnit:d3.time.day,step:1,formatString:"%B %e, %Y"},{timeUnit:d3.time.day,step:1,formatString:"%B %e, %Y"},{timeUnit:d3.time.day,step:1,formatString:"%B %e, %Y"},{timeUnit:d3.time.day,step:1,formatString:"%B %e, %Y"},{timeUnit:d3.time.day,step:1,formatString:"%B %e, %Y"},{timeUnit:d3.time.day,step:1,formatString:"%B %e, %Y"},{timeUnit:d3.time.day,step:1,formatString:"%B %e, %Y"},{timeUnit:d3.time.day,step:1,formatString:"%B %e, %Y"},{timeUnit:d3.time.day,step:1,formatString:"%B %e, %Y"},{timeUnit:d3.time.day,step:1,formatString:"%B %e, %Y"},{timeUnit:d3.time.day,step:1,formatString:"%B %e, %Y"},{timeUnit:d3.time.day,step:1,formatString:"%B %e, %Y"},{timeUnit:d3.time.day,step:1,formatString:"%B %e, %Y"},{timeUnit:d3.time.month,step:1,formatString:"%B %Y"},{timeUnit:d3.time.month,step:1,formatString:"%B %Y"},{timeUnit:d3.time.year,step:1,formatString:"%Y"},{timeUnit:d3.time.year,step:1,formatString:"%Y"},{timeUnit:d3.time.year,step:1,formatString:"%Y"},{timeUnit:d3.time.year,step:1,formatString:"%Y"},{timeUnit:d3.time.year,step:1e5,formatString:""},{timeUnit:d3.time.year,step:1e5,formatString:""},{timeUnit:d3.time.year,step:1e5,formatString:""},{timeUnit:d3.time.year,step:1e5,formatString:""},{timeUnit:d3.time.year,step:1e5,formatString:""},{timeUnit:d3.time.year,step:1e5,formatString:""},{timeUnit:d3.time.year,step:1e5,formatString:""},{timeUnit:d3.time.year,step:1e5,formatString:""},{timeUnit:d3.time.year,step:1e5,formatString:""}],d}(b.AbstractAxis);b.Time=c}(a.Axis||(a.Axis={}));a.Axis}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(c){function d(b,d,e){void 0===e&&(e=a.Formatters.general(3,!1)),c.call(this,b,d,e),this.tickLabelPositioning="center",this.showFirstTickLabel=!1,this.showLastTickLabel=!1}return __extends(d,c),d.prototype._setup=function(){c.prototype._setup.call(this),this.measurer=a._Util.Text.getTextMeasurer(this._tickLabelContainer.append("text").classed(b.AbstractAxis.TICK_LABEL_CLASS,!0))},d.prototype._computeWidth=function(){var b=this,c=this._getTickValues(),d=c.map(function(a){var c=b._formatter(a);return b.measurer(c).width}),e=a._Util.Methods.max(d);return this._computedWidth="center"===this.tickLabelPositioning?this._maxLabelTickLength()+this.tickLabelPadding()+e:Math.max(this._maxLabelTickLength(),this.tickLabelPadding()+e),this._computedWidth},d.prototype._computeHeight=function(){var b=this.measurer(a._Util.Text.HEIGHT_TEXT).height;return this._computedHeight="center"===this.tickLabelPositioning?this._maxLabelTickLength()+this.tickLabelPadding()+b:Math.max(this._maxLabelTickLength(),this.tickLabelPadding()+b),this._computedHeight},d.prototype._getTickValues=function(){return this._scale.ticks()},d.prototype._rescale=function(){if(this._isSetup){if(!this._isHorizontal()){var a=this._computeWidth();if(a>this.width()||aa,wantsHeight:e>b}},c.prototype._setup=function(){b.prototype._setup.call(this),this.textContainer=this._content.append("g"),this.measurer=a._Util.Text.getTextMeasurer(this.textContainer.append("text")),this.text(this._text)},c.prototype.text=function(a){return void 0===a?this._text:(this._text=a,this._invalidateLayout(),this)},c.prototype.orient=function(a){if(null==a)return this.orientation;if(a=a.toLowerCase(),"horizontal"!==a&&"left"!==a&&"right"!==a)throw new Error(a+" is not a valid orientation for LabelComponent");return this.orientation=a,this._invalidateLayout(),this},c.prototype._doRender=function(){b.prototype._doRender.call(this),this.textContainer.text("");var c="horizontal"===this.orientation?this.width():this.height(),d=a._Util.Text.getTruncatedText(this._text,c,this.measurer);"horizontal"===this.orientation?a._Util.Text.writeLineHorizontally(d,this.textContainer,this.width(),this.height(),this.xAlignment,this.yAlignment):a._Util.Text.writeLineVertically(d,this.textContainer,this.width(),this.height(),this.xAlignment,this.yAlignment,this.orientation)},c.prototype._computeLayout=function(c,d,e,f){return this.measurer=a._Util.Text.getTextMeasurer(this.textContainer.append("text")),b.prototype._computeLayout.call(this,c,d,e,f),this},c}(b.AbstractComponent);b.Label=c;var d=function(a){function b(b,c){a.call(this,b,c),this.classed("title-label",!0)}return __extends(b,a),b}(c);b.TitleLabel=d;var e=function(a){function b(b,c){a.call(this,b,c),this.classed("axis-label",!0)}return __extends(b,a),b}(c);b.AxisLabel=e}(a.Component||(a.Component={}));a.Component}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(a){b.call(this),this.classed("legend",!0),this.scale(a),this.xAlign("RIGHT").yAlign("TOP"),this.xOffset(5).yOffset(5),this._fixedWidthFlag=!0,this._fixedHeightFlag=!0}return __extends(c,b),c.prototype.remove=function(){b.prototype.remove.call(this),null!=this.colorScale&&this.colorScale.broadcaster.deregisterListener(this)},c.prototype.toggleCallback=function(a){return void 0!==a?(this._toggleCallback=a,this.isOff=d3.set(),this.updateListeners(),this.updateClasses(),this):this._toggleCallback},c.prototype.hoverCallback=function(a){return void 0!==a?(this._hoverCallback=a,this.datumCurrentlyFocusedOn=void 0,this.updateListeners(),this.updateClasses(),this):this._hoverCallback},c.prototype.scale=function(a){var b=this;return null!=a?(null!=this.colorScale&&this.colorScale.broadcaster.deregisterListener(this),this.colorScale=a,this.colorScale.broadcaster.registerListener(this,function(){return b.updateDomain()}),this.updateDomain(),this):this.colorScale},c.prototype.updateDomain=function(){null!=this._toggleCallback&&(this.isOff=a._Util.Methods.intersection(this.isOff,d3.set(this.scale().domain()))),null!=this._hoverCallback&&(this.datumCurrentlyFocusedOn=this.scale().domain().indexOf(this.datumCurrentlyFocusedOn)>=0?this.datumCurrentlyFocusedOn:void 0),this._invalidateLayout()},c.prototype._computeLayout=function(a,c,d,e){b.prototype._computeLayout.call(this,a,c,d,e);var f=this.measureTextHeight(),g=this.colorScale.domain().length;this.nRowsDrawn=Math.min(g,Math.floor(this.height()/f))},c.prototype._requestedSpace=function(b,d){var e=this.measureTextHeight(),f=this.colorScale.domain().length,g=Math.min(f,Math.floor((d-2*c.MARGIN)/e)),h=this._content.append("g").classed(c.SUBELEMENT_CLASS,!0),i=a._Util.Text.getTextMeasurer(h.append("text")),j=a._Util.Methods.max(this.colorScale.domain(),function(a){return i(a).width});h.remove(),j=void 0===j?0:j;var k=0===g?0:j+e+2*c.MARGIN,l=0===g?0:f*e+2*c.MARGIN;return{width:k,height:l,wantsWidth:k>b,wantsHeight:l>d}},c.prototype.measureTextHeight=function(){var b=this._content.append("g").classed(c.SUBELEMENT_CLASS,!0),d=a._Util.Text.getTextMeasurer(b.append("text"))(a._Util.Text.HEIGHT_TEXT).height;return 0===d&&(d=1),b.remove(),d},c.prototype._doRender=function(){b.prototype._doRender.call(this);var d=this.colorScale.domain().slice(0,this.nRowsDrawn),e=this.measureTextHeight(),f=this.width()-e-c.MARGIN,g=.3*e,h=this._content.selectAll("."+c.SUBELEMENT_CLASS).data(d,function(a){return a}),i=h.enter().append("g").classed(c.SUBELEMENT_CLASS,!0);i.append("circle"),i.append("g").classed("text-container",!0),h.exit().remove(),h.selectAll("circle").attr("cx",e/2).attr("cy",e/2).attr("r",g).attr("fill",this.colorScale._d3Scale),h.selectAll("g.text-container").text("").attr("transform","translate("+e+", 0)").each(function(b){var c=d3.select(this),d=a._Util.Text.getTextMeasurer(c.append("text")),e=a._Util.Text.getTruncatedText(b,f,d),g=d(e);a._Util.Text.writeLineHorizontally(e,c,g.width,g.height)}),h.attr("transform",function(a){return"translate("+c.MARGIN+","+(d.indexOf(a)*e+c.MARGIN)+")"}),this.updateClasses(),this.updateListeners()},c.prototype.updateListeners=function(){var a=this;if(this._isSetup){var b=this._content.selectAll("."+c.SUBELEMENT_CLASS);if(null!=this._hoverCallback){var d=function(b){return function(c){a.datumCurrentlyFocusedOn=b?c:void 0,a._hoverCallback(a.datumCurrentlyFocusedOn),a.updateClasses()}};b.on("mouseover",d(!0)),b.on("mouseout",d(!1))}else b.on("mouseover",null),b.on("mouseout",null);null!=this._toggleCallback?b.on("click",function(b){var c=a.isOff.has(b);c?a.isOff.remove(b):a.isOff.add(b),a._toggleCallback(b,c),a.updateClasses()}):b.on("click",null)}},c.prototype.updateClasses=function(){var a=this;if(this._isSetup){var b=this._content.selectAll("."+c.SUBELEMENT_CLASS);null!=this._hoverCallback?(b.classed("focus",function(b){return a.datumCurrentlyFocusedOn===b}),b.classed("hover",void 0!==this.datumCurrentlyFocusedOn)):(b.classed("hover",!1),b.classed("focus",!1)),null!=this._toggleCallback?(b.classed("toggled-on",function(b){return!a.isOff.has(b)}),b.classed("toggled-off",function(b){return a.isOff.has(b)})):(b.classed("toggled-on",!1),b.classed("toggled-off",!1))}},c.SUBELEMENT_CLASS="legend-row",c.MARGIN=5,c}(b.AbstractComponent);b.Legend=c}(a.Component||(a.Component={}));a.Component}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(a){var c=this;b.call(this),this.padding=5,this.classed("legend",!0),this.scale=a,this.scale.broadcaster.registerListener(this,function(){return c._invalidateLayout()}),this.xAlign("left").yAlign("center"),this._fixedWidthFlag=!0,this._fixedHeightFlag=!0}return __extends(c,b),c.prototype.remove=function(){b.prototype.remove.call(this),this.scale.broadcaster.deregisterListener(this)},c.prototype.calculateLayoutInfo=function(b,d){var e=this,f=this._content.append("g").classed(c.LEGEND_ROW_CLASS,!0),g=(f.append("g").classed(c.LEGEND_ENTRY_CLASS,!0),a._Util.Text.getTextMeasurer(f.append("text"))),h=g(a._Util.Text.HEIGHT_TEXT).height,i=Math.max(0,b-this.padding),j=function(a){var b=h+g(a).width+e.padding;return Math.min(b,i)},k=this.scale.domain(),l=a._Util.Methods.populateMap(k,j);f.remove();var m=this.packRows(i,k,l),n=Math.floor((d-2*this.padding)/h);return n!==n&&(n=0),{textHeight:h,entryLengths:l,rows:m,numRowsToDraw:Math.max(Math.min(n,m.length),0)}},c.prototype._requestedSpace=function(b,c){var d=this.calculateLayoutInfo(b,c),e=d.rows.map(function(a){return d3.sum(a,function(a){return d.entryLengths.get(a)})}),f=a._Util.Methods.max(e);f=void 0===f?0:f;var g=this.padding+f,h=d.numRowsToDraw*d.textHeight+2*this.padding,i=d.rows.length*d.textHeight+2*this.padding;return{width:g,height:h,wantsWidth:g>b,wantsHeight:i>c}},c.prototype.packRows=function(a,b,c){var d=[[]],e=d[0],f=a;return b.forEach(function(b){var g=c.get(b);g>f&&(e=[],d.push(e),f=a),e.push(b),f-=g}),d},c.prototype._doRender=function(){var d=this;b.prototype._doRender.call(this);var e=this.calculateLayoutInfo(this.width(),this.height()),f=e.rows.slice(0,e.numRowsToDraw),g=this._content.selectAll("g."+c.LEGEND_ROW_CLASS).data(f);g.enter().append("g").classed(c.LEGEND_ROW_CLASS,!0),g.exit().remove(),g.attr("transform",function(a,b){return"translate(0, "+(b*e.textHeight+d.padding)+")"});var h=g.selectAll("g."+c.LEGEND_ENTRY_CLASS).data(function(a){return a}),i=h.enter().append("g").classed(c.LEGEND_ENTRY_CLASS,!0);i.append("circle"),i.append("g").classed("text-container",!0),h.exit().remove();var j=this.padding;g.each(function(){var a=j,b=d3.select(this).selectAll("g."+c.LEGEND_ENTRY_CLASS);b.attr("transform",function(b){var c="translate("+a+", 0)";return a+=e.entryLengths.get(b),c})}),h.select("circle").attr("cx",e.textHeight/2).attr("cy",e.textHeight/2).attr("r",.3*e.textHeight).attr("fill",function(a){return d.scale.scale(a)});var k=this.padding,l=h.select("g.text-container");l.text(""),l.append("title").text(function(a){return a}),l.attr("transform","translate("+e.textHeight+", "+.1*e.textHeight+")").each(function(b){var c=d3.select(this),d=a._Util.Text.getTextMeasurer(c.append("text")),f=e.entryLengths.get(b)-e.textHeight-k,g=a._Util.Text.getTruncatedText(b,f,d),h=d(g);a._Util.Text.writeLineHorizontally(g,c,h.width,h.height)})},c.LEGEND_ROW_CLASS="legend-row",c.LEGEND_ENTRY_CLASS="legend-entry",c}(b.AbstractComponent);b.HorizontalLegend=c}(a.Component||(a.Component={}));a.Component}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(c,d){var e=this;if(null!=c&&!a.Scale.AbstractQuantitative.prototype.isPrototypeOf(c))throw new Error("xScale needs to inherit from Scale.AbstractQuantitative");if(null!=d&&!a.Scale.AbstractQuantitative.prototype.isPrototypeOf(d))throw new Error("yScale needs to inherit from Scale.AbstractQuantitative");b.call(this),this.classed("gridlines",!0),this.xScale=c,this.yScale=d,this.xScale&&this.xScale.broadcaster.registerListener(this,function(){return e._render()}),this.yScale&&this.yScale.broadcaster.registerListener(this,function(){return e._render()})}return __extends(c,b),c.prototype.remove=function(){return b.prototype.remove.call(this),this.xScale&&this.xScale.broadcaster.deregisterListener(this),this.yScale&&this.yScale.broadcaster.deregisterListener(this),this},c.prototype._setup=function(){b.prototype._setup.call(this),this.xLinesContainer=this._content.append("g").classed("x-gridlines",!0),this.yLinesContainer=this._content.append("g").classed("y-gridlines",!0)},c.prototype._doRender=function(){b.prototype._doRender.call(this),this.redrawXLines(),this.redrawYLines()},c.prototype.redrawXLines=function(){var a=this;if(this.xScale){var b=this.xScale.ticks(),c=function(b){return a.xScale.scale(b)},d=this.xLinesContainer.selectAll("line").data(b);d.enter().append("line"),d.attr("x1",c).attr("y1",0).attr("x2",c).attr("y2",this.height()).classed("zeroline",function(a){return 0===a}),d.exit().remove()}},c.prototype.redrawYLines=function(){var a=this;if(this.yScale){var b=this.yScale.ticks(),c=function(b){return a.yScale.scale(b)},d=this.yLinesContainer.selectAll("line").data(b);d.enter().append("line"),d.attr("x1",0).attr("y1",c).attr("x2",this.width()).attr("y2",c).classed("zeroline",function(a){return 0===a}),d.exit().remove()}},c}(b.AbstractComponent);b.Gridlines=c}(a.Component||(a.Component={}));a.Component}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(a){var c=this;void 0===a&&(a=[]),b.call(this),this.rowPadding=0,this.colPadding=0,this.rows=[],this.rowWeights=[],this.colWeights=[],this.nRows=0,this.nCols=0,this.classed("table",!0),a.forEach(function(a,b){a.forEach(function(a,d){c.addComponent(b,d,a)})})}return __extends(c,b),c.prototype.addComponent=function(a,b,c){if(this._addComponent(c)){this.nRows=Math.max(a+1,this.nRows),this.nCols=Math.max(b+1,this.nCols),this.padTableToSize(this.nRows,this.nCols);var d=this.rows[a][b];if(d)throw new Error("Table.addComponent cannot be called on a cell where a component already exists (for the moment)");this.rows[a][b]=c}return this},c.prototype._removeComponent=function(a){b.prototype._removeComponent.call(this,a);var c,d;a:for(var e=0;e0&&v&&e!==x,C=f>0&&w&&f!==y;if(!B&&!C)break;if(r>5)break}return e=h-d3.sum(u.guaranteedWidths),f=i-d3.sum(u.guaranteedHeights),n=c.calcProportionalSpace(k,e),o=c.calcProportionalSpace(j,f),{colProportionalSpace:n,rowProportionalSpace:o,guaranteedWidths:u.guaranteedWidths,guaranteedHeights:u.guaranteedHeights,wantsWidth:v,wantsHeight:w}},c.prototype.determineGuarantees=function(b,c){var d=a._Util.Methods.createFilledArray(0,this.nCols),e=a._Util.Methods.createFilledArray(0,this.nRows),f=a._Util.Methods.createFilledArray(!1,this.nCols),g=a._Util.Methods.createFilledArray(!1,this.nRows);return this.rows.forEach(function(a,h){a.forEach(function(a,i){var j;j=null!=a?a._requestedSpace(b[i],c[h]):{width:0,height:0,wantsWidth:!1,wantsHeight:!1};var k=Math.min(j.width,b[i]),l=Math.min(j.height,c[h]);d[i]=Math.max(d[i],k),e[h]=Math.max(e[h],l),f[i]=f[i]||j.wantsWidth,g[h]=g[h]||j.wantsHeight})}),{guaranteedWidths:d,guaranteedHeights:e,wantsWidthArr:f,wantsHeightArr:g}},c.prototype._requestedSpace=function(a,b){var c=this.iterateLayout(a,b);return{width:d3.sum(c.guaranteedWidths),height:d3.sum(c.guaranteedHeights),wantsWidth:c.wantsWidth,wantsHeight:c.wantsHeight}},c.prototype._computeLayout=function(c,d,e,f){var g=this;b.prototype._computeLayout.call(this,c,d,e,f);var h=this.iterateLayout(this.width(),this.height()),i=a._Util.Methods.addArrays(h.rowProportionalSpace,h.guaranteedHeights),j=a._Util.Methods.addArrays(h.colProportionalSpace,h.guaranteedWidths),k=0;this.rows.forEach(function(a,b){var c=0;a.forEach(function(a,d){null!=a&&a._computeLayout(c,k,j[d],i[b]),c+=j[d]+g.colPadding}),k+=i[b]+g.rowPadding})},c.prototype.padding=function(a,b){return this.rowPadding=a,this.colPadding=b,this._invalidateLayout(),this},c.prototype.rowWeight=function(a,b){return this.rowWeights[a]=b,this._invalidateLayout(),this},c.prototype.colWeight=function(a,b){return this.colWeights[a]=b,this._invalidateLayout(),this},c.prototype._isFixedWidth=function(){var a=d3.transpose(this.rows);return c.fixedSpace(a,function(a){return null==a||a._isFixedWidth()})},c.prototype._isFixedHeight=function(){return c.fixedSpace(this.rows,function(a){return null==a||a._isFixedHeight()})},c.prototype.padTableToSize=function(a,b){for(var c=0;a>c;c++){void 0===this.rows[c]&&(this.rows[c]=[],this.rowWeights[c]=null);for(var d=0;b>d;d++)void 0===this.rows[c][d]&&(this.rows[c][d]=null)}for(d=0;b>d;d++)void 0===this.colWeights[d]&&(this.colWeights[d]=null)},c.calcComponentWeights=function(a,b,c){return a.map(function(a,d){if(null!=a)return a;var e=b[d].map(c),f=e.reduce(function(a,b){return a&&b},!0);return f?0:1})},c.calcProportionalSpace=function(b,c){var d=d3.sum(b);return 0===d?a._Util.Methods.createFilledArray(0,b.length):b.map(function(a){return c*a/d})},c.fixedSpace=function(a,b){var c=function(a){return a.reduce(function(a,b){return a&&b},!0)},d=function(a){return c(a.map(b))};return c(a.map(d))},c}(b.AbstractComponentContainer);b.Table=c}(a.Component||(a.Component={}));a.Component}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(){b.call(this),this._dataChanged=!1,this._projectors={},this._animate=!1,this._animators={},this._ANIMATION_DURATION=250,this.animateOnNextRender=!0,this.clipPathEnabled=!0,this.classed("plot",!0),this._key2DatasetDrawerKey=d3.map(),this._datasetKeysInOrder=[],this.nextSeriesIndex=0}return __extends(c,b),c.prototype._anchor=function(a){b.prototype._anchor.call(this,a),this.animateOnNextRender=!0,this._dataChanged=!0,this._updateScaleExtents()},c.prototype._setup=function(){var a=this;b.prototype._setup.call(this),this._renderArea=this._content.append("g").classed("render-area",!0),this._getDrawersInOrder().forEach(function(b){return b._renderArea=a._renderArea.append("g")})},c.prototype.remove=function(){var a=this;b.prototype.remove.call(this),this._datasetKeysInOrder.forEach(function(b){return a.removeDataset(b)});var c=Object.keys(this._projectors);c.forEach(function(b){var c=a._projectors[b];c.scale&&c.scale.broadcaster.deregisterListener(a)})},c.prototype.addDataset=function(b,c){if("string"!=typeof b&&void 0!==c)throw new Error("invalid input to addDataset");"string"==typeof b&&"_"===b[0]&&a._Util.Methods.warn("Warning: Using _named series keys may produce collisions with unlabeled data sources");var d="string"==typeof b?b:"_"+this.nextSeriesIndex++,e="string"!=typeof b?b:c,c=e instanceof a.Dataset?e:new a.Dataset(e);return this._addDataset(d,c),this},c.prototype._addDataset=function(a,b){var c=this;this._key2DatasetDrawerKey.has(a)&&this.removeDataset(a);var d=this._getDrawer(a),e={drawer:d,dataset:b,key:a};this._datasetKeysInOrder.push(a),this._key2DatasetDrawerKey.set(a,e),this._isSetup&&(d._renderArea=this._renderArea.append("g")),b.broadcaster.registerListener(this,function(){return c._onDatasetUpdate()}),this._onDatasetUpdate()},c.prototype._getDrawer=function(b){return new a._Drawer.AbstractDrawer(b)},c.prototype._getAnimator=function(){return new a.Animator.Null},c.prototype._onDatasetUpdate=function(){this._updateScaleExtents(),this.animateOnNextRender=!0,this._dataChanged=!0,this._render()},c.prototype.attr=function(a,b,c){return this.project(a,b,c)},c.prototype.project=function(b,c,d){var e=this;b=b.toLowerCase();var f=this._projectors[b],g=f&&f.scale;g&&this._datasetKeysInOrder.forEach(function(a){g._removeExtent(e._plottableID.toString()+"_"+a,b),g.broadcaster.deregisterListener(e)}),d&&d.broadcaster.registerListener(this,function(){return e._render()});var h=a._Util.Methods._applyAccessor(c,this);return this._projectors[b]={accessor:h,scale:d,attribute:b},this._updateScaleExtent(b),this._render(),this},c.prototype._generateAttrToProjector=function(){var a=this,b={};return d3.keys(this._projectors).forEach(function(c){var d=a._projectors[c],e=d.accessor,f=d.scale,g=f?function(a,b){return f.scale(e(a,b))}:e;b[c]=g}),b},c.prototype._doRender=function(){this._isAnchored&&(this._paint(),this._dataChanged=!1,this.animateOnNextRender=!1)},c.prototype.animate=function(a){return this._animate=a,this},c.prototype.detach=function(){return b.prototype.detach.call(this),this._updateScaleExtents(),this},c.prototype._updateScaleExtents=function(){var a=this;d3.keys(this._projectors).forEach(function(b){return a._updateScaleExtent(b)})},c.prototype._updateScaleExtent=function(a){var b=this,c=this._projectors[a];c.scale&&this._key2DatasetDrawerKey.forEach(function(d,e){var f=e.dataset._getExtent(c.accessor,c.scale._typeCoercer),g=b._plottableID.toString()+"_"+d;0!==f.length&&b._isAnchored?c.scale._updateExtent(g,a,f):c.scale._removeExtent(g,a)})},c.prototype._applyAnimatedAttributes=function(a,b,c){return this._animate&&this.animateOnNextRender&&this._animators[b]?this._animators[b].animate(a,c):a.attr(c)},c.prototype.animator=function(a,b){return void 0===b?this._animators[a]:(this._animators[a]=b,this)},c.prototype.datasetOrder=function(b){function c(b,c){var d=a._Util.Methods.intersection(d3.set(b),d3.set(c)),e=d.size();return e===b.length&&e===c.length}return void 0===b?this._datasetKeysInOrder:(c(b,this._datasetKeysInOrder)?(this._datasetKeysInOrder=b,this._onDatasetUpdate()):a._Util.Methods.warn("Attempted to change datasetOrder, but new order is not permutation of old. Ignoring."),this)},c.prototype.removeDataset=function(b){var c;if("string"==typeof b)c=b;else if(b instanceof a.Dataset||b instanceof Array){var d=b instanceof a.Dataset?this.datasets():this.datasets().map(function(a){return a.data()}),e=d.indexOf(b);-1!==e&&(c=this._datasetKeysInOrder[e])}return this._removeDataset(c)},c.prototype._removeDataset=function(a){if(null!=a&&this._key2DatasetDrawerKey.has(a)){var b=this._key2DatasetDrawerKey.get(a);b.drawer.remove();var c=d3.values(this._projectors),d=this._plottableID.toString()+"_"+a;c.forEach(function(a){null!=a.scale&&a.scale._removeExtent(d,a.attribute)}),b.dataset.broadcaster.deregisterListener(this),this._datasetKeysInOrder.splice(this._datasetKeysInOrder.indexOf(a),1),this._key2DatasetDrawerKey.remove(a),this._onDatasetUpdate()}return this},c.prototype.datasets=function(){var a=this;return this._datasetKeysInOrder.map(function(b){return a._key2DatasetDrawerKey.get(b).dataset})},c.prototype._getDrawersInOrder=function(){var a=this;return this._datasetKeysInOrder.map(function(b){return a._key2DatasetDrawerKey.get(b).drawer})},c.prototype._paint=function(){var b=this,c=this._generateAttrToProjector(),d=this.datasets();this._getDrawersInOrder().forEach(function(e,f){var g=b._animate?b._getAnimator(e,f):new a.Animator.Null;e.draw(d[f].data(),c,g)})},c}(a.Component.AbstractComponent);b.AbstractPlot=c}(a.Plot||(a.Plot={}));a.Plot}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(){b.call(this),this.classed("pie-plot",!0)}return __extends(c,b),c.prototype._computeLayout=function(a,c,d,e){b.prototype._computeLayout.call(this,a,c,d,e),this._renderArea.attr("transform","translate("+this.width()/2+","+this.height()/2+")")},c.prototype._addDataset=function(c,d){return 1===this._datasetKeysInOrder.length?void a._Util.Methods.warn("Only one dataset is supported in Pie plots"):void b.prototype._addDataset.call(this,c,d)},c.prototype._generateAttrToProjector=function(){var a=this.retargetProjectors(b.prototype._generateAttrToProjector.call(this)),d=a["inner-radius"]||d3.functor(0),e=a["outer-radius"]||d3.functor(Math.min(this.width(),this.height())/2);return a.d=d3.svg.arc().innerRadius(d).outerRadius(e),delete a["inner-radius"],delete a["outer-radius"],null==a.fill&&(a.fill=function(a,b){return c.DEFAULT_COLOR_SCALE.scale(String(b))}),delete a.value,a},c.prototype.retargetProjectors=function(a){var b={};return d3.entries(a).forEach(function(a){b[a.key]=function(b,c){return a.value(b.data,c)}}),b},c.prototype._getDrawer=function(b){return new a._Drawer.Arc(b)},c.prototype._paint=function(){var b=this,c=this._generateAttrToProjector(),d=this.datasets();this._getDrawersInOrder().forEach(function(e,f){var g=b._animate?b._getAnimator(e,f):new a.Animator.Null,h=b.pie(d[f].data());e.draw(h,c,g)})},c.prototype.pie=function(a){var b=function(a){return a.value},c=this._projectors.value,d=c?c.accessor:b;return d3.layout.pie().sort(null).value(d)(a)},c.DEFAULT_COLOR_SCALE=new a.Scale.Color,c}(b.AbstractPlot);b.Pie=c}(a.Plot||(a.Plot={}));a.Plot}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(a,c){if(b.call(this),null==a||null==c)throw new Error("XYPlots require an xScale and yScale");this.classed("xy-plot",!0),this.project("x","x",a),this.project("y","y",c)}return __extends(c,b),c.prototype.project=function(a,c,d){return"x"===a&&d&&(this._xScale=d,this._updateXDomainer()),"y"===a&&d&&(this._yScale=d,this._updateYDomainer()),b.prototype.project.call(this,a,c,d),this -},c.prototype._computeLayout=function(a,c,d,e){b.prototype._computeLayout.call(this,a,c,d,e),this._xScale.range([0,this.width()]),this._yScale.range([this.height(),0])},c.prototype._updateXDomainer=function(){if(this._xScale instanceof a.Scale.AbstractQuantitative){var b=this._xScale;b._userSetDomainer||b.domainer().pad().nice()}},c.prototype._updateYDomainer=function(){if(this._yScale instanceof a.Scale.AbstractQuantitative){var b=this._yScale;b._userSetDomainer||b.domainer().pad().nice()}},c}(b.AbstractPlot);b.AbstractXYPlot=c}(a.Plot||(a.Plot={}));a.Plot}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(c,d){b.call(this,c,d),this._animators={"circles-reset":new a.Animator.Null,circles:(new a.Animator.IterativeDelay).duration(250).delay(5)},this.classed("scatter-plot",!0),this.project("r",3),this.project("opacity",.6),this.project("fill",function(){return a.Core.Colors.INDIGO})}return __extends(c,b),c.prototype.project=function(a,c,d){return a="cx"===a?"x":a,a="cy"===a?"y":a,b.prototype.project.call(this,a,c,d),this},c.prototype._generateAttrToProjector=function(){var a=b.prototype._generateAttrToProjector.call(this);return a.cx=a.x,delete a.x,a.cy=a.y,delete a.y,a},c.prototype._paint=function(){var a=this,b=this._generateAttrToProjector(),c=this.datasets();this._getDrawersInOrder().forEach(function(d,e){var f=c[e],g=d._renderArea.selectAll("circle").data(f.data());if(g.enter().append("circle"),a._dataChanged){var h=b.r;b.r=function(){return 0},a._applyAnimatedAttributes(g,"circles-reset",b),b.r=h}a._applyAnimatedAttributes(g,"circles",b),g.exit().remove()})},c}(b.AbstractXYPlot);b.Scatter=c}(a.Plot||(a.Plot={}));a.Plot}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(c,d,e){b.call(this,c,d),this._animators={cells:new a.Animator.Null},this.classed("grid-plot",!0),this._xScale.rangeType("bands",0,0),this._yScale.rangeType("bands",0,0),this._colorScale=e,this.project("fill","value",e)}return __extends(c,b),c.prototype._addDataset=function(c,d){return 1===this._datasetKeysInOrder.length?void a._Util.Methods.warn("Only one dataset is supported in Grid plots"):void b.prototype._addDataset.call(this,c,d)},c.prototype.project=function(a,c,d){return b.prototype.project.call(this,a,c,d),"fill"===a&&(this._colorScale=this._projectors.fill.scale),this},c.prototype._paint=function(){var a=this.datasets()[0],b=this._renderArea.selectAll("rect").data(a.data());b.enter().append("rect");var c=this._xScale.rangeBand(),d=this._yScale.rangeBand(),e=this._generateAttrToProjector();e.width=function(){return c},e.height=function(){return d},this._applyAnimatedAttributes(b,"cells",e),b.exit().remove()},c}(b.AbstractXYPlot);b.Grid=c}(a.Plot||(a.Plot={}));a.Plot}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(c,d){b.call(this,c,d),this._baselineValue=0,this._barAlignmentFactor=0,this._animators={"bars-reset":new a.Animator.Null,bars:new a.Animator.IterativeDelay,baseline:new a.Animator.Null},this.classed("bar-plot",!0),this.project("fill",function(){return a.Core.Colors.INDIGO}),this.baseline(this._baselineValue)}return __extends(c,b),c.prototype._getDrawer=function(b){return new a._Drawer.Rect(b)},c.prototype._setup=function(){b.prototype._setup.call(this),this._baseline=this._renderArea.append("line").classed("baseline",!0)},c.prototype._paint=function(){var a=this,b=(this._generateAttrToProjector(),this.datasets()),c=this._isVertical?this._yScale:this._xScale,d=c.scale(this._baselineValue),e=this._isVertical?"y":"x",f=this._isVertical?"height":"width";this._getDrawersInOrder().forEach(function(c,g){var h=b[g],i=c._renderArea.selectAll("rect").data(h.data());if(i.enter().append("rect"),a._dataChanged&&a._animate){var j=a._generateAttrToProjector();j[e]=function(){return d},j[f]=function(){return 0},a._applyAnimatedAttributes(i,"bars-reset",j)}var k=a._generateAttrToProjector();k.fill&&i.attr("fill",k.fill),a._applyAnimatedAttributes(i,"bars",k),i.exit().remove()});var g={x1:this._isVertical?0:d,y1:this._isVertical?d:0,x2:this._isVertical?this.width():d,y2:this._isVertical?d:this.height()};this._applyAnimatedAttributes(this._baseline,"baseline",g)},c.prototype.baseline=function(a){return this._baselineValue=a,this._updateXDomainer(),this._updateYDomainer(),this._render(),this},c.prototype.barAlignment=function(a){var b=a.toLowerCase(),c=this.constructor._BarAlignmentToFactor;if(void 0===c[b])throw new Error("unsupported bar alignment");return this._barAlignmentFactor=c[b],this._render(),this},c.prototype.parseExtent=function(a){if("number"==typeof a)return{min:a,max:a};if(a instanceof Object&&"min"in a&&"max"in a)return a;throw new Error("input '"+a+"' can't be parsed as an Extent")},c.prototype.selectBar=function(a,b,c){if(void 0===c&&(c=!0),!this._isSetup)return null;var d=[],e=this.parseExtent(a),f=this.parseExtent(b),g=.5;if(this._getDrawersInOrder().forEach(function(a){a._renderArea.selectAll("rect").each(function(){var a=this.getBBox();a.x+a.width>=e.min-g&&a.x<=e.max+g&&a.y+a.height>=f.min-g&&a.y<=f.max+g&&d.push(this)})}),d.length>0){var h=d3.selectAll(d);return h.classed("selected",c),h}return null},c.prototype.deselectAll=function(){return this._isSetup&&this._getDrawersInOrder().forEach(function(a){return a._renderArea.selectAll("rect").classed("selected",!1)}),this},c.prototype._updateDomainer=function(b){if(b instanceof a.Scale.AbstractQuantitative){var c=b;c._userSetDomainer||(null!=this._baselineValue?c.domainer().addPaddingException(this._baselineValue,"BAR_PLOT+"+this._plottableID).addIncludedValue(this._baselineValue,"BAR_PLOT+"+this._plottableID):c.domainer().removePaddingException("BAR_PLOT+"+this._plottableID).removeIncludedValue("BAR_PLOT+"+this._plottableID),c.domainer().pad()),c._autoDomainIfAutomaticMode()}},c.prototype._updateYDomainer=function(){this._isVertical?this._updateDomainer(this._yScale):b.prototype._updateYDomainer.call(this)},c.prototype._updateXDomainer=function(){this._isVertical?b.prototype._updateXDomainer.call(this):this._updateDomainer(this._xScale)},c.prototype._generateAttrToProjector=function(){var d=this,e=b.prototype._generateAttrToProjector.call(this),f=this._isVertical?this._yScale:this._xScale,g=this._isVertical?this._xScale:this._yScale,h=this._isVertical?"y":"x",i=this._isVertical?"x":"y",j=g instanceof a.Scale.Ordinal&&"bands"===g.rangeType(),k=f.scale(this._baselineValue);if(!e.width){var l=j?g.rangeBand():c.DEFAULT_WIDTH;e.width=function(){return l}}var m=e[i],n=e.width;if(j){var o=g.rangeBand();e[i]=function(a,b){return m(a,b)-n(a,b)/2+o/2}}else e[i]=function(a,b){return m(a,b)-n(a,b)*d._barAlignmentFactor};var p=e[h];return e[h]=function(a,b){var c=p(a,b);return c>k?k:c},e.height=function(a,b){return Math.abs(k-p(a,b))},e},c._BarAlignmentToFactor={},c.DEFAULT_WIDTH=10,c}(b.AbstractXYPlot);b.AbstractBarPlot=c}(a.Plot||(a.Plot={}));a.Plot}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(a){var b=function(a){function b(b,c){this._isVertical=!0,a.call(this,b,c)}return __extends(b,a),b.prototype._updateYDomainer=function(){this._updateDomainer(this._yScale)},b._BarAlignmentToFactor={left:0,center:.5,right:1},b}(a.AbstractBarPlot);a.VerticalBar=b}(a.Plot||(a.Plot={}));a.Plot}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(a){var b=function(a){function b(b,c){a.call(this,b,c)}return __extends(b,a),b.prototype._updateXDomainer=function(){this._updateDomainer(this._xScale)},b.prototype._generateAttrToProjector=function(){var b=a.prototype._generateAttrToProjector.call(this),c=b.width;return b.width=b.height,b.height=c,b},b._BarAlignmentToFactor={top:0,center:.5,bottom:1},b}(a.AbstractBarPlot);a.HorizontalBar=b}(a.Plot||(a.Plot={}));a.Plot}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(c,d){b.call(this,c,d),this._animators={"line-reset":new a.Animator.Null,line:(new a.Animator.Base).duration(600).easing("exp-in-out")},this.classed("line-plot",!0),this.project("stroke",function(){return a.Core.Colors.INDIGO}),this.project("stroke-width",function(){return"2px"})}return __extends(c,b),c.prototype._getResetYFunction=function(){var a=this._yScale.domain(),b=Math.max(a[0],a[1]),c=Math.min(a[0],a[1]),d=0>b&&b||c>0&&c||0,e=this._yScale.scale(d);return function(){return e}},c.prototype._generateAttrToProjector=function(){var a=b.prototype._generateAttrToProjector.call(this),c=this._wholeDatumAttributes(),d=function(a){return-1===c.indexOf(a)},e=d3.keys(a).filter(d);return e.forEach(function(b){var c=a[b];a[b]=function(a,b){return a.length>0?c(a[0],b):null}}),a},c.prototype._rejectNullsAndNaNs=function(a,b,c){var d=c(a,b);return null!=d&&d===d},c.prototype._paint=function(){var a=this,b=this._generateAttrToProjector(),c=b.x,d=b.y;delete b.x,delete b.y;var e=d3.svg.line().x(c).defined(function(b,e){return a._rejectNullsAndNaNs(b,e,c)&&a._rejectNullsAndNaNs(b,e,d)});b.d=e;var f=this.datasets();this._getDrawersInOrder().forEach(function(c,g){var h,i=f[g];h=c._renderArea.select(".line").node()?c._renderArea.select(".line"):c._renderArea.append("path").classed("line",!0),h.datum(i.data()),a._dataChanged&&(e.y(a._getResetYFunction()),a._applyAnimatedAttributes(h,"line-reset",b)),e.y(d),a._applyAnimatedAttributes(h,"line",b)})},c.prototype._wholeDatumAttributes=function(){return["x","y"]},c}(b.AbstractXYPlot);b.Line=c}(a.Plot||(a.Plot={}));a.Plot}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(c,d){b.call(this,c,d),this.classed("area-plot",!0),this.project("y0",0,d),this.project("fill",function(){return a.Core.Colors.INDIGO}),this.project("fill-opacity",function(){return.25}),this.project("stroke",function(){return a.Core.Colors.INDIGO}),this._animators["area-reset"]=new a.Animator.Null,this._animators.area=(new a.Animator.Base).duration(600).easing("exp-in-out")}return __extends(c,b),c.prototype._onDatasetUpdate=function(){b.prototype._onDatasetUpdate.call(this),null!=this._yScale&&this._updateYDomainer()},c.prototype._updateYDomainer=function(){var c=this;b.prototype._updateYDomainer.call(this);var d,e=this._projectors.y0,f=e&&e.accessor;if(null!=f){var g=this.datasets().map(function(a){return a._getExtent(f,c._yScale._typeCoercer)}),h=a._Util.Methods.flatten(g),i=a._Util.Methods.uniq(h);1===i.length&&(d=i[0])}this._yScale._userSetDomainer||(null!=d?this._yScale.domainer().addPaddingException(d,"AREA_PLOT+"+this._plottableID):this._yScale.domainer().removePaddingException("AREA_PLOT+"+this._plottableID),this._yScale._autoDomainIfAutomaticMode())},c.prototype.project=function(a,c,d){return b.prototype.project.call(this,a,c,d),"y0"===a&&this._updateYDomainer(),this},c.prototype._getResetYFunction=function(){return this._generateAttrToProjector().y0},c.prototype._paint=function(){var a=this;b.prototype._paint.call(this);var c=this._generateAttrToProjector(),d=c.x,e=c.y0,f=c.y;delete c.x,delete c.y0,delete c.y;var g=d3.svg.area().x(d).y0(e).defined(function(b,c){return a._rejectNullsAndNaNs(b,c,d)&&a._rejectNullsAndNaNs(b,c,f)});c.d=g;var h=this.datasets();this._getDrawersInOrder().forEach(function(b,d){var e,i=h[d];e=b._renderArea.select(".area").node()?b._renderArea.select(".area"):b._renderArea.insert("path",".line").classed("area",!0),e.datum(i.data()),a._dataChanged&&(g.y1(a._getResetYFunction()),a._applyAnimatedAttributes(e,"area-reset",c)),g.y1(f),a._applyAnimatedAttributes(e,"area",c)})},c.prototype._wholeDatumAttributes=function(){var a=b.prototype._wholeDatumAttributes.call(this);return a.push("y0"),a},c}(b.Line);b.Area=c}(a.Plot||(a.Plot={}));a.Plot}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(c,d,e){void 0===e&&(e=!0),this._isVertical=e,b.call(this,c,d),this.innerScale=new a.Scale.Ordinal}return __extends(c,b),c.prototype._generateAttrToProjector=function(){var a=this,c=b.prototype._generateAttrToProjector.call(this),d=c.width;this.innerScale.range([0,d(null,0)]);var e=function(){return a.innerScale.rangeBand()},f=c.height;c.width=this._isVertical?e:f,c.height=this._isVertical?f:e;var g=function(a){return a._PLOTTABLE_PROTECTED_FIELD_POSITION};return c.x=this._isVertical?g:c.x,c.y=this._isVertical?c.y:g,c},c.prototype.cluster=function(a){var b=this;this.innerScale.domain(this._datasetKeysInOrder);var c={};return this._datasetKeysInOrder.forEach(function(d){var e=b._key2DatasetDrawerKey.get(d).dataset.data();c[d]=e.map(function(c,e){var f=a(c,e),g=b._isVertical?b._xScale:b._yScale;return c._PLOTTABLE_PROTECTED_FIELD_POSITION=g.scale(f)+b.innerScale.scale(d),c})}),c},c.prototype._paint=function(){var a=this._generateAttrToProjector(),b=this._isVertical?this._projectors.x.accessor:this._projectors.y.accessor,c=this.cluster(b);this._getDrawersInOrder().forEach(function(b){return b.draw(c[b.key],a)});var d=this._isVertical?this._yScale:this._xScale,e=d.scale(this._baselineValue),f={x1:this._isVertical?0:e,y1:this._isVertical?e:0,x2:this._isVertical?this.width():e,y2:this._isVertical?e:this.height()};this._applyAnimatedAttributes(this._baseline,"baseline",f)},c}(b.AbstractBarPlot);b.ClusteredBar=c}(a.Plot||(a.Plot={}));a.Plot}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(){b.apply(this,arguments),this.stackedExtent=[0,0]}return __extends(c,b),c.prototype._onDatasetUpdate=function(){b.prototype._onDatasetUpdate.call(this),this._datasetKeysInOrder&&this._projectors.x&&this._projectors.y&&this.updateStackOffsets()},c.prototype.updateStackOffsets=function(){var b=this.generateDefaultMapArray(),c=this.getDomainKeys(),d=b.map(function(b){return a._Util.Methods.populateMap(c,function(a){return{key:a,value:Math.max(0,b.get(a).value)}})}),e=b.map(function(b){return a._Util.Methods.populateMap(c,function(a){return{key:a,value:Math.min(b.get(a).value,0)}})});this.setDatasetStackOffsets(this.stack(d),this.stack(e)),this.updateStackExtents()},c.prototype.updateStackExtents=function(){var b=this.datasets(),c=this.valueAccessor(),d=a._Util.Methods.max(b,function(b){return a._Util.Methods.max(b.data(),function(a){return c(a)+a._PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET})}),e=a._Util.Methods.min(b,function(b){return a._Util.Methods.min(b.data(),function(a){return c(a)+a._PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET})});this.stackedExtent=[Math.min(e,0),Math.max(0,d)]},c.prototype.stack=function(a){var b=this,c=function(a,b){a.offset=b};return d3.layout.stack().x(function(a){return a.key}).y(function(a){return a.value}).values(function(a){return b.getDomainKeys().map(function(b){return a.get(b)})}).out(c)(a),a},c.prototype.setDatasetStackOffsets=function(a,b){var c=this.keyAccessor(),d=this.valueAccessor();this.datasets().forEach(function(e,f){var g=a[f],h=b[f],i=e.data().every(function(a){return d(a)<=0});e.data().forEach(function(a){var b=g.get(c(a)).offset,e=h.get(c(a)).offset,f=d(a);a._PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET=0===f?i?e:b:f>0?b:e})})},c.prototype.getDomainKeys=function(){var a=this.keyAccessor(),b=d3.set(),c=this.datasets();return c.forEach(function(c){c.data().forEach(function(c){b.add(a(c))})}),b.values()},c.prototype.generateDefaultMapArray=function(){var b=this.keyAccessor(),c=this.valueAccessor(),d=this.datasets(),e=this.getDomainKeys(),f=d.map(function(){return a._Util.Methods.populateMap(e,function(a){return{key:a,value:0}})});return d.forEach(function(a,d){a.data().forEach(function(a){var e=b(a),g=c(a);f[d].set(e,{key:e,value:g})})}),f},c.prototype._updateScaleExtents=function(){b.prototype._updateScaleExtents.call(this);var a=this._isVertical?this._yScale:this._xScale;a&&(this._isAnchored&&this.stackedExtent.length>0?a._updateExtent(this._plottableID.toString(),"_PLOTTABLE_PROTECTED_FIELD_STACK_EXTENT",this.stackedExtent):a._removeExtent(this._plottableID.toString(),"_PLOTTABLE_PROTECTED_FIELD_STACK_EXTENT"))},c.prototype.keyAccessor=function(){return this._isVertical?this._projectors.x.accessor:this._projectors.y.accessor},c.prototype.valueAccessor=function(){return this._isVertical?this._projectors.y.accessor:this._projectors.x.accessor},c}(b.AbstractXYPlot);b.AbstractStacked=c}(a.Plot||(a.Plot={}));a.Plot}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(c){function d(b,d){c.call(this,b,d),this._baselineValue=0,this.classed("area-plot",!0),this.project("fill",function(){return a.Core.Colors.INDIGO}),this._isVertical=!0}return __extends(d,c),d.prototype._getDrawer=function(b){return new a._Drawer.Area(b)},d.prototype._setup=function(){c.prototype._setup.call(this),this._baseline=this._renderArea.append("line").classed("baseline",!0)},d.prototype._paint=function(){c.prototype._paint.call(this);var a=this._yScale.scale(this._baselineValue),b={x1:0,y1:a,x2:this.width(),y2:a};this._applyAnimatedAttributes(this._baseline,"baseline",b)},d.prototype._updateYDomainer=function(){c.prototype._updateYDomainer.call(this);var a=this._yScale;a._userSetDomainer||(a.domainer().addPaddingException(0,"STACKED_AREA_PLOT+"+this._plottableID),a._autoDomainIfAutomaticMode())},d.prototype._onDatasetUpdate=function(){c.prototype._onDatasetUpdate.call(this),b.Area.prototype._onDatasetUpdate.apply(this)},d.prototype._generateAttrToProjector=function(){var a=this,b=c.prototype._generateAttrToProjector.call(this),d=b.x,e=function(b){return a._yScale.scale(b.y+b._PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET)},f=function(b){return a._yScale.scale(b._PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET)};delete b.x,delete b.y0,delete b.y,b.d=d3.svg.area().x(d).y0(f).y1(e);var g=b.fill;return b.fill=function(a,b){return a&&a[0]?g(a[0],b):null},b},d}(b.AbstractStacked);b.StackedArea=c}(a.Plot||(a.Plot={}));a.Plot}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(c){function d(b,d,e){void 0===e&&(e=!0),this._isVertical=e,this._baselineValue=0,this._barAlignmentFactor=.5,c.call(this,b,d),this.classed("bar-plot",!0),this.project("fill",function(){return a.Core.Colors.INDIGO}),this.baseline(this._baselineValue),this._isVertical=e}return __extends(d,c),d.prototype._setup=function(){b.AbstractBarPlot.prototype._setup.call(this)},d.prototype._getAnimator=function(){var b=this._isVertical?this._yScale:this._xScale,c=b.scale(this._baselineValue);return new a.Animator.MovingRect(c,this._isVertical)},d.prototype._getDrawer=function(a){return b.AbstractBarPlot.prototype._getDrawer.apply(this,[a])},d.prototype._generateAttrToProjector=function(){var a=this,c=b.AbstractBarPlot.prototype._generateAttrToProjector.apply(this),d=this._isVertical?"y":"x",e=this._isVertical?this._yScale:this._xScale,f=this._projectors[d].accessor,g=function(a){return e.scale(a._PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET)},h=function(a){return e.scale(f(a)+a._PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET)},i=function(a){return Math.abs(h(a)-g(a))},j=c.width;c.height=this._isVertical?i:j,c.width=this._isVertical?j:i;var k=function(a){return f(a)<0?g(a):h(a)};return c[d]=function(b){return a._isVertical?k(b):k(b)-i(b)},c},d.prototype._paint=function(){c.prototype._paint.call(this);var a=this._isVertical?this._yScale:this._xScale,b=a.scale(this._baselineValue),d={x1:this._isVertical?0:b,y1:this._isVertical?b:0,x2:this._isVertical?this.width():b,y2:this._isVertical?b:this.height()};this._baseline.attr(d)},d.prototype.baseline=function(a){return b.AbstractBarPlot.prototype.baseline.apply(this,[a])},d.prototype._updateDomainer=function(a){return b.AbstractBarPlot.prototype._updateDomainer.apply(this,[a])},d.prototype._updateXDomainer=function(){return b.AbstractBarPlot.prototype._updateXDomainer.apply(this)},d.prototype._updateYDomainer=function(){return b.AbstractBarPlot.prototype._updateYDomainer.apply(this)},d}(b.AbstractStacked);b.StackedBar=c}(a.Plot||(a.Plot={}));a.Plot}(Plottable||(Plottable={}));var Plottable;!function(a){!function(a){var b=function(){function a(){}return a.prototype.animate=function(a,b){return a.attr(b)},a}();a.Null=b}(a.Animator||(a.Animator={}));a.Animator}(Plottable||(Plottable={}));var Plottable;!function(a){!function(a){var b=function(){function a(){this._duration=a.DEFAULT_DURATION_MILLISECONDS,this._delay=a.DEFAULT_DELAY_MILLISECONDS,this._easing=a.DEFAULT_EASING}return a.prototype.animate=function(a,b){return a.transition().ease(this.easing()).duration(this.duration()).delay(this.delay()).attr(b)},a.prototype.duration=function(a){return void 0===a?this._duration:(this._duration=a,this)},a.prototype.delay=function(a){return void 0===a?this._delay:(this._delay=a,this)},a.prototype.easing=function(a){return void 0===a?this._easing:(this._easing=a,this)},a.DEFAULT_DURATION_MILLISECONDS=300,a.DEFAULT_DELAY_MILLISECONDS=0,a.DEFAULT_EASING="exp-out",a}();a.Base=b}(a.Animator||(a.Animator={}));a.Animator}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(a){var b=function(a){function b(){a.call(this),this._maxIterativeDelay=b.DEFAULT_MAX_ITERATIVE_DELAY_MILLISECONDS,this._maxTotalDuration=b.DEFAULT_MAX_TOTAL_DURATION_MILLISECONDS}return __extends(b,a),b.prototype.animate=function(a,b){var c=this,d=a[0].length,e=Math.max(this.maxTotalDuration()-this.duration(),0),f=Math.min(this.maxIterativeDelay(),e/d);return a.transition().ease(this.easing()).duration(this.duration()).delay(function(a,b){return c.delay()+f*b}).attr(b)},b.prototype.maxIterativeDelay=function(a){return void 0===a?this._maxIterativeDelay:(this._maxIterativeDelay=a,this)},b.prototype.maxTotalDuration=function(a){return null==a?this._maxTotalDuration:(this._maxTotalDuration=a,this)},b.DEFAULT_MAX_ITERATIVE_DELAY_MILLISECONDS=15,b.DEFAULT_MAX_TOTAL_DURATION_MILLISECONDS=600,b}(a.Base);a.IterativeDelay=b}(a.Animator||(a.Animator={}));a.Animator}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(a){var b=function(a){function b(b,c){void 0===b&&(b=!0),void 0===c&&(c=!1),a.call(this),this.isVertical=b,this.isReverse=c}return __extends(b,a),b.prototype.animate=function(c,d){var e={};return b.ANIMATED_ATTRIBUTES.forEach(function(a){return e[a]=d[a]}),e[this.getMovingAttr()]=this._startMovingProjector(d),e[this.getGrowingAttr()]=function(){return 0},c.attr(e),a.prototype.animate.call(this,c,d)},b.prototype._startMovingProjector=function(a){if(this.isVertical===this.isReverse)return a[this.getMovingAttr()];var b=a[this.getMovingAttr()],c=a[this.getGrowingAttr()];return function(a,d){return b(a,d)+c(a,d)}},b.prototype.getGrowingAttr=function(){return this.isVertical?"height":"width"},b.prototype.getMovingAttr=function(){return this.isVertical?"y":"x"},b.ANIMATED_ATTRIBUTES=["height","width","x","y","fill"],b}(a.Base);a.Rect=b}(a.Animator||(a.Animator={}));a.Animator}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(a){var b=function(a){function b(b,c){void 0===c&&(c=!0),a.call(this,c),this.startPixelValue=b}return __extends(b,a),b.prototype._startMovingProjector=function(){return d3.functor(this.startPixelValue)},b}(a.Rect);a.MovingRect=b}(a.Animator||(a.Animator={}));a.Animator}(Plottable||(Plottable={}));var Plottable;!function(a){!function(a){!function(a){function b(){e||(d3.select(document).on("keydown",d),e=!0)}function c(a,c){e||b(),null==f[a]&&(f[a]=[]),f[a].push(c)}function d(){null!=f[d3.event.keyCode]&&f[d3.event.keyCode].forEach(function(a){a(d3.event)})}var e=!1,f=[];a.initialize=b,a.addCallback=c}(a.KeyEventListener||(a.KeyEventListener={}));a.KeyEventListener}(a.Core||(a.Core={}));a.Core}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(a){function b(){a.apply(this,arguments)}return __extends(b,a),b.prototype._anchor=function(a,b){this._componentToListenTo=a,this._hitBox=b},b}(a.Core.PlottableObject);b.AbstractInteraction=c}(a.Interaction||(a.Interaction={}));a.Interaction}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(a){var b=function(a){function b(){a.apply(this,arguments)}return __extends(b,a),b.prototype._anchor=function(b,c){var d=this;a.prototype._anchor.call(this,b,c),c.on(this._listenTo(),function(){var a=d3.mouse(c.node()),b=a[0],e=a[1];d._callback({x:b,y:e})})},b.prototype._listenTo=function(){return"click"},b.prototype.callback=function(a){return this._callback=a,this},b}(a.AbstractInteraction);a.Click=b;var c=function(a){function b(){a.apply(this,arguments)}return __extends(b,a),b.prototype._listenTo=function(){return"dblclick"},b}(b);a.DoubleClick=c}(a.Interaction||(a.Interaction={}));a.Interaction}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(a){b.call(this),this.activated=!1,this.keyCode=a}return __extends(c,b),c.prototype._anchor=function(c,d){var e=this;b.prototype._anchor.call(this,c,d),d.on("mouseover",function(){e.activated=!0}),d.on("mouseout",function(){e.activated=!1}),a.Core.KeyEventListener.addCallback(this.keyCode,function(){e.activated&&null!=e._callback&&e._callback()})},c.prototype.callback=function(a){return this._callback=a,this},c}(b.AbstractInteraction);b.Key=c}(a.Interaction||(a.Interaction={}));a.Interaction}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(c,d){var e=this;b.call(this),null==c&&(c=new a.Scale.Linear),null==d&&(d=new a.Scale.Linear),this._xScale=c,this._yScale=d,this.zoom=d3.behavior.zoom(),this.zoom.x(this._xScale._d3Scale),this.zoom.y(this._yScale._d3Scale),this.zoom.on("zoom",function(){return e.rerenderZoomed()})}return __extends(c,b),c.prototype.resetZoom=function(){var a=this;this.zoom=d3.behavior.zoom(),this.zoom.x(this._xScale._d3Scale),this.zoom.y(this._yScale._d3Scale),this.zoom.on("zoom",function(){return a.rerenderZoomed()}),this.zoom(this._hitBox)},c.prototype._anchor=function(a,c){b.prototype._anchor.call(this,a,c),this.zoom(c)},c.prototype.rerenderZoomed=function(){var a=this._xScale._d3Scale.domain(),b=this._yScale._d3Scale.domain();this._xScale.domain(a),this._yScale.domain(b)},c}(b.AbstractInteraction);b.PanZoom=c}(a.Interaction||(a.Interaction={}));a.Interaction}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(){b.apply(this,arguments),this.currentBar=null,this._hoverMode="point"}return __extends(c,b),c.prototype._anchor=function(c,d){var e=this;b.prototype._anchor.call(this,c,d),this.plotIsVertical=this._componentToListenTo._isVertical,this.dispatcher=new a.Dispatcher.Mouse(this._hitBox),this.dispatcher.mousemove(function(a){var b=e.getHoveredBar(a);if(null==b)e._hoverOut();else{if(null!=e.currentBar){if(e.currentBar.node()===b.node())return;e._hoverOut()}e.getBars().classed("not-hovered",!0).classed("hovered",!1),b.classed("not-hovered",!1).classed("hovered",!0),null!=e.hoverCallback&&e.hoverCallback(b.data()[0],b)}e.currentBar=b}),this.dispatcher.mouseout(function(){return e._hoverOut()}),this.dispatcher.connect()},c.prototype.getBars=function(){return this._componentToListenTo._renderArea.selectAll("rect")},c.prototype._hoverOut=function(){this.getBars().classed("not-hovered hovered",!1),null!=this.unhoverCallback&&null!=this.currentBar&&this.unhoverCallback(this.currentBar.data()[0],this.currentBar),this.currentBar=null},c.prototype.getHoveredBar=function(a){if("point"===this._hoverMode)return this._componentToListenTo.selectBar(a.x,a.y,!1);var b={min:-1/0,max:1/0};return this.plotIsVertical?this._componentToListenTo.selectBar(a.x,b,!1):this._componentToListenTo.selectBar(b,a.y,!1)},c.prototype.hoverMode=function(a){if(null==a)return this._hoverMode;var b=a.toLowerCase();if("point"!==b&&"line"!==b)throw new Error(a+" is not a valid hover mode for Interaction.BarHover");return this._hoverMode=b,this},c.prototype.onHover=function(a){return this.hoverCallback=a,this},c.prototype.onUnhover=function(a){return this.unhoverCallback=a,this},c}(b.AbstractInteraction);b.BarHover=c}(a.Interaction||(a.Interaction={}));a.Interaction}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(a){var b=function(a){function b(){var b=this;a.call(this),this.dragInitialized=!1,this._origin=[0,0],this._location=[0,0],this.dragBehavior=d3.behavior.drag(),this.dragBehavior.on("dragstart",function(){return b._dragstart()}),this.dragBehavior.on("drag",function(){return b._drag()}),this.dragBehavior.on("dragend",function(){return b._dragend()})}return __extends(b,a),b.prototype.dragstart=function(a){return void 0===a?this.ondragstart:(this.ondragstart=a,this)},b.prototype.drag=function(a){return void 0===a?this.ondrag:(this.ondrag=a,this)},b.prototype.dragend=function(a){return void 0===a?this.ondragend:(this.ondragend=a,this)},b.prototype._dragstart=function(){var a=this._componentToListenTo.width(),b=this._componentToListenTo.height(),c=function(a,b){return function(c){return Math.min(Math.max(c,a),b)}};this.constrainX=c(0,a),this.constrainY=c(0,b)},b.prototype._doDragstart=function(){null!=this.ondragstart&&this.ondragstart({x:this._origin[0],y:this._origin[1]}) -},b.prototype._drag=function(){this.dragInitialized||(this._origin=[d3.event.x,d3.event.y],this.dragInitialized=!0,this._doDragstart()),this._location=[this.constrainX(d3.event.x),this.constrainY(d3.event.y)],this._doDrag()},b.prototype._doDrag=function(){if(null!=this.ondrag){var a={x:this._origin[0],y:this._origin[1]},b={x:this._location[0],y:this._location[1]};this.ondrag(a,b)}},b.prototype._dragend=function(){this.dragInitialized&&(this.dragInitialized=!1,this._doDragend())},b.prototype._doDragend=function(){if(null!=this.ondragend){var a={x:this._origin[0],y:this._origin[1]},b={x:this._location[0],y:this._location[1]};this.ondragend(a,b)}},b.prototype._anchor=function(b,c){return a.prototype._anchor.call(this,b,c),c.call(this.dragBehavior),this},b.prototype.setupZoomCallback=function(a,b){function c(c,g){return null==c||null==g?(f&&(null!=a&&a.domain(d),null!=b&&b.domain(e)),void(f=!f)):(f=!1,null!=a&&a.domain([a.invert(c.x),a.invert(g.x)]),null!=b&&b.domain([b.invert(g.y),b.invert(c.y)]),void this.clearBox())}var d=null!=a?a.domain():null,e=null!=b?b.domain():null,f=!1;return this.drag(c),this.dragend(c),this},b}(a.AbstractInteraction);a.Drag=b}(a.Interaction||(a.Interaction={}));a.Interaction}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(a){var b=function(a){function b(){a.apply(this,arguments),this.boxIsDrawn=!1}return __extends(b,a),b.prototype._dragstart=function(){a.prototype._dragstart.call(this),this.clearBox()},b.prototype.clearBox=function(){return null!=this.dragBox?(this.dragBox.attr("height",0).attr("width",0),this.boxIsDrawn=!1,this):void 0},b.prototype.setBox=function(a,b,c,d){if(null!=this.dragBox){var e=Math.abs(a-b),f=Math.abs(c-d),g=Math.min(a,b),h=Math.min(c,d);return this.dragBox.attr({x:g,y:h,width:e,height:f}),this.boxIsDrawn=e>0&&f>0,this}},b.prototype._anchor=function(c,d){a.prototype._anchor.call(this,c,d);var e=b.CLASS_DRAG_BOX,f=this._componentToListenTo._backgroundContainer;return this.dragBox=f.append("rect").classed(e,!0).attr("x",0).attr("y",0),this},b.CLASS_DRAG_BOX="drag-box",b}(a.Drag);a.DragBox=b}(a.Interaction||(a.Interaction={}));a.Interaction}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(a){var b=function(a){function b(){a.apply(this,arguments)}return __extends(b,a),b.prototype._drag=function(){a.prototype._drag.call(this),this.setBox(this._origin[0],this._location[0])},b.prototype.setBox=function(b,c){return a.prototype.setBox.call(this,b,c,0,this._componentToListenTo.height()),this},b}(a.DragBox);a.XDragBox=b}(a.Interaction||(a.Interaction={}));a.Interaction}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(a){var b=function(a){function b(){a.apply(this,arguments)}return __extends(b,a),b.prototype._drag=function(){a.prototype._drag.call(this),this.setBox(this._origin[0],this._location[0],this._origin[1],this._location[1])},b}(a.DragBox);a.XYDragBox=b}(a.Interaction||(a.Interaction={}));a.Interaction}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(a){var b=function(a){function b(){a.apply(this,arguments)}return __extends(b,a),b.prototype._drag=function(){a.prototype._drag.call(this),this.setBox(this._origin[1],this._location[1])},b.prototype.setBox=function(b,c){return a.prototype.setBox.call(this,0,this._componentToListenTo.width(),b,c),this},b}(a.DragBox);a.YDragBox=b}(a.Interaction||(a.Interaction={}));a.Interaction}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(a){function b(b){a.call(this),this._event2Callback={},this.connected=!1,this._target=b}return __extends(b,a),b.prototype.target=function(a){if(null==a)return this._target;var b=this.connected;return this.disconnect(),this._target=a,b&&this.connect(),this},b.prototype.getEventString=function(a){return a+".dispatcher"+this._plottableID},b.prototype.connect=function(){var a=this;if(this.connected)throw new Error("Can't connect dispatcher twice!");return this.connected=!0,Object.keys(this._event2Callback).forEach(function(b){var c=a._event2Callback[b];a._target.on(a.getEventString(b),c)}),this},b.prototype.disconnect=function(){var a=this;return this.connected=!1,Object.keys(this._event2Callback).forEach(function(b){a._target.on(a.getEventString(b),null)}),this},b}(a.Core.PlottableObject);b.AbstractDispatcher=c}(a.Dispatcher||(a.Dispatcher={}));a.Dispatcher}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(a){var b=function(a){function b(b){var c=this;a.call(this,b),this._event2Callback.mouseover=function(){null!=c._mouseover&&c._mouseover(c.getMousePosition())},this._event2Callback.mousemove=function(){null!=c._mousemove&&c._mousemove(c.getMousePosition())},this._event2Callback.mouseout=function(){null!=c._mouseout&&c._mouseout(c.getMousePosition())}}return __extends(b,a),b.prototype.getMousePosition=function(){var a=d3.mouse(this._target.node());return{x:a[0],y:a[1]}},b.prototype.mouseover=function(a){return void 0===a?this._mouseover:(this._mouseover=a,this)},b.prototype.mousemove=function(a){return void 0===a?this._mousemove:(this._mousemove=a,this)},b.prototype.mouseout=function(a){return void 0===a?this._mouseout:(this._mouseout=a,this)},b}(a.AbstractDispatcher);a.Mouse=b}(a.Dispatcher||(a.Dispatcher={}));a.Dispatcher}(Plottable||(Plottable={})); \ No newline at end of file +var Plottable;!function(a){!function(a){!function(a){function b(a,b,c){return Math.min(b,c)<=a&&a<=Math.max(b,c)}function c(a){null!=window.console&&(null!=window.console.warn?console.warn(a):null!=window.console.log&&console.log(a))}function d(a,b){if(a.length!==b.length)throw new Error("attempted to add arrays of unequal length");return a.map(function(c,d){return a[d]+b[d]})}function e(a,b){var c=d3.set();return a.forEach(function(a){b.has(a)&&c.add(a)}),c}function f(a){return"function"==typeof a?a:"string"==typeof a&&"#"!==a[0]?function(b){return b[a]}:function(){return a}}function g(a,b){var c=f(a);return function(a,d){var e=b.datasets(),f=e.length>0?e[0]:null,g=f?f.metadata():null;return c(a,d,g)}}function h(a,b){var c=d3.set();return a.forEach(function(a){return c.add(a)}),b.forEach(function(a){return c.add(a)}),c}function i(a,b){var c=d3.map();return a.forEach(function(a,d){c.set(a,b(a,d))}),c}function j(a){var b=d3.set(),c=[];return a.forEach(function(a){b.has(a)||(b.add(a),c.push(a))}),c}function k(a,b){for(var c=[],d=0;b>d;d++)c[d]="function"==typeof a?a(d):a;return c}function l(a){return Array.prototype.concat.apply([],a)}function m(a,b){if(null==a||null==b)return a===b;if(a.length!==b.length)return!1;for(var c=0;cf;++f)e[f]=a+c*f;return e}function s(a,b){for(var c=[],d=2;dd;){var f=d+e>>>1,g=null==c?b[f]:c(b[f]);a>g?d=f+1:e=f}return d}a.sortedIndex=b}(a.OpenSource||(a.OpenSource={}));a.OpenSource}(a._Util||(a._Util={}));a._Util}(Plottable||(Plottable={}));var Plottable;!function(a){!function(a){var b=function(){function a(){this.counter={}}return a.prototype.setDefault=function(a){null==this.counter[a]&&(this.counter[a]=0)},a.prototype.increment=function(a){return this.setDefault(a),++this.counter[a]},a.prototype.decrement=function(a){return this.setDefault(a),--this.counter[a]},a.prototype.get=function(a){return this.setDefault(a),this.counter[a]},a}();a.IDCounter=b}(a._Util||(a._Util={}));a._Util}(Plottable||(Plottable={}));var Plottable;!function(a){!function(a){var b=function(){function a(){this.keyValuePairs=[]}return a.prototype.set=function(a,b){if(a!==a)throw new Error("NaN may not be used as a key to the StrictEqualityAssociativeArray");for(var c=0;cb){var h=e("."),i=Math.floor(b/h);return"...".substr(0,i)}for(;f+g>b;)d=d.substr(0,d.length-1).trim(),f=e(d);if(e(d+"...")>b)throw new Error("addEllipsesToLine failed :(");return d+"..."}function i(b,c,d,e,f,g){void 0===f&&(f="left"),void 0===g&&(g="top");var h={left:0,center:.5,right:1},i={top:0,center:.5,bottom:1};if(void 0===h[f]||void 0===i[g])throw new Error("unrecognized alignment x:"+f+", y:"+g);var j=c.append("g"),k=j.append("text");k.text(b);var l=a.DOM.getBBox(k),m=l.height,n=l.width;if(n>d||m>e)return a.Methods.warn("Insufficient space to fit text: "+b),k.text(""),{width:0,height:0};var o={left:"start",center:"middle",right:"end"},p=o[f],q=d*h[f],r=e*i[g],s=.85-i[g];return k.attr("text-anchor",p).attr("y",s+"em"),a.DOM.translate(j,q,r),{width:n,height:m}}function j(a,b,c,d,e,f,g){if(void 0===e&&(e="left"),void 0===f&&(f="top"),void 0===g&&(g="right"),"right"!==g&&"left"!==g)throw new Error("unrecognized rotation: "+g);var h="right"===g,j={left:"bottom",right:"top",center:"center",top:"left",bottom:"right"},k={left:"top",right:"bottom",center:"center",top:"right",bottom:"left"},l=h?j:k,m=b.append("g"),n=i(a,m,d,c,l[f],l[e]),o=d3.transform("");return o.rotate="right"===g?90:-90,o.translate=[h?c:0,h?0:d],m.attr("transform",o.toString()),m.classed("rotated-"+g,!0),{width:n.height,height:n.width}}function k(d,e,f,g,h,j){void 0===h&&(h="left"),void 0===j&&(j="top");var k=c(e.append("text"))(b.HEIGHT_TEXT).height,l=0,m=e.append("g");d.forEach(function(b,c){var d=m.append("g");a.DOM.translate(d,0,c*k);var e=i(b,d,f,k,h,j);e.width>l&&(l=e.width)});var n=k*d.length,o=g-n,p={center:.5,top:0,bottom:1};return a.DOM.translate(m,0,o*p[j]),{width:l,height:n}}function l(d,e,f,g,h,i,k){void 0===h&&(h="left"),void 0===i&&(i="top"),void 0===k&&(k="left");var l=c(e.append("text"))(b.HEIGHT_TEXT).height,m=0,n=e.append("g");d.forEach(function(b,c){var d=n.append("g");a.DOM.translate(d,c*l,0);var e=j(b,d,l,g,h,i,k);e.height>m&&(m=e.height)});var o=l*d.length,p=f-o,q={center:.5,left:0,right:1};return a.DOM.translate(n,p*q[h],0),{width:o,height:m}}function m(b,c,d,e,f,g){if(void 0===f&&(f="horizontal"),-1===["left","right","horizontal"].indexOf(f))throw new Error("Unrecognized orientation to writeText: "+f);var h="horizontal"===f,i=h?c:d,j=h?d:c,m=a.WordWrap.breakTextToFitRect(b,i,j,e);if(0===m.lines.length)return{textFits:m.textFits,usedWidth:0,usedHeight:0};var n,o;if(null==g){var p=h?a.Methods.max:d3.sum,q=h?d3.sum:a.Methods.max,r=function(a){return h?e(a).height:e(a).width},s=function(a){return h?e(a).width:e(a).height};n=p(m.lines,s,0),o=q(m.lines,r,0)}else{var t=g.g.append("g").classed("writeText-inner-g",!0),u=h?k:l,v=u.call(this,m.lines,t,c,d,g.xAlign,g.yAlign,f);n=v.width,o=v.height}return{textFits:m.textFits,usedWidth:n,usedHeight:o}}b.HEIGHT_TEXT="bqpdl",b.getTextMeasurer=c;var n="a",o=function(){function b(b){var g=this;this.cache=new a.Cache(c(b),n,a.Methods.objEq),this.measure=d(e(f(function(a){return g.cache.get(a)})))}return b.prototype.clear=function(){return this.cache.clear(),this},b}();b.CachingCharacterMeasurer=o,b.getTruncatedText=g,b.addEllipsesToLine=h,b.writeLineHorizontally=i,b.writeLineVertically=j,b.writeText=m}(a.Text||(a.Text={}));a.Text}(a._Util||(a._Util={}));a._Util}(Plottable||(Plottable={}));var Plottable;!function(a){!function(a){!function(b){function c(b,c,e,f){var g=function(a){return f(a).width},h=d(b,c,g),i=f("hello world").height,j=Math.floor(e/i),k=j>=h.length;return k||(h=h.splice(0,j),j>0&&(h[j-1]=a.Text.addEllipsesToLine(h[j-1],c,f))),{originalText:b,lines:h,textFits:k}}function d(a,b,c){for(var d=[],e=a.split("\n"),g=0,h=e.length;h>g;g++){var i=e[g];null!==i?d=d.concat(f(i,b,c)):d.push("")}return d}function e(b,c,d){var e=h(b),f=e.map(d),g=a.Methods.max(f,0);return c>=g}function f(a,b,c){for(var d,e=[],f=h(a),i="",j=0;d||je;e++){var g=a[e];""===c||j(c[0],g,d)?c+=g:(b.push(c),c=g),d=g}return c&&b.push(c),b}function i(a){return null==a?!0:""===a.trim()}function j(a,b,c){return m.test(a)&&m.test(b)?!0:m.test(a)||m.test(b)?!1:l.test(c)||k.test(b)?!1:!0}var k=/[{\[]/,l=/[!"%),-.:;?\]}]/,m=/^\s+$/;b.breakTextToFitRect=c,b.canWrapWithoutBreakingWords=e}(a.WordWrap||(a.WordWrap={}));a.WordWrap}(a._Util||(a._Util={}));a._Util}(Plottable||(Plottable={}));var Plottable;!function(a){!function(a){!function(a){function b(a){var b;try{b=a.node().getBBox()}catch(c){b={x:0,y:0,width:0,height:0}}return b}function c(b){null!=window.requestAnimationFrame?window.requestAnimationFrame(b):setTimeout(b,a.POLYFILL_TIMEOUT_MSEC)}function d(a,b){var c=a.getPropertyValue(b),d=parseFloat(c);return d!==d?0:d}function e(a){for(var b=a.node();null!==b&&"svg"!==b.nodeName;)b=b.parentNode;return null==b}function f(a){var b=window.getComputedStyle(a);return d(b,"width")+d(b,"padding-left")+d(b,"padding-right")+d(b,"border-left-width")+d(b,"border-right-width")}function g(a){var b=window.getComputedStyle(a);return d(b,"height")+d(b,"padding-top")+d(b,"padding-bottom")+d(b,"border-top-width")+d(b,"border-bottom-width")}function h(a){var b=a.node().clientWidth;if(0===b){var c=a.attr("width");if(-1!==c.indexOf("%")){for(var d=a.node().parentNode;null!=d&&0===d.clientWidth;)d=d.parentNode;if(null==d)throw new Error("Could not compute width of element");b=d.clientWidth*parseFloat(c)/100}else b=parseFloat(c)}return b}function i(a,b,c){var d=d3.transform(a.attr("transform"));return null==b?d.translate:(c=null==c?0:c,d.translate[0]=b,d.translate[1]=c,a.attr("transform",d.toString()),a)}function j(a,b){return a.rightb.right?!1:a.bottomb.bottom?!1:!0}a.getBBox=b,a.POLYFILL_TIMEOUT_MSEC=1e3/60,a.requestAnimationFramePolyfill=c,a.isSelectionRemovedFromSVG=e,a.getElementWidth=f,a.getElementHeight=g,a.getSVGPixelWidth=h,a.translate=i,a.boxesOverlap=j}(a.DOM||(a.DOM={}));a.DOM}(a._Util||(a._Util={}));a._Util}(Plottable||(Plottable={}));var Plottable;!function(a){!function(a){!function(a){function b(a){var b=d3.rgb(a),c=function(a){return a/=255,.03928>=a?a/12.92:Math.pow((a+.055)/1.055,2.4)},d=c(b.r),e=c(b.g),f=c(b.b);return.2126*d+.7152*e+.0722*f}function c(a,c){var d=b(a)+.05,e=b(c)+.05;return d>e?d/e:e/d}a.contrast=c}(a.Color||(a.Color={}));a.Color}(a._Util||(a._Util={}));a._Util}(Plottable||(Plottable={}));var Plottable;!function(a){a.MILLISECONDS_IN_ONE_DAY=864e5,function(b){function c(a,c,d,e){void 0===a&&(a=2),void 0===c&&(c="$"),void 0===d&&(d=!0),void 0===e&&(e=!0);var f=b.fixed(a);return function(a){var b=f(Math.abs(a));return e&&l(Math.abs(a),b)?"":(""!==b&&(d?b=c+b:b+=c,0>a&&(b="-"+b)),b)}}function d(a,b){return void 0===a&&(a=3),void 0===b&&(b=!0),k(a),function(c){var d=c.toFixed(a);return b&&l(c,d)?"":d}}function e(a,b){return void 0===a&&(a=3),void 0===b&&(b=!0),k(a),function(c){if("number"==typeof c){var d=Math.pow(10,a),e=String(Math.round(c*d)/d);return b&&l(c,e)?"":e}return String(c)}}function f(){return function(a){return String(a)}}function g(a,c){void 0===a&&(a=0),void 0===c&&(c=!0);var d=b.fixed(a,c);return function(a){var b=100*a,e=a.toString(),f=Math.pow(10,e.length-(e.indexOf(".")+1));b=parseInt((b*f).toString(),10)/f;var g=d(b);return c&&l(b,g)?"":(""!==g&&(g+="%"),g)}}function h(a){return void 0===a&&(a=3),k(a),function(b){return d3.format("."+a+"s")(b)}}function i(){var a=8,b={};return b[0]={format:".%L",filter:function(a){return 0!==a.getMilliseconds()}},b[1]={format:":%S",filter:function(a){return 0!==a.getSeconds()}},b[2]={format:"%I:%M",filter:function(a){return 0!==a.getMinutes()}},b[3]={format:"%I %p",filter:function(a){return 0!==a.getHours()}},b[4]={format:"%a %d",filter:function(a){return 0!==a.getDay()&&1!==a.getDate()}},b[5]={format:"%b %d",filter:function(a){return 1!==a.getDate()}},b[6]={format:"%b",filter:function(a){return 0!==a.getMonth()}},b[7]={format:"%Y",filter:function(){return!0}},function(c){for(var d=0;a>d;d++)if(b[d].filter(c))return d3.time.format(b[d].format)(c)}}function j(b,c,d){return void 0===b&&(b=0),void 0===c&&(c=a.MILLISECONDS_IN_ONE_DAY),void 0===d&&(d=""),function(a){var e=Math.round((a.valueOf()-b)/c);return e.toString()+d}}function k(a){if(0>a||a>20)throw new RangeError("Formatter precision must be between 0 and 20")}function l(a,b){return a!==parseFloat(b)}b.currency=c,b.fixed=d,b.general=e,b.identity=f,b.percentage=g,b.siSuffix=h,b.time=i,b.relativeDate=j}(a.Formatters||(a.Formatters={}));a.Formatters}(Plottable||(Plottable={}));var Plottable;!function(a){a.version="0.34.0"}(Plottable||(Plottable={}));var Plottable;!function(a){!function(a){var b=function(){function a(){}return a.CORAL_RED="#fd373e",a.INDIGO="#5279c7",a.ROBINS_EGG_BLUE="#06cccc",a.FERN="#63c261",a.BURNING_ORANGE="#ff7939",a.ROYAL_HEATH="#962565",a.CONIFER="#99ce50",a.CERISE_RED="#db2e65",a.BRIGHT_SUN="#fad419",a.JACARTA="#2c2b6f",a.PLOTTABLE_COLORS=[a.INDIGO,a.CORAL_RED,a.FERN,a.BRIGHT_SUN,a.JACARTA,a.BURNING_ORANGE,a.CERISE_RED,a.CONIFER,a.ROYAL_HEATH,a.ROBINS_EGG_BLUE],a}();a.Colors=b}(a.Core||(a.Core={}));a.Core}(Plottable||(Plottable={}));var Plottable;!function(a){!function(a){var b=function(){function a(){this._plottableID=a.nextID++}return a.nextID=0,a}();a.PlottableObject=b}(a.Core||(a.Core={}));a.Core}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(c){b.call(this),this.key2callback=new a._Util.StrictEqualityAssociativeArray,this.listenable=c}return __extends(c,b),c.prototype.registerListener=function(a,b){return this.key2callback.set(a,b),this},c.prototype.broadcast=function(){for(var a=this,b=[],c=0;c0){var f=d.valueOf();return d instanceof Date?[f-b.ONE_DAY,f+b.ONE_DAY]:[f-b.PADDING_FOR_IDENTICAL_DOMAIN,f+b.PADDING_FOR_IDENTICAL_DOMAIN]}if(a.domain()[0]===a.domain()[1])return c;var g=this.padProportion/2,h=a.invert(a.scale(d)-(a.scale(e)-a.scale(d))*g),i=a.invert(a.scale(e)+(a.scale(e)-a.scale(d))*g),j=this.paddingExceptions.values().concat(this.unregisteredPaddingExceptions.values()),k=d3.set(j);return k.has(d)&&(h=d),k.has(e)&&(i=e),[h,i]},b.prototype.niceDomain=function(a,b){return this.doNice?a._niceDomain(b,this.niceCount):b},b.prototype.includeDomain=function(a){var b=this.includedValues.values().concat(this.unregisteredIncludedValues.values());return b.reduce(function(a,b){return[Math.min(a[0],b),Math.max(a[1],b)]},a)},b.PADDING_FOR_IDENTICAL_DOMAIN=1,b.ONE_DAY=864e5,b}();a.Domainer=b}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(c){b.call(this),this._autoDomainAutomatically=!0,this.broadcaster=new a.Core.Broadcaster(this),this._rendererAttrID2Extent={},this._typeCoercer=function(a){return a},this._d3Scale=c}return __extends(c,b),c.prototype._getAllExtents=function(){return d3.values(this._rendererAttrID2Extent)},c.prototype._getExtent=function(){return[]},c.prototype.autoDomain=function(){return this._autoDomainAutomatically=!0,this._setDomain(this._getExtent()),this},c.prototype._autoDomainIfAutomaticMode=function(){this._autoDomainAutomatically&&this.autoDomain()},c.prototype.scale=function(a){return this._d3Scale(a)},c.prototype.domain=function(a){return null==a?this._getDomain():(this._autoDomainAutomatically=!1,this._setDomain(a),this)},c.prototype._getDomain=function(){return this._d3Scale.domain()},c.prototype._setDomain=function(a){this._d3Scale.domain(a),this.broadcaster.broadcast()},c.prototype.range=function(a){return null==a?this._d3Scale.range():(this._d3Scale.range(a),this)},c.prototype.copy=function(){return new c(this._d3Scale.copy())},c.prototype._updateExtent=function(a,b,c){return this._rendererAttrID2Extent[a+b]=c,this._autoDomainIfAutomaticMode(),this},c.prototype._removeExtent=function(a,b){return delete this._rendererAttrID2Extent[a+b],this._autoDomainIfAutomaticMode(),this},c}(a.Core.PlottableObject);b.AbstractScale=c}(a.Scale||(a.Scale={}));a.Scale}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(c){b.call(this,c),this._numTicks=10,this._PADDING_FOR_IDENTICAL_DOMAIN=1,this._userSetDomainer=!1,this._domainer=new a.Domainer,this._typeCoercer=function(a){return+a},this._tickGenerator=function(a){return a.getDefaultTicks()}}return __extends(c,b),c.prototype._getExtent=function(){return this._domainer.computeDomain(this._getAllExtents(),this)},c.prototype.invert=function(a){return this._d3Scale.invert(a)},c.prototype.copy=function(){return new c(this._d3Scale.copy())},c.prototype.domain=function(a){return b.prototype.domain.call(this,a)},c.prototype._setDomain=function(c){var d=function(a){return a!==a||1/0===a||a===-1/0};return d(c[0])||d(c[1])?void a._Util.Methods.warn("Warning: QuantitativeScales cannot take NaN or Infinity as a domain value. Ignoring."):void b.prototype._setDomain.call(this,c)},c.prototype.interpolate=function(a){return null==a?this._d3Scale.interpolate():(this._d3Scale.interpolate(a),this)},c.prototype.rangeRound=function(a){return this._d3Scale.rangeRound(a),this},c.prototype.getDefaultTicks=function(){return this._d3Scale.ticks(this.numTicks())},c.prototype.clamp=function(a){return null==a?this._d3Scale.clamp():(this._d3Scale.clamp(a),this)},c.prototype.ticks=function(){return this._tickGenerator(this)},c.prototype.numTicks=function(a){return null==a?this._numTicks:(this._numTicks=a,this)},c.prototype._niceDomain=function(a,b){return this._d3Scale.copy().domain(a).nice(b).domain()},c.prototype.domainer=function(a){return null==a?this._domainer:(this._domainer=a,this._userSetDomainer=!0,this._autoDomainIfAutomaticMode(),this)},c.prototype._defaultExtent=function(){return[0,1]},c.prototype.tickGenerator=function(a){return null==a?this._tickGenerator:(this._tickGenerator=a,this)},c}(b.AbstractScale);b.AbstractQuantitative=c}(a.Scale||(a.Scale={}));a.Scale}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(a){var b=function(a){function b(b){a.call(this,null==b?d3.scale.linear():b)}return __extends(b,a),b.prototype.copy=function(){return new b(this._d3Scale.copy())},b}(a.AbstractQuantitative);a.Linear=b}(a.Scale||(a.Scale={}));a.Scale}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(d){b.call(this,null==d?d3.scale.log():d),c.warned||(c.warned=!0,a._Util.Methods.warn("Plottable.Scale.Log is deprecated. If possible, use Plottable.Scale.ModifiedLog instead."))}return __extends(c,b),c.prototype.copy=function(){return new c(this._d3Scale.copy())},c.prototype._defaultExtent=function(){return[1,10]},c.warned=!1,c}(b.AbstractQuantitative);b.Log=c}(a.Scale||(a.Scale={}));a.Scale}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(a){if(void 0===a&&(a=10),b.call(this,d3.scale.linear()),this._showIntermediateTicks=!1,this.base=a,this.pivot=this.base,this.untransformedDomain=this._defaultExtent(),this._numTicks=10,1>=a)throw new Error("ModifiedLogScale: The base must be > 1")}return __extends(c,b),c.prototype.adjustedLog=function(a){var b=0>a?-1:1;return a*=b,aa?-1:1;return a*=b,a=Math.pow(this.base,a),a=d&&e>=a}),m=j.concat(l).concat(k);return m.length<=1&&(m=d3.scale.linear().domain([d,e]).ticks(b)),m},c.prototype.logTicks=function(b,c){var d=this,e=this.howManyTicks(b,c);if(0===e)return[];var f=Math.floor(Math.log(b)/Math.log(this.base)),g=Math.ceil(Math.log(c)/Math.log(this.base)),h=d3.range(g,f,-Math.ceil((g-f)/e)),i=this._showIntermediateTicks?Math.floor(e/h.length):1,j=d3.range(this.base,1,-(this.base-1)/i).map(Math.floor),k=a._Util.Methods.uniq(j),l=h.map(function(a){return k.map(function(b){return Math.pow(d.base,a-1)*b})}),m=a._Util.Methods.flatten(l),n=m.filter(function(a){return a>=b&&c>=a}),o=n.sort(function(a,b){return a-b});return o},c.prototype.howManyTicks=function(b,c){var d=this.adjustedLog(a._Util.Methods.min(this.untransformedDomain,0)),e=this.adjustedLog(a._Util.Methods.max(this.untransformedDomain,0)),f=this.adjustedLog(b),g=this.adjustedLog(c),h=(g-f)/(e-d),i=Math.ceil(h*this._numTicks);return i},c.prototype.copy=function(){return new c(this.base)},c.prototype._niceDomain=function(a){return a},c.prototype.showIntermediateTicks=function(a){return null==a?this._showIntermediateTicks:void(this._showIntermediateTicks=a)},c}(b.AbstractQuantitative);b.ModifiedLog=c}(a.Scale||(a.Scale={}));a.Scale}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(a){if(b.call(this,null==a?d3.scale.ordinal():a),this._range=[0,1],this._rangeType="bands",this._innerPadding=.3,this._outerPadding=.5,this._typeCoercer=function(a){return null!=a&&a.toString?a.toString():a},this._innerPadding>this._outerPadding)throw new Error("outerPadding must be >= innerPadding so cat axis bands work out reasonably")}return __extends(c,b),c.prototype._getExtent=function(){var b=this._getAllExtents();return a._Util.Methods.uniq(a._Util.Methods.flatten(b))},c.prototype.domain=function(a){return b.prototype.domain.call(this,a)},c.prototype._setDomain=function(a){b.prototype._setDomain.call(this,a),this.range(this.range())},c.prototype.range=function(a){return null==a?this._range:(this._range=a,"points"===this._rangeType?this._d3Scale.rangePoints(a,2*this._outerPadding):"bands"===this._rangeType&&this._d3Scale.rangeBands(a,this._innerPadding,this._outerPadding),this)},c.prototype.rangeBand=function(){return this._d3Scale.rangeBand()},c.prototype.innerPadding=function(){var a=this.domain();if(a.length<2)return 0;var b=Math.abs(this.scale(a[1])-this.scale(a[0]));return b-this.rangeBand()},c.prototype.fullBandStartAndWidth=function(a){var b=this.scale(a)-this.innerPadding()/2,c=this.rangeBand()+this.innerPadding();return[b,c]},c.prototype.rangeType=function(a,b,c){if(null==a)return this._rangeType;if("points"!==a&&"bands"!==a)throw new Error("Unsupported range type: "+a); +return this._rangeType=a,null!=b&&(this._outerPadding=b),null!=c&&(this._innerPadding=c),this.range(this.range()),this.broadcaster.broadcast(),this},c.prototype.copy=function(){return new c(this._d3Scale.copy())},c}(b.AbstractScale);b.Ordinal=c}(a.Scale||(a.Scale={}));a.Scale}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(c){var d;switch(c){case null:case void 0:d=d3.scale.ordinal().range(a.Core.Colors.PLOTTABLE_COLORS);break;case"Category10":case"category10":case"10":d=d3.scale.category10();break;case"Category20":case"category20":case"20":d=d3.scale.category20();break;case"Category20b":case"category20b":case"20b":d=d3.scale.category20b();break;case"Category20c":case"category20c":case"20c":d=d3.scale.category20c();break;default:throw new Error("Unsupported ColorScale type")}b.call(this,d)}return __extends(c,b),c.prototype._getExtent=function(){var b=this._getAllExtents(),c=[];return b.forEach(function(a){c=c.concat(a)}),a._Util.Methods.uniq(c)},c}(b.AbstractScale);b.Color=c}(a.Scale||(a.Scale={}));a.Scale}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(a){b.call(this,null==a?d3.time.scale():a),this._typeCoercer=function(a){return a&&a._isAMomentObject||a instanceof Date?a:new Date(a)}}return __extends(c,b),c.prototype._tickInterval=function(a,b){var c=d3.time.scale();return c.domain(this.domain()),c.range(this.range()),c.ticks(a.range,b)},c.prototype._setDomain=function(a){return a=a.map(this._typeCoercer),b.prototype._setDomain.call(this,a)},c.prototype.copy=function(){return new c(this._d3Scale.copy())},c.prototype._defaultExtent=function(){var b=(new Date).valueOf(),c=b-a.MILLISECONDS_IN_ONE_DAY;return[c,b]},c}(b.AbstractQuantitative);b.Time=c}(a.Scale||(a.Scale={}));a.Scale}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(a,d){void 0===a&&(a="reds"),void 0===d&&(d="linear"),this._colorRange=this._resolveColorValues(a),this._scaleType=d,b.call(this,c.getD3InterpolatedScale(this._colorRange,this._scaleType))}return __extends(c,b),c.getD3InterpolatedScale=function(a,b){var d;switch(b){case"linear":d=d3.scale.linear();break;case"log":d=d3.scale.log();break;case"sqrt":d=d3.scale.sqrt();break;case"pow":d=d3.scale.pow()}if(null==d)throw new Error("unknown Quantitative scale type "+b);return d.range([0,1]).interpolate(c.interpolateColors(a))},c.interpolateColors=function(a){if(a.length<2)throw new Error("Color scale arrays must have at least two elements.");return function(){return function(b){b=Math.max(0,Math.min(1,b));var c=b*(a.length-1),d=Math.floor(c),e=Math.ceil(c),f=c-d;return d3.interpolateLab(a[d],a[e])(f)}}},c.prototype.colorRange=function(a){return null==a?this._colorRange:(this._colorRange=this._resolveColorValues(a),this._resetScale(),this)},c.prototype.scaleType=function(a){return null==a?this._scaleType:(this._scaleType=a,this._resetScale(),this)},c.prototype._resetScale=function(){this._d3Scale=c.getD3InterpolatedScale(this._colorRange,this._scaleType),this._autoDomainIfAutomaticMode(),this.broadcaster.broadcast()},c.prototype._resolveColorValues=function(a){return a instanceof Array?a:null!=c.COLOR_SCALES[a]?c.COLOR_SCALES[a]:c.COLOR_SCALES.reds},c.prototype.autoDomain=function(){var b=this._getAllExtents();return b.length>0&&this._setDomain([a._Util.Methods.min(b,function(a){return a[0]},0),a._Util.Methods.max(b,function(a){return a[1]},0)]),this},c.COLOR_SCALES={reds:["#FFFFFF","#FFF6E1","#FEF4C0","#FED976","#FEB24C","#FD8D3C","#FC4E2A","#E31A1C","#B10026"],blues:["#FFFFFF","#CCFFFF","#A5FFFD","#85F7FB","#6ED3EF","#55A7E0","#417FD0","#2545D3","#0B02E1"],posneg:["#0B02E1","#2545D3","#417FD0","#55A7E0","#6ED3EF","#85F7FB","#A5FFFD","#CCFFFF","#FFFFFF","#FFF6E1","#FEF4C0","#FED976","#FEB24C","#FD8D3C","#FC4E2A","#E31A1C","#B10026"]},c}(b.AbstractScale);b.InterpolatedColor=c}(a.Scale||(a.Scale={}));a.Scale}(Plottable||(Plottable={}));var Plottable;!function(a){!function(a){var b=function(){function a(a){var b=this;if(this.rescaleInProgress=!1,null==a)throw new Error("ScaleDomainCoordinator requires scales to coordinate");this.scales=a,this.scales.forEach(function(a){return a.broadcaster.registerListener(b,function(a){return b.rescale(a)})})}return a.prototype.rescale=function(a){if(!this.rescaleInProgress){this.rescaleInProgress=!0;var b=a.domain();this.scales.forEach(function(a){return a.domain(b)}),this.rescaleInProgress=!1}},a}();a.ScaleDomainCoordinator=b}(a._Util||(a._Util={}));a._Util}(Plottable||(Plottable={}));var Plottable;!function(a){!function(b){!function(b){function c(b){if(0>=b)throw new Error("interval must be positive number");return function(c){var d=c.domain(),e=Math.min(d[0],d[1]),f=Math.max(d[0],d[1]),g=Math.ceil(e/b)*b,h=Math.floor((f-g)/b)+1,i=e%b===0?[]:[e],j=a._Util.Methods.range(0,h).map(function(a){return g+a*b}),k=f%b===0?[]:[f];return i.concat(j).concat(k)}}b.intervalTickGenerator=c}(b.TickGenerators||(b.TickGenerators={}));b.TickGenerators}(a.Scale||(a.Scale={}));a.Scale}(Plottable||(Plottable={}));var Plottable;!function(a){!function(b){var c=function(){function b(a){this.key=a}return b.prototype.setClass=function(a){return this._className=a,this},b.prototype.setup=function(a){this._renderArea=a},b.prototype.remove=function(){null!=this._renderArea&&this._renderArea.remove()},b.prototype._enterData=function(){},b.prototype._drawStep=function(){},b.prototype._numberOfAnimationIterations=function(a){return a.length},b.prototype.draw=function(b,c){var d=this;this._enterData(b);var e=this._numberOfAnimationIterations(b),f=0;return c.forEach(function(b){a._Util.Methods.setTimeout(function(){return d._drawStep(b)},f),f+=b.animator.getTiming(e)}),f},b}();b.AbstractDrawer=c}(a._Drawer||(a._Drawer={}));a._Drawer}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(){b.apply(this,arguments)}return __extends(c,b),c.prototype._enterData=function(a){b.prototype._enterData.call(this,a),this.pathSelection.datum(a)},c.prototype.setup=function(a){a.append("path").classed("line",!0),b.prototype.setup.call(this,a),this.pathSelection=this._renderArea.select(".line")},c.prototype.createLine=function(a,b,c){return c||(c=function(){return!0}),d3.svg.line().x(a).y(b).defined(c)},c.prototype._numberOfAnimationIterations=function(){return 1},c.prototype._drawStep=function(c){{var d=(b.prototype._drawStep.call(this,c),a._Util.Methods.copyMap(c.attrToProjector)),e=d.x,f=d.y;d.defined}delete d.x,delete d.y,d.d=this.createLine(e,f,d.defined),d.defined&&delete d.defined,d.fill&&this.pathSelection.attr("fill",d.fill),c.animator.animate(this.pathSelection,d)},c}(b.AbstractDrawer);b.Line=c}(a._Drawer||(a._Drawer={}));a._Drawer}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(c){function d(){c.apply(this,arguments),this._drawLine=!0}return __extends(d,c),d.prototype._enterData=function(a){this._drawLine?c.prototype._enterData.call(this,a):b.AbstractDrawer.prototype._enterData.call(this,a),this.areaSelection.datum(a)},d.prototype.drawLine=function(a){return this._drawLine=a,this},d.prototype.setup=function(a){a.append("path").classed("area",!0),this._drawLine?c.prototype.setup.call(this,a):b.AbstractDrawer.prototype.setup.call(this,a),this.areaSelection=this._renderArea.select(".area")},d.prototype.createArea=function(a,b,c,d){return d||(d=function(){return!0}),d3.svg.area().x(a).y0(b).y1(c).defined(d)},d.prototype._drawStep=function(d){this._drawLine?c.prototype._drawStep.call(this,d):b.AbstractDrawer.prototype._drawStep.call(this,d);{var e=a._Util.Methods.copyMap(d.attrToProjector),f=e.x,g=e.y0,h=e.y;e.defined}delete e.x,delete e.y0,delete e.y,e.d=this.createArea(f,g,h,e.defined),e.defined&&delete e.defined,e.fill&&this.areaSelection.attr("fill",e.fill),d.animator.animate(this.areaSelection,e)},d}(b.Line);b.Area=c}(a._Drawer||(a._Drawer={}));a._Drawer}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(a){var b=function(a){function b(){a.apply(this,arguments)}return __extends(b,a),b.prototype.svgElement=function(a){return this._svgElement=a,this},b.prototype._getDrawSelection=function(){return this._renderArea.selectAll(this._svgElement)},b.prototype._drawStep=function(b){a.prototype._drawStep.call(this,b);var c=this._getDrawSelection();b.attrToProjector.fill&&c.attr("fill",b.attrToProjector.fill),b.animator.animate(c,b.attrToProjector)},b.prototype._enterData=function(b){a.prototype._enterData.call(this,b);var c=this._getDrawSelection().data(b);c.enter().append(this._svgElement),null!=this._className&&c.classed(this._className,!0),c.exit().remove()},b}(a.AbstractDrawer);a.Element=b}(a._Drawer||(a._Drawer={}));a._Drawer}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=5,d=5,e=function(b){function e(a,c){b.call(this,a),this._someLabelsTooWide=!1,this.svgElement("rect"),this._isVertical=c}return __extends(e,b),e.prototype.setup=function(c){b.prototype.setup.call(this,c.append("g").classed("bar-area",!0)),this.textArea=c.append("g").classed("bar-label-text-area",!0),this.measurer=new a._Util.Text.CachingCharacterMeasurer(this.textArea.append("text")).measure},e.prototype.removeLabels=function(){this.textArea.selectAll("g").remove()},e.prototype.drawText=function(b,e){var f=this,g=b.map(function(b,g){var h=e.label(b,g).toString(),i=e.width(b,g),j=e.height(b,g),k=e.x(b,g),l=e.y(b,g),m=e.positive(b,g),n=f.measurer(h),o=e.fill(b,g),p=1.6*a._Util.Color.contrast("white",o)t;if(n.height<=j&&n.width<=i){var v=Math.min((q-r)/2,c);m||(v=-1*v),f._isVertical?l+=v:k+=v;var w=f.textArea.append("g").attr("transform","translate("+k+","+l+")"),x=p?"dark-label":"light-label";w.classed(x,!0);var y,z;f._isVertical?(y="center",z=m?"top":"bottom"):(y=m?"left":"right",z="center"),a._Util.Text.writeLineHorizontally(h,w,i,j,y,z)}return u});this._someLabelsTooWide=g.some(function(a){return a})},e}(b.Element);b.Rect=e}(a._Drawer||(a._Drawer={}));a._Drawer}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(a){b.call(this,a),this._svgElement="path"}return __extends(c,b),c.prototype.createArc=function(a,b){return d3.svg.arc().innerRadius(a).outerRadius(b)},c.prototype.retargetProjectors=function(a){var b={};return d3.entries(a).forEach(function(a){b[a.key]=function(b,c){return a.value(b.data,c)}}),b},c.prototype._drawStep=function(c){var d=a._Util.Methods.copyMap(c.attrToProjector);d=this.retargetProjectors(d);var e=d["inner-radius"],f=d["outer-radius"];return delete d["inner-radius"],delete d["outer-radius"],d.d=this.createArc(e,f),b.prototype._drawStep.call(this,{attrToProjector:d,animator:c.animator})},c.prototype.draw=function(a,c){var d=c[0].attrToProjector.value,e=d3.layout.pie().sort(null).value(d)(a);return c.forEach(function(a){return delete a.attrToProjector.value}),b.prototype.draw.call(this,e,c)},c}(b.Element);b.Arc=c}(a._Drawer||(a._Drawer={}));a._Drawer}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(){b.apply(this,arguments),this.clipPathEnabled=!1,this._xAlignProportion=0,this._yAlignProportion=0,this._fixedHeightFlag=!1,this._fixedWidthFlag=!1,this._isSetup=!1,this._isAnchored=!1,this.interactionsToRegister=[],this.boxes=[],this.isTopLevelComponent=!1,this._width=0,this._height=0,this._xOffset=0,this._yOffset=0,this.cssClasses=["component"],this.removed=!1}return __extends(c,b),c.prototype._anchor=function(a){if(this.removed)throw new Error("Can't reuse remove()-ed components!");"svg"===a.node().nodeName&&(this.rootSVG=a,this.rootSVG.classed("plottable",!0),this.rootSVG.style("overflow","visible"),this.isTopLevelComponent=!0),null!=this._element?a.node().appendChild(this._element.node()):(this._element=a.append("g"),this._setup()),this._isAnchored=!0},c.prototype._setup=function(){var a=this;this._isSetup||(this.cssClasses.forEach(function(b){a._element.classed(b,!0)}),this.cssClasses=null,this._backgroundContainer=this._element.append("g").classed("background-container",!0),this._content=this._element.append("g").classed("content",!0),this._foregroundContainer=this._element.append("g").classed("foreground-container",!0),this.boxContainer=this._element.append("g").classed("box-container",!0),this.clipPathEnabled&&this.generateClipPath(),this.addBox("bounding-box"),this.interactionsToRegister.forEach(function(b){return a.registerInteraction(b)}),this.interactionsToRegister=null,this.isTopLevelComponent&&this.autoResize(c.AUTORESIZE_BY_DEFAULT),this._isSetup=!0)},c.prototype._requestedSpace=function(){return{width:0,height:0,wantsWidth:!1,wantsHeight:!1}},c.prototype._computeLayout=function(b,c,d,e){var f=this;if(null==b||null==c||null==d||null==e){if(null==this._element)throw new Error("anchor must be called before computeLayout");if(!this.isTopLevelComponent)throw new Error("null arguments cannot be passed to _computeLayout() on a non-root node");b=0,c=0,null==this.rootSVG.attr("width")&&this.rootSVG.attr("width","100%"),null==this.rootSVG.attr("height")&&this.rootSVG.attr("height","100%");var g=this.rootSVG.node();d=a._Util.DOM.getElementWidth(g),e=a._Util.DOM.getElementHeight(g)}this.xOrigin=b,this.yOrigin=c;var h=this._requestedSpace(d,e);this._width=this._isFixedWidth()?Math.min(d,h.width):d,this._height=this._isFixedHeight()?Math.min(e,h.height):e;var i=this.xOrigin+this._xOffset,j=this.yOrigin+this._yOffset;i+=(d-this.width())*this._xAlignProportion,j+=(e-h.height)*this._yAlignProportion,this._element.attr("transform","translate("+i+","+j+")"),this.boxes.forEach(function(a){return a.attr("width",f.width()).attr("height",f.height())})},c.prototype._render=function(){this._isAnchored&&this._isSetup&&a.Core.RenderController.registerToRender(this)},c.prototype._scheduleComputeLayout=function(){this._isAnchored&&this._isSetup&&a.Core.RenderController.registerToComputeLayout(this)},c.prototype._doRender=function(){},c.prototype._invalidateLayout=function(){this._isAnchored&&this._isSetup&&(this.isTopLevelComponent?this._scheduleComputeLayout():this._parent._invalidateLayout())},c.prototype.renderTo=function(b){if(null!=b){var c;if(c="function"==typeof b.node?b:d3.select(b),!c.node()||"svg"!==c.node().nodeName)throw new Error("Plottable requires a valid SVG to renderTo");this._anchor(c)}if(null==this._element)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");return this._computeLayout(),this._render(),a.Core.RenderController.flush(),this},c.prototype.resize=function(a,b){if(!this.isTopLevelComponent)throw new Error("Cannot resize on non top-level component");return null!=a&&null!=b&&this._isAnchored&&this.rootSVG.attr({width:a,height:b}),this._invalidateLayout(),this},c.prototype.autoResize=function(b){return b?a.Core.ResizeBroadcaster.register(this):a.Core.ResizeBroadcaster.deregister(this),this},c.prototype.xAlign=function(a){if(a=a.toLowerCase(),"left"===a)this._xAlignProportion=0;else if("center"===a)this._xAlignProportion=.5;else{if("right"!==a)throw new Error("Unsupported alignment");this._xAlignProportion=1}return this._invalidateLayout(),this},c.prototype.yAlign=function(a){if(a=a.toLowerCase(),"top"===a)this._yAlignProportion=0;else if("center"===a)this._yAlignProportion=.5;else{if("bottom"!==a)throw new Error("Unsupported alignment");this._yAlignProportion=1}return this._invalidateLayout(),this},c.prototype.xOffset=function(a){return this._xOffset=a,this._invalidateLayout(),this},c.prototype.yOffset=function(a){return this._yOffset=a,this._invalidateLayout(),this},c.prototype.addBox=function(a,b){if(null==this._element)throw new Error("Adding boxes before anchoring is currently disallowed");var b=null==b?this.boxContainer:b,c=b.append("rect");return null!=a&&c.classed(a,!0),this.boxes.push(c),null!=this.width()&&null!=this.height()&&c.attr("width",this.width()).attr("height",this.height()),c},c.prototype.generateClipPath=function(){var a=/MSIE [5-9]/.test(navigator.userAgent)?"":document.location.href;this._element.attr("clip-path","url("+a+"#clipPath"+this._plottableID+")");var b=this.boxContainer.append("clipPath").attr("id","clipPath"+this._plottableID);this.addBox("clip-rect",b)},c.prototype.registerInteraction=function(a){return this._element?(this.hitBox||(this.hitBox=this.addBox("hit-box"),this.hitBox.style("fill","#ffffff").style("opacity",0)),a._anchor(this,this.hitBox)):this.interactionsToRegister.push(a),this},c.prototype.classed=function(a,b){if(null==b)return null==a?!1:null==this._element?-1!==this.cssClasses.indexOf(a):this._element.classed(a);if(null==a)return this;if(null==this._element){var c=this.cssClasses.indexOf(a);b&&-1===c?this.cssClasses.push(a):b||-1===c||this.cssClasses.splice(c,1)}else this._element.classed(a,b);return this},c.prototype._isFixedWidth=function(){return this._fixedWidthFlag},c.prototype._isFixedHeight=function(){return this._fixedHeightFlag},c.prototype.merge=function(b){var c;if(this._isSetup||this._isAnchored)throw new Error("Can't presently merge a component that's already been anchored");return a.Component.Group.prototype.isPrototypeOf(b)?(c=b,c._addComponent(this,!0),c):c=new a.Component.Group([this,b])},c.prototype.detach=function(){return this._isAnchored&&this._element.remove(),null!=this._parent&&this._parent._removeComponent(this),this._isAnchored=!1,this._parent=null,this},c.prototype.remove=function(){this.removed=!0,this.detach(),a.Core.ResizeBroadcaster.deregister(this)},c.prototype.width=function(){return this._width},c.prototype.height=function(){return this._height},c.AUTORESIZE_BY_DEFAULT=!0,c}(a.Core.PlottableObject);b.AbstractComponent=c}(a.Component||(a.Component={}));a.Component}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(a){var b=function(a){function b(){a.apply(this,arguments),this._components=[]}return __extends(b,a),b.prototype._anchor=function(b){var c=this;a.prototype._anchor.call(this,b),this._components.forEach(function(a){return a._anchor(c._content)})},b.prototype._render=function(){this._components.forEach(function(a){return a._render()})},b.prototype._removeComponent=function(a){var b=this._components.indexOf(a);b>=0&&(this._components.splice(b,1),this._invalidateLayout())},b.prototype._addComponent=function(a,b){return void 0===b&&(b=!1),!a||this._components.indexOf(a)>=0?!1:(b?this._components.unshift(a):this._components.push(a),a._parent=this,this._isAnchored&&a._anchor(this._content),this._invalidateLayout(),!0)},b.prototype.components=function(){return this._components.slice()},b.prototype.empty=function(){return 0===this._components.length},b.prototype.detachAll=function(){return this._components.slice().forEach(function(a){return a.detach()}),this},b.prototype.remove=function(){a.prototype.remove.call(this),this._components.slice().forEach(function(a){return a.remove()})},b}(a.AbstractComponent);a.AbstractComponentContainer=b}(a.Component||(a.Component={}));a.Component}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(a){var c=this;void 0===a&&(a=[]),b.call(this),this.classed("component-group",!0),a.forEach(function(a){return c._addComponent(a)})}return __extends(c,b),c.prototype._requestedSpace=function(b,c){var d=this._components.map(function(a){return a._requestedSpace(b,c)});return{width:a._Util.Methods.max(d,function(a){return a.width},0),height:a._Util.Methods.max(d,function(a){return a.height},0),wantsWidth:d.map(function(a){return a.wantsWidth}).some(function(a){return a}),wantsHeight:d.map(function(a){return a.wantsHeight}).some(function(a){return a})}},c.prototype.merge=function(a){return this._addComponent(a),this},c.prototype._computeLayout=function(a,c,d,e){var f=this;return b.prototype._computeLayout.call(this,a,c,d,e),this._components.forEach(function(a){a._computeLayout(0,0,f.width(),f.height())}),this},c.prototype._isFixedWidth=function(){return this._components.every(function(a){return a._isFixedWidth()})},c.prototype._isFixedHeight=function(){return this._components.every(function(a){return a._isFixedHeight()})},c}(b.AbstractComponentContainer);b.Group=c}(a.Component||(a.Component={}));a.Component}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(c,d,e){var f=this;if(void 0===e&&(e=a.Formatters.identity()),b.call(this),this._endTickLength=5,this._tickLength=5,this._tickLabelPadding=10,this._gutter=15,this._showEndTickLabels=!1,null==c||null==d)throw new Error("Axis requires a scale and orientation");this._scale=c,this.orient(d),this._setDefaultAlignment(),this.classed("axis",!0),this._isHorizontal()?this.classed("x-axis",!0):this.classed("y-axis",!0),this.formatter(e),this._scale.broadcaster.registerListener(this,function(){return f._rescale()})}return __extends(c,b),c.prototype.remove=function(){b.prototype.remove.call(this),this._scale.broadcaster.deregisterListener(this)},c.prototype._isHorizontal=function(){return"top"===this._orientation||"bottom"===this._orientation},c.prototype._computeWidth=function(){return this._computedWidth=this._maxLabelTickLength(),this._computedWidth},c.prototype._computeHeight=function(){return this._computedHeight=this._maxLabelTickLength(),this._computedHeight},c.prototype._requestedSpace=function(a,b){var c=0,d=0;return this._isHorizontal()?(null==this._computedHeight&&this._computeHeight(),d=this._computedHeight+this._gutter):(null==this._computedWidth&&this._computeWidth(),c=this._computedWidth+this._gutter),{width:c,height:d,wantsWidth:!this._isHorizontal()&&c>a,wantsHeight:this._isHorizontal()&&d>b}},c.prototype._isFixedHeight=function(){return this._isHorizontal()},c.prototype._isFixedWidth=function(){return!this._isHorizontal()},c.prototype._rescale=function(){this._render()},c.prototype._computeLayout=function(a,c,d,e){b.prototype._computeLayout.call(this,a,c,d,e),this._scale.range(this._isHorizontal()?[0,this.width()]:[this.height(),0])},c.prototype._setup=function(){b.prototype._setup.call(this),this._tickMarkContainer=this._content.append("g").classed(c.TICK_MARK_CLASS+"-container",!0),this._tickLabelContainer=this._content.append("g").classed(c.TICK_LABEL_CLASS+"-container",!0),this._baseline=this._content.append("line").classed("baseline",!0)},c.prototype._getTickValues=function(){return[]},c.prototype._doRender=function(){var a=this._getTickValues(),b=this._tickMarkContainer.selectAll("."+c.TICK_MARK_CLASS).data(a);b.enter().append("line").classed(c.TICK_MARK_CLASS,!0),b.attr(this._generateTickMarkAttrHash()),d3.select(b[0][0]).classed(c.END_TICK_MARK_CLASS,!0).attr(this._generateTickMarkAttrHash(!0)),d3.select(b[0][a.length-1]).classed(c.END_TICK_MARK_CLASS,!0).attr(this._generateTickMarkAttrHash(!0)),b.exit().remove(),this._baseline.attr(this._generateBaselineAttrHash())},c.prototype._generateBaselineAttrHash=function(){var a={x1:0,y1:0,x2:0,y2:0};switch(this._orientation){case"bottom":a.x2=this.width();break;case"top":a.x2=this.width(),a.y1=this.height(),a.y2=this.height();break;case"left":a.x1=this.width(),a.x2=this.width(),a.y2=this.height();break;case"right":a.y2=this.height()}return a},c.prototype._generateTickMarkAttrHash=function(a){var b=this;void 0===a&&(a=!1);var c={x1:0,y1:0,x2:0,y2:0},d=function(a){return b._scale.scale(a)};this._isHorizontal()?(c.x1=d,c.x2=d):(c.y1=d,c.y2=d);var e=a?this._endTickLength:this._tickLength;switch(this._orientation){case"bottom":c.y2=e;break;case"top":c.y1=this.height(),c.y2=this.height()-e;break;case"left":c.x1=this.width(),c.x2=this.width()-e;break;case"right":c.x2=e}return c},c.prototype._invalidateLayout=function(){this._computedWidth=null,this._computedHeight=null,b.prototype._invalidateLayout.call(this)},c.prototype._setDefaultAlignment=function(){switch(this._orientation){case"bottom":this.yAlign("top");break;case"top":this.yAlign("bottom");break;case"left":this.xAlign("right");break;case"right":this.xAlign("left")}},c.prototype.formatter=function(a){return void 0===a?this._formatter:(this._formatter=a,this._invalidateLayout(),this)},c.prototype.tickLength=function(a){if(null==a)return this._tickLength;if(0>a)throw new Error("tick length must be positive");return this._tickLength=a,this._invalidateLayout(),this},c.prototype.endTickLength=function(a){if(null==a)return this._endTickLength;if(0>a)throw new Error("end tick length must be positive");return this._endTickLength=a,this._invalidateLayout(),this},c.prototype._maxLabelTickLength=function(){return this.showEndTickLabels()?Math.max(this.tickLength(),this.endTickLength()):this.tickLength()},c.prototype.tickLabelPadding=function(a){if(null==a)return this._tickLabelPadding;if(0>a)throw new Error("tick label padding must be positive");return this._tickLabelPadding=a,this._invalidateLayout(),this},c.prototype.gutter=function(a){if(null==a)return this._gutter;if(0>a)throw new Error("gutter size must be positive");return this._gutter=a,this._invalidateLayout(),this},c.prototype.orient=function(a){if(null==a)return this._orientation;var b=a.toLowerCase();if("top"!==b&&"bottom"!==b&&"left"!==b&&"right"!==b)throw new Error("unsupported orientation");return this._orientation=b,this._invalidateLayout(),this},c.prototype.showEndTickLabels=function(a){return null==a?this._showEndTickLabels:(this._showEndTickLabels=a,this._render(),this)},c.prototype._hideEndTickLabels=function(){var a=this,b=this._element.select(".bounding-box")[0][0].getBoundingClientRect(),d=function(c){return Math.floor(b.left)<=Math.ceil(c.left)&&Math.floor(b.top)<=Math.ceil(c.top)&&Math.floor(c.right)<=Math.ceil(b.left+a.width())&&Math.floor(c.bottom)<=Math.ceil(b.top+a.height())},e=this._tickLabelContainer.selectAll("."+c.TICK_LABEL_CLASS);if(0!==e[0].length){var f=e[0][0];d(f.getBoundingClientRect())||d3.select(f).style("visibility","hidden");var g=e[0][e[0].length-1];d(g.getBoundingClientRect())||d3.select(g).style("visibility","hidden")}},c.prototype._hideOverlappingTickLabels=function(){var b,d=this._tickLabelContainer.selectAll("."+c.TICK_LABEL_CLASS).filter(function(){return"visible"===d3.select(this).style("visibility")});d.each(function(){var c=this.getBoundingClientRect(),d=d3.select(this);null!=b&&a._Util.DOM.boxesOverlap(c,b)?d.style("visibility","hidden"):(b=c,d.style("visibility","visible"))})},c.END_TICK_MARK_CLASS="end-tick-mark",c.TICK_MARK_CLASS="tick-mark",c.TICK_LABEL_CLASS="tick-label",c}(a.Component.AbstractComponent);b.AbstractAxis=c}(a.Axis||(a.Axis={}));a.Axis}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(c){function d(a,b){if(b=b.toLowerCase(),"top"!==b&&"bottom"!==b)throw new Error("unsupported orientation: "+b);c.call(this,a,b),this.classed("time-axis",!0),this.tickLabelPadding(5)}return __extends(d,c),d.prototype._computeHeight=function(){if(null!==this._computedHeight)return this._computedHeight;var a=this._measureTextHeight(this._majorTickLabels)+this._measureTextHeight(this._minorTickLabels);return this.tickLength(a),this.endTickLength(a),this._computedHeight=this._maxLabelTickLength()+2*this.tickLabelPadding(),this._computedHeight},d.prototype.calculateWorstWidth=function(a,b){var c=new Date(9999,8,29,12,59,9999);return this.measurer(d3.time.format(b)(c)).width},d.prototype.getIntervalLength=function(a){var b=this._scale.domain()[0],c=a.timeUnit.offset(b,a.step);if(c>this._scale.domain()[1])return this.width();var d=Math.abs(this._scale.scale(c)-this._scale.scale(b));return d},d.prototype.isEnoughSpace=function(a,b){var c=this.calculateWorstWidth(a,b.formatString)+2*this.tickLabelPadding(),d=Math.min(this.getIntervalLength(b),this.width());return d>c},d.prototype._setup=function(){c.prototype._setup.call(this),this._majorTickLabels=this._content.append("g").classed(b.AbstractAxis.TICK_LABEL_CLASS,!0),this._minorTickLabels=this._content.append("g").classed(b.AbstractAxis.TICK_LABEL_CLASS,!0),this.measurer=a._Util.Text.getTextMeasurer(this._majorTickLabels.append("text"))},d.prototype.getTickLevel=function(){for(var b=0;b=d._minorIntervals.length&&(a._Util.Methods.warn("zoomed out too far: could not find suitable interval to display labels"),b=d._minorIntervals.length-1),b},d.prototype._getTickIntervalValues=function(a){return this._scale._tickInterval(a.timeUnit,a.step)},d.prototype._getTickValues=function(){var a=this.getTickLevel(),b=this._getTickIntervalValues(d._minorIntervals[a]),c=this._getTickIntervalValues(d._majorIntervals[a]);return b.concat(c)},d.prototype._measureTextHeight=function(c){var d=c.append("g").classed(b.AbstractAxis.TICK_LABEL_CLASS,!0),e=this.measurer(a._Util.Text.HEIGHT_TEXT).height;return d.remove(),e},d.prototype.renderTickLabels=function(c,d,e){var f=this;c.selectAll("."+b.AbstractAxis.TICK_LABEL_CLASS).remove();var g=this._scale._tickInterval(d.timeUnit,d.step);g.splice(0,0,this._scale.domain()[0]),g.push(this._scale.domain()[1]);var h=1===d.step,i=[];h?g.map(function(a,b){b+1>=g.length||i.push(new Date((g[b+1].valueOf()-g[b].valueOf())/2+g[b].valueOf()))}):i=g,i=i.filter(function(a){return f.canFitLabelFilter(c,a,d3.time.format(d.formatString)(a),h)});var j=c.selectAll("."+b.AbstractAxis.TICK_LABEL_CLASS).data(i,function(a){return a.valueOf()}),k=j.enter().append("g").classed(b.AbstractAxis.TICK_LABEL_CLASS,!0);k.append("text");var l=h?0:this.tickLabelPadding(),m="bottom"===this._orientation?this._maxLabelTickLength()/2*e:this.height()-this._maxLabelTickLength()/2*e+2*this.tickLabelPadding(),n=j.selectAll("text");n.size()>0&&a._Util.DOM.translate(n,l,m),j.exit().remove(),j.attr("transform",function(a){return"translate("+f._scale.scale(a)+",0)" +});var o=h?"middle":"start";j.selectAll("text").text(function(a){return d3.time.format(d.formatString)(a)}).style("text-anchor",o)},d.prototype.canFitLabelFilter=function(a,b,c,d){var e,f,g=this.measurer(c).width+this.tickLabelPadding();return d?(e=this._scale.scale(b)+g/2,f=this._scale.scale(b)-g/2):(e=this._scale.scale(b)+g,f=this._scale.scale(b)),e0},d.prototype.adjustTickLength=function(a,c){var d=this._getTickIntervalValues(c),e=this._tickMarkContainer.selectAll("."+b.AbstractAxis.TICK_MARK_CLASS).filter(function(a){return d.map(function(a){return a.valueOf()}).indexOf(a.valueOf())>=0});"top"===this._orientation&&(a=this.height()-a),e.attr("y2",a)},d.prototype.generateLabellessTicks=function(a){if(!(0>a)){var c=this._getTickIntervalValues(d._minorIntervals[a]),e=this._getTickValues().concat(c),f=this._tickMarkContainer.selectAll("."+b.AbstractAxis.TICK_MARK_CLASS).data(e);f.enter().append("line").classed(b.AbstractAxis.TICK_MARK_CLASS,!0),f.attr(this._generateTickMarkAttrHash()),f.exit().remove(),this.adjustTickLength(this.tickLabelPadding(),d._minorIntervals[a])}},d.prototype._doRender=function(){c.prototype._doRender.call(this);var a=this.getTickLevel();this.renderTickLabels(this._minorTickLabels,d._minorIntervals[a],1),this.renderTickLabels(this._majorTickLabels,d._majorIntervals[a],2);var b=this._scale.domain(),e=this._scale.scale(b[1])-this._scale.scale(b[0]);return 1.5*this.getIntervalLength(d._minorIntervals[a])>=e&&this.generateLabellessTicks(a-1),this.adjustTickLength(this._maxLabelTickLength()/2,d._minorIntervals[a]),this.adjustTickLength(this._maxLabelTickLength(),d._majorIntervals[a]),this},d._minorIntervals=[{timeUnit:d3.time.second,step:1,formatString:"%I:%M:%S %p"},{timeUnit:d3.time.second,step:5,formatString:"%I:%M:%S %p"},{timeUnit:d3.time.second,step:10,formatString:"%I:%M:%S %p"},{timeUnit:d3.time.second,step:15,formatString:"%I:%M:%S %p"},{timeUnit:d3.time.second,step:30,formatString:"%I:%M:%S %p"},{timeUnit:d3.time.minute,step:1,formatString:"%I:%M %p"},{timeUnit:d3.time.minute,step:5,formatString:"%I:%M %p"},{timeUnit:d3.time.minute,step:10,formatString:"%I:%M %p"},{timeUnit:d3.time.minute,step:15,formatString:"%I:%M %p"},{timeUnit:d3.time.minute,step:30,formatString:"%I:%M %p"},{timeUnit:d3.time.hour,step:1,formatString:"%I %p"},{timeUnit:d3.time.hour,step:3,formatString:"%I %p"},{timeUnit:d3.time.hour,step:6,formatString:"%I %p"},{timeUnit:d3.time.hour,step:12,formatString:"%I %p"},{timeUnit:d3.time.day,step:1,formatString:"%a %e"},{timeUnit:d3.time.day,step:1,formatString:"%e"},{timeUnit:d3.time.month,step:1,formatString:"%B"},{timeUnit:d3.time.month,step:1,formatString:"%b"},{timeUnit:d3.time.month,step:3,formatString:"%B"},{timeUnit:d3.time.month,step:6,formatString:"%B"},{timeUnit:d3.time.year,step:1,formatString:"%Y"},{timeUnit:d3.time.year,step:1,formatString:"%y"},{timeUnit:d3.time.year,step:5,formatString:"%Y"},{timeUnit:d3.time.year,step:25,formatString:"%Y"},{timeUnit:d3.time.year,step:50,formatString:"%Y"},{timeUnit:d3.time.year,step:100,formatString:"%Y"},{timeUnit:d3.time.year,step:200,formatString:"%Y"},{timeUnit:d3.time.year,step:500,formatString:"%Y"},{timeUnit:d3.time.year,step:1e3,formatString:"%Y"}],d._majorIntervals=[{timeUnit:d3.time.day,step:1,formatString:"%B %e, %Y"},{timeUnit:d3.time.day,step:1,formatString:"%B %e, %Y"},{timeUnit:d3.time.day,step:1,formatString:"%B %e, %Y"},{timeUnit:d3.time.day,step:1,formatString:"%B %e, %Y"},{timeUnit:d3.time.day,step:1,formatString:"%B %e, %Y"},{timeUnit:d3.time.day,step:1,formatString:"%B %e, %Y"},{timeUnit:d3.time.day,step:1,formatString:"%B %e, %Y"},{timeUnit:d3.time.day,step:1,formatString:"%B %e, %Y"},{timeUnit:d3.time.day,step:1,formatString:"%B %e, %Y"},{timeUnit:d3.time.day,step:1,formatString:"%B %e, %Y"},{timeUnit:d3.time.day,step:1,formatString:"%B %e, %Y"},{timeUnit:d3.time.day,step:1,formatString:"%B %e, %Y"},{timeUnit:d3.time.day,step:1,formatString:"%B %e, %Y"},{timeUnit:d3.time.day,step:1,formatString:"%B %e, %Y"},{timeUnit:d3.time.month,step:1,formatString:"%B %Y"},{timeUnit:d3.time.month,step:1,formatString:"%B %Y"},{timeUnit:d3.time.year,step:1,formatString:"%Y"},{timeUnit:d3.time.year,step:1,formatString:"%Y"},{timeUnit:d3.time.year,step:1,formatString:"%Y"},{timeUnit:d3.time.year,step:1,formatString:"%Y"},{timeUnit:d3.time.year,step:1e5,formatString:""},{timeUnit:d3.time.year,step:1e5,formatString:""},{timeUnit:d3.time.year,step:1e5,formatString:""},{timeUnit:d3.time.year,step:1e5,formatString:""},{timeUnit:d3.time.year,step:1e5,formatString:""},{timeUnit:d3.time.year,step:1e5,formatString:""},{timeUnit:d3.time.year,step:1e5,formatString:""},{timeUnit:d3.time.year,step:1e5,formatString:""},{timeUnit:d3.time.year,step:1e5,formatString:""}],d}(b.AbstractAxis);b.Time=c}(a.Axis||(a.Axis={}));a.Axis}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(c){function d(b,d,e){void 0===e&&(e=a.Formatters.general(3,!1)),c.call(this,b,d,e),this.tickLabelPositioning="center",this.showFirstTickLabel=!1,this.showLastTickLabel=!1}return __extends(d,c),d.prototype._setup=function(){c.prototype._setup.call(this),this.measurer=a._Util.Text.getTextMeasurer(this._tickLabelContainer.append("text").classed(b.AbstractAxis.TICK_LABEL_CLASS,!0))},d.prototype._computeWidth=function(){var b=this,c=this._getTickValues(),d=c.map(function(a){var c=b._formatter(a);return b.measurer(c).width}),e=a._Util.Methods.max(d,0);return this._computedWidth="center"===this.tickLabelPositioning?this._maxLabelTickLength()+this.tickLabelPadding()+e:Math.max(this._maxLabelTickLength(),this.tickLabelPadding()+e),this._computedWidth},d.prototype._computeHeight=function(){var b=this.measurer(a._Util.Text.HEIGHT_TEXT).height;return this._computedHeight="center"===this.tickLabelPositioning?this._maxLabelTickLength()+this.tickLabelPadding()+b:Math.max(this._maxLabelTickLength(),this.tickLabelPadding()+b),this._computedHeight},d.prototype._getTickValues=function(){return this._scale.ticks()},d.prototype._rescale=function(){if(this._isSetup){if(!this._isHorizontal()){var a=this._computeWidth();if(a>this.width()||aa,wantsHeight:e>b}},c.prototype._setup=function(){b.prototype._setup.call(this),this.textContainer=this._content.append("g"),this.measurer=a._Util.Text.getTextMeasurer(this.textContainer.append("text")),this.text(this._text)},c.prototype.text=function(a){return void 0===a?this._text:(this._text=a,this._invalidateLayout(),this)},c.prototype.orient=function(a){if(null==a)return this.orientation;if(a=a.toLowerCase(),"horizontal"!==a&&"left"!==a&&"right"!==a)throw new Error(a+" is not a valid orientation for LabelComponent");return this.orientation=a,this._invalidateLayout(),this},c.prototype._doRender=function(){b.prototype._doRender.call(this),this.textContainer.text("");var c="horizontal"===this.orientation?this.width():this.height(),d=a._Util.Text.getTruncatedText(this._text,c,this.measurer);"horizontal"===this.orientation?a._Util.Text.writeLineHorizontally(d,this.textContainer,this.width(),this.height(),this.xAlignment,this.yAlignment):a._Util.Text.writeLineVertically(d,this.textContainer,this.width(),this.height(),this.xAlignment,this.yAlignment,this.orientation)},c.prototype._computeLayout=function(c,d,e,f){return this.measurer=a._Util.Text.getTextMeasurer(this.textContainer.append("text")),b.prototype._computeLayout.call(this,c,d,e,f),this},c}(b.AbstractComponent);b.Label=c;var d=function(a){function b(b,c){a.call(this,b,c),this.classed("title-label",!0)}return __extends(b,a),b}(c);b.TitleLabel=d;var e=function(a){function b(b,c){a.call(this,b,c),this.classed("axis-label",!0)}return __extends(b,a),b}(c);b.AxisLabel=e}(a.Component||(a.Component={}));a.Component}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(a){b.call(this),this.classed("legend",!0),this.scale(a),this.xAlign("RIGHT").yAlign("TOP"),this.xOffset(5).yOffset(5),this._fixedWidthFlag=!0,this._fixedHeightFlag=!0}return __extends(c,b),c.prototype.remove=function(){b.prototype.remove.call(this),null!=this.colorScale&&this.colorScale.broadcaster.deregisterListener(this)},c.prototype.toggleCallback=function(a){return void 0!==a?(this._toggleCallback=a,this.isOff=d3.set(),this.updateListeners(),this.updateClasses(),this):this._toggleCallback},c.prototype.hoverCallback=function(a){return void 0!==a?(this._hoverCallback=a,this.datumCurrentlyFocusedOn=void 0,this.updateListeners(),this.updateClasses(),this):this._hoverCallback},c.prototype.scale=function(a){var b=this;return null!=a?(null!=this.colorScale&&this.colorScale.broadcaster.deregisterListener(this),this.colorScale=a,this.colorScale.broadcaster.registerListener(this,function(){return b.updateDomain()}),this.updateDomain(),this):this.colorScale},c.prototype.updateDomain=function(){null!=this._toggleCallback&&(this.isOff=a._Util.Methods.intersection(this.isOff,d3.set(this.scale().domain()))),null!=this._hoverCallback&&(this.datumCurrentlyFocusedOn=this.scale().domain().indexOf(this.datumCurrentlyFocusedOn)>=0?this.datumCurrentlyFocusedOn:void 0),this._invalidateLayout()},c.prototype._computeLayout=function(a,c,d,e){b.prototype._computeLayout.call(this,a,c,d,e);var f=this.measureTextHeight(),g=this.colorScale.domain().length;this.nRowsDrawn=Math.min(g,Math.floor(this.height()/f))},c.prototype._requestedSpace=function(b,d){var e=this.measureTextHeight(),f=this.colorScale.domain().length,g=Math.min(f,Math.floor((d-2*c.MARGIN)/e)),h=this._content.append("g").classed(c.SUBELEMENT_CLASS,!0),i=a._Util.Text.getTextMeasurer(h.append("text")),j=a._Util.Methods.max(this.colorScale.domain(),function(a){return i(a).width},0);h.remove(),j=void 0===j?0:j;var k=0===g?0:j+e+2*c.MARGIN,l=0===g?0:f*e+2*c.MARGIN;return{width:k,height:l,wantsWidth:k>b,wantsHeight:l>d}},c.prototype.measureTextHeight=function(){var b=this._content.append("g").classed(c.SUBELEMENT_CLASS,!0),d=a._Util.Text.getTextMeasurer(b.append("text"))(a._Util.Text.HEIGHT_TEXT).height;return 0===d&&(d=1),b.remove(),d},c.prototype._doRender=function(){b.prototype._doRender.call(this);var d=this.colorScale.domain().slice(0,this.nRowsDrawn),e=this.measureTextHeight(),f=this.width()-e-c.MARGIN,g=.3*e,h=this._content.selectAll("."+c.SUBELEMENT_CLASS).data(d,function(a){return a}),i=h.enter().append("g").classed(c.SUBELEMENT_CLASS,!0);i.append("circle"),i.append("g").classed("text-container",!0),h.exit().remove(),h.selectAll("circle").attr("cx",e/2).attr("cy",e/2).attr("r",g).attr("fill",this.colorScale._d3Scale),h.selectAll("g.text-container").text("").attr("transform","translate("+e+", 0)").each(function(b){var c=d3.select(this),d=a._Util.Text.getTextMeasurer(c.append("text")),e=a._Util.Text.getTruncatedText(b,f,d),g=d(e);a._Util.Text.writeLineHorizontally(e,c,g.width,g.height)}),h.attr("transform",function(a){return"translate("+c.MARGIN+","+(d.indexOf(a)*e+c.MARGIN)+")"}),this.updateClasses(),this.updateListeners()},c.prototype.updateListeners=function(){var a=this;if(this._isSetup){var b=this._content.selectAll("."+c.SUBELEMENT_CLASS);if(null!=this._hoverCallback){var d=function(b){return function(c){a.datumCurrentlyFocusedOn=b?c:void 0,a._hoverCallback(a.datumCurrentlyFocusedOn),a.updateClasses()}};b.on("mouseover",d(!0)),b.on("mouseout",d(!1))}else b.on("mouseover",null),b.on("mouseout",null);null!=this._toggleCallback?b.on("click",function(b){var c=a.isOff.has(b);c?a.isOff.remove(b):a.isOff.add(b),a._toggleCallback(b,c),a.updateClasses()}):b.on("click",null)}},c.prototype.updateClasses=function(){var a=this;if(this._isSetup){var b=this._content.selectAll("."+c.SUBELEMENT_CLASS);null!=this._hoverCallback?(b.classed("focus",function(b){return a.datumCurrentlyFocusedOn===b}),b.classed("hover",void 0!==this.datumCurrentlyFocusedOn)):(b.classed("hover",!1),b.classed("focus",!1)),null!=this._toggleCallback?(b.classed("toggled-on",function(b){return!a.isOff.has(b)}),b.classed("toggled-off",function(b){return a.isOff.has(b)})):(b.classed("toggled-on",!1),b.classed("toggled-off",!1))}},c.SUBELEMENT_CLASS="legend-row",c.MARGIN=5,c}(b.AbstractComponent);b.Legend=c}(a.Component||(a.Component={}));a.Component}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(a){var c=this;b.call(this),this.padding=5,this.classed("legend",!0),this.scale=a,this.scale.broadcaster.registerListener(this,function(){return c._invalidateLayout()}),this.xAlign("left").yAlign("center"),this._fixedWidthFlag=!0,this._fixedHeightFlag=!0}return __extends(c,b),c.prototype.remove=function(){b.prototype.remove.call(this),this.scale.broadcaster.deregisterListener(this)},c.prototype.calculateLayoutInfo=function(b,d){var e=this,f=this._content.append("g").classed(c.LEGEND_ROW_CLASS,!0),g=(f.append("g").classed(c.LEGEND_ENTRY_CLASS,!0),a._Util.Text.getTextMeasurer(f.append("text"))),h=g(a._Util.Text.HEIGHT_TEXT).height,i=Math.max(0,b-this.padding),j=function(a){var b=h+g(a).width+e.padding;return Math.min(b,i)},k=this.scale.domain(),l=a._Util.Methods.populateMap(k,j);f.remove();var m=this.packRows(i,k,l),n=Math.floor((d-2*this.padding)/h);return n!==n&&(n=0),{textHeight:h,entryLengths:l,rows:m,numRowsToDraw:Math.max(Math.min(n,m.length),0)}},c.prototype._requestedSpace=function(b,c){var d=this.calculateLayoutInfo(b,c),e=d.rows.map(function(a){return d3.sum(a,function(a){return d.entryLengths.get(a)})}),f=a._Util.Methods.max(e,0);f=void 0===f?0:f;var g=this.padding+f,h=d.numRowsToDraw*d.textHeight+2*this.padding,i=d.rows.length*d.textHeight+2*this.padding;return{width:g,height:h,wantsWidth:g>b,wantsHeight:i>c}},c.prototype.packRows=function(a,b,c){var d=[[]],e=d[0],f=a;return b.forEach(function(b){var g=c.get(b);g>f&&(e=[],d.push(e),f=a),e.push(b),f-=g}),d},c.prototype._doRender=function(){var d=this;b.prototype._doRender.call(this);var e=this.calculateLayoutInfo(this.width(),this.height()),f=e.rows.slice(0,e.numRowsToDraw),g=this._content.selectAll("g."+c.LEGEND_ROW_CLASS).data(f);g.enter().append("g").classed(c.LEGEND_ROW_CLASS,!0),g.exit().remove(),g.attr("transform",function(a,b){return"translate(0, "+(b*e.textHeight+d.padding)+")"});var h=g.selectAll("g."+c.LEGEND_ENTRY_CLASS).data(function(a){return a}),i=h.enter().append("g").classed(c.LEGEND_ENTRY_CLASS,!0);i.append("circle"),i.append("g").classed("text-container",!0),h.exit().remove();var j=this.padding;g.each(function(){var a=j,b=d3.select(this).selectAll("g."+c.LEGEND_ENTRY_CLASS);b.attr("transform",function(b){var c="translate("+a+", 0)";return a+=e.entryLengths.get(b),c})}),h.select("circle").attr("cx",e.textHeight/2).attr("cy",e.textHeight/2).attr("r",.3*e.textHeight).attr("fill",function(a){return d.scale.scale(a)});var k=this.padding,l=h.select("g.text-container");l.text(""),l.append("title").text(function(a){return a}),l.attr("transform","translate("+e.textHeight+", "+.1*e.textHeight+")").each(function(b){var c=d3.select(this),d=a._Util.Text.getTextMeasurer(c.append("text")),f=e.entryLengths.get(b)-e.textHeight-k,g=a._Util.Text.getTruncatedText(b,f,d),h=d(g);a._Util.Text.writeLineHorizontally(g,c,h.width,h.height)})},c.LEGEND_ROW_CLASS="legend-row",c.LEGEND_ENTRY_CLASS="legend-entry",c}(b.AbstractComponent);b.HorizontalLegend=c}(a.Component||(a.Component={}));a.Component}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(c,d){var e=this;if(null!=c&&!a.Scale.AbstractQuantitative.prototype.isPrototypeOf(c))throw new Error("xScale needs to inherit from Scale.AbstractQuantitative");if(null!=d&&!a.Scale.AbstractQuantitative.prototype.isPrototypeOf(d))throw new Error("yScale needs to inherit from Scale.AbstractQuantitative");b.call(this),this.classed("gridlines",!0),this.xScale=c,this.yScale=d,this.xScale&&this.xScale.broadcaster.registerListener(this,function(){return e._render()}),this.yScale&&this.yScale.broadcaster.registerListener(this,function(){return e._render()})}return __extends(c,b),c.prototype.remove=function(){return b.prototype.remove.call(this),this.xScale&&this.xScale.broadcaster.deregisterListener(this),this.yScale&&this.yScale.broadcaster.deregisterListener(this),this},c.prototype._setup=function(){b.prototype._setup.call(this),this.xLinesContainer=this._content.append("g").classed("x-gridlines",!0),this.yLinesContainer=this._content.append("g").classed("y-gridlines",!0)},c.prototype._doRender=function(){b.prototype._doRender.call(this),this.redrawXLines(),this.redrawYLines()},c.prototype.redrawXLines=function(){var a=this;if(this.xScale){var b=this.xScale.ticks(),c=function(b){return a.xScale.scale(b)},d=this.xLinesContainer.selectAll("line").data(b);d.enter().append("line"),d.attr("x1",c).attr("y1",0).attr("x2",c).attr("y2",this.height()).classed("zeroline",function(a){return 0===a}),d.exit().remove()}},c.prototype.redrawYLines=function(){var a=this;if(this.yScale){var b=this.yScale.ticks(),c=function(b){return a.yScale.scale(b)},d=this.yLinesContainer.selectAll("line").data(b);d.enter().append("line"),d.attr("x1",0).attr("y1",c).attr("x2",this.width()).attr("y2",c).classed("zeroline",function(a){return 0===a}),d.exit().remove()}},c}(b.AbstractComponent);b.Gridlines=c}(a.Component||(a.Component={}));a.Component}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(a){var c=this;void 0===a&&(a=[]),b.call(this),this.rowPadding=0,this.colPadding=0,this.rows=[],this.rowWeights=[],this.colWeights=[],this.nRows=0,this.nCols=0,this.classed("table",!0),a.forEach(function(a,b){a.forEach(function(a,d){c.addComponent(b,d,a)})})}return __extends(c,b),c.prototype.addComponent=function(a,b,c){if(this._addComponent(c)){this.nRows=Math.max(a+1,this.nRows),this.nCols=Math.max(b+1,this.nCols),this.padTableToSize(this.nRows,this.nCols);var d=this.rows[a][b];if(d)throw new Error("Table.addComponent cannot be called on a cell where a component already exists (for the moment)");this.rows[a][b]=c}return this},c.prototype._removeComponent=function(a){b.prototype._removeComponent.call(this,a);var c,d;a:for(var e=0;e0&&v&&e!==x,C=f>0&&w&&f!==y;if(!B&&!C)break;if(r>5)break}return e=h-d3.sum(u.guaranteedWidths),f=i-d3.sum(u.guaranteedHeights),n=c.calcProportionalSpace(k,e),o=c.calcProportionalSpace(j,f),{colProportionalSpace:n,rowProportionalSpace:o,guaranteedWidths:u.guaranteedWidths,guaranteedHeights:u.guaranteedHeights,wantsWidth:v,wantsHeight:w}},c.prototype.determineGuarantees=function(b,c){var d=a._Util.Methods.createFilledArray(0,this.nCols),e=a._Util.Methods.createFilledArray(0,this.nRows),f=a._Util.Methods.createFilledArray(!1,this.nCols),g=a._Util.Methods.createFilledArray(!1,this.nRows);return this.rows.forEach(function(a,h){a.forEach(function(a,i){var j;j=null!=a?a._requestedSpace(b[i],c[h]):{width:0,height:0,wantsWidth:!1,wantsHeight:!1};var k=Math.min(j.width,b[i]),l=Math.min(j.height,c[h]);d[i]=Math.max(d[i],k),e[h]=Math.max(e[h],l),f[i]=f[i]||j.wantsWidth,g[h]=g[h]||j.wantsHeight})}),{guaranteedWidths:d,guaranteedHeights:e,wantsWidthArr:f,wantsHeightArr:g}},c.prototype._requestedSpace=function(a,b){var c=this.iterateLayout(a,b);return{width:d3.sum(c.guaranteedWidths),height:d3.sum(c.guaranteedHeights),wantsWidth:c.wantsWidth,wantsHeight:c.wantsHeight}},c.prototype._computeLayout=function(c,d,e,f){var g=this;b.prototype._computeLayout.call(this,c,d,e,f);var h=this.iterateLayout(this.width(),this.height()),i=a._Util.Methods.addArrays(h.rowProportionalSpace,h.guaranteedHeights),j=a._Util.Methods.addArrays(h.colProportionalSpace,h.guaranteedWidths),k=0;this.rows.forEach(function(a,b){var c=0;a.forEach(function(a,d){null!=a&&a._computeLayout(c,k,j[d],i[b]),c+=j[d]+g.colPadding}),k+=i[b]+g.rowPadding})},c.prototype.padding=function(a,b){return this.rowPadding=a,this.colPadding=b,this._invalidateLayout(),this},c.prototype.rowWeight=function(a,b){return this.rowWeights[a]=b,this._invalidateLayout(),this},c.prototype.colWeight=function(a,b){return this.colWeights[a]=b,this._invalidateLayout(),this},c.prototype._isFixedWidth=function(){var a=d3.transpose(this.rows);return c.fixedSpace(a,function(a){return null==a||a._isFixedWidth()})},c.prototype._isFixedHeight=function(){return c.fixedSpace(this.rows,function(a){return null==a||a._isFixedHeight()})},c.prototype.padTableToSize=function(a,b){for(var c=0;a>c;c++){void 0===this.rows[c]&&(this.rows[c]=[],this.rowWeights[c]=null);for(var d=0;b>d;d++)void 0===this.rows[c][d]&&(this.rows[c][d]=null)}for(d=0;b>d;d++)void 0===this.colWeights[d]&&(this.colWeights[d]=null)},c.calcComponentWeights=function(a,b,c){return a.map(function(a,d){if(null!=a)return a;var e=b[d].map(c),f=e.reduce(function(a,b){return a&&b},!0);return f?0:1})},c.calcProportionalSpace=function(b,c){var d=d3.sum(b);return 0===d?a._Util.Methods.createFilledArray(0,b.length):b.map(function(a){return c*a/d})},c.fixedSpace=function(a,b){var c=function(a){return a.reduce(function(a,b){return a&&b},!0)},d=function(a){return c(a.map(b))};return c(a.map(d))},c}(b.AbstractComponentContainer);b.Table=c}(a.Component||(a.Component={}));a.Component}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(){b.call(this),this._dataChanged=!1,this._projectors={},this._animate=!1,this._animators={},this._ANIMATION_DURATION=250,this._animateOnNextRender=!0,this.clipPathEnabled=!0,this.classed("plot",!0),this._key2DatasetDrawerKey=d3.map(),this._datasetKeysInOrder=[],this.nextSeriesIndex=0}return __extends(c,b),c.prototype._anchor=function(a){b.prototype._anchor.call(this,a),this._animateOnNextRender=!0,this._dataChanged=!0,this._updateScaleExtents()},c.prototype._setup=function(){var a=this;b.prototype._setup.call(this),this._renderArea=this._content.append("g").classed("render-area",!0),this._getDrawersInOrder().forEach(function(b){return b.setup(a._renderArea.append("g"))})},c.prototype.remove=function(){var a=this;b.prototype.remove.call(this),this._datasetKeysInOrder.forEach(function(b){return a.removeDataset(b)});var c=Object.keys(this._projectors);c.forEach(function(b){var c=a._projectors[b];c.scale&&c.scale.broadcaster.deregisterListener(a)})},c.prototype.addDataset=function(b,c){if("string"!=typeof b&&void 0!==c)throw new Error("invalid input to addDataset");"string"==typeof b&&"_"===b[0]&&a._Util.Methods.warn("Warning: Using _named series keys may produce collisions with unlabeled data sources");var d="string"==typeof b?b:"_"+this.nextSeriesIndex++,e="string"!=typeof b?b:c,c=e instanceof a.Dataset?e:new a.Dataset(e);return this._addDataset(d,c),this},c.prototype._addDataset=function(a,b){var c=this;this._key2DatasetDrawerKey.has(a)&&this.removeDataset(a);var d=this._getDrawer(a),e={drawer:d,dataset:b,key:a}; +this._datasetKeysInOrder.push(a),this._key2DatasetDrawerKey.set(a,e),this._isSetup&&d.setup(this._renderArea.append("g")),b.broadcaster.registerListener(this,function(){return c._onDatasetUpdate()}),this._onDatasetUpdate()},c.prototype._getDrawer=function(b){return new a._Drawer.AbstractDrawer(b)},c.prototype._getAnimator=function(b){return this._animate&&this._animateOnNextRender?this._animators[b]||new a.Animator.Null:new a.Animator.Null},c.prototype._onDatasetUpdate=function(){this._updateScaleExtents(),this._animateOnNextRender=!0,this._dataChanged=!0,this._render()},c.prototype.attr=function(a,b,c){return this.project(a,b,c)},c.prototype.project=function(b,c,d){var e=this;b=b.toLowerCase();var f=this._projectors[b],g=f&&f.scale;g&&this._datasetKeysInOrder.forEach(function(a){g._removeExtent(e._plottableID.toString()+"_"+a,b),g.broadcaster.deregisterListener(e)}),d&&d.broadcaster.registerListener(this,function(){return e._render()});var h=a._Util.Methods._applyAccessor(c,this);return this._projectors[b]={accessor:h,scale:d,attribute:b},this._updateScaleExtent(b),this._render(),this},c.prototype._generateAttrToProjector=function(){var a=this,b={};return d3.keys(this._projectors).forEach(function(c){var d=a._projectors[c],e=d.accessor,f=d.scale,g=f?function(a,b){return f.scale(e(a,b))}:e;b[c]=g}),b},c.prototype._doRender=function(){this._isAnchored&&(this.paint(),this._dataChanged=!1,this._animateOnNextRender=!1)},c.prototype.animate=function(a){return this._animate=a,this},c.prototype.detach=function(){return b.prototype.detach.call(this),this._updateScaleExtents(),this},c.prototype._updateScaleExtents=function(){var a=this;d3.keys(this._projectors).forEach(function(b){return a._updateScaleExtent(b)})},c.prototype._updateScaleExtent=function(a){var b=this,c=this._projectors[a];c.scale&&this._key2DatasetDrawerKey.forEach(function(d,e){var f=e.dataset._getExtent(c.accessor,c.scale._typeCoercer),g=b._plottableID.toString()+"_"+d;0!==f.length&&b._isAnchored?c.scale._updateExtent(g,a,f):c.scale._removeExtent(g,a)})},c.prototype.animator=function(a,b){return void 0===b?this._animators[a]:(this._animators[a]=b,this)},c.prototype.datasetOrder=function(b){function c(b,c){var d=a._Util.Methods.intersection(d3.set(b),d3.set(c)),e=d.size();return e===b.length&&e===c.length}return void 0===b?this._datasetKeysInOrder:(c(b,this._datasetKeysInOrder)?(this._datasetKeysInOrder=b,this._onDatasetUpdate()):a._Util.Methods.warn("Attempted to change datasetOrder, but new order is not permutation of old. Ignoring."),this)},c.prototype.removeDataset=function(b){var c;if("string"==typeof b)c=b;else if(b instanceof a.Dataset||b instanceof Array){var d=b instanceof a.Dataset?this.datasets():this.datasets().map(function(a){return a.data()}),e=d.indexOf(b);-1!==e&&(c=this._datasetKeysInOrder[e])}return this._removeDataset(c)},c.prototype._removeDataset=function(a){if(null!=a&&this._key2DatasetDrawerKey.has(a)){var b=this._key2DatasetDrawerKey.get(a);b.drawer.remove();var c=d3.values(this._projectors),d=this._plottableID.toString()+"_"+a;c.forEach(function(a){null!=a.scale&&a.scale._removeExtent(d,a.attribute)}),b.dataset.broadcaster.deregisterListener(this),this._datasetKeysInOrder.splice(this._datasetKeysInOrder.indexOf(a),1),this._key2DatasetDrawerKey.remove(a),this._onDatasetUpdate()}return this},c.prototype.datasets=function(){var a=this;return this._datasetKeysInOrder.map(function(b){return a._key2DatasetDrawerKey.get(b).dataset})},c.prototype._getDrawersInOrder=function(){var a=this;return this._datasetKeysInOrder.map(function(b){return a._key2DatasetDrawerKey.get(b).drawer})},c.prototype._generateDrawSteps=function(){return[{attrToProjector:this._generateAttrToProjector(),animator:new a.Animator.Null}]},c.prototype._additionalPaint=function(){},c.prototype._getDataToDraw=function(){var a=this,b=d3.map();return this._datasetKeysInOrder.forEach(function(c){b.set(c,a._key2DatasetDrawerKey.get(c).dataset.data())}),b},c.prototype.paint=function(){var b=this._generateDrawSteps(),c=this._getDataToDraw(),d=this._getDrawersInOrder(),e=this._datasetKeysInOrder.map(function(a,e){return d[e].draw(c.get(a),b)}),f=a._Util.Methods.max(e,0);this._additionalPaint(f)},c}(a.Component.AbstractComponent);b.AbstractPlot=c}(a.Plot||(a.Plot={}));a.Plot}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(){b.call(this),this.classed("pie-plot",!0)}return __extends(c,b),c.prototype._computeLayout=function(a,c,d,e){b.prototype._computeLayout.call(this,a,c,d,e),this._renderArea.attr("transform","translate("+this.width()/2+","+this.height()/2+")")},c.prototype._addDataset=function(c,d){return 1===this._datasetKeysInOrder.length?void a._Util.Methods.warn("Only one dataset is supported in Pie plots"):void b.prototype._addDataset.call(this,c,d)},c.prototype._generateAttrToProjector=function(){var a=b.prototype._generateAttrToProjector.call(this);a["inner-radius"]=a["inner-radius"]||d3.functor(0),a["outer-radius"]=a["outer-radius"]||d3.functor(Math.min(this.width(),this.height())/2),null==a.fill&&(a.fill=function(a,b){return c.DEFAULT_COLOR_SCALE.scale(String(b))});var d=function(a){return a.value},e=this._projectors.value;return a.value=e?e.accessor:d,a},c.prototype._getDrawer=function(b){return new a._Drawer.Arc(b).setClass("arc")},c.DEFAULT_COLOR_SCALE=new a.Scale.Color,c}(b.AbstractPlot);b.Pie=c}(a.Plot||(a.Plot={}));a.Plot}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(a,c){if(b.call(this),null==a||null==c)throw new Error("XYPlots require an xScale and yScale");this.classed("xy-plot",!0),this.project("x","x",a),this.project("y","y",c)}return __extends(c,b),c.prototype.project=function(a,c,d){return"x"===a&&d&&(this._xScale=d,this._updateXDomainer()),"y"===a&&d&&(this._yScale=d,this._updateYDomainer()),b.prototype.project.call(this,a,c,d),this},c.prototype._computeLayout=function(a,c,d,e){b.prototype._computeLayout.call(this,a,c,d,e),this._xScale.range([0,this.width()]),this._yScale.range([this.height(),0])},c.prototype._updateXDomainer=function(){if(this._xScale instanceof a.Scale.AbstractQuantitative){var b=this._xScale;b._userSetDomainer||b.domainer().pad().nice()}},c.prototype._updateYDomainer=function(){if(this._yScale instanceof a.Scale.AbstractQuantitative){var b=this._yScale;b._userSetDomainer||b.domainer().pad().nice()}},c}(b.AbstractPlot);b.AbstractXYPlot=c}(a.Plot||(a.Plot={}));a.Plot}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(c,d){b.call(this,c,d),this.closeDetectionRadius=5,this.classed("scatter-plot",!0),this.project("r",3),this.project("opacity",.6),this.project("fill",function(){return a.Core.Colors.INDIGO}),this._animators["circles-reset"]=new a.Animator.Null,this._animators.circles=(new a.Animator.Base).duration(250).delay(5)}return __extends(c,b),c.prototype.project=function(a,c,d){return a="cx"===a?"x":a,a="cy"===a?"y":a,b.prototype.project.call(this,a,c,d),this},c.prototype._getDrawer=function(b){return new a._Drawer.Element(b).svgElement("circle")},c.prototype._generateAttrToProjector=function(){var a=b.prototype._generateAttrToProjector.call(this);return a.cx=a.x,delete a.x,a.cy=a.y,delete a.y,a},c.prototype._generateDrawSteps=function(){var a=[];if(this._dataChanged){var b=this._generateAttrToProjector();b.r=function(){return 0},a.push({attrToProjector:b,animator:this._getAnimator("circles-reset")})}return a.push({attrToProjector:this._generateAttrToProjector(),animator:this._getAnimator("circles")}),a},c.prototype._getClosestStruckPoint=function(a,b){var c,d=this._getDrawersInOrder(),e=this._generateAttrToProjector(),f=function(b,c){var d=e.cx(b,c)-a.x,f=e.cy(b,c)-a.y;return d*d+f*f},g=!1,h=b*b;d.forEach(function(a){a._getDrawSelection().each(function(a,b){var d=f(a,b),i=e.r(a,b);i*i>d?((!g||h>d)&&(c=this,h=d),g=!0):!g&&h>d&&(c=this,h=d)})});var i=d3.select(c);return{selection:c?i:null,data:c?i.data():null}},c.prototype._hoverOverComponent=function(){},c.prototype._hoverOutComponent=function(){},c.prototype._doHover=function(a){return this._getClosestStruckPoint(a,this.closeDetectionRadius)},c}(b.AbstractXYPlot);b.Scatter=c}(a.Plot||(a.Plot={}));a.Plot}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(c,d,e){b.call(this,c,d),this._animators={cells:new a.Animator.Null},this.classed("grid-plot",!0),this._xScale.rangeType("bands",0,0),this._yScale.rangeType("bands",0,0),this._colorScale=e,this.project("fill","value",e),this._animators.cells=new a.Animator.Null}return __extends(c,b),c.prototype._addDataset=function(c,d){return 1===this._datasetKeysInOrder.length?void a._Util.Methods.warn("Only one dataset is supported in Grid plots"):void b.prototype._addDataset.call(this,c,d)},c.prototype._getDrawer=function(b){return new a._Drawer.Element(b).svgElement("rect")},c.prototype.project=function(a,c,d){return b.prototype.project.call(this,a,c,d),"fill"===a&&(this._colorScale=this._projectors.fill.scale),this},c.prototype._generateAttrToProjector=function(){var a=b.prototype._generateAttrToProjector.call(this),c=this._xScale.rangeBand(),d=this._yScale.rangeBand();return a.width=function(){return c},a.height=function(){return d},a},c.prototype._generateDrawSteps=function(){return[{attrToProjector:this._generateAttrToProjector(),animator:this._getAnimator("cells")}]},c}(b.AbstractXYPlot);b.Grid=c}(a.Plot||(a.Plot={}));a.Plot}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(c,d){b.call(this,c,d),this._baselineValue=0,this._barAlignmentFactor=.5,this._barLabelFormatter=a.Formatters.identity(),this._barLabelsEnabled=!1,this._hoverMode="point",this.hideBarsIfAnyAreTooWide=!0,this.classed("bar-plot",!0),this.project("fill",function(){return a.Core.Colors.INDIGO}),this._animators["bars-reset"]=new a.Animator.Null,this._animators.bars=new a.Animator.Base,this._animators.baseline=new a.Animator.Null,this.baseline(this._baselineValue)}return __extends(c,b),c.prototype._getDrawer=function(b){return new a._Drawer.Rect(b,this._isVertical)},c.prototype._setup=function(){b.prototype._setup.call(this),this._baseline=this._renderArea.append("line").classed("baseline",!0)},c.prototype.baseline=function(a){return this._baselineValue=a,this._updateXDomainer(),this._updateYDomainer(),this._render(),this},c.prototype.barAlignment=function(a){var b=a.toLowerCase(),c=this.constructor._BarAlignmentToFactor;if(void 0===c[b])throw new Error("unsupported bar alignment");return this._barAlignmentFactor=c[b],this._render(),this},c.prototype.parseExtent=function(a){if("number"==typeof a)return{min:a,max:a};if(a instanceof Object&&"min"in a&&"max"in a)return a;throw new Error("input '"+a+"' can't be parsed as an Extent")},c.prototype.barLabelsEnabled=function(a){return void 0===a?this._barLabelsEnabled:(this._barLabelsEnabled=a,this._render(),this)},c.prototype.barLabelFormatter=function(a){return null==a?this._barLabelFormatter:(this._barLabelFormatter=a,this._render(),this)},c.prototype.selectBar=function(a,b,c){if(void 0===c&&(c=!0),!this._isSetup)return null;var d=[],e=this.parseExtent(a),f=this.parseExtent(b),g=.5;if(this._getDrawersInOrder().forEach(function(a){a._renderArea.selectAll("rect").each(function(){var a=this.getBBox();a.x+a.width>=e.min-g&&a.x<=e.max+g&&a.y+a.height>=f.min-g&&a.y<=f.max+g&&d.push(this)})}),d.length>0){var h=d3.selectAll(d);return h.classed("selected",c),h}return null},c.prototype.deselectAll=function(){return this._isSetup&&this._getDrawersInOrder().forEach(function(a){return a._renderArea.selectAll("rect").classed("selected",!1)}),this},c.prototype._updateDomainer=function(b){if(b instanceof a.Scale.AbstractQuantitative){var c=b;c._userSetDomainer||(null!=this._baselineValue?c.domainer().addPaddingException(this._baselineValue,"BAR_PLOT+"+this._plottableID).addIncludedValue(this._baselineValue,"BAR_PLOT+"+this._plottableID):c.domainer().removePaddingException("BAR_PLOT+"+this._plottableID).removeIncludedValue("BAR_PLOT+"+this._plottableID),c.domainer().pad()),c._autoDomainIfAutomaticMode()}},c.prototype._updateYDomainer=function(){this._isVertical?this._updateDomainer(this._yScale):b.prototype._updateYDomainer.call(this)},c.prototype._updateXDomainer=function(){this._isVertical?b.prototype._updateXDomainer.call(this):this._updateDomainer(this._xScale)},c.prototype._additionalPaint=function(b){var c=this,d=this._isVertical?this._yScale:this._xScale,e=d.scale(this._baselineValue),f={x1:this._isVertical?0:e,y1:this._isVertical?e:0,x2:this._isVertical?this.width():e,y2:this._isVertical?e:this.height()};this._getAnimator("baseline").animate(this._baseline,f);var g=this._getDrawersInOrder();g.forEach(function(a){return a.removeLabels()}),this._barLabelsEnabled&&a._Util.Methods.setTimeout(function(){return c._drawLabels()},b)},c.prototype._drawLabels=function(){var a=this._getDrawersInOrder(),b=this._generateAttrToProjector(),c=this._getDataToDraw();this._datasetKeysInOrder.forEach(function(d,e){return a[e].drawText(c.get(d),b)}),this.hideBarsIfAnyAreTooWide&&a.some(function(a){return a._someLabelsTooWide})&&a.forEach(function(a){return a.removeLabels()})},c.prototype._generateDrawSteps=function(){var a=[];if(this._dataChanged&&this._animate){var b=this._generateAttrToProjector(),c=this._isVertical?this._yScale:this._xScale,d=c.scale(this._baselineValue),e=this._isVertical?"y":"x",f=this._isVertical?"height":"width";b[e]=function(){return d},b[f]=function(){return 0},a.push({attrToProjector:b,animator:this._getAnimator("bars-reset")})}return a.push({attrToProjector:this._generateAttrToProjector(),animator:this._getAnimator("bars")}),a},c.prototype._generateAttrToProjector=function(){var d=this,e=b.prototype._generateAttrToProjector.call(this),f=this._isVertical?this._yScale:this._xScale,g=this._isVertical?this._xScale:this._yScale,h=this._isVertical?"y":"x",i=this._isVertical?"x":"y",j=g instanceof a.Scale.Ordinal&&"bands"===g.rangeType(),k=f.scale(this._baselineValue);if(!e.width){var l=j?g.rangeBand():c.DEFAULT_WIDTH;e.width=function(){return l}}var m=e[i],n=e.width;if(j){var o=g.rangeBand();e[i]=function(a,b){return m(a,b)-n(a,b)/2+o/2}}else e[i]=function(a,b){return m(a,b)-n(a,b)*d._barAlignmentFactor};var p=e[h];e[h]=function(a,b){var c=p(a,b);return c>k?k:c},e.height=function(a,b){return Math.abs(k-p(a,b))};var q=this._projectors[h].accessor;return this.barLabelsEnabled&&this.barLabelFormatter&&(e.label=function(a,b){return d._barLabelFormatter(q(a,b))},e.positive=function(a,b){return p(a,b)<=k}),e},c.prototype.hoverMode=function(a){if(null==a)return this._hoverMode;var b=a.toLowerCase();if("point"!==b&&"line"!==b)throw new Error(a+" is not a valid hover mode");return this._hoverMode=b,this},c.prototype.clearHoverSelection=function(){this._getDrawersInOrder().forEach(function(a){a._renderArea.selectAll("rect").classed("not-hovered hovered",!1)})},c.prototype._hoverOverComponent=function(){},c.prototype._hoverOutComponent=function(){this.clearHoverSelection()},c.prototype._doHover=function(a){var b=a.x,c=a.y;if("line"===this._hoverMode){var d={min:-1/0,max:1/0};this._isVertical?c=d:b=d}var e=this.selectBar(b,c,!1);return e?(this._getDrawersInOrder().forEach(function(a){a._renderArea.selectAll("rect").classed({hovered:!1,"not-hovered":!0})}),e.classed({hovered:!0,"not-hovered":!1})):this.clearHoverSelection(),{data:e?e.data():null,selection:e}},c._BarAlignmentToFactor={},c.DEFAULT_WIDTH=10,c}(b.AbstractXYPlot);b.AbstractBarPlot=c}(a.Plot||(a.Plot={}));a.Plot}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(a){var b=function(a){function b(b,c){this._isVertical=!0,a.call(this,b,c)}return __extends(b,a),b.prototype._updateYDomainer=function(){this._updateDomainer(this._yScale)},b._BarAlignmentToFactor={left:0,center:.5,right:1},b}(a.AbstractBarPlot);a.VerticalBar=b}(a.Plot||(a.Plot={}));a.Plot}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(a){var b=function(a){function b(b,c){a.call(this,b,c)}return __extends(b,a),b.prototype._updateXDomainer=function(){this._updateDomainer(this._xScale)},b.prototype._generateAttrToProjector=function(){var b=a.prototype._generateAttrToProjector.call(this),c=b.width;return b.width=b.height,b.height=c,b},b._BarAlignmentToFactor={top:0,center:.5,bottom:1},b}(a.AbstractBarPlot);a.HorizontalBar=b}(a.Plot||(a.Plot={}));a.Plot}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(c,d){b.call(this,c,d),this.classed("line-plot",!0),this.project("stroke",function(){return a.Core.Colors.INDIGO}),this.project("stroke-width",function(){return"2px"}),this._animators.reset=new a.Animator.Null,this._animators.main=(new a.Animator.Base).duration(600).easing("exp-in-out")}return __extends(c,b),c.prototype._rejectNullsAndNaNs=function(a,b,c){var d=c(a,b);return null!=d&&d===d},c.prototype._getDrawer=function(b){return new a._Drawer.Line(b)},c.prototype._getResetYFunction=function(){var a=this._yScale.domain(),b=Math.max(a[0],a[1]),c=Math.min(a[0],a[1]),d=0>b&&b||c>0&&c||0,e=this._yScale.scale(d);return function(){return e}},c.prototype._generateDrawSteps=function(){var a=[];if(this._dataChanged){var b=this._generateAttrToProjector();b.y=this._getResetYFunction(),a.push({attrToProjector:b,animator:this._getAnimator("reset")})}return a.push({attrToProjector:this._generateAttrToProjector(),animator:this._getAnimator("main")}),a},c.prototype._generateAttrToProjector=function(){var a=this,c=b.prototype._generateAttrToProjector.call(this),d=this._wholeDatumAttributes(),e=function(a){return-1===d.indexOf(a)},f=d3.keys(c).filter(e);f.forEach(function(a){var b=c[a];c[a]=function(a,c){return a.length>0?b(a[0],c):null}});var g=c.x,h=c.y;return c.defined=function(b,c){return a._rejectNullsAndNaNs(b,c,g)&&a._rejectNullsAndNaNs(b,c,h)},c},c.prototype._wholeDatumAttributes=function(){return["x","y"]},c}(b.AbstractXYPlot);b.Line=c}(a.Plot||(a.Plot={}));a.Plot}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(c,d){b.call(this,c,d),this.classed("area-plot",!0),this.project("y0",0,d),this.project("fill",function(){return a.Core.Colors.INDIGO}),this.project("fill-opacity",function(){return.25}),this.project("stroke",function(){return a.Core.Colors.INDIGO}),this._animators.reset=new a.Animator.Null,this._animators.main=(new a.Animator.Base).duration(600).easing("exp-in-out")}return __extends(c,b),c.prototype._onDatasetUpdate=function(){b.prototype._onDatasetUpdate.call(this),null!=this._yScale&&this._updateYDomainer()},c.prototype._getDrawer=function(b){return new a._Drawer.Area(b)},c.prototype._updateYDomainer=function(){var c=this;b.prototype._updateYDomainer.call(this);var d,e=this._projectors.y0,f=e&&e.accessor;if(null!=f){var g=this.datasets().map(function(a){return a._getExtent(f,c._yScale._typeCoercer)}),h=a._Util.Methods.flatten(g),i=a._Util.Methods.uniq(h);1===i.length&&(d=i[0])}this._yScale._userSetDomainer||(null!=d?this._yScale.domainer().addPaddingException(d,"AREA_PLOT+"+this._plottableID):this._yScale.domainer().removePaddingException("AREA_PLOT+"+this._plottableID),this._yScale._autoDomainIfAutomaticMode())},c.prototype.project=function(a,c,d){return b.prototype.project.call(this,a,c,d),"y0"===a&&this._updateYDomainer(),this},c.prototype._getResetYFunction=function(){return this._generateAttrToProjector().y0},c.prototype._wholeDatumAttributes=function(){var a=b.prototype._wholeDatumAttributes.call(this);return a.push("y0"),a},c}(b.Line);b.Area=c}(a.Plot||(a.Plot={}));a.Plot}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(c,d,e){void 0===e&&(e=!0),this._isVertical=e,b.call(this,c,d),this.innerScale=new a.Scale.Ordinal}return __extends(c,b),c.prototype._generateAttrToProjector=function(){var a=this,c=b.prototype._generateAttrToProjector.call(this),d=c.width;this.innerScale.range([0,d(null,0)]);var e=function(){return a.innerScale.rangeBand()},f=c.height;c.width=this._isVertical?e:f,c.height=this._isVertical?f:e;var g=function(a){return a._PLOTTABLE_PROTECTED_FIELD_POSITION};return c.x=this._isVertical?g:c.x,c.y=this._isVertical?c.y:g,c},c.prototype._getDataToDraw=function(){var a=this,b=this._isVertical?this._projectors.x.accessor:this._projectors.y.accessor;this.innerScale.domain(this._datasetKeysInOrder);var c=d3.map();return this._datasetKeysInOrder.forEach(function(d){var e=a._key2DatasetDrawerKey.get(d).dataset.data();c.set(d,e.map(function(c,e){var f=b(c,e),g=a._isVertical?a._xScale:a._yScale;return c._PLOTTABLE_PROTECTED_FIELD_POSITION=g.scale(f)+a.innerScale.scale(d),c}))}),c},c}(b.AbstractBarPlot);b.ClusteredBar=c}(a.Plot||(a.Plot={}));a.Plot}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(){b.apply(this,arguments),this.stackedExtent=[0,0]}return __extends(c,b),c.prototype.project=function(a,c,d){return b.prototype.project.call(this,a,c,d),this._projectors.x&&this._projectors.y&&("x"===a||"y"===a)&&this._updateStackOffsets(),this},c.prototype._onDatasetUpdate=function(){b.prototype._onDatasetUpdate.call(this),this._datasetKeysInOrder&&this._projectors.x&&this._projectors.y&&this._updateStackOffsets()},c.prototype._updateStackOffsets=function(){var b=this._generateDefaultMapArray(),c=this._getDomainKeys(),d=b.map(function(b){return a._Util.Methods.populateMap(c,function(a){return{key:a,value:Math.max(0,b.get(a).value)}})}),e=b.map(function(b){return a._Util.Methods.populateMap(c,function(a){return{key:a,value:Math.min(b.get(a).value,0)}})});this._setDatasetStackOffsets(this._stack(d),this._stack(e)),this._updateStackExtents()},c.prototype._updateStackExtents=function(){var b=this.datasets(),c=this._valueAccessor(),d=a._Util.Methods.max(b,function(b){return a._Util.Methods.max(b.data(),function(a){return+c(a)+a._PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET},0)},0),e=a._Util.Methods.min(b,function(b){return a._Util.Methods.min(b.data(),function(a){return+c(a)+a._PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET},0)},0);this.stackedExtent=[Math.min(e,0),Math.max(0,d)]},c.prototype._stack=function(a){var b=this;if(0===a.length)return a;var c=function(a,b){a.offset=b};return d3.layout.stack().x(function(a){return a.key}).y(function(a){return+a.value}).values(function(a){return b._getDomainKeys().map(function(b){return a.get(b)})}).out(c)(a),a},c.prototype._setDatasetStackOffsets=function(a,b){var c=this._keyAccessor(),d=this._valueAccessor();this.datasets().forEach(function(e,f){var g=a[f],h=b[f],i=e.data().every(function(a){return d(a)<=0});e.data().forEach(function(a){var b=g.get(c(a)).offset,e=h.get(c(a)).offset,f=d(a);a._PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET=0===f?i?e:b:f>0?b:e})})},c.prototype._getDomainKeys=function(){var a=this._keyAccessor(),b=d3.set(),c=this.datasets();return c.forEach(function(c){c.data().forEach(function(c){b.add(a(c))})}),b.values()},c.prototype._generateDefaultMapArray=function(){var b=this._keyAccessor(),c=this._valueAccessor(),d=this.datasets(),e=this._getDomainKeys(),f=d.map(function(){return a._Util.Methods.populateMap(e,function(a){return{key:a,value:0}})});return d.forEach(function(a,d){a.data().forEach(function(a){var e=b(a),g=c(a);f[d].set(e,{key:e,value:g})})}),f},c.prototype._updateScaleExtents=function(){b.prototype._updateScaleExtents.call(this);var a=this._isVertical?this._yScale:this._xScale;a&&(this._isAnchored&&this.stackedExtent.length>0?a._updateExtent(this._plottableID.toString(),"_PLOTTABLE_PROTECTED_FIELD_STACK_EXTENT",this.stackedExtent):a._removeExtent(this._plottableID.toString(),"_PLOTTABLE_PROTECTED_FIELD_STACK_EXTENT"))},c.prototype._keyAccessor=function(){return this._isVertical?this._projectors.x.accessor:this._projectors.y.accessor},c.prototype._valueAccessor=function(){return this._isVertical?this._projectors.y.accessor:this._projectors.x.accessor},c}(b.AbstractXYPlot);b.AbstractStacked=c}(a.Plot||(a.Plot={}));a.Plot}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(c){function d(b,d){c.call(this,b,d),this._baselineValue=0,this.classed("area-plot",!0),this.project("fill",function(){return a.Core.Colors.INDIGO}),this._isVertical=!0}return __extends(d,c),d.prototype._getDrawer=function(b){return new a._Drawer.Area(b).drawLine(!1)},d.prototype._setup=function(){c.prototype._setup.call(this),this._baseline=this._renderArea.append("line").classed("baseline",!0)},d.prototype._updateStackOffsets=function(){var b=this._getDomainKeys(),d=this._isVertical?this._projectors.x.accessor:this._projectors.y.accessor,e=this.datasets().map(function(a){return d3.set(a.data().map(function(a,b){return d(a,b).toString()})).values()});e.some(function(a){return a.length!==b.length})&&a._Util.Methods.warn("the domains across the datasets are not the same. Plot may produce unintended behavior."),c.prototype._updateStackOffsets.call(this)},d.prototype._additionalPaint=function(){var a=this._yScale.scale(this._baselineValue),b={x1:0,y1:a,x2:this.width(),y2:a};this._getAnimator("baseline").animate(this._baseline,b)},d.prototype._updateYDomainer=function(){c.prototype._updateYDomainer.call(this);var a=this._yScale;a._userSetDomainer||(a.domainer().addPaddingException(0,"STACKED_AREA_PLOT+"+this._plottableID),a._autoDomainIfAutomaticMode())},d.prototype._onDatasetUpdate=function(){c.prototype._onDatasetUpdate.call(this),b.Area.prototype._onDatasetUpdate.apply(this)},d.prototype._generateAttrToProjector=function(){var a=this,b=c.prototype._generateAttrToProjector.call(this),d=this._projectors.y.accessor;b.y=function(b){return a._yScale.scale(+d(b)+b._PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET)},b.y0=function(b){return a._yScale.scale(b._PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET)};var e=b.fill;return b.fill=function(a,b){return a&&a[0]?e(a[0],b):null},b},d}(b.AbstractStacked);b.StackedArea=c}(a.Plot||(a.Plot={}));a.Plot}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(c){function d(b,d,e){void 0===e&&(e=!0),this._isVertical=e,this._baselineValue=0,c.call(this,b,d),this.classed("bar-plot",!0),this.project("fill",function(){return a.Core.Colors.INDIGO}),this.baseline(this._baselineValue),this._isVertical=e}return __extends(d,c),d.prototype._getAnimator=function(b){if(this._animate&&this._animateOnNextRender){if(this._animators[b])return this._animators[b];if("stacked-bar"===b){var c=this._isVertical?this._yScale:this._xScale,d=c.scale(this._baselineValue);return new a.Animator.MovingRect(d,this._isVertical)}}return new a.Animator.Null},d.prototype._generateAttrToProjector=function(){var a=this,c=b.AbstractBarPlot.prototype._generateAttrToProjector.apply(this),d=this._isVertical?"y":"x",e=this._isVertical?this._yScale:this._xScale,f=this._projectors[d].accessor,g=function(a){return e.scale(a._PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET)},h=function(a){return e.scale(+f(a)+a._PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET)},i=function(a){return Math.abs(h(a)-g(a))},j=c.width;c.height=this._isVertical?i:j,c.width=this._isVertical?j:i;var k=function(a){return+f(a)<0?g(a):h(a)};return c[d]=function(b){return a._isVertical?k(b):k(b)-i(b)},c},d.prototype._generateDrawSteps=function(){return[{attrToProjector:this._generateAttrToProjector(),animator:this._getAnimator("stacked-bar")}]},d.prototype.project=function(a,d,e){return c.prototype.project.call(this,a,d,e),b.AbstractStacked.prototype.project.apply(this,[a,d,e]),this},d.prototype._onDatasetUpdate=function(){return c.prototype._onDatasetUpdate.call(this),b.AbstractStacked.prototype._onDatasetUpdate.apply(this),this},d.prototype._updateStackOffsets=function(){b.AbstractStacked.prototype._updateStackOffsets.call(this)},d.prototype._updateStackExtents=function(){b.AbstractStacked.prototype._updateStackExtents.call(this)},d.prototype._stack=function(a){return b.AbstractStacked.prototype._stack.call(this,a)},d.prototype._setDatasetStackOffsets=function(a,c){b.AbstractStacked.prototype._setDatasetStackOffsets.call(this,a,c)},d.prototype._getDomainKeys=function(){return b.AbstractStacked.prototype._getDomainKeys.call(this)},d.prototype._generateDefaultMapArray=function(){return b.AbstractStacked.prototype._generateDefaultMapArray.call(this)},d.prototype._updateScaleExtents=function(){b.AbstractStacked.prototype._updateScaleExtents.call(this)},d.prototype._keyAccessor=function(){return b.AbstractStacked.prototype._keyAccessor.call(this)},d.prototype._valueAccessor=function(){return b.AbstractStacked.prototype._valueAccessor.call(this)},d}(b.AbstractBarPlot);b.StackedBar=c}(a.Plot||(a.Plot={}));a.Plot}(Plottable||(Plottable={}));var Plottable;!function(a){!function(a){var b=function(){function a(){}return a.prototype.getTiming=function(){return 0},a.prototype.animate=function(a,b){return a.attr(b)},a}();a.Null=b}(a.Animator||(a.Animator={}));a.Animator}(Plottable||(Plottable={}));var Plottable;!function(a){!function(a){var b=function(){function a(){this._duration=a.DEFAULT_DURATION_MILLISECONDS,this._delay=a.DEFAULT_DELAY_MILLISECONDS,this._easing=a.DEFAULT_EASING,this._maxIterativeDelay=a.DEFAULT_MAX_ITERATIVE_DELAY_MILLISECONDS,this._maxTotalDuration=a.DEFAULT_MAX_TOTAL_DURATION_MILLISECONDS}return a.prototype.getTiming=function(a){var b=Math.max(this.maxTotalDuration()-this.duration(),0),c=Math.min(this.maxIterativeDelay(),b/Math.max(a-1,1)),d=c*a+this.delay()+this.duration();return d},a.prototype.animate=function(a,b){var c=this,d=a[0].length,e=Math.max(this.maxTotalDuration()-this.duration(),0),f=Math.min(this.maxIterativeDelay(),e/Math.max(d-1,1));return a.transition().ease(this.easing()).duration(this.duration()).delay(function(a,b){return c.delay()+f*b}).attr(b)},a.prototype.duration=function(a){return null==a?this._duration:(this._duration=a,this)},a.prototype.delay=function(a){return null==a?this._delay:(this._delay=a,this)},a.prototype.easing=function(a){return null==a?this._easing:(this._easing=a,this) +},a.prototype.maxIterativeDelay=function(a){return null==a?this._maxIterativeDelay:(this._maxIterativeDelay=a,this)},a.prototype.maxTotalDuration=function(a){return null==a?this._maxTotalDuration:(this._maxTotalDuration=a,this)},a.DEFAULT_DURATION_MILLISECONDS=300,a.DEFAULT_DELAY_MILLISECONDS=0,a.DEFAULT_MAX_ITERATIVE_DELAY_MILLISECONDS=15,a.DEFAULT_MAX_TOTAL_DURATION_MILLISECONDS=600,a.DEFAULT_EASING="exp-out",a}();a.Base=b}(a.Animator||(a.Animator={}));a.Animator}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(a){var b=function(a){function b(b,c){void 0===b&&(b=!0),void 0===c&&(c=!1),a.call(this),this.isVertical=b,this.isReverse=c}return __extends(b,a),b.prototype.animate=function(c,d){var e={};return b.ANIMATED_ATTRIBUTES.forEach(function(a){return e[a]=d[a]}),e[this.getMovingAttr()]=this._startMovingProjector(d),e[this.getGrowingAttr()]=function(){return 0},c.attr(e),a.prototype.animate.call(this,c,d)},b.prototype._startMovingProjector=function(a){if(this.isVertical===this.isReverse)return a[this.getMovingAttr()];var b=a[this.getMovingAttr()],c=a[this.getGrowingAttr()];return function(a,d){return b(a,d)+c(a,d)}},b.prototype.getGrowingAttr=function(){return this.isVertical?"height":"width"},b.prototype.getMovingAttr=function(){return this.isVertical?"y":"x"},b.ANIMATED_ATTRIBUTES=["height","width","x","y","fill"],b}(a.Base);a.Rect=b}(a.Animator||(a.Animator={}));a.Animator}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(a){var b=function(a){function b(b,c){void 0===c&&(c=!0),a.call(this,c),this.startPixelValue=b}return __extends(b,a),b.prototype._startMovingProjector=function(){return d3.functor(this.startPixelValue)},b}(a.Rect);a.MovingRect=b}(a.Animator||(a.Animator={}));a.Animator}(Plottable||(Plottable={}));var Plottable;!function(a){!function(a){!function(a){function b(){e||(d3.select(document).on("keydown",d),e=!0)}function c(a,c){e||b(),null==f[a]&&(f[a]=[]),f[a].push(c)}function d(){null!=f[d3.event.keyCode]&&f[d3.event.keyCode].forEach(function(a){a(d3.event)})}var e=!1,f=[];a.initialize=b,a.addCallback=c}(a.KeyEventListener||(a.KeyEventListener={}));a.KeyEventListener}(a.Core||(a.Core={}));a.Core}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(a){function b(){a.apply(this,arguments)}return __extends(b,a),b.prototype._anchor=function(a,b){this._componentToListenTo=a,this._hitBox=b},b}(a.Core.PlottableObject);b.AbstractInteraction=c}(a.Interaction||(a.Interaction={}));a.Interaction}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(a){var b=function(a){function b(){a.apply(this,arguments)}return __extends(b,a),b.prototype._anchor=function(b,c){var d=this;a.prototype._anchor.call(this,b,c),c.on(this._listenTo(),function(){var a=d3.mouse(c.node()),b=a[0],e=a[1];d._callback({x:b,y:e})})},b.prototype._listenTo=function(){return"click"},b.prototype.callback=function(a){return this._callback=a,this},b}(a.AbstractInteraction);a.Click=b;var c=function(a){function b(){a.apply(this,arguments)}return __extends(b,a),b.prototype._listenTo=function(){return"dblclick"},b}(b);a.DoubleClick=c}(a.Interaction||(a.Interaction={}));a.Interaction}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(a){b.call(this),this.activated=!1,this.keyCode=a}return __extends(c,b),c.prototype._anchor=function(c,d){var e=this;b.prototype._anchor.call(this,c,d),d.on("mouseover",function(){e.activated=!0}),d.on("mouseout",function(){e.activated=!1}),a.Core.KeyEventListener.addCallback(this.keyCode,function(){e.activated&&null!=e._callback&&e._callback()})},c.prototype.callback=function(a){return this._callback=a,this},c}(b.AbstractInteraction);b.Key=c}(a.Interaction||(a.Interaction={}));a.Interaction}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(c,d){var e=this;b.call(this),null==c&&(c=new a.Scale.Linear),null==d&&(d=new a.Scale.Linear),this._xScale=c,this._yScale=d,this.zoom=d3.behavior.zoom(),this.zoom.x(this._xScale._d3Scale),this.zoom.y(this._yScale._d3Scale),this.zoom.on("zoom",function(){return e.rerenderZoomed()})}return __extends(c,b),c.prototype.resetZoom=function(){var a=this;this.zoom=d3.behavior.zoom(),this.zoom.x(this._xScale._d3Scale),this.zoom.y(this._yScale._d3Scale),this.zoom.on("zoom",function(){return a.rerenderZoomed()}),this.zoom(this._hitBox)},c.prototype._anchor=function(a,c){b.prototype._anchor.call(this,a,c),this.zoom(c)},c.prototype.rerenderZoomed=function(){var a=this._xScale._d3Scale.domain(),b=this._yScale._d3Scale.domain();this._xScale.domain(a),this._yScale.domain(b)},c}(b.AbstractInteraction);b.PanZoom=c}(a.Interaction||(a.Interaction={}));a.Interaction}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(){b.apply(this,arguments),this.currentBar=null,this._hoverMode="point"}return __extends(c,b),c.prototype._anchor=function(c,d){var e=this;b.prototype._anchor.call(this,c,d),a._Util.Methods.warn("Interaction.BarHover is deprecated; please use Interaction.Hover instead"),this.plotIsVertical=this._componentToListenTo._isVertical,this.dispatcher=new a.Dispatcher.Mouse(this._hitBox),this.dispatcher.mousemove(function(a){var b=e.getHoveredBar(a);if(null==b)e._hoverOut();else{if(null!=e.currentBar){if(e.currentBar.node()===b.node())return;e._hoverOut()}e.getBars().classed("not-hovered",!0).classed("hovered",!1),b.classed("not-hovered",!1).classed("hovered",!0),null!=e.hoverCallback&&e.hoverCallback(b.data()[0],b)}e.currentBar=b}),this.dispatcher.mouseout(function(){return e._hoverOut()}),this.dispatcher.connect()},c.prototype.getBars=function(){return this._componentToListenTo._renderArea.selectAll("rect")},c.prototype._hoverOut=function(){this.getBars().classed("not-hovered hovered",!1),null!=this.unhoverCallback&&null!=this.currentBar&&this.unhoverCallback(this.currentBar.data()[0],this.currentBar),this.currentBar=null},c.prototype.getHoveredBar=function(a){if("point"===this._hoverMode)return this._componentToListenTo.selectBar(a.x,a.y,!1);var b={min:-1/0,max:1/0};return this.plotIsVertical?this._componentToListenTo.selectBar(a.x,b,!1):this._componentToListenTo.selectBar(b,a.y,!1)},c.prototype.hoverMode=function(a){if(null==a)return this._hoverMode;var b=a.toLowerCase();if("point"!==b&&"line"!==b)throw new Error(a+" is not a valid hover mode for Interaction.BarHover");return this._hoverMode=b,this},c.prototype.onHover=function(a){return this.hoverCallback=a,this},c.prototype.onUnhover=function(a){return this.unhoverCallback=a,this},c}(b.AbstractInteraction);b.BarHover=c}(a.Interaction||(a.Interaction={}));a.Interaction}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(a){var b=function(a){function b(){var b=this;a.call(this),this.dragInitialized=!1,this._origin=[0,0],this._location=[0,0],this.dragBehavior=d3.behavior.drag(),this.dragBehavior.on("dragstart",function(){return b._dragstart()}),this.dragBehavior.on("drag",function(){return b._drag()}),this.dragBehavior.on("dragend",function(){return b._dragend()})}return __extends(b,a),b.prototype.dragstart=function(a){return void 0===a?this.ondragstart:(this.ondragstart=a,this)},b.prototype.drag=function(a){return void 0===a?this.ondrag:(this.ondrag=a,this)},b.prototype.dragend=function(a){return void 0===a?this.ondragend:(this.ondragend=a,this)},b.prototype._dragstart=function(){var a=this._componentToListenTo.width(),b=this._componentToListenTo.height(),c=function(a,b){return function(c){return Math.min(Math.max(c,a),b)}};this.constrainX=c(0,a),this.constrainY=c(0,b)},b.prototype._doDragstart=function(){null!=this.ondragstart&&this.ondragstart({x:this._origin[0],y:this._origin[1]})},b.prototype._drag=function(){this.dragInitialized||(this._origin=[d3.event.x,d3.event.y],this.dragInitialized=!0,this._doDragstart()),this._location=[this.constrainX(d3.event.x),this.constrainY(d3.event.y)],this._doDrag()},b.prototype._doDrag=function(){if(null!=this.ondrag){var a={x:this._origin[0],y:this._origin[1]},b={x:this._location[0],y:this._location[1]};this.ondrag(a,b)}},b.prototype._dragend=function(){this.dragInitialized&&(this.dragInitialized=!1,this._doDragend())},b.prototype._doDragend=function(){if(null!=this.ondragend){var a={x:this._origin[0],y:this._origin[1]},b={x:this._location[0],y:this._location[1]};this.ondragend(a,b)}},b.prototype._anchor=function(b,c){return a.prototype._anchor.call(this,b,c),c.call(this.dragBehavior),this},b.prototype.setupZoomCallback=function(a,b){function c(c,g){return null==c||null==g?(f&&(null!=a&&a.domain(d),null!=b&&b.domain(e)),void(f=!f)):(f=!1,null!=a&&a.domain([a.invert(c.x),a.invert(g.x)]),null!=b&&b.domain([b.invert(g.y),b.invert(c.y)]),void this.clearBox())}var d=null!=a?a.domain():null,e=null!=b?b.domain():null,f=!1;return this.drag(c),this.dragend(c),this},b}(a.AbstractInteraction);a.Drag=b}(a.Interaction||(a.Interaction={}));a.Interaction}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(a){var b=function(a){function b(){a.apply(this,arguments),this.boxIsDrawn=!1}return __extends(b,a),b.prototype._dragstart=function(){a.prototype._dragstart.call(this),this.clearBox()},b.prototype.clearBox=function(){return null!=this.dragBox?(this.dragBox.attr("height",0).attr("width",0),this.boxIsDrawn=!1,this):void 0},b.prototype.setBox=function(a,b,c,d){if(null!=this.dragBox){var e=Math.abs(a-b),f=Math.abs(c-d),g=Math.min(a,b),h=Math.min(c,d);return this.dragBox.attr({x:g,y:h,width:e,height:f}),this.boxIsDrawn=e>0&&f>0,this}},b.prototype._anchor=function(c,d){a.prototype._anchor.call(this,c,d);var e=b.CLASS_DRAG_BOX,f=this._componentToListenTo._backgroundContainer;return this.dragBox=f.append("rect").classed(e,!0).attr("x",0).attr("y",0),this},b.CLASS_DRAG_BOX="drag-box",b}(a.Drag);a.DragBox=b}(a.Interaction||(a.Interaction={}));a.Interaction}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(a){var b=function(a){function b(){a.apply(this,arguments)}return __extends(b,a),b.prototype._drag=function(){a.prototype._drag.call(this),this.setBox(this._origin[0],this._location[0])},b.prototype.setBox=function(b,c){return a.prototype.setBox.call(this,b,c,0,this._componentToListenTo.height()),this},b}(a.DragBox);a.XDragBox=b}(a.Interaction||(a.Interaction={}));a.Interaction}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(a){var b=function(a){function b(){a.apply(this,arguments)}return __extends(b,a),b.prototype._drag=function(){a.prototype._drag.call(this),this.setBox(this._origin[0],this._location[0],this._origin[1],this._location[1])},b}(a.DragBox);a.XYDragBox=b}(a.Interaction||(a.Interaction={}));a.Interaction}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(a){var b=function(a){function b(){a.apply(this,arguments)}return __extends(b,a),b.prototype._drag=function(){a.prototype._drag.call(this),this.setBox(this._origin[1],this._location[1])},b.prototype.setBox=function(b,c){return a.prototype.setBox.call(this,0,this._componentToListenTo.width(),b,c),this},b}(a.DragBox);a.YDragBox=b}(a.Interaction||(a.Interaction={}));a.Interaction}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(b){function c(){b.apply(this,arguments),this.currentHoverData={data:null,selection:null}}return __extends(c,b),c.prototype._anchor=function(c,d){var e=this;b.prototype._anchor.call(this,c,d),this.dispatcher=new a.Dispatcher.Mouse(this._hitBox),this.dispatcher.mouseover(function(a){e._componentToListenTo._hoverOverComponent(a),e.handleHoverOver(a)}),this.dispatcher.mouseout(function(a){e._componentToListenTo._hoverOutComponent(a),e.safeHoverOut(e.currentHoverData),e.currentHoverData={data:null,selection:null}}),this.dispatcher.mousemove(function(a){return e.handleHoverOver(a)}),this.dispatcher.connect()},c.diffHoverData=function(a,b){if(null==a.data||null==b.data)return a;var c=function(a){return-1===b.data.indexOf(a)},d=a.data.filter(c);if(0===d.length)return{data:null,selection:null};var e=a.selection.filter(c);return{data:d,selection:e}},c.prototype.handleHoverOver=function(a){var b=this.currentHoverData,d=this._componentToListenTo._doHover(a),e=c.diffHoverData(b,d);this.safeHoverOut(e);var f=c.diffHoverData(d,b);this.safeHoverOver(f),this.currentHoverData=d},c.prototype.safeHoverOut=function(a){this.hoverOutCallback&&a.data&&this.hoverOutCallback(a)},c.prototype.safeHoverOver=function(a){this.hoverOverCallback&&a.data&&this.hoverOverCallback(a)},c.prototype.onHoverOver=function(a){return this.hoverOverCallback=a,this},c.prototype.onHoverOut=function(a){return this.hoverOutCallback=a,this},c.prototype.getCurrentHoverData=function(){return this.currentHoverData},c}(b.AbstractInteraction);b.Hover=c}(a.Interaction||(a.Interaction={}));a.Interaction}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(b){var c=function(a){function b(b){a.call(this),this._event2Callback={},this.connected=!1,this._target=b}return __extends(b,a),b.prototype.target=function(a){if(null==a)return this._target;var b=this.connected;return this.disconnect(),this._target=a,b&&this.connect(),this},b.prototype.getEventString=function(a){return a+".dispatcher"+this._plottableID},b.prototype.connect=function(){var a=this;if(this.connected)throw new Error("Can't connect dispatcher twice!");return this.connected=!0,Object.keys(this._event2Callback).forEach(function(b){var c=a._event2Callback[b];a._target.on(a.getEventString(b),c)}),this},b.prototype.disconnect=function(){var a=this;return this.connected=!1,Object.keys(this._event2Callback).forEach(function(b){a._target.on(a.getEventString(b),null)}),this},b}(a.Core.PlottableObject);b.AbstractDispatcher=c}(a.Dispatcher||(a.Dispatcher={}));a.Dispatcher}(Plottable||(Plottable={}));var __extends=this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},Plottable;!function(a){!function(a){var b=function(a){function b(b){var c=this;a.call(this,b),this._event2Callback.mouseover=function(){null!=c._mouseover&&c._mouseover(c.getMousePosition())},this._event2Callback.mousemove=function(){null!=c._mousemove&&c._mousemove(c.getMousePosition())},this._event2Callback.mouseout=function(){null!=c._mouseout&&c._mouseout(c.getMousePosition())}}return __extends(b,a),b.prototype.getMousePosition=function(){var a=d3.mouse(this._target.node());return{x:a[0],y:a[1]}},b.prototype.mouseover=function(a){return void 0===a?this._mouseover:(this._mouseover=a,this)},b.prototype.mousemove=function(a){return void 0===a?this._mousemove:(this._mousemove=a,this)},b.prototype.mouseout=function(a){return void 0===a?this._mouseout:(this._mouseout=a,this)},b}(a.AbstractDispatcher);a.Mouse=b}(a.Dispatcher||(a.Dispatcher={}));a.Dispatcher}(Plottable||(Plottable={})); \ No newline at end of file diff --git a/plottable.zip b/plottable.zip index 16df58e4e7..87c0844885 100644 Binary files a/plottable.zip and b/plottable.zip differ diff --git a/quicktests/color/index.html b/quicktests/color/index.html new file mode 100644 index 0000000000..4b3ce26d91 --- /dev/null +++ b/quicktests/color/index.html @@ -0,0 +1,28 @@ + + + + + + + + + + + + + Plottable Color Quick Test + + + + + +
+
+ + + diff --git a/quicktests/color/main.css b/quicktests/color/main.css new file mode 100644 index 0000000000..161f68bde0 --- /dev/null +++ b/quicktests/color/main.css @@ -0,0 +1,46 @@ +svg{ + float: left; +} + +.navigation{ + background-color: #db2e65; + width: 100%; + height: 50px; + position: fixed; + top: 0px; + left: 0px; + text-align: center; + z-index: 1; +} + +.results{ + margin-top: 50px; + position: relative; +} + +input{ + top: 10px; + position: relative; + border: solid 1px; + border-color: white; + border-radius: 5; + width: 150px; + height: 30px; + font-family: sans-serif; + font-weight: 100; + font-size: 13px; + text-align: center; +} + +#render{ + background-color: white; + width: 50px; +} + +#render:hover{ + background-color: whitesmoke; +} + +#render:active{ + background-color: white; +} diff --git a/quicktests/color/main.js b/quicktests/color/main.js new file mode 100644 index 0000000000..5a9dc175e4 --- /dev/null +++ b/quicktests/color/main.js @@ -0,0 +1,182 @@ +(function iife(){ + +"use strict"; + +var P = Plottable.Plot; +var singlePlots = [P.VerticalBar]; +var singleHorizontalPlots = [P.HorizontalBar]; +var multipleDatasetPlots = [P.Line, P.Area, P.Scatter]; +var stackedPlots = [P.StackedBar, P.StackedArea, P.ClusteredBar]; +var stackedHorizontalPlots = [P.StackedBar, P.ClusteredBar]; +var piePlots = [P.Pie]; +var otherPlots = [P.Grid]; + +var plots = singlePlots.concat(singleHorizontalPlots, multipleDatasetPlots, stackedPlots, piePlots); +var div = d3.select(".results"); +var plotwidth; +var plotheight; + +//functions + +function renderPlots(plottablePlots){ + plottablePlots.forEach(function(plot){ + var box = div.append("svg").attr("height", plotheight).attr("width", plotwidth); + var chart = new Plottable.Component.Table([[plot]]); + chart.renderTo(box); + }); +} + +function addAllDatasets(plot, arr, numOfDatasets){ + if (numOfDatasets === "single") { + plot.addDataset("d1" , arr[0]); + } + if (numOfDatasets === "multiple") { + arr.forEach(function(dataset){ + plot.addDataset(dataset); + }); + } + return plot; +} + +function generatePlots(plots, dataType){ + var plottablePlots = []; + plots.forEach(function(PlotType){ + var xScale = new Plottable.Scale.Ordinal(); + var yScale = new Plottable.Scale.Linear(); + var colorScale = new Plottable.Scale.Color(); + var plot = new PlotType(xScale, yScale); + plot.attr("fill", "type", colorScale) + .animate(true); + + + if (singlePlots.indexOf(PlotType) > -1) { //if single dataset plot + plot = addAllDatasets(plot, dataType[0], "single"); + plottablePlots.push(plot); + } + + if (singleHorizontalPlots.indexOf(PlotType) > -1) { //if single horizontal plot + xScale = new Plottable.Scale.Linear(); + yScale = new Plottable.Scale.Ordinal(); + colorScale = new Plottable.Scale.Color(); + plot = new PlotType(xScale, yScale); + plot.project("x", "y", xScale) + .project("y", "x", yScale) + .attr("fill", "type", colorScale) + .animate(true); + plot = addAllDatasets(plot, dataType[0], "single"); + plottablePlots.push(plot); + } + + if (multipleDatasetPlots.indexOf(PlotType) > -1) { //if multiple dataset plot + plot = addAllDatasets(plot, dataType[1], "multiple"); + plottablePlots.push(plot); + } + + if (stackedPlots.indexOf(PlotType) > -1) { //if stacked dataset plot + plot = addAllDatasets(plot, dataType[2], "multiple"); + plottablePlots.push(plot); + } + + if (stackedHorizontalPlots.indexOf(PlotType) > -1) { //if stacked horizontal dataset plot + xScale = new Plottable.Scale.Linear(); + yScale = new Plottable.Scale.Ordinal(); + colorScale = new Plottable.Scale.Color(); + plot = new PlotType(xScale, yScale, false); + + plot.project("x", "y", xScale) + .project("y", "x", yScale) + .attr("fill", "type", colorScale) + .animate(true); + + plot = addAllDatasets(plot, dataType[2], "multiple"); + plottablePlots.push(plot); + } + + if (piePlots.indexOf(PlotType) > -1) { //if pie dataset plot + plot.project("value", "x"); + plot = addAllDatasets(plot, dataType[0], "single"); + plottablePlots.push(plot); + } + + }); + renderPlots(plottablePlots); +} + +var orderByX = function(a,b){ + return a.x - b.x; +}; + +function setDatasetType(dataset, setType){ + dataset.forEach(function(datum){ + datum.type = setType; + }); +} + +function makeRandomData(numPoints, series, scaleFactor) { + if (typeof scaleFactor === "undefined") { scaleFactor = 1; } + var data = []; + for (var j = 0; j < series; j++){ + var dataset = []; + for (var i = 0; i < numPoints; i++) { + var x = Math.random(); + var r = { x: x, y: (x + x * Math.random()) * scaleFactor}; + dataset.push(r); + } + dataset.sort(orderByX); + setDatasetType(dataset, j); + data.push(dataset); + } + return data; +} + +function prepareSingleData(data){ + data[0].map(function(element){element.type = ""+ element.x;}); + return data; +} + +function prepareMultipleData(data){ + return data; +} + +function prepareStackedData(data){ + var stackedData = []; + var firstDataset = data[0]; + for (var i = 0; i < data.length; i++) { + var dataset = data[i]; + for (var j = 0; j < dataset.length; j++) { + dataset[j].x = firstDataset[j].x; + } + stackedData.push(dataset); + } + return stackedData; +} + +function prepareData(seriesNumber){ + var data = [{x: "0", y: 0, type: "0"}]; + + var categories = 5; //change this number for more/less data in multiple & stacked dataset + var series = seriesNumber; //change this number for more/less stack + var alldata = []; + + var singleData = prepareSingleData(makeRandomData(series, 1)); + var multipleData = prepareMultipleData(makeRandomData(categories, series, 1)); + var stackedData = prepareStackedData(makeRandomData(categories, series, 1)); + alldata.push(singleData, multipleData, stackedData); + return alldata; +} + +function initialize(){ + var seriesNumber = Number(d3.select("#series").node().value); + plotwidth = Number(d3.select("#width").node().value); + plotheight = Number(d3.select("#height").node().value); + + d3.selectAll("svg").remove(); + var dataArray = prepareData(seriesNumber); + generatePlots(plots, dataArray); +} + +var button = document.getElementById("render"); +button.onclick = initialize; + +})(); + diff --git a/quicktests/html/clustered_bar.html b/quicktests/html/clustered_bar.html index 14c70d8573..b71fcd3646 100644 --- a/quicktests/html/clustered_bar.html +++ b/quicktests/html/clustered_bar.html @@ -12,6 +12,8 @@ padding: 20px; } + + diff --git a/quicktests/html/interaction_hover_scatter.html b/quicktests/html/interaction_hover_scatter.html new file mode 100644 index 0000000000..891c08f3ff --- /dev/null +++ b/quicktests/html/interaction_hover_scatter.html @@ -0,0 +1,31 @@ + + + Interaction.Hover + Plot.Scatter + + + + + + + + + + + +
+ + + diff --git a/quicktests/html/missing_stacked_area.html b/quicktests/html/missing_stacked_area.html new file mode 100644 index 0000000000..754be87db8 --- /dev/null +++ b/quicktests/html/missing_stacked_area.html @@ -0,0 +1,32 @@ + + + + Missing Stacked Bar + + + + + + + + + + + +
+ + + diff --git a/quicktests/html/select_clustered_bars.html b/quicktests/html/select_clustered_bars.html new file mode 100644 index 0000000000..b4a0fc60c8 --- /dev/null +++ b/quicktests/html/select_clustered_bars.html @@ -0,0 +1,32 @@ + + + + Select Bars + + + + + + + + + + + +
+ + + diff --git a/quicktests/html/tick_generator.html b/quicktests/html/tick_generator.html new file mode 100644 index 0000000000..85b8d6d4da --- /dev/null +++ b/quicktests/html/tick_generator.html @@ -0,0 +1,32 @@ + + + + Tick generator + + + + + + + + + + + +
+ + + diff --git a/quicktests/js/animate_verticalBar.js b/quicktests/js/animate_verticalBar.js index 9d4311f6a1..0e27092313 100644 --- a/quicktests/js/animate_verticalBar.js +++ b/quicktests/js/animate_verticalBar.js @@ -6,7 +6,6 @@ function makeData() { function run(div, data, Plottable) { "use strict"; - var svg = div.append("svg").attr("height", 500); var doAnimate = true; diff --git a/quicktests/js/clustered_bar.js b/quicktests/js/clustered_bar.js index 8eefb17e49..5c1071e7f6 100644 --- a/quicktests/js/clustered_bar.js +++ b/quicktests/js/clustered_bar.js @@ -25,11 +25,12 @@ function run(div, data, Plottable) { .attr("y", "y", yScale) .attr("fill", "type", colorScale) .attr("type", "type") - .attr("yval", "y"); + .attr("yval", "y") + .barLabels(true); var center = clusteredBarRenderer.merge(new Plottable.Component.Legend(colorScale)); - horizChart = new Plottable.Component.Table([ + var horizChart = new Plottable.Component.Table([ [yAxis, center], [null, xAxis] ]).renderTo(svg); } diff --git a/quicktests/js/interaction_hover_scatter.js b/quicktests/js/interaction_hover_scatter.js new file mode 100644 index 0000000000..4a850f4f23 --- /dev/null +++ b/quicktests/js/interaction_hover_scatter.js @@ -0,0 +1,46 @@ +function makeData() { + "use strict"; + var blueSet = makeRandomData(20); + blueSet.forEach(function(d) { d.color = "blue"; }); + var redSet = makeRandomData(20); + redSet.forEach(function(d) { d.color = "red"; }); + return [blueSet, redSet]; +} + +function run(div, data, Plottable) { + "use strict"; + + var svg = div.append("svg").attr("height", 500); + var xScale = new Plottable.Scale.Linear(); + var yScale = new Plottable.Scale.Linear(); + var xAxis = new Plottable.Axis.Numeric(xScale, "bottom"); + var yAxis = new Plottable.Axis.Numeric(yScale, "left"); + var title = new Plottable.Component.TitleLabel("Hover over points"); + + var plot = new Plottable.Plot.Scatter(xScale, yScale) + .addDataset(data[0]) + .addDataset(data[1]) + .project("r", 10) + .project("fill", "color") + .project("x", "x", xScale) + .project("y", "y", yScale); + + var chart = new Plottable.Component.Table([ + [null, title], + [yAxis, plot], + [null, xAxis]]); + + chart.renderTo(svg); + + var hover = new Plottable.Interaction.Hover(); + hover.onHoverOver(function(hoverData) { + var color = hoverData.data[0].color.toUpperCase(); + var xString = hoverData.data[0].x.toFixed(2); + var yString = hoverData.data[0].y.toFixed(2); + title.text(color + ": [ " + xString + ", " + yString + " ]"); + }); + hover.onHoverOut(function(hoverData) { + title.text("Hover over points"); + }); + plot.registerInteraction(hover); +} diff --git a/quicktests/js/missing_clustered_bar.js b/quicktests/js/missing_clustered_bar.js index d4c64c8300..2f2e307a51 100644 --- a/quicktests/js/missing_clustered_bar.js +++ b/quicktests/js/missing_clustered_bar.js @@ -25,7 +25,8 @@ function run(div, data, Plottable) { .attr("y", "y", yScale) .attr("fill", "type", colorScale) .attr("type", "type") - .attr("yval", "y"); + .attr("yval", "y") + .barLabelsEnabled(true); var center = clusteredBarRenderer.merge(new Plottable.Component.Legend(colorScale)); diff --git a/quicktests/js/missing_stacked_area.js b/quicktests/js/missing_stacked_area.js new file mode 100644 index 0000000000..e9fc4d6ed5 --- /dev/null +++ b/quicktests/js/missing_stacked_area.js @@ -0,0 +1,36 @@ +function makeData() { + "use strict"; + + var data1 = [{name: "jon", y: 1, type: "q1"}, {name: "dan", y: 2, type: "q1"}, {name: "zoo", y: 1, type: "q1"}]; + var data2 = [{name: "jon", y: 2, type: "q2"}, {name: "dan", y: 4, type: "q2"}]; + var data3 = [{name: "dan", y: 15, type: "q3"}, {name: "zoo", y: 15, type: "q3"}]; + return [data1, data2, data3]; +} + +function run(div, data, Plottable) { + "use strict"; + + var svg = div.append("svg").attr("height", 500); + var xScale = new Plottable.Scale.Ordinal().rangeType("points"); + var yScale = new Plottable.Scale.Linear(); + var colorScale = new Plottable.Scale.Color("10"); + + var xAxis = new Plottable.Axis.Category(xScale, "bottom"); + var yAxis = new Plottable.Axis.Numeric(yScale, "left"); + var stackedAreaPlot = new Plottable.Plot.StackedArea(xScale, yScale) + .attr("x", "name", xScale) + .attr("y", "y", yScale) + .attr("fill", "type", colorScale) + .attr("type", "type") + .attr("yval", "y") + .addDataset("d1", data[0]) + .addDataset("d2", data[1]) + .addDataset("d3", data[2]) + .animate(true); + + var center = stackedAreaPlot.merge(new Plottable.Component.Legend(colorScale)); + + var horizChart = new Plottable.Component.Table([ + [yAxis, center], [null, xAxis] + ]).renderTo(svg); +} diff --git a/quicktests/js/negative_stacked_bar.js b/quicktests/js/negative_stacked_bar.js index 3d9be90433..aeb48d4d92 100644 --- a/quicktests/js/negative_stacked_bar.js +++ b/quicktests/js/negative_stacked_bar.js @@ -12,9 +12,7 @@ function makeData() { function run(div, data, Plottable) { "use strict"; - var svg = div.append("svg").attr("height", 800); - var xScale1 = new Plottable.Scale.Ordinal(); var yScale1 = new Plottable.Scale.Linear(); var xScale2 = new Plottable.Scale.Linear(); @@ -40,7 +38,10 @@ function run(div, data, Plottable) { .addDataset("d2", data[1]) .addDataset("d3", data[2]) .addDataset("d4", data[3]) - .addDataset("d5", data[4]); + .addDataset("d5", data[4]) + .barLabelsEnabled(true) + .barLabelFormatter(Plottable.Formatters.siSuffix()) + .animate(true); var horizontalPlot = new Plottable.Plot.StackedBar(xScale2, yScale2, false) .project("x", "earnings", xScale2) @@ -50,7 +51,10 @@ function run(div, data, Plottable) { .addDataset("d2", data[1]) .addDataset("d3", data[2]) .addDataset("d4", data[3]) - .addDataset("d5", data[4]); + .addDataset("d5", data[4]) + .barLabelsEnabled(true) + .barLabelFormatter(Plottable.Formatters.siSuffix()) + .animate(true); var chart1 = new Plottable.Component.Table([ [yAxis1, verticalPlot], [null, xAxis1] @@ -59,7 +63,7 @@ function run(div, data, Plottable) { var chart2 = new Plottable.Component.Table([ [yAxis2, horizontalPlot], [null, xAxis2] ]); - + var finalchart = new Plottable.Component.Table([ [title], [legend], diff --git a/quicktests/js/rainfall_ClusteredBar.js b/quicktests/js/rainfall_ClusteredBar.js index 8423932bdb..b39e7588af 100644 --- a/quicktests/js/rainfall_ClusteredBar.js +++ b/quicktests/js/rainfall_ClusteredBar.js @@ -25,6 +25,7 @@ function run(div, data, Plottable){ .addDataset(data[2]) .project("x", "month", xScale) .project("y", "avg", yScale) + .project("label", "avg") .project("fill", "city", colorScale); var legend = new Plottable.Component.HorizontalLegend(colorScale); diff --git a/quicktests/js/select_clustered_bars.js b/quicktests/js/select_clustered_bars.js new file mode 100644 index 0000000000..13bbc36904 --- /dev/null +++ b/quicktests/js/select_clustered_bars.js @@ -0,0 +1,44 @@ + +function makeData() { + "use strict"; + + var data1 = [{name: "jon", y: 1, type: "q1"}, {name: "dan", y: 2, type: "q1"}, {name: "zoo", y: 1, type: "q1"}]; + var data2 = [{name: "jon", y: 2, type: "q2"}, {name: "dan", y: 4, type: "q2"}, {name: "zoo", y: 2, type: "q2"}]; + var data3 = [{name: "jon", y: 4, type: "q3"}, {name: "dan", y: 15, type: "q3"}, {name: "zoo", y: 15, type: "q3"}]; + return [data1, data2, data3]; +} + +function run(div, data, Plottable) { + "use strict"; + + var svg = div.append("svg").attr("height", 500); + + var xScale = new Plottable.Scale.Ordinal(); + var xAxis = new Plottable.Axis.Category(xScale, "bottom"); + + var yScale = new Plottable.Scale.Linear(); + var yAxis = new Plottable.Axis.Numeric(yScale, "left"); + + var barPlot = new Plottable.Plot.ClusteredBar(xScale, yScale) + .addDataset(data[0]) + .addDataset(data[1]) + .addDataset(data[2]) + .project("x", "name", xScale); + + var chart = new Plottable.Component.Table([ + [yAxis, barPlot], + [null, xAxis]]).renderTo(svg); + + var clickInteraction = new Plottable.Interaction.Click(); + barPlot.registerInteraction(clickInteraction); + clickInteraction.callback(function (p) { + var selectedBar = barPlot.selectBar(p.x, p.y, true); + if (selectedBar == null) { + d3.selectAll(".selected").style("fill", null); + barPlot.deselectAll(); + } else { + d3.selectAll(".selected").style("fill", "red"); + } + }); + +} diff --git a/quicktests/js/stacked_bar.js b/quicktests/js/stacked_bar.js index 455cd56e71..ab437faaaa 100644 --- a/quicktests/js/stacked_bar.js +++ b/quicktests/js/stacked_bar.js @@ -1,7 +1,7 @@ function makeData() { "use strict"; - var data1 = [{name: "jon", y: 1, type: "q1"}, {name: "dan", y: 2, type: "q1"}, {name: "zoo", y: 1, type: "q1"}]; + var data1 = [{name: "jon", y: 0.5, type: "q1"}, {name: "dan", y: 2, type: "q1"}, {name: "zoo", y: 1, type: "q1"}]; var data2 = [{name: "jon", y: 2, type: "q2"}, {name: "dan", y: 4, type: "q2"}, {name: "zoo", y: 2, type: "q2"}]; var data3 = [{name: "jon", y: 4, type: "q3"}, {name: "dan", y: 15, type: "q3"}, {name: "zoo", y: 15, type: "q3"}]; return [data1, data2, data3]; @@ -26,7 +26,8 @@ function run(div, data, Plottable) { .addDataset("d1", data[0]) .addDataset("d2", data[1]) .addDataset("d3", data[2]) - .animate(true); + .animate(true) + .barLabels(true); var center = stackedBarPlot.merge(new Plottable.Component.Legend(colorScale)); diff --git a/quicktests/js/tick_generator.js b/quicktests/js/tick_generator.js new file mode 100644 index 0000000000..4915d9b666 --- /dev/null +++ b/quicktests/js/tick_generator.js @@ -0,0 +1,27 @@ +function makeData() { + "use strict"; + + return makeRandomData(50); +} + +function run(div, data, Plottable) { + "use strict"; + + var svg = div.append("svg").attr("height", 500); + + var xScale = new Plottable.Scale.Linear().tickGenerator(Plottable.Scale.TickGenerators.intervalTickGenerator(0.2)); + var yScale = new Plottable.Scale.Linear(); + var xAxis = new Plottable.Axis.Numeric(xScale, "bottom"); + var yAxis = new Plottable.Axis.Numeric(yScale, "left"); + var titleLabel = new Plottable.Component.TitleLabel("Ticks in Increments of 0.2"); + + var plot = new Plottable.Plot.Line(xScale, yScale); + plot.addDataset(data); + + var chart = new Plottable.Component.Table([ + [null, titleLabel], + [yAxis, plot], + [null, xAxis] + ]); + chart.renderTo(svg); +} diff --git a/quicktests/list_of_quicktests.json b/quicktests/list_of_quicktests.json index 10893f9c92..3ab6fa1256 100644 --- a/quicktests/list_of_quicktests.json +++ b/quicktests/list_of_quicktests.json @@ -114,6 +114,11 @@ "categories":["Data", "Linear Scale", "Numeric Axis", "Accessor", "Project", "Area Plot", "Gridlines", "Merge", "Group", "DragBox Interaction"] }, + { + "name":"interaction_hover_scatter", + "categories":["Label", "Scatter Plot", "Hover Interaction"] + }, + { "name":"interaction_panzoom", "categories":["Linear Scale", "Numeric Axis", "Scatter Plot", "Gridlines", "Merge", "PanZoom Interaction"] @@ -164,11 +169,6 @@ "categories":["Linear Scale", "Numeric Axis", "Animate", "Legend", "Category Axis", "Linear Scale", "Vertical Bar Plot", "Stacked Plot"] }, - { - "name":"negative_stacked_bar", - "categories":["Linear Scale", "Numeric Axis", "Legend", "Category Axis", "Linear Scale", "Vertical Bar Plot", "Stacked Plot"] - }, - { "name":"no_data", "categories":[ "Linear Scale", "Numeric Axis", "Area Plot", "Scatter Plot", "Vertical Bar Plot", "Horizontal Bar Plot", "Line Plot", "Table", "Layout"] @@ -252,5 +252,8 @@ { "name":"stocks", "categories":["Integration", "Vertical Bar Plot", "Legend", "Layout", "PanZoom Interaction", "Key Interaction"] + }, { + "name":"tick_generator", + "categories":["Linear Scale", "Tick Generation", "Line Plot"] } ] diff --git a/src/animators/baseAnimator.ts b/src/animators/baseAnimator.ts index a0d419aee3..6eaa8c930f 100644 --- a/src/animators/baseAnimator.ts +++ b/src/animators/baseAnimator.ts @@ -5,6 +5,15 @@ export module Animator { /** * The base animator implementation with easing, duration, and delay. + * + * The maximum delay between animations can be configured with maxIterativeDelay. + * + * The maximum total animation duration can be configured with maxTotalDuration. + * maxTotalDuration does not set actual total animation duration. + * + * The actual interval delay is calculated by following formula: + * min(maxIterativeDelay(), + * max(maxTotalDuration() - duration(), 0) / ) */ export class Base implements PlotAnimator { /** @@ -15,6 +24,14 @@ export module Animator { * The default starting delay of the animation in milliseconds */ public static DEFAULT_DELAY_MILLISECONDS = 0; + /** + * The default maximum start delay between each start of an animation + */ + public static DEFAULT_MAX_ITERATIVE_DELAY_MILLISECONDS = 15; + /** + * The default maximum total animation duration + */ + public static DEFAULT_MAX_TOTAL_DURATION_MILLISECONDS = 600; /** * The default easing of the animation */ @@ -23,6 +40,8 @@ export module Animator { private _duration: number; private _delay: number; private _easing: string; + private _maxIterativeDelay: number; + private _maxTotalDuration: number; /** * Constructs the default animator @@ -33,13 +52,26 @@ export module Animator { this._duration = Base.DEFAULT_DURATION_MILLISECONDS; this._delay = Base.DEFAULT_DELAY_MILLISECONDS; this._easing = Base.DEFAULT_EASING; + this._maxIterativeDelay = Base.DEFAULT_MAX_ITERATIVE_DELAY_MILLISECONDS; + this._maxTotalDuration = Base.DEFAULT_MAX_TOTAL_DURATION_MILLISECONDS; } - public animate(selection: any, attrToProjector: AttributeToProjector): D3.Transition.Transition { + public getTiming(numberOfIterations: number) { + var maxDelayForLastIteration = Math.max(this.maxTotalDuration() - this.duration(), 0); + var adjustedIterativeDelay = Math.min(this.maxIterativeDelay(), maxDelayForLastIteration / Math.max(numberOfIterations - 1, 1)); + var time = adjustedIterativeDelay * numberOfIterations + this.delay() + this.duration(); + return time; + } + + public animate(selection: any, attrToProjector: AttributeToProjector): any { + var numberOfIterations = selection[0].length; + var maxDelayForLastIteration = Math.max(this.maxTotalDuration() - this.duration(), 0); + var adjustedIterativeDelay = Math.min(this.maxIterativeDelay(), maxDelayForLastIteration / Math.max(numberOfIterations - 1, 1)); + return selection.transition() .ease(this.easing()) .duration(this.duration()) - .delay(this.delay()) + .delay((d: any, i: number) => this.delay() + adjustedIterativeDelay * i) .attr(attrToProjector); } @@ -57,7 +89,7 @@ export module Animator { */ public duration(duration: number): Base; public duration(duration?: number): any{ - if (duration === undefined) { + if (duration == null) { return this._duration; } else { this._duration = duration; @@ -79,7 +111,7 @@ export module Animator { */ public delay(delay: number): Base; public delay(delay?: number): any{ - if (delay === undefined) { + if (delay == null) { return this._delay; } else { this._delay = delay; @@ -101,13 +133,57 @@ export module Animator { */ public easing(easing: string): Base; public easing(easing?: string): any{ - if (easing === undefined) { + if (easing == null) { return this._easing; } else { this._easing = easing; return this; } } + + /** + * Gets the maximum start delay between animations in milliseconds. + * + * @returns {number} The current maximum iterative delay. + */ + public maxIterativeDelay(): number; + /** + * Sets the maximum start delay between animations in milliseconds. + * + * @param {number} maxIterDelay The maximum iterative delay in milliseconds. + * @returns {Base} The calling Base Animator. + */ + public maxIterativeDelay(maxIterDelay: number): Base; + public maxIterativeDelay(maxIterDelay?: number): any { + if (maxIterDelay == null) { + return this._maxIterativeDelay; + } else { + this._maxIterativeDelay = maxIterDelay; + return this; + } + } + + /** + * Gets the maximum total animation duration in milliseconds. + * + * @returns {number} The current maximum total animation duration. + */ + public maxTotalDuration(): number; + /** + * Sets the maximum total animation duration in miliseconds. + * + * @param {number} maxDuration The maximum total animation duration in milliseconds. + * @returns {Base} The calling Base Animator. + */ + public maxTotalDuration(maxDuration: number): Base; + public maxTotalDuration(maxDuration?: number): any { + if (maxDuration == null) { + return this._maxTotalDuration; + } else { + this._maxTotalDuration = maxDuration; + return this; + } + } } } diff --git a/src/animators/iterativeDelayAnimator.ts b/src/animators/iterativeDelayAnimator.ts deleted file mode 100644 index f179e9d57c..0000000000 --- a/src/animators/iterativeDelayAnimator.ts +++ /dev/null @@ -1,101 +0,0 @@ -/// - -module Plottable { -export module Animator { - - /** - * An animator that delays the animation of the attributes using the index - * of the selection data. - * - * The maximum delay between animations can be configured with maxIterativeDelay. - * - * The maximum total animation duration can be configured with maxTotalDuration. - * maxTotalDuration does not set actual total animation duration. - * - * The actual interval delay is calculated by following formula: - * min(maxIterativeDelay(), - * max(totalDurationLimit() - duration(), 0) / ) - */ - export class IterativeDelay extends Base { - /** - * The default maximum start delay between each start of an animation - */ - public static DEFAULT_MAX_ITERATIVE_DELAY_MILLISECONDS = 15; - - /** - * The default maximum total animation duration - */ - public static DEFAULT_MAX_TOTAL_DURATION_MILLISECONDS = 600; - - private _maxIterativeDelay: number; - private _maxTotalDuration: number; - - /** - * Constructs an animator with a start delay between each selection animation - * - * @constructor - */ - constructor() { - super(); - this._maxIterativeDelay = IterativeDelay.DEFAULT_MAX_ITERATIVE_DELAY_MILLISECONDS; - this._maxTotalDuration = IterativeDelay.DEFAULT_MAX_TOTAL_DURATION_MILLISECONDS; - } - - public animate(selection: any, attrToProjector: AttributeToProjector): D3.Transition.Transition { - var numberOfIterations = selection[0].length; - var maxDelayForLastIteration = Math.max(this.maxTotalDuration() - this.duration(), 0); - var adjustedIterativeDelay = Math.min(this.maxIterativeDelay(), maxDelayForLastIteration / numberOfIterations); - return selection.transition() - .ease(this.easing()) - .duration(this.duration()) - .delay((d: any, i: number) => this.delay() + adjustedIterativeDelay * i) - .attr(attrToProjector); - } - - /** - * Gets the maximum start delay between animations in milliseconds. - * - * @returns {number} The current maximum iterative delay. - */ - public maxIterativeDelay(): number; - /** - * Sets the maximum start delay between animations in milliseconds. - * - * @param {number} maxIterDelay The maximum iterative delay in milliseconds. - * @returns {IterativeDelay} The calling IterativeDelay Animator. - */ - public maxIterativeDelay(maxIterDelay: number): IterativeDelay; - public maxIterativeDelay(maxIterDelay?: number): any { - if (maxIterDelay === undefined) { - return this._maxIterativeDelay; - } else { - this._maxIterativeDelay = maxIterDelay; - return this; - } - } - - /** - * Gets the maximum total animation duration in milliseconds. - * - * @returns {number} The current maximum total animation duration. - */ - public maxTotalDuration(): number; - /** - * Sets the maximum total animation duration in miliseconds. - * - * @param {number} maxDuration The maximum total animation duration in milliseconds. - * @returns {IterativeDelay} The calling IterativeDelay Animator. - */ - public maxTotalDuration(maxDuration: number): IterativeDelay; - public maxTotalDuration(maxDuration?: number): any { - if (maxDuration == null) { - return this._maxTotalDuration; - } else { - this._maxTotalDuration = maxDuration; - return this; - } - } - } - -} -} diff --git a/src/animators/nullAnimator.ts b/src/animators/nullAnimator.ts index 09d38f7b79..ef1477b6ac 100644 --- a/src/animators/nullAnimator.ts +++ b/src/animators/nullAnimator.ts @@ -8,7 +8,11 @@ export module Animator { * immediately set on the selection. */ export class Null implements PlotAnimator { - public animate(selection: any, attrToProjector: AttributeToProjector): D3.Selection { + + public getTiming(selection: any) { + return 0; + } + public animate(selection: any, attrToProjector: AttributeToProjector): any { return selection.attr(attrToProjector); } } diff --git a/src/animators/rectAnimator.ts b/src/animators/rectAnimator.ts index a801baeace..5d76f8370b 100644 --- a/src/animators/rectAnimator.ts +++ b/src/animators/rectAnimator.ts @@ -19,7 +19,7 @@ export module Animator { this.isReverse = isReverse; } - public animate(selection: any, attrToProjector: AttributeToProjector): D3.Transition.Transition { + public animate(selection: any, attrToProjector: AttributeToProjector) { var startAttrToProjector: AttributeToProjector = {}; Rect.ANIMATED_ATTRIBUTES.forEach((attr: string) => startAttrToProjector[attr] = attrToProjector[attr]); diff --git a/src/components/axes/categoryAxis.ts b/src/components/axes/categoryAxis.ts index 61c9301c69..b486cda89a 100644 --- a/src/components/axes/categoryAxis.ts +++ b/src/components/axes/categoryAxis.ts @@ -154,8 +154,8 @@ export module Axis { var heightFn = this._isHorizontal() ? _Util.Methods.max : d3.sum; return { textFits: textWriteResults.every((t: _Util.Text.IWriteTextResult) => t.textFits), - usedWidth : widthFn(textWriteResults, (t: _Util.Text.IWriteTextResult) => t.usedWidth), - usedHeight: heightFn(textWriteResults, (t: _Util.Text.IWriteTextResult) => t.usedHeight) + usedWidth : widthFn<_Util.Text.IWriteTextResult, number>(textWriteResults, (t: _Util.Text.IWriteTextResult) => t.usedWidth, 0), + usedHeight: heightFn<_Util.Text.IWriteTextResult, number>(textWriteResults, (t: _Util.Text.IWriteTextResult) => t.usedHeight, 0) }; } diff --git a/src/components/axes/numericAxis.ts b/src/components/axes/numericAxis.ts index 26d5e23a1c..f3aba24208 100644 --- a/src/components/axes/numericAxis.ts +++ b/src/components/axes/numericAxis.ts @@ -38,7 +38,7 @@ export module Axis { return this.measurer(formattedValue).width; }); - var maxTextLength = _Util.Methods.max(textLengths); + var maxTextLength = _Util.Methods.max(textLengths, 0); if (this.tickLabelPositioning === "center") { this._computedWidth = this._maxLabelTickLength() + this.tickLabelPadding() + maxTextLength; diff --git a/src/components/componentGroup.ts b/src/components/componentGroup.ts index 0866a5dfe5..f762328453 100644 --- a/src/components/componentGroup.ts +++ b/src/components/componentGroup.ts @@ -23,8 +23,8 @@ export module Component { public _requestedSpace(offeredWidth: number, offeredHeight: number): _SpaceRequest { var requests = this._components.map((c: AbstractComponent) => c._requestedSpace(offeredWidth, offeredHeight)); return { - width : _Util.Methods.max(requests, (request: _SpaceRequest) => request.width ), - height: _Util.Methods.max(requests, (request: _SpaceRequest) => request.height), + width : _Util.Methods.max<_SpaceRequest, number>(requests, (request: _SpaceRequest) => request.width, 0), + height: _Util.Methods.max<_SpaceRequest, number>(requests, (request: _SpaceRequest) => request.height, 0), wantsWidth : requests.map((r: _SpaceRequest) => r.wantsWidth ).some((x: boolean) => x), wantsHeight: requests.map((r: _SpaceRequest) => r.wantsHeight).some((x: boolean) => x) }; diff --git a/src/components/horizontalLegend.ts b/src/components/horizontalLegend.ts index 1cfbf4f2f8..153a535d19 100644 --- a/src/components/horizontalLegend.ts +++ b/src/components/horizontalLegend.ts @@ -79,7 +79,7 @@ export module Component { var rowLengths = estimatedLayout.rows.map((row: string[]) => { return d3.sum(row, (entry: string) => estimatedLayout.entryLengths.get(entry)); }); - var longestRowLength = _Util.Methods.max(rowLengths); + var longestRowLength = _Util.Methods.max(rowLengths, 0); longestRowLength = longestRowLength === undefined ? 0 : longestRowLength; // HACKHACK: #843 var desiredWidth = this.padding + longestRowLength; diff --git a/src/components/legend.ts b/src/components/legend.ts index 1ce6129293..cfc70ce3d3 100644 --- a/src/components/legend.ts +++ b/src/components/legend.ts @@ -177,7 +177,7 @@ export module Component { var rowsICanFit = Math.min(totalNumRows, Math.floor( (offeredHeight - 2 * Legend.MARGIN) / textHeight)); var fakeLegendEl = this._content.append("g").classed(Legend.SUBELEMENT_CLASS, true); var measure = _Util.Text.getTextMeasurer(fakeLegendEl.append("text")); - var maxWidth = _Util.Methods.max(this.colorScale.domain(), (d: string) => measure(d).width); + var maxWidth = _Util.Methods.max(this.colorScale.domain(), (d: string) => measure(d).width, 0); fakeLegendEl.remove(); maxWidth = maxWidth === undefined ? 0 : maxWidth; var desiredWidth = rowsICanFit === 0 ? 0 : maxWidth + textHeight + 2 * Legend.MARGIN; diff --git a/src/components/plots/abstractBarPlot.ts b/src/components/plots/abstractBarPlot.ts index 5ab3b3da61..f9077b27a7 100644 --- a/src/components/plots/abstractBarPlot.ts +++ b/src/components/plots/abstractBarPlot.ts @@ -2,19 +2,17 @@ module Plottable { export module Plot { - export class AbstractBarPlot extends AbstractXYPlot { + export class AbstractBarPlot extends AbstractXYPlot implements Interaction.Hoverable { public static _BarAlignmentToFactor: {[alignment: string]: number} = {}; private static DEFAULT_WIDTH = 10; public _baseline: D3.Selection; public _baselineValue = 0; - public _barAlignmentFactor = 0; + public _barAlignmentFactor = 0.5; public _isVertical: boolean; - - public _animators: Animator.PlotAnimatorMap = { - "bars-reset" : new Animator.Null(), - "bars" : new Animator.IterativeDelay(), - "baseline" : new Animator.Null() - }; + private _barLabelFormatter: Formatter = Formatters.identity(); + private _barLabelsEnabled = false; + private _hoverMode = "point"; + private hideBarsIfAnyAreTooWide = true; /** * Constructs a BarPlot. @@ -27,12 +25,14 @@ export module Plot { super(xScale, yScale); this.classed("bar-plot", true); this.project("fill", () => Core.Colors.INDIGO); - // super() doesn't set baseline + this._animators["bars-reset"] = new Animator.Null(); + this._animators["bars"] = new Animator.Base(); + this._animators["baseline"] = new Animator.Null(); this.baseline(this._baselineValue); } public _getDrawer(key: string) { - return new Plottable._Drawer.Rect(key); + return new Plottable._Drawer.Rect(key, this._isVertical); } public _setup() { @@ -40,47 +40,6 @@ export module Plot { this._baseline = this._renderArea.append("line").classed("baseline", true); } - // HACKHACK #1106 - should use drawers for paint logic - public _paint() { - var attrToProjector = this._generateAttrToProjector(); - var datasets = this.datasets(); - var primaryScale: Scale.AbstractScale = this._isVertical ? this._yScale : this._xScale; - var scaledBaseline = primaryScale.scale(this._baselineValue); - var positionAttr = this._isVertical ? "y" : "x"; - var dimensionAttr = this._isVertical ? "height" : "width"; - - this._getDrawersInOrder().forEach((d, i) => { - var dataset = datasets[i]; - var bars = d._renderArea.selectAll("rect").data(dataset.data()); - bars.enter().append("rect"); - - if (this._dataChanged && this._animate) { - var resetAttrToProjector = this._generateAttrToProjector(); - resetAttrToProjector[positionAttr] = () => scaledBaseline; - resetAttrToProjector[dimensionAttr] = () => 0; - this._applyAnimatedAttributes(bars, "bars-reset", resetAttrToProjector); - } - - var attrToProjector = this._generateAttrToProjector(); - if (attrToProjector["fill"]) { - bars.attr("fill", attrToProjector["fill"]); // so colors don't animate - } - this._applyAnimatedAttributes(bars, "bars", attrToProjector); - - bars.exit().remove(); - }); - - var baselineAttr: any = { - "x1": this._isVertical ? 0 : scaledBaseline, - "y1": this._isVertical ? scaledBaseline : 0, - "x2": this._isVertical ? this.width() : scaledBaseline, - "y2": this._isVertical ? scaledBaseline : this.height() - }; - - this._applyAnimatedAttributes(this._baseline, "baseline", baselineAttr); - - } - /** * Sets the baseline for the bars to the specified value. * @@ -128,6 +87,52 @@ export module Plot { } } + /** + * Get whether bar labels are enabled. + * + * @returns {boolean} Whether bars should display labels or not. + */ + public barLabelsEnabled(): boolean; + /** + * Set whether bar labels are enabled. + * @param {boolean} Whether bars should display labels or not. + * + * @returns {AbstractBarPlot} The calling plot. + */ + public barLabelsEnabled(enabled: boolean): AbstractBarPlot; + public barLabelsEnabled(enabled?: boolean): any { + if (enabled === undefined) { + return this._barLabelsEnabled; + } else { + this._barLabelsEnabled = enabled; + this._render(); + return this; + } + } + + /** + * Get the formatter for bar labels. + * + * @returns {Formatter} The formatting function for bar labels. + */ + public barLabelFormatter(): Formatter; + /** + * Change the formatting function for bar labels. + * @param {Formatter} The formatting function for bar labels. + * + * @returns {AbstractBarPlot} The calling plot. + */ + public barLabelFormatter(formatter: Formatter): AbstractBarPlot; + public barLabelFormatter(formatter?: Formatter): any { + if (formatter == null) { + return this._barLabelFormatter; + } else { + this._barLabelFormatter = formatter; + this._render(); + return this; + } + } + /** * Selects the bar under the given pixel position (if [xValOrExtent] * and [yValOrExtent] are {number}s), under a given line (if only one @@ -226,6 +231,52 @@ export module Plot { } } + public _additionalPaint(time: number) { + var primaryScale: Scale.AbstractScale = this._isVertical ? this._yScale : this._xScale; + var scaledBaseline = primaryScale.scale(this._baselineValue); + + var baselineAttr: any = { + "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); + + var drawers: _Drawer.Rect[] = this._getDrawersInOrder(); + drawers.forEach((d: _Drawer.Rect) => d.removeLabels()); + if (this._barLabelsEnabled) { + _Util.Methods.setTimeout(() => this._drawLabels(), time); + } + } + + public _drawLabels() { + var drawers: _Drawer.Rect[] = this._getDrawersInOrder(); + var attrToProjector = this._generateAttrToProjector(); + var dataToDraw = this._getDataToDraw(); + this._datasetKeysInOrder.forEach((k, i) => drawers[i].drawText(dataToDraw.get(k), attrToProjector)); + if (this.hideBarsIfAnyAreTooWide && drawers.some((d: _Drawer.Rect) => d._someLabelsTooWide)) { + drawers.forEach((d: _Drawer.Rect) => d.removeLabels()); + } + } + + public _generateDrawSteps(): _Drawer.DrawStep[] { + var drawSteps: _Drawer.DrawStep[] = []; + if (this._dataChanged && this._animate) { + var resetAttrToProjector = this._generateAttrToProjector(); + var primaryScale: Scale.AbstractScale = this._isVertical ? this._yScale : this._xScale; + var scaledBaseline = primaryScale.scale(this._baselineValue); + var positionAttr = this._isVertical ? "y" : "x"; + var dimensionAttr = this._isVertical ? "height" : "width"; + resetAttrToProjector[positionAttr] = () => scaledBaseline; + resetAttrToProjector[dimensionAttr] = () => 0; + drawSteps.push({attrToProjector: resetAttrToProjector, animator: this._getAnimator("bars-reset")}); + } + drawSteps.push({attrToProjector: this._generateAttrToProjector(), animator: this._getAnimator("bars")}); + return drawSteps; + } + public _generateAttrToProjector() { // Primary scale/direction: the "length" of the bars // Secondary scale/direction: the "width" of the bars @@ -263,9 +314,87 @@ export module Plot { attrToProjector["height"] = (d: any, i: number) => { return Math.abs(scaledBaseline - originalPositionFn(d, i)); }; - + var primaryAccessor = this._projectors[primaryAttr].accessor; + if (this.barLabelsEnabled && this.barLabelFormatter) { + attrToProjector["label"] = (d: any, i: number) => { + return this._barLabelFormatter(primaryAccessor(d, i)); + }; + attrToProjector["positive"] = (d: any, i: number) => originalPositionFn(d, i) <= scaledBaseline; + } return attrToProjector; } + + /** + * Gets the current hover mode. + * + * @return {string} The current hover mode. + */ + public hoverMode(): string; + /** + * Sets the hover mode for hover interactions. There are two modes: + * - "point": Selects the bar under the mouse cursor (default). + * - "line" : Selects any bar that would be hit by a line extending + * in the same direction as the bar and passing through + * the cursor. + * + * @param {string} mode The desired hover mode. + * @return {AbstractBarPlot} The calling Bar Plot. + */ + public hoverMode(mode: String): AbstractBarPlot; + public hoverMode(mode?: String): any { + if (mode == null) { + return this._hoverMode; + } + var modeLC = mode.toLowerCase(); + if (modeLC !== "point" && modeLC !== "line") { + throw new Error(mode + " is not a valid hover mode"); + } + this._hoverMode = modeLC; + return this; + } + + private clearHoverSelection() { + this._getDrawersInOrder().forEach((d, i) => { + d._renderArea.selectAll("rect").classed("not-hovered hovered", false); + }); + } + + //===== Hover logic ===== + public _hoverOverComponent(p: Point) { + // no-op + } + + public _hoverOutComponent(p: Point) { + this.clearHoverSelection(); + } + + public _doHover(p: Point): Interaction.HoverData { + var xPositionOrExtent: any = p.x; + var yPositionOrExtent: any = p.y; + if (this._hoverMode === "line") { + var maxExtent: Extent = { min: -Infinity, max: Infinity }; + if (this._isVertical) { + yPositionOrExtent = maxExtent; + } else { + xPositionOrExtent = maxExtent; + } + } + var selectedBars = this.selectBar(xPositionOrExtent, yPositionOrExtent, false); + + if (selectedBars) { + this._getDrawersInOrder().forEach((d, i) => { + d._renderArea.selectAll("rect").classed({ "hovered": false, "not-hovered": true }); + }); + selectedBars.classed({ "hovered": true, "not-hovered": false }); + } else { + this.clearHoverSelection(); + } + return { + data: selectedBars ? selectedBars.data() : null, + selection: selectedBars + }; + } + //===== /Hover logic ===== } } } diff --git a/src/components/plots/abstractPlot.ts b/src/components/plots/abstractPlot.ts index c550319f35..1bf7e7ec90 100644 --- a/src/components/plots/abstractPlot.ts +++ b/src/components/plots/abstractPlot.ts @@ -13,7 +13,7 @@ export module Plot { public _animate: boolean = false; public _animators: Animator.PlotAnimatorMap = {}; public _ANIMATION_DURATION = 250; // milliseconds - private animateOnNextRender = true; + public _animateOnNextRender = true; private nextSeriesIndex: number; /** @@ -38,7 +38,7 @@ export module Plot { public _anchor(element: D3.Selection) { super._anchor(element); - this.animateOnNextRender = true; + this._animateOnNextRender = true; this._dataChanged = true; this._updateScaleExtents(); } @@ -47,7 +47,7 @@ export module Plot { super._setup(); this._renderArea = this._content.append("g").classed("render-area", true); // HACKHACK on 591 - this._getDrawersInOrder().forEach((d) => d._renderArea = this._renderArea.append("g")); + this._getDrawersInOrder().forEach((d) => d.setup(this._renderArea.append("g"))); } public remove() { @@ -101,7 +101,7 @@ export module Plot { this._key2DatasetDrawerKey.set(key, ddk); if (this._isSetup) { - drawer._renderArea = this._renderArea.append("g"); + drawer.setup(this._renderArea.append("g")); } dataset.broadcaster.registerListener(this, () => this._onDatasetUpdate()); this._onDatasetUpdate(); @@ -111,13 +111,17 @@ export module Plot { return new _Drawer.AbstractDrawer(key); } - public _getAnimator(drawer: _Drawer.AbstractDrawer, index: number): Animator.PlotAnimator { - return new Animator.Null(); + public _getAnimator(key: string): Animator.PlotAnimator { + if (this._animate && this._animateOnNextRender) { + return this._animators[key] || new Animator.Null(); + } else { + return new Animator.Null(); + } } public _onDatasetUpdate() { this._updateScaleExtents(); - this.animateOnNextRender = true; + this._animateOnNextRender = true; this._dataChanged = true; this._render(); } @@ -188,9 +192,9 @@ export module Plot { public _doRender() { if (this._isAnchored) { - this._paint(); + this.paint(); this._dataChanged = false; - this.animateOnNextRender = false; + this._animateOnNextRender = false; } } @@ -234,28 +238,6 @@ export module Plot { } } - /** - * Applies attributes to the selection. - * - * If animation is enabled and a valid animator's key is specified, the - * attributes are applied with the animator. Otherwise, they are applied - * immediately to the selection. - * - * The animation will not animate during auto-resize renders. - * - * @param {D3.Selection} selection The selection of elements to update. - * @param {string} animatorKey The key for the animator. - * @param {AttributeToProjector} attrToProjector The set of attributes to set on the selection. - * @returns {D3.Selection} The resulting selection (potentially after the transition) - */ - public _applyAnimatedAttributes(selection: any, animatorKey: string, attrToProjector: AttributeToProjector): any { - if (this._animate && this.animateOnNextRender && this._animators[animatorKey]) { - return this._animators[animatorKey].animate(selection, attrToProjector); - } else { - return selection.attr(attrToProjector); - } - } - /** * Get the animator associated with the specified Animator key. * @@ -377,13 +359,29 @@ export module Plot { return this._datasetKeysInOrder.map((k) => this._key2DatasetDrawerKey.get(k).drawer); } - public _paint() { - var attrHash = this._generateAttrToProjector(); - var datasets = this.datasets(); - this._getDrawersInOrder().forEach((d, i) => { - var animator = this._animate ? this._getAnimator(d, i) : new Animator.Null(); - d.draw(datasets[i].data(), attrHash, animator); + public _generateDrawSteps(): _Drawer.DrawStep[] { + return [{attrToProjector: this._generateAttrToProjector(), animator: new Animator.Null()}]; + } + + public _additionalPaint(time: number) { + // no-op + } + + public _getDataToDraw() { + var datasets: D3.Map = d3.map(); + this._datasetKeysInOrder.forEach((key: string) => { + datasets.set(key, this._key2DatasetDrawerKey.get(key).dataset.data()); }); + return datasets; + } + + private paint() { + var drawSteps = this._generateDrawSteps(); + var dataToDraw = this._getDataToDraw(); + var drawers = this._getDrawersInOrder(); + var times = this._datasetKeysInOrder.map((k, i) => drawers[i].draw(dataToDraw.get(k), drawSteps)); + var maxTime = _Util.Methods.max(times, 0); + this._additionalPaint(maxTime); } } } diff --git a/src/components/plots/abstractStackedPlot.ts b/src/components/plots/abstractStackedPlot.ts index 36f36c6ea1..62fe3f714c 100644 --- a/src/components/plots/abstractStackedPlot.ts +++ b/src/components/plots/abstractStackedPlot.ts @@ -2,7 +2,7 @@ module Plottable { export module Plot { - interface StackedDatum { + export interface StackedDatum { key: any; value: number; offset?: number; @@ -12,17 +12,25 @@ export module Plot { private stackedExtent = [0, 0]; public _isVertical: boolean; + public project(attrToSet: string, accessor: any, scale?: Scale.AbstractScale) { + super.project(attrToSet, accessor, scale); + if (this._projectors["x"] && this._projectors["y"] && (attrToSet === "x" || attrToSet === "y")) { + this._updateStackOffsets(); + } + return this; + } + public _onDatasetUpdate() { super._onDatasetUpdate(); // HACKHACK Caused since onDataSource is called before projectors are set up. Should be fixed by #803 if (this._datasetKeysInOrder && this._projectors["x"] && this._projectors["y"]) { - this.updateStackOffsets(); + this._updateStackOffsets(); } } - private updateStackOffsets() { - var dataMapArray = this.generateDefaultMapArray(); - var domainKeys = this.getDomainKeys(); + public _updateStackOffsets() { + var dataMapArray = this._generateDefaultMapArray(); + var domainKeys = this._getDomainKeys(); var positiveDataMapArray: D3.Map[] = dataMapArray.map((dataMap) => { return _Util.Methods.populateMap(domainKeys, (domainKey) => { @@ -36,24 +44,24 @@ export module Plot { }); }); - this.setDatasetStackOffsets(this.stack(positiveDataMapArray), this.stack(negativeDataMapArray)); - this.updateStackExtents(); + this._setDatasetStackOffsets(this._stack(positiveDataMapArray), this._stack(negativeDataMapArray)); + this._updateStackExtents(); } - private updateStackExtents() { + public _updateStackExtents() { var datasets = this.datasets(); - var valueAccessor = this.valueAccessor(); - var maxStackExtent = _Util.Methods.max(datasets, (dataset: Dataset) => { - return _Util.Methods.max(dataset.data(), (datum: any) => { - return valueAccessor(datum) + datum["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"]; - }); - }); - - var minStackExtent = _Util.Methods.min(datasets, (dataset: Dataset) => { - return _Util.Methods.min(dataset.data(), (datum: any) => { - return valueAccessor(datum) + datum["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"]; - }); - }); + var valueAccessor = this._valueAccessor(); + var maxStackExtent = _Util.Methods.max(datasets, (dataset: Dataset) => { + return _Util.Methods.max(dataset.data(), (datum: any) => { + return +valueAccessor(datum) + datum["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"]; + }, 0); + }, 0); + + var minStackExtent = _Util.Methods.min(datasets, (dataset: Dataset) => { + return _Util.Methods.min(dataset.data(), (datum: any) => { + return +valueAccessor(datum) + datum["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"]; + }, 0); + }, 0); this.stackedExtent = [Math.min(minStackExtent, 0), Math.max(0, maxStackExtent)]; } @@ -62,15 +70,20 @@ export module Plot { * Feeds the data through d3's stack layout function which will calculate * the stack offsets and use the the function declared in .out to set the offsets on the data. */ - private stack(dataArray: D3.Map[]): D3.Map[] { + public _stack(dataArray: D3.Map[]): D3.Map[] { + // HACKHACK d3's stack layout logic crashes on 0-length dataArray https://github.com/mbostock/d3/issues/2004 + if (dataArray.length === 0) { + return dataArray; + } + var outFunction = (d: StackedDatum, y0: number, y: number) => { d.offset = y0; }; d3.layout.stack() .x((d) => d.key) - .y((d) => d.value) - .values((d) => this.getDomainKeys().map((domainKey) => d.get(domainKey))) + .y((d) => +d.value) + .values((d) => this._getDomainKeys().map((domainKey) => d.get(domainKey))) .out(outFunction)(dataArray); return dataArray; @@ -80,9 +93,9 @@ export module Plot { * After the stack offsets have been determined on each separate dataset, the offsets need * to be determined correctly on the overall datasets */ - private setDatasetStackOffsets(positiveDataMapArray: D3.Map[], negativeDataMapArray: D3.Map[]) { - var keyAccessor = this.keyAccessor(); - var valueAccessor = this.valueAccessor(); + public _setDatasetStackOffsets(positiveDataMapArray: D3.Map[], negativeDataMapArray: D3.Map[]) { + var keyAccessor = this._keyAccessor(); + var valueAccessor = this._valueAccessor(); this.datasets().forEach((dataset, datasetIndex) => { var positiveDataMap = positiveDataMapArray[datasetIndex]; @@ -103,8 +116,8 @@ export module Plot { }); } - private getDomainKeys(): string[] { - var keyAccessor = this.keyAccessor(); + public _getDomainKeys(): string[] { + var keyAccessor = this._keyAccessor(); var domainKeys = d3.set(); var datasets = this.datasets(); @@ -117,11 +130,11 @@ export module Plot { return domainKeys.values(); } - private generateDefaultMapArray(): D3.Map[] { - var keyAccessor = this.keyAccessor(); - var valueAccessor = this.valueAccessor(); + public _generateDefaultMapArray(): D3.Map[] { + var keyAccessor = this._keyAccessor(); + var valueAccessor = this._valueAccessor(); var datasets = this.datasets(); - var domainKeys = this.getDomainKeys(); + var domainKeys = this._getDomainKeys(); var dataMapArray = datasets.map(() => { return _Util.Methods.populateMap(domainKeys, (domainKey) => { @@ -153,11 +166,11 @@ export module Plot { } } - private keyAccessor(): AppliedAccessor { + public _keyAccessor(): AppliedAccessor { return this._isVertical ? this._projectors["x"].accessor : this._projectors["y"].accessor; } - private valueAccessor(): AppliedAccessor { + public _valueAccessor(): AppliedAccessor { return this._isVertical ? this._projectors["y"].accessor : this._projectors["x"].accessor; } } diff --git a/src/components/plots/areaPlot.ts b/src/components/plots/areaPlot.ts index 65bd91e298..2ee2a19b30 100644 --- a/src/components/plots/areaPlot.ts +++ b/src/components/plots/areaPlot.ts @@ -12,7 +12,6 @@ export module Plot { * Constructs an AreaPlot. * * @constructor - * @param {DatasetInterface | any} dataset The dataset to render. * @param {QuantitativeScale} xScale The x scale to use. * @param {QuantitativeScale} yScale The y scale to use. */ @@ -23,10 +22,10 @@ export module Plot { this.project("fill", () => Core.Colors.INDIGO); // default this.project("fill-opacity", () => 0.25); // default this.project("stroke", () => Core.Colors.INDIGO); // default - this._animators["area-reset"] = new Animator.Null(); - this._animators["area"] = new Animator.Base() - .duration(600) - .easing("exp-in-out"); + this._animators["reset"] = new Animator.Null(); + this._animators["main"] = new Animator.Base() + .duration(600) + .easing("exp-in-out"); } public _onDatasetUpdate() { @@ -36,6 +35,10 @@ export module Plot { } } + public _getDrawer(key: string) { + return new Plottable._Drawer.Area(key); + } + public _updateYDomainer() { super._updateYDomainer(); @@ -74,45 +77,6 @@ export module Plot { return this._generateAttrToProjector()["y0"]; } - // HACKHACK #1106 - should use drawers for paint logic - public _paint() { - super._paint(); - var attrToProjector = this._generateAttrToProjector(); - var xFunction = attrToProjector["x"]; - var y0Function = attrToProjector["y0"]; - var yFunction = attrToProjector["y"]; - delete attrToProjector["x"]; - delete attrToProjector["y0"]; - delete attrToProjector["y"]; - - var area = d3.svg.area() - .x(xFunction) - .y0(y0Function) - .defined((d, i) => this._rejectNullsAndNaNs(d, i, xFunction) && this._rejectNullsAndNaNs(d, i, yFunction)); - attrToProjector["d"] = area; - - var datasets = this.datasets(); - this._getDrawersInOrder().forEach((d, i) => { - var dataset = datasets[i]; - var areaPath: D3.Selection; - if (d._renderArea.select(".area").node()) { - areaPath = d._renderArea.select(".area"); - } else { - // Make sure to insert the area before the line - areaPath = d._renderArea.insert("path", ".line").classed("area", true); - } - areaPath.datum(dataset.data()); - - if (this._dataChanged) { - area.y1(this._getResetYFunction()); - this._applyAnimatedAttributes(areaPath, "area-reset", attrToProjector); - } - - area.y1(yFunction); - this._applyAnimatedAttributes(areaPath, "area", attrToProjector); - }); - } - public _wholeDatumAttributes() { var wholeDatumAttributes = super._wholeDatumAttributes(); wholeDatumAttributes.push("y0"); diff --git a/src/components/plots/clusteredBarPlot.ts b/src/components/plots/clusteredBarPlot.ts index 125f5cb8c1..7598473851 100644 --- a/src/components/plots/clusteredBarPlot.ts +++ b/src/components/plots/clusteredBarPlot.ts @@ -40,41 +40,22 @@ export module Plot { return attrToProjector; } - private cluster(accessor: _Accessor) { + public _getDataToDraw() { + var accessor = this._isVertical ? this._projectors["x"].accessor : this._projectors["y"].accessor; this.innerScale.domain(this._datasetKeysInOrder); - var clusters: {[key: string]: any[]} = {}; + var clusters: D3.Map = d3.map(); this._datasetKeysInOrder.forEach((key: string) => { var data = this._key2DatasetDrawerKey.get(key).dataset.data(); - clusters[key] = data.map((d, i) => { + clusters.set(key, data.map((d, i) => { var val = accessor(d, i); var primaryScale: Scale.AbstractScale = this._isVertical ? this._xScale : this._yScale; d["_PLOTTABLE_PROTECTED_FIELD_POSITION"] = primaryScale.scale(val) + this.innerScale.scale(key); return d; - }); + })); }); return clusters; } - - public _paint() { - var attrHash = this._generateAttrToProjector(); - var accessor = this._isVertical ? this._projectors["x"].accessor : this._projectors["y"].accessor; - var clusteredData = this.cluster(accessor); - this._getDrawersInOrder().forEach((d) => d.draw(clusteredData[d.key], attrHash)); - - var primaryScale: Scale.AbstractScale = this._isVertical ? this._yScale : this._xScale; - var scaledBaseline = primaryScale.scale(this._baselineValue); - - var baselineAttr: any = { - "x1": this._isVertical ? 0 : scaledBaseline, - "y1": this._isVertical ? scaledBaseline : 0, - "x2": this._isVertical ? this.width() : scaledBaseline, - "y2": this._isVertical ? scaledBaseline : this.height() - }; - - this._applyAnimatedAttributes(this._baseline, "baseline", baselineAttr); - - } } } } diff --git a/src/components/plots/gridPlot.ts b/src/components/plots/gridPlot.ts index 15475481d7..1f8eff298f 100644 --- a/src/components/plots/gridPlot.ts +++ b/src/components/plots/gridPlot.ts @@ -33,6 +33,7 @@ export module Plot { this._colorScale = colorScale; this.project("fill", "value", colorScale); // default + this._animators["cells"] = new Animator.Null(); } public _addDataset(key: string, dataset: Dataset) { @@ -43,6 +44,10 @@ export module Plot { super._addDataset(key, dataset); } + public _getDrawer(key: string) { + return new _Drawer.Element(key).svgElement("rect"); + } + /** * @param {string} attrToSet One of ["x", "y", "fill"]. If "fill" is used, * the data should return a valid CSS color. @@ -55,20 +60,17 @@ export module Plot { return this; } - public _paint() { - var dataset = this.datasets()[0]; - var cells = this._renderArea.selectAll("rect").data(dataset.data()); - cells.enter().append("rect"); - + public _generateAttrToProjector() { + var attrToProjector = super._generateAttrToProjector(); var xStep = this._xScale.rangeBand(); var yStep = this._yScale.rangeBand(); - - var attrToProjector = this._generateAttrToProjector(); attrToProjector["width"] = () => xStep; attrToProjector["height"] = () => yStep; + return attrToProjector; + } - this._applyAnimatedAttributes(cells, "cells", attrToProjector); - cells.exit().remove(); + public _generateDrawSteps(): _Drawer.DrawStep[] { + return [{attrToProjector: this._generateAttrToProjector(), animator: this._getAnimator("cells")}]; } } } diff --git a/src/components/plots/linePlot.ts b/src/components/plots/linePlot.ts index ed433a40b0..19503f32f8 100644 --- a/src/components/plots/linePlot.ts +++ b/src/components/plots/linePlot.ts @@ -5,18 +5,11 @@ export module Plot { export class Line extends AbstractXYPlot { public _yScale: Scale.AbstractQuantitative; - public _animators: Animator.PlotAnimatorMap = { - "line-reset" : new Animator.Null(), - "line" : new Animator.Base() - .duration(600) - .easing("exp-in-out") - }; /** * Constructs a LinePlot. * * @constructor - * @param {any | DatasetInterface} dataset The dataset to render. * @param {QuantitativeScale} xScale The x scale to use. * @param {QuantitativeScale} yScale The y scale to use. */ @@ -25,6 +18,19 @@ export module Plot { this.classed("line-plot", true); this.project("stroke", () => Core.Colors.INDIGO); // default this.project("stroke-width", () => "2px"); // default + this._animators["reset"] = new Animator.Null(); + this._animators["main"] = new Animator.Base() + .duration(600) + .easing("exp-in-out"); + } + + public _rejectNullsAndNaNs(d: any, i: number, projector: AppliedAccessor) { + var value = projector(d, i); + return value != null && value === value; + } + + public _getDrawer(key: string) { + return new Plottable._Drawer.Line(key); } public _getResetYFunction() { @@ -39,6 +45,19 @@ export module Plot { return (d: any, i: number) => scaledStartValue; } + public _generateDrawSteps(): _Drawer.DrawStep[] { + var drawSteps: _Drawer.DrawStep[] = []; + if (this._dataChanged) { + var attrToProjector = this._generateAttrToProjector(); + attrToProjector["y"] = this._getResetYFunction(); + drawSteps.push({attrToProjector: attrToProjector, animator: this._getAnimator("reset")}); + } + + drawSteps.push({attrToProjector: this._generateAttrToProjector(), animator: this._getAnimator("main")}); + + return drawSteps; + } + public _generateAttrToProjector() { var attrToProjector = super._generateAttrToProjector(); var wholeDatumAttributes = this._wholeDatumAttributes(); @@ -48,46 +67,11 @@ export module Plot { var projector = attrToProjector[attribute]; attrToProjector[attribute] = (data: any[], i: number) => data.length > 0 ? projector(data[0], i) : null; }); - return attrToProjector; - } - - public _rejectNullsAndNaNs(d: any, i: number, projector: AppliedAccessor) { - var value = projector(d, i); - return value != null && value === value; - } - // HACKHACK #1106 - should use drawers for paint logic - public _paint() { - var attrToProjector = this._generateAttrToProjector(); var xFunction = attrToProjector["x"]; var yFunction = attrToProjector["y"]; - delete attrToProjector["x"]; - delete attrToProjector["y"]; - - var line = d3.svg.line() - .x(xFunction) - .defined((d, i) => this._rejectNullsAndNaNs(d, i, xFunction) && this._rejectNullsAndNaNs(d, i, yFunction)); - attrToProjector["d"] = line; - - var datasets = this.datasets(); - this._getDrawersInOrder().forEach((d, i) => { - var dataset = datasets[i]; - var linePath: D3.Selection; - if (d._renderArea.select(".line").node()) { - linePath = d._renderArea.select(".line"); - } else { - linePath = d._renderArea.append("path").classed("line", true); - } - linePath.datum(dataset.data()); - - if (this._dataChanged) { - line.y(this._getResetYFunction()); - this._applyAnimatedAttributes(linePath, "line-reset", attrToProjector); - } - - line.y(yFunction); - this._applyAnimatedAttributes(linePath, "line", attrToProjector); - }); + attrToProjector["defined"] = (d, i) => this._rejectNullsAndNaNs(d, i, xFunction) && this._rejectNullsAndNaNs(d, i, yFunction); + return attrToProjector; } public _wholeDatumAttributes() { diff --git a/src/components/plots/piePlot.ts b/src/components/plots/piePlot.ts index 186f152b24..e1dbdedfc8 100644 --- a/src/components/plots/piePlot.ts +++ b/src/components/plots/piePlot.ts @@ -41,58 +41,24 @@ export module Plot { public _generateAttrToProjector(): AttributeToProjector { - var attrToProjector = this.retargetProjectors(super._generateAttrToProjector()); - var innerRadiusF = attrToProjector["inner-radius"] || d3.functor(0); - var outerRadiusF = attrToProjector["outer-radius"] || d3.functor(Math.min(this.width(), this.height()) / 2); - attrToProjector["d"] = d3.svg.arc() - .innerRadius(innerRadiusF) - .outerRadius(outerRadiusF); - delete attrToProjector["inner-radius"]; - delete attrToProjector["outer-radius"]; + var attrToProjector = super._generateAttrToProjector(); + attrToProjector["inner-radius"] = attrToProjector["inner-radius"] || d3.functor(0); + attrToProjector["outer-radius"] = attrToProjector["outer-radius"] || d3.functor(Math.min(this.width(), this.height()) / 2); if (attrToProjector["fill"] == null) { attrToProjector["fill"] = (d: any, i: number) => Pie.DEFAULT_COLOR_SCALE.scale(String(i)); } - delete attrToProjector["value"]; - return attrToProjector; - } + var defaultAccessor = (d: any) => d.value; + var valueProjector = this._projectors["value"]; + attrToProjector["value"] = valueProjector ? valueProjector.accessor : defaultAccessor; - /** - * Since the data goes through a pie function, which returns an array of ArcDescriptors, - * projectors will need to be retargeted so they point to the data portion of each arc descriptor. - */ - private retargetProjectors(attrToProjector: AttributeToProjector): AttributeToProjector { - var retargetedAttrToProjector: AttributeToProjector = {}; - d3.entries(attrToProjector).forEach((entry) => { - retargetedAttrToProjector[entry.key] = (d: D3.Layout.ArcDescriptor, i: number) => entry.value(d.data, i); - }); - return retargetedAttrToProjector; + return attrToProjector; } public _getDrawer(key: string): _Drawer.AbstractDrawer { - return new Plottable._Drawer.Arc(key); + return new Plottable._Drawer.Arc(key).setClass("arc"); } - - public _paint() { - var attrHash = this._generateAttrToProjector(); - var datasets = this.datasets(); - this._getDrawersInOrder().forEach((d, i) => { - var animator = this._animate ? this._getAnimator(d, i) : new Animator.Null(); - var pieData = this.pie(datasets[i].data()); - d.draw(pieData, attrHash, animator); - }); - } - - private pie(d: any[]): D3.Layout.ArcDescriptor[] { - var defaultAccessor = (d: any) => d.value; - var valueProjector = this._projectors["value"]; - var valueAccessor = valueProjector ? valueProjector.accessor : defaultAccessor; - return d3.layout.pie() - .sort(null) - .value(valueAccessor)(d); - } - } } } diff --git a/src/components/plots/scatterPlot.ts b/src/components/plots/scatterPlot.ts index a1932ac05e..30b9b56055 100644 --- a/src/components/plots/scatterPlot.ts +++ b/src/components/plots/scatterPlot.ts @@ -2,20 +2,13 @@ module Plottable { export module Plot { - export class Scatter extends AbstractXYPlot { - - public _animators: Animator.PlotAnimatorMap = { - "circles-reset" : new Animator.Null(), - "circles" : new Animator.IterativeDelay() - .duration(250) - .delay(5) - }; + export class Scatter extends AbstractXYPlot implements Interaction.Hoverable { + private closeDetectionRadius = 5; /** * Constructs a ScatterPlot. * * @constructor - * @param {DatasetInterface | any} dataset The dataset to render. * @param {Scale} xScale The x scale to use. * @param {Scale} yScale The y scale to use. */ @@ -25,6 +18,10 @@ export module Plot { this.project("r", 3); // default this.project("opacity", 0.6); // default this.project("fill", () => Core.Colors.INDIGO); // default + this._animators["circles-reset"] = new Animator.Null(); + this._animators["circles"] = new Animator.Base() + .duration(250) + .delay(5); } /** @@ -39,6 +36,10 @@ export module Plot { return this; } + public _getDrawer(key: string) { + return new Plottable._Drawer.Element(key).svgElement("circle"); + } + public _generateAttrToProjector() { var attrToProjector = super._generateAttrToProjector(); attrToProjector["cx"] = attrToProjector["x"]; @@ -48,27 +49,70 @@ export module Plot { return attrToProjector; } - // HACKHACK #1106 - should use drawers for paint logic - public _paint() { + public _generateDrawSteps(): _Drawer.DrawStep[] { + var drawSteps: _Drawer.DrawStep[] = []; + if (this._dataChanged) { + var resetAttrToProjector = this._generateAttrToProjector(); + resetAttrToProjector["r"] = () => 0; + drawSteps.push({attrToProjector: resetAttrToProjector, animator: this._getAnimator("circles-reset")}); + } + + drawSteps.push({attrToProjector: this._generateAttrToProjector(), animator: this._getAnimator("circles")}); + return drawSteps; + } + + public _getClosestStruckPoint(p: Point, range: number) { + var drawers = <_Drawer.Element[]> this._getDrawersInOrder(); var attrToProjector = this._generateAttrToProjector(); - var datasets = this.datasets(); - - this._getDrawersInOrder().forEach((d, i) => { - var dataset = datasets[i]; - var circles = d._renderArea.selectAll("circle").data(dataset.data()); - circles.enter().append("circle"); - - if (this._dataChanged) { - var rFunction = attrToProjector["r"]; - attrToProjector["r"] = () => 0; - this._applyAnimatedAttributes(circles, "circles-reset", attrToProjector); - attrToProjector["r"] = rFunction; - } - - this._applyAnimatedAttributes(circles, "circles", attrToProjector); - circles.exit().remove(); + + var getDistSq = (d: any, i: number) => { + var dx = attrToProjector["cx"](d, i) - p.x; + var dy = attrToProjector["cy"](d, i) - p.y; + return (dx * dx + dy * dy); + }; + + var overAPoint = false; + var closestElement: Element; + var minDistSq = range * range; + + drawers.forEach((drawer) => { + drawer._getDrawSelection().each(function (d, i) { + var distSq = getDistSq(d, i); + var r = attrToProjector["r"](d, i); + + if (distSq < r * r) { // cursor is over this point + if (!overAPoint || distSq < minDistSq) { + closestElement = this; + minDistSq = distSq; + } + overAPoint = true; + } else if (!overAPoint && distSq < minDistSq) { + closestElement = this; + minDistSq = distSq; + } + }); }); + + var closestSelection = d3.select(closestElement); + return { + selection: closestElement ? closestSelection : null, + data: closestElement ? closestSelection.data() : null + }; + } + + //===== Hover logic ===== + public _hoverOverComponent(p: Point) { + // no-op + } + + public _hoverOutComponent(p: Point) { + // no-op + } + + public _doHover(p: Point): Interaction.HoverData { + return this._getClosestStruckPoint(p, this.closeDetectionRadius); } + //===== /Hover logic ===== } } } diff --git a/src/components/plots/stackedAreaPlot.ts b/src/components/plots/stackedAreaPlot.ts index 6dcb23b799..a39e74be61 100644 --- a/src/components/plots/stackedAreaPlot.ts +++ b/src/components/plots/stackedAreaPlot.ts @@ -22,7 +22,7 @@ export module Plot { } public _getDrawer(key: string) { - return new Plottable._Drawer.Area(key); + return new Plottable._Drawer.Area(key).drawLine(false); } public _setup() { @@ -30,8 +30,18 @@ export module Plot { this._baseline = this._renderArea.append("line").classed("baseline", true); } - public _paint() { - super._paint(); + public _updateStackOffsets() { + var domainKeys = this._getDomainKeys(); + var keyAccessor = this._isVertical ? this._projectors["x"].accessor : this._projectors["y"].accessor; + var keySets = this.datasets().map((dataset) => d3.set(dataset.data().map((datum, i) => keyAccessor(datum, i).toString())).values()); + + if (keySets.some((keySet) => keySet.length !== domainKeys.length)) { + _Util.Methods.warn("the domains across the datasets are not the same. Plot may produce unintended behavior."); + } + super._updateStackOffsets(); + } + + public _additionalPaint() { var scaledBaseline = this._yScale.scale(this._baselineValue); var baselineAttr: any = { "x1": 0, @@ -39,8 +49,8 @@ export module Plot { "x2": this.width(), "y2": scaledBaseline }; - this._applyAnimatedAttributes(this._baseline, "baseline", baselineAttr); + this._getAnimator("baseline").animate(this._baseline, baselineAttr); } public _updateYDomainer() { @@ -60,18 +70,9 @@ export module Plot { public _generateAttrToProjector() { var attrToProjector = super._generateAttrToProjector(); - var xFunction = attrToProjector["x"]; - var yFunction = (d: any) => this._yScale.scale(d.y + d["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"]); - var y0Function = (d: any) => this._yScale.scale(d["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"]); - - delete attrToProjector["x"]; - delete attrToProjector["y0"]; - delete attrToProjector["y"]; - - attrToProjector["d"] = d3.svg.area() - .x(xFunction) - .y0(y0Function) - .y1(yFunction); + var yAccessor = this._projectors["y"].accessor; + attrToProjector["y"] = (d: any) => this._yScale.scale(+yAccessor(d) + d["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"]); + attrToProjector["y0"] = (d: any) => this._yScale.scale(d["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"]); // Align fill with first index var fillProjector = attrToProjector["fill"]; diff --git a/src/components/plots/stackedBarPlot.ts b/src/components/plots/stackedBarPlot.ts index 2ac6ea694e..4a33bcc98c 100644 --- a/src/components/plots/stackedBarPlot.ts +++ b/src/components/plots/stackedBarPlot.ts @@ -2,7 +2,7 @@ module Plottable { export module Plot { - export class StackedBar extends AbstractStacked { + export class StackedBar extends AbstractBarPlot { public _baselineValue: number; public _baseline: D3.Selection; public _barAlignmentFactor: number; @@ -19,7 +19,6 @@ export module Plot { constructor(xScale?: Scale.AbstractScale, yScale?: Scale.AbstractScale, isVertical = true) { this._isVertical = isVertical; // Has to be set before super() this._baselineValue = 0; - this._barAlignmentFactor = 0.5; super(xScale, yScale); this.classed("bar-plot", true); this.project("fill", () => Core.Colors.INDIGO); @@ -27,18 +26,18 @@ export module Plot { this._isVertical = isVertical; } - public _setup() { - AbstractBarPlot.prototype._setup.call(this); - } + public _getAnimator(key: string): Animator.PlotAnimator { + if (this._animate && this._animateOnNextRender) { + if (this._animators[key]) { + return this._animators[key]; + } else if (key === "stacked-bar") { + var primaryScale: Scale.AbstractScale = this._isVertical ? this._yScale : this._xScale; + var scaledBaseline = primaryScale.scale(this._baselineValue); + return new Animator.MovingRect(scaledBaseline, this._isVertical); + } + } - public _getAnimator(drawer: _Drawer.AbstractDrawer, index: number) { - var primaryScale: Scale.AbstractScale = this._isVertical ? this._yScale : this._xScale; - var scaledBaseline = primaryScale.scale(this._baselineValue); - return new Animator.MovingRect(scaledBaseline, this._isVertical); - } - - public _getDrawer(key: string) { - return AbstractBarPlot.prototype._getDrawer.apply(this, [key]); + return new Animator.Null(); } public _generateAttrToProjector() { @@ -48,46 +47,71 @@ export module Plot { var primaryScale: Scale.AbstractScale = this._isVertical ? this._yScale : this._xScale; var primaryAccessor = this._projectors[primaryAttr].accessor; var getStart = (d: any) => primaryScale.scale(d["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"]); - var getEnd = (d: any) => primaryScale.scale(primaryAccessor(d) + d["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"]); + var getEnd = (d: any) => primaryScale.scale(+primaryAccessor(d) + d["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"]); var heightF = (d: any) => Math.abs(getEnd(d) - getStart(d)); var widthF = attrToProjector["width"]; attrToProjector["height"] = this._isVertical ? heightF : widthF; attrToProjector["width"] = this._isVertical ? widthF : heightF; - var attrFunction = (d: any) => primaryAccessor(d) < 0 ? getStart(d) : getEnd(d); + var attrFunction = (d: any) => +primaryAccessor(d) < 0 ? getStart(d) : getEnd(d); attrToProjector[primaryAttr] = (d: any) => this._isVertical ? attrFunction(d) : attrFunction(d) - heightF(d); return attrToProjector; } - public _paint() { - super._paint(); - var primaryScale: Scale.AbstractScale = this._isVertical ? this._yScale : this._xScale; - var scaledBaseline = primaryScale.scale(this._baselineValue); - var baselineAttr: any = { - "x1": this._isVertical ? 0 : scaledBaseline, - "y1": this._isVertical ? scaledBaseline : 0, - "x2": this._isVertical ? this.width() : scaledBaseline, - "y2": this._isVertical ? scaledBaseline : this.height() - }; - this._baseline.attr(baselineAttr); + public _generateDrawSteps(): _Drawer.DrawStep[] { + return [{attrToProjector: this._generateAttrToProjector(), animator: this._getAnimator("stacked-bar")}]; + } + + public project(attrToSet: string, accessor: any, scale?: Scale.AbstractScale) { + super.project(attrToSet, accessor, scale); + AbstractStacked.prototype.project.apply(this, [attrToSet, accessor, scale]); + return this; + } + + public _onDatasetUpdate() { + super._onDatasetUpdate(); + AbstractStacked.prototype._onDatasetUpdate.apply(this); + return this; + } + + //===== Stack logic from AbstractStackedPlot ===== + public _updateStackOffsets() { + AbstractStacked.prototype._updateStackOffsets.call(this); + } + + public _updateStackExtents() { + AbstractStacked.prototype._updateStackExtents.call(this); + } + + public _stack(dataArray: D3.Map[]): D3.Map[] { + return AbstractStacked.prototype._stack.call(this, dataArray); + } + + public _setDatasetStackOffsets(positiveDataMapArray: D3.Map[], negativeDataMapArray: D3.Map[]) { + AbstractStacked.prototype._setDatasetStackOffsets.call(this, positiveDataMapArray, negativeDataMapArray); + } + + public _getDomainKeys() { + return AbstractStacked.prototype._getDomainKeys.call(this); } - public baseline(value: number) { - return AbstractBarPlot.prototype.baseline.apply(this, [value]); + public _generateDefaultMapArray(): D3.Map[] { + return AbstractStacked.prototype._generateDefaultMapArray.call(this); } - public _updateDomainer(scale: Scale.AbstractScale) { - return AbstractBarPlot.prototype._updateDomainer.apply(this, [scale]); + public _updateScaleExtents() { + AbstractStacked.prototype._updateScaleExtents.call(this); } - public _updateXDomainer() { - return AbstractBarPlot.prototype._updateXDomainer.apply(this); + public _keyAccessor(): AppliedAccessor { + return AbstractStacked.prototype._keyAccessor.call(this); } - public _updateYDomainer() { - return AbstractBarPlot.prototype._updateYDomainer.apply(this); + public _valueAccessor(): AppliedAccessor { + return AbstractStacked.prototype._valueAccessor.call(this); } + //===== /Stack logic ===== } } } diff --git a/src/components/plots/verticalBarPlot.ts b/src/components/plots/verticalBarPlot.ts index 0d64ceb52c..e815afe681 100644 --- a/src/components/plots/verticalBarPlot.ts +++ b/src/components/plots/verticalBarPlot.ts @@ -18,7 +18,6 @@ export module Plot { * Constructs a VerticalBarPlot. * * @constructor - * @param {DatasetInterface | any} dataset The dataset to render. * @param {Scale} xScale The x scale to use. * @param {QuantitativeScale} yScale The y scale to use. */ diff --git a/src/components/table.ts b/src/components/table.ts index 523dcab096..2fd1d7c579 100644 --- a/src/components/table.ts +++ b/src/components/table.ts @@ -289,8 +289,22 @@ export module Component { * Sets the layout weight of a particular row. * Space is allocated to rows based on their weight. Rows with higher weights receive proportionally more space. * - * A common case would be to have one graph take up 2/3rds of the space, - * and the other graph take up 1/3rd. + * A common case would be to have one row take up 2/3rds of the space, + * and the other row take up 1/3rd. + * + * Example: + * + * ```JavaScript + * plot = new Plottable.Component.Table([ + * [row1], + * [row2] + * ]); + * + * // assign twice as much space to the first row + * plot + * .rowWeight(0, 2) + * .rowWeight(1, 1) + * ``` * * @param {number} index The index of the row. * @param {number} weight The weight to be set on the row. @@ -305,9 +319,8 @@ export module Component { /** * Sets the layout weight of a particular column. * Space is allocated to columns based on their weight. Columns with higher weights receive proportionally more space. - * - * A common case would be to have one graph take up 2/3rds of the space, - * and the other graph take up 1/3rd. + * + * Please see `rowWeight` docs for an example. * * @param {number} index The index of the column. * @param {number} weight The weight to be set on the column. diff --git a/src/core/animator.ts b/src/core/animator.ts index 7538b70311..46ba81d2c4 100644 --- a/src/core/animator.ts +++ b/src/core/animator.ts @@ -15,6 +15,13 @@ export module Animator { * animators. */ animate(selection: any, attrToProjector: AttributeToProjector): any; + + /** + * Given the number of elements, return the total time the animation requires + * @param number numberofIterations The number of elements that will be drawn + * @returns {any} The time required for the animation + */ + getTiming(numberOfIterations: number): number; } export interface PlotAnimatorMap { diff --git a/src/core/colors.ts b/src/core/colors.ts index 0bdf925acd..76ce3265a2 100644 --- a/src/core/colors.ts +++ b/src/core/colors.ts @@ -6,16 +6,16 @@ export module Core { * Colors we use as defaults on a number of graphs. */ export class Colors { - public static CORAL_RED = "#fd373e"; - public static INDIGO = "#5279c7"; + public static CORAL_RED = "#fd373e"; + public static INDIGO = "#5279c7"; public static ROBINS_EGG_BLUE = "#06cccc"; - public static FERN = "#63c261"; - public static BURNING_ORANGE = "#ff7939"; - public static ROYAL_HEATH = "#962565"; - public static CONIFER = "#99ce50"; - public static CERISE_RED = "#db2e65"; - public static BRIGHT_SUN = "#fad419"; - public static JACARTA = "#2c2b6f"; + public static FERN = "#63c261"; + public static BURNING_ORANGE = "#ff7939"; + public static ROYAL_HEATH = "#962565"; + public static CONIFER = "#99ce50"; + public static CERISE_RED = "#db2e65"; + public static BRIGHT_SUN = "#fad419"; + public static JACARTA = "#2c2b6f"; public static PLOTTABLE_COLORS = [ diff --git a/src/core/domainer.ts b/src/core/domainer.ts index 541dce523e..ae219041bf 100644 --- a/src/core/domainer.ts +++ b/src/core/domainer.ts @@ -49,7 +49,7 @@ module Plottable { } else if (extents.length === 0) { domain = scale._defaultExtent(); } else { - domain = [_Util.Methods.min(extents, (e) => e[0]), _Util.Methods.max(extents, (e) => e[1])]; + domain = [_Util.Methods.min(extents, (e) => e[0], 0), _Util.Methods.max(extents, (e) => e[1], 0)]; } domain = this.includeDomain(domain); domain = this.padDomain(scale, domain); diff --git a/src/core/interfaces.ts b/src/core/interfaces.ts index 00f495d0ec..2de682b19f 100644 --- a/src/core/interfaces.ts +++ b/src/core/interfaces.ts @@ -1,18 +1,4 @@ module Plottable { - // TODO: I'm pretty sure this is a mistake, and we always just mean - // any[]|DataSource. I mean, we just straight-up cast this thing into a - // DataSource, but then we expect data to be a function - export interface DatasetInterface { - data: any[]; - metadata: Metadata; - } - - // TODO: only used by DatasetInterface, remove when DatasetInterface removed - export interface Metadata { - cssClass?: string; - color?: string; - } - export interface _Accessor { (datum: any, index?: number, metadata?: any): any; }; diff --git a/src/drawers/abstractDrawer.ts b/src/drawers/abstractDrawer.ts index dc98916e35..c69f79a83e 100644 --- a/src/drawers/abstractDrawer.ts +++ b/src/drawers/abstractDrawer.ts @@ -2,10 +2,31 @@ module Plottable { export module _Drawer { + /** + * A step for the drawer to draw. + * + * Specifies how AttributeToProjector needs to be animated. + */ + export interface DrawStep { + attrToProjector: AttributeToProjector; + animator: Animator.PlotAnimator; + } + export class AbstractDrawer { public _renderArea: D3.Selection; + public _className: string; public key: string; + /** + * Sets the class, which needs to be applied to bound elements. + * + * @param{string} className The class name to be applied. + */ + public setClass(className: string): AbstractDrawer { + this._className = className; + return this; + } + /** * Constructs a Drawer * @@ -16,6 +37,10 @@ export module _Drawer { this.key = key; } + public setup(area: D3.Selection) { + this._renderArea = area; + } + /** * Removes the Drawer and its renderArea */ @@ -26,13 +51,44 @@ export module _Drawer { } /** - * Draws the data into the renderArea using the attrHash for attributes + * Enter new data to render area and creates binding + * + * @param{any[]} data The data to be drawn + */ + public _enterData(data: any[]) { + // no-op + } + + /** + * Draws data using one step + * + * @param{DataStep} step The step, how data should be drawn. + */ + public _drawStep(step: DrawStep) { + // no-op + } + + public _numberOfAnimationIterations(data: any[]): number { + return data.length; + } + + /** + * Draws the data into the renderArea using the spefic steps * * @param{any[]} data The data to be drawn - * @param{attrHash} AttributeToProjector The list of attributes to set on the data + * @param{DrawStep[]} drawSteps The list of steps, which needs to be drawn */ - public draw(data: any[], attrToProjector: AttributeToProjector, animator = new Animator.Null()) { - // no-op + public draw(data: any[], drawSteps: DrawStep[]): number { + this._enterData(data); + var numberOfIterations = this._numberOfAnimationIterations(data); + + var delay = 0; + drawSteps.forEach((drawStep, i) => { + _Util.Methods.setTimeout(() => this._drawStep(drawStep), delay); + delay += drawStep.animator.getTiming(numberOfIterations); + }); + + return delay; } } } diff --git a/src/drawers/arcDrawer.ts b/src/drawers/arcDrawer.ts index eafbd074f0..fa68bed86f 100644 --- a/src/drawers/arcDrawer.ts +++ b/src/drawers/arcDrawer.ts @@ -2,16 +2,47 @@ module Plottable { export module _Drawer { - export class Arc extends AbstractDrawer { + export class Arc extends Element { - public draw(data: any[], attrToProjector: AttributeToProjector, animator = new Animator.Null()) { - var svgElement = "path"; - var dataElements = this._renderArea.selectAll(svgElement).data(data); + constructor(key: string) { + super(key); + this._svgElement = "path"; + } + + private createArc(innerRadiusF: AppliedAccessor, outerRadiusF: AppliedAccessor) { + return d3.svg.arc() + .innerRadius(innerRadiusF) + .outerRadius(outerRadiusF); + } + + private retargetProjectors(attrToProjector: AttributeToProjector): AttributeToProjector { + var retargetedAttrToProjector: AttributeToProjector = {}; + d3.entries(attrToProjector).forEach((entry) => { + retargetedAttrToProjector[entry.key] = (d: D3.Layout.ArcDescriptor, i: number) => entry.value(d.data, i); + }); + return retargetedAttrToProjector; + } + + public _drawStep(step: DrawStep) { + var attrToProjector = _Util.Methods.copyMap(step.attrToProjector); + attrToProjector = this.retargetProjectors(attrToProjector); + var innerRadiusF = attrToProjector["inner-radius"]; + var outerRadiusF = attrToProjector["outer-radius"]; + delete attrToProjector["inner-radius"]; + delete attrToProjector["outer-radius"]; + + attrToProjector["d"] = this.createArc(innerRadiusF, outerRadiusF); + return super._drawStep({attrToProjector: attrToProjector, animator: step.animator}); + } + + public draw(data: any[], drawSteps: DrawStep[]) { + var valueAccessor = drawSteps[0].attrToProjector["value"]; + var pie = d3.layout.pie() + .sort(null) + .value(valueAccessor)(data); - dataElements.enter().append(svgElement); - dataElements.classed("arc", true); - animator.animate(dataElements, attrToProjector); - dataElements.exit().remove(); + drawSteps.forEach(s => delete s.attrToProjector["value"]); + return super.draw(pie, drawSteps); } } } diff --git a/src/drawers/areaDrawer.ts b/src/drawers/areaDrawer.ts index 97127bedd3..6d4236cd56 100644 --- a/src/drawers/areaDrawer.ts +++ b/src/drawers/areaDrawer.ts @@ -2,15 +2,79 @@ module Plottable { export module _Drawer { - export class Area extends AbstractDrawer { + export class Area extends Line { + private areaSelection: D3.Selection; + private _drawLine = true; - public draw(data: any[], attrToProjector: AttributeToProjector) { - var svgElement = "path"; - var dataElements = this._renderArea.selectAll(svgElement).data([data]); + public _enterData(data: any[]) { + if (this._drawLine) { + super._enterData(data); + } else { + AbstractDrawer.prototype._enterData.call(this, data); + } + this.areaSelection.datum(data); + } + + /** + * Sets the value determining if line should be drawn. + * + * @param{boolean} draw The value determing if line should be drawn. + */ + public drawLine(draw: boolean): Area { + this._drawLine = draw; + return this; + } + + public setup(area: D3.Selection) { + area.append("path").classed("area", true); + if (this._drawLine) { + super.setup(area); + } else { + AbstractDrawer.prototype.setup.call(this, area); + } + this.areaSelection = this._renderArea.select(".area"); + } + + private createArea(xFunction: AppliedAccessor, + y0Function: AppliedAccessor, + y1Function: AppliedAccessor, + definedFunction: (d: any, i: number) => boolean) { + if(!definedFunction) { + definedFunction = () => true; + } + + return d3.svg.area() + .x(xFunction) + .y0(y0Function) + .y1(y1Function) + .defined(definedFunction); + } + + public _drawStep(step: DrawStep) { + if (this._drawLine) { + super._drawStep(step); + } else { + AbstractDrawer.prototype._drawStep.call(this, step); + } + + var attrToProjector = _Util.Methods.copyMap(step.attrToProjector); + var xFunction = attrToProjector["x"]; + var y0Function = attrToProjector["y0"]; + var y1Function = attrToProjector["y"]; + var definedFunction = attrToProjector["defined"]; + delete attrToProjector["x"]; + delete attrToProjector["y0"]; + delete attrToProjector["y"]; + + attrToProjector["d"] = this.createArea(xFunction, y0Function, y1Function, attrToProjector["defined"]); + if (attrToProjector["defined"]) { + delete attrToProjector["defined"]; + } - dataElements.enter().append(svgElement); - dataElements.attr(attrToProjector).classed("area", true); - dataElements.exit().remove(); + if (attrToProjector["fill"]) { + this.areaSelection.attr("fill", attrToProjector["fill"]); // so colors don't animate + } + step.animator.animate(this.areaSelection, attrToProjector); } } } diff --git a/src/drawers/elementDrawer.ts b/src/drawers/elementDrawer.ts new file mode 100644 index 0000000000..e3e73e97ba --- /dev/null +++ b/src/drawers/elementDrawer.ts @@ -0,0 +1,42 @@ +/// + +module Plottable { +export module _Drawer { + export class Element extends AbstractDrawer { + public _svgElement: string; + + /** + * Sets the svg element, which needs to be bind to data + * + * @param{string} tag The svg element to be bind + */ + public svgElement(tag: string): Element { + this._svgElement = tag; + return this; + } + + public _getDrawSelection() { + return this._renderArea.selectAll(this._svgElement); + } + + public _drawStep(step: DrawStep) { + super._drawStep(step); + var drawSelection = this._getDrawSelection(); + if (step.attrToProjector["fill"]) { + drawSelection.attr("fill", step.attrToProjector["fill"]); // so colors don't animate + } + step.animator.animate(drawSelection, step.attrToProjector); + } + + public _enterData(data: any[]) { + super._enterData(data); + var dataElements = this._getDrawSelection().data(data); + dataElements.enter().append(this._svgElement); + if (this._className != null) { + dataElements.classed(this._className, true); + } + dataElements.exit().remove(); + } + } +} +} diff --git a/src/drawers/lineDrawer.ts b/src/drawers/lineDrawer.ts new file mode 100644 index 0000000000..6be928cce1 --- /dev/null +++ b/src/drawers/lineDrawer.ts @@ -0,0 +1,55 @@ +/// + +module Plottable { +export module _Drawer { + export class Line extends AbstractDrawer { + private pathSelection: D3.Selection; + + public _enterData(data: any[]) { + super._enterData(data); + this.pathSelection.datum(data); + } + + public setup(area: D3.Selection) { + area.append("path").classed("line", true); + super.setup(area); + this.pathSelection = this._renderArea.select(".line"); + } + + private createLine(xFunction: AppliedAccessor, yFunction: AppliedAccessor, definedFunction: (d: any, i: number) => boolean) { + if(!definedFunction) { + definedFunction = () => true; + } + + return d3.svg.line() + .x(xFunction) + .y(yFunction) + .defined(definedFunction); + } + + public _numberOfAnimationIterations(data: any[]): number { + return 1; + } + + public _drawStep(step: DrawStep) { + var baseTime = super._drawStep(step); + var attrToProjector = _Util.Methods.copyMap(step.attrToProjector); + var xFunction = attrToProjector["x"]; + var yFunction = attrToProjector["y"]; + var definedFunction = attrToProjector["defined"]; + delete attrToProjector["x"]; + delete attrToProjector["y"]; + + attrToProjector["d"] = this.createLine(xFunction, yFunction, attrToProjector["defined"]); + if (attrToProjector["defined"]) { + delete attrToProjector["defined"]; + } + + if (attrToProjector["fill"]) { + this.pathSelection.attr("fill", attrToProjector["fill"]); // so colors don't animate + } + step.animator.animate(this.pathSelection, attrToProjector); + } + } +} +} diff --git a/src/drawers/rectDrawer.ts b/src/drawers/rectDrawer.ts index 10148d42a4..1ac3fc23f5 100644 --- a/src/drawers/rectDrawer.ts +++ b/src/drawers/rectDrawer.ts @@ -2,15 +2,74 @@ module Plottable { export module _Drawer { - export class Rect extends AbstractDrawer { + var LABEL_VERTICAL_PADDING = 5; + var LABEL_HORIZONTAL_PADDING = 5; + export class Rect extends Element { + public _someLabelsTooWide = false; + public _isVertical: boolean; + private textArea: D3.Selection; + private measurer: _Util.Text.TextMeasurer; - public draw(data: any[], attrToProjector: AttributeToProjector, animator = new Animator.Null()) { - var svgElement = "rect"; - var dataElements = this._renderArea.selectAll(svgElement).data(data); + constructor(key: string, isVertical: boolean) { + super(key); + this.svgElement("rect"); + this._isVertical = isVertical; + } + + public setup(area: D3.Selection) { + // need to put the bars in a seperate container so we can ensure that they don't cover labels + super.setup(area.append("g").classed("bar-area", true)); + this.textArea = area.append("g").classed("bar-label-text-area", true); + this.measurer = new _Util.Text.CachingCharacterMeasurer(this.textArea.append("text")).measure; + } + + public removeLabels() { + this.textArea.selectAll("g").remove(); + } + + public drawText(data: any[], attrToProjector: AttributeToProjector) { + var labelTooWide: boolean[] = data.map((d, i) => { + var text = attrToProjector["label"](d, i).toString(); + var w = attrToProjector["width"](d, i); + var h = attrToProjector["height"](d, i); + var x = attrToProjector["x"](d, i); + var y = attrToProjector["y"](d, i); + var positive = attrToProjector["positive"](d, i); + var measurement = this.measurer(text); + var color = attrToProjector["fill"](d, i); + var dark = _Util.Color.contrast("white", color) * 1.6 < _Util.Color.contrast("black", color); + var primary = this._isVertical ? h : w; + var primarySpace = this._isVertical ? measurement.height : measurement.width; + + var secondaryAttrTextSpace = this._isVertical ? measurement.width : measurement.height; + var secondaryAttrAvailableSpace = this._isVertical ? w : h; + var tooWide = secondaryAttrTextSpace + 2 * LABEL_HORIZONTAL_PADDING > secondaryAttrAvailableSpace; + if (measurement.height <= h && measurement.width <= w) { + var offset = Math.min((primary - primarySpace) / 2, LABEL_VERTICAL_PADDING); + if (!positive) {offset = offset * -1;} + if (this._isVertical) { + y += offset; + } else { + x += offset; + } - dataElements.enter().append(svgElement); - animator.animate(dataElements, attrToProjector); - dataElements.exit().remove(); + var g = this.textArea.append("g").attr("transform", "translate(" + x + "," + y + ")"); + var className = dark ? "dark-label" : "light-label"; + g.classed(className, true); + var xAlign: string; + var yAlign: string; + if (this._isVertical) { + xAlign = "center"; + yAlign = positive ? "top" : "bottom"; + } else { + xAlign = positive ? "left" : "right"; + yAlign = "center"; + } + _Util.Text.writeLineHorizontally(text, g, w, h, xAlign, yAlign); + } + return tooWide; + }); + this._someLabelsTooWide = labelTooWide.some((d: boolean) => d); } } } diff --git a/src/interactions/barHoverInteraction.ts b/src/interactions/barHoverInteraction.ts index 476ea7547d..58e6f3e197 100644 --- a/src/interactions/barHoverInteraction.ts +++ b/src/interactions/barHoverInteraction.ts @@ -13,6 +13,9 @@ export module Interaction { public _anchor(barPlot: Plot.AbstractBarPlot, hitBox: D3.Selection) { super._anchor(barPlot, hitBox); + + _Util.Methods.warn("Interaction.BarHover is deprecated; please use Interaction.Hover instead"); + this.plotIsVertical = this._componentToListenTo._isVertical; this.dispatcher = new Dispatcher.Mouse(this._hitBox); diff --git a/src/interactions/hoverInteraction.ts b/src/interactions/hoverInteraction.ts new file mode 100644 index 0000000000..1643d71fa6 --- /dev/null +++ b/src/interactions/hoverInteraction.ts @@ -0,0 +1,152 @@ +/// + +module Plottable { +export module Interaction { + export interface HoverData { + data: any[]; + selection: D3.Selection; + } + + export interface Hoverable extends Component.AbstractComponent { + /** + * Called when the user first mouses over the Component. + * + * @param {Point} The cursor's position relative to the Component's origin. + */ + _hoverOverComponent(p: Point): void; + /** + * Called when the user mouses out of the Component. + * + * @param {Point} The cursor's position relative to the Component's origin. + */ + _hoverOutComponent(p: Point): void; + /** + * Returns the HoverData associated with the given position, and performs + * any visual changes associated with hovering inside a Component. + * + * @param {Point} The cursor's position relative to the Component's origin. + * @return {HoverData} The HoverData associated with the given position. + */ + _doHover(p: Point): HoverData; + } + + export class Hover extends Interaction.AbstractInteraction { + public _componentToListenTo: Hoverable; + private dispatcher: Dispatcher.Mouse; + private hoverOverCallback: (hoverData: HoverData) => any; + private hoverOutCallback: (hoverData: HoverData) => any; + + private currentHoverData: HoverData = { + data: null, + selection: null + }; + + public _anchor(component: Hoverable, hitBox: D3.Selection) { + super._anchor(component, hitBox); + this.dispatcher = new Dispatcher.Mouse(this._hitBox); + + this.dispatcher.mouseover((p: Point) => { + this._componentToListenTo._hoverOverComponent(p); + this.handleHoverOver(p); + }); + + this.dispatcher.mouseout((p: Point) => { + this._componentToListenTo._hoverOutComponent(p); + this.safeHoverOut(this.currentHoverData); + this.currentHoverData = { + data: null, + selection: null + }; + }); + + this.dispatcher.mousemove((p: Point) => this.handleHoverOver(p)); + + this.dispatcher.connect(); + } + + /** + * Returns a HoverData consisting of all data and selections in a but not in b. + */ + private static diffHoverData(a: HoverData, b: HoverData): HoverData { + if (a.data == null || b.data == null) { + return a; + } + + var notInB = (d: any) => b.data.indexOf(d) === -1; + + var diffData = a.data.filter(notInB); + if (diffData.length === 0) { + return { + data: null, + selection: null + }; + } + + var diffSelection = a.selection.filter(notInB); + return { + data: diffData, + selection: diffSelection + }; + } + + private handleHoverOver(p: Point) { + var lastHoverData = this.currentHoverData; + var newHoverData = this._componentToListenTo._doHover(p); + + var outData = Hover.diffHoverData(lastHoverData, newHoverData); + this.safeHoverOut(outData); + + var overData = Hover.diffHoverData(newHoverData, lastHoverData); + this.safeHoverOver(overData); + + this.currentHoverData = newHoverData; + } + + private safeHoverOut(outData: HoverData) { + if (this.hoverOutCallback && outData.data) { + this.hoverOutCallback(outData); + } + } + + private safeHoverOver(overData: HoverData) { + if (this.hoverOverCallback && overData.data) { + this.hoverOverCallback(overData); + } + } + + /** + * Attaches an callback to be called when the user mouses over an element. + * + * @param {(hoverData: HoverData) => any} callback The callback to be called. + * The callback will be passed data for newly hovered-over elements. + * @return {Interaction.Hover} The calling Interaction.Hover. + */ + public onHoverOver(callback: (hoverData: HoverData) => any) { + this.hoverOverCallback = callback; + return this; + } + + /** + * Attaches a callback to be called when the user mouses off of an element. + * + * @param {(hoverData: HoverData) => any} callback The callback to be called. + * The callback will be passed data from the hovered-out elements. + * @return {Interaction.Hover} The calling Interaction.Hover. + */ + public onHoverOut(callback: (hoverData: HoverData) => any) { + this.hoverOutCallback = callback; + return this; + } + + /** + * Retrieves the HoverData associated with the elements the user is currently hovering over. + * + * @return {HoverData} The data and selection corresponding to the elements + * the user is currently hovering over. + */ + public getCurrentHoverData(): HoverData { + return this.currentHoverData; + } + } +} +} diff --git a/src/reference.ts b/src/reference.ts index 4f0636b290..befd64bb67 100644 --- a/src/reference.ts +++ b/src/reference.ts @@ -6,6 +6,7 @@ /// /// /// +/// /// @@ -31,11 +32,14 @@ /// /// /// +/// /// -/// +/// /// +/// /// +/// /// /// @@ -70,7 +74,6 @@ /// /// /// -/// /// /// @@ -85,6 +88,7 @@ /// /// /// +/// /// /// diff --git a/src/scales/interpolatedColorScale.ts b/src/scales/interpolatedColorScale.ts index 5296dad123..c335cc9eab 100644 --- a/src/scales/interpolatedColorScale.ts +++ b/src/scales/interpolatedColorScale.ts @@ -211,7 +211,7 @@ export module Scale { // unlike other QuantitativeScales, interpolatedColorScale ignores its domainer var extents = this._getAllExtents(); if (extents.length > 0) { - this._setDomain([_Util.Methods.min(extents, (x) => x[0]), _Util.Methods.max(extents, (x) => x[1])]); + this._setDomain([_Util.Methods.min(extents, (x) => x[0], 0), _Util.Methods.max(extents, (x) => x[1], 0)]); } return this; } diff --git a/src/scales/modifiedLogScale.ts b/src/scales/modifiedLogScale.ts index 3def2dcb0e..0f3e3054ca 100644 --- a/src/scales/modifiedLogScale.ts +++ b/src/scales/modifiedLogScale.ts @@ -104,8 +104,8 @@ export module Scale { // 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 = (x: number, y: number, z: number) => [x, y, z].sort((a, b) => a - b)[1]; - var min = _Util.Methods.min(this.untransformedDomain); - var max = _Util.Methods.max(this.untransformedDomain); + var min = _Util.Methods.min(this.untransformedDomain, 0); + var max = _Util.Methods.max(this.untransformedDomain, 0); var negativeLower = min; var negativeUpper = middle(min, max, -this.pivot); var positiveLower = middle(min, max, this.pivot); @@ -165,8 +165,8 @@ export module Scale { * distance when plotted. */ private howManyTicks(lower: number, upper: number): number { - var adjustedMin = this.adjustedLog(_Util.Methods.min(this.untransformedDomain)); - var adjustedMax = this.adjustedLog(_Util.Methods.max(this.untransformedDomain)); + var adjustedMin = this.adjustedLog(_Util.Methods.min(this.untransformedDomain, 0)); + var adjustedMax = this.adjustedLog(_Util.Methods.max(this.untransformedDomain, 0)); var adjustedLower = this.adjustedLog(lower); var adjustedUpper = this.adjustedLog(upper); var proportion = (adjustedUpper - adjustedLower) / (adjustedMax - adjustedMin); diff --git a/src/scales/quantitativeScale.ts b/src/scales/quantitativeScale.ts index a481a84382..1d5a2c2ba2 100644 --- a/src/scales/quantitativeScale.ts +++ b/src/scales/quantitativeScale.ts @@ -9,6 +9,8 @@ export module Scale { public _userSetDomainer: boolean = false; public _domainer: Domainer = new Domainer(); public _typeCoercer = (d: any) => +d; + public _tickGenerator: TickGenerators.TickGenerator = (scale: Plottable.Scale.AbstractQuantitative) => scale.getDefaultTicks(); + /** * Constructs a new QuantitativeScale. @@ -88,6 +90,13 @@ export module Scale { return this; } + /** + * Gets ticks generated by the default algorithm. + */ + public getDefaultTicks(): D[] { + return this._d3Scale.ticks(this.numTicks()); + } + /** * Gets the clamp status of the QuantitativeScale (whether to cut off values outside the ouput range). * @@ -112,13 +121,10 @@ export module Scale { /** * Gets a set of tick values spanning the domain. * - * @param {number} [count] The approximate number of ticks to generate. - * If not supplied, the number specified by - * numTicks() is used instead. * @returns {any[]} The generated ticks. */ - public ticks(count = this.numTicks()): any[] { - return this._d3Scale.ticks(count); + public ticks(): any[] { + return this._tickGenerator(this); } /** @@ -183,6 +189,28 @@ export module Scale { public _defaultExtent(): any[] { return [0, 1]; } + + /** + * Gets the tick generator of the AbstractQuantitative. + * + * @returns {TickGenerator} The current tick generator. + */ + public tickGenerator(): TickGenerators.TickGenerator; + /** + * Sets a tick generator + * + * @param {TickGenerator} generator, the new tick generator. + * @return {AbstractQuantitative} The calling AbstractQuantitative. + */ + public tickGenerator(generator: TickGenerators.TickGenerator): AbstractQuantitative; + public tickGenerator(generator?: TickGenerators.TickGenerator): any { + if(generator == null) { + return this._tickGenerator; + } else { + this._tickGenerator = generator; + return this; + } + } } } } diff --git a/src/scales/tickGenerators.ts b/src/scales/tickGenerators.ts new file mode 100644 index 0000000000..a501e2a5a3 --- /dev/null +++ b/src/scales/tickGenerators.ts @@ -0,0 +1,39 @@ +/// + +module Plottable { + export module Scale { + export module TickGenerators { + export interface TickGenerator { + (scale: Plottable.Scale.AbstractQuantitative): D[] + } + /** + * Creates a tick generator using the specified interval. + * + * Generates ticks at multiples of the interval while also including the domain boundaries. + * + * @param {number} interval The interval between two ticks (not including the end ticks). + * + * @returns {TickGenerator} A tick generator using the specified interval. + */ + export function intervalTickGenerator(interval: number) : TickGenerator { + if(interval <= 0) { + throw new Error("interval must be positive number"); + } + + return function(s: Scale.AbstractQuantitative) { + 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 = _Util.Methods.range(0, numTicks).map(t => firstTick + t * interval); + var highTicks = high % interval === 0 ? [] : [high]; + + return lowTicks.concat(middleTicks).concat(highTicks); + }; + } + } +} +} diff --git a/src/utils/color.ts b/src/utils/color.ts new file mode 100644 index 0000000000..e43532be33 --- /dev/null +++ b/src/utils/color.ts @@ -0,0 +1,39 @@ +/// + +module Plottable { +export module _Util { + export module Color { + /** + * 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: string) { + var rgb = d3.rgb(color); + + var lum = (x: number) => { + x = x / 255; + return x <= 0.03928 ? x / 12.92 : Math.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; + } + + /** + * 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 + */ + export function contrast(a: string, b: string) { + var l1 = luminance(a) + 0.05; + var l2 = luminance(b) + 0.05; + return l1 > l2 ? l1 / l2 : l2 / l1; + } + } +} +} diff --git a/src/utils/textUtils.ts b/src/utils/textUtils.ts index ab9d71b859..0e4602f8d2 100644 --- a/src/utils/textUtils.ts +++ b/src/utils/textUtils.ts @@ -55,7 +55,7 @@ export module _Util { var whs = s.trim().split("").map(tm); return { width: d3.sum(whs, (wh) => wh.width), - height: _Util.Methods.max(whs, (wh) => wh.height) + height: _Util.Methods.max(whs, (wh) => wh.height, 0) }; }; } @@ -83,7 +83,7 @@ export module _Util { }); return { width: d3.sum(whs, (x) => x.width), - height: _Util.Methods.max(whs, (x) => x.height) + height: _Util.Methods.max(whs, (x) => x.height, 0) }; } else { return tm(s); @@ -214,7 +214,7 @@ export module _Util { innerG.attr("transform", xForm.toString()); innerG.classed("rotated-" + rotation, true); - return wh; + return {width: wh.height, height: wh.width}; } function writeTextHorizontally(brokenText: string[], g: D3.Selection, @@ -298,8 +298,11 @@ export module _Util { if (write == null) { var widthFn = orientHorizontally ? _Util.Methods.max : d3.sum; var heightFn = orientHorizontally ? d3.sum : _Util.Methods.max; - usedWidth = widthFn(wrappedText.lines, (line: string) => tm(line).width); - usedHeight = heightFn(wrappedText.lines, (line: string) => tm(line).height); + var heightAcc = (line: string) => orientHorizontally ? tm(line).height : tm(line).width; + var widthAcc = (line: string) => orientHorizontally ? tm(line).width : tm(line).height; + + usedWidth = widthFn(wrappedText.lines, widthAcc, 0); + usedHeight = heightFn(wrappedText.lines, heightAcc, 0); } else { var innerG = write.g.append("g").classed("writeText-inner-g", true); // unleash your inner G // the outerG contains general transforms for positining the whole block, the inner g diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 979c5b1320..7bd0e4cfb8 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -207,11 +207,16 @@ export module _Util { return arrayEq(keysA, keysB) && arrayEq(valuesA, valuesB); } - export function max(arr: number[], default_val?: number): number; - export function max(arr: T[], acc: (x: T) => number, default_val?: number): number; - export function max(arr: any[], one: any = 0, two: any = 0) { + /** + * Computes the max value from the array. + * + * If type is not comparable then t will be converted to a comparable before computing max. + */ + export function max(arr: C[], default_val: C): C; + export function max(arr: T[], acc: (x?: T, i?: number) => C, default_val: C): C; + export function max(arr: any[], one: any, two?: any): any { if (arr.length === 0) { - if (typeof(one) === "number") { + if (typeof(one) !== "function") { return one; } else { return two; @@ -223,11 +228,16 @@ export module _Util { /* tslint:enable:ban */ } - export function min(arr: number[], default_val?: number): number; - export function min(arr: T[], acc: (x: T) => number, default_val?: number): number; - export function min(arr: any[], one: any = 0, two: any = 0) { + /** + * Computes the min value from the array. + * + * If type is not comparable then t will be converted to a comparable before computing min. + */ + export function min(arr: C[], default_val: C): C; + export function min(arr: T[], acc: (x?: T, i?: number) => C, default_val: C): C; + export function min(arr: any[], one: any, two?: any): any { if (arr.length === 0) { - if (typeof(one) === "number") { + if (typeof(one) !== "function") { return one; } else { return two; @@ -238,6 +248,48 @@ export module _Util { return acc === undefined ? d3.min(arr) : d3.min(arr, acc); /* tslint:enable:ban */ } + + /** + * Creates shallow copy of map. + * @param {{ [key: string]: any }} oldMap Map to copy + * + * @returns {[{ [key: string]: any }} coppied map. + */ + export function copyMap(oldMap: { [key: string]: T }): { [key: string]: T } { + var newMap: { [key: string]: any } = {}; + d3.keys(oldMap).forEach(key => newMap[key] = oldMap[key]); + return newMap; + } + + export function range(start: number, stop: number, step = 1): number[] { + if(step === 0) { + throw new Error("step cannot be 0"); + } + var length = Math.max(Math.ceil((stop - start) / step), 0); + var range: number[] = []; + + for (var i = 0; i < length; ++i) { + range[i] = start + step * i; + } + + return range; + } + + /** 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. + */ + export function setTimeout(f: Function, time: number, ...args: any[]) { + if (time === 0) { + f(args); + return -1; + } else { + return window.setTimeout(f, time, args); + } + } } } } diff --git a/src/utils/wordWrapUtils.ts b/src/utils/wordWrapUtils.ts index 7c76e22e4e..a01616801d 100644 --- a/src/utils/wordWrapUtils.ts +++ b/src/utils/wordWrapUtils.ts @@ -60,7 +60,7 @@ export module _Util { export function canWrapWithoutBreakingWords(text: string, width: number, widthMeasure: (s: string) => number): boolean { var tokens = tokenize(text); var widths = tokens.map(widthMeasure); - var maxWidth = _Util.Methods.max(widths); + var maxWidth = _Util.Methods.max(widths, 0); return maxWidth <= width; } diff --git a/test/components/plots/barPlotTests.ts b/test/components/plots/barPlotTests.ts index 8703771073..72c54f9282 100644 --- a/test/components/plots/barPlotTests.ts +++ b/test/components/plots/barPlotTests.ts @@ -41,8 +41,8 @@ describe("Plots", () => { assert.equal(bar1.attr("width"), "10", "bar1 width is correct"); assert.equal(bar0.attr("height"), "100", "bar0 height is correct"); assert.equal(bar1.attr("height"), "150", "bar1 height is correct"); - assert.equal(bar0.attr("x"), "150", "bar0 x is correct"); - assert.equal(bar1.attr("x"), "450", "bar1 x is correct"); + assert.equal(bar0.attr("x"), "145", "bar0 x is correct"); + assert.equal(bar1.attr("x"), "445", "bar1 x is correct"); assert.equal(bar0.attr("y"), "100", "bar0 y is correct"); assert.equal(bar1.attr("y"), "200", "bar1 y is correct"); @@ -199,8 +199,8 @@ describe("Plots", () => { assert.equal(bar1.attr("height"), "10", "bar1 height is correct"); assert.equal(bar0.attr("width"), "100", "bar0 width is correct"); assert.equal(bar1.attr("width"), "150", "bar1 width is correct"); - assert.equal(bar0.attr("y"), "300", "bar0 y is correct"); - assert.equal(bar1.attr("y"), "100", "bar1 y is correct"); + assert.equal(bar0.attr("y"), "295", "bar0 y is correct"); + assert.equal(bar1.attr("y"), "95", "bar1 y is correct"); assert.equal(bar0.attr("x"), "300", "bar0 x is correct"); assert.equal(bar1.attr("x"), "150", "bar1 x is correct"); @@ -326,5 +326,85 @@ describe("Plots", () => { svg.remove(); }); }); + + describe("Vertical Bar Plot With Bar Labels", () => { + var plot: Plottable.Plot.VerticalBar; + var data: any[]; + var dataset: Plottable.Dataset; + var xScale: Plottable.Scale.Ordinal; + var yScale: Plottable.Scale.Linear; + var svg: D3.Selection; + + beforeEach(() => { + svg = generateSVG(); + data = [{x: "foo", y: 5}, {x: "bar", y: 640}, {x: "zoo", y: 12345}]; + dataset = new Plottable.Dataset(data); + xScale = new Plottable.Scale.Ordinal(); + yScale = new Plottable.Scale.Linear(); + plot = new Plottable.Plot.VerticalBar(xScale, yScale); + plot.addDataset(dataset); + }); + + it("bar labels disabled by default", () => { + plot.renderTo(svg); + var texts = svg.selectAll("text")[0].map((n: any) => d3.select(n).text()); + assert.lengthOf(texts, 0, "by default, no texts are drawn"); + svg.remove(); + }); + + + it("bar labels render properly", () => { + plot.renderTo(svg); + plot.barLabelsEnabled(true); + var texts = svg.selectAll("text")[0].map((n: any) => d3.select(n).text()); + assert.lengthOf(texts, 2, "both texts drawn"); + assert.equal(texts[0], "640", "first label is 640"); + assert.equal(texts[1], "12345", "first label is 12345"); + svg.remove(); + }); + + it("bar labels hide if bars too skinny", () => { + plot.barLabelsEnabled(true); + plot.renderTo(svg); + plot.barLabelFormatter((n: number) => n.toString() + (n === 12345 ? "looong" : "")); + var texts = svg.selectAll("text")[0].map((n: any) => d3.select(n).text()); + assert.lengthOf(texts, 0, "no text drawn"); + svg.remove(); + }); + + it("formatters are used properly", () => { + plot.barLabelsEnabled(true); + plot.barLabelFormatter((n: number) => n.toString() + "%"); + plot.renderTo(svg); + var texts = svg.selectAll("text")[0].map((n: any) => d3.select(n).text()); + assert.lengthOf(texts, 2, "both texts drawn"); + assert.equal(texts[0], "640%", "first label is 640%"); + assert.equal(texts[1], "12345%", "first label is 12345%"); + svg.remove(); + }); + + it("bar labels are removed instantly on dataset change, even if animation is enabled", (done) => { + plot.barLabelsEnabled(true); + plot.animate(true); + plot.renderTo(svg); + var texts = svg.selectAll("text")[0].map((n: any) => d3.select(n).text()); + assert.lengthOf(texts, 2, "both texts drawn"); + var originalDrawLabels = plot._drawLabels; + var called = false; + plot._drawLabels = () => { + if (!called) { + originalDrawLabels.apply(plot); + texts = svg.selectAll("text")[0].map((n: any) => d3.select(n).text()); + assert.lengthOf(texts, 2, "texts were repopulated by drawLabels after the update"); + svg.remove(); + called = true; // for some reason, in phantomJS, `done` was being called multiple times and this caused the test to fail. + done(); + } + }; + dataset.data(data); + texts = svg.selectAll("text")[0].map((n: any) => d3.select(n).text()); + assert.lengthOf(texts, 0, "texts were immediately removed"); + }); + }); }); }); diff --git a/test/components/plots/newStylePlotTests.ts b/test/components/plots/newStylePlotTests.ts index 3ca67b5655..d72cf1e467 100644 --- a/test/components/plots/newStylePlotTests.ts +++ b/test/components/plots/newStylePlotTests.ts @@ -8,7 +8,7 @@ describe("Plots", () => { beforeEach(() => { p = new Plottable.Plot.AbstractPlot(); - p._getDrawer = (k: string) => new Plottable._Drawer.Rect(k); + p._getDrawer = (k: string) => new Plottable._Drawer.Element(k).svgElement("rect"); }); afterEach(() => { diff --git a/test/components/plots/plotTests.ts b/test/components/plots/plotTests.ts index 85c0cb7acd..4d0a3a751d 100644 --- a/test/components/plots/plotTests.ts +++ b/test/components/plots/plotTests.ts @@ -257,5 +257,22 @@ describe("Plots", () => { svg.remove(); }); + + it("additionalPaint timing works properly", () => { + var animator = new Plottable.Animator.Base().delay(10).duration(10).maxIterativeDelay(0); + var x = new Plottable.Scale.Linear(); + var y = new Plottable.Scale.Linear(); + var plot = new Plottable.Plot.VerticalBar(x, y).addDataset([]).animate(true); + var recordedTime: number = -1; + var additionalPaint = (x: number) => { + recordedTime = Math.max(x, recordedTime); + }; + plot._additionalPaint = additionalPaint; + plot.animator("bars", animator); + var svg = generateSVG(); + plot.renderTo(svg); + svg.remove(); + assert.equal(recordedTime, 20, "additionalPaint passed appropriate time argument"); + }); }); }); diff --git a/test/components/plots/scatterPlotTests.ts b/test/components/plots/scatterPlotTests.ts index 93d3e0944a..e35bf4440f 100644 --- a/test/components/plots/scatterPlotTests.ts +++ b/test/components/plots/scatterPlotTests.ts @@ -45,6 +45,44 @@ describe("Plots", () => { svg.remove(); }); + it("_getClosestStruckPoint()", () => { + var svg = generateSVG(400, 400); + var xScale = new Plottable.Scale.Linear(); + var yScale = new Plottable.Scale.Linear(); + xScale.domain([0, 400]); + yScale.domain([400, 0]); + + var data1 = [ + { x: 80, y: 200, r: 20 }, + { x: 100, y: 200, r: 20 }, + { x: 125, y: 200, r: 5 }, + { x: 138, y: 200, r: 5 } + ]; + + var plot = new Plottable.Plot.Scatter(xScale, yScale); + plot.addDataset(data1); + plot.project("x", "x").project("y", "y").project("r", "r"); + plot.renderTo(svg); + + var twoOverlappingCirclesResult = plot._getClosestStruckPoint({ x: 85, y: 200 }, 10); + assert.strictEqual(twoOverlappingCirclesResult.data[0], data1[0], + "returns closest circle among circles that the test point touches"); + + var overlapAndCloseToPointResult = plot._getClosestStruckPoint({ x: 118, y: 200 }, 10); + assert.strictEqual(overlapAndCloseToPointResult.data[0], data1[1], + "returns closest circle that test point touches, even if non-touched circles are closer"); + + var twoPointsInRangeResult = plot._getClosestStruckPoint({ x: 130, y: 200 }, 10); + assert.strictEqual(twoPointsInRangeResult.data[0], data1[2], + "returns closest circle within range if test point does not touch any circles"); + + var farFromAnyPointsResult = plot._getClosestStruckPoint({ x: 400, y: 400 }, 10); + assert.isNull(farFromAnyPointsResult.data, + "returns no data if no circle were within range and test point does not touch any circles"); + + svg.remove(); + }); + describe("Example ScatterPlot with quadratic series", () => { var svg: D3.Selection; var xScale: Plottable.Scale.Linear; diff --git a/test/components/plots/stackedAreaPlotTests.ts b/test/components/plots/stackedAreaPlotTests.ts index 1bec6663c4..39c6c7f3b2 100644 --- a/test/components/plots/stackedAreaPlotTests.ts +++ b/test/components/plots/stackedAreaPlotTests.ts @@ -277,5 +277,76 @@ describe("Plots", () => { assert.strictEqual(oldUpperBound, yScale.domain()[1], "upper bound does not change"); svg.remove(); }); + + it("warning is thrown when datasets are updated with different domains", () => { + var flag = false; + var oldWarn = Plottable._Util.Methods.warn; + ( Plottable._Util.Methods).warn = (msg: string) => { + if (msg.indexOf("domain") > -1) { flag = true; } + }; + + var missingDomainData = [ + { x: 1, y: 0, type: "c" } + ]; + var dataset = new Plottable.Dataset(missingDomainData); + renderer.addDataset(dataset); + + ( Plottable._Util.Methods).warn = oldWarn; + assert.isTrue(flag, "warning has been issued about differing domains"); + + svg.remove(); + }); + }); + + describe("Stacked Area Plot Project", () => { + var svg: D3.Selection; + var xScale: Plottable.Scale.Linear; + var yScale: Plottable.Scale.Linear; + var renderer: Plottable.Plot.StackedArea; + var SVG_WIDTH = 600; + var SVG_HEIGHT = 400; + + beforeEach(() => { + svg = generateSVG(SVG_WIDTH, SVG_HEIGHT); + xScale = new Plottable.Scale.Linear().domain([1, 3]); + yScale = new Plottable.Scale.Linear().domain([0, 4]); + var colorScale = new Plottable.Scale.Color("10").domain(["a", "b"]); + + var data1 = [ + {x: 1, yTest: 1, type: "a"}, + {x: 3, yTest: 2, type: "a"} + ]; + var data2 = [ + {x: 1, yTest: 3, type: "b"}, + {x: 3, yTest: 1, type: "b"} + ]; + + renderer = new Plottable.Plot.StackedArea(xScale, yScale); + renderer.project("y", "yTest", yScale); + renderer.addDataset(data1); + renderer.addDataset(data2); + renderer.project("fill", "type", colorScale); + var xAxis = new Plottable.Axis.Numeric(xScale, "bottom"); + var table = new Plottable.Component.Table([[renderer], [xAxis]]).renderTo(svg); + }); + + it("renders correctly", () => { + var areas = renderer._renderArea.selectAll(".area"); + var area0 = d3.select(areas[0][0]); + var d0 = normalizePath(area0.attr("d")).split(/[a-zA-Z]/); + var d0Ys = d0.slice(1, d0.length - 1).map((s) => parseFloat(s.split(",")[1])); + assert.strictEqual(d0Ys.indexOf(0), -1, "bottom area never touches the top"); + + var area1 = d3.select(areas[0][1]); + var d1 = normalizePath(area1.attr("d")).split(/[a-zA-Z]/); + var d1Ys = d1.slice(1, d1.length - 1).map((s) => parseFloat(s.split(",")[1])); + assert.notEqual(d1Ys.indexOf(0), -1, "touches the top"); + + var domain = yScale.domain(); + assert.strictEqual(0, domain[0], "domain starts at a min value at 0"); + assert.strictEqual(4, domain[1], "highest area stacking is at upper limit of yScale domain"); + svg.remove(); + }); + }); }); diff --git a/test/components/plots/stackedPlotTests.ts b/test/components/plots/stackedPlotTests.ts index 5c9009046b..5a1526d229 100644 --- a/test/components/plots/stackedPlotTests.ts +++ b/test/components/plots/stackedPlotTests.ts @@ -72,5 +72,82 @@ describe("Plots", () => { assert.strictEqual(( data2[0])["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"], -2, "positive offset was used"); assert.strictEqual(( data4[0])["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"], -3, "positive offset was used"); }); + + it("project can be called after addDataset", () => { + var data1 = [ + { a: 1, b: 2 } + ]; + var data2 = [ + { a: 1, b: 4 } + ]; + + stackedPlot.addDataset(data1); + stackedPlot.addDataset(data2); + + assert.isTrue(isNaN(( data2[0])["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"]), "stacking is initially incorrect"); + + stackedPlot.project("x", "a"); + stackedPlot.project("y", "b"); + + assert.strictEqual(( data2[0])["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"], 2, "stacking was done correctly"); + }); + + it("strings are coerced to numbers for stacking", () => { + var data1 = [ + { x: 1, y: "-2" } + ]; + var data2 = [ + { x: 1, y: "3" } + ]; + var data3 = [ + { x: 1, y: "-1" } + ]; + var data4 = [ + { x: 1, y: "5" } + ]; + var data5 = [ + { x: 1, y: "1" } + ]; + var data6 = [ + { x: 1, y: "-1" } + ]; + + stackedPlot.addDataset(data1); + stackedPlot.addDataset(data2); + + stackedPlot.addDataset(data3); + stackedPlot.addDataset(data4); + stackedPlot.addDataset(data5); + stackedPlot.addDataset(data6); + + assert.strictEqual(( data3[0])["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"], -2, "stacking on data1 numerical y value"); + assert.strictEqual(( data4[0])["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"], 3, "stacking on data2 numerical y value"); + assert.strictEqual(( data5[0])["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"], 8, "stacking on data1 + data3 numerical y values"); + assert.strictEqual(( data6[0])["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"], -3, "stacking on data2 + data4 numerical y values"); + + assert.deepEqual(( stackedPlot).stackedExtent, [-4, 9], "stacked extent is as normal"); + }); + + it("stacks correctly on empty data", () => { + var data1: any[] = [ + ]; + var data2: any[] = [ + ]; + + stackedPlot.addDataset(data1); + stackedPlot.addDataset(data2); + + assert.deepEqual(data1, [], "empty data causes no stacking to happen"); + assert.deepEqual(data2, [], "empty data causes no stacking to happen"); + }); + + it("does not crash on stacking no datasets", () => { + var data1 = [ + {x: 1, y: -2} + ]; + + stackedPlot.addDataset("a", data1); + assert.doesNotThrow(() => stackedPlot.removeDataset("a"), Error); + }); }); }); diff --git a/test/drawers/drawerTests.ts b/test/drawers/drawerTests.ts new file mode 100644 index 0000000000..f6bfe569f1 --- /dev/null +++ b/test/drawers/drawerTests.ts @@ -0,0 +1,93 @@ +/// + +class MockAnimator implements Plottable.Animator.PlotAnimator { + private time: number; + private callback: Function; + constructor(time: number, callback?: Function) { + this.time = time; + this.callback = callback; + } + public getTiming(selection: any) { + return this.time; + } + + + public animate(selection: any, attrToProjector: Plottable.AttributeToProjector): any { + if (this.callback) { + this.callback(); + } + return selection; + } +} + +class MockDrawer extends Plottable._Drawer.AbstractDrawer { + public _drawStep(step: Plottable._Drawer.DrawStep) { + step.animator.animate(this._renderArea, step.attrToProjector); + } +} + +describe("Drawers", () => { + describe("Abstract Drawer", () => { + var oldTimeout: any; + var timings: number[] = []; + var svg: D3.Selection; + var drawer: MockDrawer; + before(() => { + oldTimeout = Plottable._Util.Methods.setTimeout; + Plottable._Util.Methods.setTimeout = function(f: Function, time: number, ...args: any[]) { + timings.push(time); + return oldTimeout(f, time, args); + }; + }); + + after(() => { + Plottable._Util.Methods.setTimeout = oldTimeout; + }); + + beforeEach(() => { + timings = []; + svg = generateSVG(); + drawer = new MockDrawer("foo"); + drawer.setup(svg); + }); + + afterEach(() => { + svg.remove(); // no point keeping it around since we don't draw anything in it anyway + }); + + it("drawer timing works as expected for null animators", () => { + var a1 = new Plottable.Animator.Null(); + var a2 = new Plottable.Animator.Null(); + var ds1: Plottable._Drawer.DrawStep = {attrToProjector: {}, animator: a1}; + var ds2: Plottable._Drawer.DrawStep = {attrToProjector: {}, animator: a2}; + var steps = [ds1, ds2]; + drawer.draw([], steps); + assert.deepEqual(timings, [0, 0], "setTimeout called twice with 0 time both times"); + }); + + it("drawer timing works for non-null animators", (done) => { + var callback1Called = false; + var callback2Called = false; + var callback1 = () => { + callback1Called = true; + }; + var callback2 = () => { + assert.isTrue(callback1Called, "callback2 called after callback 1"); + callback2Called = true; + }; + var callback3 = () => { + assert.isTrue(callback2Called, "callback3 called after callback 2"); + done(); + }; + var a1 = new MockAnimator(20, callback1); + var a2 = new MockAnimator(10, callback2); + var a3 = new MockAnimator(0, callback3); + var ds1: Plottable._Drawer.DrawStep = {attrToProjector: {}, animator: a1}; + var ds2: Plottable._Drawer.DrawStep = {attrToProjector: {}, animator: a2}; + var ds3: Plottable._Drawer.DrawStep = {attrToProjector: {}, animator: a3}; + var steps = [ds1, ds2, ds3]; + drawer.draw([], steps); + assert.deepEqual(timings, [0, 20, 30], "setTimeout called with appropriate times"); + }); + }); +}); diff --git a/test/interactions/hoverInteractionTests.ts b/test/interactions/hoverInteractionTests.ts new file mode 100644 index 0000000000..810d285105 --- /dev/null +++ b/test/interactions/hoverInteractionTests.ts @@ -0,0 +1,136 @@ +/// + +var assert = chai.assert; + +class TestHoverable extends Plottable.Component.AbstractComponent + implements Plottable.Interaction.Hoverable { + public _hoverOverComponent(p: Plottable.Point) { + // cast-override + } + + public _hoverOutComponent(p: Plottable.Point) { + // cast-override + } + + public _doHover(p: Plottable.Point): Plottable.Interaction.HoverData { + var data: string[] = []; + if (p.x < 250) { + data.push("left"); + } + if (p.x > 150) { + data.push("right"); + } + return { + data: data, + selection: this._element + }; + } +} + +describe("Interactions", () => { + describe("Hover", () => { + var svg: D3.Selection; + var testTarget: TestHoverable; + var hitbox: D3.Selection; + var hoverInteraction: Plottable.Interaction.Hover; + var overData: Plottable.Interaction.HoverData; + var overCallbackCalled = false; + var outData: Plottable.Interaction.HoverData; + var outCallbackCalled = false; + + + beforeEach(() => { + svg = generateSVG(); + testTarget = new TestHoverable(); + testTarget.classed("test-hoverable", true); + testTarget.renderTo(svg); + + hoverInteraction = new Plottable.Interaction.Hover(); + overCallbackCalled = false; + hoverInteraction.onHoverOver((hd: Plottable.Interaction.HoverData) => { + overCallbackCalled = true; + overData = hd; + }); + + outCallbackCalled = false; + hoverInteraction.onHoverOut((hd: Plottable.Interaction.HoverData) => { + outCallbackCalled = true; + outData = hd; + }); + + testTarget.registerInteraction(hoverInteraction); + hitbox = testTarget._element.select(".hit-box"); + }); + + it("correctly triggers onHoverOver() callbacks", () => { + overCallbackCalled = false; + triggerFakeMouseEvent("mouseover", hitbox, 100, 200); + assert.isTrue(overCallbackCalled, "onHoverOver was called on mousing over a target area"); + assert.deepEqual(overData.data, ["left"], + "onHoverOver was called with the correct data (mouse onto left)"); + + overCallbackCalled = false; + triggerFakeMouseEvent("mousemove", hitbox, 100, 200); + assert.isFalse(overCallbackCalled, + "onHoverOver isn't called if the hover data didn't change"); + + overCallbackCalled = false; + triggerFakeMouseEvent("mousemove", hitbox, 200, 200); + assert.isTrue(overCallbackCalled, "onHoverOver was called when mousing into a new region"); + assert.deepEqual(overData.data, ["right"], + "onHoverOver was called with the new data only (left --> center)"); + + triggerFakeMouseEvent("mouseout", hitbox, 400, 200); + overCallbackCalled = false; + triggerFakeMouseEvent("mouseover", hitbox, 200, 200); + assert.deepEqual(overData.data, ["left", "right"], "onHoverOver is called with the correct data"); + + svg.remove(); + }); + + it("correctly triggers onHoverOut() callbacks", () => { + triggerFakeMouseEvent("mouseover", hitbox, 100, 200); + + outCallbackCalled = false; + triggerFakeMouseEvent("mousemove", hitbox, 200, 200); + assert.isFalse(outCallbackCalled, + "onHoverOut isn't called when mousing into a new region without leaving the old one"); + + outCallbackCalled = false; + triggerFakeMouseEvent("mousemove", hitbox, 300, 200); + assert.isTrue(outCallbackCalled, "onHoverOut was called when the hover data changes"); + assert.deepEqual(outData.data, ["left"], + "onHoverOut was called with the correct data (center --> right)"); + + outCallbackCalled = false; + triggerFakeMouseEvent("mouseout", hitbox, 400, 200); + assert.isTrue(outCallbackCalled, "onHoverOut is called on mousing out of the Component"); + assert.deepEqual(outData.data, ["right"], "onHoverOut was called with the correct data"); + + outCallbackCalled = false; + triggerFakeMouseEvent("mouseover", hitbox, 200, 200); + triggerFakeMouseEvent("mouseout", hitbox, 200, 400); + assert.deepEqual(outData.data, ["left", "right"], "onHoverOut is called with the correct data"); + + svg.remove(); + }); + + it("getCurrentHoverData()", () => { + triggerFakeMouseEvent("mouseover", hitbox, 100, 200); + var currentlyHovered = hoverInteraction.getCurrentHoverData(); + assert.deepEqual(currentlyHovered.data, ["left"], + "retrieves data corresponding to the current position"); + + triggerFakeMouseEvent("mousemove", hitbox, 200, 200); + currentlyHovered = hoverInteraction.getCurrentHoverData(); + assert.deepEqual(currentlyHovered.data, ["left", "right"], + "retrieves data corresponding to the current position"); + + triggerFakeMouseEvent("mouseout", hitbox, 400, 200); + currentlyHovered = hoverInteraction.getCurrentHoverData(); + assert.isNull(currentlyHovered.data, "returns null if not currently hovering"); + + svg.remove(); + }); + }); +}); diff --git a/test/scales/scaleTests.ts b/test/scales/scaleTests.ts index 4133e22f7f..34d408ee77 100644 --- a/test/scales/scaleTests.ts +++ b/test/scales/scaleTests.ts @@ -147,12 +147,9 @@ describe("Scales", () => { var ticks10 = scale.ticks(); assert.closeTo(ticks10.length, 10, 1, "defaults to (about) 10 ticks"); - var ticks20 = scale.ticks(20); + scale.numTicks(20); + var ticks20 = scale.ticks(); assert.closeTo(ticks20.length, 20, 1, "can request a different number of ticks"); - - scale.numTicks(5); - var ticks5 = scale.ticks(); - assert.closeTo(ticks5.length, 5, 1, "can change the default number of ticks"); }); it("autorange defaults to [1, 10] on log scale", () => { @@ -190,6 +187,16 @@ describe("Scales", () => { svg.remove(); }); + it("custom tick generator", () => { + var scale = new Plottable.Scale.Linear(); + scale.domain([0, 10]); + var ticks = scale.ticks(); + assert.closeTo(ticks.length, 10, 1, "ticks were generated correctly with default generator"); + scale.tickGenerator((scale) => scale.getDefaultTicks().filter(tick => tick % 3 === 0)); + ticks = scale.ticks(); + assert.deepEqual(ticks, [0, 3, 6, 9], "ticks were generated correctly with custom generator"); + }); + }); describe("Ordinal Scales", () => { diff --git a/test/scales/tickGeneratorsTests.ts b/test/scales/tickGeneratorsTests.ts new file mode 100644 index 0000000000..9aecc2ff9f --- /dev/null +++ b/test/scales/tickGeneratorsTests.ts @@ -0,0 +1,41 @@ +/// + +var assert = chai.assert; + +describe("Tick generators", () => { + describe("interval", () => { + it("generate ticks within domain", () => { + var start = 0.5, end = 4.01, interval = 1; + var scale = new Plottable.Scale.Linear().domain([start, end]); + var ticks = Plottable.Scale.TickGenerators.intervalTickGenerator(interval)(scale); + assert.deepEqual(ticks, [0.5, 1, 2, 3, 4, 4.01], "generated ticks contains all possible ticks within range"); + }); + + it("domain crossing 0", () => { + var start = -1.5, end = 1, interval = 0.5; + var scale = new Plottable.Scale.Linear().domain([start, end]); + var ticks = Plottable.Scale.TickGenerators.intervalTickGenerator(interval)(scale); + assert.deepEqual(ticks, [-1.5, -1, -0.5, 0, 0.5, 1], "generated all number divisible by 0.5 in domain"); + }); + + it("generate ticks with reversed domain", () => { + var start = -2.2, end = -7.6, interval = 2.5; + var scale = new Plottable.Scale.Linear().domain([start, end]); + var ticks = Plottable.Scale.TickGenerators.intervalTickGenerator(interval)(scale); + assert.deepEqual(ticks, [-7.6, -7.5, -5, -2.5, -2.2], "generated all ticks between lower and higher value"); + }); + + it("passing big interval", () => { + var start = 0.5, end = 10.01, interval = 11; + var scale = new Plottable.Scale.Linear().domain([start, end]); + var ticks = Plottable.Scale.TickGenerators.intervalTickGenerator(interval)(scale); + assert.deepEqual(ticks, [0.5, 10.01], "no middle ticks were added"); + }); + + it("passing non positive interval", () => { + var scale = new Plottable.Scale.Linear().domain([0, 1]); + assert.throws(() => Plottable.Scale.TickGenerators.intervalTickGenerator(0), "interval must be positive number"); + assert.throws(() => Plottable.Scale.TickGenerators.intervalTickGenerator(-2), "interval must be positive number"); + }); + }); +}); diff --git a/test/testReference.ts b/test/testReference.ts index de41af9475..3925ec8d63 100644 --- a/test/testReference.ts +++ b/test/testReference.ts @@ -8,6 +8,7 @@ /// +/// /// /// /// @@ -39,6 +40,7 @@ /// /// /// +/// /// /// @@ -50,5 +52,6 @@ /// /// +/// /// diff --git a/test/tests.js b/test/tests.js index 12765408d0..25372dfef9 100644 --- a/test/tests.js +++ b/test/tests.js @@ -143,6 +143,104 @@ after(function () { } }); +/// +var __extends = this.__extends || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + __.prototype = b.prototype; + d.prototype = new __(); +}; +var MockAnimator = (function () { + function MockAnimator(time, callback) { + this.time = time; + this.callback = callback; + } + MockAnimator.prototype.getTiming = function (selection) { + return this.time; + }; + MockAnimator.prototype.animate = function (selection, attrToProjector) { + if (this.callback) { + this.callback(); + } + return selection; + }; + return MockAnimator; +})(); +var MockDrawer = (function (_super) { + __extends(MockDrawer, _super); + function MockDrawer() { + _super.apply(this, arguments); + } + MockDrawer.prototype._drawStep = function (step) { + step.animator.animate(this._renderArea, step.attrToProjector); + }; + return MockDrawer; +})(Plottable._Drawer.AbstractDrawer); +describe("Drawers", function () { + describe("Abstract Drawer", function () { + var oldTimeout; + var timings = []; + var svg; + var drawer; + before(function () { + oldTimeout = Plottable._Util.Methods.setTimeout; + Plottable._Util.Methods.setTimeout = function (f, time) { + var args = []; + for (var _i = 2; _i < arguments.length; _i++) { + args[_i - 2] = arguments[_i]; + } + timings.push(time); + return oldTimeout(f, time, args); + }; + }); + after(function () { + Plottable._Util.Methods.setTimeout = oldTimeout; + }); + beforeEach(function () { + timings = []; + svg = generateSVG(); + drawer = new MockDrawer("foo"); + drawer.setup(svg); + }); + afterEach(function () { + svg.remove(); // no point keeping it around since we don't draw anything in it anyway + }); + it("drawer timing works as expected for null animators", function () { + var a1 = new Plottable.Animator.Null(); + var a2 = new Plottable.Animator.Null(); + var ds1 = { attrToProjector: {}, animator: a1 }; + var ds2 = { attrToProjector: {}, animator: a2 }; + var steps = [ds1, ds2]; + drawer.draw([], steps); + assert.deepEqual(timings, [0, 0], "setTimeout called twice with 0 time both times"); + }); + it("drawer timing works for non-null animators", function (done) { + var callback1Called = false; + var callback2Called = false; + var callback1 = function () { + callback1Called = true; + }; + var callback2 = function () { + assert.isTrue(callback1Called, "callback2 called after callback 1"); + callback2Called = true; + }; + var callback3 = function () { + assert.isTrue(callback2Called, "callback3 called after callback 2"); + done(); + }; + var a1 = new MockAnimator(20, callback1); + var a2 = new MockAnimator(10, callback2); + var a3 = new MockAnimator(0, callback3); + var ds1 = { attrToProjector: {}, animator: a1 }; + var ds2 = { attrToProjector: {}, animator: a2 }; + var ds3 = { attrToProjector: {}, animator: a3 }; + var steps = [ds1, ds2, ds3]; + drawer.draw([], steps); + assert.deepEqual(timings, [0, 20, 30], "setTimeout called with appropriate times"); + }); + }); +}); + /// var assert = chai.assert; describe("BaseAxis", function () { @@ -1659,6 +1757,22 @@ describe("Plots", function () { assertDomainIsClose(scale1.domain(), [1, 3], "extent shrinks further if we project plot2 away"); svg.remove(); }); + it("additionalPaint timing works properly", function () { + var animator = new Plottable.Animator.Base().delay(10).duration(10).maxIterativeDelay(0); + var x = new Plottable.Scale.Linear(); + var y = new Plottable.Scale.Linear(); + var plot = new Plottable.Plot.VerticalBar(x, y).addDataset([]).animate(true); + var recordedTime = -1; + var additionalPaint = function (x) { + recordedTime = Math.max(x, recordedTime); + }; + plot._additionalPaint = additionalPaint; + plot.animator("bars", animator); + var svg = generateSVG(); + plot.renderTo(svg); + svg.remove(); + assert.equal(recordedTime, 20, "additionalPaint passed appropriate time argument"); + }); }); }); @@ -1796,7 +1910,7 @@ describe("Plots", function () { var oldWarn = Plottable._Util.Methods.warn; beforeEach(function () { p = new Plottable.Plot.AbstractPlot(); - p._getDrawer = function (k) { return new Plottable._Drawer.Rect(k); }; + p._getDrawer = function (k) { return new Plottable._Drawer.Element(k).svgElement("rect"); }; }); afterEach(function () { Plottable._Util.Methods.warn = oldWarn; @@ -2090,8 +2204,8 @@ describe("Plots", function () { assert.equal(bar1.attr("width"), "10", "bar1 width is correct"); assert.equal(bar0.attr("height"), "100", "bar0 height is correct"); assert.equal(bar1.attr("height"), "150", "bar1 height is correct"); - assert.equal(bar0.attr("x"), "150", "bar0 x is correct"); - assert.equal(bar1.attr("x"), "450", "bar1 x is correct"); + assert.equal(bar0.attr("x"), "145", "bar0 x is correct"); + assert.equal(bar1.attr("x"), "445", "bar1 x is correct"); assert.equal(bar0.attr("y"), "100", "bar0 y is correct"); assert.equal(bar1.attr("y"), "200", "bar1 y is correct"); var baseline = renderArea.select(".baseline"); @@ -2221,8 +2335,8 @@ describe("Plots", function () { assert.equal(bar1.attr("height"), "10", "bar1 height is correct"); assert.equal(bar0.attr("width"), "100", "bar0 width is correct"); assert.equal(bar1.attr("width"), "150", "bar1 width is correct"); - assert.equal(bar0.attr("y"), "300", "bar0 y is correct"); - assert.equal(bar1.attr("y"), "100", "bar1 y is correct"); + assert.equal(bar0.attr("y"), "295", "bar0 y is correct"); + assert.equal(bar1.attr("y"), "95", "bar1 y is correct"); assert.equal(bar0.attr("x"), "300", "bar0 x is correct"); assert.equal(bar1.attr("x"), "150", "bar1 x is correct"); var baseline = renderArea.select(".baseline"); @@ -2332,6 +2446,78 @@ describe("Plots", function () { svg.remove(); }); }); + describe("Vertical Bar Plot With Bar Labels", function () { + var plot; + var data; + var dataset; + var xScale; + var yScale; + var svg; + beforeEach(function () { + svg = generateSVG(); + data = [{ x: "foo", y: 5 }, { x: "bar", y: 640 }, { x: "zoo", y: 12345 }]; + dataset = new Plottable.Dataset(data); + xScale = new Plottable.Scale.Ordinal(); + yScale = new Plottable.Scale.Linear(); + plot = new Plottable.Plot.VerticalBar(xScale, yScale); + plot.addDataset(dataset); + }); + it("bar labels disabled by default", function () { + plot.renderTo(svg); + var texts = svg.selectAll("text")[0].map(function (n) { return d3.select(n).text(); }); + assert.lengthOf(texts, 0, "by default, no texts are drawn"); + svg.remove(); + }); + it("bar labels render properly", function () { + plot.renderTo(svg); + plot.barLabelsEnabled(true); + var texts = svg.selectAll("text")[0].map(function (n) { return d3.select(n).text(); }); + assert.lengthOf(texts, 2, "both texts drawn"); + assert.equal(texts[0], "640", "first label is 640"); + assert.equal(texts[1], "12345", "first label is 12345"); + svg.remove(); + }); + it("bar labels hide if bars too skinny", function () { + plot.barLabelsEnabled(true); + plot.renderTo(svg); + plot.barLabelFormatter(function (n) { return n.toString() + (n === 12345 ? "looong" : ""); }); + var texts = svg.selectAll("text")[0].map(function (n) { return d3.select(n).text(); }); + assert.lengthOf(texts, 0, "no text drawn"); + svg.remove(); + }); + it("formatters are used properly", function () { + plot.barLabelsEnabled(true); + plot.barLabelFormatter(function (n) { return n.toString() + "%"; }); + plot.renderTo(svg); + var texts = svg.selectAll("text")[0].map(function (n) { return d3.select(n).text(); }); + assert.lengthOf(texts, 2, "both texts drawn"); + assert.equal(texts[0], "640%", "first label is 640%"); + assert.equal(texts[1], "12345%", "first label is 12345%"); + svg.remove(); + }); + it("bar labels are removed instantly on dataset change, even if animation is enabled", function (done) { + plot.barLabelsEnabled(true); + plot.animate(true); + plot.renderTo(svg); + var texts = svg.selectAll("text")[0].map(function (n) { return d3.select(n).text(); }); + assert.lengthOf(texts, 2, "both texts drawn"); + var originalDrawLabels = plot._drawLabels; + var called = false; + plot._drawLabels = function () { + if (!called) { + originalDrawLabels.apply(plot); + texts = svg.selectAll("text")[0].map(function (n) { return d3.select(n).text(); }); + assert.lengthOf(texts, 2, "texts were repopulated by drawLabels after the update"); + svg.remove(); + called = true; // for some reason, in phantomJS, `done` was being called multiple times and this caused the test to fail. + done(); + } + }; + dataset.data(data); + texts = svg.selectAll("text")[0].map(function (n) { return d3.select(n).text(); }); + assert.lengthOf(texts, 0, "texts were immediately removed"); + }); + }); }); }); @@ -2468,6 +2654,32 @@ describe("Plots", function () { assert.closeTo(parseFloat(c2.attr("cy")), 0, 0.01, "second circle cy is correct after metadata change"); svg.remove(); }); + it("_getClosestStruckPoint()", function () { + var svg = generateSVG(400, 400); + var xScale = new Plottable.Scale.Linear(); + var yScale = new Plottable.Scale.Linear(); + xScale.domain([0, 400]); + yScale.domain([400, 0]); + var data1 = [ + { x: 80, y: 200, r: 20 }, + { x: 100, y: 200, r: 20 }, + { x: 125, y: 200, r: 5 }, + { x: 138, y: 200, r: 5 } + ]; + var plot = new Plottable.Plot.Scatter(xScale, yScale); + plot.addDataset(data1); + plot.project("x", "x").project("y", "y").project("r", "r"); + plot.renderTo(svg); + var twoOverlappingCirclesResult = plot._getClosestStruckPoint({ x: 85, y: 200 }, 10); + assert.strictEqual(twoOverlappingCirclesResult.data[0], data1[0], "returns closest circle among circles that the test point touches"); + var overlapAndCloseToPointResult = plot._getClosestStruckPoint({ x: 118, y: 200 }, 10); + assert.strictEqual(overlapAndCloseToPointResult.data[0], data1[1], "returns closest circle that test point touches, even if non-touched circles are closer"); + var twoPointsInRangeResult = plot._getClosestStruckPoint({ x: 130, y: 200 }, 10); + assert.strictEqual(twoPointsInRangeResult.data[0], data1[2], "returns closest circle within range if test point does not touch any circles"); + var farFromAnyPointsResult = plot._getClosestStruckPoint({ x: 400, y: 400 }, 10); + assert.isNull(farFromAnyPointsResult.data, "returns no data if no circle were within range and test point does not touch any circles"); + svg.remove(); + }); describe("Example ScatterPlot with quadratic series", function () { var svg; var xScale; @@ -2613,6 +2825,68 @@ describe("Plots", function () { assert.strictEqual(data2[0]["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"], -2, "positive offset was used"); assert.strictEqual(data4[0]["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"], -3, "positive offset was used"); }); + it("project can be called after addDataset", function () { + var data1 = [ + { a: 1, b: 2 } + ]; + var data2 = [ + { a: 1, b: 4 } + ]; + stackedPlot.addDataset(data1); + stackedPlot.addDataset(data2); + assert.isTrue(isNaN(data2[0]["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"]), "stacking is initially incorrect"); + stackedPlot.project("x", "a"); + stackedPlot.project("y", "b"); + assert.strictEqual(data2[0]["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"], 2, "stacking was done correctly"); + }); + it("strings are coerced to numbers for stacking", function () { + var data1 = [ + { x: 1, y: "-2" } + ]; + var data2 = [ + { x: 1, y: "3" } + ]; + var data3 = [ + { x: 1, y: "-1" } + ]; + var data4 = [ + { x: 1, y: "5" } + ]; + var data5 = [ + { x: 1, y: "1" } + ]; + var data6 = [ + { x: 1, y: "-1" } + ]; + stackedPlot.addDataset(data1); + stackedPlot.addDataset(data2); + stackedPlot.addDataset(data3); + stackedPlot.addDataset(data4); + stackedPlot.addDataset(data5); + stackedPlot.addDataset(data6); + assert.strictEqual(data3[0]["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"], -2, "stacking on data1 numerical y value"); + assert.strictEqual(data4[0]["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"], 3, "stacking on data2 numerical y value"); + assert.strictEqual(data5[0]["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"], 8, "stacking on data1 + data3 numerical y values"); + assert.strictEqual(data6[0]["_PLOTTABLE_PROTECTED_FIELD_STACK_OFFSET"], -3, "stacking on data2 + data4 numerical y values"); + assert.deepEqual(stackedPlot.stackedExtent, [-4, 9], "stacked extent is as normal"); + }); + it("stacks correctly on empty data", function () { + var data1 = [ + ]; + var data2 = [ + ]; + stackedPlot.addDataset(data1); + stackedPlot.addDataset(data2); + assert.deepEqual(data1, [], "empty data causes no stacking to happen"); + assert.deepEqual(data2, [], "empty data causes no stacking to happen"); + }); + it("does not crash on stacking no datasets", function () { + var data1 = [ + { x: 1, y: -2 } + ]; + stackedPlot.addDataset("a", data1); + assert.doesNotThrow(function () { return stackedPlot.removeDataset("a"); }, Error); + }); }); }); @@ -2844,6 +3118,67 @@ describe("Plots", function () { assert.strictEqual(oldUpperBound, yScale.domain()[1], "upper bound does not change"); svg.remove(); }); + it("warning is thrown when datasets are updated with different domains", function () { + var flag = false; + var oldWarn = Plottable._Util.Methods.warn; + Plottable._Util.Methods.warn = function (msg) { + if (msg.indexOf("domain") > -1) { + flag = true; + } + }; + var missingDomainData = [ + { x: 1, y: 0, type: "c" } + ]; + var dataset = new Plottable.Dataset(missingDomainData); + renderer.addDataset(dataset); + Plottable._Util.Methods.warn = oldWarn; + assert.isTrue(flag, "warning has been issued about differing domains"); + svg.remove(); + }); + }); + describe("Stacked Area Plot Project", function () { + var svg; + var xScale; + var yScale; + var renderer; + var SVG_WIDTH = 600; + var SVG_HEIGHT = 400; + beforeEach(function () { + svg = generateSVG(SVG_WIDTH, SVG_HEIGHT); + xScale = new Plottable.Scale.Linear().domain([1, 3]); + yScale = new Plottable.Scale.Linear().domain([0, 4]); + var colorScale = new Plottable.Scale.Color("10").domain(["a", "b"]); + var data1 = [ + { x: 1, yTest: 1, type: "a" }, + { x: 3, yTest: 2, type: "a" } + ]; + var data2 = [ + { x: 1, yTest: 3, type: "b" }, + { x: 3, yTest: 1, type: "b" } + ]; + renderer = new Plottable.Plot.StackedArea(xScale, yScale); + renderer.project("y", "yTest", yScale); + renderer.addDataset(data1); + renderer.addDataset(data2); + renderer.project("fill", "type", colorScale); + var xAxis = new Plottable.Axis.Numeric(xScale, "bottom"); + var table = new Plottable.Component.Table([[renderer], [xAxis]]).renderTo(svg); + }); + it("renders correctly", function () { + var areas = renderer._renderArea.selectAll(".area"); + var area0 = d3.select(areas[0][0]); + var d0 = normalizePath(area0.attr("d")).split(/[a-zA-Z]/); + var d0Ys = d0.slice(1, d0.length - 1).map(function (s) { return parseFloat(s.split(",")[1]); }); + assert.strictEqual(d0Ys.indexOf(0), -1, "bottom area never touches the top"); + var area1 = d3.select(areas[0][1]); + var d1 = normalizePath(area1.attr("d")).split(/[a-zA-Z]/); + var d1Ys = d1.slice(1, d1.length - 1).map(function (s) { return parseFloat(s.split(",")[1]); }); + assert.notEqual(d1Ys.indexOf(0), -1, "touches the top"); + var domain = yScale.domain(); + assert.strictEqual(0, domain[0], "domain starts at a min value at 0"); + assert.strictEqual(4, domain[1], "highest area stacking is at upper limit of yScale domain"); + svg.remove(); + }); }); }); @@ -4582,11 +4917,9 @@ describe("Scales", function () { var scale = new Plottable.Scale.Linear(); var ticks10 = scale.ticks(); assert.closeTo(ticks10.length, 10, 1, "defaults to (about) 10 ticks"); - var ticks20 = scale.ticks(20); + scale.numTicks(20); + var ticks20 = scale.ticks(); assert.closeTo(ticks20.length, 20, 1, "can request a different number of ticks"); - scale.numTicks(5); - var ticks5 = scale.ticks(); - assert.closeTo(ticks5.length, 5, 1, "can change the default number of ticks"); }); it("autorange defaults to [1, 10] on log scale", function () { var scale = new Plottable.Scale.Log(); @@ -4620,6 +4953,15 @@ describe("Scales", function () { assert.deepEqual(xScale.domain(), [2, 1000], "the domain was calculated appropriately"); svg.remove(); }); + it("custom tick generator", function () { + var scale = new Plottable.Scale.Linear(); + scale.domain([0, 10]); + var ticks = scale.ticks(); + assert.closeTo(ticks.length, 10, 1, "ticks were generated correctly with default generator"); + scale.tickGenerator(function (scale) { return scale.getDefaultTicks().filter(function (tick) { return tick % 3 === 0; }); }); + ticks = scale.ticks(); + assert.deepEqual(ticks, [0, 3, 6, 9], "ticks were generated correctly with custom generator"); + }); }); describe("Ordinal Scales", function () { it("defaults to \"bands\" range type", function () { @@ -4906,6 +5248,42 @@ describe("TimeScale tests", function () { }); }); +/// +var assert = chai.assert; +describe("Tick generators", function () { + describe("interval", function () { + it("generate ticks within domain", function () { + var start = 0.5, end = 4.01, interval = 1; + var scale = new Plottable.Scale.Linear().domain([start, end]); + var ticks = Plottable.Scale.TickGenerators.intervalTickGenerator(interval)(scale); + assert.deepEqual(ticks, [0.5, 1, 2, 3, 4, 4.01], "generated ticks contains all possible ticks within range"); + }); + it("domain crossing 0", function () { + var start = -1.5, end = 1, interval = 0.5; + var scale = new Plottable.Scale.Linear().domain([start, end]); + var ticks = Plottable.Scale.TickGenerators.intervalTickGenerator(interval)(scale); + assert.deepEqual(ticks, [-1.5, -1, -0.5, 0, 0.5, 1], "generated all number divisible by 0.5 in domain"); + }); + it("generate ticks with reversed domain", function () { + var start = -2.2, end = -7.6, interval = 2.5; + var scale = new Plottable.Scale.Linear().domain([start, end]); + var ticks = Plottable.Scale.TickGenerators.intervalTickGenerator(interval)(scale); + assert.deepEqual(ticks, [-7.6, -7.5, -5, -2.5, -2.2], "generated all ticks between lower and higher value"); + }); + it("passing big interval", function () { + var start = 0.5, end = 10.01, interval = 11; + var scale = new Plottable.Scale.Linear().domain([start, end]); + var ticks = Plottable.Scale.TickGenerators.intervalTickGenerator(interval)(scale); + assert.deepEqual(ticks, [0.5, 10.01], "no middle ticks were added"); + }); + it("passing non positive interval", function () { + var scale = new Plottable.Scale.Linear().domain([0, 1]); + assert.throws(function () { return Plottable.Scale.TickGenerators.intervalTickGenerator(0); }, "interval must be positive number"); + assert.throws(function () { return Plottable.Scale.TickGenerators.intervalTickGenerator(-2); }, "interval must be positive number"); + }); + }); +}); + /// var assert = chai.assert; describe("_Util.DOM", function () { @@ -5411,12 +5789,12 @@ describe("_Util.Text", function () { var height = 1; var textSelection = svg.append("text"); var measure = Plottable._Util.Text.getTextMeasurer(textSelection); - var results = Plottable._Util.Text.writeText("hello world", width, height, measure, "horizontal"); + var results = Plottable._Util.Text.writeText("abc", width, height, measure, "horizontal"); assert.isFalse(results.textFits, "measurement mode: text doesn't fit"); assert.equal(0, results.usedWidth, "measurement mode: no width used"); assert.equal(0, results.usedHeight, "measurement mode: no height used"); var writeOptions = { g: svg, xAlign: "center", yAlign: "center" }; - results = Plottable._Util.Text.writeText("hello world", width, height, measure, "horizontal", writeOptions); + results = Plottable._Util.Text.writeText("abc", width, height, measure, "horizontal", writeOptions); assert.isFalse(results.textFits, "write mode: text doesn't fit"); assert.equal(0, results.usedWidth, "write mode: no width used"); assert.equal(0, results.usedHeight, "write mode: no height used"); @@ -5424,6 +5802,38 @@ describe("_Util.Text", function () { assert.lengthOf(svg.selectAll("text")[0], 0, "no text was written"); svg.remove(); }); + it("behaves appropriately when text is in horizontal position", function () { + var svg = generateSVG(); + var width = 100; + var height = 50; + var textSelection = svg.append("text"); + var measure = Plottable._Util.Text.getTextMeasurer(textSelection); + var measureResults = Plottable._Util.Text.writeText("abc", width, height, measure, "horizontal"); + assert.isTrue(measureResults.textFits, "mesurement mode: text fits"); + assert.operator(measureResults.usedHeight, "<=", measureResults.usedWidth, "mesurement mode: used more width than height"); + var writeOptions = { g: svg, xAlign: "left", yAlign: "top" }; + var writeResults = Plottable._Util.Text.writeText("abc", width, height, measure, "horizontal", writeOptions); + assert.isTrue(writeResults.textFits, "write mode: text fits"); + assert.equal(measureResults.usedWidth, writeResults.usedWidth, "write mode: used the same width as measurement"); + assert.equal(measureResults.usedHeight, writeResults.usedHeight, "write mode: used the same height as measurement"); + svg.remove(); + }); + it("behaves appropriately when text is in vertical position", function () { + var svg = generateSVG(); + var width = 100; + var height = 50; + var textSelection = svg.append("text"); + var measure = Plottable._Util.Text.getTextMeasurer(textSelection); + var measureResults = Plottable._Util.Text.writeText("abc", width, height, measure, "left"); + assert.isTrue(measureResults.textFits, "mesurement mode: text fits"); + assert.operator(measureResults.usedHeight, ">=", measureResults.usedWidth, "mesurement mode: used more height than width"); + var writeOptions = { g: svg, xAlign: "left", yAlign: "top" }; + var writeResults = Plottable._Util.Text.writeText("abc", width, height, measure, "left", writeOptions); + assert.isTrue(writeResults.textFits, "write mode: text fits"); + assert.equal(measureResults.usedWidth, writeResults.usedWidth, "write mode: used the same width as measurement"); + assert.equal(measureResults.usedHeight, writeResults.usedHeight, "write mode: used the same height as measurement"); + svg.remove(); + }); }); describe("getTextMeasurer", function () { var svg; @@ -5612,25 +6022,49 @@ describe("_Util.Methods", function () { var strings = ["foo", "bar", "foo", "foo", "baz", "bam"]; assert.deepEqual(Plottable._Util.Methods.uniq(strings), ["foo", "bar", "baz", "bam"]); }); - it("max/min work as expected", function () { - var alist = [1, 2, 3, 4, 5]; - var dbl = function (x) { return x * 2; }; + describe("min/max", function () { var max = Plottable._Util.Methods.max; var min = Plottable._Util.Methods.min; - assert.deepEqual(max(alist), 5, "max works as expected on plain array"); - assert.deepEqual(max(alist, 99), 5, "max ignores default on non-empty array"); - assert.deepEqual(max(alist, dbl), 10, "max applies function appropriately"); - assert.deepEqual(max([]), 0, "default value zero by default"); - assert.deepEqual(max([], 10), 10, "works as intended with default value"); - assert.deepEqual(max([], dbl), 0, "default value zero as expected when fn provided"); - assert.deepEqual(max([], dbl, 5), 5, "default value works with function"); - assert.deepEqual(min(alist, 0), 1, "min works for basic list"); - assert.deepEqual(min(alist, dbl, 0), 2, "min works with function arg"); - assert.deepEqual(min([]), 0, "min defaults to 0"); - assert.deepEqual(min([], dbl, 5), 5, "min accepts custom default and function"); - var strings = ["a", "bb", "ccc", "ddd"]; - assert.deepEqual(max(strings, function (s) { return s.length; }), 3, "works on arrays of non-numbers with a function"); - assert.deepEqual(max([], function (s) { return s.length; }, 5), 5, "defaults work even with non-number function type"); + var today = new Date(); + it("max/min work as expected", function () { + var alist = [1, 2, 3, 4, 5]; + var dbl = function (x) { return x * 2; }; + var dblIndexOffset = function (x, i) { return x * 2 - i; }; + var numToDate = function (x) { + var t = new Date(today.getTime()); + t.setDate(today.getDate() + x); + return t; + }; + assert.deepEqual(max(alist, 99), 5, "max ignores default on non-empty array"); + assert.deepEqual(max(alist, dbl, 0), 10, "max applies function appropriately"); + assert.deepEqual(max(alist, dblIndexOffset, 5), 6, "max applies function with index"); + assert.deepEqual(max(alist, numToDate, today), numToDate(5), "max applies non-numeric function appropriately"); + assert.deepEqual(max([], 10), 10, "works as intended with default value"); + assert.deepEqual(max([], dbl, 5), 5, "default value works with function"); + assert.deepEqual(max([], numToDate, today), today, "default non-numeric value works with non-numeric function"); + assert.deepEqual(min(alist, 0), 1, "min works for basic list"); + assert.deepEqual(min(alist, dbl, 0), 2, "min works with function arg"); + assert.deepEqual(min(alist, dblIndexOffset, 0), 2, "min works with function index arg"); + assert.deepEqual(min(alist, numToDate, today), numToDate(1), "min works with non-numeric function arg"); + assert.deepEqual(min([], dbl, 5), 5, "min accepts custom default and function"); + assert.deepEqual(min([], numToDate, today), today, "min accepts non-numeric default and function"); + }); + it("max/min works as expected on non-numeric values (strings)", function () { + var strings = ["a", "bb", "ccc", "ddd"]; + assert.deepEqual(max(strings, function (s) { return s.length; }, 0), 3, "works on arrays of non-numbers with a function"); + assert.deepEqual(max([], function (s) { return s.length; }, 5), 5, "defaults work even with non-number function type"); + }); + it("max/min works as expected on non-numeric values (dates)", function () { + var tomorrow = new Date(today.getTime()); + tomorrow.setDate(today.getDate() + 1); + var dayAfterTomorrow = new Date(today.getTime()); + dayAfterTomorrow.setDate(today.getDate() + 2); + var dates = [today, tomorrow, dayAfterTomorrow, null]; + assert.deepEqual(min(dates, dayAfterTomorrow), today, "works on arrays of non-numeric values but comparable"); + assert.deepEqual(max(dates, today), dayAfterTomorrow, "works on arrays of non-number values but comparable"); + assert.deepEqual(max([null], today), undefined, "returns undefined from array of null values"); + assert.deepEqual(max([], today), today, "correct default non-numeric value returned"); + }); }); it("objEq works as expected", function () { assert.isTrue(Plottable._Util.Methods.objEq({}, {})); @@ -5654,6 +6088,42 @@ describe("_Util.Methods", function () { var emptyMap = Plottable._Util.Methods.populateMap(emptyKeys, function (key) { return key + "Value"; }); assert.isTrue(emptyMap.empty(), "no entries in map if no keys in input array"); }); + it("copyMap works as expected", function () { + var oldMap = {}; + oldMap["a"] = 1; + oldMap["b"] = 2; + oldMap["c"] = 3; + oldMap["undefined"] = undefined; + oldMap["null"] = null; + oldMap["fun"] = function (d) { return d; }; + oldMap["NaN"] = 0 / 0; + oldMap["inf"] = 1 / 0; + var map = Plottable._Util.Methods.copyMap(oldMap); + assert.deepEqual(map, oldMap, "All values were copied."); + map = Plottable._Util.Methods.copyMap({}); + assert.deepEqual(map, {}, "No values were added."); + }); + it("range works as expected", function () { + var start = 0; + var end = 6; + var range = Plottable._Util.Methods.range(start, end); + assert.deepEqual(range, [0, 1, 2, 3, 4, 5], "all entries has been generated"); + range = Plottable._Util.Methods.range(start, end, 2); + assert.deepEqual(range, [0, 2, 4], "all entries has been generated"); + range = Plottable._Util.Methods.range(start, end, 11); + assert.deepEqual(range, [0], "all entries has been generated"); + assert.throws(function () { return Plottable._Util.Methods.range(start, end, 0); }, "step cannot be 0"); + range = Plottable._Util.Methods.range(start, end, -1); + assert.lengthOf(range, 0, "no entries because of invalid step"); + range = Plottable._Util.Methods.range(end, start, -1); + assert.deepEqual(range, [6, 5, 4, 3, 2, 1], "all entries has been generated"); + range = Plottable._Util.Methods.range(-2, 2); + assert.deepEqual(range, [-2, -1, 0, 1], "all entries has been generated range crossing 0"); + range = Plottable._Util.Methods.range(0.2, 4); + assert.deepEqual(range, [0.2, 1.2, 2.2, 3.2], "all entries has been generated with float start"); + range = Plottable._Util.Methods.range(0.6, 2.2, 0.5); + assert.deepEqual(range, [0.6, 1.1, 1.6, 2.1], "all entries has been generated with float step"); + }); }); /// @@ -5990,6 +6460,121 @@ describe("Interactions", function () { }); }); +/// +var __extends = this.__extends || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + __.prototype = b.prototype; + d.prototype = new __(); +}; +var assert = chai.assert; +var TestHoverable = (function (_super) { + __extends(TestHoverable, _super); + function TestHoverable() { + _super.apply(this, arguments); + } + TestHoverable.prototype._hoverOverComponent = function (p) { + // cast-override + }; + TestHoverable.prototype._hoverOutComponent = function (p) { + // cast-override + }; + TestHoverable.prototype._doHover = function (p) { + var data = []; + if (p.x < 250) { + data.push("left"); + } + if (p.x > 150) { + data.push("right"); + } + return { + data: data, + selection: this._element + }; + }; + return TestHoverable; +})(Plottable.Component.AbstractComponent); +describe("Interactions", function () { + describe("Hover", function () { + var svg; + var testTarget; + var hitbox; + var hoverInteraction; + var overData; + var overCallbackCalled = false; + var outData; + var outCallbackCalled = false; + beforeEach(function () { + svg = generateSVG(); + testTarget = new TestHoverable(); + testTarget.classed("test-hoverable", true); + testTarget.renderTo(svg); + hoverInteraction = new Plottable.Interaction.Hover(); + overCallbackCalled = false; + hoverInteraction.onHoverOver(function (hd) { + overCallbackCalled = true; + overData = hd; + }); + outCallbackCalled = false; + hoverInteraction.onHoverOut(function (hd) { + outCallbackCalled = true; + outData = hd; + }); + testTarget.registerInteraction(hoverInteraction); + hitbox = testTarget._element.select(".hit-box"); + }); + it("correctly triggers onHoverOver() callbacks", function () { + overCallbackCalled = false; + triggerFakeMouseEvent("mouseover", hitbox, 100, 200); + assert.isTrue(overCallbackCalled, "onHoverOver was called on mousing over a target area"); + assert.deepEqual(overData.data, ["left"], "onHoverOver was called with the correct data (mouse onto left)"); + overCallbackCalled = false; + triggerFakeMouseEvent("mousemove", hitbox, 100, 200); + assert.isFalse(overCallbackCalled, "onHoverOver isn't called if the hover data didn't change"); + overCallbackCalled = false; + triggerFakeMouseEvent("mousemove", hitbox, 200, 200); + assert.isTrue(overCallbackCalled, "onHoverOver was called when mousing into a new region"); + assert.deepEqual(overData.data, ["right"], "onHoverOver was called with the new data only (left --> center)"); + triggerFakeMouseEvent("mouseout", hitbox, 400, 200); + overCallbackCalled = false; + triggerFakeMouseEvent("mouseover", hitbox, 200, 200); + assert.deepEqual(overData.data, ["left", "right"], "onHoverOver is called with the correct data"); + svg.remove(); + }); + it("correctly triggers onHoverOut() callbacks", function () { + triggerFakeMouseEvent("mouseover", hitbox, 100, 200); + outCallbackCalled = false; + triggerFakeMouseEvent("mousemove", hitbox, 200, 200); + assert.isFalse(outCallbackCalled, "onHoverOut isn't called when mousing into a new region without leaving the old one"); + outCallbackCalled = false; + triggerFakeMouseEvent("mousemove", hitbox, 300, 200); + assert.isTrue(outCallbackCalled, "onHoverOut was called when the hover data changes"); + assert.deepEqual(outData.data, ["left"], "onHoverOut was called with the correct data (center --> right)"); + outCallbackCalled = false; + triggerFakeMouseEvent("mouseout", hitbox, 400, 200); + assert.isTrue(outCallbackCalled, "onHoverOut is called on mousing out of the Component"); + assert.deepEqual(outData.data, ["right"], "onHoverOut was called with the correct data"); + outCallbackCalled = false; + triggerFakeMouseEvent("mouseover", hitbox, 200, 200); + triggerFakeMouseEvent("mouseout", hitbox, 200, 400); + assert.deepEqual(outData.data, ["left", "right"], "onHoverOut is called with the correct data"); + svg.remove(); + }); + it("getCurrentHoverData()", function () { + triggerFakeMouseEvent("mouseover", hitbox, 100, 200); + var currentlyHovered = hoverInteraction.getCurrentHoverData(); + assert.deepEqual(currentlyHovered.data, ["left"], "retrieves data corresponding to the current position"); + triggerFakeMouseEvent("mousemove", hitbox, 200, 200); + currentlyHovered = hoverInteraction.getCurrentHoverData(); + assert.deepEqual(currentlyHovered.data, ["left", "right"], "retrieves data corresponding to the current position"); + triggerFakeMouseEvent("mouseout", hitbox, 400, 200); + currentlyHovered = hoverInteraction.getCurrentHoverData(); + assert.isNull(currentlyHovered.data, "returns null if not currently hovering"); + svg.remove(); + }); + }); +}); + /// var assert = chai.assert; describe("Dispatchers", function () { diff --git a/test/utils/textUtilsTests.ts b/test/utils/textUtilsTests.ts index 09cd32e099..2e4f7bf3ab 100644 --- a/test/utils/textUtilsTests.ts +++ b/test/utils/textUtilsTests.ts @@ -91,13 +91,13 @@ describe("_Util.Text", () => { var height = 1; var textSelection = svg.append("text"); var measure = Plottable._Util.Text.getTextMeasurer(textSelection); - var results = Plottable._Util.Text.writeText("hello world", width, height, measure, "horizontal"); + var results = Plottable._Util.Text.writeText("abc", width, height, measure, "horizontal"); assert.isFalse(results.textFits, "measurement mode: text doesn't fit"); assert.equal(0, results.usedWidth, "measurement mode: no width used"); assert.equal(0, results.usedHeight, "measurement mode: no height used"); var writeOptions = {g: svg, xAlign: "center", yAlign: "center"}; - results = Plottable._Util.Text.writeText("hello world", width, height, measure, "horizontal", writeOptions); + results = Plottable._Util.Text.writeText("abc", width, height, measure, "horizontal", writeOptions); assert.isFalse(results.textFits, "write mode: text doesn't fit"); assert.equal(0, results.usedWidth, "write mode: no width used"); assert.equal(0, results.usedHeight, "write mode: no height used"); @@ -105,6 +105,48 @@ describe("_Util.Text", () => { assert.lengthOf(svg.selectAll("text")[0], 0, "no text was written"); svg.remove(); }); + + it("behaves appropriately when text is in horizontal position", () => { + var svg = generateSVG(); + var width = 100; + var height = 50; + var textSelection = svg.append("text"); + var measure = Plottable._Util.Text.getTextMeasurer(textSelection); + var measureResults = Plottable._Util.Text.writeText("abc", width, height, measure, "horizontal"); + assert.isTrue(measureResults.textFits, "mesurement mode: text fits"); + assert.operator(measureResults.usedHeight, + "<=", + measureResults.usedWidth, + "mesurement mode: used more width than height"); + + var writeOptions = {g: svg, xAlign: "left", yAlign: "top"}; + var writeResults = Plottable._Util.Text.writeText("abc", width, height, measure, "horizontal", writeOptions); + assert.isTrue(writeResults.textFits, "write mode: text fits"); + assert.equal(measureResults.usedWidth, writeResults.usedWidth, "write mode: used the same width as measurement"); + assert.equal(measureResults.usedHeight, writeResults.usedHeight, "write mode: used the same height as measurement"); + svg.remove(); + }); + + it("behaves appropriately when text is in vertical position", () => { + var svg = generateSVG(); + var width = 100; + var height = 50; + var textSelection = svg.append("text"); + var measure = Plottable._Util.Text.getTextMeasurer(textSelection); + var measureResults = Plottable._Util.Text.writeText("abc", width, height, measure, "left"); + assert.isTrue(measureResults.textFits, "mesurement mode: text fits"); + assert.operator(measureResults.usedHeight, + ">=", + measureResults.usedWidth, + "mesurement mode: used more height than width"); + + var writeOptions = {g: svg, xAlign: "left", yAlign: "top"}; + var writeResults = Plottable._Util.Text.writeText("abc", width, height, measure, "left", writeOptions); + assert.isTrue(writeResults.textFits, "write mode: text fits"); + assert.equal(measureResults.usedWidth, writeResults.usedWidth, "write mode: used the same width as measurement"); + assert.equal(measureResults.usedHeight, writeResults.usedHeight, "write mode: used the same height as measurement"); + svg.remove(); + }); }); describe("getTextMeasurer", () => { diff --git a/test/utils/utilsTests.ts b/test/utils/utilsTests.ts index edc5bc6e78..d777a09d1c 100644 --- a/test/utils/utilsTests.ts +++ b/test/utils/utilsTests.ts @@ -42,27 +42,55 @@ describe("_Util.Methods", () => { assert.deepEqual(Plottable._Util.Methods.uniq(strings), ["foo", "bar", "baz", "bam"]); }); - it("max/min work as expected", () => { - var alist = [1,2,3,4,5]; - var dbl = (x: number) => x * 2; + + describe("min/max", () => { var max = Plottable._Util.Methods.max; var min = Plottable._Util.Methods.min; - assert.deepEqual(max(alist), 5, "max works as expected on plain array"); - assert.deepEqual(max(alist, 99), 5, "max ignores default on non-empty array"); - assert.deepEqual(max(alist, dbl), 10, "max applies function appropriately"); - assert.deepEqual(max([]), 0, "default value zero by default"); - assert.deepEqual(max([], 10), 10, "works as intended with default value"); - assert.deepEqual(max([], dbl), 0, "default value zero as expected when fn provided"); - assert.deepEqual(max([], dbl, 5), 5, "default value works with function"); - - assert.deepEqual(min(alist, 0), 1, "min works for basic list"); - assert.deepEqual(min(alist, dbl, 0), 2, "min works with function arg"); - assert.deepEqual(min([]), 0, "min defaults to 0"); - assert.deepEqual(min([], dbl, 5), 5, "min accepts custom default and function"); - - var strings = ["a", "bb", "ccc", "ddd"]; - assert.deepEqual(max(strings, (s: string) => s.length), 3, "works on arrays of non-numbers with a function"); - assert.deepEqual(max([], (s: string) => s.length, 5), 5, "defaults work even with non-number function type"); + var today = new Date(); + + it("max/min work as expected", () => { + var alist = [1,2,3,4,5]; + var dbl = (x: number) => x * 2; + var dblIndexOffset = (x: number, i: number) => x * 2 - i; + var numToDate = (x: number) => { + var t = new Date(today.getTime()); + t.setDate(today.getDate() + x); + return t; + }; + + assert.deepEqual(max(alist, 99), 5, "max ignores default on non-empty array"); + assert.deepEqual(max(alist, dbl, 0), 10, "max applies function appropriately"); + assert.deepEqual(max(alist, dblIndexOffset, 5), 6, "max applies function with index"); + assert.deepEqual(max(alist, numToDate, today), numToDate(5), "max applies non-numeric function appropriately"); + assert.deepEqual(max([], 10), 10, "works as intended with default value"); + assert.deepEqual(max([], dbl, 5), 5, "default value works with function"); + assert.deepEqual(max([], numToDate, today), today, "default non-numeric value works with non-numeric function"); + + assert.deepEqual(min(alist, 0), 1, "min works for basic list"); + assert.deepEqual(min(alist, dbl, 0), 2, "min works with function arg"); + assert.deepEqual(min(alist, dblIndexOffset, 0), 2, "min works with function index arg"); + assert.deepEqual(min(alist, numToDate, today), numToDate(1), "min works with non-numeric function arg"); + assert.deepEqual(min([], dbl, 5), 5, "min accepts custom default and function"); + assert.deepEqual(min([], numToDate, today), today, "min accepts non-numeric default and function"); + }); + + it("max/min works as expected on non-numeric values (strings)", () => { + var strings = ["a", "bb", "ccc", "ddd"]; + assert.deepEqual(max(strings, (s: string) => s.length, 0), 3, "works on arrays of non-numbers with a function"); + assert.deepEqual(max([], (s: string) => s.length, 5), 5, "defaults work even with non-number function type"); + }); + + it("max/min works as expected on non-numeric values (dates)", () => { + var tomorrow = new Date(today.getTime()); + tomorrow.setDate(today.getDate() + 1); + var dayAfterTomorrow = new Date(today.getTime()); + dayAfterTomorrow.setDate(today.getDate() + 2); + var dates: Date[] = [today, tomorrow, dayAfterTomorrow, null]; + assert.deepEqual(min(dates, dayAfterTomorrow), today, "works on arrays of non-numeric values but comparable"); + assert.deepEqual(max(dates, today), dayAfterTomorrow, "works on arrays of non-number values but comparable"); + assert.deepEqual(max([null], today), undefined, "returns undefined from array of null values"); + assert.deepEqual(max([], today), today, "correct default non-numeric value returned"); + }); }); it("objEq works as expected", () => { @@ -95,4 +123,54 @@ describe("_Util.Methods", () => { assert.isTrue(emptyMap.empty(), "no entries in map if no keys in input array"); }); + + it("copyMap works as expected", () => { + var oldMap: {[key: string]: any} = {}; + oldMap["a"] = 1; + oldMap["b"] = 2; + oldMap["c"] = 3; + oldMap["undefined"] = undefined; + oldMap["null"] = null; + oldMap["fun"] = (d: number) => d; + oldMap["NaN"] = 0 / 0; + oldMap["inf"] = 1 / 0; + + var map = Plottable._Util.Methods.copyMap(oldMap); + + assert.deepEqual(map, oldMap, "All values were copied."); + + map = Plottable._Util.Methods.copyMap({}); + + assert.deepEqual(map, {}, "No values were added."); + }); + + it("range works as expected", () => { + var start = 0; + var end = 6; + var range = Plottable._Util.Methods.range(start, end); + assert.deepEqual(range, [0, 1, 2, 3, 4, 5], "all entries has been generated"); + + range = Plottable._Util.Methods.range(start, end, 2); + assert.deepEqual(range, [0, 2, 4], "all entries has been generated"); + + range = Plottable._Util.Methods.range(start, end, 11); + assert.deepEqual(range, [0], "all entries has been generated"); + + assert.throws(() => Plottable._Util.Methods.range(start, end, 0), "step cannot be 0"); + + range = Plottable._Util.Methods.range(start, end, -1); + assert.lengthOf(range, 0, "no entries because of invalid step"); + + range = Plottable._Util.Methods.range(end, start, -1); + assert.deepEqual(range, [6, 5, 4, 3, 2, 1], "all entries has been generated"); + + range = Plottable._Util.Methods.range(-2, 2); + assert.deepEqual(range, [-2, -1, 0, 1], "all entries has been generated range crossing 0"); + + range = Plottable._Util.Methods.range(0.2, 4); + assert.deepEqual(range, [0.2, 1.2, 2.2, 3.2], "all entries has been generated with float start"); + + range = Plottable._Util.Methods.range(0.6, 2.2, 0.5); + assert.deepEqual(range, [0.6, 1.1, 1.6, 2.1], "all entries has been generated with float step"); + }); }); diff --git a/tslint.json b/tslint.json index fbe79a1d83..ffa592358f 100644 --- a/tslint.json +++ b/tslint.json @@ -4,7 +4,6 @@ "curly": true, "eofline": true, "forin": true, - "indent": [true, 2], "jsdoc-format": true, "label-position": true, "label-undefined": true, diff --git a/typings/d3/d3.d.ts b/typings/d3/d3.d.ts index 42e1c3c316..92a2d253a1 100644 --- a/typings/d3/d3.d.ts +++ b/typings/d3/d3.d.ts @@ -89,7 +89,7 @@ declare module D3 { * @param arr Array to search * @param map Accsessor function */ - min(arr: T[], map: (v: T) => U): U; + min(arr: T[], map: (v?: T, i?: number) => U): U; /** * Find the minimum value in an array * @@ -102,7 +102,7 @@ declare module D3 { * @param arr Array to search * @param map Accsessor function */ - max(arr: T[], map: (v: T) => U): U; + max(arr: T[], map: (v?: T, i?: number) => U): U; /** * Find the maximum value in an array * @@ -719,6 +719,7 @@ declare module D3 { (name: string): string; (name: string, value: any): Selection; (name: string, valueFunction: (data: any, index: number) => any): Selection; + (classValueMap: Object): Selection; }; style: {