UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

1,268 lines (1,060 loc) • 46.7 kB
"use strict"; var commonUtils = require("../../core/utils/common"), noop = commonUtils.noop, eventsEngine = require("../../events/core/events_engine"), typeUtils = require("../../core/utils/type"), each = require("../../core/utils/iterator").each, extend = require("../../core/utils/extend").extend, inArray = require("../../core/utils/array").inArray, eventUtils = require("../../events/utils"), BaseWidget = require("../core/base_widget"), legendModule = require("../components/legend"), dataValidatorModule = require("../components/data_validator"), seriesModule = require("../series/base_series"), chartThemeManagerModule = require("../components/chart_theme_manager"), LayoutManagerModule = require("./layout_manager"), trackerModule = require("./tracker"), headerBlockModule = require("./header_block"), REINIT_REFRESH_ACTION = "_reinit", REINIT_DATA_SOURCE_REFRESH_ACTION = "_updateDataSource", DATA_INIT_REFRESH_ACTION = "_dataInit", FORCE_RENDER_REFRESH_ACTION = "_forceRender", RESIZE_REFRESH_ACTION = "_resize", ACTIONS_BY_PRIORITY = [REINIT_REFRESH_ACTION, REINIT_DATA_SOURCE_REFRESH_ACTION, DATA_INIT_REFRESH_ACTION, FORCE_RENDER_REFRESH_ACTION, RESIZE_REFRESH_ACTION], vizUtils = require("../core/utils"), _map = vizUtils.map, _each = each, _extend = extend, _isArray = Array.isArray, _isDefined = typeUtils.isDefined, _setCanvasValues = vizUtils.setCanvasValues, DEFAULT_OPACITY = 0.3, REFRESH_SERIES_DATA_INIT_ACTION_OPTIONS = ["series", "commonSeriesSettings", "dataPrepareSettings", "seriesSelectionMode", "pointSelectionMode", "synchronizeMultiAxes", "resolveLabelsOverlapping"], REFRESH_SERIES_FAMILIES_ACTION_OPTIONS = ["equalBarWidth", "minBubbleSize", "maxBubbleSize", "barWidth", "barGroupPadding", "barGroupWidth", "negativesAsZeroes", "negativesAsZeros" // misspelling case ], FORCE_RENDER_REFRESH_ACTION_OPTIONS = ["adaptiveLayout", "crosshair", "resolveLabelOverlapping", "adjustOnZoom", "zoomingMode", "scrollingMode"]; function checkHeightRollingStock(rollingStocks, stubCanvas) { var canvasSize = stubCanvas.end - stubCanvas.start, size = 0; rollingStocks.forEach(function (rollingStock) { size += rollingStock.getBoundingRect().width; }); while (canvasSize < size) { size -= findAndKillSmallValue(rollingStocks); } } function findAndKillSmallValue(rollingStocks) { var smallestObject, width; smallestObject = rollingStocks.reduce(function (prev, rollingStock, index) { if (!rollingStock) return prev; var value = rollingStock.getLabels()[0].getData().value; return value < prev.value ? { value: value, rollingStock: rollingStock, index: index } : prev; }, { rollingStock: undefined, value: Infinity, index: undefined }); smallestObject.rollingStock.getLabels()[0].draw(false); width = smallestObject.rollingStock.getBoundingRect().width; rollingStocks[smallestObject.index] = null; return width; } function checkStackOverlap(rollingStocks) { var i, j, iLength, jLength, overlap = false; for (i = 0, iLength = rollingStocks.length - 1; i < iLength; i++) { for (j = i + 1, jLength = rollingStocks.length; j < jLength; j++) { if (i !== j && checkStacksOverlapping(rollingStocks[i], rollingStocks[j], true)) { overlap = true; break; } } if (overlap) break; } return overlap; } function resolveLabelOverlappingInOneDirection(points, canvas, isRotated, shiftFunction) { var rollingStocks = [], stubCanvas = { start: isRotated ? canvas.left : canvas.top, end: isRotated ? canvas.width - canvas.right : canvas.height - canvas.bottom }, hasStackedSeries = false; points.forEach(function (p) { if (!p) return; hasStackedSeries = hasStackedSeries || p.series.isStackedSeries() || p.series.isFullStackedSeries(); p.getLabels().forEach(function (l) { l.isVisible() && rollingStocks.push(new RollingStock(l, isRotated, shiftFunction)); }); }); if (hasStackedSeries) { !isRotated && rollingStocks.reverse(); } else { var rollingStocksTmp = rollingStocks.slice(); rollingStocks.sort(function (a, b) { return a.getInitialPosition() - b.getInitialPosition() || rollingStocksTmp.indexOf(a) - rollingStocksTmp.indexOf(b); }); } if (!checkStackOverlap(rollingStocks)) return; checkHeightRollingStock(rollingStocks, stubCanvas); prepareOverlapStacks(rollingStocks); rollingStocks.reverse(); moveRollingStock(rollingStocks, stubCanvas); } function checkStacksOverlapping(firstRolling, secondRolling, inTwoSides) { if (!firstRolling || !secondRolling) return; var firstRect = firstRolling.getBoundingRect(), secondRect = secondRolling.getBoundingRect(), oppositeOverlapping = inTwoSides ? firstRect.oppositeStart <= secondRect.oppositeStart && firstRect.oppositeEnd > secondRect.oppositeStart || secondRect.oppositeStart <= firstRect.oppositeStart && secondRect.oppositeEnd > firstRect.oppositeStart : true; return firstRect.end > secondRect.start && oppositeOverlapping; } function prepareOverlapStacks(rollingStocks) { var i, currentRollingStock, root; for (i = 0; i < rollingStocks.length - 1; i++) { currentRollingStock = root || rollingStocks[i]; if (checkStacksOverlapping(currentRollingStock, rollingStocks[i + 1])) { currentRollingStock.toChain(rollingStocks[i + 1]); rollingStocks[i + 1] = null; root = currentRollingStock; } else { root = rollingStocks[i + 1] || currentRollingStock; } } } function moveRollingStock(rollingStocks, canvas) { var i, j, currentRollingStock, nextRollingStock, currentBBox, nextBBox; for (i = 0; i < rollingStocks.length; i++) { currentRollingStock = rollingStocks[i]; if (rollingStocksIsOut(currentRollingStock, canvas)) { currentBBox = currentRollingStock.getBoundingRect(); for (j = i + 1; j < rollingStocks.length; j++) { nextRollingStock = rollingStocks[j]; if (!nextRollingStock) { continue; } nextBBox = nextRollingStock.getBoundingRect(); if (nextBBox.end > currentBBox.start - (currentBBox.end - canvas.end)) { nextRollingStock.toChain(currentRollingStock); rollingStocks[i] = currentRollingStock = null; break; } } } currentRollingStock && currentRollingStock.setRollingStockInCanvas(canvas); } } function rollingStocksIsOut(rollingStock, canvas) { return rollingStock && rollingStock.getBoundingRect().end > canvas.end; } function RollingStock(label, isRotated, shiftFunction) { var bBox = label.getBoundingRect(), x = bBox.x, y = bBox.y, endX = bBox.x + bBox.width, endY = bBox.y + bBox.height; this.labels = [label]; this.shiftFunction = shiftFunction; this._bBox = { start: isRotated ? x : y, width: isRotated ? bBox.width : bBox.height, end: isRotated ? endX : endY, oppositeStart: isRotated ? y : x, oppositeEnd: isRotated ? endY : endX }; this._initialPosition = isRotated ? bBox.x : bBox.y; return this; } RollingStock.prototype = { toChain: function toChain(nextRollingStock) { var nextRollingStockBBox = nextRollingStock.getBoundingRect(); nextRollingStock.shift(nextRollingStockBBox.start - this._bBox.end); this._changeBoxWidth(nextRollingStockBBox.width); this.labels = this.labels.concat(nextRollingStock.labels); }, getBoundingRect: function getBoundingRect() { return this._bBox; }, shift: function shift(shiftLength) { var shiftFunction = this.shiftFunction; _each(this.labels, function (index, label) { var bBox = label.getBoundingRect(), coords = shiftFunction(bBox, shiftLength); if (!label.hideInsideLabel(coords)) { label.shift(coords.x, coords.y); } }); this._bBox.end -= shiftLength; this._bBox.start -= shiftLength; }, setRollingStockInCanvas: function setRollingStockInCanvas(canvas) { if (this._bBox.end > canvas.end) { this.shift(this._bBox.end - canvas.end); } }, getLabels: function getLabels() { return this.labels; }, getInitialPosition: function getInitialPosition() { return this._initialPosition; }, _changeBoxWidth: function _changeBoxWidth(width) { this._bBox.end += width; this._bBox.width += width; } }; function getLegendFields(name) { return { nameField: name + "Name", colorField: name + "Color", indexField: name + "Index" }; } function getLegendSettings(legendDataField) { var formatObjectFields = getLegendFields(legendDataField); return { getFormatObject: function getFormatObject(data) { var res = {}; res[formatObjectFields.indexField] = data.id; res[formatObjectFields.colorField] = data.states.normal.fill; res[formatObjectFields.nameField] = data.text; return res; }, textField: formatObjectFields.nameField }; } function checkOverlapping(firstRect, secondRect) { return (firstRect.x <= secondRect.x && secondRect.x <= firstRect.x + firstRect.width || firstRect.x >= secondRect.x && firstRect.x <= secondRect.x + secondRect.width) && (firstRect.y <= secondRect.y && secondRect.y <= firstRect.y + firstRect.height || firstRect.y >= secondRect.y && firstRect.y <= secondRect.y + secondRect.height); } var overlapping = { resolveLabelOverlappingInOneDirection: resolveLabelOverlappingInOneDirection }; function suppressCommonLayout(layout) { layout.forward = function (rect) { return rect; }; layout.backward = noop; } var BaseChart = BaseWidget.inherit({ _eventsMap: { onSeriesClick: { name: "seriesClick" }, onPointClick: { name: "pointClick" }, onArgumentAxisClick: { name: "argumentAxisClick" }, onLegendClick: { name: "legendClick" }, onSeriesSelectionChanged: { name: "seriesSelectionChanged" }, onPointSelectionChanged: { name: "pointSelectionChanged" }, onSeriesHoverChanged: { name: "seriesHoverChanged" }, onPointHoverChanged: { name: "pointHoverChanged" }, onDone: { name: "done" }, onZoomStart: { name: "zoomStart" }, onZoomEnd: { name: "zoomEnd" } }, _rootClassPrefix: "dxc", _rootClass: "dxc-chart", _initialChanges: ["REINIT"], _themeDependentChanges: ["REFRESH_SERIES_REINIT"], _createThemeManager: function _createThemeManager() { var option = this.option(), themeManager = new chartThemeManagerModule.ThemeManager(option, this._chartType); themeManager.setTheme(option.theme, option.rtlEnabled); return themeManager; }, _initCore: function _initCore() { var that = this; suppressCommonLayout(that._layout); that._canvasClipRect = that._renderer.clipRect(); that._createHtmlStructure(); that._headerBlock = new headerBlockModule.HeaderBlock(); that._createLegend(); that._createTracker(); that._needHandleRenderComplete = true; that.layoutManager = new LayoutManagerModule.LayoutManager(); that._createScrollBar(); eventsEngine.on(that._$element, "contextmenu", function (event) { ///#DEBUG that.eventType = "contextmenu"; ///#ENDDEBUG if (eventUtils.isTouchEvent(event) || eventUtils.isPointerEvent(event)) { event.preventDefault(); } }); eventsEngine.on(that._$element, "MSHoldVisual", function (event) { ///#DEBUG that.eventType = "MSHoldVisual"; ///#ENDDEBUG event.preventDefault(); }); }, // Common functionality is overridden because Chart has its own layout logic. Nevertheless common logic should be used. _getLayoutItems: noop, _layoutManagerOptions: function _layoutManagerOptions() { return this._themeManager.getOptions("adaptiveLayout"); }, _reinit: function _reinit() { var that = this; // _skipRender = !that._initialized; _setCanvasValues(that._canvas); that._reinitAxes(); // NOTE: T273635 // Changing the `_initialized` flag prevents `_render` which is synchronously called from the `_updateDataSource` when data source is local and series rendering is synchronous // This is possible because `_render` checks the `_initialized` flag // if (!_skipRender) { that._skipRender = true; // T273635, T351032 // } that._updateDataSource(); if (!that.series) { that._dataSpecificInit(false); } // if (!_skipRender) { that._skipRender = false; // T273635, T351032 // } that._correctAxes(); /* _skipRender || */that._forceRender(); }, _correctAxes: noop, _createHtmlStructure: function _createHtmlStructure() { var that = this, renderer = that._renderer, root = renderer.root; that._backgroundRect = renderer.rect().attr({ fill: "gray", opacity: 0.0001 }).append(root); that._panesBackgroundGroup = renderer.g().attr({ "class": "dxc-background" }).append(root); that._stripsGroup = renderer.g().attr({ "class": "dxc-strips-group" }).linkOn(root, "strips"); // TODO: Must be created in the same place where used (advanced chart) that._gridGroup = renderer.g().attr({ "class": "dxc-grids-group" }).linkOn(root, "grids"); // TODO: Must be created in the same place where used (advanced chart) that._axesGroup = renderer.g().attr({ "class": "dxc-axes-group" }).linkOn(root, "axes"); // TODO: Must be created in the same place where used (advanced chart) that._labelAxesGroup = renderer.g().attr({ "class": "dxc-strips-labels-group" }).linkOn(root, "strips-labels"); // TODO: Must be created in the same place where used (advanced chart) that._panesBorderGroup = renderer.g().attr({ "class": "dxc-border" }).linkOn(root, "border"); // TODO: Must be created in the same place where used (chart) that._seriesGroup = renderer.g().attr({ "class": "dxc-series-group" }).linkOn(root, "series"); that._constantLinesGroup = renderer.g().attr({ "class": "dxc-constant-lines-group" }).linkOn(root, "constant-lines"); // TODO: Must be created in the same place where used (advanced chart) that._scaleBreaksGroup = renderer.g().attr({ "class": "dxc-scale-breaks" }).linkOn(root, "scale-breaks"); that._labelsGroup = renderer.g().attr({ "class": "dxc-labels-group" }).linkOn(root, "labels"); that._crosshairCursorGroup = renderer.g().attr({ "class": "dxc-crosshair-cursor" }).linkOn(root, "crosshair"); that._legendGroup = renderer.g().attr({ "class": "dxc-legend", "clip-path": that._getCanvasClipRectID() }).linkOn(root, "legend"); that._scrollBarGroup = renderer.g().attr({ "class": "dxc-scroll-bar" }).linkOn(root, "scroll-bar"); }, _disposeObjectsInArray: function _disposeObjectsInArray(propName, fieldNames) { _each(this[propName] || [], function (_, item) { if (fieldNames && item) { _each(fieldNames, function (_, field) { item[field] && item[field].dispose(); }); } else { item && item.dispose(); } }); this[propName] = null; }, _disposeCore: function _disposeCore() { var that = this, disposeObject = function disposeObject(propName) { // TODO: What is the purpose of the `if` check in a private function? if (that[propName]) { that[propName].dispose(); that[propName] = null; } }, unlinkGroup = function unlinkGroup(name) { that[name].linkOff(); }, disposeObjectsInArray = this._disposeObjectsInArray; that._renderer.stopAllAnimations(); that.businessRanges = null; disposeObjectsInArray.call(that, "series"); disposeObject("_headerBlock"); disposeObject("_tracker"); disposeObject("_crosshair"); that.layoutManager = that._userOptions = that._canvas = that._groupsData = null; unlinkGroup("_stripsGroup"); unlinkGroup("_gridGroup"); unlinkGroup("_axesGroup"); unlinkGroup("_constantLinesGroup"); unlinkGroup("_labelAxesGroup"); unlinkGroup("_panesBorderGroup"); unlinkGroup("_seriesGroup"); unlinkGroup("_labelsGroup"); unlinkGroup("_crosshairCursorGroup"); unlinkGroup("_legendGroup"); unlinkGroup("_scrollBarGroup"); unlinkGroup("_scaleBreaksGroup"); disposeObject("_canvasClipRect"); disposeObject("_panesBackgroundGroup"); disposeObject("_backgroundRect"); disposeObject("_stripsGroup"); disposeObject("_gridGroup"); disposeObject("_axesGroup"); disposeObject("_constantLinesGroup"); disposeObject("_labelAxesGroup"); disposeObject("_panesBorderGroup"); disposeObject("_seriesGroup"); disposeObject("_labelsGroup"); disposeObject("_crosshairCursorGroup"); disposeObject("_legendGroup"); disposeObject("_scrollBarGroup"); disposeObject("_scaleBreaksGroup"); }, _getAnimationOptions: function _getAnimationOptions() { return this._themeManager.getOptions("animation"); }, _getDefaultSize: function _getDefaultSize() { return { width: 400, height: 400 }; }, // TODO: Theme manager should stop knowing about user options then this method can be removed _getOption: function _getOption(name) { return this._themeManager.getOptions(name); }, _applySize: function _applySize() { // if (this._initialized) { // this._resize(); // } this._processRefreshData(RESIZE_REFRESH_ACTION); }, // _resize: function () { // if (this._updateLockCount) {// T244164 // this._processRefreshData(RESIZE_REFRESH_ACTION); // } else { // this._render(this.__renderOptions || { animate: false, isResize: true }); // } // }, _resize: function _resize() { this._doRender(this.__renderOptions || { animate: false, isResize: true }); }, _trackerType: "ChartTracker", _createTracker: function _createTracker() { var that = this; that._tracker = new trackerModule[that._trackerType]({ seriesGroup: that._seriesGroup, renderer: that._renderer, tooltip: that._tooltip, legend: that._legend, eventTrigger: that._eventTrigger }); }, _getTrackerSettings: function _getTrackerSettings() { return this._getSelectionModes(); }, _getSelectionModes: function _getSelectionModes() { var themeManager = this._themeManager; return { seriesSelectionMode: themeManager.getOptions("seriesSelectionMode"), pointSelectionMode: themeManager.getOptions("pointSelectionMode") }; }, _updateTracker: function _updateTracker(trackerCanvases) { var that = this; that._tracker.update(that._getTrackerSettings()); that._tracker.setCanvases({ left: 0, right: that._canvas.width, top: 0, bottom: that._canvas.height }, trackerCanvases); }, _doRender: function _doRender(_options) { var that = this, drawOptions, recreateCanvas; if ( /* !that._initialized || */that._skipRender) return; // NOTE: Because _render can be called from _init! if (that._canvas.width === 0 && that._canvas.height === 0) return; that._resetIsReady(); // T207606 drawOptions = that._prepareDrawOptions(_options); recreateCanvas = drawOptions.recreateCanvas; // T207665 that.__originalCanvas = that._canvas; that._canvas = extend({}, that._canvas); // NOTE: Instance of the original canvas must be preserved // T207665 if (recreateCanvas) { that.__currentCanvas = that._canvas; } else { that._canvas = that.__currentCanvas; } ///#DEBUG that.DEBUG_canvas = that._canvas; ///#ENDDEBUG recreateCanvas && that._updateCanvasClipRect(that._canvas); that._renderer.stopAllAnimations(true); _setCanvasValues(that._canvas); that._cleanGroups(); that._renderElements(drawOptions); }, _renderElements: function _renderElements(drawOptions) { var that = this, preparedOptions = that._prepareToRender(drawOptions), isRotated = that._isRotated(), isLegendInside = that._isLegendInside(), trackerCanvases = [], layoutTargets = that._getLayoutTargets(), dirtyCanvas = extend({}, that._canvas), argBusinessRange, zoomMinArg, drawElements = [], layoutCanvas = drawOptions.drawTitle && drawOptions.drawLegend && drawOptions.adjustAxes, zoomMaxArg; ///#DEBUG that.DEBUG_dirtyCanvas = dirtyCanvas; ///#ENDDEBUG if (layoutCanvas) { drawElements = that._getDrawElements(drawOptions, isLegendInside); } that._renderer.lock(); that.layoutManager.setOptions(that._layoutManagerOptions()); that.layoutManager.layoutElements(drawElements, that._canvas, function (sizeShortage) { var panesCanvases = that._renderAxes(drawOptions, preparedOptions, isRotated); that._shrinkAxes(drawOptions, sizeShortage, panesCanvases); }, layoutTargets, isRotated); layoutCanvas && that._updateCanvasClipRect(dirtyCanvas); that._applyClipRects(preparedOptions); that._appendSeriesGroups(); that._createCrosshairCursor(); _each(layoutTargets, function () { var canvas = this.canvas; trackerCanvases.push({ left: canvas.left, right: canvas.width - canvas.right, top: canvas.top, bottom: canvas.height - canvas.bottom }); }); if (that._scrollBar) { argBusinessRange = that._argumentAxes[0].getTranslator().getBusinessRange(); if (argBusinessRange.axisType === "discrete" && argBusinessRange.categories && argBusinessRange.categories.length <= 1) { zoomMinArg = zoomMaxArg = undefined; } else { zoomMinArg = argBusinessRange.minVisible; zoomMaxArg = argBusinessRange.maxVisible; } that._scrollBar.init(argBusinessRange, !that._argumentAxes[0].getOptions().valueMarginsEnabled).setPosition(zoomMinArg, zoomMaxArg); } that._updateTracker(trackerCanvases); that._updateLegendPosition(drawOptions, isLegendInside); that._renderSeries(drawOptions, isRotated, isLegendInside); that._renderer.unlock(); }, _createCrosshairCursor: noop, _appendSeriesGroups: function _appendSeriesGroups() { this._seriesGroup.linkAppend(); this._labelsGroup.linkAppend(); this._appendAdditionalSeriesGroups(); }, _renderSeries: function _renderSeries(drawOptions, isRotated, isLegendInside) { this._calculateSeriesLayout(drawOptions, isRotated); this._renderSeriesElements(drawOptions, isRotated, isLegendInside); }, _calculateSeriesLayout: function _calculateSeriesLayout(drawOptions, isRotated) { drawOptions.hideLayoutLabels = this.layoutManager.needMoreSpaceForPanesCanvas(this._getLayoutTargets(), isRotated) && !this._themeManager.getOptions("adaptiveLayout").keepLabels; this._updateSeriesDimensions(drawOptions); }, _renderSeriesElements: function _renderSeriesElements(drawOptions, isRotated, isLegendInside) { var that = this, i, series = that.series, singleSeries, seriesLength = series.length, resolveLabelOverlapping = that._themeManager.getOptions("resolveLabelOverlapping"); for (i = 0; i < seriesLength; i++) { singleSeries = series[i]; that._applyExtraSettings(singleSeries, drawOptions); singleSeries.draw(drawOptions.animate && singleSeries.getPoints().length <= drawOptions.animationPointsLimit && that._renderer.animationEnabled(), drawOptions.hideLayoutLabels, that._getLegendCallBack(singleSeries)); } that._adjustSeriesLabels(resolveLabelOverlapping === "shift"); if (resolveLabelOverlapping !== "none") { that._resolveLabelOverlapping(resolveLabelOverlapping); } that._renderTrackers(isLegendInside); that._tracker.repairTooltip(); that._clearCanvas(); that._drawn(); that._renderCompleteHandler(); }, _clearCanvas: function _clearCanvas() { // T207665, T336349, T503616 this._canvas = this.__originalCanvas; }, _resolveLabelOverlapping: function _resolveLabelOverlapping(resolveLabelOverlapping) { var func; switch (resolveLabelOverlapping) { case "stack": func = this._resolveLabelOverlappingStack; break; case "hide": func = this._resolveLabelOverlappingHide; break; case "shift": func = this._resolveLabelOverlappingShift; break; } typeUtils.isFunction(func) && func.call(this); }, _getVisibleSeries: function _getVisibleSeries() { return commonUtils.grep(this.getAllSeries(), function (series) { return series.isVisible(); }); }, _resolveLabelOverlappingHide: function _resolveLabelOverlappingHide() { var labels = [], currentLabel, nextLabel, currentLabelRect, nextLabelRect, i, j, points, series = this._getVisibleSeries(); for (i = 0; i < series.length; i++) { points = series[i].getVisiblePoints(); for (j = 0; j < points.length; j++) { labels.push.apply(labels, points[j].getLabels()); } } for (i = 0; i < labels.length; i++) { currentLabel = labels[i]; if (!currentLabel.isVisible()) { continue; } currentLabelRect = currentLabel.getBoundingRect(); for (j = i + 1; j < labels.length; j++) { nextLabel = labels[j]; nextLabelRect = nextLabel.getBoundingRect(); if (checkOverlapping(currentLabelRect, nextLabelRect)) { nextLabel.draw(false); } } } }, _cleanGroups: function _cleanGroups() { var that = this; that._stripsGroup.linkRemove().clear(); // TODO: Must be removed in the same place where appended (advanced chart) that._gridGroup.linkRemove().clear(); // TODO: Must be removed in the same place where appended (advanced chart) that._axesGroup.linkRemove().clear(); // TODO: Must be removed in the same place where appended (advanced chart) that._constantLinesGroup.linkRemove().clear(); // TODO: Must be removed in the same place where appended (advanced chart) that._labelAxesGroup.linkRemove().clear(); // TODO: Must be removed in the same place where appended (advanced chart) // that._seriesGroup.linkRemove().clear(); that._labelsGroup.linkRemove().clear(); that._crosshairCursorGroup.linkRemove().clear(); that._scaleBreaksGroup.linkRemove().clear(); }, _createLegend: function _createLegend() { var that = this, legendSettings = getLegendSettings(that._legendDataField); that._legend = new legendModule.Legend({ renderer: that._renderer, group: that._legendGroup, backgroundClass: "dxc-border", itemGroupClass: "dxc-item", textField: legendSettings.textField, getFormatObject: legendSettings.getFormatObject }); }, _updateLegend: function _updateLegend() { var that = this, themeManager = that._themeManager, legendOptions = themeManager.getOptions("legend"), legendData = that._getLegendData(); legendOptions.containerBackgroundColor = themeManager.getOptions("containerBackgroundColor"); legendOptions._incidentOccurred = that._incidentOccurred; // TODO: Why is `_` used? that._legend.update(legendData, legendOptions); }, _prepareDrawOptions: function _prepareDrawOptions(drawOptions) { var animationOptions = this._getAnimationOptions(), options; options = extend({}, { force: false, adjustAxes: true, drawLegend: true, drawTitle: true, animate: animationOptions.enabled, animationPointsLimit: animationOptions.maxPointCountSupported }, drawOptions, this.__renderOptions); // NOTE: This is to support `render` method options if (!_isDefined(options.recreateCanvas)) { options.recreateCanvas = options.adjustAxes && options.drawLegend && options.drawTitle; } return options; }, _processRefreshData: function _processRefreshData(newRefreshAction) { var currentRefreshActionPosition = inArray(this._currentRefreshData, ACTIONS_BY_PRIORITY), newRefreshActionPosition = inArray(newRefreshAction, ACTIONS_BY_PRIORITY); if (!this._currentRefreshData || currentRefreshActionPosition >= 0 && newRefreshActionPosition < currentRefreshActionPosition) { this._currentRefreshData = newRefreshAction; // this._invalidate(); } }, _getLegendData: function _getLegendData() { return _map(this._getLegendTargets(), function (item) { var legendData = item.legendData, style = item.getLegendStyles, opacity = style.normal.opacity; if (!item.visible) { if (!_isDefined(opacity) || opacity > DEFAULT_OPACITY) { opacity = DEFAULT_OPACITY; } legendData.textOpacity = DEFAULT_OPACITY; } legendData.states = { hover: style.hover, selection: style.selection, normal: _extend({}, style.normal, { opacity: opacity }) }; return legendData; }); }, _getLegendOptions: function _getLegendOptions(item) { return { legendData: { text: item[this._legendItemTextField], argument: item.argument, id: item.index, argumentIndex: item.argumentIndex }, getLegendStyles: item.getLegendStyles(), visible: item.isVisible() }; }, _disposeSeries: function _disposeSeries() { var that = this; _each(that.series || [], function (_, series) { series.dispose(); }); that.series = null; _each(that.seriesFamilies || [], function (_, family) { family.dispose(); }); that.seriesFamilies = null; that._needHandleRenderComplete = true; }, _optionChanged: function _optionChanged(arg) { this._themeManager.resetOptions(arg.name); this.callBase.apply(this, arguments); }, _applyChanges: function _applyChanges() { var that = this; that._themeManager.update(that._options); that.callBase.apply(that, arguments); that._doRefresh(); }, _optionChangesMap: { animation: "ANIMATION", dataSource: "DATA_SOURCE", palette: "PALETTE", paletteExtensionMode: "PALETTE", legend: "DATA_INIT", seriesTemplate: "DATA_INIT", "export": "FORCE_RENDER", valueAxis: "AXES_AND_PANES", argumentAxis: "AXES_AND_PANES", commonAxisSettings: "AXES_AND_PANES", panes: "AXES_AND_PANES", defaultPane: "AXES_AND_PANES", useAggregation: "AXES_AND_PANES", containerBackgroundColor: "AXES_AND_PANES", rotated: "ROTATED", customizePoint: "REFRESH_SERIES_REINIT", customizeLabel: "REFRESH_SERIES_REINIT", scrollBar: "SCROLL_BAR" }, _customChangesOrder: ["ANIMATION", "REFRESH_SERIES_FAMILIES", "DATA_SOURCE", "PALETTE", "REFRESH_SERIES_DATA_INIT", "DATA_INIT", "FORCE_RENDER", "AXES_AND_PANES", "ROTATED", "REFRESH_SERIES_REINIT", "SCROLL_BAR", "CHART_TOOLTIP", "REINIT"], _change_ANIMATION: function _change_ANIMATION() { this._renderer.updateAnimationOptions(this._getAnimationOptions()); }, _change_DATA_SOURCE: function _change_DATA_SOURCE() { this._needHandleRenderComplete = true; this._processRefreshData(REINIT_DATA_SOURCE_REFRESH_ACTION); }, _change_PALETTE: function _change_PALETTE() { this._themeManager.updatePalette(); this._refreshSeries(DATA_INIT_REFRESH_ACTION); }, _change_REFRESH_SERIES_DATA_INIT: function _change_REFRESH_SERIES_DATA_INIT() { this._refreshSeries(DATA_INIT_REFRESH_ACTION); }, _change_DATA_INIT: function _change_DATA_INIT() { this._processRefreshData(DATA_INIT_REFRESH_ACTION); }, _change_REFRESH_SERIES_FAMILIES: function _change_REFRESH_SERIES_FAMILIES() { this._processSeriesFamilies(); this._populateBusinessRange(true); this._processRefreshData(FORCE_RENDER_REFRESH_ACTION); }, _change_FORCE_RENDER: function _change_FORCE_RENDER() { this._processRefreshData(FORCE_RENDER_REFRESH_ACTION); }, _change_AXES_AND_PANES: function _change_AXES_AND_PANES() { this._refreshSeries(REINIT_REFRESH_ACTION); }, _change_ROTATED: function _change_ROTATED() { this._createScrollBar(); this._refreshSeries(REINIT_REFRESH_ACTION); }, _change_REFRESH_SERIES_REINIT: function _change_REFRESH_SERIES_REINIT() { this._refreshSeries(REINIT_REFRESH_ACTION); }, _change_SCROLL_BAR: function _change_SCROLL_BAR() { this._createScrollBar(); this._processRefreshData(FORCE_RENDER_REFRESH_ACTION); }, _change_CHART_TOOLTIP: function _change_CHART_TOOLTIP() { this._organizeStackPoints(); }, _change_REINIT: function _change_REINIT() { this._processRefreshData(REINIT_REFRESH_ACTION); }, _refreshSeries: function _refreshSeries(actionName) { this._disposeSeries(); this._processRefreshData(actionName); }, _doRefresh: function _doRefresh() { var methodName = this._currentRefreshData; if (methodName) { this._currentRefreshData = null; this._renderer.stopAllAnimations(true); this[methodName](); } }, _updateCanvasClipRect: function _updateCanvasClipRect(canvas) { var that = this, width, height; width = Math.max(canvas.width - canvas.left - canvas.right, 0); height = Math.max(canvas.height - canvas.top - canvas.bottom, 0); that._canvasClipRect.attr({ x: canvas.left, y: canvas.top, width: width, height: height }); that._backgroundRect.attr({ x: canvas.left, y: canvas.top, width: width, height: height }); }, _getCanvasClipRectID: function _getCanvasClipRectID() { return this._canvasClipRect.id; }, _dataSourceChangedHandler: function _dataSourceChangedHandler() { this._resetZoom(); this._dataInit(); }, _dataInit: function _dataInit() { this._dataSpecificInit(true); }, _processSingleSeries: function _processSingleSeries(singleSeries) { singleSeries.createPoints(false); }, _handleSeriesDataUpdated: function _handleSeriesDataUpdated() { var _this = this; if (this._getVisibleSeries().some(function (s) { return s.useAggregation(); })) { this._populateMarginOptions(); } this.series.forEach(function (s) { return _this._processSingleSeries(s); }, this); }, _dataSpecificInit: function _dataSpecificInit(needRedraw) { var that = this; that.series = that.series || that._populateSeries(); that._repopulateSeries(); that._seriesPopulatedHandlerCore(); that._populateBusinessRange(true); that._tracker.updateSeries(that.series); that._updateLegend(); needRedraw && that._forceRender(); }, _forceRender: function _forceRender() { this._doRender({ force: true }); }, _repopulateSeries: function _repopulateSeries() { var that = this, parsedData, themeManager = that._themeManager, data = that._dataSourceItems(), dataValidatorOptions = themeManager.getOptions("dataPrepareSettings"), seriesTemplate = themeManager.getOptions("seriesTemplate"); if (seriesTemplate) { that._populateSeries(data); } that._groupSeries(); parsedData = dataValidatorModule.validateData(data, that._groupsData, that._incidentOccurred, dataValidatorOptions); themeManager.resetPalette(); that.series.forEach(function (singleSeries) { singleSeries.updateData(parsedData[singleSeries.getArgumentField()]); }); that._handleSeriesDataUpdated(); that._organizeStackPoints(); }, _organizeStackPoints: function _organizeStackPoints() { var that = this, themeManager = that._themeManager, sharedTooltip = themeManager.getOptions("tooltip").shared, stackPoints = {}; _each(that.series || [], function (_, singleSeries) { that._resetStackPoints(singleSeries); sharedTooltip && that._prepareStackPoints(singleSeries, stackPoints); }); }, _renderCompleteHandler: function _renderCompleteHandler() { var that = this, allSeriesInited = true; if (that._needHandleRenderComplete) { _each(that.series, function (_, s) { allSeriesInited = allSeriesInited && s.canRenderCompleteHandle(); }); if (allSeriesInited) { that._needHandleRenderComplete = false; that._eventTrigger("done", { target: that }); } } }, _getDrawElements: function _getDrawElements(drawOptions, legendHasInsidePosition) { var that = this, drawElements = [], exportOptions = that._themeManager.getOptions("export"), titleOptions = that._title.getLayoutOptions() || {}, legendOptions, headerElements = []; if (that._exportMenu && exportOptions.enabled) { headerElements.push(that._exportMenu); drawElements.push(that._headerBlock); } if (drawOptions.drawTitle) { titleOptions.verticalAlignment !== "bottom" && headerElements.length ? headerElements.push(that._title) : drawElements.push(that._title); } if (drawOptions.drawLegend && that._legend) { that._legendGroup.linkAppend(); if (!legendHasInsidePosition) { legendOptions = that._legend.getLayoutOptions(); if (headerElements.length === 1 && legendOptions.verticalAlignment !== "bottom" && legendOptions.cutSide === "vertical") { headerElements.push(that._legend); } else { drawElements.push(that._legend); } } } if (headerElements.length) { that._headerBlock.update(headerElements, that._canvas); } return drawElements; }, _resetZoom: noop, _dataIsReady: function _dataIsReady() { // In order to support scenario when chart is created without "dataSource" and it is considered // as data is being loaded the check for state of "dataSource" option is added return _isDefined(this.option("dataSource")) && this._dataIsLoaded(); }, _populateSeries: function _populateSeries(data) { var that = this, themeManager = that._themeManager, seriesTemplate = themeManager.getOptions("seriesTemplate"), seriesOptions = seriesTemplate ? vizUtils.processSeriesTemplate(seriesTemplate, data || []) : that.option("series"), allSeriesOptions = _isArray(seriesOptions) ? seriesOptions : seriesOptions ? [seriesOptions] : [], extraOptions = that._getExtraOptions(), particularSeriesOptions, particularSeries, seriesTheme, i, seriesVisibilityChanged = function seriesVisibilityChanged() { that._specialProcessSeries(); that._populateBusinessRange(false); that._renderer.stopAllAnimations(true); that._updateLegend(); that._doRender({ force: true }); }, eventPipe; that._disposeSeries(); that.series = []; themeManager.resetPalette(); eventPipe = function eventPipe(data) { that.series.forEach(function (currentSeries) { currentSeries.notify(data); }); }; for (i = 0; i < allSeriesOptions.length; i++) { particularSeriesOptions = _extend(true, {}, allSeriesOptions[i], extraOptions); if (!particularSeriesOptions.name) { particularSeriesOptions.name = "Series " + (i + 1).toString(); } particularSeriesOptions.rotated = that._isRotated(); particularSeriesOptions.customizePoint = themeManager.getOptions("customizePoint"); particularSeriesOptions.customizeLabel = themeManager.getOptions("customizeLabel"); particularSeriesOptions.visibilityChanged = seriesVisibilityChanged; particularSeriesOptions.incidentOccurred = that._incidentOccurred; seriesTheme = themeManager.getOptions("series", particularSeriesOptions, allSeriesOptions.length); if (!that._checkPaneName(seriesTheme)) { continue; } particularSeries = new seriesModule.Series({ renderer: that._renderer, seriesGroup: that._seriesGroup, labelsGroup: that._labelsGroup, eventTrigger: that._eventTrigger, commonSeriesModes: that._getSelectionModes(), eventPipe: eventPipe, argumentAxis: that._getArgumentAxis(), valueAxis: that._getValueAxis(seriesTheme.pane, seriesTheme.axis) }, seriesTheme); if (!particularSeries.isUpdated) { that._incidentOccurred("E2101", [seriesTheme.type]); } else { particularSeries.index = that.series.length; that.series.push(particularSeries); } } return that.series; }, // API getAllSeries: function getAllSeries() { return this.series.slice(); }, getSeriesByName: function getSeriesByName(name) { var found = null; _each(this.series, function (i, singleSeries) { if (singleSeries.name === name) { found = singleSeries; return false; } }); return found; }, getSeriesByPos: function getSeriesByPos(pos) { return this.series[pos]; }, clearSelection: function clearSelection() { this._tracker.clearSelection(); }, hideTooltip: function hideTooltip() { this._tracker._hideTooltip(); }, render: function render(renderOptions) { var that = this; that.__renderOptions = renderOptions; that.__forceRender = renderOptions && renderOptions.force; that.callBase.apply(that, arguments); that.__renderOptions = that.__forceRender = null; return that; } }); REFRESH_SERIES_DATA_INIT_ACTION_OPTIONS.forEach(function (name) { BaseChart.prototype._optionChangesMap[name] = "REFRESH_SERIES_DATA_INIT"; }); FORCE_RENDER_REFRESH_ACTION_OPTIONS.forEach(function (name) { BaseChart.prototype._optionChangesMap[name] = "FORCE_RENDER"; }); REFRESH_SERIES_FAMILIES_ACTION_OPTIONS.forEach(function (name) { BaseChart.prototype._optionChangesMap[name] = "REFRESH_SERIES_FAMILIES"; }); exports.overlapping = overlapping; exports.BaseChart = BaseChart; // PLUGINS_SECTION BaseChart.addPlugin(require("../core/export").plugin); BaseChart.addPlugin(require("../core/title").plugin); BaseChart.addPlugin(require("../core/tooltip").plugin); BaseChart.addPlugin(require("../core/loading_indicator").plugin); BaseChart.addPlugin(require("../core/data_source").plugin); // These are charts specifics on using title - they cannot be omitted because of charts custom layout. var _change_TITLE = BaseChart.prototype._change_TITLE; BaseChart.prototype._change_TITLE = function () { _change_TITLE.apply(this, arguments); this._change(["FORCE_RENDER"]); }; // These are charts specifics on using tooltip. var _change_TOOLTIP = BaseChart.prototype._change_TOOLTIP; BaseChart.prototype._change_TOOLTIP = function () { _change_TOOLTIP.apply(this, arguments); this._change(["CHART_TOOLTIP"]); };