UNPKG

kibana-with-account

Version:

kibana-with-account is develop based kibana project

237 lines (199 loc) 7.28 kB
define(function (require) { return function YAxisFactory(Private) { var d3 = require('d3'); var _ = require('lodash'); var $ = require('jquery'); var errors = require('ui/errors'); var ErrorHandler = Private(require('ui/vislib/lib/_error_handler')); /** * Appends y axis to the visualization * * @class YAxis * @constructor * @param args {{el: (HTMLElement), yMax: (Number), _attr: (Object|*)}} */ _.class(YAxis).inherits(ErrorHandler); function YAxis(args) { this.el = args.el; this.scale = null; this.domain = [args.yMin, args.yMax]; this.yAxisFormatter = args.yAxisFormatter; this._attr = args._attr || {}; } /** * Renders the y axis * * @method render * @return {D3.UpdateSelection} Renders y axis to visualization */ YAxis.prototype.render = function () { d3.select(this.el).selectAll('.y-axis-div').call(this.draw()); }; YAxis.prototype._isPercentage = function () { return (this._attr.mode === 'percentage'); }; YAxis.prototype._isUserDefined = function () { return (this._attr.setYExtents); }; YAxis.prototype._isYExtents = function () { return (this._attr.defaultYExtents); }; YAxis.prototype._validateUserExtents = function (domain) { var self = this; return domain.map(function (val) { val = parseInt(val, 10); if (isNaN(val)) throw new Error(val + ' is not a valid number'); if (self._isPercentage() && self._attr.setYExtents) return val / 100; return val; }); }; YAxis.prototype._getExtents = function (domain) { var min = domain[0]; var max = domain[1]; if (this._isUserDefined()) return this._validateUserExtents(domain); if (this._isYExtents()) return domain; if (this._attr.scale === 'log') return this._logDomain(min, max); // Negative values cannot be displayed with a log scale. if (!this._isYExtents() && !this._isUserDefined()) return [Math.min(0, min), Math.max(0, max)]; return domain; }; YAxis.prototype._throwCustomError = function (message) { throw new Error(message); }; YAxis.prototype._throwLogScaleValuesError = function () { throw new errors.InvalidLogScaleValues(); }; /** * Returns the appropriate D3 scale * * @param fnName {String} D3 scale * @returns {*} */ YAxis.prototype._getScaleType = function (fnName) { if (fnName === 'square root') fnName = 'sqrt'; // Rename 'square root' to 'sqrt' fnName = fnName || 'linear'; if (typeof d3.scale[fnName] !== 'function') return this._throwCustomError('YAxis.getScaleType: ' + fnName + ' is not a function'); return d3.scale[fnName](); }; /** * Return the domain for log scale, i.e. the extent of the log scale. * Log scales must begin at 1 since the log(0) = -Infinity * * @param scale * @param yMin * @param yMax * @returns {*[]} */ YAxis.prototype._logDomain = function (min, max) { if (min < 0 || max < 0) return this._throwLogScaleValuesError(); return [1, max]; }; /** * Creates the d3 y scale function * * @method getYScale * @param height {Number} DOM Element height * @returns {D3.Scale.QuantitiveScale|*} D3 yScale function */ YAxis.prototype.getYScale = function (height) { var scale = this._getScaleType(this._attr.scale); var domain = this._getExtents(this.domain); this.yScale = scale .domain(domain) .range([height, 0]); if (!this._isUserDefined()) this.yScale.nice(); // round extents when not user defined // Prevents bars from going off the chart when the y extents are within the domain range if (this._attr.type === 'histogram') this.yScale.clamp(true); return this.yScale; }; YAxis.prototype.getScaleType = function () { return this._attr.scale; }; YAxis.prototype.tickFormat = function () { var isPercentage = this._attr.mode === 'percentage'; if (isPercentage) return d3.format('%'); if (this.yAxisFormatter) return this.yAxisFormatter; return d3.format('n'); }; YAxis.prototype._validateYScale = function (yScale) { if (!yScale || _.isNaN(yScale)) throw new Error('yScale is ' + yScale); }; /** * Creates the d3 y axis function * * @method getYAxis * @param height {Number} DOM Element height * @returns {D3.Svg.Axis|*} D3 yAxis function */ YAxis.prototype.getYAxis = function (height) { var yScale = this.getYScale(height); this._validateYScale(yScale); // Create the d3 yAxis function this.yAxis = d3.svg.axis() .scale(yScale) .tickFormat(this.tickFormat(this.domain)) .ticks(this.tickScale(height)) .orient('left'); return this.yAxis; }; /** * Create a tick scale for the y axis that modifies the number of ticks * based on the height of the wrapping DOM element * Avoid using even numbers in the yTickScale.range * Causes the top most tickValue in the chart to be missing * * @method tickScale * @param height {Number} DOM element height * @returns {number} Number of y axis ticks */ YAxis.prototype.tickScale = function (height) { var yTickScale = d3.scale.linear() .clamp(true) .domain([20, 40, 1000]) .range([0, 3, 11]); return Math.ceil(yTickScale(height)); }; /** * Renders the y axis to the visualization * * @method draw * @returns {Function} Renders y axis to visualization */ YAxis.prototype.draw = function () { var self = this; var margin = this._attr.margin; var mode = this._attr.mode; var isWiggleOrSilhouette = (mode === 'wiggle' || mode === 'silhouette'); return function (selection) { selection.each(function () { var el = this; var div = d3.select(el); var width = $(el).parent().width(); var height = $(el).height(); var adjustedHeight = height - margin.top - margin.bottom; // Validate whether width and height are not 0 or `NaN` self.validateWidthandHeight(width, adjustedHeight); var yAxis = self.getYAxis(adjustedHeight); // The yAxis should not appear if mode is set to 'wiggle' or 'silhouette' if (!isWiggleOrSilhouette) { // Append svg and y axis var svg = div.append('svg') .attr('width', width) .attr('height', height); svg.append('g') .attr('class', 'y axis') .attr('transform', 'translate(' + (width - 2) + ',' + margin.top + ')') .call(yAxis); var container = svg.select('g.y.axis').node(); if (container) { var cWidth = Math.max(width, container.getBBox().width); svg.attr('width', cWidth); svg.select('g') .attr('transform', 'translate(' + (cWidth - 2) + ',' + margin.top + ')'); } } }); }; }; return YAxis; }; });