UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

548 lines (470 loc) • 18.4 kB
"use strict"; var BaseSparkline = require("./base_sparkline"), dataValidatorModule = require("../components/data_validator"), seriesModule = require("../series/base_series"), MIN_BAR_WIDTH = 1, MAX_BAR_WIDTH = 50, DEFAULT_BAR_INTERVAL = 4, DEFAULT_CANVAS_WIDTH = 250, DEFAULT_CANVAS_HEIGHT = 30, DEFAULT_POINT_BORDER = 2, ALLOWED_TYPES = { "line": true, "spline": true, "stepline": true, "area": true, "steparea": true, "splinearea": true, "bar": true, "winloss": true }, _math = Math, _abs = _math.abs, _round = _math.round, _max = _math.max, _min = _math.min, _isFinite = isFinite, vizUtils = require("../core/utils"), _map = vizUtils.map, _normalizeEnum = vizUtils.normalizeEnum, _isDefined = require("../../core/utils/type").isDefined, _Number = Number, _String = String; var dxSparkline = BaseSparkline.inherit({ _rootClassPrefix: "dxsl", _rootClass: "dxsl-sparkline", _widgetType: "sparkline", _defaultSize: { width: DEFAULT_CANVAS_WIDTH, height: DEFAULT_CANVAS_HEIGHT }, _initCore: function _initCore() { this.callBase(); this._createSeries(); }, _initialChanges: ["DATA_SOURCE"], _dataSourceChangedHandler: function _dataSourceChangedHandler() { this._requestChange(["UPDATE"]); }, _updateWidgetElements: function _updateWidgetElements() { this._updateSeries(); this.callBase(); }, _disposeWidgetElements: function _disposeWidgetElements() { var that = this; that._series && that._series.dispose(); that._series = that._seriesGroup = that._seriesLabelGroup = null; }, _cleanWidgetElements: function _cleanWidgetElements() { this._seriesGroup.remove(); this._seriesLabelGroup.remove(); this._seriesGroup.clear(); this._seriesLabelGroup.clear(); }, _drawWidgetElements: function _drawWidgetElements() { if (this._dataIsLoaded()) { this._drawSeries(); this._drawn(); } }, _getCorrectCanvas: function _getCorrectCanvas() { var options = this._allOptions, canvas = this._canvas, halfPointSize = options.pointSize && Math.ceil(options.pointSize / 2) + DEFAULT_POINT_BORDER, type = options.type; if (type !== "bar" && type !== "winloss" && (options.showFirstLast || options.showMinMax)) { return { width: canvas.width, height: canvas.height, left: canvas.left + halfPointSize, right: canvas.right + halfPointSize, top: canvas.top + halfPointSize, bottom: canvas.bottom + halfPointSize }; } return canvas; }, _prepareOptions: function _prepareOptions() { var that = this; that._allOptions = that.callBase(); that._allOptions.type = _normalizeEnum(that._allOptions.type); if (!ALLOWED_TYPES[that._allOptions.type]) { that._allOptions.type = "line"; } }, _createHtmlElements: function _createHtmlElements() { this._seriesGroup = this._renderer.g().attr({ "class": "dxsl-series" }); this._seriesLabelGroup = this._renderer.g().attr({ "class": "dxsl-series-labels" }); }, _createSeries: function _createSeries() { this._series = new seriesModule.Series({ renderer: this._renderer, seriesGroup: this._seriesGroup, labelsGroup: this._seriesLabelGroup, argumentAxis: this._argumentAxis, valueAxis: this._valueAxis }, { widgetType: "chart", type: "line" }); }, ///#DEBUG getSeriesOptions: function getSeriesOptions() { return this._series.getOptions(); }, ///#ENDDEBUG _updateSeries: function _updateSeries() { var that = this, groupsData, seriesOptions, singleSeries = that._series; that._prepareDataSource(); seriesOptions = that._prepareSeriesOptions(); singleSeries.updateOptions(seriesOptions); groupsData = { groups: [{ series: [singleSeries] }] }; groupsData.argumentOptions = { type: seriesOptions.type === "bar" ? "discrete" : undefined }; that._simpleDataSource = dataValidatorModule.validateData(that._simpleDataSource, groupsData, that._incidentOccurred, { checkTypeForAllData: false, convertToAxisDataType: true, sortingMethod: true })[singleSeries.getArgumentField()]; singleSeries.updateData(that._simpleDataSource); singleSeries.createPoints(); that._groupsDataCategories = groupsData.categories; }, _optionChangesMap: { dataSource: "DATA_SOURCE" }, _optionChangesOrder: ["DATA_SOURCE"], _change_DATA_SOURCE: function _change_DATA_SOURCE() { this._updateDataSource(); }, _parseNumericDataSource: function _parseNumericDataSource(data, argField, valField) { var ignoreEmptyPoints = this.option("ignoreEmptyPoints"); return _map(data, function (dataItem, index) { var item = null, isDataNumber, value; if (dataItem !== undefined) { item = {}; isDataNumber = _isFinite(dataItem); item[argField] = isDataNumber ? _String(index) : dataItem[argField]; value = isDataNumber ? dataItem : dataItem[valField]; item[valField] = value === null ? ignoreEmptyPoints ? undefined : value : _Number(value); item = item[argField] !== undefined && item[valField] !== undefined ? item : null; } return item; }); }, _parseWinlossDataSource: function _parseWinlossDataSource(data, argField, valField) { var lowBarValue = -1, zeroBarValue = 0, highBarValue = 1, delta = 0.0001, target = this._allOptions.winlossThreshold; return _map(data, function (dataItem) { var item = {}; item[argField] = dataItem[argField]; if (_abs(dataItem[valField] - target) < delta) { item[valField] = zeroBarValue; } else if (dataItem[valField] > target) { item[valField] = highBarValue; } else { item[valField] = lowBarValue; } return item; }); }, _prepareDataSource: function _prepareDataSource() { var that = this, options = that._allOptions, argField = options.argumentField, valField = options.valueField, dataSource = that._dataSourceItems() || [], data = that._parseNumericDataSource(dataSource, argField, valField); if (options.type === "winloss") { that._winlossDataSource = data; that._simpleDataSource = that._parseWinlossDataSource(data, argField, valField); } else { that._simpleDataSource = data; } }, _prepareSeriesOptions: function _prepareSeriesOptions() { var that = this, options = that._allOptions, type = options.type === "winloss" ? "bar" : options.type; return { visible: true, argumentField: options.argumentField, valueField: options.valueField, color: options.lineColor, width: options.lineWidth, widgetType: "chart", type: type, opacity: type.indexOf("area") !== -1 ? that._allOptions.areaOpacity : undefined, customizePoint: that._getCustomizeFunction(), point: { size: options.pointSize, symbol: options.pointSymbol, border: { visible: true, width: DEFAULT_POINT_BORDER }, color: options.pointColor, visible: false, hoverStyle: { border: {} }, selectionStyle: { border: {} } }, border: { color: options.lineColor, width: options.lineWidth, visible: type !== "bar" } }; }, _createBarCustomizeFunction: function _createBarCustomizeFunction(pointIndexes) { var that = this, options = that._allOptions, winlossData = that._winlossDataSource; return function () { var index = this.index, isWinloss = options.type === "winloss", target = isWinloss ? options.winlossThreshold : 0, value = isWinloss ? winlossData[index][options.valueField] : this.value, positiveColor = isWinloss ? options.winColor : options.barPositiveColor, negativeColor = isWinloss ? options.lossColor : options.barNegativeColor, color; if (value >= target) { color = positiveColor; } else { color = negativeColor; } if (index === pointIndexes.first || index === pointIndexes.last) { color = options.firstLastColor; } if (index === pointIndexes.min) { color = options.minColor; } if (index === pointIndexes.max) { color = options.maxColor; } return { color: color }; }; }, _createLineCustomizeFunction: function _createLineCustomizeFunction(pointIndexes) { var that = this, options = that._allOptions; return function () { var color, index = this.index; if (index === pointIndexes.first || index === pointIndexes.last) { color = options.firstLastColor; } if (index === pointIndexes.min) { color = options.minColor; } if (index === pointIndexes.max) { color = options.maxColor; } return color ? { visible: true, border: { color: color } } : {}; }; }, _getCustomizeFunction: function _getCustomizeFunction() { var that = this, options = that._allOptions, dataSource = that._winlossDataSource || that._simpleDataSource, drawnPointIndexes = that._getExtremumPointsIndexes(dataSource), customizeFunction; if (options.type === "winloss" || options.type === "bar") { customizeFunction = that._createBarCustomizeFunction(drawnPointIndexes); } else { customizeFunction = that._createLineCustomizeFunction(drawnPointIndexes); } return customizeFunction; }, _getExtremumPointsIndexes: function _getExtremumPointsIndexes(data) { var that = this, options = that._allOptions, lastIndex = data.length - 1, indexes = {}; that._minMaxIndexes = that._findMinMax(data); if (options.showFirstLast) { indexes.first = 0; indexes.last = lastIndex; } if (options.showMinMax) { indexes.min = that._minMaxIndexes.minIndex; indexes.max = that._minMaxIndexes.maxIndex; } return indexes; }, _findMinMax: function _findMinMax(data) { var that = this, valField = that._allOptions.valueField, firstItem = data[0] || {}, firstValue = firstItem[valField] || 0, min = firstValue, max = firstValue, minIndex = 0, maxIndex = 0, dataLength = data.length, value, i; for (i = 1; i < dataLength; i++) { value = data[i][valField]; if (value < min) { min = value; minIndex = i; } if (value > max) { max = value; maxIndex = i; } } return { minIndex: minIndex, maxIndex: maxIndex }; }, _getStick: function _getStick() { return { stick: this._series.type !== "bar" }; }, _updateRange: function _updateRange() { var that = this, series = that._series, type = series.type, isBarType = type === "bar", isWinlossType = type === "winloss", DEFAULT_VALUE_RANGE_MARGIN = 0.15, DEFAULT_ARGUMENT_RANGE_MARGIN = 0.1, WINLOSS_MAX_RANGE = 1, WINLOSS_MIN_RANGE = -1, rangeData = series.getRangeData(), minValue = that._allOptions.minValue, hasMinY = _isDefined(minValue) && _isFinite(minValue), maxValue = that._allOptions.maxValue, hasMaxY = _isDefined(maxValue) && _isFinite(maxValue), valCoef, argCoef; valCoef = (rangeData.val.max - rangeData.val.min) * DEFAULT_VALUE_RANGE_MARGIN; if (isBarType || isWinlossType || type === "area") { if (rangeData.val.min !== 0) { rangeData.val.min -= valCoef; } if (rangeData.val.max !== 0) { rangeData.val.max += valCoef; } } else { rangeData.val.min -= valCoef; rangeData.val.max += valCoef; } if (hasMinY || hasMaxY) { if (hasMinY && hasMaxY) { rangeData.val.minVisible = _min(minValue, maxValue); rangeData.val.maxVisible = _max(minValue, maxValue); } else { rangeData.val.minVisible = hasMinY ? _Number(minValue) : undefined; rangeData.val.maxVisible = hasMaxY ? _Number(maxValue) : undefined; } if (isWinlossType) { rangeData.val.minVisible = hasMinY ? _max(rangeData.val.minVisible, WINLOSS_MIN_RANGE) : undefined; rangeData.val.maxVisible = hasMaxY ? _min(rangeData.val.maxVisible, WINLOSS_MAX_RANGE) : undefined; } } if (series.getPoints().length > 1) { if (isBarType) { argCoef = (rangeData.arg.max - rangeData.arg.min) * DEFAULT_ARGUMENT_RANGE_MARGIN; rangeData.arg.min = rangeData.arg.min - argCoef; rangeData.arg.max = rangeData.arg.max + argCoef; } } rangeData.arg.categories = that._groupsDataCategories; that._ranges = rangeData; }, _getBarWidth: function _getBarWidth(pointsCount) { var that = this, canvas = that._canvas, intervalWidth = pointsCount * DEFAULT_BAR_INTERVAL, rangeWidth = canvas.width - canvas.left - canvas.right - intervalWidth, width = _round(rangeWidth / pointsCount); if (width < MIN_BAR_WIDTH) { width = MIN_BAR_WIDTH; } if (width > MAX_BAR_WIDTH) { width = MAX_BAR_WIDTH; } return width; }, _correctPoints: function _correctPoints() { var that = this, seriesType = that._allOptions.type, seriesPoints = that._series.getPoints(), pointsLength = seriesPoints.length, barWidth, i; if (seriesType === "bar" || seriesType === "winloss") { barWidth = that._getBarWidth(pointsLength); for (i = 0; i < pointsLength; i++) { seriesPoints[i].correctCoordinates({ width: barWidth, offset: 0 }); } } }, _drawSeries: function _drawSeries() { var that = this; if (that._simpleDataSource.length > 0) { that._correctPoints(); that._series.draw(); that._seriesGroup.append(that._renderer.root); } }, _isTooltipEnabled: function _isTooltipEnabled() { return !!this._simpleDataSource.length; }, _getTooltipData: function _getTooltipData() { var that = this, options = that._allOptions, dataSource = that._winlossDataSource || that._simpleDataSource, tooltip = that._tooltip; if (dataSource.length === 0) { return {}; } var minMax = that._minMaxIndexes, valueField = options.valueField, first = dataSource[0][valueField], last = dataSource[dataSource.length - 1][valueField], min = dataSource[minMax.minIndex][valueField], max = dataSource[minMax.maxIndex][valueField], formattedFirst = tooltip.formatValue(first), formattedLast = tooltip.formatValue(last), formattedMin = tooltip.formatValue(min), formattedMax = tooltip.formatValue(max), customizeObject = { firstValue: formattedFirst, lastValue: formattedLast, minValue: formattedMin, maxValue: formattedMax, originalFirstValue: first, originalLastValue: last, originalMinValue: min, originalMaxValue: max, valueText: ["Start:", formattedFirst, "End:", formattedLast, "Min:", formattedMin, "Max:", formattedMax] }; if (options.type === "winloss") { customizeObject.originalThresholdValue = options.winlossThreshold; customizeObject.thresholdValue = tooltip.formatValue(options.winlossThreshold); } return customizeObject; } }); _map(["lineColor", "lineWidth", "areaOpacity", "minColor", "maxColor", "barPositiveColor", "barNegativeColor", "winColor", "lessColor", "firstLastColor", "pointSymbol", "pointColor", "pointSize", "type", "argumentField", "valueField", "winlossThreshold", "showFirstLast", "showMinMax", "ignoreEmptyPoints", "minValue", "maxValue"], function (name) { dxSparkline.prototype._optionChangesMap[name] = "OPTIONS"; }); require("../../core/component_registrator")("dxSparkline", dxSparkline); module.exports = dxSparkline; // PLUGINS_SECTION dxSparkline.addPlugin(require("../core/data_source").plugin);