| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| |
| /** |
| * AUTO-GENERATED FILE. DO NOT MODIFY. |
| */ |
| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| import { __extends } from "tslib"; |
| import * as zrUtil from 'zrender/lib/core/util.js'; |
| import env from 'zrender/lib/core/env.js'; |
| import * as modelUtil from '../util/model.js'; |
| import ComponentModel from './Component.js'; |
| import { PaletteMixin } from './mixin/palette.js'; |
| import { DataFormatMixin } from '../model/mixin/dataFormat.js'; |
| import { getLayoutParams, mergeLayoutParam, fetchLayoutMode } from '../util/layout.js'; |
| import { createTask } from '../core/task.js'; |
| import { mountExtend } from '../util/clazz.js'; |
| import { SourceManager } from '../data/helper/sourceManager.js'; |
| import { defaultSeriesFormatTooltip } from '../component/tooltip/seriesFormatTooltip.js'; |
| var inner = modelUtil.makeInner(); |
| function getSelectionKey(data, dataIndex) { |
| return data.getName(dataIndex) || data.getId(dataIndex); |
| } |
| export var SERIES_UNIVERSAL_TRANSITION_PROP = '__universalTransitionEnabled'; |
| var SeriesModel = /** @class */function (_super) { |
| __extends(SeriesModel, _super); |
| function SeriesModel() { |
| // [Caution]: Because this class or desecendants can be used as `XXX.extend(subProto)`, |
| // the class members must not be initialized in constructor or declaration place. |
| // Otherwise there is bad case: |
| // class A {xxx = 1;} |
| // enableClassExtend(A); |
| // class B extends A {} |
| // var C = B.extend({xxx: 5}); |
| // var c = new C(); |
| // console.log(c.xxx); // expect 5 but always 1. |
| var _this = _super !== null && _super.apply(this, arguments) || this; |
| // --------------------------------------- |
| // Props about data selection |
| // --------------------------------------- |
| _this._selectedDataIndicesMap = {}; |
| return _this; |
| } |
| SeriesModel.prototype.init = function (option, parentModel, ecModel) { |
| this.seriesIndex = this.componentIndex; |
| this.dataTask = createTask({ |
| count: dataTaskCount, |
| reset: dataTaskReset |
| }); |
| this.dataTask.context = { |
| model: this |
| }; |
| this.mergeDefaultAndTheme(option, ecModel); |
| var sourceManager = inner(this).sourceManager = new SourceManager(this); |
| sourceManager.prepareSource(); |
| var data = this.getInitialData(option, ecModel); |
| wrapData(data, this); |
| this.dataTask.context.data = data; |
| if (process.env.NODE_ENV !== 'production') { |
| zrUtil.assert(data, 'getInitialData returned invalid data.'); |
| } |
| inner(this).dataBeforeProcessed = data; |
| // If we reverse the order (make data firstly, and then make |
| // dataBeforeProcessed by cloneShallow), cloneShallow will |
| // cause data.graph.data !== data when using |
| // module:echarts/data/Graph or module:echarts/data/Tree. |
| // See module:echarts/data/helper/linkSeriesData |
| // Theoretically, it is unreasonable to call `seriesModel.getData()` in the model |
| // init or merge stage, because the data can be restored. So we do not `restoreData` |
| // and `setData` here, which forbids calling `seriesModel.getData()` in this stage. |
| // Call `seriesModel.getRawData()` instead. |
| // this.restoreData(); |
| autoSeriesName(this); |
| this._initSelectedMapFromData(data); |
| }; |
| /** |
| * Util for merge default and theme to option |
| */ |
| SeriesModel.prototype.mergeDefaultAndTheme = function (option, ecModel) { |
| var layoutMode = fetchLayoutMode(this); |
| var inputPositionParams = layoutMode ? getLayoutParams(option) : {}; |
| // Backward compat: using subType on theme. |
| // But if name duplicate between series subType |
| // (for example: parallel) add component mainType, |
| // add suffix 'Series'. |
| var themeSubType = this.subType; |
| if (ComponentModel.hasClass(themeSubType)) { |
| themeSubType += 'Series'; |
| } |
| zrUtil.merge(option, ecModel.getTheme().get(this.subType)); |
| zrUtil.merge(option, this.getDefaultOption()); |
| // Default label emphasis `show` |
| modelUtil.defaultEmphasis(option, 'label', ['show']); |
| this.fillDataTextStyle(option.data); |
| if (layoutMode) { |
| mergeLayoutParam(option, inputPositionParams, layoutMode); |
| } |
| }; |
| SeriesModel.prototype.mergeOption = function (newSeriesOption, ecModel) { |
| // this.settingTask.dirty(); |
| newSeriesOption = zrUtil.merge(this.option, newSeriesOption, true); |
| this.fillDataTextStyle(newSeriesOption.data); |
| var layoutMode = fetchLayoutMode(this); |
| if (layoutMode) { |
| mergeLayoutParam(this.option, newSeriesOption, layoutMode); |
| } |
| var sourceManager = inner(this).sourceManager; |
| sourceManager.dirty(); |
| sourceManager.prepareSource(); |
| var data = this.getInitialData(newSeriesOption, ecModel); |
| wrapData(data, this); |
| this.dataTask.dirty(); |
| this.dataTask.context.data = data; |
| inner(this).dataBeforeProcessed = data; |
| autoSeriesName(this); |
| this._initSelectedMapFromData(data); |
| }; |
| SeriesModel.prototype.fillDataTextStyle = function (data) { |
| // Default data label emphasis `show` |
| // FIXME Tree structure data ? |
| // FIXME Performance ? |
| if (data && !zrUtil.isTypedArray(data)) { |
| var props = ['show']; |
| for (var i = 0; i < data.length; i++) { |
| if (data[i] && data[i].label) { |
| modelUtil.defaultEmphasis(data[i], 'label', props); |
| } |
| } |
| } |
| }; |
| /** |
| * Init a data structure from data related option in series |
| * Must be overridden. |
| */ |
| SeriesModel.prototype.getInitialData = function (option, ecModel) { |
| return; |
| }; |
| /** |
| * Append data to list |
| */ |
| SeriesModel.prototype.appendData = function (params) { |
| // FIXME ??? |
| // (1) If data from dataset, forbidden append. |
| // (2) support append data of dataset. |
| var data = this.getRawData(); |
| data.appendData(params.data); |
| }; |
| /** |
| * Consider some method like `filter`, `map` need make new data, |
| * We should make sure that `seriesModel.getData()` get correct |
| * data in the stream procedure. So we fetch data from upstream |
| * each time `task.perform` called. |
| */ |
| SeriesModel.prototype.getData = function (dataType) { |
| var task = getCurrentTask(this); |
| if (task) { |
| var data = task.context.data; |
| return dataType == null || !data.getLinkedData ? data : data.getLinkedData(dataType); |
| } else { |
| // When series is not alive (that may happen when click toolbox |
| // restore or setOption with not merge mode), series data may |
| // be still need to judge animation or something when graphic |
| // elements want to know whether fade out. |
| return inner(this).data; |
| } |
| }; |
| SeriesModel.prototype.getAllData = function () { |
| var mainData = this.getData(); |
| return mainData && mainData.getLinkedDataAll ? mainData.getLinkedDataAll() : [{ |
| data: mainData |
| }]; |
| }; |
| SeriesModel.prototype.setData = function (data) { |
| var task = getCurrentTask(this); |
| if (task) { |
| var context = task.context; |
| // Consider case: filter, data sample. |
| // FIXME:TS never used, so comment it |
| // if (context.data !== data && task.modifyOutputEnd) { |
| // task.setOutputEnd(data.count()); |
| // } |
| context.outputData = data; |
| // Caution: setData should update context.data, |
| // Because getData may be called multiply in a |
| // single stage and expect to get the data just |
| // set. (For example, AxisProxy, x y both call |
| // getData and setDate sequentially). |
| // So the context.data should be fetched from |
| // upstream each time when a stage starts to be |
| // performed. |
| if (task !== this.dataTask) { |
| context.data = data; |
| } |
| } |
| inner(this).data = data; |
| }; |
| SeriesModel.prototype.getEncode = function () { |
| var encode = this.get('encode', true); |
| if (encode) { |
| return zrUtil.createHashMap(encode); |
| } |
| }; |
| SeriesModel.prototype.getSourceManager = function () { |
| return inner(this).sourceManager; |
| }; |
| SeriesModel.prototype.getSource = function () { |
| return this.getSourceManager().getSource(); |
| }; |
| /** |
| * Get data before processed |
| */ |
| SeriesModel.prototype.getRawData = function () { |
| return inner(this).dataBeforeProcessed; |
| }; |
| SeriesModel.prototype.getColorBy = function () { |
| var colorBy = this.get('colorBy'); |
| return colorBy || 'series'; |
| }; |
| SeriesModel.prototype.isColorBySeries = function () { |
| return this.getColorBy() === 'series'; |
| }; |
| /** |
| * Get base axis if has coordinate system and has axis. |
| * By default use coordSys.getBaseAxis(); |
| * Can be overridden for some chart. |
| * @return {type} description |
| */ |
| SeriesModel.prototype.getBaseAxis = function () { |
| var coordSys = this.coordinateSystem; |
| // @ts-ignore |
| return coordSys && coordSys.getBaseAxis && coordSys.getBaseAxis(); |
| }; |
| /** |
| * Retrieve the index of nearest value in the view coordinate. |
| * Data position is compared with each axis's dataToCoord. |
| * |
| * @param axisDim axis dimension |
| * @param dim data dimension |
| * @param value |
| * @param [maxDistance=Infinity] The maximum distance in view coordinate space |
| * @return If and only if multiple indices has |
| * the same value, they are put to the result. |
| */ |
| SeriesModel.prototype.indicesOfNearest = function (axisDim, dim, value, maxDistance) { |
| var data = this.getData(); |
| var coordSys = this.coordinateSystem; |
| var axis = coordSys && coordSys.getAxis(axisDim); |
| if (!coordSys || !axis) { |
| return []; |
| } |
| var targetCoord = axis.dataToCoord(value); |
| if (maxDistance == null) { |
| maxDistance = Infinity; |
| } |
| var nearestIndices = []; |
| var minDist = Infinity; |
| var minDiff = -1; |
| var nearestIndicesLen = 0; |
| data.each(dim, function (dimValue, idx) { |
| var dataCoord = axis.dataToCoord(dimValue); |
| var diff = targetCoord - dataCoord; |
| var dist = Math.abs(diff); |
| if (dist <= maxDistance) { |
| // When the `value` is at the middle of `this.get(dim, i)` and `this.get(dim, i+1)`, |
| // we'd better not push both of them to `nearestIndices`, otherwise it is easy to |
| // get more than one item in `nearestIndices` (more specifically, in `tooltip`). |
| // So we choose the one that `diff >= 0` in this case. |
| // But if `this.get(dim, i)` and `this.get(dim, j)` get the same value, both of them |
| // should be push to `nearestIndices`. |
| if (dist < minDist || dist === minDist && diff >= 0 && minDiff < 0) { |
| minDist = dist; |
| minDiff = diff; |
| nearestIndicesLen = 0; |
| } |
| if (diff === minDiff) { |
| nearestIndices[nearestIndicesLen++] = idx; |
| } |
| } |
| }); |
| nearestIndices.length = nearestIndicesLen; |
| return nearestIndices; |
| }; |
| /** |
| * Default tooltip formatter |
| * |
| * @param dataIndex |
| * @param multipleSeries |
| * @param dataType |
| * @param renderMode valid values: 'html'(by default) and 'richText'. |
| * 'html' is used for rendering tooltip in extra DOM form, and the result |
| * string is used as DOM HTML content. |
| * 'richText' is used for rendering tooltip in rich text form, for those where |
| * DOM operation is not supported. |
| * @return formatted tooltip with `html` and `markers` |
| * Notice: The override method can also return string |
| */ |
| SeriesModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) { |
| return defaultSeriesFormatTooltip({ |
| series: this, |
| dataIndex: dataIndex, |
| multipleSeries: multipleSeries |
| }); |
| }; |
| SeriesModel.prototype.isAnimationEnabled = function () { |
| var ecModel = this.ecModel; |
| // Disable animation if using echarts in node but not give ssr flag. |
| // In ssr mode, renderToString will generate svg with css animation. |
| if (env.node && !(ecModel && ecModel.ssr)) { |
| return false; |
| } |
| var animationEnabled = this.getShallow('animation'); |
| if (animationEnabled) { |
| if (this.getData().count() > this.getShallow('animationThreshold')) { |
| animationEnabled = false; |
| } |
| } |
| return !!animationEnabled; |
| }; |
| SeriesModel.prototype.restoreData = function () { |
| this.dataTask.dirty(); |
| }; |
| SeriesModel.prototype.getColorFromPalette = function (name, scope, requestColorNum) { |
| var ecModel = this.ecModel; |
| // PENDING |
| var color = PaletteMixin.prototype.getColorFromPalette.call(this, name, scope, requestColorNum); |
| if (!color) { |
| color = ecModel.getColorFromPalette(name, scope, requestColorNum); |
| } |
| return color; |
| }; |
| /** |
| * Use `data.mapDimensionsAll(coordDim)` instead. |
| * @deprecated |
| */ |
| SeriesModel.prototype.coordDimToDataDim = function (coordDim) { |
| return this.getRawData().mapDimensionsAll(coordDim); |
| }; |
| /** |
| * Get progressive rendering count each step |
| */ |
| SeriesModel.prototype.getProgressive = function () { |
| return this.get('progressive'); |
| }; |
| /** |
| * Get progressive rendering count each step |
| */ |
| SeriesModel.prototype.getProgressiveThreshold = function () { |
| return this.get('progressiveThreshold'); |
| }; |
| // PENGING If selectedMode is null ? |
| SeriesModel.prototype.select = function (innerDataIndices, dataType) { |
| this._innerSelect(this.getData(dataType), innerDataIndices); |
| }; |
| SeriesModel.prototype.unselect = function (innerDataIndices, dataType) { |
| var selectedMap = this.option.selectedMap; |
| if (!selectedMap) { |
| return; |
| } |
| var selectedMode = this.option.selectedMode; |
| var data = this.getData(dataType); |
| if (selectedMode === 'series' || selectedMap === 'all') { |
| this.option.selectedMap = {}; |
| this._selectedDataIndicesMap = {}; |
| return; |
| } |
| for (var i = 0; i < innerDataIndices.length; i++) { |
| var dataIndex = innerDataIndices[i]; |
| var nameOrId = getSelectionKey(data, dataIndex); |
| selectedMap[nameOrId] = false; |
| this._selectedDataIndicesMap[nameOrId] = -1; |
| } |
| }; |
| SeriesModel.prototype.toggleSelect = function (innerDataIndices, dataType) { |
| var tmpArr = []; |
| for (var i = 0; i < innerDataIndices.length; i++) { |
| tmpArr[0] = innerDataIndices[i]; |
| this.isSelected(innerDataIndices[i], dataType) ? this.unselect(tmpArr, dataType) : this.select(tmpArr, dataType); |
| } |
| }; |
| SeriesModel.prototype.getSelectedDataIndices = function () { |
| if (this.option.selectedMap === 'all') { |
| return [].slice.call(this.getData().getIndices()); |
| } |
| var selectedDataIndicesMap = this._selectedDataIndicesMap; |
| var nameOrIds = zrUtil.keys(selectedDataIndicesMap); |
| var dataIndices = []; |
| for (var i = 0; i < nameOrIds.length; i++) { |
| var dataIndex = selectedDataIndicesMap[nameOrIds[i]]; |
| if (dataIndex >= 0) { |
| dataIndices.push(dataIndex); |
| } |
| } |
| return dataIndices; |
| }; |
| SeriesModel.prototype.isSelected = function (dataIndex, dataType) { |
| var selectedMap = this.option.selectedMap; |
| if (!selectedMap) { |
| return false; |
| } |
| var data = this.getData(dataType); |
| return (selectedMap === 'all' || selectedMap[getSelectionKey(data, dataIndex)]) && !data.getItemModel(dataIndex).get(['select', 'disabled']); |
| }; |
| SeriesModel.prototype.isUniversalTransitionEnabled = function () { |
| if (this[SERIES_UNIVERSAL_TRANSITION_PROP]) { |
| return true; |
| } |
| var universalTransitionOpt = this.option.universalTransition; |
| // Quick reject |
| if (!universalTransitionOpt) { |
| return false; |
| } |
| if (universalTransitionOpt === true) { |
| return true; |
| } |
| // Can be simply 'universalTransition: true' |
| return universalTransitionOpt && universalTransitionOpt.enabled; |
| }; |
| SeriesModel.prototype._innerSelect = function (data, innerDataIndices) { |
| var _a, _b; |
| var option = this.option; |
| var selectedMode = option.selectedMode; |
| var len = innerDataIndices.length; |
| if (!selectedMode || !len) { |
| return; |
| } |
| if (selectedMode === 'series') { |
| option.selectedMap = 'all'; |
| } else if (selectedMode === 'multiple') { |
| if (!zrUtil.isObject(option.selectedMap)) { |
| option.selectedMap = {}; |
| } |
| var selectedMap = option.selectedMap; |
| for (var i = 0; i < len; i++) { |
| var dataIndex = innerDataIndices[i]; |
| // TODO different types of data share same object. |
| var nameOrId = getSelectionKey(data, dataIndex); |
| selectedMap[nameOrId] = true; |
| this._selectedDataIndicesMap[nameOrId] = data.getRawIndex(dataIndex); |
| } |
| } else if (selectedMode === 'single' || selectedMode === true) { |
| var lastDataIndex = innerDataIndices[len - 1]; |
| var nameOrId = getSelectionKey(data, lastDataIndex); |
| option.selectedMap = (_a = {}, _a[nameOrId] = true, _a); |
| this._selectedDataIndicesMap = (_b = {}, _b[nameOrId] = data.getRawIndex(lastDataIndex), _b); |
| } |
| }; |
| SeriesModel.prototype._initSelectedMapFromData = function (data) { |
| // Ignore select info in data if selectedMap exists. |
| // NOTE It's only for legacy usage. edge data is not supported. |
| if (this.option.selectedMap) { |
| return; |
| } |
| var dataIndices = []; |
| if (data.hasItemOption) { |
| data.each(function (idx) { |
| var rawItem = data.getRawDataItem(idx); |
| if (rawItem && rawItem.selected) { |
| dataIndices.push(idx); |
| } |
| }); |
| } |
| if (dataIndices.length > 0) { |
| this._innerSelect(data, dataIndices); |
| } |
| }; |
| // /** |
| // * @see {module:echarts/stream/Scheduler} |
| // */ |
| // abstract pipeTask: null |
| SeriesModel.registerClass = function (clz) { |
| return ComponentModel.registerClass(clz); |
| }; |
| SeriesModel.protoInitialize = function () { |
| var proto = SeriesModel.prototype; |
| proto.type = 'series.__base__'; |
| proto.seriesIndex = 0; |
| proto.ignoreStyleOnData = false; |
| proto.hasSymbolVisual = false; |
| proto.defaultSymbol = 'circle'; |
| // Make sure the values can be accessed! |
| proto.visualStyleAccessPath = 'itemStyle'; |
| proto.visualDrawType = 'fill'; |
| }(); |
| return SeriesModel; |
| }(ComponentModel); |
| zrUtil.mixin(SeriesModel, DataFormatMixin); |
| zrUtil.mixin(SeriesModel, PaletteMixin); |
| mountExtend(SeriesModel, ComponentModel); |
| /** |
| * MUST be called after `prepareSource` called |
| * Here we need to make auto series, especially for auto legend. But we |
| * do not modify series.name in option to avoid side effects. |
| */ |
| function autoSeriesName(seriesModel) { |
| // User specified name has higher priority, otherwise it may cause |
| // series can not be queried unexpectedly. |
| var name = seriesModel.name; |
| if (!modelUtil.isNameSpecified(seriesModel)) { |
| seriesModel.name = getSeriesAutoName(seriesModel) || name; |
| } |
| } |
| function getSeriesAutoName(seriesModel) { |
| var data = seriesModel.getRawData(); |
| var dataDims = data.mapDimensionsAll('seriesName'); |
| var nameArr = []; |
| zrUtil.each(dataDims, function (dataDim) { |
| var dimInfo = data.getDimensionInfo(dataDim); |
| dimInfo.displayName && nameArr.push(dimInfo.displayName); |
| }); |
| return nameArr.join(' '); |
| } |
| function dataTaskCount(context) { |
| return context.model.getRawData().count(); |
| } |
| function dataTaskReset(context) { |
| var seriesModel = context.model; |
| seriesModel.setData(seriesModel.getRawData().cloneShallow()); |
| return dataTaskProgress; |
| } |
| function dataTaskProgress(param, context) { |
| // Avoid repeat cloneShallow when data just created in reset. |
| if (context.outputData && param.end > context.outputData.count()) { |
| context.model.getRawData().cloneShallow(context.outputData); |
| } |
| } |
| // TODO refactor |
| function wrapData(data, seriesModel) { |
| zrUtil.each(zrUtil.concatArray(data.CHANGABLE_METHODS, data.DOWNSAMPLE_METHODS), function (methodName) { |
| data.wrapMethod(methodName, zrUtil.curry(onDataChange, seriesModel)); |
| }); |
| } |
| function onDataChange(seriesModel, newList) { |
| var task = getCurrentTask(seriesModel); |
| if (task) { |
| // Consider case: filter, selectRange |
| task.setOutputEnd((newList || this).count()); |
| } |
| return newList; |
| } |
| function getCurrentTask(seriesModel) { |
| var scheduler = (seriesModel.ecModel || {}).scheduler; |
| var pipeline = scheduler && scheduler.getPipeline(seriesModel.uid); |
| if (pipeline) { |
| // When pipline finished, the currrentTask keep the last |
| // task (renderTask). |
| var task = pipeline.currentTask; |
| if (task) { |
| var agentStubMap = task.agentStubMap; |
| if (agentStubMap) { |
| task = agentStubMap.get(seriesModel.uid); |
| } |
| } |
| return task; |
| } |
| } |
| export default SeriesModel; |