| /* | 
 | * 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 * as zrUtil from 'zrender/src/core/util'; | 
 | import env from 'zrender/src/core/env'; | 
 | import * as modelUtil from '../util/model'; | 
 | import { | 
 |     DataHost, DimensionName, StageHandlerProgressParams, | 
 |     SeriesOption, ZRColor, BoxLayoutOptionMixin, | 
 |     ScaleDataValue, | 
 |     Dictionary, | 
 |     OptionDataItemObject, | 
 |     SeriesDataType, | 
 |     SeriesEncodeOptionMixin, | 
 |     OptionEncodeValue, | 
 |     ColorBy, | 
 |     StatesOptionMixin | 
 | } from '../util/types'; | 
 | import ComponentModel, { ComponentModelConstructor } from './Component'; | 
 | import {PaletteMixin} from './mixin/palette'; | 
 | import { DataFormatMixin } from '../model/mixin/dataFormat'; | 
 | import Model from '../model/Model'; | 
 | import { | 
 |     getLayoutParams, | 
 |     mergeLayoutParam, | 
 |     fetchLayoutMode | 
 | } from '../util/layout'; | 
 | import {createTask} from '../core/task'; | 
 | import GlobalModel from './Global'; | 
 | import { CoordinateSystem } from '../coord/CoordinateSystem'; | 
 | import { ExtendableConstructor, mountExtend, Constructor } from '../util/clazz'; | 
 | import { PipelineContext, SeriesTaskContext, GeneralTask, OverallTask, SeriesTask } from '../core/Scheduler'; | 
 | import LegendVisualProvider from '../visual/LegendVisualProvider'; | 
 | import SeriesData from '../data/SeriesData'; | 
 | import Axis from '../coord/Axis'; | 
 | import type { BrushCommonSelectorsForSeries, BrushSelectableArea } from '../component/brush/selector'; | 
 | import makeStyleMapper from './mixin/makeStyleMapper'; | 
 | import { SourceManager } from '../data/helper/sourceManager'; | 
 | import { Source } from '../data/Source'; | 
 | import { defaultSeriesFormatTooltip } from '../component/tooltip/seriesFormatTooltip'; | 
 | import {ECSymbol} from '../util/symbol'; | 
 | import {Group} from '../util/graphic'; | 
 | import {LegendIconParams} from '../component/legend/LegendModel'; | 
 | import {dimPermutations} from '../component/marker/MarkAreaView'; | 
 |  | 
 | const inner = modelUtil.makeInner<{ | 
 |     data: SeriesData | 
 |     dataBeforeProcessed: SeriesData | 
 |     sourceManager: SourceManager | 
 | }, SeriesModel>(); | 
 |  | 
 | function getSelectionKey(data: SeriesData, dataIndex: number): string { | 
 |     return data.getName(dataIndex) || data.getId(dataIndex); | 
 | } | 
 |  | 
 | export const SERIES_UNIVERSAL_TRANSITION_PROP = '__universalTransitionEnabled'; | 
 |  | 
 | interface SeriesModel { | 
 |     /** | 
 |      * Convenient for override in extended class. | 
 |      * Implement it if needed. | 
 |      */ | 
 |     preventIncremental(): boolean; | 
 |     /** | 
 |      * See tooltip. | 
 |      * Implement it if needed. | 
 |      * @return Point of tooltip. null/undefined can be returned. | 
 |      */ | 
 |     getTooltipPosition(dataIndex: number): number[]; | 
 |  | 
 |     /** | 
 |      * Get data indices for show tooltip content. See tooltip. | 
 |      * Implement it if needed. | 
 |      */ | 
 |     getAxisTooltipData( | 
 |         dim: DimensionName[], | 
 |         value: ScaleDataValue, | 
 |         baseAxis: Axis | 
 |     ): { | 
 |         dataIndices: number[], | 
 |         nestestValue: any | 
 |     }; | 
 |  | 
 |     /** | 
 |      * Get position for marker | 
 |      */ | 
 |     getMarkerPosition( | 
 |         value: ScaleDataValue[], | 
 |         dims?: typeof dimPermutations[number], | 
 |         startingAtTick?: boolean | 
 |     ): number[]; | 
 |  | 
 |     /** | 
 |      * Get legend icon symbol according to each series type | 
 |      */ | 
 |     getLegendIcon(opt: LegendIconParams): ECSymbol | Group; | 
 |  | 
 |     /** | 
 |      * See `component/brush/selector.js` | 
 |      * Defined the brush selector for this series. | 
 |      */ | 
 |     brushSelector( | 
 |         dataIndex: number, | 
 |         data: SeriesData, | 
 |         selectors: BrushCommonSelectorsForSeries, | 
 |         area: BrushSelectableArea | 
 |     ): boolean; | 
 |  | 
 |     enableAriaDecal(): void; | 
 | } | 
 |  | 
 | class SeriesModel<Opt extends SeriesOption = SeriesOption> extends ComponentModel<Opt> { | 
 |  | 
 |     // [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. | 
 |  | 
 |     // @readonly | 
 |     type: string; | 
 |  | 
 |     // Should be implenented in subclass. | 
 |     defaultOption: SeriesOption; | 
 |  | 
 |     // @readonly | 
 |     seriesIndex: number; | 
 |  | 
 |     // coodinateSystem will be injected in the echarts/CoordinateSystem | 
 |     coordinateSystem: CoordinateSystem; | 
 |  | 
 |     // Injected outside | 
 |     dataTask: SeriesTask; | 
 |     // Injected outside | 
 |     pipelineContext: PipelineContext; | 
 |  | 
 |     // --------------------------------------- | 
 |     // Props to tell visual/style.ts about how to do visual encoding. | 
 |     // --------------------------------------- | 
 |     // legend visual provider to the legend component | 
 |     legendVisualProvider: LegendVisualProvider; | 
 |  | 
 |     // Access path of style for visual | 
 |     visualStyleAccessPath: string; | 
 |     // Which property is treated as main color. Which can get from the palette. | 
 |     visualDrawType: 'fill' | 'stroke'; | 
 |     // Style mapping rules. | 
 |     visualStyleMapper: ReturnType<typeof makeStyleMapper>; | 
 |     // If ignore style on data. It's only for global visual/style.ts | 
 |     // Enabled when series it self will handle it. | 
 |     ignoreStyleOnData: boolean; | 
 |     // If do symbol visual encoding | 
 |     hasSymbolVisual: boolean; | 
 |     // Default symbol type. | 
 |     defaultSymbol: string; | 
 |     // Symbol provide to legend. | 
 |     legendIcon: string; | 
 |  | 
 |     // It will be set temporary when cross series transition setting is from setOption. | 
 |     // TODO if deprecate further? | 
 |     [SERIES_UNIVERSAL_TRANSITION_PROP]: boolean; | 
 |  | 
 |     // --------------------------------------- | 
 |     // Props about data selection | 
 |     // --------------------------------------- | 
 |     private _selectedDataIndicesMap: Dictionary<number> = {}; | 
 |     readonly preventUsingHoverLayer: boolean; | 
 |  | 
 |     static protoInitialize = (function () { | 
 |         const 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'; | 
 |     })(); | 
 |  | 
 |  | 
 |     init(option: Opt, parentModel: Model, ecModel: GlobalModel) { | 
 |  | 
 |         this.seriesIndex = this.componentIndex; | 
 |  | 
 |         this.dataTask = createTask<SeriesTaskContext>({ | 
 |             count: dataTaskCount, | 
 |             reset: dataTaskReset | 
 |         }); | 
 |         this.dataTask.context = {model: this}; | 
 |  | 
 |         this.mergeDefaultAndTheme(option, ecModel); | 
 |  | 
 |         const sourceManager = inner(this).sourceManager = new SourceManager(this); | 
 |         sourceManager.prepareSource(); | 
 |  | 
 |         const data = this.getInitialData(option, ecModel); | 
 |         wrapData(data, this); | 
 |         this.dataTask.context.data = data; | 
 |  | 
 |         if (__DEV__) { | 
 |             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 | 
 |      */ | 
 |     mergeDefaultAndTheme(option: Opt, ecModel: GlobalModel): void { | 
 |         const layoutMode = fetchLayoutMode(this); | 
 |         const inputPositionParams = layoutMode | 
 |             ? getLayoutParams(option as BoxLayoutOptionMixin) : {}; | 
 |  | 
 |         // Backward compat: using subType on theme. | 
 |         // But if name duplicate between series subType | 
 |         // (for example: parallel) add component mainType, | 
 |         // add suffix 'Series'. | 
 |         let themeSubType = this.subType; | 
 |         if ((ComponentModel as ComponentModelConstructor).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 as ArrayLike<any>); | 
 |  | 
 |         if (layoutMode) { | 
 |             mergeLayoutParam(option as BoxLayoutOptionMixin, inputPositionParams, layoutMode); | 
 |         } | 
 |     } | 
 |  | 
 |     mergeOption(newSeriesOption: Opt, ecModel: GlobalModel) { | 
 |         // this.settingTask.dirty(); | 
 |  | 
 |         newSeriesOption = zrUtil.merge(this.option, newSeriesOption, true); | 
 |         this.fillDataTextStyle(newSeriesOption.data as ArrayLike<any>); | 
 |  | 
 |         const layoutMode = fetchLayoutMode(this); | 
 |         if (layoutMode) { | 
 |             mergeLayoutParam( | 
 |                 this.option as BoxLayoutOptionMixin, | 
 |                 newSeriesOption as BoxLayoutOptionMixin, | 
 |                 layoutMode | 
 |             ); | 
 |         } | 
 |  | 
 |         const sourceManager = inner(this).sourceManager; | 
 |         sourceManager.dirty(); | 
 |         sourceManager.prepareSource(); | 
 |  | 
 |         const 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); | 
 |     } | 
 |  | 
 |     fillDataTextStyle(data: ArrayLike<any>): void { | 
 |         // Default data label emphasis `show` | 
 |         // FIXME Tree structure data ? | 
 |         // FIXME Performance ? | 
 |         if (data && !zrUtil.isTypedArray(data)) { | 
 |             const props = ['show']; | 
 |             for (let 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. | 
 |      */ | 
 |     getInitialData(option: Opt, ecModel: GlobalModel): SeriesData { | 
 |         return; | 
 |     } | 
 |  | 
 |     /** | 
 |      * Append data to list | 
 |      */ | 
 |     appendData(params: {data: ArrayLike<any>}): void { | 
 |         // FIXME ??? | 
 |         // (1) If data from dataset, forbidden append. | 
 |         // (2) support append data of dataset. | 
 |         const 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. | 
 |      */ | 
 |     getData(dataType?: SeriesDataType): SeriesData<this> { | 
 |         const task = getCurrentTask(this); | 
 |         if (task) { | 
 |             const data = task.context.data; | 
 |             return (dataType == null ? data : data.getLinkedData(dataType)) as SeriesData<this>; | 
 |         } | 
 |         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 as SeriesData<this>; | 
 |         } | 
 |     } | 
 |  | 
 |     getAllData(): ({ | 
 |         data: SeriesData, | 
 |         type?: SeriesDataType | 
 |     })[] { | 
 |         const mainData = this.getData(); | 
 |         return (mainData && mainData.getLinkedDataAll) | 
 |             ? mainData.getLinkedDataAll() | 
 |             : [{ data: mainData }]; | 
 |     } | 
 |  | 
 |     setData(data: SeriesData): void { | 
 |         const task = getCurrentTask(this); | 
 |         if (task) { | 
 |             const 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; | 
 |     } | 
 |  | 
 |     getEncode() { | 
 |         const encode = (this as Model<SeriesEncodeOptionMixin>).get('encode', true); | 
 |         if (encode) { | 
 |             return zrUtil.createHashMap<OptionEncodeValue, DimensionName>(encode); | 
 |         } | 
 |     } | 
 |  | 
 |     getSourceManager(): SourceManager { | 
 |         return inner(this).sourceManager; | 
 |     } | 
 |  | 
 |     getSource(): Source { | 
 |         return this.getSourceManager().getSource(); | 
 |     } | 
 |  | 
 |     /** | 
 |      * Get data before processed | 
 |      */ | 
 |     getRawData(): SeriesData { | 
 |         return inner(this).dataBeforeProcessed; | 
 |     } | 
 |  | 
 |     getColorBy(): ColorBy { | 
 |         const colorBy = this.get('colorBy'); | 
 |         return colorBy || 'series'; | 
 |     } | 
 |  | 
 |     isColorBySeries(): boolean { | 
 |         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 | 
 |      */ | 
 |     getBaseAxis(): Axis { | 
 |         const coordSys = this.coordinateSystem; | 
 |         // @ts-ignore | 
 |         return coordSys && coordSys.getBaseAxis && coordSys.getBaseAxis(); | 
 |     } | 
 |  | 
 |     /** | 
 |      * 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 | 
 |      */ | 
 |     formatTooltip( | 
 |         dataIndex: number, | 
 |         multipleSeries?: boolean, | 
 |         dataType?: SeriesDataType | 
 |     ): ReturnType<DataFormatMixin['formatTooltip']> { | 
 |         return defaultSeriesFormatTooltip({ | 
 |             series: this, | 
 |             dataIndex: dataIndex, | 
 |             multipleSeries: multipleSeries | 
 |         }); | 
 |     } | 
 |  | 
 |     isAnimationEnabled(): boolean { | 
 |         const 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; | 
 |         } | 
 |         let animationEnabled = this.getShallow('animation'); | 
 |         if (animationEnabled) { | 
 |             if (this.getData().count() > this.getShallow('animationThreshold')) { | 
 |                 animationEnabled = false; | 
 |             } | 
 |         } | 
 |         return !!animationEnabled; | 
 |     } | 
 |  | 
 |     restoreData() { | 
 |         this.dataTask.dirty(); | 
 |     } | 
 |  | 
 |     getColorFromPalette(name: string, scope: any, requestColorNum?: number): ZRColor { | 
 |         const ecModel = this.ecModel; | 
 |         // PENDING | 
 |         let 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 | 
 |      */ | 
 |     coordDimToDataDim(coordDim: DimensionName): DimensionName[] { | 
 |         return this.getRawData().mapDimensionsAll(coordDim); | 
 |     } | 
 |  | 
 |     /** | 
 |      * Get progressive rendering count each step | 
 |      */ | 
 |     getProgressive(): number | false { | 
 |         return this.get('progressive'); | 
 |     } | 
 |  | 
 |     /** | 
 |      * Get progressive rendering count each step | 
 |      */ | 
 |     getProgressiveThreshold(): number { | 
 |         return this.get('progressiveThreshold'); | 
 |     } | 
 |  | 
 |     // PENGING If selectedMode is null ? | 
 |     select(innerDataIndices: number[], dataType?: SeriesDataType): void { | 
 |         this._innerSelect(this.getData(dataType), innerDataIndices); | 
 |     } | 
 |  | 
 |     unselect(innerDataIndices: number[], dataType?: SeriesDataType): void { | 
 |         const selectedMap = this.option.selectedMap; | 
 |         if (!selectedMap) { | 
 |             return; | 
 |         } | 
 |         const selectedMode = this.option.selectedMode; | 
 |  | 
 |         const data = this.getData(dataType); | 
 |         if (selectedMode === 'series' || selectedMap === 'all') { | 
 |             this.option.selectedMap = {}; | 
 |             this._selectedDataIndicesMap = {}; | 
 |             return; | 
 |         } | 
 |  | 
 |         for (let i = 0; i < innerDataIndices.length; i++) { | 
 |             const dataIndex = innerDataIndices[i]; | 
 |             const nameOrId = getSelectionKey(data, dataIndex); | 
 |             selectedMap[nameOrId] = false; | 
 |             this._selectedDataIndicesMap[nameOrId] = -1; | 
 |         } | 
 |     } | 
 |  | 
 |     toggleSelect(innerDataIndices: number[], dataType?: SeriesDataType): void { | 
 |         const tmpArr: number[] = []; | 
 |         for (let i = 0; i < innerDataIndices.length; i++) { | 
 |             tmpArr[0] = innerDataIndices[i]; | 
 |             this.isSelected(innerDataIndices[i], dataType) | 
 |                 ? this.unselect(tmpArr, dataType) | 
 |                 : this.select(tmpArr, dataType); | 
 |         } | 
 |     } | 
 |  | 
 |     getSelectedDataIndices(): number[] { | 
 |         if (this.option.selectedMap === 'all') { | 
 |             return [].slice.call(this.getData().getIndices()); | 
 |         } | 
 |         const selectedDataIndicesMap = this._selectedDataIndicesMap; | 
 |         const nameOrIds = zrUtil.keys(selectedDataIndicesMap); | 
 |         const dataIndices = []; | 
 |         for (let i = 0; i < nameOrIds.length; i++) { | 
 |             const dataIndex = selectedDataIndicesMap[nameOrIds[i]]; | 
 |             if (dataIndex >= 0) { | 
 |                 dataIndices.push(dataIndex); | 
 |             } | 
 |         } | 
 |         return dataIndices; | 
 |     } | 
 |  | 
 |     isSelected(dataIndex: number, dataType?: SeriesDataType): boolean { | 
 |         const selectedMap = this.option.selectedMap; | 
 |         if (!selectedMap) { | 
 |             return false; | 
 |         } | 
 |  | 
 |         const data = this.getData(dataType); | 
 |  | 
 |         return (selectedMap === 'all' || selectedMap[getSelectionKey(data, dataIndex)]) | 
 |             && !data.getItemModel<StatesOptionMixin<unknown, unknown>>(dataIndex).get(['select', 'disabled']); | 
 |     } | 
 |  | 
 |     isUniversalTransitionEnabled(): boolean { | 
 |         if (this[SERIES_UNIVERSAL_TRANSITION_PROP]) { | 
 |             return true; | 
 |         } | 
 |  | 
 |         const universalTransitionOpt = this.option.universalTransition; | 
 |         // Quick reject | 
 |         if (!universalTransitionOpt) { | 
 |             return false; | 
 |         } | 
 |  | 
 |         if (universalTransitionOpt === true) { | 
 |             return true; | 
 |         } | 
 |  | 
 |         // Can be simply 'universalTransition: true' | 
 |         return universalTransitionOpt && universalTransitionOpt.enabled; | 
 |     } | 
 |  | 
 |     private _innerSelect(data: SeriesData, innerDataIndices: number[]) { | 
 |         const option = this.option; | 
 |         const selectedMode = option.selectedMode; | 
 |         const len = innerDataIndices.length; | 
 |         if (!selectedMode || !len) { | 
 |             return; | 
 |         } | 
 |  | 
 |         if (selectedMode === 'series') { | 
 |             option.selectedMap = 'all'; | 
 |         } | 
 |         else if (selectedMode === 'multiple') { | 
 |             if (!zrUtil.isObject(option.selectedMap)) { | 
 |                 option.selectedMap = {}; | 
 |             } | 
 |             const selectedMap = option.selectedMap; | 
 |             for (let i = 0; i < len; i++) { | 
 |                 const dataIndex = innerDataIndices[i]; | 
 |                 // TODO different types of data share same object. | 
 |                 const nameOrId = getSelectionKey(data, dataIndex); | 
 |                 selectedMap[nameOrId] = true; | 
 |                 this._selectedDataIndicesMap[nameOrId] = data.getRawIndex(dataIndex); | 
 |             } | 
 |         } | 
 |         else if (selectedMode === 'single' || selectedMode === true) { | 
 |             const lastDataIndex = innerDataIndices[len - 1]; | 
 |             const nameOrId = getSelectionKey(data, lastDataIndex); | 
 |             option.selectedMap = { | 
 |                 [nameOrId]: true | 
 |             }; | 
 |             this._selectedDataIndicesMap = { | 
 |                 [nameOrId]: data.getRawIndex(lastDataIndex) | 
 |             }; | 
 |         } | 
 |     } | 
 |  | 
 |     private _initSelectedMapFromData(data: SeriesData) { | 
 |         // 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; | 
 |         } | 
 |  | 
 |         const dataIndices: number[] = []; | 
 |         if (data.hasItemOption) { | 
 |             data.each(function (idx) { | 
 |                 const rawItem = data.getRawDataItem(idx); | 
 |                 if (rawItem && (rawItem as OptionDataItemObject<unknown>).selected) { | 
 |                     dataIndices.push(idx); | 
 |                 } | 
 |             }); | 
 |         } | 
 |  | 
 |         if (dataIndices.length > 0) { | 
 |             this._innerSelect(data, dataIndices); | 
 |         } | 
 |     } | 
 |  | 
 |     // /** | 
 |     //  * @see {module:echarts/stream/Scheduler} | 
 |     //  */ | 
 |     // abstract pipeTask: null | 
 |  | 
 |     static registerClass(clz: Constructor): Constructor { | 
 |         return ComponentModel.registerClass(clz); | 
 |     } | 
 | } | 
 |  | 
 | interface SeriesModel<Opt extends SeriesOption = SeriesOption> | 
 |     extends DataFormatMixin, PaletteMixin<Opt>, DataHost { | 
 |  | 
 |     // methods that can be implemented optionally to provide to components | 
 |     /** | 
 |      * Get dimension to render shadow in dataZoom component | 
 |      */ | 
 |     getShadowDim?(): string | 
 | } | 
 | zrUtil.mixin(SeriesModel, DataFormatMixin); | 
 | zrUtil.mixin(SeriesModel, PaletteMixin); | 
 |  | 
 | export type SeriesModelConstructor = typeof SeriesModel & ExtendableConstructor; | 
 | mountExtend(SeriesModel, ComponentModel as SeriesModelConstructor); | 
 |  | 
 |  | 
 | /** | 
 |  * 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: SeriesModel): void { | 
 |     // User specified name has higher priority, otherwise it may cause | 
 |     // series can not be queried unexpectedly. | 
 |     const name = seriesModel.name; | 
 |     if (!modelUtil.isNameSpecified(seriesModel)) { | 
 |         seriesModel.name = getSeriesAutoName(seriesModel) || name; | 
 |     } | 
 | } | 
 |  | 
 | function getSeriesAutoName(seriesModel: SeriesModel): string { | 
 |     const data = seriesModel.getRawData(); | 
 |     const dataDims = data.mapDimensionsAll('seriesName'); | 
 |     const nameArr: string[] = []; | 
 |     zrUtil.each(dataDims, function (dataDim) { | 
 |         const dimInfo = data.getDimensionInfo(dataDim); | 
 |         dimInfo.displayName && nameArr.push(dimInfo.displayName); | 
 |     }); | 
 |     return nameArr.join(' '); | 
 | } | 
 |  | 
 | function dataTaskCount(context: SeriesTaskContext): number { | 
 |     return context.model.getRawData().count(); | 
 | } | 
 |  | 
 | function dataTaskReset(context: SeriesTaskContext) { | 
 |     const seriesModel = context.model; | 
 |     seriesModel.setData(seriesModel.getRawData().cloneShallow()); | 
 |     return dataTaskProgress; | 
 | } | 
 |  | 
 | function dataTaskProgress(param: StageHandlerProgressParams, context: SeriesTaskContext): void { | 
 |     // Avoid repead 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: SeriesData, seriesModel: SeriesModel): void { | 
 |     zrUtil.each(zrUtil.concatArray(data.CHANGABLE_METHODS, data.DOWNSAMPLE_METHODS), function (methodName) { | 
 |         data.wrapMethod(methodName as any, zrUtil.curry(onDataChange, seriesModel)); | 
 |     }); | 
 | } | 
 |  | 
 | function onDataChange(this: SeriesData, seriesModel: SeriesModel, newList: SeriesData): SeriesData { | 
 |     const task = getCurrentTask(seriesModel); | 
 |     if (task) { | 
 |         // Consider case: filter, selectRange | 
 |         task.setOutputEnd((newList || this).count()); | 
 |     } | 
 |     return newList; | 
 | } | 
 |  | 
 | function getCurrentTask(seriesModel: SeriesModel): GeneralTask { | 
 |     const scheduler = (seriesModel.ecModel || {}).scheduler; | 
 |     const pipeline = scheduler && scheduler.getPipeline(seriesModel.uid); | 
 |  | 
 |     if (pipeline) { | 
 |         // When pipline finished, the currrentTask keep the last | 
 |         // task (renderTask). | 
 |         let task = pipeline.currentTask; | 
 |         if (task) { | 
 |             const agentStubMap = (task as OverallTask).agentStubMap; | 
 |             if (agentStubMap) { | 
 |                 task = agentStubMap.get(seriesModel.uid); | 
 |             } | 
 |         } | 
 |         return task; | 
 |     } | 
 | } | 
 |  | 
 |  | 
 | export default SeriesModel; |