|  | /* | 
|  | * 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 zrender from 'zrender/src/zrender'; | 
|  | import { | 
|  | assert, | 
|  | each, | 
|  | isFunction, | 
|  | isObject, | 
|  | indexOf, | 
|  | bind, | 
|  | clone, | 
|  | setAsPrimitive, | 
|  | extend, | 
|  | HashMap, | 
|  | createHashMap, | 
|  | map, | 
|  | defaults, | 
|  | isDom, | 
|  | isArray, | 
|  | noop, | 
|  | isString, | 
|  | retrieve2 | 
|  | } from 'zrender/src/core/util'; | 
|  | import env from 'zrender/src/core/env'; | 
|  | import timsort from 'zrender/src/core/timsort'; | 
|  | import Eventful, { EventCallbackSingleParam } from 'zrender/src/core/Eventful'; | 
|  | import Element, { ElementEvent } from 'zrender/src/Element'; | 
|  | import GlobalModel, {QueryConditionKindA, GlobalModelSetOptionOpts} from '../model/Global'; | 
|  | import ExtensionAPI from './ExtensionAPI'; | 
|  | import CoordinateSystemManager from './CoordinateSystem'; | 
|  | import OptionManager from '../model/OptionManager'; | 
|  | import backwardCompat from '../preprocessor/backwardCompat'; | 
|  | import dataStack from '../processor/dataStack'; | 
|  | import ComponentModel from '../model/Component'; | 
|  | import SeriesModel from '../model/Series'; | 
|  | import ComponentView, {ComponentViewConstructor} from '../view/Component'; | 
|  | import ChartView, {ChartViewConstructor} from '../view/Chart'; | 
|  | import * as graphic from '../util/graphic'; | 
|  | import {getECData} from '../util/innerStore'; | 
|  | import { | 
|  | isHighDownDispatcher, | 
|  | HOVER_STATE_EMPHASIS, | 
|  | HOVER_STATE_BLUR, | 
|  | blurSeriesFromHighlightPayload, | 
|  | toggleSelectionFromPayload, | 
|  | updateSeriesElementSelection, | 
|  | getAllSelectedIndices, | 
|  | isSelectChangePayload, | 
|  | isHighDownPayload, | 
|  | HIGHLIGHT_ACTION_TYPE, | 
|  | DOWNPLAY_ACTION_TYPE, | 
|  | SELECT_ACTION_TYPE, | 
|  | UNSELECT_ACTION_TYPE, | 
|  | TOGGLE_SELECT_ACTION_TYPE, | 
|  | savePathStates, | 
|  | enterEmphasis, | 
|  | leaveEmphasis, | 
|  | leaveBlur, | 
|  | enterSelect, | 
|  | leaveSelect, | 
|  | enterBlur, | 
|  | allLeaveBlur, | 
|  | findComponentHighDownDispatchers, | 
|  | blurComponent, | 
|  | handleGlobalMouseOverForHighDown, | 
|  | handleGlobalMouseOutForHighDown | 
|  | } from '../util/states'; | 
|  | import * as modelUtil from '../util/model'; | 
|  | import {throttle} from '../util/throttle'; | 
|  | import {seriesStyleTask, dataStyleTask, dataColorPaletteTask} from '../visual/style'; | 
|  | import loadingDefault from '../loading/default'; | 
|  | import Scheduler from './Scheduler'; | 
|  | import lightTheme from '../theme/light'; | 
|  | import darkTheme from '../theme/dark'; | 
|  | import {CoordinateSystemMaster, CoordinateSystemCreator, CoordinateSystemHostModel} from '../coord/CoordinateSystem'; | 
|  | import { parseClassType } from '../util/clazz'; | 
|  | import {ECEventProcessor} from '../util/ECEventProcessor'; | 
|  | import { | 
|  | Payload, ECElement, RendererType, ECActionEvent, | 
|  | ActionHandler, ActionInfo, OptionPreprocessor, PostUpdater, | 
|  | LoadingEffect, LoadingEffectCreator, StageHandlerInternal, | 
|  | StageHandlerOverallReset, StageHandler, | 
|  | ViewRootGroup, DimensionDefinitionLoose, ECEventData, ThemeOption, | 
|  | ECBasicOption, | 
|  | ECUnitOption, | 
|  | ZRColor, | 
|  | ComponentMainType, | 
|  | ComponentSubType, | 
|  | ColorString, | 
|  | SelectChangedPayload, | 
|  | ScaleDataValue, | 
|  | ZRElementEventName, | 
|  | ECElementEvent, | 
|  | AnimationOption | 
|  | } from '../util/types'; | 
|  | import Displayable from 'zrender/src/graphic/Displayable'; | 
|  | import { seriesSymbolTask, dataSymbolTask } from '../visual/symbol'; | 
|  | import { getVisualFromData, getItemVisualFromData } from '../visual/helper'; | 
|  | import { deprecateLog, deprecateReplaceLog, error } from '../util/log'; | 
|  | import { handleLegacySelectEvents } from '../legacy/dataSelectAction'; | 
|  |  | 
|  | import { registerExternalTransform } from '../data/helper/transform'; | 
|  | import { createLocaleObject, SYSTEM_LANG, LocaleOption } from './locale'; | 
|  |  | 
|  | import type {EChartsOption} from '../export/option'; | 
|  | import { findEventDispatcher } from '../util/event'; | 
|  | import decal from '../visual/decal'; | 
|  | import CanvasPainter from 'zrender/src/canvas/Painter'; | 
|  | import SVGPainter from 'zrender/src/svg/Painter'; | 
|  | import lifecycle, { | 
|  | LifecycleEvents, | 
|  | UpdateLifecycleTransitionItem, | 
|  | UpdateLifecycleParams, | 
|  | UpdateLifecycleTransitionOpt | 
|  | } from './lifecycle'; | 
|  | import { platformApi, setPlatformAPI } from 'zrender/src/core/platform'; | 
|  | import { getImpl } from './impl'; | 
|  | import type geoSourceManager from '../coord/geo/geoSourceManager'; | 
|  |  | 
|  | declare let global: any; | 
|  |  | 
|  | type ModelFinder = modelUtil.ModelFinder; | 
|  |  | 
|  | const hasWindow = typeof window !== 'undefined'; | 
|  |  | 
|  | export const version = '5.4.0'; | 
|  |  | 
|  | export const dependencies = { | 
|  | zrender: '5.4.0' | 
|  | }; | 
|  |  | 
|  | const TEST_FRAME_REMAIN_TIME = 1; | 
|  |  | 
|  | const PRIORITY_PROCESSOR_SERIES_FILTER = 800; | 
|  | // Some data processors depends on the stack result dimension (to calculate data extent). | 
|  | // So data stack stage should be in front of data processing stage. | 
|  | const PRIORITY_PROCESSOR_DATASTACK = 900; | 
|  | // "Data filter" will block the stream, so it should be | 
|  | // put at the beginning of data processing. | 
|  | const PRIORITY_PROCESSOR_FILTER = 1000; | 
|  | const PRIORITY_PROCESSOR_DEFAULT = 2000; | 
|  | const PRIORITY_PROCESSOR_STATISTIC = 5000; | 
|  |  | 
|  | const PRIORITY_VISUAL_LAYOUT = 1000; | 
|  | const PRIORITY_VISUAL_PROGRESSIVE_LAYOUT = 1100; | 
|  | const PRIORITY_VISUAL_GLOBAL = 2000; | 
|  | const PRIORITY_VISUAL_CHART = 3000; | 
|  | const PRIORITY_VISUAL_COMPONENT = 4000; | 
|  | // Visual property in data. Greater than `PRIORITY_VISUAL_COMPONENT` to enable to | 
|  | // overwrite the viusal result of component (like `visualMap`) | 
|  | // using data item specific setting (like itemStyle.xxx on data item) | 
|  | const PRIORITY_VISUAL_CHART_DATA_CUSTOM = 4500; | 
|  | // Greater than `PRIORITY_VISUAL_CHART_DATA_CUSTOM` to enable to layout based on | 
|  | // visual result like `symbolSize`. | 
|  | const PRIORITY_VISUAL_POST_CHART_LAYOUT = 4600; | 
|  | const PRIORITY_VISUAL_BRUSH = 5000; | 
|  | const PRIORITY_VISUAL_ARIA = 6000; | 
|  | const PRIORITY_VISUAL_DECAL = 7000; | 
|  |  | 
|  | export const PRIORITY = { | 
|  | PROCESSOR: { | 
|  | FILTER: PRIORITY_PROCESSOR_FILTER, | 
|  | SERIES_FILTER: PRIORITY_PROCESSOR_SERIES_FILTER, | 
|  | STATISTIC: PRIORITY_PROCESSOR_STATISTIC | 
|  | }, | 
|  | VISUAL: { | 
|  | LAYOUT: PRIORITY_VISUAL_LAYOUT, | 
|  | PROGRESSIVE_LAYOUT: PRIORITY_VISUAL_PROGRESSIVE_LAYOUT, | 
|  | GLOBAL: PRIORITY_VISUAL_GLOBAL, | 
|  | CHART: PRIORITY_VISUAL_CHART, | 
|  | POST_CHART_LAYOUT: PRIORITY_VISUAL_POST_CHART_LAYOUT, | 
|  | COMPONENT: PRIORITY_VISUAL_COMPONENT, | 
|  | BRUSH: PRIORITY_VISUAL_BRUSH, | 
|  | CHART_ITEM: PRIORITY_VISUAL_CHART_DATA_CUSTOM, | 
|  | ARIA: PRIORITY_VISUAL_ARIA, | 
|  | DECAL: PRIORITY_VISUAL_DECAL | 
|  | } | 
|  | }; | 
|  |  | 
|  | // Main process have three entries: `setOption`, `dispatchAction` and `resize`, | 
|  | // where they must not be invoked nestedly, except the only case: invoke | 
|  | // dispatchAction with updateMethod "none" in main process. | 
|  | // This flag is used to carry out this rule. | 
|  | // All events will be triggered out side main process (i.e. when !this[IN_MAIN_PROCESS]). | 
|  | const IN_MAIN_PROCESS_KEY = '__flagInMainProcess' as const; | 
|  | const PENDING_UPDATE = '__pendingUpdate' as const; | 
|  | const STATUS_NEEDS_UPDATE_KEY = '__needsUpdateStatus' as const; | 
|  | const ACTION_REG = /^[a-zA-Z0-9_]+$/; | 
|  |  | 
|  | const CONNECT_STATUS_KEY = '__connectUpdateStatus' as const; | 
|  | const CONNECT_STATUS_PENDING = 0 as const; | 
|  | const CONNECT_STATUS_UPDATING = 1 as const; | 
|  | const CONNECT_STATUS_UPDATED = 2 as const; | 
|  | type ConnectStatus = | 
|  | typeof CONNECT_STATUS_PENDING | 
|  | | typeof CONNECT_STATUS_UPDATING | 
|  | | typeof CONNECT_STATUS_UPDATED; | 
|  |  | 
|  | export type SetOptionTransitionOpt = UpdateLifecycleTransitionOpt; | 
|  | export type SetOptionTransitionOptItem = UpdateLifecycleTransitionItem; | 
|  |  | 
|  | export interface SetOptionOpts { | 
|  | notMerge?: boolean; | 
|  | lazyUpdate?: boolean; | 
|  | silent?: boolean; | 
|  | // Rule: only `id` mapped will be merged, | 
|  | // other components of the certain `mainType` will be removed. | 
|  | replaceMerge?: GlobalModelSetOptionOpts['replaceMerge']; | 
|  | transition?: SetOptionTransitionOpt | 
|  | }; | 
|  |  | 
|  | export interface ResizeOpts { | 
|  | width?: number | 'auto', // Can be 'auto' (the same as null/undefined) | 
|  | height?: number | 'auto', // Can be 'auto' (the same as null/undefined) | 
|  | animation?: AnimationOption | 
|  | silent?: boolean // by default false. | 
|  | }; | 
|  |  | 
|  | interface PostIniter { | 
|  | (chart: EChartsType): void | 
|  | } | 
|  |  | 
|  | type EventMethodName = 'on' | 'off'; | 
|  | function createRegisterEventWithLowercaseECharts(method: EventMethodName) { | 
|  | return function (this: ECharts, ...args: any): ECharts { | 
|  | if (this.isDisposed()) { | 
|  | disposedWarning(this.id); | 
|  | return; | 
|  | } | 
|  | return toLowercaseNameAndCallEventful<ECharts>(this, method, args); | 
|  | }; | 
|  | } | 
|  | function createRegisterEventWithLowercaseMessageCenter(method: EventMethodName) { | 
|  | return function (this: MessageCenter, ...args: any): MessageCenter { | 
|  | return toLowercaseNameAndCallEventful<MessageCenter>(this, method, args); | 
|  | }; | 
|  | } | 
|  | function toLowercaseNameAndCallEventful<T>(host: T, method: EventMethodName, args: any): T { | 
|  | // `args[0]` is event name. Event name is all lowercase. | 
|  | args[0] = args[0] && args[0].toLowerCase(); | 
|  | return Eventful.prototype[method].apply(host, args) as any; | 
|  | } | 
|  |  | 
|  |  | 
|  | class MessageCenter extends Eventful {} | 
|  | const messageCenterProto = MessageCenter.prototype; | 
|  | messageCenterProto.on = createRegisterEventWithLowercaseMessageCenter('on'); | 
|  | messageCenterProto.off = createRegisterEventWithLowercaseMessageCenter('off'); | 
|  |  | 
|  | // --------------------------------------- | 
|  | // Internal method names for class ECharts | 
|  | // --------------------------------------- | 
|  | let prepare: (ecIns: ECharts) => void; | 
|  | let prepareView: (ecIns: ECharts, isComponent: boolean) => void; | 
|  | let updateDirectly: ( | 
|  | ecIns: ECharts, method: string, payload: Payload, mainType: ComponentMainType, subType?: ComponentSubType | 
|  | ) => void; | 
|  | type UpdateMethod = (this: ECharts, payload?: Payload, renderParams?: UpdateLifecycleParams) => void; | 
|  | let updateMethods: { | 
|  | prepareAndUpdate: UpdateMethod, | 
|  | update: UpdateMethod, | 
|  | updateTransform: UpdateMethod, | 
|  | updateView: UpdateMethod, | 
|  | updateVisual: UpdateMethod, | 
|  | updateLayout: UpdateMethod | 
|  | }; | 
|  | let doConvertPixel: ( | 
|  | ecIns: ECharts, | 
|  | methodName: string, | 
|  | finder: ModelFinder, | 
|  | value: (number | number[]) | (ScaleDataValue | ScaleDataValue[]) | 
|  | ) => (number | number[]); | 
|  | let updateStreamModes: (ecIns: ECharts, ecModel: GlobalModel) => void; | 
|  | let doDispatchAction: (this: ECharts, payload: Payload, silent: boolean) => void; | 
|  | let flushPendingActions: (this: ECharts, silent: boolean) => void; | 
|  | let triggerUpdatedEvent: (this: ECharts, silent: boolean) => void; | 
|  | let bindRenderedEvent: (zr: zrender.ZRenderType, ecIns: ECharts) => void; | 
|  | let bindMouseEvent: (zr: zrender.ZRenderType, ecIns: ECharts) => void; | 
|  | let render: ( | 
|  | ecIns: ECharts, ecModel: GlobalModel, api: ExtensionAPI, payload: Payload, updateParams: UpdateLifecycleParams | 
|  | ) => void; | 
|  | let renderComponents: ( | 
|  | ecIns: ECharts, ecModel: GlobalModel, api: ExtensionAPI, payload: Payload, | 
|  | updateParams: UpdateLifecycleParams, dirtyList?: ComponentView[] | 
|  | ) => void; | 
|  | let renderSeries: ( | 
|  | ecIns: ECharts, ecModel: GlobalModel, api: ExtensionAPI, payload: Payload | 'remain', | 
|  | updateParams: UpdateLifecycleParams, | 
|  | dirtyMap?: {[uid: string]: any} | 
|  | ) => void; | 
|  | let createExtensionAPI: (ecIns: ECharts) => ExtensionAPI; | 
|  | let enableConnect: (ecIns: ECharts) => void; | 
|  |  | 
|  | let markStatusToUpdate: (ecIns: ECharts) => void; | 
|  | let applyChangedStates: (ecIns: ECharts) => void; | 
|  |  | 
|  | type RenderedEventParam = { elapsedTime: number }; | 
|  | type ECEventDefinition = { | 
|  | [key in ZRElementEventName]: EventCallbackSingleParam<ECElementEvent> | 
|  | } & { | 
|  | rendered: EventCallbackSingleParam<RenderedEventParam> | 
|  | finished: () => void | boolean | 
|  | } & { | 
|  | // TODO: Use ECActionEvent | 
|  | [key: string]: (...args: unknown[]) => void | boolean | 
|  | }; | 
|  | type EChartsInitOpts = { | 
|  | locale?: string | LocaleOption, | 
|  | renderer?: RendererType, | 
|  | devicePixelRatio?: number, | 
|  | useDirtyRect?: boolean, | 
|  | useCoarsePointer?: boolean, | 
|  | pointerSize?: number, | 
|  | ssr?: boolean, | 
|  | width?: number | string, | 
|  | height?: number | string | 
|  | }; | 
|  | class ECharts extends Eventful<ECEventDefinition> { | 
|  |  | 
|  | /** | 
|  | * @readonly | 
|  | */ | 
|  | id: string; | 
|  |  | 
|  | /** | 
|  | * Group id | 
|  | * @readonly | 
|  | */ | 
|  | group: string; | 
|  |  | 
|  | private _ssr: boolean; | 
|  |  | 
|  | private _zr: zrender.ZRenderType; | 
|  |  | 
|  | private _dom: HTMLElement; | 
|  |  | 
|  | private _model: GlobalModel; | 
|  |  | 
|  | private _throttledZrFlush: zrender.ZRenderType extends {flush: infer R} ? R : never; | 
|  |  | 
|  | private _theme: ThemeOption; | 
|  |  | 
|  | private _locale: LocaleOption; | 
|  |  | 
|  | private _chartsViews: ChartView[] = []; | 
|  |  | 
|  | private _chartsMap: {[viewId: string]: ChartView} = {}; | 
|  |  | 
|  | private _componentsViews: ComponentView[] = []; | 
|  |  | 
|  | private _componentsMap: {[viewId: string]: ComponentView} = {}; | 
|  |  | 
|  | private _coordSysMgr: CoordinateSystemManager; | 
|  |  | 
|  | private _api: ExtensionAPI; | 
|  |  | 
|  | private _scheduler: Scheduler; | 
|  |  | 
|  | private _messageCenter: MessageCenter; | 
|  |  | 
|  | // Can't dispatch action during rendering procedure | 
|  | private _pendingActions: Payload[] = []; | 
|  |  | 
|  | // We use never here so ECEventProcessor will not been exposed. | 
|  | // which may include many unexpected types won't be exposed in the types to developers. | 
|  | protected _$eventProcessor: never; | 
|  |  | 
|  | private _disposed: boolean; | 
|  |  | 
|  | private _loadingFX: LoadingEffect; | 
|  |  | 
|  |  | 
|  | private [PENDING_UPDATE]: { | 
|  | silent: boolean | 
|  | updateParams: UpdateLifecycleParams | 
|  | }; | 
|  | private [IN_MAIN_PROCESS_KEY]: boolean; | 
|  | private [CONNECT_STATUS_KEY]: ConnectStatus; | 
|  | private [STATUS_NEEDS_UPDATE_KEY]: boolean; | 
|  |  | 
|  | constructor( | 
|  | dom: HTMLElement, | 
|  | // Theme name or themeOption. | 
|  | theme?: string | ThemeOption, | 
|  | opts?: EChartsInitOpts | 
|  | ) { | 
|  | super(new ECEventProcessor()); | 
|  |  | 
|  | opts = opts || {}; | 
|  |  | 
|  | // Get theme by name | 
|  | if (isString(theme)) { | 
|  | theme = themeStorage[theme] as object; | 
|  | } | 
|  |  | 
|  | this._dom = dom; | 
|  |  | 
|  | let defaultRenderer = 'canvas'; | 
|  | let defaultCoarsePointer: 'auto' | boolean = 'auto'; | 
|  | let defaultUseDirtyRect = false; | 
|  | if (__DEV__) { | 
|  | const root = ( | 
|  | /* eslint-disable-next-line */ | 
|  | hasWindow ? window : global | 
|  | ) as any; | 
|  |  | 
|  | defaultRenderer = root.__ECHARTS__DEFAULT__RENDERER__ || defaultRenderer; | 
|  |  | 
|  | defaultCoarsePointer = retrieve2(root.__ECHARTS__DEFAULT__COARSE_POINTER, defaultCoarsePointer); | 
|  |  | 
|  | const devUseDirtyRect = root.__ECHARTS__DEFAULT__USE_DIRTY_RECT__; | 
|  | defaultUseDirtyRect = devUseDirtyRect == null | 
|  | ? defaultUseDirtyRect | 
|  | : devUseDirtyRect; | 
|  | } | 
|  |  | 
|  | const zr = this._zr = zrender.init(dom, { | 
|  | renderer: opts.renderer || defaultRenderer, | 
|  | devicePixelRatio: opts.devicePixelRatio, | 
|  | width: opts.width, | 
|  | height: opts.height, | 
|  | ssr: opts.ssr, | 
|  | useDirtyRect: retrieve2(opts.useDirtyRect, defaultUseDirtyRect), | 
|  | useCoarsePointer: retrieve2(opts.useCoarsePointer, defaultCoarsePointer), | 
|  | pointerSize: opts.pointerSize | 
|  | }); | 
|  | this._ssr = opts.ssr; | 
|  |  | 
|  | // Expect 60 fps. | 
|  | this._throttledZrFlush = throttle(bind(zr.flush, zr), 17); | 
|  |  | 
|  | theme = clone(theme); | 
|  | theme && backwardCompat(theme as ECUnitOption, true); | 
|  |  | 
|  | this._theme = theme; | 
|  |  | 
|  | this._locale = createLocaleObject(opts.locale || SYSTEM_LANG); | 
|  |  | 
|  | this._coordSysMgr = new CoordinateSystemManager(); | 
|  |  | 
|  | const api = this._api = createExtensionAPI(this); | 
|  |  | 
|  | // Sort on demand | 
|  | function prioritySortFunc(a: StageHandlerInternal, b: StageHandlerInternal): number { | 
|  | return a.__prio - b.__prio; | 
|  | } | 
|  | timsort(visualFuncs, prioritySortFunc); | 
|  | timsort(dataProcessorFuncs, prioritySortFunc); | 
|  |  | 
|  | this._scheduler = new Scheduler(this, api, dataProcessorFuncs, visualFuncs); | 
|  |  | 
|  | this._messageCenter = new MessageCenter(); | 
|  |  | 
|  | // Init mouse events | 
|  | this._initEvents(); | 
|  |  | 
|  | // In case some people write `window.onresize = chart.resize` | 
|  | this.resize = bind(this.resize, this); | 
|  |  | 
|  | zr.animation.on('frame', this._onframe, this); | 
|  |  | 
|  | bindRenderedEvent(zr, this); | 
|  |  | 
|  | bindMouseEvent(zr, this); | 
|  |  | 
|  | // ECharts instance can be used as value. | 
|  | setAsPrimitive(this); | 
|  | } | 
|  |  | 
|  | private _onframe(): void { | 
|  | if (this._disposed) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | applyChangedStates(this); | 
|  |  | 
|  | const scheduler = this._scheduler; | 
|  |  | 
|  | // Lazy update | 
|  | if (this[PENDING_UPDATE]) { | 
|  | const silent = (this[PENDING_UPDATE] as any).silent; | 
|  |  | 
|  | this[IN_MAIN_PROCESS_KEY] = true; | 
|  |  | 
|  | try { | 
|  | prepare(this); | 
|  | updateMethods.update.call(this, null, this[PENDING_UPDATE].updateParams); | 
|  | } | 
|  | catch (e) { | 
|  | this[IN_MAIN_PROCESS_KEY] = false; | 
|  | this[PENDING_UPDATE] = null; | 
|  | throw e; | 
|  | } | 
|  |  | 
|  | // At present, in each frame, zrender performs: | 
|  | //   (1) animation step forward. | 
|  | //   (2) trigger('frame') (where this `_onframe` is called) | 
|  | //   (3) zrender flush (render). | 
|  | // If we do nothing here, since we use `setToFinal: true`, the step (3) above | 
|  | // will render the final state of the elements before the real animation started. | 
|  | this._zr.flush(); | 
|  |  | 
|  | this[IN_MAIN_PROCESS_KEY] = false; | 
|  | this[PENDING_UPDATE] = null; | 
|  |  | 
|  | flushPendingActions.call(this, silent); | 
|  | triggerUpdatedEvent.call(this, silent); | 
|  | } | 
|  | // Avoid do both lazy update and progress in one frame. | 
|  | else if (scheduler.unfinished) { | 
|  | // Stream progress. | 
|  | let remainTime = TEST_FRAME_REMAIN_TIME; | 
|  | const ecModel = this._model; | 
|  | const api = this._api; | 
|  | scheduler.unfinished = false; | 
|  | do { | 
|  | const startTime = +new Date(); | 
|  |  | 
|  | scheduler.performSeriesTasks(ecModel); | 
|  |  | 
|  | // Currently dataProcessorFuncs do not check threshold. | 
|  | scheduler.performDataProcessorTasks(ecModel); | 
|  |  | 
|  | updateStreamModes(this, ecModel); | 
|  |  | 
|  | // Do not update coordinate system here. Because that coord system update in | 
|  | // each frame is not a good user experience. So we follow the rule that | 
|  | // the extent of the coordinate system is determined in the first frame (the | 
|  | // frame is executed immediately after task reset. | 
|  | // this._coordSysMgr.update(ecModel, api); | 
|  |  | 
|  | // console.log('--- ec frame visual ---', remainTime); | 
|  | scheduler.performVisualTasks(ecModel); | 
|  |  | 
|  | renderSeries(this, this._model, api, 'remain', {}); | 
|  |  | 
|  | remainTime -= (+new Date() - startTime); | 
|  | } | 
|  | while (remainTime > 0 && scheduler.unfinished); | 
|  |  | 
|  | // Call flush explicitly for trigger finished event. | 
|  | if (!scheduler.unfinished) { | 
|  | this._zr.flush(); | 
|  | } | 
|  | // Else, zr flushing be ensue within the same frame, | 
|  | // because zr flushing is after onframe event. | 
|  | } | 
|  | } | 
|  |  | 
|  | getDom(): HTMLElement { | 
|  | return this._dom; | 
|  | } | 
|  |  | 
|  | getId(): string { | 
|  | return this.id; | 
|  | } | 
|  |  | 
|  | getZr(): zrender.ZRenderType { | 
|  | return this._zr; | 
|  | } | 
|  |  | 
|  | isSSR(): boolean { | 
|  | return this._ssr; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Usage: | 
|  | * chart.setOption(option, notMerge, lazyUpdate); | 
|  | * chart.setOption(option, { | 
|  | *     notMerge: ..., | 
|  | *     lazyUpdate: ..., | 
|  | *     silent: ... | 
|  | * }); | 
|  | * | 
|  | * @param opts opts or notMerge. | 
|  | * @param opts.notMerge Default `false`. | 
|  | * @param opts.lazyUpdate Default `false`. Useful when setOption frequently. | 
|  | * @param opts.silent Default `false`. | 
|  | * @param opts.replaceMerge Default undefined. | 
|  | */ | 
|  | // Expose to user full option. | 
|  | setOption<Opt extends ECBasicOption>(option: Opt, notMerge?: boolean, lazyUpdate?: boolean): void; | 
|  | setOption<Opt extends ECBasicOption>(option: Opt, opts?: SetOptionOpts): void; | 
|  | /* eslint-disable-next-line */ | 
|  | setOption<Opt extends ECBasicOption>(option: Opt, notMerge?: boolean | SetOptionOpts, lazyUpdate?: boolean): void { | 
|  | if (this[IN_MAIN_PROCESS_KEY]) { | 
|  | if (__DEV__) { | 
|  | error('`setOption` should not be called during main process.'); | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (this._disposed) { | 
|  | disposedWarning(this.id); | 
|  | return; | 
|  | } | 
|  |  | 
|  | let silent; | 
|  | let replaceMerge; | 
|  | let transitionOpt: SetOptionTransitionOpt; | 
|  | if (isObject(notMerge)) { | 
|  | lazyUpdate = notMerge.lazyUpdate; | 
|  | silent = notMerge.silent; | 
|  | replaceMerge = notMerge.replaceMerge; | 
|  | transitionOpt = notMerge.transition; | 
|  | notMerge = notMerge.notMerge; | 
|  | } | 
|  |  | 
|  | this[IN_MAIN_PROCESS_KEY] = true; | 
|  |  | 
|  | if (!this._model || notMerge) { | 
|  | const optionManager = new OptionManager(this._api); | 
|  | const theme = this._theme; | 
|  | const ecModel = this._model = new GlobalModel(); | 
|  | ecModel.scheduler = this._scheduler; | 
|  | ecModel.ssr = this._ssr; | 
|  | ecModel.init(null, null, null, theme, this._locale, optionManager); | 
|  | } | 
|  |  | 
|  | this._model.setOption(option as ECBasicOption, { replaceMerge }, optionPreprocessorFuncs); | 
|  |  | 
|  | const updateParams = { | 
|  | seriesTransition: transitionOpt, | 
|  | optionChanged: true | 
|  | } as UpdateLifecycleParams; | 
|  |  | 
|  | if (lazyUpdate) { | 
|  | this[PENDING_UPDATE] = { | 
|  | silent: silent, | 
|  | updateParams: updateParams | 
|  | }; | 
|  | this[IN_MAIN_PROCESS_KEY] = false; | 
|  |  | 
|  | // `setOption(option, {lazyMode: true})` may be called when zrender has been slept. | 
|  | // It should wake it up to make sure zrender start to render at the next frame. | 
|  | this.getZr().wakeUp(); | 
|  | } | 
|  | else { | 
|  | try { | 
|  | prepare(this); | 
|  | updateMethods.update.call(this, null, updateParams); | 
|  | } | 
|  | catch (e) { | 
|  | this[PENDING_UPDATE] = null; | 
|  | this[IN_MAIN_PROCESS_KEY] = false; | 
|  |  | 
|  | throw e; | 
|  | } | 
|  |  | 
|  | // Ensure zr refresh sychronously, and then pixel in canvas can be | 
|  | // fetched after `setOption`. | 
|  | if (!this._ssr) { | 
|  | // not use flush when using ssr mode. | 
|  | this._zr.flush(); | 
|  | } | 
|  |  | 
|  | this[PENDING_UPDATE] = null; | 
|  | this[IN_MAIN_PROCESS_KEY] = false; | 
|  |  | 
|  | flushPendingActions.call(this, silent); | 
|  | triggerUpdatedEvent.call(this, silent); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @deprecated | 
|  | */ | 
|  | private setTheme(): void { | 
|  | deprecateLog('ECharts#setTheme() is DEPRECATED in ECharts 3.0'); | 
|  | } | 
|  |  | 
|  | // We don't want developers to use getModel directly. | 
|  | private getModel(): GlobalModel { | 
|  | return this._model; | 
|  | } | 
|  |  | 
|  | getOption(): ECBasicOption { | 
|  | return this._model && this._model.getOption() as ECBasicOption; | 
|  | } | 
|  |  | 
|  | getWidth(): number { | 
|  | return this._zr.getWidth(); | 
|  | } | 
|  |  | 
|  | getHeight(): number { | 
|  | return this._zr.getHeight(); | 
|  | } | 
|  |  | 
|  | getDevicePixelRatio(): number { | 
|  | return (this._zr.painter as CanvasPainter).dpr | 
|  | /* eslint-disable-next-line */ | 
|  | || (hasWindow && window.devicePixelRatio) || 1; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Get canvas which has all thing rendered | 
|  | * @deprecated Use renderToCanvas instead. | 
|  | */ | 
|  | getRenderedCanvas(opts?: any): HTMLCanvasElement { | 
|  | if (__DEV__) { | 
|  | deprecateReplaceLog('getRenderedCanvas', 'renderToCanvas'); | 
|  | } | 
|  | return this.renderToCanvas(opts); | 
|  | } | 
|  |  | 
|  | renderToCanvas(opts?: { | 
|  | backgroundColor?: ZRColor | 
|  | pixelRatio?: number | 
|  | }): HTMLCanvasElement { | 
|  | opts = opts || {}; | 
|  | const painter = this._zr.painter; | 
|  | if (__DEV__) { | 
|  | if (painter.type !== 'canvas') { | 
|  | throw new Error('renderToCanvas can only be used in the canvas renderer.'); | 
|  | } | 
|  | } | 
|  | return (painter as CanvasPainter).getRenderedCanvas({ | 
|  | backgroundColor: (opts.backgroundColor || this._model.get('backgroundColor')) as ColorString, | 
|  | pixelRatio: opts.pixelRatio || this.getDevicePixelRatio() | 
|  | }); | 
|  | } | 
|  |  | 
|  | renderToSVGString(opts?: { | 
|  | useViewBox?: boolean | 
|  | }): string { | 
|  | opts = opts || {}; | 
|  | const painter = this._zr.painter; | 
|  | if (__DEV__) { | 
|  | if (painter.type !== 'svg') { | 
|  | throw new Error('renderToSVGString can only be used in the svg renderer.'); | 
|  | } | 
|  | } | 
|  | return (painter as SVGPainter).renderToString({ | 
|  | useViewBox: opts.useViewBox | 
|  | }); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Get svg data url | 
|  | */ | 
|  | getSvgDataURL(): string { | 
|  | if (!env.svgSupported) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | const zr = this._zr; | 
|  | const list = zr.storage.getDisplayList(); | 
|  | // Stop animations | 
|  | each(list, function (el: Element) { | 
|  | el.stopAnimation(null, true); | 
|  | }); | 
|  |  | 
|  | return (zr.painter as SVGPainter).toDataURL(); | 
|  | } | 
|  |  | 
|  | getDataURL(opts?: { | 
|  | // file type 'png' by default | 
|  | type?: 'png' | 'jpeg' | 'svg', | 
|  | pixelRatio?: number, | 
|  | backgroundColor?: ZRColor, | 
|  | // component type array | 
|  | excludeComponents?: ComponentMainType[] | 
|  | }): string { | 
|  | if (this._disposed) { | 
|  | disposedWarning(this.id); | 
|  | return; | 
|  | } | 
|  |  | 
|  | opts = opts || {}; | 
|  | const excludeComponents = opts.excludeComponents; | 
|  | const ecModel = this._model; | 
|  | const excludesComponentViews: ComponentView[] = []; | 
|  | const self = this; | 
|  |  | 
|  | each(excludeComponents, function (componentType) { | 
|  | ecModel.eachComponent({ | 
|  | mainType: componentType | 
|  | }, function (component) { | 
|  | const view = self._componentsMap[component.__viewId]; | 
|  | if (!view.group.ignore) { | 
|  | excludesComponentViews.push(view); | 
|  | view.group.ignore = true; | 
|  | } | 
|  | }); | 
|  | }); | 
|  |  | 
|  | const url = this._zr.painter.getType() === 'svg' | 
|  | ? this.getSvgDataURL() | 
|  | : this.renderToCanvas(opts).toDataURL( | 
|  | 'image/' + (opts && opts.type || 'png') | 
|  | ); | 
|  |  | 
|  | each(excludesComponentViews, function (view) { | 
|  | view.group.ignore = false; | 
|  | }); | 
|  |  | 
|  | return url; | 
|  | } | 
|  |  | 
|  | getConnectedDataURL(opts?: { | 
|  | // file type 'png' by default | 
|  | type?: 'png' | 'jpeg' | 'svg', | 
|  | pixelRatio?: number, | 
|  | backgroundColor?: ZRColor, | 
|  | connectedBackgroundColor?: ZRColor | 
|  | excludeComponents?: string[] | 
|  | }): string { | 
|  | if (this._disposed) { | 
|  | disposedWarning(this.id); | 
|  | return; | 
|  | } | 
|  |  | 
|  | const isSvg = opts.type === 'svg'; | 
|  | const groupId = this.group; | 
|  | const mathMin = Math.min; | 
|  | const mathMax = Math.max; | 
|  | const MAX_NUMBER = Infinity; | 
|  | if (connectedGroups[groupId]) { | 
|  | let left = MAX_NUMBER; | 
|  | let top = MAX_NUMBER; | 
|  | let right = -MAX_NUMBER; | 
|  | let bottom = -MAX_NUMBER; | 
|  | const canvasList: {dom: HTMLCanvasElement | string, left: number, top: number}[] = []; | 
|  | const dpr = (opts && opts.pixelRatio) || this.getDevicePixelRatio(); | 
|  |  | 
|  | each(instances, function (chart, id) { | 
|  | if (chart.group === groupId) { | 
|  | const canvas = isSvg | 
|  | ? (chart.getZr().painter as SVGPainter).getSvgDom().innerHTML | 
|  | : chart.renderToCanvas(clone(opts)); | 
|  | const boundingRect = chart.getDom().getBoundingClientRect(); | 
|  | left = mathMin(boundingRect.left, left); | 
|  | top = mathMin(boundingRect.top, top); | 
|  | right = mathMax(boundingRect.right, right); | 
|  | bottom = mathMax(boundingRect.bottom, bottom); | 
|  | canvasList.push({ | 
|  | dom: canvas, | 
|  | left: boundingRect.left, | 
|  | top: boundingRect.top | 
|  | }); | 
|  | } | 
|  | }); | 
|  |  | 
|  | left *= dpr; | 
|  | top *= dpr; | 
|  | right *= dpr; | 
|  | bottom *= dpr; | 
|  | const width = right - left; | 
|  | const height = bottom - top; | 
|  | const targetCanvas = platformApi.createCanvas(); | 
|  | const zr = zrender.init(targetCanvas, { | 
|  | renderer: isSvg ? 'svg' : 'canvas' | 
|  | }); | 
|  | zr.resize({ | 
|  | width: width, | 
|  | height: height | 
|  | }); | 
|  |  | 
|  | if (isSvg) { | 
|  | let content = ''; | 
|  | each(canvasList, function (item) { | 
|  | const x = item.left - left; | 
|  | const y = item.top - top; | 
|  | content += '<g transform="translate(' + x + ',' | 
|  | + y + ')">' + item.dom + '</g>'; | 
|  | }); | 
|  | (zr.painter as SVGPainter).getSvgRoot().innerHTML = content; | 
|  |  | 
|  | if (opts.connectedBackgroundColor) { | 
|  | (zr.painter as SVGPainter).setBackgroundColor(opts.connectedBackgroundColor as string); | 
|  | } | 
|  |  | 
|  | zr.refreshImmediately(); | 
|  | return (zr.painter as SVGPainter).toDataURL(); | 
|  | } | 
|  | else { | 
|  | // Background between the charts | 
|  | if (opts.connectedBackgroundColor) { | 
|  | zr.add(new graphic.Rect({ | 
|  | shape: { | 
|  | x: 0, | 
|  | y: 0, | 
|  | width: width, | 
|  | height: height | 
|  | }, | 
|  | style: { | 
|  | fill: opts.connectedBackgroundColor | 
|  | } | 
|  | })); | 
|  | } | 
|  |  | 
|  | each(canvasList, function (item) { | 
|  | const img = new graphic.Image({ | 
|  | style: { | 
|  | x: item.left * dpr - left, | 
|  | y: item.top * dpr - top, | 
|  | image: item.dom | 
|  | } | 
|  | }); | 
|  | zr.add(img); | 
|  | }); | 
|  | zr.refreshImmediately(); | 
|  |  | 
|  | return targetCanvas.toDataURL('image/' + (opts && opts.type || 'png')); | 
|  | } | 
|  | } | 
|  | else { | 
|  | return this.getDataURL(opts); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Convert from logical coordinate system to pixel coordinate system. | 
|  | * See CoordinateSystem#convertToPixel. | 
|  | */ | 
|  | convertToPixel(finder: ModelFinder, value: ScaleDataValue): number; | 
|  | convertToPixel(finder: ModelFinder, value: ScaleDataValue[]): number[]; | 
|  | convertToPixel(finder: ModelFinder, value: ScaleDataValue | ScaleDataValue[]): number | number[] { | 
|  | return doConvertPixel(this, 'convertToPixel', finder, value); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Convert from pixel coordinate system to logical coordinate system. | 
|  | * See CoordinateSystem#convertFromPixel. | 
|  | */ | 
|  | convertFromPixel(finder: ModelFinder, value: number): number; | 
|  | convertFromPixel(finder: ModelFinder, value: number[]): number[]; | 
|  | convertFromPixel(finder: ModelFinder, value: number | number[]): number | number[] { | 
|  | return doConvertPixel(this, 'convertFromPixel', finder, value); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Is the specified coordinate systems or components contain the given pixel point. | 
|  | * @param {Array|number} value | 
|  | * @return {boolean} result | 
|  | */ | 
|  | containPixel(finder: ModelFinder, value: number[]): boolean { | 
|  | if (this._disposed) { | 
|  | disposedWarning(this.id); | 
|  | return; | 
|  | } | 
|  |  | 
|  | const ecModel = this._model; | 
|  | let result: boolean; | 
|  |  | 
|  | const findResult = modelUtil.parseFinder(ecModel, finder); | 
|  |  | 
|  | each(findResult, function (models, key) { | 
|  | key.indexOf('Models') >= 0 && each(models as ComponentModel[], function (model) { | 
|  | const coordSys = (model as CoordinateSystemHostModel).coordinateSystem; | 
|  | if (coordSys && coordSys.containPoint) { | 
|  | result = result || !!coordSys.containPoint(value); | 
|  | } | 
|  | else if (key === 'seriesModels') { | 
|  | const view = this._chartsMap[model.__viewId]; | 
|  | if (view && view.containPoint) { | 
|  | result = result || view.containPoint(value, model as SeriesModel); | 
|  | } | 
|  | else { | 
|  | if (__DEV__) { | 
|  | console.warn(key + ': ' + (view | 
|  | ? 'The found component do not support containPoint.' | 
|  | : 'No view mapping to the found component.' | 
|  | )); | 
|  | } | 
|  | } | 
|  | } | 
|  | else { | 
|  | if (__DEV__) { | 
|  | console.warn(key + ': containPoint is not supported'); | 
|  | } | 
|  | } | 
|  | }, this); | 
|  | }, this); | 
|  |  | 
|  | return !!result; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Get visual from series or data. | 
|  | * @param finder | 
|  | *        If string, e.g., 'series', means {seriesIndex: 0}. | 
|  | *        If Object, could contain some of these properties below: | 
|  | *        { | 
|  | *            seriesIndex / seriesId / seriesName, | 
|  | *            dataIndex / dataIndexInside | 
|  | *        } | 
|  | *        If dataIndex is not specified, series visual will be fetched, | 
|  | *        but not data item visual. | 
|  | *        If all of seriesIndex, seriesId, seriesName are not specified, | 
|  | *        visual will be fetched from first series. | 
|  | * @param visualType 'color', 'symbol', 'symbolSize' | 
|  | */ | 
|  | getVisual(finder: ModelFinder, visualType: string) { | 
|  | const ecModel = this._model; | 
|  |  | 
|  | const parsedFinder = modelUtil.parseFinder(ecModel, finder, { | 
|  | defaultMainType: 'series' | 
|  | }) as modelUtil.ParsedModelFinderKnown; | 
|  |  | 
|  | const seriesModel = parsedFinder.seriesModel; | 
|  |  | 
|  | if (__DEV__) { | 
|  | if (!seriesModel) { | 
|  | console.warn('There is no specified seires model'); | 
|  | } | 
|  | } | 
|  |  | 
|  | const data = seriesModel.getData(); | 
|  |  | 
|  | const dataIndexInside = parsedFinder.hasOwnProperty('dataIndexInside') | 
|  | ? parsedFinder.dataIndexInside | 
|  | : parsedFinder.hasOwnProperty('dataIndex') | 
|  | ? data.indexOfRawIndex(parsedFinder.dataIndex) | 
|  | : null; | 
|  |  | 
|  | return dataIndexInside != null | 
|  | ? getItemVisualFromData(data, dataIndexInside, visualType) | 
|  | : getVisualFromData(data, visualType); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Get view of corresponding component model | 
|  | */ | 
|  | private getViewOfComponentModel(componentModel: ComponentModel): ComponentView { | 
|  | return this._componentsMap[componentModel.__viewId]; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Get view of corresponding series model | 
|  | */ | 
|  | private getViewOfSeriesModel(seriesModel: SeriesModel): ChartView { | 
|  | return this._chartsMap[seriesModel.__viewId]; | 
|  | } | 
|  |  | 
|  |  | 
|  | private _initEvents(): void { | 
|  | each(MOUSE_EVENT_NAMES, (eveName) => { | 
|  | const handler = (e: ElementEvent) => { | 
|  | const ecModel = this.getModel(); | 
|  | const el = e.target; | 
|  | let params: ECElementEvent; | 
|  | const isGlobalOut = eveName === 'globalout'; | 
|  | // no e.target when 'globalout'. | 
|  | if (isGlobalOut) { | 
|  | params = {} as ECElementEvent; | 
|  | } | 
|  | else { | 
|  | el && findEventDispatcher(el, (parent) => { | 
|  | const ecData = getECData(parent); | 
|  | if (ecData && ecData.dataIndex != null) { | 
|  | const dataModel = ecData.dataModel || ecModel.getSeriesByIndex(ecData.seriesIndex); | 
|  | params = ( | 
|  | dataModel && dataModel.getDataParams(ecData.dataIndex, ecData.dataType) || {} | 
|  | ) as ECElementEvent; | 
|  | return true; | 
|  | } | 
|  | // If element has custom eventData of components | 
|  | else if (ecData.eventData) { | 
|  | params = extend({}, ecData.eventData) as ECElementEvent; | 
|  | return true; | 
|  | } | 
|  | }, true); | 
|  | } | 
|  |  | 
|  | // Contract: if params prepared in mouse event, | 
|  | // these properties must be specified: | 
|  | // { | 
|  | //    componentType: string (component main type) | 
|  | //    componentIndex: number | 
|  | // } | 
|  | // Otherwise event query can not work. | 
|  |  | 
|  | if (params) { | 
|  | let componentType = params.componentType; | 
|  | let componentIndex = params.componentIndex; | 
|  | // Special handling for historic reason: when trigger by | 
|  | // markLine/markPoint/markArea, the componentType is | 
|  | // 'markLine'/'markPoint'/'markArea', but we should better | 
|  | // enable them to be queried by seriesIndex, since their | 
|  | // option is set in each series. | 
|  | if (componentType === 'markLine' | 
|  | || componentType === 'markPoint' | 
|  | || componentType === 'markArea' | 
|  | ) { | 
|  | componentType = 'series'; | 
|  | componentIndex = params.seriesIndex; | 
|  | } | 
|  | const model = componentType && componentIndex != null | 
|  | && ecModel.getComponent(componentType, componentIndex); | 
|  | const view = model && this[ | 
|  | model.mainType === 'series' ? '_chartsMap' : '_componentsMap' | 
|  | ][model.__viewId]; | 
|  |  | 
|  | if (__DEV__) { | 
|  | // `event.componentType` and `event[componentTpype + 'Index']` must not | 
|  | // be missed, otherwise there is no way to distinguish source component. | 
|  | // See `dataFormat.getDataParams`. | 
|  | if (!isGlobalOut && !(model && view)) { | 
|  | console.warn('model or view can not be found by params'); | 
|  | } | 
|  | } | 
|  |  | 
|  | params.event = e; | 
|  | params.type = eveName; | 
|  |  | 
|  | (this._$eventProcessor as ECEventProcessor).eventInfo = { | 
|  | targetEl: el, | 
|  | packedEvent: params, | 
|  | model: model, | 
|  | view: view | 
|  | }; | 
|  |  | 
|  | this.trigger(eveName, params); | 
|  | } | 
|  | }; | 
|  | // Consider that some component (like tooltip, brush, ...) | 
|  | // register zr event handler, but user event handler might | 
|  | // do anything, such as call `setOption` or `dispatchAction`, | 
|  | // which probably update any of the content and probably | 
|  | // cause problem if it is called previous other inner handlers. | 
|  | (handler as any).zrEventfulCallAtLast = true; | 
|  | this._zr.on(eveName, handler, this); | 
|  | }); | 
|  |  | 
|  | each(eventActionMap, (actionType, eventType) => { | 
|  | this._messageCenter.on(eventType, function (event: Payload) { | 
|  | (this as any).trigger(eventType, event); | 
|  | }, this); | 
|  | }); | 
|  |  | 
|  | // Extra events | 
|  | // TODO register? | 
|  | each( | 
|  | ['selectchanged'], | 
|  | (eventType) => { | 
|  | this._messageCenter.on(eventType, function (event: Payload) { | 
|  | (this as any).trigger(eventType, event); | 
|  | }, this); | 
|  | } | 
|  | ); | 
|  |  | 
|  | handleLegacySelectEvents(this._messageCenter, this, this._api); | 
|  | } | 
|  |  | 
|  | isDisposed(): boolean { | 
|  | return this._disposed; | 
|  | } | 
|  |  | 
|  | clear(): void { | 
|  | if (this._disposed) { | 
|  | disposedWarning(this.id); | 
|  | return; | 
|  | } | 
|  | this.setOption({ series: [] } as EChartsOption, true); | 
|  | } | 
|  |  | 
|  | dispose(): void { | 
|  | if (this._disposed) { | 
|  | disposedWarning(this.id); | 
|  | return; | 
|  | } | 
|  | this._disposed = true; | 
|  |  | 
|  | const dom = this.getDom(); | 
|  | if (dom) { | 
|  | modelUtil.setAttribute(this.getDom(), DOM_ATTRIBUTE_KEY, ''); | 
|  | } | 
|  |  | 
|  | const chart = this; | 
|  | const api = chart._api; | 
|  | const ecModel = chart._model; | 
|  |  | 
|  | each(chart._componentsViews, function (component) { | 
|  | component.dispose(ecModel, api); | 
|  | }); | 
|  | each(chart._chartsViews, function (chart) { | 
|  | chart.dispose(ecModel, api); | 
|  | }); | 
|  |  | 
|  | // Dispose after all views disposed | 
|  | chart._zr.dispose(); | 
|  |  | 
|  | // Set properties to null. | 
|  | // To reduce the memory cost in case the top code still holds this instance unexpectedly. | 
|  | chart._dom = | 
|  | chart._model = | 
|  | chart._chartsMap = | 
|  | chart._componentsMap = | 
|  | chart._chartsViews = | 
|  | chart._componentsViews = | 
|  | chart._scheduler = | 
|  | chart._api = | 
|  | chart._zr = | 
|  | chart._throttledZrFlush = | 
|  | chart._theme = | 
|  | chart._coordSysMgr = | 
|  | chart._messageCenter = null; | 
|  |  | 
|  | delete instances[chart.id]; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Resize the chart | 
|  | */ | 
|  | resize(opts?: ResizeOpts): void { | 
|  | if (this[IN_MAIN_PROCESS_KEY]) { | 
|  | if (__DEV__) { | 
|  | error('`resize` should not be called during main process.'); | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (this._disposed) { | 
|  | disposedWarning(this.id); | 
|  | return; | 
|  | } | 
|  |  | 
|  | this._zr.resize(opts); | 
|  |  | 
|  | const ecModel = this._model; | 
|  |  | 
|  | // Resize loading effect | 
|  | this._loadingFX && this._loadingFX.resize(); | 
|  |  | 
|  | if (!ecModel) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | let needPrepare = ecModel.resetOption('media'); | 
|  |  | 
|  | let silent = opts && opts.silent; | 
|  |  | 
|  | // There is some real cases that: | 
|  | // chart.setOption(option, { lazyUpdate: true }); | 
|  | // chart.resize(); | 
|  | if (this[PENDING_UPDATE]) { | 
|  | if (silent == null) { | 
|  | silent = (this[PENDING_UPDATE] as any).silent; | 
|  | } | 
|  | needPrepare = true; | 
|  | this[PENDING_UPDATE] = null; | 
|  | } | 
|  |  | 
|  | this[IN_MAIN_PROCESS_KEY] = true; | 
|  |  | 
|  | try { | 
|  | needPrepare && prepare(this); | 
|  | updateMethods.update.call(this, { | 
|  | type: 'resize', | 
|  | animation: extend({ | 
|  | // Disable animation | 
|  | duration: 0 | 
|  | }, opts && opts.animation) | 
|  | }); | 
|  | } | 
|  | catch (e) { | 
|  | this[IN_MAIN_PROCESS_KEY] = false; | 
|  | throw e; | 
|  | } | 
|  |  | 
|  | this[IN_MAIN_PROCESS_KEY] = false; | 
|  |  | 
|  | flushPendingActions.call(this, silent); | 
|  |  | 
|  | triggerUpdatedEvent.call(this, silent); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Show loading effect | 
|  | * @param name 'default' by default | 
|  | * @param cfg cfg of registered loading effect | 
|  | */ | 
|  | showLoading(cfg?: object): void; | 
|  | showLoading(name?: string, cfg?: object): void; | 
|  | showLoading(name?: string | object, cfg?: object): void { | 
|  | if (this._disposed) { | 
|  | disposedWarning(this.id); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (isObject(name)) { | 
|  | cfg = name as object; | 
|  | name = ''; | 
|  | } | 
|  | name = name || 'default'; | 
|  |  | 
|  | this.hideLoading(); | 
|  | if (!loadingEffects[name]) { | 
|  | if (__DEV__) { | 
|  | console.warn('Loading effects ' + name + ' not exists.'); | 
|  | } | 
|  | return; | 
|  | } | 
|  | const el = loadingEffects[name](this._api, cfg); | 
|  | const zr = this._zr; | 
|  | this._loadingFX = el; | 
|  |  | 
|  | zr.add(el); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Hide loading effect | 
|  | */ | 
|  | hideLoading(): void { | 
|  | if (this._disposed) { | 
|  | disposedWarning(this.id); | 
|  | return; | 
|  | } | 
|  |  | 
|  | this._loadingFX && this._zr.remove(this._loadingFX); | 
|  | this._loadingFX = null; | 
|  | } | 
|  |  | 
|  | makeActionFromEvent(eventObj: ECActionEvent): Payload { | 
|  | const payload = extend({}, eventObj) as Payload; | 
|  | payload.type = eventActionMap[eventObj.type]; | 
|  | return payload; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @param opt If pass boolean, means opt.silent | 
|  | * @param opt.silent Default `false`. Whether trigger events. | 
|  | * @param opt.flush Default `undefined`. | 
|  | *        true: Flush immediately, and then pixel in canvas can be fetched | 
|  | *            immediately. Caution: it might affect performance. | 
|  | *        false: Not flush. | 
|  | *        undefined: Auto decide whether perform flush. | 
|  | */ | 
|  | dispatchAction( | 
|  | payload: Payload, | 
|  | opt?: boolean | { | 
|  | silent?: boolean, | 
|  | flush?: boolean | undefined | 
|  | } | 
|  | ): void { | 
|  | if (this._disposed) { | 
|  | disposedWarning(this.id); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (!isObject(opt)) { | 
|  | opt = {silent: !!opt}; | 
|  | } | 
|  |  | 
|  | if (!actions[payload.type]) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Avoid dispatch action before setOption. Especially in `connect`. | 
|  | if (!this._model) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | // May dispatchAction in rendering procedure | 
|  | if (this[IN_MAIN_PROCESS_KEY]) { | 
|  | this._pendingActions.push(payload); | 
|  | return; | 
|  | } | 
|  |  | 
|  | const silent = opt.silent; | 
|  | doDispatchAction.call(this, payload, silent); | 
|  |  | 
|  | const flush = opt.flush; | 
|  | if (flush) { | 
|  | this._zr.flush(); | 
|  | } | 
|  | else if (flush !== false && env.browser.weChat) { | 
|  | // In WeChat embedded browser, `requestAnimationFrame` and `setInterval` | 
|  | // hang when sliding page (on touch event), which cause that zr does not | 
|  | // refresh until user interaction finished, which is not expected. | 
|  | // But `dispatchAction` may be called too frequently when pan on touch | 
|  | // screen, which impacts performance if do not throttle them. | 
|  | this._throttledZrFlush(); | 
|  | } | 
|  |  | 
|  | flushPendingActions.call(this, silent); | 
|  |  | 
|  | triggerUpdatedEvent.call(this, silent); | 
|  | } | 
|  |  | 
|  | updateLabelLayout() { | 
|  | lifecycle.trigger('series:layoutlabels', this._model, this._api, { | 
|  | // Not adding series labels. | 
|  | // TODO | 
|  | updatedSeries: [] | 
|  | }); | 
|  | } | 
|  |  | 
|  | appendData(params: { | 
|  | seriesIndex: number, | 
|  | data: any | 
|  | }): void { | 
|  | if (this._disposed) { | 
|  | disposedWarning(this.id); | 
|  | return; | 
|  | } | 
|  |  | 
|  | const seriesIndex = params.seriesIndex; | 
|  | const ecModel = this.getModel(); | 
|  | const seriesModel = ecModel.getSeriesByIndex(seriesIndex) as SeriesModel; | 
|  |  | 
|  | if (__DEV__) { | 
|  | assert(params.data && seriesModel); | 
|  | } | 
|  |  | 
|  | seriesModel.appendData(params); | 
|  |  | 
|  | // Note: `appendData` does not support that update extent of coordinate | 
|  | // system, util some scenario require that. In the expected usage of | 
|  | // `appendData`, the initial extent of coordinate system should better | 
|  | // be fixed by axis `min`/`max` setting or initial data, otherwise if | 
|  | // the extent changed while `appendData`, the location of the painted | 
|  | // graphic elements have to be changed, which make the usage of | 
|  | // `appendData` meaningless. | 
|  |  | 
|  | this._scheduler.unfinished = true; | 
|  |  | 
|  | this.getZr().wakeUp(); | 
|  | } | 
|  |  | 
|  |  | 
|  | // A work around for no `internal` modifier in ts yet but | 
|  | // need to strictly hide private methods to JS users. | 
|  | private static internalField = (function () { | 
|  |  | 
|  | prepare = function (ecIns: ECharts): void { | 
|  | const scheduler = ecIns._scheduler; | 
|  |  | 
|  | scheduler.restorePipelines(ecIns._model); | 
|  | scheduler.prepareStageTasks(); | 
|  |  | 
|  | prepareView(ecIns, true); | 
|  | prepareView(ecIns, false); | 
|  |  | 
|  | scheduler.plan(); | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Prepare view instances of charts and components | 
|  | */ | 
|  | prepareView = function (ecIns: ECharts, isComponent: boolean): void { | 
|  | const ecModel = ecIns._model; | 
|  | const scheduler = ecIns._scheduler; | 
|  | const viewList = isComponent ? ecIns._componentsViews : ecIns._chartsViews; | 
|  | const viewMap = isComponent ? ecIns._componentsMap : ecIns._chartsMap; | 
|  | const zr = ecIns._zr; | 
|  | const api = ecIns._api; | 
|  |  | 
|  | for (let i = 0; i < viewList.length; i++) { | 
|  | viewList[i].__alive = false; | 
|  | } | 
|  |  | 
|  | isComponent | 
|  | ? ecModel.eachComponent(function (componentType, model) { | 
|  | componentType !== 'series' && doPrepare(model); | 
|  | }) | 
|  | : ecModel.eachSeries(doPrepare); | 
|  |  | 
|  | function doPrepare(model: ComponentModel): void { | 
|  | // By default view will be reused if possible for the case that `setOption` with "notMerge" | 
|  | // mode and need to enable transition animation. (Usually, when they have the same id, or | 
|  | // especially no id but have the same type & name & index. See the `model.id` generation | 
|  | // rule in `makeIdAndName` and `viewId` generation rule here). | 
|  | // But in `replaceMerge` mode, this feature should be able to disabled when it is clear that | 
|  | // the new model has nothing to do with the old model. | 
|  | const requireNewView = model.__requireNewView; | 
|  | // This command should not work twice. | 
|  | model.__requireNewView = false; | 
|  | // Consider: id same and type changed. | 
|  | const viewId = '_ec_' + model.id + '_' + model.type; | 
|  | let view = !requireNewView && viewMap[viewId]; | 
|  | if (!view) { | 
|  | const classType = parseClassType(model.type); | 
|  | const Clazz = isComponent | 
|  | ? (ComponentView as ComponentViewConstructor).getClass(classType.main, classType.sub) | 
|  | : ( | 
|  | // FIXME:TS | 
|  | // (ChartView as ChartViewConstructor).getClass('series', classType.sub) | 
|  | // For backward compat, still support a chart type declared as only subType | 
|  | // like "liquidfill", but recommend "series.liquidfill" | 
|  | // But need a base class to make a type series. | 
|  | (ChartView as ChartViewConstructor).getClass(classType.sub) | 
|  | ); | 
|  |  | 
|  | if (__DEV__) { | 
|  | assert(Clazz, classType.sub + ' does not exist.'); | 
|  | } | 
|  |  | 
|  | view = new Clazz(); | 
|  | view.init(ecModel, api); | 
|  | viewMap[viewId] = view; | 
|  | viewList.push(view as any); | 
|  | zr.add(view.group); | 
|  | } | 
|  |  | 
|  | model.__viewId = view.__id = viewId; | 
|  | view.__alive = true; | 
|  | view.__model = model; | 
|  | view.group.__ecComponentInfo = { | 
|  | mainType: model.mainType, | 
|  | index: model.componentIndex | 
|  | }; | 
|  | !isComponent && scheduler.prepareView( | 
|  | view as ChartView, model as SeriesModel, ecModel, api | 
|  | ); | 
|  | } | 
|  |  | 
|  | for (let i = 0; i < viewList.length;) { | 
|  | const view = viewList[i]; | 
|  | if (!view.__alive) { | 
|  | !isComponent && (view as ChartView).renderTask.dispose(); | 
|  | zr.remove(view.group); | 
|  | view.dispose(ecModel, api); | 
|  | viewList.splice(i, 1); | 
|  | if (viewMap[view.__id] === view) { | 
|  | delete viewMap[view.__id]; | 
|  | } | 
|  | view.__id = view.group.__ecComponentInfo = null; | 
|  | } | 
|  | else { | 
|  | i++; | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | updateDirectly = function ( | 
|  | ecIns: ECharts, | 
|  | method: string, | 
|  | payload: Payload, | 
|  | mainType: ComponentMainType, | 
|  | subType?: ComponentSubType | 
|  | ): void { | 
|  | const ecModel = ecIns._model; | 
|  |  | 
|  | ecModel.setUpdatePayload(payload); | 
|  |  | 
|  | // broadcast | 
|  | if (!mainType) { | 
|  | // FIXME | 
|  | // Chart will not be update directly here, except set dirty. | 
|  | // But there is no such scenario now. | 
|  | each([].concat(ecIns._componentsViews).concat(ecIns._chartsViews), callView); | 
|  | return; | 
|  | } | 
|  |  | 
|  | const query: QueryConditionKindA['query'] = {}; | 
|  | query[mainType + 'Id'] = payload[mainType + 'Id']; | 
|  | query[mainType + 'Index'] = payload[mainType + 'Index']; | 
|  | query[mainType + 'Name'] = payload[mainType + 'Name']; | 
|  |  | 
|  | const condition = {mainType: mainType, query: query} as QueryConditionKindA; | 
|  | subType && (condition.subType = subType); // subType may be '' by parseClassType; | 
|  |  | 
|  | const excludeSeriesId = payload.excludeSeriesId; | 
|  | let excludeSeriesIdMap: HashMap<true, string>; | 
|  | if (excludeSeriesId != null) { | 
|  | excludeSeriesIdMap = createHashMap(); | 
|  | each(modelUtil.normalizeToArray(excludeSeriesId), id => { | 
|  | const modelId = modelUtil.convertOptionIdName(id, null); | 
|  | if (modelId != null) { | 
|  | excludeSeriesIdMap.set(modelId, true); | 
|  | } | 
|  | }); | 
|  | } | 
|  |  | 
|  | // If dispatchAction before setOption, do nothing. | 
|  | ecModel && ecModel.eachComponent(condition, function (model) { | 
|  | const isExcluded = excludeSeriesIdMap && excludeSeriesIdMap.get(model.id) !== null; | 
|  | if (isExcluded) { | 
|  | return; | 
|  | }; | 
|  | if (isHighDownPayload(payload)) { | 
|  | if (model instanceof SeriesModel) { | 
|  | if ( | 
|  | payload.type === HIGHLIGHT_ACTION_TYPE | 
|  | && !payload.notBlur && !model.get(['emphasis', 'disabled']) | 
|  | ) { | 
|  | blurSeriesFromHighlightPayload(model, payload, ecIns._api); | 
|  | } | 
|  | } | 
|  | else { | 
|  | const { focusSelf, dispatchers } = findComponentHighDownDispatchers( | 
|  | model.mainType, model.componentIndex, payload.name, ecIns._api | 
|  | ); | 
|  | if (payload.type === HIGHLIGHT_ACTION_TYPE && focusSelf && !payload.notBlur) { | 
|  | blurComponent(model.mainType, model.componentIndex, ecIns._api); | 
|  | } | 
|  | // PENDING: | 
|  | // Whether to put this "enter emphasis" code in `ComponentView`, | 
|  | // which will be the same as `ChartView` but might be not necessary | 
|  | // and will be far from this logic. | 
|  | if (dispatchers) { | 
|  | each(dispatchers, dispatcher => { | 
|  | payload.type === HIGHLIGHT_ACTION_TYPE | 
|  | ? enterEmphasis(dispatcher) | 
|  | : leaveEmphasis(dispatcher); | 
|  | }); | 
|  | } | 
|  | } | 
|  | } | 
|  | else if (isSelectChangePayload(payload)) { | 
|  | // TODO geo | 
|  | if (model instanceof SeriesModel) { | 
|  | toggleSelectionFromPayload(model, payload, ecIns._api); | 
|  | updateSeriesElementSelection(model); | 
|  | markStatusToUpdate(ecIns); | 
|  | } | 
|  | } | 
|  | }, ecIns); | 
|  |  | 
|  | ecModel && ecModel.eachComponent(condition, function (model) { | 
|  | const isExcluded = excludeSeriesIdMap && excludeSeriesIdMap.get(model.id) !== null; | 
|  | if (isExcluded) { | 
|  | return; | 
|  | }; | 
|  | callView(ecIns[ | 
|  | mainType === 'series' ? '_chartsMap' : '_componentsMap' | 
|  | ][model.__viewId]); | 
|  | }, ecIns); | 
|  |  | 
|  | function callView(view: ComponentView | ChartView) { | 
|  | view && view.__alive && (view as any)[method] && (view as any)[method]( | 
|  | view.__model, ecModel, ecIns._api, payload | 
|  | ); | 
|  | } | 
|  | }; | 
|  |  | 
|  | updateMethods = { | 
|  |  | 
|  | prepareAndUpdate(this: ECharts, payload: Payload): void { | 
|  | prepare(this); | 
|  | updateMethods.update.call(this, payload, { | 
|  | // Needs to mark option changed if newOption is given. | 
|  | // It's from MagicType. | 
|  | // TODO If use a separate flag optionChanged in payload? | 
|  | optionChanged: payload.newOption != null | 
|  | }); | 
|  | }, | 
|  |  | 
|  | update(this: ECharts, payload: Payload, updateParams: UpdateLifecycleParams): void { | 
|  | const ecModel = this._model; | 
|  | const api = this._api; | 
|  | const zr = this._zr; | 
|  | const coordSysMgr = this._coordSysMgr; | 
|  | const scheduler = this._scheduler; | 
|  |  | 
|  | // update before setOption | 
|  | if (!ecModel) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | ecModel.setUpdatePayload(payload); | 
|  |  | 
|  | scheduler.restoreData(ecModel, payload); | 
|  |  | 
|  | scheduler.performSeriesTasks(ecModel); | 
|  |  | 
|  | // TODO | 
|  | // Save total ecModel here for undo/redo (after restoring data and before processing data). | 
|  | // Undo (restoration of total ecModel) can be carried out in 'action' or outside API call. | 
|  |  | 
|  | // Create new coordinate system each update | 
|  | // In LineView may save the old coordinate system and use it to get the original point. | 
|  | coordSysMgr.create(ecModel, api); | 
|  |  | 
|  | scheduler.performDataProcessorTasks(ecModel, payload); | 
|  |  | 
|  | // Current stream render is not supported in data process. So we can update | 
|  | // stream modes after data processing, where the filtered data is used to | 
|  | // determine whether to use progressive rendering. | 
|  | updateStreamModes(this, ecModel); | 
|  |  | 
|  | // We update stream modes before coordinate system updated, then the modes info | 
|  | // can be fetched when coord sys updating (consider the barGrid extent fix). But | 
|  | // the drawback is the full coord info can not be fetched. Fortunately this full | 
|  | // coord is not required in stream mode updater currently. | 
|  | coordSysMgr.update(ecModel, api); | 
|  |  | 
|  | clearColorPalette(ecModel); | 
|  | scheduler.performVisualTasks(ecModel, payload); | 
|  |  | 
|  | render(this, ecModel, api, payload, updateParams); | 
|  |  | 
|  | // Set background | 
|  | const backgroundColor = ecModel.get('backgroundColor') || 'transparent'; | 
|  | const darkMode = ecModel.get('darkMode'); | 
|  |  | 
|  | zr.setBackgroundColor(backgroundColor); | 
|  |  | 
|  | // Force set dark mode. | 
|  | if (darkMode != null && darkMode !== 'auto') { | 
|  | zr.setDarkMode(darkMode); | 
|  | } | 
|  |  | 
|  | lifecycle.trigger('afterupdate', ecModel, api); | 
|  | }, | 
|  |  | 
|  | updateTransform(this: ECharts, payload: Payload): void { | 
|  | const ecModel = this._model; | 
|  | const api = this._api; | 
|  |  | 
|  | // update before setOption | 
|  | if (!ecModel) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | ecModel.setUpdatePayload(payload); | 
|  |  | 
|  | // ChartView.markUpdateMethod(payload, 'updateTransform'); | 
|  |  | 
|  | const componentDirtyList = []; | 
|  | ecModel.eachComponent((componentType, componentModel) => { | 
|  | if (componentType === 'series') { | 
|  | return; | 
|  | } | 
|  |  | 
|  | const componentView = this.getViewOfComponentModel(componentModel); | 
|  | if (componentView && componentView.__alive) { | 
|  | if (componentView.updateTransform) { | 
|  | const result = componentView.updateTransform(componentModel, ecModel, api, payload); | 
|  | result && result.update && componentDirtyList.push(componentView); | 
|  | } | 
|  | else { | 
|  | componentDirtyList.push(componentView); | 
|  | } | 
|  | } | 
|  | }); | 
|  |  | 
|  | const seriesDirtyMap = createHashMap(); | 
|  | ecModel.eachSeries((seriesModel) => { | 
|  | const chartView = this._chartsMap[seriesModel.__viewId]; | 
|  | if (chartView.updateTransform) { | 
|  | const result = chartView.updateTransform(seriesModel, ecModel, api, payload); | 
|  | result && result.update && seriesDirtyMap.set(seriesModel.uid, 1); | 
|  | } | 
|  | else { | 
|  | seriesDirtyMap.set(seriesModel.uid, 1); | 
|  | } | 
|  | }); | 
|  |  | 
|  | clearColorPalette(ecModel); | 
|  | // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline. | 
|  | // this._scheduler.performVisualTasks(ecModel, payload, 'layout', true); | 
|  | this._scheduler.performVisualTasks( | 
|  | ecModel, payload, {setDirty: true, dirtyMap: seriesDirtyMap} | 
|  | ); | 
|  |  | 
|  | // Currently, not call render of components. Geo render cost a lot. | 
|  | // renderComponents(ecIns, ecModel, api, payload, componentDirtyList); | 
|  | renderSeries(this, ecModel, api, payload, {}, seriesDirtyMap); | 
|  |  | 
|  | lifecycle.trigger('afterupdate', ecModel, api); | 
|  | }, | 
|  |  | 
|  | updateView(this: ECharts, payload: Payload): void { | 
|  | const ecModel = this._model; | 
|  |  | 
|  | // update before setOption | 
|  | if (!ecModel) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | ecModel.setUpdatePayload(payload); | 
|  |  | 
|  | ChartView.markUpdateMethod(payload, 'updateView'); | 
|  |  | 
|  | clearColorPalette(ecModel); | 
|  |  | 
|  | // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline. | 
|  | this._scheduler.performVisualTasks(ecModel, payload, {setDirty: true}); | 
|  |  | 
|  | render(this, ecModel, this._api, payload, {}); | 
|  |  | 
|  | lifecycle.trigger('afterupdate', ecModel, this._api); | 
|  | }, | 
|  |  | 
|  | updateVisual(this: ECharts, payload: Payload): void { | 
|  | // updateMethods.update.call(this, payload); | 
|  |  | 
|  | const ecModel = this._model; | 
|  |  | 
|  | // update before setOption | 
|  | if (!ecModel) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | ecModel.setUpdatePayload(payload); | 
|  |  | 
|  | // clear all visual | 
|  | ecModel.eachSeries(function (seriesModel) { | 
|  | seriesModel.getData().clearAllVisual(); | 
|  | }); | 
|  |  | 
|  | // Perform visual | 
|  | ChartView.markUpdateMethod(payload, 'updateVisual'); | 
|  |  | 
|  | clearColorPalette(ecModel); | 
|  |  | 
|  | // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline. | 
|  | this._scheduler.performVisualTasks(ecModel, payload, {visualType: 'visual', setDirty: true}); | 
|  |  | 
|  | ecModel.eachComponent((componentType, componentModel) => {  // TODO componentType may be series. | 
|  | if (componentType !== 'series') { | 
|  | const componentView = this.getViewOfComponentModel(componentModel); | 
|  | componentView && componentView.__alive | 
|  | && componentView.updateVisual(componentModel, ecModel, this._api, payload); | 
|  | } | 
|  | }); | 
|  |  | 
|  | ecModel.eachSeries((seriesModel) => { | 
|  | const chartView = this._chartsMap[seriesModel.__viewId]; | 
|  | chartView.updateVisual(seriesModel, ecModel, this._api, payload); | 
|  | }); | 
|  |  | 
|  | lifecycle.trigger('afterupdate', ecModel, this._api); | 
|  | }, | 
|  |  | 
|  | updateLayout(this: ECharts, payload: Payload): void { | 
|  | updateMethods.update.call(this, payload); | 
|  | } | 
|  | }; | 
|  |  | 
|  | doConvertPixel = function ( | 
|  | ecIns: ECharts, | 
|  | methodName: 'convertFromPixel' | 'convertToPixel', | 
|  | finder: ModelFinder, | 
|  | value: (number | number[]) | (ScaleDataValue | ScaleDataValue[]) | 
|  | ): (number | number[]) { | 
|  | if (ecIns._disposed) { | 
|  | disposedWarning(ecIns.id); | 
|  | return; | 
|  | } | 
|  | const ecModel = ecIns._model; | 
|  | const coordSysList = ecIns._coordSysMgr.getCoordinateSystems(); | 
|  | let result; | 
|  |  | 
|  | const parsedFinder = modelUtil.parseFinder(ecModel, finder); | 
|  |  | 
|  | for (let i = 0; i < coordSysList.length; i++) { | 
|  | const coordSys = coordSysList[i]; | 
|  | if (coordSys[methodName] | 
|  | && (result = coordSys[methodName](ecModel, parsedFinder, value as any)) != null | 
|  | ) { | 
|  | return result; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (__DEV__) { | 
|  | console.warn( | 
|  | 'No coordinate system that supports ' + methodName + ' found by the given finder.' | 
|  | ); | 
|  | } | 
|  | }; | 
|  |  | 
|  | updateStreamModes = function (ecIns: ECharts, ecModel: GlobalModel): void { | 
|  | const chartsMap = ecIns._chartsMap; | 
|  | const scheduler = ecIns._scheduler; | 
|  | ecModel.eachSeries(function (seriesModel) { | 
|  | scheduler.updateStreamModes(seriesModel, chartsMap[seriesModel.__viewId]); | 
|  | }); | 
|  | }; | 
|  |  | 
|  | doDispatchAction = function (this: ECharts, payload: Payload, silent: boolean): void { | 
|  | const ecModel = this.getModel(); | 
|  | const payloadType = payload.type; | 
|  | const escapeConnect = payload.escapeConnect; | 
|  | const actionWrap = actions[payloadType]; | 
|  | const actionInfo = actionWrap.actionInfo; | 
|  |  | 
|  | const cptTypeTmp = (actionInfo.update || 'update').split(':'); | 
|  | const updateMethod = cptTypeTmp.pop(); | 
|  | const cptType = cptTypeTmp[0] != null && parseClassType(cptTypeTmp[0]); | 
|  |  | 
|  | this[IN_MAIN_PROCESS_KEY] = true; | 
|  |  | 
|  | let payloads: Payload[] = [payload]; | 
|  | let batched = false; | 
|  | // Batch action | 
|  | if (payload.batch) { | 
|  | batched = true; | 
|  | payloads = map<Payload['batch'][0], Payload, unknown>(payload.batch, function (item) { | 
|  | item = defaults(extend({}, item), payload); | 
|  | item.batch = null; | 
|  | return item as Payload; | 
|  | }); | 
|  | } | 
|  |  | 
|  | const eventObjBatch: ECEventData[] = []; | 
|  | let eventObj: ECActionEvent; | 
|  |  | 
|  | const isSelectChange = isSelectChangePayload(payload); | 
|  | const isHighDown = isHighDownPayload(payload); | 
|  |  | 
|  | // Only leave blur once if there are multiple batches. | 
|  | if (isHighDown) { | 
|  | allLeaveBlur(this._api); | 
|  | } | 
|  |  | 
|  | each(payloads, (batchItem) => { | 
|  | // Action can specify the event by return it. | 
|  | eventObj = actionWrap.action(batchItem, this._model, this._api) as ECActionEvent; | 
|  | // Emit event outside | 
|  | eventObj = eventObj || extend({} as ECActionEvent, batchItem); | 
|  | // Convert type to eventType | 
|  | eventObj.type = actionInfo.event || eventObj.type; | 
|  | eventObjBatch.push(eventObj); | 
|  |  | 
|  | // light update does not perform data process, layout and visual. | 
|  | if (isHighDown) { | 
|  | const { queryOptionMap, mainTypeSpecified } = modelUtil.preParseFinder(payload as ModelFinder); | 
|  | const componentMainType = mainTypeSpecified ? queryOptionMap.keys()[0] : 'series'; | 
|  | updateDirectly(this, updateMethod, batchItem as Payload, componentMainType); | 
|  | markStatusToUpdate(this); | 
|  | } | 
|  | else if (isSelectChange) { | 
|  | // At present `dispatchAction({ type: 'select', ... })` is not supported on components. | 
|  | // geo still use 'geoselect'. | 
|  | updateDirectly(this, updateMethod, batchItem as Payload, 'series'); | 
|  | markStatusToUpdate(this); | 
|  | } | 
|  | else if (cptType) { | 
|  | updateDirectly(this, updateMethod, batchItem as Payload, cptType.main, cptType.sub); | 
|  | } | 
|  | }); | 
|  |  | 
|  | if (updateMethod !== 'none' && !isHighDown && !isSelectChange && !cptType) { | 
|  | try { | 
|  | // Still dirty | 
|  | if (this[PENDING_UPDATE]) { | 
|  | prepare(this); | 
|  | updateMethods.update.call(this, payload); | 
|  | this[PENDING_UPDATE] = null; | 
|  | } | 
|  | else { | 
|  | updateMethods[updateMethod as keyof typeof updateMethods].call(this, payload); | 
|  | } | 
|  | } | 
|  | catch (e) { | 
|  | this[IN_MAIN_PROCESS_KEY] = false; | 
|  | throw e; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Follow the rule of action batch | 
|  | if (batched) { | 
|  | eventObj = { | 
|  | type: actionInfo.event || payloadType, | 
|  | escapeConnect: escapeConnect, | 
|  | batch: eventObjBatch | 
|  | }; | 
|  | } | 
|  | else { | 
|  | eventObj = eventObjBatch[0] as ECActionEvent; | 
|  | } | 
|  |  | 
|  | this[IN_MAIN_PROCESS_KEY] = false; | 
|  |  | 
|  | if (!silent) { | 
|  | const messageCenter = this._messageCenter; | 
|  | messageCenter.trigger(eventObj.type, eventObj); | 
|  | // Extra triggered 'selectchanged' event | 
|  | if (isSelectChange) { | 
|  | const newObj: SelectChangedPayload = { | 
|  | type: 'selectchanged', | 
|  | escapeConnect: escapeConnect, | 
|  | selected: getAllSelectedIndices(ecModel), | 
|  | isFromClick: payload.isFromClick || false, | 
|  | fromAction: payload.type as 'select' | 'unselect' | 'toggleSelected', | 
|  | fromActionPayload: payload | 
|  | }; | 
|  | messageCenter.trigger(newObj.type, newObj); | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | flushPendingActions = function (this: ECharts, silent: boolean): void { | 
|  | const pendingActions = this._pendingActions; | 
|  | while (pendingActions.length) { | 
|  | const payload = pendingActions.shift(); | 
|  | doDispatchAction.call(this, payload, silent); | 
|  | } | 
|  | }; | 
|  |  | 
|  | triggerUpdatedEvent = function (this: ECharts, silent): void { | 
|  | !silent && this.trigger('updated'); | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Event `rendered` is triggered when zr | 
|  | * rendered. It is useful for realtime | 
|  | * snapshot (reflect animation). | 
|  | * | 
|  | * Event `finished` is triggered when: | 
|  | * (1) zrender rendering finished. | 
|  | * (2) initial animation finished. | 
|  | * (3) progressive rendering finished. | 
|  | * (4) no pending action. | 
|  | * (5) no delayed setOption needs to be processed. | 
|  | */ | 
|  | bindRenderedEvent = function (zr: zrender.ZRenderType, ecIns: ECharts): void { | 
|  | zr.on('rendered', function (params: RenderedEventParam) { | 
|  |  | 
|  | ecIns.trigger('rendered', params); | 
|  |  | 
|  | // The `finished` event should not be triggered repeatedly, | 
|  | // so it should only be triggered when rendering indeed happens | 
|  | // in zrender. (Consider the case that dipatchAction is keep | 
|  | // triggering when mouse move). | 
|  | if ( | 
|  | // Although zr is dirty if initial animation is not finished | 
|  | // and this checking is called on frame, we also check | 
|  | // animation finished for robustness. | 
|  | zr.animation.isFinished() | 
|  | && !ecIns[PENDING_UPDATE] | 
|  | && !ecIns._scheduler.unfinished | 
|  | && !ecIns._pendingActions.length | 
|  | ) { | 
|  | ecIns.trigger('finished'); | 
|  | } | 
|  | }); | 
|  | }; | 
|  |  | 
|  | bindMouseEvent = function (zr: zrender.ZRenderType, ecIns: ECharts): void { | 
|  | zr.on('mouseover', function (e) { | 
|  | const el = e.target; | 
|  | const dispatcher = findEventDispatcher(el, isHighDownDispatcher); | 
|  | if (dispatcher) { | 
|  | handleGlobalMouseOverForHighDown(dispatcher, e, ecIns._api); | 
|  | markStatusToUpdate(ecIns); | 
|  | } | 
|  | }).on('mouseout', function (e) { | 
|  | const el = e.target; | 
|  | const dispatcher = findEventDispatcher(el, isHighDownDispatcher); | 
|  | if (dispatcher) { | 
|  | handleGlobalMouseOutForHighDown(dispatcher, e, ecIns._api); | 
|  | markStatusToUpdate(ecIns); | 
|  | } | 
|  | }).on('click', function (e) { | 
|  | const el = e.target; | 
|  | const dispatcher = findEventDispatcher( | 
|  | el, (target) => getECData(target).dataIndex != null, true | 
|  | ); | 
|  | if (dispatcher) { | 
|  | const actionType = (dispatcher as ECElement).selected ? 'unselect' : 'select'; | 
|  | const ecData = getECData(dispatcher); | 
|  | ecIns._api.dispatchAction({ | 
|  | type: actionType, | 
|  | dataType: ecData.dataType, | 
|  | dataIndexInside: ecData.dataIndex, | 
|  | seriesIndex: ecData.seriesIndex, | 
|  | isFromClick: true | 
|  | }); | 
|  | } | 
|  | }); | 
|  | }; | 
|  |  | 
|  | function clearColorPalette(ecModel: GlobalModel): void { | 
|  | ecModel.clearColorPalette(); | 
|  | ecModel.eachSeries(function (seriesModel) { | 
|  | seriesModel.clearColorPalette(); | 
|  | }); | 
|  | }; | 
|  |  | 
|  | // Allocate zlevels for series and components | 
|  | function allocateZlevels(ecModel: GlobalModel) { | 
|  | interface ZLevelItem { | 
|  | z: number, | 
|  | zlevel: number, | 
|  | idx: number, | 
|  | type: string, | 
|  | key: string | 
|  | }; | 
|  | const componentZLevels: ZLevelItem[] = []; | 
|  | const seriesZLevels: ZLevelItem[] = []; | 
|  | let hasSeperateZLevel = false; | 
|  | ecModel.eachComponent(function (componentType, componentModel) { | 
|  | const zlevel = componentModel.get('zlevel') || 0; | 
|  | const z = componentModel.get('z') || 0; | 
|  | const zlevelKey = componentModel.getZLevelKey(); | 
|  | hasSeperateZLevel = hasSeperateZLevel || !!zlevelKey; | 
|  | (componentType === 'series' ? seriesZLevels : componentZLevels).push({ | 
|  | zlevel, | 
|  | z, | 
|  | idx: componentModel.componentIndex, | 
|  | type: componentType, | 
|  | key: zlevelKey | 
|  | }); | 
|  | }); | 
|  |  | 
|  | if (hasSeperateZLevel) { | 
|  | // Series after component | 
|  | const zLevels: ZLevelItem[] = componentZLevels.concat(seriesZLevels); | 
|  | let lastSeriesZLevel: number; | 
|  | let lastSeriesKey: string; | 
|  |  | 
|  | timsort(zLevels, (a, b) => { | 
|  | if (a.zlevel === b.zlevel) { | 
|  | return a.z - b.z; | 
|  | } | 
|  | return a.zlevel - b.zlevel; | 
|  | }); | 
|  | each(zLevels, item => { | 
|  | const componentModel = ecModel.getComponent(item.type, item.idx); | 
|  | let zlevel = item.zlevel; | 
|  | const key = item.key; | 
|  | if (lastSeriesZLevel != null) { | 
|  | zlevel = Math.max(lastSeriesZLevel, zlevel); | 
|  | } | 
|  | if (key) { | 
|  | if (zlevel === lastSeriesZLevel && key !== lastSeriesKey) { | 
|  | zlevel++; | 
|  | } | 
|  | lastSeriesKey = key; | 
|  | } | 
|  | else if (lastSeriesKey) { | 
|  | if (zlevel === lastSeriesZLevel) { | 
|  | zlevel++; | 
|  | } | 
|  | lastSeriesKey = ''; | 
|  | } | 
|  | lastSeriesZLevel = zlevel; | 
|  | componentModel.setZLevel(zlevel); | 
|  | }); | 
|  | } | 
|  | } | 
|  |  | 
|  | render = ( | 
|  | ecIns: ECharts, ecModel: GlobalModel, api: ExtensionAPI, payload: Payload, | 
|  | updateParams: UpdateLifecycleParams | 
|  | ) => { | 
|  | allocateZlevels(ecModel); | 
|  |  | 
|  | renderComponents(ecIns, ecModel, api, payload, updateParams); | 
|  |  | 
|  | each(ecIns._chartsViews, function (chart: ChartView) { | 
|  | chart.__alive = false; | 
|  | }); | 
|  |  | 
|  | renderSeries(ecIns, ecModel, api, payload, updateParams); | 
|  |  | 
|  | // Remove groups of unrendered charts | 
|  | each(ecIns._chartsViews, function (chart: ChartView) { | 
|  | if (!chart.__alive) { | 
|  | chart.remove(ecModel, api); | 
|  | } | 
|  | }); | 
|  | }; | 
|  |  | 
|  | renderComponents = ( | 
|  | ecIns: ECharts, ecModel: GlobalModel, api: ExtensionAPI, payload: Payload, | 
|  | updateParams: UpdateLifecycleParams, dirtyList?: ComponentView[] | 
|  | ) => { | 
|  | each(dirtyList || ecIns._componentsViews, function (componentView: ComponentView) { | 
|  | const componentModel = componentView.__model; | 
|  | clearStates(componentModel, componentView); | 
|  |  | 
|  | componentView.render(componentModel, ecModel, api, payload); | 
|  |  | 
|  | updateZ(componentModel, componentView); | 
|  |  | 
|  | updateStates(componentModel, componentView); | 
|  | }); | 
|  |  | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Render each chart and component | 
|  | */ | 
|  | renderSeries = ( | 
|  | ecIns: ECharts, | 
|  | ecModel: GlobalModel, | 
|  | api: ExtensionAPI, | 
|  | payload: Payload | 'remain', | 
|  | updateParams: UpdateLifecycleParams, | 
|  | dirtyMap?: {[uid: string]: any} | 
|  | ) => { | 
|  | // Render all charts | 
|  | const scheduler = ecIns._scheduler; | 
|  |  | 
|  | updateParams = extend(updateParams || {}, { | 
|  | updatedSeries: ecModel.getSeries() | 
|  | }); | 
|  |  | 
|  | // TODO progressive? | 
|  | lifecycle.trigger('series:beforeupdate', ecModel, api, updateParams); | 
|  |  | 
|  | let unfinished: boolean = false; | 
|  | ecModel.eachSeries(function (seriesModel) { | 
|  | const chartView = ecIns._chartsMap[seriesModel.__viewId]; | 
|  | chartView.__alive = true; | 
|  |  | 
|  | const renderTask = chartView.renderTask; | 
|  | scheduler.updatePayload(renderTask, payload); | 
|  |  | 
|  | // TODO states on marker. | 
|  | clearStates(seriesModel, chartView); | 
|  |  | 
|  | if (dirtyMap && dirtyMap.get(seriesModel.uid)) { | 
|  | renderTask.dirty(); | 
|  | } | 
|  | if (renderTask.perform(scheduler.getPerformArgs(renderTask))) { | 
|  | unfinished = true; | 
|  | } | 
|  |  | 
|  | chartView.group.silent = !!seriesModel.get('silent'); | 
|  | // Should not call markRedraw on group, because it will disable zrender | 
|  | // incremental render (always render from the __startIndex each frame) | 
|  | // chartView.group.markRedraw(); | 
|  |  | 
|  | updateBlend(seriesModel, chartView); | 
|  |  | 
|  | updateSeriesElementSelection(seriesModel); | 
|  | }); | 
|  |  | 
|  | scheduler.unfinished = unfinished || scheduler.unfinished; | 
|  |  | 
|  | lifecycle.trigger('series:layoutlabels', ecModel, api, updateParams); | 
|  |  | 
|  | // transition after label is layouted. | 
|  | lifecycle.trigger('series:transition', ecModel, api, updateParams); | 
|  |  | 
|  | ecModel.eachSeries(function (seriesModel) { | 
|  | const chartView = ecIns._chartsMap[seriesModel.__viewId]; | 
|  | // Update Z after labels updated. Before applying states. | 
|  | updateZ(seriesModel, chartView); | 
|  |  | 
|  | // NOTE: Update states after label is updated. | 
|  | // label should be in normal status when layouting. | 
|  | updateStates(seriesModel, chartView); | 
|  | }); | 
|  |  | 
|  | // If use hover layer | 
|  | updateHoverLayerStatus(ecIns, ecModel); | 
|  |  | 
|  | lifecycle.trigger('series:afterupdate', ecModel, api, updateParams); | 
|  | }; | 
|  |  | 
|  | markStatusToUpdate = function (ecIns: ECharts): void { | 
|  | ecIns[STATUS_NEEDS_UPDATE_KEY] = true; | 
|  | // Wake up zrender if it's sleep. Let it update states in the next frame. | 
|  | ecIns.getZr().wakeUp(); | 
|  | }; | 
|  |  | 
|  | applyChangedStates = function (ecIns: ECharts): void { | 
|  | if (!ecIns[STATUS_NEEDS_UPDATE_KEY]) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | ecIns.getZr().storage.traverse(function (el: ECElement) { | 
|  | // Not applied on removed elements, it may still in fading. | 
|  | if (graphic.isElementRemoved(el)) { | 
|  | return; | 
|  | } | 
|  | applyElementStates(el); | 
|  | }); | 
|  |  | 
|  | ecIns[STATUS_NEEDS_UPDATE_KEY] = false; | 
|  | }; | 
|  |  | 
|  | function applyElementStates(el: ECElement) { | 
|  | const newStates = []; | 
|  |  | 
|  | const oldStates = el.currentStates; | 
|  | // Keep other states. | 
|  | for (let i = 0; i < oldStates.length; i++) { | 
|  | const stateName = oldStates[i]; | 
|  | if (!(stateName === 'emphasis' || stateName === 'blur' || stateName === 'select')) { | 
|  | newStates.push(stateName); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Only use states when it's exists. | 
|  | if (el.selected && el.states.select) { | 
|  | newStates.push('select'); | 
|  | } | 
|  | if (el.hoverState === HOVER_STATE_EMPHASIS && el.states.emphasis) { | 
|  | newStates.push('emphasis'); | 
|  | } | 
|  | else if (el.hoverState === HOVER_STATE_BLUR && el.states.blur) { | 
|  | newStates.push('blur'); | 
|  | } | 
|  | el.useStates(newStates); | 
|  | } | 
|  |  | 
|  | function updateHoverLayerStatus(ecIns: ECharts, ecModel: GlobalModel): void { | 
|  | const zr = ecIns._zr; | 
|  | const storage = zr.storage; | 
|  | let elCount = 0; | 
|  |  | 
|  | storage.traverse(function (el) { | 
|  | if (!el.isGroup) { | 
|  | elCount++; | 
|  | } | 
|  | }); | 
|  |  | 
|  | if (elCount > ecModel.get('hoverLayerThreshold') && !env.node && !env.worker) { | 
|  | ecModel.eachSeries(function (seriesModel) { | 
|  | if (seriesModel.preventUsingHoverLayer) { | 
|  | return; | 
|  | } | 
|  | const chartView = ecIns._chartsMap[seriesModel.__viewId]; | 
|  | if (chartView.__alive) { | 
|  | chartView.eachRendered((el: ECElement) => { | 
|  | if (el.states.emphasis) { | 
|  | el.states.emphasis.hoverLayer = true; | 
|  | } | 
|  | }); | 
|  | } | 
|  | }); | 
|  | } | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Update chart and blend. | 
|  | */ | 
|  | function updateBlend(seriesModel: SeriesModel, chartView: ChartView): void { | 
|  | const blendMode = seriesModel.get('blendMode') || null; | 
|  | chartView.eachRendered((el: Displayable) => { | 
|  | // FIXME marker and other components | 
|  | if (!el.isGroup) { | 
|  | // DON'T mark the element dirty. In case element is incremental and don't want to rerender. | 
|  | el.style.blend = blendMode; | 
|  | } | 
|  | }); | 
|  | }; | 
|  |  | 
|  | function updateZ(model: ComponentModel, view: ComponentView | ChartView): void { | 
|  | if (model.preventAutoZ) { | 
|  | return; | 
|  | } | 
|  | const z = model.get('z') || 0; | 
|  | const zlevel = model.get('zlevel') || 0; | 
|  | // Set z and zlevel | 
|  | view.eachRendered((el) => { | 
|  | doUpdateZ(el, z, zlevel, -Infinity); | 
|  | // Don't traverse the children because it has been traversed in _updateZ. | 
|  | return true; | 
|  | }); | 
|  | }; | 
|  |  | 
|  | function doUpdateZ(el: Element, z: number, zlevel: number, maxZ2: number): number { | 
|  | // Group may also have textContent | 
|  | const label = el.getTextContent(); | 
|  | const labelLine = el.getTextGuideLine(); | 
|  | const isGroup = el.isGroup; | 
|  |  | 
|  | if (isGroup) { | 
|  | // set z & zlevel of children elements of Group | 
|  | const children = (el as graphic.Group).childrenRef(); | 
|  | for (let i = 0; i < children.length; i++) { | 
|  | maxZ2 = Math.max(doUpdateZ(children[i], z, zlevel, maxZ2), maxZ2); | 
|  | } | 
|  | } | 
|  | else { | 
|  | // not Group | 
|  | (el as Displayable).z = z; | 
|  | (el as Displayable).zlevel = zlevel; | 
|  |  | 
|  | maxZ2 = Math.max((el as Displayable).z2, maxZ2); | 
|  | } | 
|  |  | 
|  | // always set z and zlevel if label/labelLine exists | 
|  | if (label) { | 
|  | label.z = z; | 
|  | label.zlevel = zlevel; | 
|  | // lift z2 of text content | 
|  | // TODO if el.emphasis.z2 is spcefied, what about textContent. | 
|  | isFinite(maxZ2) && (label.z2 = maxZ2 + 2); | 
|  | } | 
|  | if (labelLine) { | 
|  | const textGuideLineConfig = el.textGuideLineConfig; | 
|  | labelLine.z = z; | 
|  | labelLine.zlevel = zlevel; | 
|  | isFinite(maxZ2) | 
|  | && (labelLine.z2 = maxZ2 + (textGuideLineConfig && textGuideLineConfig.showAbove ? 1 : -1)); | 
|  | } | 
|  | return maxZ2; | 
|  | } | 
|  |  | 
|  | // Clear states without animation. | 
|  | // TODO States on component. | 
|  | function clearStates(model: ComponentModel, view: ComponentView | ChartView): void { | 
|  | view.eachRendered(function (el: Displayable) { | 
|  | // Not applied on removed elements, it may still in fading. | 
|  | if (graphic.isElementRemoved(el)) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | const textContent = el.getTextContent(); | 
|  | const textGuide = el.getTextGuideLine(); | 
|  | if (el.stateTransition) { | 
|  | el.stateTransition = null; | 
|  | } | 
|  | if (textContent && textContent.stateTransition) { | 
|  | textContent.stateTransition = null; | 
|  | } | 
|  | if (textGuide && textGuide.stateTransition) { | 
|  | textGuide.stateTransition = null; | 
|  | } | 
|  |  | 
|  | // TODO If el is incremental. | 
|  | if (el.hasState()) { | 
|  | el.prevStates = el.currentStates; | 
|  | el.clearStates(); | 
|  | } | 
|  | else if (el.prevStates) { | 
|  | el.prevStates = null; | 
|  | } | 
|  | }); | 
|  | } | 
|  |  | 
|  | function updateStates(model: ComponentModel, view: ComponentView | ChartView): void { | 
|  | const stateAnimationModel = (model as SeriesModel).getModel('stateAnimation'); | 
|  | const enableAnimation = model.isAnimationEnabled(); | 
|  | const duration = stateAnimationModel.get('duration'); | 
|  | const stateTransition = duration > 0 ? { | 
|  | duration, | 
|  | delay: stateAnimationModel.get('delay'), | 
|  | easing: stateAnimationModel.get('easing') | 
|  | // additive: stateAnimationModel.get('additive') | 
|  | } : null; | 
|  | view.eachRendered(function (el: Displayable) { | 
|  | if (el.states && el.states.emphasis) { | 
|  | // Not applied on removed elements, it may still in fading. | 
|  | if (graphic.isElementRemoved(el)) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (el instanceof graphic.Path) { | 
|  | savePathStates(el); | 
|  | } | 
|  |  | 
|  | // Only updated on changed element. In case element is incremental and don't want to rerender. | 
|  | // TODO, a more proper way? | 
|  | if (el.__dirty) { | 
|  | const prevStates = el.prevStates; | 
|  | // Restore states without animation | 
|  | if (prevStates) { | 
|  | el.useStates(prevStates); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Update state transition and enable animation again. | 
|  | if (enableAnimation) { | 
|  | el.stateTransition = stateTransition; | 
|  | const textContent = el.getTextContent(); | 
|  | const textGuide = el.getTextGuideLine(); | 
|  | // TODO Is it necessary to animate label? | 
|  | if (textContent) { | 
|  | textContent.stateTransition = stateTransition; | 
|  | } | 
|  | if (textGuide) { | 
|  | textGuide.stateTransition = stateTransition; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Use highlighted and selected flag to toggle states. | 
|  | if (el.__dirty) { | 
|  | applyElementStates(el); | 
|  | } | 
|  | } | 
|  | }); | 
|  | }; | 
|  |  | 
|  | createExtensionAPI = function (ecIns: ECharts): ExtensionAPI { | 
|  | return new (class extends ExtensionAPI { | 
|  | getCoordinateSystems(): CoordinateSystemMaster[] { | 
|  | return ecIns._coordSysMgr.getCoordinateSystems(); | 
|  | } | 
|  | getComponentByElement(el: Element) { | 
|  | while (el) { | 
|  | const modelInfo = (el as ViewRootGroup).__ecComponentInfo; | 
|  | if (modelInfo != null) { | 
|  | return ecIns._model.getComponent(modelInfo.mainType, modelInfo.index); | 
|  | } | 
|  | el = el.parent; | 
|  | } | 
|  | } | 
|  | enterEmphasis(el: Element, highlightDigit?: number) { | 
|  | enterEmphasis(el, highlightDigit); | 
|  | markStatusToUpdate(ecIns); | 
|  | } | 
|  | leaveEmphasis(el: Element, highlightDigit?: number) { | 
|  | leaveEmphasis(el, highlightDigit); | 
|  | markStatusToUpdate(ecIns); | 
|  | } | 
|  | enterBlur(el: Element) { | 
|  | enterBlur(el); | 
|  | markStatusToUpdate(ecIns); | 
|  | } | 
|  | leaveBlur(el: Element) { | 
|  | leaveBlur(el); | 
|  | markStatusToUpdate(ecIns); | 
|  | } | 
|  | enterSelect(el: Element) { | 
|  | enterSelect(el); | 
|  | markStatusToUpdate(ecIns); | 
|  | } | 
|  | leaveSelect(el: Element) { | 
|  | leaveSelect(el); | 
|  | markStatusToUpdate(ecIns); | 
|  | } | 
|  | getModel(): GlobalModel { | 
|  | return ecIns.getModel(); | 
|  | } | 
|  | getViewOfComponentModel(componentModel: ComponentModel): ComponentView { | 
|  | return ecIns.getViewOfComponentModel(componentModel); | 
|  | } | 
|  | getViewOfSeriesModel(seriesModel: SeriesModel): ChartView { | 
|  | return ecIns.getViewOfSeriesModel(seriesModel); | 
|  | } | 
|  | })(ecIns); | 
|  | }; | 
|  |  | 
|  | enableConnect = function (chart: ECharts): void { | 
|  |  | 
|  | function updateConnectedChartsStatus(charts: ECharts[], status: ConnectStatus) { | 
|  | for (let i = 0; i < charts.length; i++) { | 
|  | const otherChart = charts[i]; | 
|  | otherChart[CONNECT_STATUS_KEY] = status; | 
|  | } | 
|  | } | 
|  |  | 
|  | each(eventActionMap, function (actionType, eventType) { | 
|  | chart._messageCenter.on(eventType, function (event: ECActionEvent) { | 
|  | if (connectedGroups[chart.group] && chart[CONNECT_STATUS_KEY] !== CONNECT_STATUS_PENDING) { | 
|  | if (event && event.escapeConnect) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | const action = chart.makeActionFromEvent(event); | 
|  | const otherCharts: ECharts[] = []; | 
|  |  | 
|  | each(instances, function (otherChart) { | 
|  | if (otherChart !== chart && otherChart.group === chart.group) { | 
|  | otherCharts.push(otherChart); | 
|  | } | 
|  | }); | 
|  |  | 
|  | updateConnectedChartsStatus(otherCharts, CONNECT_STATUS_PENDING); | 
|  | each(otherCharts, function (otherChart) { | 
|  | if (otherChart[CONNECT_STATUS_KEY] !== CONNECT_STATUS_UPDATING) { | 
|  | otherChart.dispatchAction(action); | 
|  | } | 
|  | }); | 
|  | updateConnectedChartsStatus(otherCharts, CONNECT_STATUS_UPDATED); | 
|  | } | 
|  | }); | 
|  | }); | 
|  | }; | 
|  | })(); | 
|  | } | 
|  |  | 
|  | const echartsProto = ECharts.prototype; | 
|  | echartsProto.on = createRegisterEventWithLowercaseECharts('on'); | 
|  | echartsProto.off = createRegisterEventWithLowercaseECharts('off'); | 
|  | /** | 
|  | * @deprecated | 
|  | */ | 
|  | // @ts-ignore | 
|  | echartsProto.one = function (eventName: string, cb: Function, ctx?: any) { | 
|  | const self = this; | 
|  | deprecateLog('ECharts#one is deprecated.'); | 
|  | function wrapped(this: unknown, ...args2: any) { | 
|  | cb && cb.apply && cb.apply(this, args2); | 
|  | // @ts-ignore | 
|  | self.off(eventName, wrapped); | 
|  | }; | 
|  | // @ts-ignore | 
|  | this.on.call(this, eventName, wrapped, ctx); | 
|  | }; | 
|  |  | 
|  | const MOUSE_EVENT_NAMES: ZRElementEventName[] = [ | 
|  | 'click', 'dblclick', 'mouseover', 'mouseout', 'mousemove', | 
|  | 'mousedown', 'mouseup', 'globalout', 'contextmenu' | 
|  | ]; | 
|  |  | 
|  | function disposedWarning(id: string): void { | 
|  | if (__DEV__) { | 
|  | console.warn('Instance ' + id + ' has been disposed'); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | const actions: { | 
|  | [actionType: string]: { | 
|  | action: ActionHandler, | 
|  | actionInfo: ActionInfo | 
|  | } | 
|  | } = {}; | 
|  |  | 
|  | /** | 
|  | * Map eventType to actionType | 
|  | */ | 
|  | const eventActionMap: {[eventType: string]: string} = {}; | 
|  |  | 
|  | const dataProcessorFuncs: StageHandlerInternal[] = []; | 
|  |  | 
|  | const optionPreprocessorFuncs: OptionPreprocessor[] = []; | 
|  |  | 
|  | const visualFuncs: StageHandlerInternal[] = []; | 
|  |  | 
|  | const themeStorage: {[themeName: string]: ThemeOption} = {}; | 
|  |  | 
|  | const loadingEffects: {[effectName: string]: LoadingEffectCreator} = {}; | 
|  |  | 
|  | const instances: {[id: string]: ECharts} = {}; | 
|  | const connectedGroups: {[groupId: string]: boolean} = {}; | 
|  |  | 
|  | let idBase: number = +(new Date()) - 0; | 
|  | let groupIdBase: number = +(new Date()) - 0; | 
|  | const DOM_ATTRIBUTE_KEY = '_echarts_instance_'; | 
|  |  | 
|  |  | 
|  | /** | 
|  | * @param opts.devicePixelRatio Use window.devicePixelRatio by default | 
|  | * @param opts.renderer Can choose 'canvas' or 'svg' to render the chart. | 
|  | * @param opts.width Use clientWidth of the input `dom` by default. | 
|  | *        Can be 'auto' (the same as null/undefined) | 
|  | * @param opts.height Use clientHeight of the input `dom` by default. | 
|  | *        Can be 'auto' (the same as null/undefined) | 
|  | * @param opts.locale Specify the locale. | 
|  | * @param opts.useDirtyRect Enable dirty rectangle rendering or not. | 
|  | */ | 
|  | export function init( | 
|  | dom: HTMLElement, | 
|  | theme?: string | object, | 
|  | opts?: EChartsInitOpts | 
|  | ): EChartsType { | 
|  | const isClient = !(opts && opts.ssr); | 
|  | if (isClient) { | 
|  | if (__DEV__) { | 
|  | if (!dom) { | 
|  | throw new Error('Initialize failed: invalid dom.'); | 
|  | } | 
|  | } | 
|  |  | 
|  | const existInstance = getInstanceByDom(dom); | 
|  | if (existInstance) { | 
|  | if (__DEV__) { | 
|  | console.warn('There is a chart instance already initialized on the dom.'); | 
|  | } | 
|  | return existInstance; | 
|  | } | 
|  |  | 
|  | if (__DEV__) { | 
|  | if (isDom(dom) | 
|  | && dom.nodeName.toUpperCase() !== 'CANVAS' | 
|  | && ( | 
|  | (!dom.clientWidth && (!opts || opts.width == null)) | 
|  | || (!dom.clientHeight && (!opts || opts.height == null)) | 
|  | ) | 
|  | ) { | 
|  | console.warn('Can\'t get DOM width or height. Please check ' | 
|  | + 'dom.clientWidth and dom.clientHeight. They should not be 0.' | 
|  | + 'For example, you may need to call this in the callback ' | 
|  | + 'of window.onload.'); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | const chart = new ECharts(dom, theme, opts); | 
|  | chart.id = 'ec_' + idBase++; | 
|  | instances[chart.id] = chart; | 
|  |  | 
|  | isClient && modelUtil.setAttribute(dom, DOM_ATTRIBUTE_KEY, chart.id); | 
|  |  | 
|  | enableConnect(chart); | 
|  |  | 
|  | lifecycle.trigger('afterinit', chart); | 
|  |  | 
|  | return chart; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @usage | 
|  | * (A) | 
|  | * ```js | 
|  | * let chart1 = echarts.init(dom1); | 
|  | * let chart2 = echarts.init(dom2); | 
|  | * chart1.group = 'xxx'; | 
|  | * chart2.group = 'xxx'; | 
|  | * echarts.connect('xxx'); | 
|  | * ``` | 
|  | * (B) | 
|  | * ```js | 
|  | * let chart1 = echarts.init(dom1); | 
|  | * let chart2 = echarts.init(dom2); | 
|  | * echarts.connect('xxx', [chart1, chart2]); | 
|  | * ``` | 
|  | */ | 
|  | export function connect(groupId: string | EChartsType[]): string { | 
|  | // Is array of charts | 
|  | if (isArray(groupId)) { | 
|  | const charts = groupId; | 
|  | groupId = null; | 
|  | // If any chart has group | 
|  | each(charts, function (chart) { | 
|  | if (chart.group != null) { | 
|  | groupId = chart.group; | 
|  | } | 
|  | }); | 
|  | groupId = groupId || ('g_' + groupIdBase++); | 
|  | each(charts, function (chart) { | 
|  | chart.group = groupId as string; | 
|  | }); | 
|  | } | 
|  | connectedGroups[groupId as string] = true; | 
|  | return groupId as string; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @deprecated | 
|  | */ | 
|  | export function disConnect(groupId: string): void { | 
|  | connectedGroups[groupId] = false; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Alias and backward compatibility | 
|  | */ | 
|  | export const disconnect = disConnect; | 
|  |  | 
|  | /** | 
|  | * Dispose a chart instance | 
|  | */ | 
|  | export function dispose(chart: EChartsType | HTMLElement | string): void { | 
|  | if (isString(chart)) { | 
|  | chart = instances[chart]; | 
|  | } | 
|  | else if (!(chart instanceof ECharts)) { | 
|  | // Try to treat as dom | 
|  | chart = getInstanceByDom(chart); | 
|  | } | 
|  | if ((chart instanceof ECharts) && !chart.isDisposed()) { | 
|  | chart.dispose(); | 
|  | } | 
|  | } | 
|  |  | 
|  | export function getInstanceByDom(dom: HTMLElement): EChartsType | undefined { | 
|  | return instances[modelUtil.getAttribute(dom, DOM_ATTRIBUTE_KEY)]; | 
|  | } | 
|  |  | 
|  | export function getInstanceById(key: string): EChartsType | undefined { | 
|  | return instances[key]; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Register theme | 
|  | */ | 
|  | export function registerTheme(name: string, theme: ThemeOption): void { | 
|  | themeStorage[name] = theme; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Register option preprocessor | 
|  | */ | 
|  | export function registerPreprocessor(preprocessorFunc: OptionPreprocessor): void { | 
|  | if (indexOf(optionPreprocessorFuncs, preprocessorFunc) < 0) { | 
|  | optionPreprocessorFuncs.push(preprocessorFunc); | 
|  | } | 
|  | } | 
|  |  | 
|  | export function registerProcessor( | 
|  | priority: number | StageHandler | StageHandlerOverallReset, | 
|  | processor?: StageHandler | StageHandlerOverallReset | 
|  | ): void { | 
|  | normalizeRegister(dataProcessorFuncs, priority, processor, PRIORITY_PROCESSOR_DEFAULT); | 
|  | } | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Register postIniter | 
|  | * @param {Function} postInitFunc | 
|  | */ | 
|  | export function registerPostInit(postInitFunc: PostIniter): void { | 
|  | registerUpdateLifecycle('afterinit', postInitFunc); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Register postUpdater | 
|  | * @param {Function} postUpdateFunc | 
|  | */ | 
|  | export function registerPostUpdate(postUpdateFunc: PostUpdater): void { | 
|  | registerUpdateLifecycle('afterupdate', postUpdateFunc); | 
|  | } | 
|  |  | 
|  | export function registerUpdateLifecycle<T extends keyof LifecycleEvents>( | 
|  | name: T, cb: (...args: LifecycleEvents[T]) => void | 
|  | ): void { | 
|  | (lifecycle as any).on(name, cb); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @usage | 
|  | * registerAction('someAction', 'someEvent', function () { ... }); | 
|  | * registerAction('someAction', function () { ... }); | 
|  | * registerAction( | 
|  | *     {type: 'someAction', event: 'someEvent', update: 'updateView'}, | 
|  | *     function () { ... } | 
|  | * ); | 
|  | * | 
|  | * @param {(string|Object)} actionInfo | 
|  | * @param {string} actionInfo.type | 
|  | * @param {string} [actionInfo.event] | 
|  | * @param {string} [actionInfo.update] | 
|  | * @param {string} [eventName] | 
|  | * @param {Function} action | 
|  | */ | 
|  | export function registerAction(type: string, eventName: string, action: ActionHandler): void; | 
|  | export function registerAction(type: string, action: ActionHandler): void; | 
|  | export function registerAction(actionInfo: ActionInfo, action: ActionHandler): void; | 
|  | export function registerAction( | 
|  | actionInfo: string | ActionInfo, | 
|  | eventName: string | ActionHandler, | 
|  | action?: ActionHandler | 
|  | ): void { | 
|  | if (isFunction(eventName)) { | 
|  | action = eventName; | 
|  | eventName = ''; | 
|  | } | 
|  | const actionType = isObject(actionInfo) | 
|  | ? (actionInfo as ActionInfo).type | 
|  | : ([actionInfo, actionInfo = { | 
|  | event: eventName | 
|  | } as ActionInfo][0]); | 
|  |  | 
|  | // Event name is all lowercase | 
|  | (actionInfo as ActionInfo).event = ( | 
|  | (actionInfo as ActionInfo).event || actionType as string | 
|  | ).toLowerCase(); | 
|  | eventName = (actionInfo as ActionInfo).event; | 
|  |  | 
|  | if (eventActionMap[eventName as string]) { | 
|  | // Already registered. | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Validate action type and event name. | 
|  | assert(ACTION_REG.test(actionType as string) && ACTION_REG.test(eventName)); | 
|  |  | 
|  | if (!actions[actionType as string]) { | 
|  | actions[actionType as string] = {action: action, actionInfo: actionInfo as ActionInfo}; | 
|  | } | 
|  | eventActionMap[eventName as string] = actionType as string; | 
|  | } | 
|  |  | 
|  | export function registerCoordinateSystem( | 
|  | type: string, | 
|  | coordSysCreator: CoordinateSystemCreator | 
|  | ): void { | 
|  | CoordinateSystemManager.register(type, coordSysCreator); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Get dimensions of specified coordinate system. | 
|  | * @param {string} type | 
|  | * @return {Array.<string|Object>} | 
|  | */ | 
|  | export function getCoordinateSystemDimensions(type: string): DimensionDefinitionLoose[] { | 
|  | const coordSysCreator = CoordinateSystemManager.get(type); | 
|  | if (coordSysCreator) { | 
|  | return coordSysCreator.getDimensionsInfo | 
|  | ? coordSysCreator.getDimensionsInfo() | 
|  | : coordSysCreator.dimensions.slice(); | 
|  | } | 
|  | } | 
|  |  | 
|  | export {registerLocale} from './locale'; | 
|  |  | 
|  | /** | 
|  | * Layout is a special stage of visual encoding | 
|  | * Most visual encoding like color are common for different chart | 
|  | * But each chart has it's own layout algorithm | 
|  | */ | 
|  | function registerLayout(priority: number, layoutTask: StageHandler | StageHandlerOverallReset): void; | 
|  | function registerLayout(layoutTask: StageHandler | StageHandlerOverallReset): void; | 
|  | function registerLayout( | 
|  | priority: number | StageHandler | StageHandlerOverallReset, | 
|  | layoutTask?: StageHandler | StageHandlerOverallReset | 
|  | ): void { | 
|  | normalizeRegister(visualFuncs, priority, layoutTask, PRIORITY_VISUAL_LAYOUT, 'layout'); | 
|  | } | 
|  |  | 
|  | function registerVisual(priority: number, layoutTask: StageHandler | StageHandlerOverallReset): void; | 
|  | function registerVisual(layoutTask: StageHandler | StageHandlerOverallReset): void; | 
|  | function registerVisual( | 
|  | priority: number | StageHandler | StageHandlerOverallReset, | 
|  | visualTask?: StageHandler | StageHandlerOverallReset | 
|  | ): void { | 
|  | normalizeRegister(visualFuncs, priority, visualTask, PRIORITY_VISUAL_CHART, 'visual'); | 
|  | } | 
|  |  | 
|  | export {registerLayout, registerVisual}; | 
|  |  | 
|  | const registeredTasks: (StageHandler | StageHandlerOverallReset)[] = []; | 
|  |  | 
|  | function normalizeRegister( | 
|  | targetList: StageHandler[], | 
|  | priority: number | StageHandler | StageHandlerOverallReset, | 
|  | fn: StageHandler | StageHandlerOverallReset, | 
|  | defaultPriority: number, | 
|  | visualType?: StageHandlerInternal['visualType'] | 
|  | ): void { | 
|  | if (isFunction(priority) || isObject(priority)) { | 
|  | fn = priority as (StageHandler | StageHandlerOverallReset); | 
|  | priority = defaultPriority; | 
|  | } | 
|  |  | 
|  | if (__DEV__) { | 
|  | if (isNaN(priority) || priority == null) { | 
|  | throw new Error('Illegal priority'); | 
|  | } | 
|  | // Check duplicate | 
|  | each(targetList, function (wrap) { | 
|  | assert((wrap as StageHandlerInternal).__raw !== fn); | 
|  | }); | 
|  | } | 
|  |  | 
|  | // Already registered | 
|  | if (indexOf(registeredTasks, fn) >= 0) { | 
|  | return; | 
|  | } | 
|  | registeredTasks.push(fn); | 
|  |  | 
|  | const stageHandler = Scheduler.wrapStageHandler(fn, visualType); | 
|  |  | 
|  | stageHandler.__prio = priority; | 
|  | stageHandler.__raw = fn; | 
|  | targetList.push(stageHandler); | 
|  | } | 
|  |  | 
|  | export function registerLoading( | 
|  | name: string, | 
|  | loadingFx: LoadingEffectCreator | 
|  | ): void { | 
|  | loadingEffects[name] = loadingFx; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * ZRender need a canvas context to do measureText. | 
|  | * But in node environment canvas may be created by node-canvas. | 
|  | * So we need to specify how to create a canvas instead of using document.createElement('canvas') | 
|  | * | 
|  | * | 
|  | * @deprecated use setPlatformAPI({ createCanvas }) instead. | 
|  | * | 
|  | * @example | 
|  | *     let Canvas = require('canvas'); | 
|  | *     let echarts = require('echarts'); | 
|  | *     echarts.setCanvasCreator(function () { | 
|  | *         // Small size is enough. | 
|  | *         return new Canvas(32, 32); | 
|  | *     }); | 
|  | */ | 
|  | export function setCanvasCreator(creator: () => HTMLCanvasElement): void { | 
|  | if (__DEV__) { | 
|  | deprecateLog('setCanvasCreator is deprecated. Use setPlatformAPI({ createCanvas }) instead.'); | 
|  | } | 
|  | setPlatformAPI({ | 
|  | createCanvas: creator | 
|  | }); | 
|  | } | 
|  |  | 
|  | type RegisterMapParams = Parameters<typeof geoSourceManager.registerMap>; | 
|  | /** | 
|  | * The parameters and usage: see `geoSourceManager.registerMap`. | 
|  | * Compatible with previous `echarts.registerMap`. | 
|  | */ | 
|  | export function registerMap( | 
|  | mapName: RegisterMapParams[0], | 
|  | geoJson: RegisterMapParams[1], | 
|  | specialAreas?: RegisterMapParams[2] | 
|  | ): void { | 
|  | const registerMap = getImpl('registerMap'); | 
|  | registerMap && registerMap(mapName, geoJson, specialAreas); | 
|  | } | 
|  |  | 
|  | export function getMap(mapName: string) { | 
|  | const getMap = getImpl('getMap'); | 
|  | return getMap && getMap(mapName); | 
|  | } | 
|  |  | 
|  | export const registerTransform = registerExternalTransform; | 
|  |  | 
|  | /** | 
|  | * Globa dispatchAction to a specified chart instance. | 
|  | */ | 
|  | // export function dispatchAction(payload: { chartId: string } & Payload, opt?: Parameters<ECharts['dispatchAction']>[1]) { | 
|  | //     if (!payload || !payload.chartId) { | 
|  | //         // Must have chartId to find chart | 
|  | //         return; | 
|  | //     } | 
|  | //     const chart = instances[payload.chartId]; | 
|  | //     if (chart) { | 
|  | //         chart.dispatchAction(payload, opt); | 
|  | //     } | 
|  | // } | 
|  |  | 
|  |  | 
|  |  | 
|  | // Builtin global visual | 
|  | registerVisual(PRIORITY_VISUAL_GLOBAL, seriesStyleTask); | 
|  | registerVisual(PRIORITY_VISUAL_CHART_DATA_CUSTOM, dataStyleTask); | 
|  | registerVisual(PRIORITY_VISUAL_CHART_DATA_CUSTOM, dataColorPaletteTask); | 
|  |  | 
|  | registerVisual(PRIORITY_VISUAL_GLOBAL, seriesSymbolTask); | 
|  | registerVisual(PRIORITY_VISUAL_CHART_DATA_CUSTOM, dataSymbolTask); | 
|  |  | 
|  | registerVisual(PRIORITY_VISUAL_DECAL, decal); | 
|  |  | 
|  | registerPreprocessor(backwardCompat); | 
|  | registerProcessor(PRIORITY_PROCESSOR_DATASTACK, dataStack); | 
|  | registerLoading('default', loadingDefault); | 
|  |  | 
|  | // Default actions | 
|  |  | 
|  | registerAction({ | 
|  | type: HIGHLIGHT_ACTION_TYPE, | 
|  | event: HIGHLIGHT_ACTION_TYPE, | 
|  | update: HIGHLIGHT_ACTION_TYPE | 
|  | }, noop); | 
|  |  | 
|  | registerAction({ | 
|  | type: DOWNPLAY_ACTION_TYPE, | 
|  | event: DOWNPLAY_ACTION_TYPE, | 
|  | update: DOWNPLAY_ACTION_TYPE | 
|  | }, noop); | 
|  |  | 
|  | registerAction({ | 
|  | type: SELECT_ACTION_TYPE, | 
|  | event: SELECT_ACTION_TYPE, | 
|  | update: SELECT_ACTION_TYPE | 
|  | }, noop); | 
|  |  | 
|  | registerAction({ | 
|  | type: UNSELECT_ACTION_TYPE, | 
|  | event: UNSELECT_ACTION_TYPE, | 
|  | update: UNSELECT_ACTION_TYPE | 
|  | }, noop); | 
|  |  | 
|  | registerAction({ | 
|  | type: TOGGLE_SELECT_ACTION_TYPE, | 
|  | event: TOGGLE_SELECT_ACTION_TYPE, | 
|  | update: TOGGLE_SELECT_ACTION_TYPE | 
|  | }, noop); | 
|  |  | 
|  | // Default theme | 
|  | registerTheme('light', lightTheme); | 
|  | registerTheme('dark', darkTheme); | 
|  |  | 
|  | // For backward compatibility, where the namespace `dataTool` will | 
|  | // be mounted on `echarts` is the extension `dataTool` is imported. | 
|  | export const dataTool = {}; | 
|  |  | 
|  | export interface EChartsType extends ECharts {} |