You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@echarts.apache.org by su...@apache.org on 2020/02/16 21:28:08 UTC
[incubator-echarts] 06/09: Migrate to TS -- Part I (some of the
basic class and utils) Note: lots of the common type are put in
`src/util/types.ts`.
This is an automated email from the ASF dual-hosted git repository.
sushuang pushed a commit to branch typescript
in repository https://gitbox.apache.org/repos/asf/incubator-echarts.git
commit 3229e88e2310d673d52c49c14b8605c85f0860b1
Author: 100pah <su...@gmail.com>
AuthorDate: Mon Feb 17 01:31:44 2020 +0800
Migrate to TS -- Part I (some of the basic class and utils)
Note: lots of the common type are put in `src/util/types.ts`.
---
index.d.ts | 1 +
src/CoordinateSystem.ts | 43 +-
src/ExtensionAPI.ts | 50 +-
src/chart/custom.ts | 8 +-
src/chart/graph/GraphSeries.ts | 2 +-
src/chart/helper/createListFromArray.ts | 6 +-
src/chart/helper/createRenderPlanner.ts | 10 +-
src/component/dataZoom/dataZoomProcessor.ts | 5 +-
src/component/dataset.ts | 4 +-
src/component/helper/BrushController.ts | 4 +-
src/component/helper/RoamController.ts | 4 +-
src/component/marker/MarkerModel.ts | 6 +-
src/component/timeline/SliderTimelineModel.ts | 6 +-
src/config.ts | 4 +-
src/coord/CoordinateSystem.ts | 86 +
src/coord/ICoordinateSystem | 85 -
src/coord/View.ts | 4 +-
src/coord/geo/mapDataStorage.ts | 87 +-
src/data/DataDiffer.ts | 138 +-
src/data/DataDimensionInfo.ts | 70 +-
src/data/List.ts | 3430 +++++++++++++------------
src/data/OrdinalMeta.ts | 178 +-
src/data/Source.ts | 106 +-
src/data/helper/completeDimensions.ts | 6 +-
src/data/helper/dataProvider.ts | 493 ++--
src/data/helper/dimensionHelper.ts | 55 +-
src/data/helper/sourceHelper.ts | 8 +-
src/data/helper/sourceType.ts | 30 -
src/echarts.ts | 3352 ++++++++++++------------
src/export.ts | 4 +-
src/loading/default.ts | 16 +-
src/model/Component.ts | 222 +-
src/model/Global.ts | 563 ++--
src/model/Model.ts | 183 +-
src/model/OptionManager.ts | 212 +-
src/model/Series.ts | 307 +--
src/model/mixin/colorPalette.ts | 33 +-
src/model/mixin/dataFormat.ts | 114 +-
src/processor/dataSample.ts | 5 +-
src/stream/Scheduler.ts | 915 ++++---
src/stream/task.ts | 507 ++--
src/util/ECEventProcessor.ts | 152 ++
src/util/clazz.ts | 265 +-
src/util/component.ts | 122 +-
src/util/format.ts | 56 +-
src/util/graphic.ts | 4 +-
src/util/model.ts | 189 +-
src/util/throttle.ts | 4 +-
src/util/types.ts | 446 ++++
src/view/Chart.ts | 241 +-
src/view/Component.ts | 93 +-
src/visual/LegendVisualProvider.ts | 47 +-
src/visual/dataColor.ts | 5 +-
53 files changed, 7125 insertions(+), 5861 deletions(-)
diff --git a/index.d.ts b/index.d.ts
new file mode 100644
index 0000000..aa892ea
--- /dev/null
+++ b/index.d.ts
@@ -0,0 +1 @@
+export * from './echarts.all';
\ No newline at end of file
diff --git a/src/CoordinateSystem.ts b/src/CoordinateSystem.ts
index cec2173..6d75c47 100644
--- a/src/CoordinateSystem.ts
+++ b/src/CoordinateSystem.ts
@@ -18,45 +18,44 @@
*/
import * as zrUtil from 'zrender/src/core/util';
+import GlobalModel from './model/Global';
+import ExtensionAPI from './ExtensionAPI';
+import { CoordinateSystemCreator, CoordinateSystem } from './coord/CoordinateSystem';
-var coordinateSystemCreators = {};
+var coordinateSystemCreators: {[type: string]: CoordinateSystemCreator} = {};
-function CoordinateSystemManager() {
+class CoordinateSystemManager {
- this._coordinateSystems = [];
-}
-
-CoordinateSystemManager.prototype = {
-
- constructor: CoordinateSystemManager,
+ private _coordinateSystems: CoordinateSystem[] = [];
- create: function (ecModel, api) {
- var coordinateSystems = [];
+ create(ecModel: GlobalModel, api: ExtensionAPI): void {
+ var coordinateSystems: CoordinateSystem[] = [];
zrUtil.each(coordinateSystemCreators, function (creater, type) {
var list = creater.create(ecModel, api);
coordinateSystems = coordinateSystems.concat(list || []);
});
this._coordinateSystems = coordinateSystems;
- },
+ }
- update: function (ecModel, api) {
+ update(ecModel: GlobalModel, api: ExtensionAPI): void {
zrUtil.each(this._coordinateSystems, function (coordSys) {
coordSys.update && coordSys.update(ecModel, api);
});
- },
+ }
- getCoordinateSystems: function () {
+ getCoordinateSystems(): CoordinateSystem[] {
return this._coordinateSystems.slice();
}
-};
-CoordinateSystemManager.register = function (type, coordinateSystemCreator) {
- coordinateSystemCreators[type] = coordinateSystemCreator;
-};
+ static register = function (type: string, creator: CoordinateSystemCreator): void {
+ coordinateSystemCreators[type] = creator;
+ }
+
+ static get = function (type: string): CoordinateSystemCreator {
+ return coordinateSystemCreators[type];
+ }
-CoordinateSystemManager.get = function (type) {
- return coordinateSystemCreators[type];
-};
+}
-export default CoordinateSystemManager;
\ No newline at end of file
+export default CoordinateSystemManager;
diff --git a/src/ExtensionAPI.ts b/src/ExtensionAPI.ts
index 80b552e..41fcd5f 100644
--- a/src/ExtensionAPI.ts
+++ b/src/ExtensionAPI.ts
@@ -18,18 +18,44 @@
*/
import * as zrUtil from 'zrender/src/core/util';
+import {EChartsType} from './echarts';
+import {CoordinateSystem} from './coord/CoordinateSystem';
+import Element from 'zrender/src/Element';
+import ComponentModel from './model/Component';
-var echartsAPIList = [
- 'getDom', 'getZr', 'getWidth', 'getHeight', 'getDevicePixelRatio', 'dispatchAction', 'isDisposed',
- 'on', 'off', 'getDataURL', 'getConnectedDataURL', 'getModel', 'getOption',
- 'getViewOfComponentModel', 'getViewOfSeriesModel'
-];
-// And `getCoordinateSystems` and `getComponentByElement` will be injected in echarts.js
-
-function ExtensionAPI(chartInstance) {
- zrUtil.each(echartsAPIList, function (name) {
- this[name] = zrUtil.bind(chartInstance[name], chartInstance);
- }, this);
+var availableMethods = {
+ getDom: 1,
+ getZr: 1,
+ getWidth: 1,
+ getHeight: 1,
+ getDevicePixelRatio: 1,
+ dispatchAction: 1,
+ isDisposed: 1,
+ on: 1,
+ off: 1,
+ getDataURL: 1,
+ getConnectedDataURL: 1,
+ getModel: 1,
+ getOption: 1,
+ getViewOfComponentModel: 1,
+ getViewOfSeriesModel: 1
+};
+
+interface ExtensionAPI extends Pick<EChartsType, keyof typeof availableMethods> {}
+
+abstract class ExtensionAPI {
+
+ constructor(ecInstance: EChartsType) {
+ zrUtil.each(availableMethods, function (v, name: string) {
+ (this as any)[name] = zrUtil.bind((ecInstance as any)[name], ecInstance);
+ }, this);
+ }
+
+ // Implemented in echarts.js
+ abstract getCoordinateSystems(): CoordinateSystem[];
+
+ // Implemented in echarts.js
+ abstract getComponentByElement(el: Element): ComponentModel;
}
-export default ExtensionAPI;
\ No newline at end of file
+export default ExtensionAPI;
diff --git a/src/chart/custom.ts b/src/chart/custom.ts
index c1dd8be..726d2ac 100644
--- a/src/chart/custom.ts
+++ b/src/chart/custom.ts
@@ -17,6 +17,8 @@
* under the License.
*/
+// @ts-nocheck
+
import {__DEV__} from '../config';
import * as zrUtil from 'zrender/src/core/util';
import * as graphicUtil from '../util/graphic';
@@ -28,6 +30,8 @@ import SeriesModel from '../model/Series';
import Model from '../model/Model';
import ChartView from '../view/Chart';
import {createClipPath} from './helper/createClipPathFromCoordSys';
+import {EventQueryItem, ECEvent} from '../util/types';
+import Element from 'zrender/src/Element';
import prepareCartesian2d from '../coord/cartesian/prepareCustom';
import prepareGeo from '../coord/geo/prepareCustom';
@@ -208,7 +212,9 @@ ChartView.extend({
/**
* @override
*/
- filterForExposedEvent: function (eventType, query, targetEl, packedEvent) {
+ filterForExposedEvent: function (
+ eventType: string, query: EventQueryItem, targetEl: Element, packedEvent: ECEvent
+ ): boolean {
var elementName = query.element;
if (elementName == null || targetEl.name === elementName) {
return true;
diff --git a/src/chart/graph/GraphSeries.ts b/src/chart/graph/GraphSeries.ts
index a0b5334..4cd1339 100644
--- a/src/chart/graph/GraphSeries.ts
+++ b/src/chart/graph/GraphSeries.ts
@@ -59,7 +59,7 @@ var GraphSeries = echarts.extendSeriesModel({
mergeDefaultAndTheme: function (option) {
GraphSeries.superApply(this, 'mergeDefaultAndTheme', arguments);
- defaultEmphasis(option, ['edgeLabel'], ['show']);
+ defaultEmphasis(option, 'edgeLabel', ['show']);
},
getInitialData: function (option, ecModel) {
diff --git a/src/chart/helper/createListFromArray.ts b/src/chart/helper/createListFromArray.ts
index 2b6cadc..690d973 100644
--- a/src/chart/helper/createListFromArray.ts
+++ b/src/chart/helper/createListFromArray.ts
@@ -17,10 +17,11 @@
* under the License.
*/
+// @ts-nocheck
+
import * as zrUtil from 'zrender/src/core/util';
import List from '../../data/List';
import createDimensions from '../../data/helper/createDimensions';
-import {SOURCE_FORMAT_ORIGINAL} from '../../data/helper/sourceType';
import {getDimensionTypeByAxis} from '../../data/helper/dimensionHelper';
import {getDataItemValue} from '../../util/model';
import CoordinateSystem from '../../CoordinateSystem';
@@ -28,6 +29,7 @@ import {getCoordSysInfoBySeries} from '../../model/referHelper';
import Source from '../../data/Source';
import {enableDataStack} from '../../data/helper/dataStackHelper';
import {makeSeriesEncodeForAxisCoordSys} from '../../data/helper/sourceHelper';
+import { SOURCE_FORMAT_ORIGINAL } from '../../util/types';
/**
* @param {module:echarts/data/Source|Array} source Or raw data.
@@ -120,7 +122,7 @@ function createListFromArray(source, seriesModel, opt) {
return list;
}
-function isNeedCompleteOrdinalData(source) {
+function isNeedCompleteOrdinalData(source: Source) {
if (source.sourceFormat === SOURCE_FORMAT_ORIGINAL) {
var sampleItem = firstDataNotNull(source.data || []);
return sampleItem != null
diff --git a/src/chart/helper/createRenderPlanner.ts b/src/chart/helper/createRenderPlanner.ts
index 0fc5041..ed7bc87 100644
--- a/src/chart/helper/createRenderPlanner.ts
+++ b/src/chart/helper/createRenderPlanner.ts
@@ -17,7 +17,11 @@
* under the License.
*/
+// @ts-nocheck
+
import {makeInner} from '../../util/model';
+import SeriesModel from '../../model/Series';
+import { StageHandlerPlanReturn, StageHandlerPlan } from '../../util/types';
/**
* @return {string} If large mode changed, return string 'reset';
@@ -25,7 +29,7 @@ import {makeInner} from '../../util/model';
export default function () {
var inner = makeInner();
- return function (seriesModel) {
+ return function (seriesModel: SeriesModel): StageHandlerPlanReturn {
var fields = inner(seriesModel);
var pipelineContext = seriesModel.pipelineContext;
@@ -38,6 +42,8 @@ export default function () {
var large = fields.large = pipelineContext && pipelineContext.large;
var progressive = fields.progressiveRender = pipelineContext && pipelineContext.progressiveRender;
- return !!((originalLarge ^ large) || (originalProgressive ^ progressive)) && 'reset';
+ return (
+ !!((originalLarge ^ large as any) || (originalProgressive ^ progressive as any)) && 'reset'
+ ) as StageHandlerPlanReturn;
};
}
diff --git a/src/component/dataZoom/dataZoomProcessor.ts b/src/component/dataZoom/dataZoomProcessor.ts
index cfd1406..c58e8b9 100644
--- a/src/component/dataZoom/dataZoomProcessor.ts
+++ b/src/component/dataZoom/dataZoomProcessor.ts
@@ -17,6 +17,8 @@
* under the License.
*/
+// @ts-nocheck
+
import * as echarts from '../../echarts';
import {createHashMap, each} from 'zrender/src/core/util';
@@ -40,7 +42,8 @@ echarts.registerProcessor({
return seriesModelMap;
},
- modifyOutputEnd: true,
+ // FIXME:TS never used, so comment it
+ // modifyOutputEnd: true,
// Consider appendData, where filter should be performed. Because data process is
// in block mode currently, it is not need to worry about that the overallProgress
diff --git a/src/component/dataset.ts b/src/component/dataset.ts
index 18f2022..dbae472 100644
--- a/src/component/dataset.ts
+++ b/src/component/dataset.ts
@@ -17,6 +17,8 @@
* under the License.
*/
+// @ts-nocheck
+
/**
* This module is imported by echarts directly.
*
@@ -29,7 +31,7 @@
import ComponentModel from '../model/Component';
import ComponentView from '../view/Component';
import {detectSourceFormat} from '../data/helper/sourceHelper';
-import {SERIES_LAYOUT_BY_COLUMN} from '../data/helper/sourceType';
+import { SERIES_LAYOUT_BY_COLUMN } from '../util/types';
ComponentModel.extend({
diff --git a/src/component/helper/BrushController.ts b/src/component/helper/BrushController.ts
index c8fb8cd..92d5c47 100644
--- a/src/component/helper/BrushController.ts
+++ b/src/component/helper/BrushController.ts
@@ -17,9 +17,11 @@
* under the License.
*/
+// @ts-nocheck
+
import {__DEV__} from '../../config';
import * as zrUtil from 'zrender/src/core/util';
-import Eventful from 'zrender/src/mixin/Eventful';
+import Eventful from 'zrender/src/core/Eventful';
import * as graphic from '../../util/graphic';
import * as interactionMutex from './interactionMutex';
import DataDiffer from '../../data/DataDiffer';
diff --git a/src/component/helper/RoamController.ts b/src/component/helper/RoamController.ts
index 2124837..1b1f00d 100644
--- a/src/component/helper/RoamController.ts
+++ b/src/component/helper/RoamController.ts
@@ -17,8 +17,10 @@
* under the License.
*/
+// @ts-nocheck
+
import * as zrUtil from 'zrender/src/core/util';
-import Eventful from 'zrender/src/mixin/Eventful';
+import Eventful from 'zrender/src/core/Eventful';
import * as eventTool from 'zrender/src/core/event';
import * as interactionMutex from './interactionMutex';
diff --git a/src/component/marker/MarkerModel.ts b/src/component/marker/MarkerModel.ts
index 8c3a314..d7269bc 100644
--- a/src/component/marker/MarkerModel.ts
+++ b/src/component/marker/MarkerModel.ts
@@ -17,13 +17,15 @@
* under the License.
*/
+// @ts-nocheck
+
import {__DEV__} from '../../config';
import * as echarts from '../../echarts';
import * as zrUtil from 'zrender/src/core/util';
import env from 'zrender/src/core/env';
import * as modelUtil from '../../util/model';
import * as formatUtil from '../../util/format';
-import dataFormatMixin from '../../model/mixin/dataFormat';
+import DataFormatMixin from '../../model/mixin/dataFormat';
var addCommas = formatUtil.addCommas;
var encodeHTML = formatUtil.encodeHTML;
@@ -152,6 +154,6 @@ var MarkerModel = echarts.extendComponentModel({
}
});
-zrUtil.mixin(MarkerModel, dataFormatMixin);
+zrUtil.mixin(MarkerModel, DataFormatMixin.prototype);
export default MarkerModel;
\ No newline at end of file
diff --git a/src/component/timeline/SliderTimelineModel.ts b/src/component/timeline/SliderTimelineModel.ts
index a70bab3..517ad43 100644
--- a/src/component/timeline/SliderTimelineModel.ts
+++ b/src/component/timeline/SliderTimelineModel.ts
@@ -17,9 +17,11 @@
* under the License.
*/
+// @ts-nocheck
+
import * as zrUtil from 'zrender/src/core/util';
import TimelineModel from './TimelineModel';
-import dataFormatMixin from '../../model/mixin/dataFormat';
+import DataFormatMixin from '../../model/mixin/dataFormat';
var SliderTimelineModel = TimelineModel.extend({
@@ -117,6 +119,6 @@ var SliderTimelineModel = TimelineModel.extend({
});
-zrUtil.mixin(SliderTimelineModel, dataFormatMixin);
+zrUtil.mixin(SliderTimelineModel, DataFormatMixin.prototype);
export default SliderTimelineModel;
\ No newline at end of file
diff --git a/src/config.ts b/src/config.ts
index 06283ed..430f86e 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -27,11 +27,11 @@ var dev;
// In browser
if (typeof window !== 'undefined') {
- dev = window.__DEV__;
+ dev = (window as any).__DEV__;
}
// In node
else if (typeof global !== 'undefined') {
- dev = global.__DEV__;
+ dev = (global as any).__DEV__;
}
if (typeof dev === 'undefined') {
diff --git a/src/coord/CoordinateSystem.ts b/src/coord/CoordinateSystem.ts
new file mode 100644
index 0000000..6b98390
--- /dev/null
+++ b/src/coord/CoordinateSystem.ts
@@ -0,0 +1,86 @@
+/*
+* 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.
+*/
+
+// @ts-nocheck
+
+import Model from '../model/Model';
+import GlobalModel from '../model/Global';
+import {ModelFinder} from '../util/model';
+import ExtensionAPI from '../ExtensionAPI';
+import { DimensionDefinitionLoose } from '../util/types';
+
+export interface CoordinateSystemCreator {
+
+ create: (ecModel: GlobalModel, api: ExtensionAPI) => CoordinateSystem;
+
+ // FIXME current dimensions must be string[].
+ // check and unify the definition.
+ dimensions: string[];
+
+ // dimensionsInfo like [{name: ..., type: ...}, 'xxx', ...]
+ getDimensionsInfo?: () => DimensionDefinitionLoose[];
+
+}
+
+export interface CoordinateSystem {
+
+ // FIXME current dimensions must be string[].
+ // check and unify the definition.
+ dimensions: string[];
+
+ model: Model;
+
+ update: (ecModel: GlobalModel, api: ExtensionAPI) => void;
+
+ // @return {module:echarts/coord/Axis}
+ getAxis: (dim: string) => any; // FIXME:TS temp any
+
+ // @return {Array.<module:echarts/coord/Axis>}
+ getAxes?: () => [] // FIXME:TS temp any
+
+ axisPointerEnabled?: () => boolean;
+
+ // @param {*|Array.<*>} data
+ // @param {*} Defined by the coordinate system itself
+ // @param {Array.<*>} out
+ // @return {Array.<number>} point Point in global pixel coordinate system.
+ dataToPoint: (...args) => number[];
+
+ // @param {Array.<number>} point Point in global pixel coordinate system.
+ // @param {*} Defined by the coordinate system itself
+ // @param {Array.<*>} out
+ // @return {*|Array.<*>} data
+ pointToData: (...args) => any;
+
+ // @param point Point in global pixel coordinate system.
+ containPoint: (point: number[]) => boolean;
+
+ // This methods is also responsible for determine whether this
+ // coodinate system is applicable to the given `finder`.
+ // Each coordinate system will be tried, util one returns none
+ // null/undefined value.
+ convertToPixel: (ecModel: any, finder: ModelFinder, value: any) => number | number[];
+
+ // This methods is also responsible for determine whether this
+ // coodinate system is applicable to the given `finder`.
+ // Each coordinate system will be tried, util one returns none
+ // null/undefined value.
+ convertFromPixel: (ecModel: any, finder: ModelFinder, pixelValue: number | number[]) => any;
+
+}
\ No newline at end of file
diff --git a/src/coord/ICoordinateSystem b/src/coord/ICoordinateSystem
deleted file mode 100644
index 592c85d..0000000
--- a/src/coord/ICoordinateSystem
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
-* 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.
-*/
-
-/**
- * Coordinate System Interface:
- *
- *
- * Class members:
- *
- * + dimensions {Array.<strign>}: mandatory
- *
- *
- * Instance members:
- *
- * + dimensions {Array.<strign>}: mandatory
- *
- * + model {module:echarts/model/Model}: mandatory
- *
- * + create: mandatory
- * @param {module:echarts/model/Global} ecModel
- * @param {module:echarts/ExtensionAPI} api
- * @return {Object} coordinate system instance
- *
- * + update: mandatory
- * @param {module:echarts/model/Global} ecModel
- * @param {module:echarts/ExtensionAPI} api
- *
- * + getAxis {Function}: mandatory
- * @param {string} dim
- * @return {module:echarts/coord/Axis}
- *
- * + getAxes: {Function}: optional
- * @return {Array.<module:echarts/coord/Axis>}
- *
- * + axisPointerEnabled {boolean}
- *
- * + dataToPoint {Function}: mandatory
- * @param {*|Array.<*>} data
- * @param {*} Defined by the coordinate system itself
- * @param {Array.<*>} out
- * @return {Array.<number>} point Point in global pixel coordinate system.
- *
- * + pointToData {Function}: mandatory
- * @param {Array.<number>} point Point in global pixel coordinate system.
- * @param {*} Defined by the coordinate system itself
- * @param {Array.<*>} out
- * @return {*|Array.<*>} data
- *
- * + containPoint {Function}: mandatory
- * @param {Array.<number>} point Point in global pixel coordinate system.
- * @return {boolean}
- *
- * + getDimensionsInfo {Function}: optional
- * @return {Array.<string|Object>} dimensionsInfo
- * Like [{name: ..., type: ...}, 'xxx', ...]
- *
- * + convertToPixel:
- * + convertFromPixel:
- * These two methods is also responsible for determine whether this
- * coodinate system is applicable to the given `finder`.
- * Each coordinate system will be tried, util one returns none
- * null/undefined value.
- * @param {module:echarts/model/Global} ecModel
- * @param {Object} finder
- * @param {Array|number} value
- * @return {Array|number} convert result.
- *
- *
- */
diff --git a/src/coord/View.ts b/src/coord/View.ts
index 4043107..be65472 100644
--- a/src/coord/View.ts
+++ b/src/coord/View.ts
@@ -17,6 +17,8 @@
* under the License.
*/
+// @ts-nocheck
+
/**
* Simple view coordinate system
* Mapping given x, y to transformd view x, y
@@ -26,7 +28,7 @@ import * as zrUtil from 'zrender/src/core/util';
import * as vector from 'zrender/src/core/vector';
import * as matrix from 'zrender/src/core/matrix';
import BoundingRect from 'zrender/src/core/BoundingRect';
-import Transformable from 'zrender/src/mixin/Transformable';
+import Transformable from 'zrender/src/core/Transformable';
var v2ApplyTransform = vector.applyTransform;
diff --git a/src/coord/geo/mapDataStorage.ts b/src/coord/geo/mapDataStorage.ts
index 8f6de57..54be749 100644
--- a/src/coord/geo/mapDataStorage.ts
+++ b/src/coord/geo/mapDataStorage.ts
@@ -17,39 +17,94 @@
* under the License.
*/
+// @ts-nocheck
+
import {__DEV__} from '../../config';
import {createHashMap, isString, isArray, each, assert} from 'zrender/src/core/util';
import {parseXML} from 'zrender/src/tool/parseSVG';
-
-var storage = createHashMap();
-
// For minimize the code size of common echarts package,
// do not put too much logic in this module.
+export type GeoMapSVGSource = 'string' | Document;
+export type GeoMapGeoJSONSource = 'string' | object;
+export type GeoSpecialAreas = object;
+
+interface GeoMapGeoJSONDefinition {
+ geoJSON?: GeoMapGeoJSONSource;
+ geoJson?: GeoMapGeoJSONSource;
+ specialAreas?: GeoSpecialAreas;
+}
+interface GeoMapSVGDefinition {
+ svg?: GeoMapSVGSource;
+ specialAreas?: GeoSpecialAreas;
+}
+export type GeoMapDefinition = GeoMapGeoJSONDefinition | GeoMapSVGDefinition;
+
+interface GeoMapRecord {
+ type: 'geoJSON' | 'svg';
+ source: GeoMapGeoJSONSource | GeoMapSVGSource;
+ specialAreas: GeoSpecialAreas;
+ geoJSON: object;
+ svgXML: Node
+}
+
+
+var storage = createHashMap<GeoMapRecord[]>();
+
+
export default {
- // The format of record: see `echarts.registerMap`.
- // Compatible with previous `echarts.registerMap`.
- registerMap: function (mapName, rawGeoJson, rawSpecialAreas) {
+ /**
+ * Compatible with previous `echarts.registerMap`.
+ * @usage
+ * ```js
+ * $.get('USA.json', function (geoJson) {
+ * echarts.registerMap('USA', geoJson);
+ * // Or
+ * echarts.registerMap('USA', {
+ * geoJson: geoJson,
+ * specialAreas: {}
+ * })
+ * });
+ *
+ * $.get('airport.svg', function (svg) {
+ * echarts.registerMap('airport', {
+ * svg: svg
+ * }
+ * });
+ *
+ * echarts.registerMap('eu', [
+ * {svg: eu-topographic.svg},
+ * {geoJSON: eu.json}
+ * ])
+ * ```
+ */
+ registerMap: function (
+ mapName: string,
+ rawGeoJson: GeoMapDefinition | GeoMapDefinition[] | GeoMapGeoJSONSource,
+ rawSpecialAreas?: GeoSpecialAreas
+ ): GeoMapRecord[] {
var records;
if (isArray(rawGeoJson)) {
records = rawGeoJson;
}
- else if (rawGeoJson.svg) {
+ else if ((rawGeoJson as GeoMapSVGDefinition).svg) {
records = [{
type: 'svg',
- source: rawGeoJson.svg,
- specialAreas: rawGeoJson.specialAreas
+ source: (rawGeoJson as GeoMapSVGDefinition).svg,
+ specialAreas: (rawGeoJson as GeoMapSVGDefinition).specialAreas
}];
}
else {
// Backward compatibility.
- if (rawGeoJson.geoJson && !rawGeoJson.features) {
- rawSpecialAreas = rawGeoJson.specialAreas;
- rawGeoJson = rawGeoJson.geoJson;
+ var geoSource = (rawGeoJson as GeoMapGeoJSONDefinition).geoJson
+ || (rawGeoJson as GeoMapGeoJSONDefinition).geoJSON;
+ if (geoSource && !(rawGeoJson as any).features) {
+ rawSpecialAreas = (rawGeoJson as GeoMapGeoJSONDefinition).specialAreas;
+ rawGeoJson = geoSource;
}
records = [{
type: 'geoJSON',
@@ -74,7 +129,7 @@ export default {
return storage.set(mapName, records);
},
- retrieveMap: function (mapName) {
+ retrieveMap: function (mapName: string): GeoMapRecord[] {
return storage.get(mapName);
}
@@ -82,7 +137,7 @@ export default {
var parsers = {
- geoJSON: function (record) {
+ geoJSON: function (record: GeoMapRecord): void {
var source = record.source;
record.geoJSON = !isString(source)
? source
@@ -97,8 +152,8 @@ var parsers = {
// if we do it here, the clone of zrender elements has to be
// required. So we do it once for each geo instance, util real
// performance issues call for optimizing it.
- svg: function (record) {
- record.svgXML = parseXML(record.source);
+ svg: function (record: GeoMapRecord): void {
+ record.svgXML = parseXML(record.source as GeoMapSVGSource);
}
};
diff --git a/src/data/DataDiffer.ts b/src/data/DataDiffer.ts
index 27e66d8..76c75dc 100644
--- a/src/data/DataDiffer.ts
+++ b/src/data/DataDiffer.ts
@@ -17,68 +17,88 @@
* under the License.
*/
+import {ArrayLike} from 'zrender/src/core/types';
-function defaultKeyGetter(item) {
+// return key.
+export type DiffKeyGetter = (this: DataDiffer, value: any, index: number) => string;
+export type DiffCallbackAdd = (newIndex: number) => void
+export type DiffCallbackUpdate = (newIndex: number, oldIndex: number) => void
+export type DiffCallbackRemove = (oldIndex: number) => void
+
+type DataIndexMap = {[key: string]: number | number[]};
+
+function defaultKeyGetter<T>(item: T): T {
return item;
}
-/**
- * @param {Array} oldArr
- * @param {Array} newArr
- * @param {Function} oldKeyGetter
- * @param {Function} newKeyGetter
- * @param {Object} [context] Can be visited by this.context in callback.
- */
-function DataDiffer(oldArr, newArr, oldKeyGetter, newKeyGetter, context) {
- this._old = oldArr;
- this._new = newArr;
-
- this._oldKeyGetter = oldKeyGetter || defaultKeyGetter;
- this._newKeyGetter = newKeyGetter || defaultKeyGetter;
-
- this.context = context;
-}
+class DataDiffer {
+
+ private _old: ArrayLike<any>;
+ private _new: ArrayLike<any>;
+ private _oldKeyGetter: DiffKeyGetter;
+ private _newKeyGetter: DiffKeyGetter;
+ private _add: DiffCallbackAdd;
+ private _update: DiffCallbackUpdate;
+ private _remove: DiffCallbackRemove;
-DataDiffer.prototype = {
+ readonly context: any;
- constructor: DataDiffer,
+ /**
+ * @param context Can be visited by this.context in callback.
+ */
+ constructor(
+ oldArr: ArrayLike<any>,
+ newArr: ArrayLike<any>,
+ oldKeyGetter: DiffKeyGetter,
+ newKeyGetter: DiffKeyGetter,
+ context?: any
+ ) {
+ this._old = oldArr;
+ this._new = newArr;
+
+ this._oldKeyGetter = oldKeyGetter || defaultKeyGetter;
+ this._newKeyGetter = newKeyGetter || defaultKeyGetter;
+
+ // Visible in callback via `this.context`;
+ this.context = context;
+ }
/**
* Callback function when add a data
*/
- add: function (func) {
+ add(func: DiffCallbackAdd): DataDiffer {
this._add = func;
return this;
- },
+ }
/**
* Callback function when update a data
*/
- update: function (func) {
+ update(func: DiffCallbackUpdate): DataDiffer {
this._update = func;
return this;
- },
+ }
/**
* Callback function when remove a data
*/
- remove: function (func) {
+ remove(func: DiffCallbackRemove): DataDiffer {
this._remove = func;
return this;
- },
+ }
- execute: function () {
+ execute(): void {
var oldArr = this._old;
var newArr = this._new;
- var oldDataIndexMap = {};
- var newDataIndexMap = {};
- var oldDataKeyArr = [];
- var newDataKeyArr = [];
+ var oldDataIndexMap: DataIndexMap = {};
+ var newDataIndexMap: DataIndexMap = {};
+ var oldDataKeyArr: string[] = [];
+ var newDataKeyArr: string[] = [];
var i;
- initIndexMap(oldArr, oldDataIndexMap, oldDataKeyArr, '_oldKeyGetter', this);
- initIndexMap(newArr, newDataIndexMap, newDataKeyArr, '_newKeyGetter', this);
+ this._initIndexMap(oldArr, oldDataIndexMap, oldDataKeyArr, '_oldKeyGetter');
+ this._initIndexMap(newArr, newDataIndexMap, newDataKeyArr, '_newKeyGetter');
for (i = 0; i < oldArr.length; i++) {
var key = oldDataKeyArr[i];
@@ -88,22 +108,22 @@ DataDiffer.prototype = {
if (idx != null) {
// Consider there is duplicate key (for example, use dataItem.name as key).
// We should make sure every item in newArr and oldArr can be visited.
- var len = idx.length;
+ var len = (idx as number[]).length;
if (len) {
len === 1 && (newDataIndexMap[key] = null);
- idx = idx.shift();
+ idx = (idx as number[]).shift();
}
else {
newDataIndexMap[key] = null;
}
- this._update && this._update(idx, i);
+ this._update && this._update(idx as number, i);
}
else {
this._remove && this._remove(i);
}
}
- for (var i = 0; i < newDataKeyArr.length; i++) {
+ for (i = 0; i < newDataKeyArr.length; i++) {
var key = newDataKeyArr[i];
if (newDataIndexMap.hasOwnProperty(key)) {
var idx = newDataIndexMap[key];
@@ -111,35 +131,41 @@ DataDiffer.prototype = {
continue;
}
// idx can never be empty array here. see 'set null' logic above.
- if (!idx.length) {
- this._add && this._add(idx);
+ if (!(idx as number[]).length) {
+ this._add && this._add(idx as number);
}
else {
- for (var j = 0, len = idx.length; j < len; j++) {
- this._add && this._add(idx[j]);
+ for (var j = 0, len = (idx as number[]).length; j < len; j++) {
+ this._add && this._add((idx as number[])[j]);
}
}
}
}
}
-};
-
-function initIndexMap(arr, map, keyArr, keyGetterName, dataDiffer) {
- for (var i = 0; i < arr.length; i++) {
- // Add prefix to avoid conflict with Object.prototype.
- var key = '_ec_' + dataDiffer[keyGetterName](arr[i], i);
- var existence = map[key];
- if (existence == null) {
- keyArr.push(key);
- map[key] = i;
- }
- else {
- if (!existence.length) {
- map[key] = existence = [existence];
+
+ private _initIndexMap(
+ arr: ArrayLike<any>,
+ map: DataIndexMap,
+ keyArr: string[],
+ keyGetterName: '_oldKeyGetter' | '_newKeyGetter'
+ ): void {
+ for (var i = 0; i < arr.length; i++) {
+ // Add prefix to avoid conflict with Object.prototype.
+ var key = '_ec_' + this[keyGetterName](arr[i], i);
+ var existence = map[key];
+ if (existence == null) {
+ keyArr.push(key);
+ map[key] = i;
+ }
+ else {
+ if (!(existence as number[]).length) {
+ map[key] = existence = [existence as number];
+ }
+ (existence as number[]).push(i);
}
- existence.push(i);
}
}
+
}
-export default DataDiffer;
\ No newline at end of file
+export default DataDiffer;
diff --git a/src/data/DataDimensionInfo.ts b/src/data/DataDimensionInfo.ts
index 3bda37d..f27a5b4 100644
--- a/src/data/DataDimensionInfo.ts
+++ b/src/data/DataDimensionInfo.ts
@@ -18,30 +18,33 @@
*/
import * as zrUtil from 'zrender/src/core/util';
+import OrdinalMeta from './OrdinalMeta';
+import { DataVisualDimensions, DimensionType } from '../util/types';
-/**
- * @class
- * @param {Object|DataDimensionInfo} [opt] All of the fields will be shallow copied.
- */
-function DataDimensionInfo(opt) {
- if (opt != null) {
- zrUtil.extend(this, opt);
- }
+class DataDimensionInfo {
+
+ /**
+ * Dimension type. The enumerable values are the key of
+ * `dataCtors` of `data/List`.
+ * Optional.
+ */
+ type: DimensionType;
/**
* Dimension name.
* Mandatory.
- * @type {string}
*/
- // this.name;
-
+ name: string;
/**
* The origin name in dimsDef, see source helper.
* If displayName given, the tooltip will displayed vertically.
* Optional.
- * @type {string}
*/
- // this.displayName;
+ displayName: string;
+
+ // FIXME: check whether it is still used.
+ // See Series.ts#formatArrayValue
+ tooltip?: boolean
/**
* Which coordSys dimension this dimension mapped to.
@@ -50,31 +53,20 @@ function DataDimensionInfo(opt) {
* or an generated "extra coord name" if does not mapped to any "coordSysDim"
* (That is determined by whether `isExtraCoord` is `true`).
* Mandatory.
- * @type {string}
*/
- // this.coordDim;
+ coordDim: string;
/**
* The index of this dimension in `series.encode[coordDim]`.
* Mandatory.
- * @type {number}
*/
- // this.coordDimIndex;
-
- /**
- * Dimension type. The enumerable values are the key of
- * `dataCtors` of `data/List`.
- * Optional.
- * @type {string}
- */
- // this.type;
+ coordDimIndex: number;
/**
* This index of this dimension info in `data/List#_dimensionInfos`.
* Mandatory after added to `data/List`.
- * @type {number}
*/
- // this.index;
+ index: number;
/**
* The format of `otherDims` is:
@@ -108,28 +100,34 @@ function DataDimensionInfo(opt) {
* ```
*
* This prop should never be `null`/`undefined` after initialized.
- * @type {Object}
*/
- this.otherDims = {};
+ otherDims: DataVisualDimensions = {};
/**
* Be `true` if this dimension is not mapped to any "coordSysDim" that the
* "coordSys" required.
* Mandatory.
- * @type {boolean}
*/
- // this.isExtraCoord;
+ isExtraCoord: boolean;
+
+ defaultTooltip: boolean;
+
+ ordinalMeta: OrdinalMeta;
/**
- * @type {module:data/OrdinalMeta}
+ * Whether to create inverted indices.
*/
- // this.ordinalMeta;
+ createInvertedIndices: boolean;
/**
- * Whether to create inverted indices.
- * @type {boolean}
+ * @param opt All of the fields will be shallow copied.
*/
- // this.createInvertedIndices;
+ constructor(opt: object | DataDimensionInfo) {
+ if (opt != null) {
+ zrUtil.extend(this, opt);
+ }
+ }
+
};
export default DataDimensionInfo;
diff --git a/src/data/List.ts b/src/data/List.ts
index 7ec46e6..f9d7ac1 100644
--- a/src/data/List.ts
+++ b/src/data/List.ts
@@ -28,10 +28,19 @@ import {__DEV__} from '../config';
import * as zrUtil from 'zrender/src/core/util';
import Model from '../model/Model';
import DataDiffer from './DataDiffer';
-import Source from './Source';
-import {defaultDimValueGetters, DefaultDataProvider} from './helper/dataProvider';
-import {summarizeDimensions} from './helper/dimensionHelper';
+import Source, { SourceConstructor } from './Source';
+import {DefaultDataProvider, DataProvider} from './helper/dataProvider';
+import {summarizeDimensions, DimensionSummary, DimensionUserOuput} from './helper/dimensionHelper';
import DataDimensionInfo from './DataDimensionInfo';
+import {ArrayLike, Dictionary, FunctionPropertyNames} from 'zrender/src/core/types';
+import Element from 'zrender/src/Element';
+import {
+ DimensionIndex, DimensionName, ECElement, DimensionLoose, OptionDataItem,
+ ParsedDataValue, ParsedDataNumeric, OrdinalRawValueIndex
+} from '../util/types';
+import {parseDate} from '../util/number';
+import {isDataItemOption} from '../util/model';
+
var isObject = zrUtil.isObject;
@@ -53,22 +62,54 @@ var dataCtors = {
'time': Array
};
+export type ListDimensionType = keyof typeof dataCtors;
+
// Caution: MUST not use `new CtorUint32Array(arr, 0, len)`, because the Ctor of array is
// different from the Ctor of typed array.
var CtorUint32Array = typeof Uint32Array === UNDEFINED ? Array : Uint32Array;
var CtorInt32Array = typeof Int32Array === UNDEFINED ? Array : Int32Array;
var CtorUint16Array = typeof Uint16Array === UNDEFINED ? Array : Uint16Array;
-function getIndicesCtor(list) {
- // The possible max value in this._indicies is always this._rawCount despite of filtering.
- return list._rawCount > 65535 ? CtorUint32Array : CtorUint16Array;
-}
+type DataTypedArray = Uint32Array | Int32Array | Uint16Array | Float64Array;
+type DataTypedArrayConstructor = typeof Uint32Array | typeof Int32Array | typeof Uint16Array | typeof Float64Array;
+type DataArrayLikeConstructor = typeof Array | DataTypedArrayConstructor;
+
+
+
+type DimValueGetter = (
+ this: List,
+ dataItem: any,
+ dimName: DimensionName,
+ dataIndex: number,
+ dimIndex: DimensionIndex
+) => ParsedDataValue;
+
+type DataValueChunk = ArrayLike<ParsedDataValue>;
+type DataStorage = {[dimName: string]: DataValueChunk[]};
+type NameRepeatCount = {[name: string]: number};
+
+
+type ItrParamDims = DimensionLoose | Array<DimensionLoose>;
+// If Ctx not specified, use List as Ctx
+type CtxOrList<Ctx> = unknown extends Ctx ? List : Ctx;
+type EachCb0<Ctx> = (this: CtxOrList<Ctx>, idx: number) => void;
+type EachCb1<Ctx> = (this: CtxOrList<Ctx>, x: ParsedDataValue, idx: number) => void;
+type EachCb2<Ctx> = (this: CtxOrList<Ctx>, x: ParsedDataValue, y: ParsedDataValue, idx: number) => void;
+type EachCb<Ctx> = (this: CtxOrList<Ctx>, ...args: any) => void;
+type FilterCb0<Ctx> = (this: CtxOrList<Ctx>, idx: number) => boolean;
+type FilterCb1<Ctx> = (this: CtxOrList<Ctx>, x: ParsedDataValue, idx: number) => boolean;
+type FilterCb2<Ctx> = (this: CtxOrList<Ctx>, x: ParsedDataValue, y: ParsedDataValue, idx: number) => boolean;
+type FilterCb<Ctx> = (this: CtxOrList<Ctx>, ...args: any) => boolean;
+type MapArrayCb0<Ctx> = (this: CtxOrList<Ctx>, idx: number) => any;
+type MapArrayCb1<Ctx> = (this: CtxOrList<Ctx>, x: ParsedDataValue, idx: number) => any;
+type MapArrayCb2<Ctx> = (this: CtxOrList<Ctx>, x: ParsedDataValue, y: ParsedDataValue, idx: number) => any;
+type MapArrayCb<Ctx> = (this: CtxOrList<Ctx>, ...args: any) => any;
+type MapCb1<Ctx> = (this: CtxOrList<Ctx>, x: ParsedDataValue, idx: number) => ParsedDataValue | ParsedDataValue[];
+type MapCb2<Ctx> = (this: CtxOrList<Ctx>, x: ParsedDataValue, y: ParsedDataValue, idx: number) =>
+ ParsedDataValue | ParsedDataValue[];
+type MapCb<Ctx> = (this: CtxOrList<Ctx>, ...args: any) => ParsedDataValue | ParsedDataValue[];
+
-function cloneChunk(originalChunk) {
- var Ctor = originalChunk.constructor;
- // Only shallow clone is enough when Array.
- return Ctor === Array ? originalChunk.slice() : new Ctor(originalChunk);
-}
var TRANSFERABLE_PROPERTIES = [
'hasItemOption', '_nameList', '_idList', '_invertedIndicesMap',
@@ -79,1960 +120,2005 @@ var CLONE_PROPERTIES = [
'_extent', '_approximateExtent', '_rawExtent'
];
-function transferProperties(target, source) {
- zrUtil.each(TRANSFERABLE_PROPERTIES.concat(source.__wrappedMethods || []), function (propName) {
- if (source.hasOwnProperty(propName)) {
- target[propName] = source[propName];
- }
- });
- target.__wrappedMethods = source.__wrappedMethods;
- zrUtil.each(CLONE_PROPERTIES, function (propName) {
- target[propName] = zrUtil.clone(source[propName]);
- });
+class List {
- target._calculationInfo = zrUtil.extend(source._calculationInfo);
-}
+ readonly type = 'list';
+ readonly dimensions: string[];
+ // Infomation of each data dimension, like data type.
+ private _dimensionInfos: {[dimName: string]: DataDimensionInfo};
+ readonly hostModel: Model;
+ readonly dataType: string;
-/**
- * @constructor
- * @alias module:echarts/data/List
- *
- * @param {Array.<string|Object|module:data/DataDimensionInfo>} dimensions
- * For example, ['someDimName', {name: 'someDimName', type: 'someDimType'}, ...].
- * Dimensions should be concrete names like x, y, z, lng, lat, angle, radius
- * @param {module:echarts/model/Model} hostModel
- */
-var List = function (dimensions, hostModel) {
+ // Indices stores the indices of data subset after filtered.
+ // This data subset will be used in chart.
+ private _indices: ArrayLike<any>;
- dimensions = dimensions || ['x', 'y'];
+ private _count: number = 0;
+ private _rawCount: number = 0;
+ private _storage: DataStorage = {};
+ private _nameList: string[] = [];
+ private _idList: string[] = [];
- var dimensionInfos = {};
- var dimensionNames = [];
- var invertedIndicesMap = {};
+ // Models of data option is stored sparse for optimizing memory cost
+ // Never used yet (not used yet).
+ // private _optionModels: Model[] = [];
- for (var i = 0; i < dimensions.length; i++) {
- // Use the original dimensions[i], where other flag props may exists.
- var dimensionInfo = dimensions[i];
+ // Global visual properties after visual coding
+ private _visual: Dictionary<any> = {};
- if (zrUtil.isString(dimensionInfo)) {
- dimensionInfo = new DataDimensionInfo({name: dimensionInfo});
- }
- else if (!(dimensionInfo instanceof DataDimensionInfo)) {
- dimensionInfo = new DataDimensionInfo(dimensionInfo);
- }
+ // Globel layout properties.
+ private _layout: Dictionary<any> = {};
- var dimensionName = dimensionInfo.name;
- dimensionInfo.type = dimensionInfo.type || 'float';
- if (!dimensionInfo.coordDim) {
- dimensionInfo.coordDim = dimensionName;
- dimensionInfo.coordDimIndex = 0;
- }
+ // Item visual properties after visual coding
+ private _itemVisuals: Dictionary<any>[] = [];
- dimensionInfo.otherDims = dimensionInfo.otherDims || {};
- dimensionNames.push(dimensionName);
- dimensionInfos[dimensionName] = dimensionInfo;
+ // Key: visual type, Value: boolean
+ // @readonly
+ hasItemVisual: Dictionary<boolean> = {};
- dimensionInfo.index = i;
+ // Item layout properties after layout
+ private _itemLayouts: any[] = [];
- if (dimensionInfo.createInvertedIndices) {
- invertedIndicesMap[dimensionName] = [];
- }
- }
+ // Graphic elemnents
+ private _graphicEls: Element[] = [];
- /**
- * @readOnly
- * @type {Array.<string>}
- */
- this.dimensions = dimensionNames;
+ // Max size of each chunk.
+ private _chunkSize: number = 1e5;
- /**
- * Infomation of each data dimension, like data type.
- * @type {Object}
- */
- this._dimensionInfos = dimensionInfos;
+ private _chunkCount: number = 0;
- /**
- * @type {module:echarts/model/Model}
- */
- this.hostModel = hostModel;
+ private _rawData: DataProvider;
- /**
- * @type {module:echarts/model/Model}
- */
- this.dataType;
+ // Raw extent will not be cloned, but only transfered.
+ // It will not be calculated util needed.
+ private _rawExtent: {[dimName: string]: [number, number]} = {};
- /**
- * Indices stores the indices of data subset after filtered.
- * This data subset will be used in chart.
- * @type {Array.<number>}
- * @readOnly
- */
- this._indices = null;
+ private _extent: {[dimName: string]: [number, number]} = {};
- this._count = 0;
- this._rawCount = 0;
+ // key: dim, value: extent
+ private _approximateExtent: {[dimName: string]: [number, number]} = {};
- /**
- * Data storage
- * @type {Object.<key, Array.<TypedArray|Array>>}
- * @private
- */
- this._storage = {};
+ private _dimensionsSummary: DimensionSummary;
- /**
- * @type {Array.<string>}
- */
- this._nameList = [];
- /**
- * @type {Array.<string>}
- */
- this._idList = [];
+ private _invertedIndicesMap: {[dimName: string]: ArrayLike<number>};
- /**
- * Models of data option is stored sparse for optimizing memory cost
- * @type {Array.<module:echarts/model/Model>}
- * @private
- */
- this._optionModels = [];
+ private _calculationInfo: {[key: string]: any} = {};
- /**
- * Global visual properties after visual coding
- * @type {Object}
- * @private
- */
- this._visual = {};
+ // User output info of this data.
+ // DO NOT use it in other places!
+ // When preparing user params for user callbacks, we have
+ // to clone these inner data structures to prevent users
+ // from modifying them to effect built-in logic. And for
+ // performance consideration we make this `userOutput` to
+ // avoid clone them too many times.
+ readonly userOutput: DimensionUserOuput;
- /**
- * Globel layout properties.
- * @type {Object}
- * @private
- */
- this._layout = {};
+ // If each data item has it's own option
+ hasItemOption: boolean = true;
- /**
- * Item visual properties after visual coding
- * @type {Array.<Object>}
- * @private
- */
- this._itemVisuals = [];
+ // @readonly
+ defaultDimValueGetter: DimValueGetter;
+ private _dimValueGetter: DimValueGetter;
+ private _dimValueGetterArrayRows: DimValueGetter;
- /**
- * Key: visual type, Value: boolean
- * @type {Object}
- * @readOnly
- */
- this.hasItemVisual = {};
+ private _nameRepeatCount: NameRepeatCount;
+ private _nameDimIdx: number;
+ private _idDimIdx: number;
- /**
- * Item layout properties after layout
- * @type {Array.<Object>}
- * @private
- */
- this._itemLayouts = [];
+ private __wrappedMethods: string[];
- /**
- * Graphic elemnents
- * @type {Array.<module:zrender/Element>}
- * @private
- */
- this._graphicEls = [];
+ // Methods that create a new list based on this list should be listed here.
+ // Notice that those method should `RETURN` the new list.
+ TRANSFERABLE_METHODS = ['cloneShallow', 'downSample', 'map'];
+ // Methods that change indices of this list should be listed here.
+ CHANGABLE_METHODS = ['filterSelf', 'selectRange'];
- /**
- * Max size of each chunk.
- * @type {number}
- * @private
- */
- this._chunkSize = 1e5;
/**
- * @type {number}
- * @private
+ * @param dimensions
+ * For example, ['someDimName', {name: 'someDimName', type: 'someDimType'}, ...].
+ * Dimensions should be concrete names like x, y, z, lng, lat, angle, radius
*/
- this._chunkCount = 0;
+ constructor(dimensions: Array<string | object | DataDimensionInfo>, hostModel: Model) {
+ dimensions = dimensions || ['x', 'y'];
+
+ var dimensionInfos: Dictionary<DataDimensionInfo> = {};
+ var dimensionNames = [];
+ var invertedIndicesMap: Dictionary<number[]> = {};
+
+ for (var i = 0; i < dimensions.length; i++) {
+ // Use the original dimensions[i], where other flag props may exists.
+ var dimInfoInput = dimensions[i];
+
+ var dimensionInfo: DataDimensionInfo =
+ zrUtil.isString(dimInfoInput)
+ ? new DataDimensionInfo({name: dimInfoInput})
+ : !(dimInfoInput instanceof DataDimensionInfo)
+ ? new DataDimensionInfo(dimInfoInput)
+ : dimInfoInput;
+
+ var dimensionName = dimensionInfo.name;
+ dimensionInfo.type = dimensionInfo.type || 'float';
+ if (!dimensionInfo.coordDim) {
+ dimensionInfo.coordDim = dimensionName;
+ dimensionInfo.coordDimIndex = 0;
+ }
- /**
- * @type {Array.<Array|Object>}
- * @private
- */
- this._rawData;
+ dimensionInfo.otherDims = dimensionInfo.otherDims || {};
+ dimensionNames.push(dimensionName);
+ dimensionInfos[dimensionName] = dimensionInfo;
- /**
- * Raw extent will not be cloned, but only transfered.
- * It will not be calculated util needed.
- * key: dim,
- * value: {end: number, extent: Array.<number>}
- * @type {Object}
- * @private
- */
- this._rawExtent = {};
+ dimensionInfo.index = i;
- /**
- * @type {Object}
- * @private
- */
- this._extent = {};
+ if (dimensionInfo.createInvertedIndices) {
+ invertedIndicesMap[dimensionName] = [];
+ }
+ }
- /**
- * key: dim
- * value: extent
- * @type {Object}
- * @private
- */
- this._approximateExtent = {};
+ this.dimensions = dimensionNames;
+ this._dimensionInfos = dimensionInfos;
+ this.hostModel = hostModel;
+
+ // Cache summary info for fast visit. See "dimensionHelper".
+ this._dimensionsSummary = summarizeDimensions(this);
+
+ this._invertedIndicesMap = invertedIndicesMap;
+
+ this.userOutput = this._dimensionsSummary.userOutput;
+ }
/**
- * Cache summary info for fast visit. See "dimensionHelper".
- * @type {Object}
- * @private
+ * The meanings of the input parameter `dim`:
+ *
+ * + If dim is a number (e.g., `1`), it means the index of the dimension.
+ * For example, `getDimension(0)` will return 'x' or 'lng' or 'radius'.
+ * + If dim is a number-like string (e.g., `"1"`):
+ * + If there is the same concrete dim name defined in `this.dimensions`, it means that concrete name.
+ * + If not, it will be converted to a number, which means the index of the dimension.
+ * (why? because of the backward compatbility. We have been tolerating number-like string in
+ * dimension setting, although now it seems that it is not a good idea.)
+ * For example, `visualMap[i].dimension: "1"` is the same meaning as `visualMap[i].dimension: 1`,
+ * if no dimension name is defined as `"1"`.
+ * + If dim is a not-number-like string, it means the concrete dim name.
+ * For example, it can be be default name `"x"`, `"y"`, `"z"`, `"lng"`, `"lat"`, `"angle"`, `"radius"`,
+ * or customized in `dimensions` property of option like `"age"`.
+ *
+ * Get dimension name
+ * @param dim See above.
+ * @return Concrete dim name.
*/
- this._dimensionsSummary = summarizeDimensions(this);
+ getDimension(dim: DimensionLoose): DimensionName {
+ if (typeof dim === 'number'
+ // If being a number-like string but not being defined a dimension name.
+ || (!isNaN(dim as any) && !this._dimensionInfos.hasOwnProperty(dim))
+ ) {
+ dim = this.dimensions[dim as DimensionIndex];
+ }
+ return dim as DimensionName;
+ }
/**
- * @type {Object.<Array|TypedArray>}
- * @private
+ * Get type and calculation info of particular dimension
+ * @param dim
+ * Dimension can be concrete names like x, y, z, lng, lat, angle, radius
+ * Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius'
*/
- this._invertedIndicesMap = invertedIndicesMap;
+ getDimensionInfo(dim: DimensionLoose): DataDimensionInfo {
+ // Do not clone, because there may be categories in dimInfo.
+ return this._dimensionInfos[this.getDimension(dim)];
+ }
/**
- * @type {Object}
- * @private
+ * concrete dimension name list on coord.
*/
- this._calculationInfo = {};
+ getDimensionsOnCoord(): DimensionName[] {
+ return this._dimensionsSummary.dataDimsOnCoord.slice();
+ }
/**
- * User output info of this data.
- * DO NOT use it in other places!
- *
- * When preparing user params for user callbacks, we have
- * to clone these inner data structures to prevent users
- * from modifying them to effect built-in logic. And for
- * performance consideration we make this `userOutput` to
- * avoid clone them too many times.
- *
- * @type {Object}
- * @readOnly
+ * @param coordDim
+ * @param idx A coordDim may map to more than one data dim.
+ * If idx is `true`, return a array of all mapped dims.
+ * If idx is not specified, return the first dim not extra.
+ * @return concrete data dim.
+ * If idx is number, and not found, return null/undefined.
+ * If idx is `true`, and not found, return empty array (always return array).
*/
- this.userOutput = this._dimensionsSummary.userOutput;
-};
-
-var listProto = List.prototype;
-
-listProto.type = 'list';
-
-/**
- * If each data item has it's own option
- * @type {boolean}
- */
-listProto.hasItemOption = true;
+ mapDimension<Idx extends (number | true)>(
+ coordDim: DimensionName,
+ idx?: Idx
+ ): (true extends Idx ? DimensionName[] : DimensionName) {
+ var dimensionsSummary = this._dimensionsSummary;
+
+ if (idx == null) {
+ return dimensionsSummary.encodeFirstDimNotExtra[coordDim] as any;
+ }
-/**
- * The meanings of the input parameter `dim`:
- *
- * + If dim is a number (e.g., `1`), it means the index of the dimension.
- * For example, `getDimension(0)` will return 'x' or 'lng' or 'radius'.
- * + If dim is a number-like string (e.g., `"1"`):
- * + If there is the same concrete dim name defined in `this.dimensions`, it means that concrete name.
- * + If not, it will be converted to a number, which means the index of the dimension.
- * (why? because of the backward compatbility. We have been tolerating number-like string in
- * dimension setting, although now it seems that it is not a good idea.)
- * For example, `visualMap[i].dimension: "1"` is the same meaning as `visualMap[i].dimension: 1`,
- * if no dimension name is defined as `"1"`.
- * + If dim is a not-number-like string, it means the concrete dim name.
- * For example, it can be be default name `"x"`, `"y"`, `"z"`, `"lng"`, `"lat"`, `"angle"`, `"radius"`,
- * or customized in `dimensions` property of option like `"age"`.
- *
- * Get dimension name
- * @param {string|number} dim See above.
- * @return {string} Concrete dim name.
- */
-listProto.getDimension = function (dim) {
- if (typeof dim === 'number'
- // If being a number-like string but not being defined a dimension name.
- || (!isNaN(dim) && !this._dimensionInfos.hasOwnProperty(dim))
- ) {
- dim = this.dimensions[dim];
+ var dims = dimensionsSummary.encode[coordDim];
+ return idx === true
+ // always return array if idx is `true`
+ ? (dims || []).slice()
+ : (dims ? dims[idx as number] as any : null);
}
- return dim;
-};
-/**
- * Get type and calculation info of particular dimension
- * @param {string|number} dim
- * Dimension can be concrete names like x, y, z, lng, lat, angle, radius
- * Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius'
- */
-listProto.getDimensionInfo = function (dim) {
- // Do not clone, because there may be categories in dimInfo.
- return this._dimensionInfos[this.getDimension(dim)];
-};
+ /**
+ * Initialize from data
+ * @param data source or data or data provider.
+ * @param nameLIst The name of a datum is used on data diff and
+ * defualt label/tooltip.
+ * A name can be specified in encode.itemName,
+ * or dataItem.name (only for series option data),
+ * or provided in nameList from outside.
+ */
+ initData(
+ data: any,
+ nameList?: string[],
+ dimValueGetter?: DimValueGetter
+ ): void {
+
+ var notProvider = (Source as SourceConstructor).isInstance(data)
+ || zrUtil.isArrayLike(data);
+ if (notProvider) {
+ data = new DefaultDataProvider(data, this.dimensions.length);
+ }
-/**
- * @return {Array.<string>} concrete dimension name list on coord.
- */
-listProto.getDimensionsOnCoord = function () {
- return this._dimensionsSummary.dataDimsOnCoord.slice();
-};
+ if (__DEV__) {
+ if (!notProvider
+ && (typeof data.getItem !== 'function' || typeof data.count !== 'function')
+ ) {
+ throw new Error('Inavlid data provider.');
+ }
+ }
-/**
- * @param {string} coordDim
- * @param {number} [idx] A coordDim may map to more than one data dim.
- * If idx is `true`, return a array of all mapped dims.
- * If idx is not specified, return the first dim not extra.
- * @return {string|Array.<string>} concrete data dim.
- * If idx is number, and not found, return null/undefined.
- * If idx is `true`, and not found, return empty array (always return array).
- */
-listProto.mapDimension = function (coordDim, idx) {
- var dimensionsSummary = this._dimensionsSummary;
+ this._rawData = data;
- if (idx == null) {
- return dimensionsSummary.encodeFirstDimNotExtra[coordDim];
- }
+ // Clear
+ this._storage = {};
+ this._indices = null;
- var dims = dimensionsSummary.encode[coordDim];
- return idx === true
- // always return array if idx is `true`
- ? (dims || []).slice()
- : (dims && dims[idx]);
-};
+ this._nameList = nameList || [];
-/**
- * Initialize from data
- * @param {Array.<Object|number|Array>} data source or data or data provider.
- * @param {Array.<string>} [nameLIst] The name of a datum is used on data diff and
- * defualt label/tooltip.
- * A name can be specified in encode.itemName,
- * or dataItem.name (only for series option data),
- * or provided in nameList from outside.
- * @param {Function} [dimValueGetter] (dataItem, dimName, dataIndex, dimIndex) => number
- */
-listProto.initData = function (data, nameList, dimValueGetter) {
+ this._idList = [];
- var notProvider = Source.isInstance(data) || zrUtil.isArrayLike(data);
- if (notProvider) {
- data = new DefaultDataProvider(data, this.dimensions.length);
- }
+ this._nameRepeatCount = {};
- if (__DEV__) {
- if (!notProvider && (typeof data.getItem !== 'function' || typeof data.count !== 'function')) {
- throw new Error('Inavlid data provider.');
+ if (!dimValueGetter) {
+ this.hasItemOption = false;
}
- }
-
- this._rawData = data;
- // Clear
- this._storage = {};
- this._indices = null;
+ this.defaultDimValueGetter = defaultDimValueGetters[
+ this._rawData.getSource().sourceFormat
+ ];
+ // Default dim value getter
+ this._dimValueGetter = dimValueGetter = dimValueGetter
+ || this.defaultDimValueGetter;
+ this._dimValueGetterArrayRows = defaultDimValueGetters.arrayRows;
- this._nameList = nameList || [];
+ // Reset raw extent.
+ this._rawExtent = {};
- this._idList = [];
+ this._initDataFromProvider(0, data.count());
- this._nameRepeatCount = {};
+ // If data has no item option.
+ if (data.pure) {
+ this.hasItemOption = false;
+ }
+ }
- if (!dimValueGetter) {
- this.hasItemOption = false;
+ getProvider(): DataProvider {
+ return this._rawData;
}
/**
- * @readOnly
+ * Caution: Can be only called on raw data (before `this._indices` created).
*/
- this.defaultDimValueGetter = defaultDimValueGetters[
- this._rawData.getSource().sourceFormat
- ];
- // Default dim value getter
- this._dimValueGetter = dimValueGetter = dimValueGetter
- || this.defaultDimValueGetter;
- this._dimValueGetterArrayRows = defaultDimValueGetters.arrayRows;
-
- // Reset raw extent.
- this._rawExtent = {};
-
- this._initDataFromProvider(0, data.count());
-
- // If data has no item option.
- if (data.pure) {
- this.hasItemOption = false;
- }
-};
-
-listProto.getProvider = function () {
- return this._rawData;
-};
-
-/**
- * Caution: Can be only called on raw data (before `this._indices` created).
- */
-listProto.appendData = function (data) {
- if (__DEV__) {
- zrUtil.assert(!this._indices, 'appendData can only be called on raw data.');
- }
+ appendData(data: ArrayLike<any>): void {
+ if (__DEV__) {
+ zrUtil.assert(!this._indices, 'appendData can only be called on raw data.');
+ }
- var rawData = this._rawData;
- var start = this.count();
- rawData.appendData(data);
- var end = rawData.count();
- if (!rawData.persistent) {
- end += start;
+ var rawData = this._rawData;
+ var start = this.count();
+ rawData.appendData(data);
+ var end = rawData.count();
+ if (!rawData.persistent) {
+ end += start;
+ }
+ this._initDataFromProvider(start, end);
}
- this._initDataFromProvider(start, end);
-};
-/**
- * Caution: Can be only called on raw data (before `this._indices` created).
- * This method does not modify `rawData` (`dataProvider`), but only
- * add values to storage.
- *
- * The final count will be increased by `Math.max(values.length, names.length)`.
- *
- * @param {Array.<Array.<*>>} values That is the SourceType: 'arrayRows', like
- * [
- * [12, 33, 44],
- * [NaN, 43, 1],
- * ['-', 'asdf', 0]
- * ]
- * Each item is exaclty cooresponding to a dimension.
- * @param {Array.<string>} [names]
- */
-listProto.appendValues = function (values, names) {
- var chunkSize = this._chunkSize;
- var storage = this._storage;
- var dimensions = this.dimensions;
- var dimLen = dimensions.length;
- var rawExtent = this._rawExtent;
-
- var start = this.count();
- var end = start + Math.max(values.length, names ? names.length : 0);
- var originalChunkCount = this._chunkCount;
-
- for (var i = 0; i < dimLen; i++) {
- var dim = dimensions[i];
- if (!rawExtent[dim]) {
- rawExtent[dim] = getInitialExtent();
- }
- if (!storage[dim]) {
- storage[dim] = [];
+ /**
+ * Caution: Can be only called on raw data (before `this._indices` created).
+ * This method does not modify `rawData` (`dataProvider`), but only
+ * add values to storage.
+ *
+ * The final count will be increased by `Math.max(values.length, names.length)`.
+ *
+ * @param values That is the SourceType: 'arrayRows', like
+ * [
+ * [12, 33, 44],
+ * [NaN, 43, 1],
+ * ['-', 'asdf', 0]
+ * ]
+ * Each item is exaclty cooresponding to a dimension.
+ */
+ appendValues(values: any[][], names?: string[]): void {
+ var chunkSize = this._chunkSize;
+ var storage = this._storage;
+ var dimensions = this.dimensions;
+ var dimLen = dimensions.length;
+ var rawExtent = this._rawExtent;
+
+ var start = this.count();
+ var end = start + Math.max(values.length, names ? names.length : 0);
+ var originalChunkCount = this._chunkCount;
+
+ for (var i = 0; i < dimLen; i++) {
+ var dim = dimensions[i];
+ if (!rawExtent[dim]) {
+ rawExtent[dim] = getInitialExtent();
+ }
+ if (!storage[dim]) {
+ storage[dim] = [];
+ }
+ prepareChunks(storage, this._dimensionInfos[dim], chunkSize, originalChunkCount, end);
+ this._chunkCount = storage[dim].length;
}
- prepareChunks(storage, this._dimensionInfos[dim], chunkSize, originalChunkCount, end);
- this._chunkCount = storage[dim].length;
- }
- var emptyDataItem = new Array(dimLen);
- for (var idx = start; idx < end; idx++) {
- var sourceIdx = idx - start;
- var chunkIndex = Math.floor(idx / chunkSize);
- var chunkOffset = idx % chunkSize;
-
- // Store the data by dimensions
- for (var k = 0; k < dimLen; k++) {
- var dim = dimensions[k];
- var val = this._dimValueGetterArrayRows(
- values[sourceIdx] || emptyDataItem, dim, sourceIdx, k
- );
- storage[dim][chunkIndex][chunkOffset] = val;
+ var emptyDataItem = new Array(dimLen);
+ for (var idx = start; idx < end; idx++) {
+ var sourceIdx = idx - start;
+ var chunkIndex = Math.floor(idx / chunkSize);
+ var chunkOffset = idx % chunkSize;
+
+ // Store the data by dimensions
+ for (var k = 0; k < dimLen; k++) {
+ var dim = dimensions[k];
+ var val = this._dimValueGetterArrayRows(
+ values[sourceIdx] || emptyDataItem, dim, sourceIdx, k
+ ) as ParsedDataNumeric;
+ storage[dim][chunkIndex][chunkOffset] = val;
+
+ var dimRawExtent = rawExtent[dim];
+ val < dimRawExtent[0] && (dimRawExtent[0] = val);
+ val > dimRawExtent[1] && (dimRawExtent[1] = val);
+ }
- var dimRawExtent = rawExtent[dim];
- val < dimRawExtent[0] && (dimRawExtent[0] = val);
- val > dimRawExtent[1] && (dimRawExtent[1] = val);
+ if (names) {
+ this._nameList[idx] = names[sourceIdx];
+ }
}
- if (names) {
- this._nameList[idx] = names[sourceIdx];
- }
- }
+ this._rawCount = this._count = end;
- this._rawCount = this._count = end;
+ // Reset data extent
+ this._extent = {};
- // Reset data extent
- this._extent = {};
+ prepareInvertedIndex(this);
+ }
- prepareInvertedIndex(this);
-};
+ private _initDataFromProvider(start: number, end: number): void {
+ // Optimize.
+ if (start >= end) {
+ return;
+ }
-listProto._initDataFromProvider = function (start, end) {
- // Optimize.
- if (start >= end) {
- return;
- }
+ var chunkSize = this._chunkSize;
+ var rawData = this._rawData;
+ var storage = this._storage;
+ var dimensions = this.dimensions;
+ var dimLen = dimensions.length;
+ var dimensionInfoMap = this._dimensionInfos;
+ var nameList = this._nameList;
+ var idList = this._idList;
+ var rawExtent = this._rawExtent;
+ var nameRepeatCount: NameRepeatCount = this._nameRepeatCount = {};
+ var nameDimIdx;
+
+ var originalChunkCount = this._chunkCount;
+ for (var i = 0; i < dimLen; i++) {
+ var dim = dimensions[i];
+ if (!rawExtent[dim]) {
+ rawExtent[dim] = getInitialExtent();
+ }
- var chunkSize = this._chunkSize;
- var rawData = this._rawData;
- var storage = this._storage;
- var dimensions = this.dimensions;
- var dimLen = dimensions.length;
- var dimensionInfoMap = this._dimensionInfos;
- var nameList = this._nameList;
- var idList = this._idList;
- var rawExtent = this._rawExtent;
- var nameRepeatCount = this._nameRepeatCount = {};
- var nameDimIdx;
+ var dimInfo = dimensionInfoMap[dim];
+ if (dimInfo.otherDims.itemName === 0) {
+ nameDimIdx = this._nameDimIdx = i;
+ }
+ if (dimInfo.otherDims.itemId === 0) {
+ this._idDimIdx = i;
+ }
- var originalChunkCount = this._chunkCount;
- for (var i = 0; i < dimLen; i++) {
- var dim = dimensions[i];
- if (!rawExtent[dim]) {
- rawExtent[dim] = getInitialExtent();
- }
+ if (!storage[dim]) {
+ storage[dim] = [];
+ }
- var dimInfo = dimensionInfoMap[dim];
- if (dimInfo.otherDims.itemName === 0) {
- nameDimIdx = this._nameDimIdx = i;
- }
- if (dimInfo.otherDims.itemId === 0) {
- this._idDimIdx = i;
- }
+ prepareChunks(storage, dimInfo, chunkSize, originalChunkCount, end);
- if (!storage[dim]) {
- storage[dim] = [];
+ this._chunkCount = storage[dim].length;
}
- prepareChunks(storage, dimInfo, chunkSize, originalChunkCount, end);
-
- this._chunkCount = storage[dim].length;
- }
+ var dataItem = new Array(dimLen) as OptionDataItem;
+ for (var idx = start; idx < end; idx++) {
+ // NOTICE: Try not to write things into dataItem
+ dataItem = rawData.getItem(idx, dataItem);
+ // Each data item is value
+ // [1, 2]
+ // 2
+ // Bar chart, line chart which uses category axis
+ // only gives the 'y' value. 'x' value is the indices of category
+ // Use a tempValue to normalize the value to be a (x, y) value
+ var chunkIndex = Math.floor(idx / chunkSize);
+ var chunkOffset = idx % chunkSize;
+
+ // Store the data by dimensions
+ for (var k = 0; k < dimLen; k++) {
+ var dim = dimensions[k];
+ var dimStorage = storage[dim][chunkIndex];
+ // PENDING NULL is empty or zero
+ var val = this._dimValueGetter(dataItem, dim, idx, k) as ParsedDataNumeric;
+ dimStorage[chunkOffset] = val;
+
+ var dimRawExtent = rawExtent[dim];
+ val < dimRawExtent[0] && (dimRawExtent[0] = val);
+ val > dimRawExtent[1] && (dimRawExtent[1] = val);
+ }
- var dataItem = new Array(dimLen);
- for (var idx = start; idx < end; idx++) {
- // NOTICE: Try not to write things into dataItem
- dataItem = rawData.getItem(idx, dataItem);
- // Each data item is value
- // [1, 2]
- // 2
- // Bar chart, line chart which uses category axis
- // only gives the 'y' value. 'x' value is the indices of category
- // Use a tempValue to normalize the value to be a (x, y) value
- var chunkIndex = Math.floor(idx / chunkSize);
- var chunkOffset = idx % chunkSize;
-
- // Store the data by dimensions
- for (var k = 0; k < dimLen; k++) {
- var dim = dimensions[k];
- var dimStorage = storage[dim][chunkIndex];
- // PENDING NULL is empty or zero
- var val = this._dimValueGetter(dataItem, dim, idx, k);
- dimStorage[chunkOffset] = val;
-
- var dimRawExtent = rawExtent[dim];
- val < dimRawExtent[0] && (dimRawExtent[0] = val);
- val > dimRawExtent[1] && (dimRawExtent[1] = val);
- }
-
- // ??? FIXME not check by pure but sourceFormat?
- // TODO refactor these logic.
- if (!rawData.pure) {
- var name = nameList[idx];
-
- if (dataItem && name == null) {
- // If dataItem is {name: ...}, it has highest priority.
- // That is appropriate for many common cases.
- if (dataItem.name != null) {
- // There is no other place to persistent dataItem.name,
- // so save it to nameList.
- nameList[idx] = name = dataItem.name;
- }
- else if (nameDimIdx != null) {
- var nameDim = dimensions[nameDimIdx];
- var nameDimChunk = storage[nameDim][chunkIndex];
- if (nameDimChunk) {
- name = nameDimChunk[chunkOffset];
- var ordinalMeta = dimensionInfoMap[nameDim].ordinalMeta;
- if (ordinalMeta && ordinalMeta.categories.length) {
- name = ordinalMeta.categories[name];
+ // ??? FIXME not check by pure but sourceFormat?
+ // TODO refactor these logic.
+ if (!rawData.pure) {
+ var name: any = nameList[idx];
+
+ if (dataItem && name == null) {
+ // If dataItem is {name: ...}, it has highest priority.
+ // That is appropriate for many common cases.
+ if ((dataItem as any).name != null) {
+ // There is no other place to persistent dataItem.name,
+ // so save it to nameList.
+ nameList[idx] = name = (dataItem as any).name;
+ }
+ else if (nameDimIdx != null) {
+ var nameDim = dimensions[nameDimIdx];
+ var nameDimChunk = storage[nameDim][chunkIndex];
+ if (nameDimChunk) {
+ name = nameDimChunk[chunkOffset];
+ var ordinalMeta = dimensionInfoMap[nameDim].ordinalMeta;
+ if (ordinalMeta && ordinalMeta.categories.length) {
+ name = ordinalMeta.categories[name];
+ }
}
}
}
- }
- // Try using the id in option
- // id or name is used on dynamical data, mapping old and new items.
- var id = dataItem == null ? null : dataItem.id;
+ // Try using the id in option
+ // id or name is used on dynamical data, mapping old and new items.
+ var id = dataItem == null ? null : (dataItem as any).id;
- if (id == null && name != null) {
- // Use name as id and add counter to avoid same name
- nameRepeatCount[name] = nameRepeatCount[name] || 0;
- id = name;
- if (nameRepeatCount[name] > 0) {
- id += '__ec__' + nameRepeatCount[name];
+ if (id == null && name != null) {
+ // Use name as id and add counter to avoid same name
+ nameRepeatCount[name] = nameRepeatCount[name] || 0;
+ id = name;
+ if (nameRepeatCount[name] > 0) {
+ id += '__ec__' + nameRepeatCount[name];
+ }
+ nameRepeatCount[name]++;
}
- nameRepeatCount[name]++;
+ id != null && (idList[idx] = id);
}
- id != null && (idList[idx] = id);
}
- }
-
- if (!rawData.persistent && rawData.clean) {
- // Clean unused data if data source is typed array.
- rawData.clean();
- }
- this._rawCount = this._count = end;
+ if (!rawData.persistent && rawData.clean) {
+ // Clean unused data if data source is typed array.
+ rawData.clean();
+ }
- // Reset data extent
- this._extent = {};
+ this._rawCount = this._count = end;
- prepareInvertedIndex(this);
-};
+ // Reset data extent
+ this._extent = {};
-function prepareChunks(storage, dimInfo, chunkSize, chunkCount, end) {
- var DataCtor = dataCtors[dimInfo.type];
- var lastChunkIndex = chunkCount - 1;
- var dim = dimInfo.name;
- var resizeChunkArray = storage[dim][lastChunkIndex];
- if (resizeChunkArray && resizeChunkArray.length < chunkSize) {
- var newStore = new DataCtor(Math.min(end - lastChunkIndex * chunkSize, chunkSize));
- // The cost of the copy is probably inconsiderable
- // within the initial chunkSize.
- for (var j = 0; j < resizeChunkArray.length; j++) {
- newStore[j] = resizeChunkArray[j];
- }
- storage[dim][lastChunkIndex] = newStore;
+ prepareInvertedIndex(this);
}
- // Create new chunks.
- for (var k = chunkCount * chunkSize; k < end; k += chunkSize) {
- storage[dim].push(new DataCtor(Math.min(end - k, chunkSize)));
+ count(): number {
+ return this._count;
}
-}
-function prepareInvertedIndex(list) {
- var invertedIndicesMap = list._invertedIndicesMap;
- zrUtil.each(invertedIndicesMap, function (invertedIndices, dim) {
- var dimInfo = list._dimensionInfos[dim];
+ getIndices(): ArrayLike<number> {
+ var newIndices;
- // Currently, only dimensions that has ordinalMeta can create inverted indices.
- var ordinalMeta = dimInfo.ordinalMeta;
- if (ordinalMeta) {
- invertedIndices = invertedIndicesMap[dim] = new CtorInt32Array(
- ordinalMeta.categories.length
- );
- // The default value of TypedArray is 0. To avoid miss
- // mapping to 0, we should set it as INDEX_NOT_FOUND.
- for (var i = 0; i < invertedIndices.length; i++) {
- invertedIndices[i] = INDEX_NOT_FOUND;
+ var indices = this._indices;
+ if (indices) {
+ var Ctor = indices.constructor as DataArrayLikeConstructor;
+ var thisCount = this._count;
+ // `new Array(a, b, c)` is different from `new Uint32Array(a, b, c)`.
+ if (Ctor === Array) {
+ newIndices = new Ctor(thisCount);
+ for (var i = 0; i < thisCount; i++) {
+ newIndices[i] = indices[i];
+ }
}
- for (var i = 0; i < list._count; i++) {
- // Only support the case that all values are distinct.
- invertedIndices[list.get(dim, i)] = i;
+ else {
+ newIndices = new (Ctor as DataTypedArrayConstructor)(
+ (indices as DataTypedArray).buffer, 0, thisCount
+ );
}
}
- });
-}
-
-function getRawValueFromStore(list, dimIndex, rawIndex) {
- var val;
- if (dimIndex != null) {
- var chunkSize = list._chunkSize;
- var chunkIndex = Math.floor(rawIndex / chunkSize);
- var chunkOffset = rawIndex % chunkSize;
- var dim = list.dimensions[dimIndex];
- var chunk = list._storage[dim][chunkIndex];
- if (chunk) {
- val = chunk[chunkOffset];
- var ordinalMeta = list._dimensionInfos[dim].ordinalMeta;
- if (ordinalMeta && ordinalMeta.categories.length) {
- val = ordinalMeta.categories[val];
+ else {
+ var Ctor = getIndicesCtor(this);
+ newIndices = new Ctor(this.count());
+ for (var i = 0; i < newIndices.length; i++) {
+ newIndices[i] = i;
}
}
- }
- return val;
-}
-/**
- * @return {number}
- */
-listProto.count = function () {
- return this._count;
-};
+ return newIndices;
+ }
-listProto.getIndices = function () {
- var newIndices;
-
- var indices = this._indices;
- if (indices) {
- var Ctor = indices.constructor;
- var thisCount = this._count;
- // `new Array(a, b, c)` is different from `new Uint32Array(a, b, c)`.
- if (Ctor === Array) {
- newIndices = new Ctor(thisCount);
- for (var i = 0; i < thisCount; i++) {
- newIndices[i] = indices[i];
- }
- }
- else {
- newIndices = new Ctor(indices.buffer, 0, thisCount);
+ /**
+ * Get value. Return NaN if idx is out of range.
+ * @param dim Dim must be concrete name.
+ */
+ get(dim: DimensionName, idx: number): ParsedDataValue {
+ if (!(idx >= 0 && idx < this._count)) {
+ return NaN;
}
- }
- else {
- var Ctor = getIndicesCtor(this);
- var newIndices = new Ctor(this.count());
- for (var i = 0; i < newIndices.length; i++) {
- newIndices[i] = i;
+ var storage = this._storage;
+ if (!storage[dim]) {
+ // TODO Warn ?
+ return NaN;
}
+
+ idx = this.getRawIndex(idx);
+
+ var chunkIndex = Math.floor(idx / this._chunkSize);
+ var chunkOffset = idx % this._chunkSize;
+
+ var chunkStore = storage[dim][chunkIndex];
+ var value = chunkStore[chunkOffset];
+ // FIXME ordinal data type is not stackable
+ // if (stack) {
+ // var dimensionInfo = this._dimensionInfos[dim];
+ // if (dimensionInfo && dimensionInfo.stackable) {
+ // var stackedOn = this.stackedOn;
+ // while (stackedOn) {
+ // // Get no stacked data of stacked on
+ // var stackedValue = stackedOn.get(dim, idx);
+ // // Considering positive stack, negative stack and empty data
+ // if ((value >= 0 && stackedValue > 0) // Positive stack
+ // || (value <= 0 && stackedValue < 0) // Negative stack
+ // ) {
+ // value += stackedValue;
+ // }
+ // stackedOn = stackedOn.stackedOn;
+ // }
+ // }
+ // }
+
+ return value;
}
- return newIndices;
-};
+ /**
+ * @param dim concrete dim
+ */
+ getByRawIndex(dim: DimensionName, rawIdx: number): ParsedDataValue {
+ if (!(rawIdx >= 0 && rawIdx < this._rawCount)) {
+ return NaN;
+ }
+ var dimStore = this._storage[dim];
+ if (!dimStore) {
+ // TODO Warn ?
+ return NaN;
+ }
-/**
- * Get value. Return NaN if idx is out of range.
- * @param {string} dim Dim must be concrete name.
- * @param {number} idx
- * @param {boolean} stack
- * @return {number}
- */
-listProto.get = function (dim, idx /*, stack */) {
- if (!(idx >= 0 && idx < this._count)) {
- return NaN;
+ var chunkIndex = Math.floor(rawIdx / this._chunkSize);
+ var chunkOffset = rawIdx % this._chunkSize;
+ var chunkStore = dimStore[chunkIndex];
+ return chunkStore[chunkOffset];
}
- var storage = this._storage;
- if (!storage[dim]) {
- // TODO Warn ?
- return NaN;
+
+ /**
+ * FIXME Use `get` on chrome maybe slow(in filterSelf and selectRange).
+ * Hack a much simpler _getFast
+ */
+ private _getFast(dim: DimensionName, rawIdx: number): ParsedDataValue {
+ var chunkIndex = Math.floor(rawIdx / this._chunkSize);
+ var chunkOffset = rawIdx % this._chunkSize;
+ var chunkStore = this._storage[dim][chunkIndex];
+ return chunkStore[chunkOffset];
}
- idx = this.getRawIndex(idx);
-
- var chunkIndex = Math.floor(idx / this._chunkSize);
- var chunkOffset = idx % this._chunkSize;
-
- var chunkStore = storage[dim][chunkIndex];
- var value = chunkStore[chunkOffset];
- // FIXME ordinal data type is not stackable
- // if (stack) {
- // var dimensionInfo = this._dimensionInfos[dim];
- // if (dimensionInfo && dimensionInfo.stackable) {
- // var stackedOn = this.stackedOn;
- // while (stackedOn) {
- // // Get no stacked data of stacked on
- // var stackedValue = stackedOn.get(dim, idx);
- // // Considering positive stack, negative stack and empty data
- // if ((value >= 0 && stackedValue > 0) // Positive stack
- // || (value <= 0 && stackedValue < 0) // Negative stack
- // ) {
- // value += stackedValue;
- // }
- // stackedOn = stackedOn.stackedOn;
- // }
- // }
- // }
+ /**
+ * Get value for multi dimensions.
+ * @param dimensions If ignored, using all dimensions.
+ */
+ getValues(idx: number): ParsedDataValue[];
+ getValues(dimensions: DimensionName[], idx: number): ParsedDataValue[];
+ getValues(dimensions: DimensionName[] | number, idx?: number): ParsedDataValue[] {
+ var values = [];
+
+ if (!zrUtil.isArray(dimensions)) {
+ // stack = idx;
+ idx = dimensions;
+ dimensions = this.dimensions;
+ }
- return value;
-};
+ for (var i = 0, len = dimensions.length; i < len; i++) {
+ values.push(this.get(dimensions[i], idx /*, stack */));
+ }
-/**
- * @param {string} dim concrete dim
- * @param {number} rawIndex
- * @return {number|string}
- */
-listProto.getByRawIndex = function (dim, rawIdx) {
- if (!(rawIdx >= 0 && rawIdx < this._rawCount)) {
- return NaN;
+ return values;
}
- var dimStore = this._storage[dim];
- if (!dimStore) {
- // TODO Warn ?
- return NaN;
+
+ /**
+ * If value is NaN. Inlcuding '-'
+ * Only check the coord dimensions.
+ */
+ hasValue(idx: number): boolean {
+ var dataDimsOnCoord = this._dimensionsSummary.dataDimsOnCoord;
+ for (var i = 0, len = dataDimsOnCoord.length; i < len; i++) {
+ // Ordinal type originally can be string or number.
+ // But when an ordinal type is used on coord, it can
+ // not be string but only number. So we can also use isNaN.
+ if (isNaN(this.get(dataDimsOnCoord[i], idx) as any)) {
+ return false;
+ }
+ }
+ return true;
}
- var chunkIndex = Math.floor(rawIdx / this._chunkSize);
- var chunkOffset = rawIdx % this._chunkSize;
- var chunkStore = dimStore[chunkIndex];
- return chunkStore[chunkOffset];
-};
+ /**
+ * Get extent of data in one dimension
+ */
+ getDataExtent(dim: DimensionLoose): [number, number] {
+ // Make sure use concrete dim as cache name.
+ dim = this.getDimension(dim);
+ var dimData = this._storage[dim];
+ var initialExtent = getInitialExtent();
-/**
- * FIXME Use `get` on chrome maybe slow(in filterSelf and selectRange).
- * Hack a much simpler _getFast
- * @private
- */
-listProto._getFast = function (dim, rawIdx) {
- var chunkIndex = Math.floor(rawIdx / this._chunkSize);
- var chunkOffset = rawIdx % this._chunkSize;
- var chunkStore = this._storage[dim][chunkIndex];
- return chunkStore[chunkOffset];
-};
+ // stack = !!((stack || false) && this.getCalculationInfo(dim));
-/**
- * Get value for multi dimensions.
- * @param {Array.<string>} [dimensions] If ignored, using all dimensions.
- * @param {number} idx
- * @return {number}
- */
-listProto.getValues = function (dimensions, idx /*, stack */) {
- var values = [];
+ if (!dimData) {
+ return initialExtent;
+ }
- if (!zrUtil.isArray(dimensions)) {
- // stack = idx;
- idx = dimensions;
- dimensions = this.dimensions;
- }
+ // Make more strict checkings to ensure hitting cache.
+ var currEnd = this.count();
+ // var cacheName = [dim, !!stack].join('_');
+ // var cacheName = dim;
- for (var i = 0, len = dimensions.length; i < len; i++) {
- values.push(this.get(dimensions[i], idx /*, stack */));
- }
+ // Consider the most cases when using data zoom, `getDataExtent`
+ // happened before filtering. We cache raw extent, which is not
+ // necessary to be cleared and recalculated when restore data.
+ var useRaw = !this._indices; // && !stack;
+ var dimExtent: [number, number];
- return values;
-};
+ if (useRaw) {
+ return this._rawExtent[dim].slice() as [number, number];
+ }
+ dimExtent = this._extent[dim];
+ if (dimExtent) {
+ return dimExtent.slice() as [number, number];
+ }
+ dimExtent = initialExtent;
-/**
- * If value is NaN. Inlcuding '-'
- * Only check the coord dimensions.
- * @param {string} dim
- * @param {number} idx
- * @return {number}
- */
-listProto.hasValue = function (idx) {
- var dataDimsOnCoord = this._dimensionsSummary.dataDimsOnCoord;
- for (var i = 0, len = dataDimsOnCoord.length; i < len; i++) {
- // Ordinal type originally can be string or number.
- // But when an ordinal type is used on coord, it can
- // not be string but only number. So we can also use isNaN.
- if (isNaN(this.get(dataDimsOnCoord[i], idx))) {
- return false;
+ var min = dimExtent[0];
+ var max = dimExtent[1];
+
+ for (var i = 0; i < currEnd; i++) {
+ // var value = stack ? this.get(dim, i, true) : this._getFast(dim, this.getRawIndex(i));
+ var value = this._getFast(dim, this.getRawIndex(i)) as ParsedDataNumeric;
+ value < min && (min = value);
+ value > max && (max = value);
}
- }
- return true;
-};
-/**
- * Get extent of data in one dimension
- * @param {string} dim
- * @param {boolean} stack
- */
-listProto.getDataExtent = function (dim /*, stack */) {
- // Make sure use concrete dim as cache name.
- dim = this.getDimension(dim);
- var dimData = this._storage[dim];
- var initialExtent = getInitialExtent();
+ dimExtent = [min, max];
- // stack = !!((stack || false) && this.getCalculationInfo(dim));
+ this._extent[dim] = dimExtent;
- if (!dimData) {
- return initialExtent;
+ return dimExtent;
}
- // Make more strict checkings to ensure hitting cache.
- var currEnd = this.count();
- // var cacheName = [dim, !!stack].join('_');
- // var cacheName = dim;
-
- // Consider the most cases when using data zoom, `getDataExtent`
- // happened before filtering. We cache raw extent, which is not
- // necessary to be cleared and recalculated when restore data.
- var useRaw = !this._indices; // && !stack;
- var dimExtent;
+ /**
+ * Optimize for the scenario that data is filtered by a given extent.
+ * Consider that if data amount is more than hundreds of thousand,
+ * extent calculation will cost more than 10ms and the cache will
+ * be erased because of the filtering.
+ */
+ getApproximateExtent(dim: DimensionLoose): [number, number] {
+ dim = this.getDimension(dim);
+ return this._approximateExtent[dim] || this.getDataExtent(dim /*, stack */);
+ }
- if (useRaw) {
- return this._rawExtent[dim].slice();
+ setApproximateExtent(extent: [number, number], dim: DimensionLoose): void {
+ dim = this.getDimension(dim);
+ this._approximateExtent[dim] = extent.slice() as [number, number];
}
- dimExtent = this._extent[dim];
- if (dimExtent) {
- return dimExtent.slice();
+
+ getCalculationInfo(key: string): object {
+ return this._calculationInfo[key];
}
- dimExtent = initialExtent;
- var min = dimExtent[0];
- var max = dimExtent[1];
+ /**
+ * @param key or k-v object
+ */
+ setCalculationInfo(key: string | object, value?: any) {
+ isObject(key)
+ ? zrUtil.extend(this._calculationInfo, key as object)
+ : (this._calculationInfo[key] = value);
+ }
- for (var i = 0; i < currEnd; i++) {
- // var value = stack ? this.get(dim, i, true) : this._getFast(dim, this.getRawIndex(i));
- var value = this._getFast(dim, this.getRawIndex(i));
- value < min && (min = value);
- value > max && (max = value);
+ /**
+ * Get sum of data in one dimension
+ */
+ getSum(dim: DimensionName): number {
+ var dimData = this._storage[dim];
+ var sum = 0;
+ if (dimData) {
+ for (var i = 0, len = this.count(); i < len; i++) {
+ var value = this.get(dim, i) as number;
+ if (!isNaN(value)) {
+ sum += value;
+ }
+ }
+ }
+ return sum;
}
- dimExtent = [min, max];
+ /**
+ * Get median of data in one dimension
+ */
+ getMedian(dim: DimensionLoose): number {
+ var dimDataArray: ParsedDataValue[] = [];
+ // map all data of one dimension
+ this.each(dim, function (val) {
+ if (!isNaN(val as number)) {
+ dimDataArray.push(val);
+ }
+ });
+
+ // TODO
+ // Use quick select?
+
+ // immutability & sort
+ var sortedDimDataArray = [].concat(dimDataArray).sort(function (a, b) {
+ return a - b;
+ });
+ var len = this.count();
+ // calculate median
+ return len === 0
+ ? 0
+ : len % 2 === 1
+ ? sortedDimDataArray[(len - 1) / 2]
+ : (sortedDimDataArray[len / 2] + sortedDimDataArray[len / 2 - 1]) / 2;
+ }
+
+ // /**
+ // * Retreive the index with given value
+ // * @param {string} dim Concrete dimension.
+ // * @param {number} value
+ // * @return {number}
+ // */
+ // Currently incorrect: should return dataIndex but not rawIndex.
+ // Do not fix it until this method is to be used somewhere.
+ // FIXME Precision of float value
+ // indexOf(dim, value) {
+ // var storage = this._storage;
+ // var dimData = storage[dim];
+ // var chunkSize = this._chunkSize;
+ // if (dimData) {
+ // for (var i = 0, len = this.count(); i < len; i++) {
+ // var chunkIndex = Math.floor(i / chunkSize);
+ // var chunkOffset = i % chunkSize;
+ // if (dimData[chunkIndex][chunkOffset] === value) {
+ // return i;
+ // }
+ // }
+ // }
+ // return -1;
+ // }
- this._extent[dim] = dimExtent;
+ /**
+ * Only support the dimension which inverted index created.
+ * Do not support other cases until required.
+ * @param dim concrete dim
+ * @param value ordinal index
+ * @return rawIndex
+ */
+ rawIndexOf(dim: DimensionName, value: OrdinalRawValueIndex): number {
+ var invertedIndices = dim && this._invertedIndicesMap[dim];
+ if (__DEV__) {
+ if (!invertedIndices) {
+ throw new Error('Do not supported yet');
+ }
+ }
+ var rawIndex = invertedIndices[value];
+ if (rawIndex == null || isNaN(rawIndex)) {
+ return INDEX_NOT_FOUND;
+ }
+ return rawIndex;
+ }
- return dimExtent;
-};
-
-/**
- * Optimize for the scenario that data is filtered by a given extent.
- * Consider that if data amount is more than hundreds of thousand,
- * extent calculation will cost more than 10ms and the cache will
- * be erased because of the filtering.
- */
-listProto.getApproximateExtent = function (dim /*, stack */) {
- dim = this.getDimension(dim);
- return this._approximateExtent[dim] || this.getDataExtent(dim /*, stack */);
-};
-
-listProto.setApproximateExtent = function (extent, dim /*, stack */) {
- dim = this.getDimension(dim);
- this._approximateExtent[dim] = extent.slice();
-};
-
-/**
- * @param {string} key
- * @return {*}
- */
-listProto.getCalculationInfo = function (key) {
- return this._calculationInfo[key];
-};
-
-/**
- * @param {string|Object} key or k-v object
- * @param {*} [value]
- */
-listProto.setCalculationInfo = function (key, value) {
- isObject(key)
- ? zrUtil.extend(this._calculationInfo, key)
- : (this._calculationInfo[key] = value);
-};
-
-/**
- * Get sum of data in one dimension
- * @param {string} dim
- */
-listProto.getSum = function (dim /*, stack */) {
- var dimData = this._storage[dim];
- var sum = 0;
- if (dimData) {
+ /**
+ * Retreive the index with given name
+ */
+ indexOfName(name: string): number {
for (var i = 0, len = this.count(); i < len; i++) {
- var value = this.get(dim, i /*, stack */);
- if (!isNaN(value)) {
- sum += value;
+ if (this.getName(i) === name) {
+ return i;
}
}
- }
- return sum;
-};
-
-/**
- * Get median of data in one dimension
- * @param {string} dim
- */
-listProto.getMedian = function (dim /*, stack */) {
- var dimDataArray = [];
- // map all data of one dimension
- this.each(dim, function (val, idx) {
- if (!isNaN(val)) {
- dimDataArray.push(val);
- }
- });
-
- // TODO
- // Use quick select?
-
- // immutability & sort
- var sortedDimDataArray = [].concat(dimDataArray).sort(function (a, b) {
- return a - b;
- });
- var len = this.count();
- // calculate median
- return len === 0
- ? 0
- : len % 2 === 1
- ? sortedDimDataArray[(len - 1) / 2]
- : (sortedDimDataArray[len / 2] + sortedDimDataArray[len / 2 - 1]) / 2;
-};
-// /**
-// * Retreive the index with given value
-// * @param {string} dim Concrete dimension.
-// * @param {number} value
-// * @return {number}
-// */
-// Currently incorrect: should return dataIndex but not rawIndex.
-// Do not fix it until this method is to be used somewhere.
-// FIXME Precision of float value
-// listProto.indexOf = function (dim, value) {
-// var storage = this._storage;
-// var dimData = storage[dim];
-// var chunkSize = this._chunkSize;
-// if (dimData) {
-// for (var i = 0, len = this.count(); i < len; i++) {
-// var chunkIndex = Math.floor(i / chunkSize);
-// var chunkOffset = i % chunkSize;
-// if (dimData[chunkIndex][chunkOffset] === value) {
-// return i;
-// }
-// }
-// }
-// return -1;
-// };
+ return -1;
+ }
-/**
- * Only support the dimension which inverted index created.
- * Do not support other cases until required.
- * @param {string} concrete dim
- * @param {number|string} value
- * @return {number} rawIndex
- */
-listProto.rawIndexOf = function (dim, value) {
- var invertedIndices = dim && this._invertedIndicesMap[dim];
- if (__DEV__) {
- if (!invertedIndices) {
- throw new Error('Do not supported yet');
+ /**
+ * Retreive the index with given raw data index
+ */
+ indexOfRawIndex(rawIndex: number): number {
+ if (rawIndex >= this._rawCount || rawIndex < 0) {
+ return -1;
}
- }
- var rawIndex = invertedIndices[value];
- if (rawIndex == null || isNaN(rawIndex)) {
- return INDEX_NOT_FOUND;
- }
- return rawIndex;
-};
-/**
- * Retreive the index with given name
- * @param {number} idx
- * @param {number} name
- * @return {number}
- */
-listProto.indexOfName = function (name) {
- for (var i = 0, len = this.count(); i < len; i++) {
- if (this.getName(i) === name) {
- return i;
+ if (!this._indices) {
+ return rawIndex;
}
- }
- return -1;
-};
+ // Indices are ascending
+ var indices = this._indices;
-/**
- * Retreive the index with given raw data index
- * @param {number} idx
- * @param {number} name
- * @return {number}
- */
-listProto.indexOfRawIndex = function (rawIndex) {
- if (rawIndex >= this._rawCount || rawIndex < 0) {
+ // If rawIndex === dataIndex
+ var rawDataIndex = indices[rawIndex];
+ if (rawDataIndex != null && rawDataIndex < this._count && rawDataIndex === rawIndex) {
+ return rawIndex;
+ }
+
+ var left = 0;
+ var right = this._count - 1;
+ while (left <= right) {
+ var mid = (left + right) / 2 | 0;
+ if (indices[mid] < rawIndex) {
+ left = mid + 1;
+ }
+ else if (indices[mid] > rawIndex) {
+ right = mid - 1;
+ }
+ else {
+ return mid;
+ }
+ }
return -1;
}
- if (!this._indices) {
- return rawIndex;
- }
+ /**
+ * Retreive the index of nearest value
+ * @param dim
+ * @param value
+ * @param [maxDistance=Infinity]
+ * @return If and only if multiple indices has
+ * the same value, they are put to the result.
+ */
+ indicesOfNearest(
+ dim: DimensionName, value: number, maxDistance?: number
+ ): number[] {
+ var storage = this._storage;
+ var dimData = storage[dim];
+ var nearestIndices: number[] = [];
+
+ if (!dimData) {
+ return nearestIndices;
+ }
- // Indices are ascending
- var indices = this._indices;
+ if (maxDistance == null) {
+ maxDistance = Infinity;
+ }
- // If rawIndex === dataIndex
- var rawDataIndex = indices[rawIndex];
- if (rawDataIndex != null && rawDataIndex < this._count && rawDataIndex === rawIndex) {
- return rawIndex;
- }
+ var minDist = Infinity;
+ var minDiff = -1;
+ var nearestIndicesLen = 0;
- var left = 0;
- var right = this._count - 1;
- while (left <= right) {
- var mid = (left + right) / 2 | 0;
- if (indices[mid] < rawIndex) {
- left = mid + 1;
- }
- else if (indices[mid] > rawIndex) {
- right = mid - 1;
- }
- else {
- return mid;
+ // Check the test case of `test/ut/spec/data/List.js`.
+ for (var i = 0, len = this.count(); i < len; i++) {
+ var diff = value - (this.get(dim, i) as number);
+ var dist = Math.abs(diff);
+ if (dist <= maxDistance) {
+ // When the `value` is at the middle of `this.get(dim, i)` and `this.get(dim, i+1)`,
+ // we'd better not push both of them to `nearestIndices`, otherwise it is easy to
+ // get more than one item in `nearestIndices` (more specifically, in `tooltip`).
+ // So we chose the one that `diff >= 0` in this csae.
+ // But if `this.get(dim, i)` and `this.get(dim, j)` get the same value, both of them
+ // should be push to `nearestIndices`.
+ if (dist < minDist
+ || (dist === minDist && diff >= 0 && minDiff < 0)
+ ) {
+ minDist = dist;
+ minDiff = diff;
+ nearestIndicesLen = 0;
+ }
+ if (diff === minDiff) {
+ nearestIndices[nearestIndicesLen++] = i;
+ }
+ }
}
- }
- return -1;
-};
-
-/**
- * Retreive the index of nearest value
- * @param {string} dim
- * @param {number} value
- * @param {number} [maxDistance=Infinity]
- * @return {Array.<number>} If and only if multiple indices has
- * the same value, they are put to the result.
- */
-listProto.indicesOfNearest = function (dim, value, maxDistance) {
- var storage = this._storage;
- var dimData = storage[dim];
- var nearestIndices = [];
+ nearestIndices.length = nearestIndicesLen;
- if (!dimData) {
return nearestIndices;
}
- if (maxDistance == null) {
- maxDistance = Infinity;
- }
+ /**
+ * Get raw data index.
+ * Do not initialize.
+ * Default `getRawIndex`. And it can be changed.
+ */
+ getRawIndex: (idx: number) => number = getRawIndexWithoutIndices;
- var minDist = Infinity;
- var minDiff = -1;
- var nearestIndicesLen = 0;
-
- // Check the test case of `test/ut/spec/data/List.js`.
- for (var i = 0, len = this.count(); i < len; i++) {
- var diff = value - this.get(dim, i);
- var dist = Math.abs(diff);
- if (dist <= maxDistance) {
- // When the `value` is at the middle of `this.get(dim, i)` and `this.get(dim, i+1)`,
- // we'd better not push both of them to `nearestIndices`, otherwise it is easy to
- // get more than one item in `nearestIndices` (more specifically, in `tooltip`).
- // So we chose the one that `diff >= 0` in this csae.
- // But if `this.get(dim, i)` and `this.get(dim, j)` get the same value, both of them
- // should be push to `nearestIndices`.
- if (dist < minDist
- || (dist === minDist && diff >= 0 && minDiff < 0)
- ) {
- minDist = dist;
- minDiff = diff;
- nearestIndicesLen = 0;
- }
- if (diff === minDiff) {
- nearestIndices[nearestIndicesLen++] = i;
+ /**
+ * Get raw data item
+ */
+ getRawDataItem(idx: number): OptionDataItem {
+ if (!this._rawData.persistent) {
+ var val = [];
+ for (var i = 0; i < this.dimensions.length; i++) {
+ var dim = this.dimensions[i];
+ val.push(this.get(dim, idx));
}
+ return val;
+ }
+ else {
+ return this._rawData.getItem(this.getRawIndex(idx));
}
}
- nearestIndices.length = nearestIndicesLen;
-
- return nearestIndices;
-};
-
-/**
- * Get raw data index
- * @param {number} idx
- * @return {number}
- */
-listProto.getRawIndex = getRawIndexWithoutIndices;
-function getRawIndexWithoutIndices(idx) {
- return idx;
-}
+ getName(idx: number): string {
+ var rawIndex = this.getRawIndex(idx);
+ return this._nameList[rawIndex]
+ || getRawValueFromStore(this, this._nameDimIdx, rawIndex)
+ || '';
+ }
-function getRawIndexWithIndices(idx) {
- if (idx < this._count && idx >= 0) {
- return this._indices[idx];
+ getId(idx: number): string {
+ return getId(this, this.getRawIndex(idx));
}
- return -1;
-}
-/**
- * Get raw data item
- * @param {number} idx
- * @return {number}
- */
-listProto.getRawDataItem = function (idx) {
- if (!this._rawData.persistent) {
- var val = [];
- for (var i = 0; i < this.dimensions.length; i++) {
- var dim = this.dimensions[i];
- val.push(this.get(dim, idx));
+ /**
+ * Data iteration
+ * @param ctx default this
+ * @example
+ * list.each('x', function (x, idx) {});
+ * list.each(['x', 'y'], function (x, y, idx) {});
+ * list.each(function (idx) {})
+ */
+ each<Ctx>(cb: EachCb0<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): void;
+ each<Ctx>(dims: DimensionLoose, cb: EachCb1<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): void;
+ each<Ctx>(dims: [DimensionLoose], cb: EachCb1<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): void;
+ each<Ctx>(dims: [DimensionLoose, DimensionLoose], cb: EachCb2<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): void;
+ each<Ctx>(dims: ItrParamDims, cb: EachCb<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): void;
+ each<Ctx>(
+ dims: ItrParamDims | EachCb<Ctx>,
+ cb: EachCb<Ctx> | Ctx,
+ ctx?: Ctx,
+ ctxCompat?: Ctx
+ ): void {
+ 'use strict';
+
+ if (!this._count) {
+ return;
}
- return val;
- }
- else {
- return this._rawData.getItem(this.getRawIndex(idx));
- }
-};
-/**
- * @param {number} idx
- * @param {boolean} [notDefaultIdx=false]
- * @return {string}
- */
-listProto.getName = function (idx) {
- var rawIndex = this.getRawIndex(idx);
- return this._nameList[rawIndex]
- || getRawValueFromStore(this, this._nameDimIdx, rawIndex)
- || '';
-};
+ if (typeof dims === 'function') {
+ ctxCompat = ctx;
+ ctx = cb as Ctx;
+ cb = dims;
+ dims = [];
+ }
-/**
- * @param {number} idx
- * @param {boolean} [notDefaultIdx=false]
- * @return {string}
- */
-listProto.getId = function (idx) {
- return getId(this, this.getRawIndex(idx));
-};
+ // ctxCompat just for compat echarts3
+ var fCtx = (ctx || ctxCompat || this) as CtxOrList<Ctx>;
-function getId(list, rawIndex) {
- var id = list._idList[rawIndex];
- if (id == null) {
- id = getRawValueFromStore(list, list._idDimIdx, rawIndex);
- }
- if (id == null) {
- // FIXME Check the usage in graph, should not use prefix.
- id = ID_PREFIX + rawIndex;
- }
- return id;
-}
+ var dimNames = zrUtil.map(normalizeDimensions(dims), this.getDimension, this);
-function normalizeDimensions(dimensions) {
- if (!zrUtil.isArray(dimensions)) {
- dimensions = [dimensions];
- }
- return dimensions;
-}
+ if (__DEV__) {
+ validateDimensions(this, dimNames);
+ }
-function validateDimensions(list, dims) {
- for (var i = 0; i < dims.length; i++) {
- // stroage may be empty when no data, so use
- // dimensionInfos to check.
- if (!list._dimensionInfos[dims[i]]) {
- console.error('Unkown dimension ' + dims[i]);
+ var dimSize = dimNames.length;
+
+ for (var i = 0; i < this.count(); i++) {
+ // Simple optimization
+ switch (dimSize) {
+ case 0:
+ (cb as EachCb0<Ctx>).call(fCtx, i);
+ break;
+ case 1:
+ (cb as EachCb1<Ctx>).call(fCtx, this.get(dimNames[0], i), i);
+ break;
+ case 2:
+ (cb as EachCb2<Ctx>).call(fCtx, this.get(dimNames[0], i), this.get(dimNames[1], i), i);
+ break;
+ default:
+ var k = 0;
+ var value = [];
+ for (; k < dimSize; k++) {
+ value[k] = this.get(dimNames[k], i);
+ }
+ // Index
+ value[k] = i;
+ (cb as EachCb<Ctx>).apply(fCtx, value);
+ }
}
}
-}
-/**
- * Data iteration
- * @param {string|Array.<string>}
- * @param {Function} cb
- * @param {*} [context=this]
- *
- * @example
- * list.each('x', function (x, idx) {});
- * list.each(['x', 'y'], function (x, y, idx) {});
- * list.each(function (idx) {})
- */
-listProto.each = function (dims, cb, context, contextCompat) {
- 'use strict';
+ /**
+ * Data filter
+ */
+ filterSelf<Ctx>(cb: FilterCb0<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): List;
+ filterSelf<Ctx>(dims: DimensionLoose, cb: FilterCb1<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): List;
+ filterSelf<Ctx>(dims: [DimensionLoose], cb: FilterCb1<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): List;
+ filterSelf<Ctx>(dims: [DimensionLoose, DimensionLoose], cb: FilterCb2<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): List;
+ filterSelf<Ctx>(dims: ItrParamDims, cb: FilterCb<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): List;
+ filterSelf<Ctx>(
+ dims: ItrParamDims | FilterCb<Ctx>,
+ cb: FilterCb<Ctx> | Ctx,
+ ctx?: Ctx,
+ ctxCompat?: Ctx
+ ): List {
+ 'use strict';
+
+ if (!this._count) {
+ return;
+ }
- if (!this._count) {
- return;
- }
+ if (typeof dims === 'function') {
+ ctxCompat = ctx;
+ ctx = cb as Ctx;
+ cb = dims;
+ dims = [];
+ }
- if (typeof dims === 'function') {
- contextCompat = context;
- context = cb;
- cb = dims;
- dims = [];
- }
+ // ctxCompat just for compat echarts3
+ var fCtx = (ctx || ctxCompat || this) as CtxOrList<Ctx>;
- // contextCompat just for compat echarts3
- context = context || contextCompat || this;
+ var dimNames = zrUtil.map(
+ normalizeDimensions(dims), this.getDimension, this
+ );
- dims = zrUtil.map(normalizeDimensions(dims), this.getDimension, this);
+ if (__DEV__) {
+ validateDimensions(this, dimNames);
+ }
- if (__DEV__) {
- validateDimensions(this, dims);
- }
- var dimSize = dims.length;
-
- for (var i = 0; i < this.count(); i++) {
- // Simple optimization
- switch (dimSize) {
- case 0:
- cb.call(context, i);
- break;
- case 1:
- cb.call(context, this.get(dims[0], i), i);
- break;
- case 2:
- cb.call(context, this.get(dims[0], i), this.get(dims[1], i), i);
- break;
- default:
- var k = 0;
- var value = [];
- for (; k < dimSize; k++) {
- value[k] = this.get(dims[k], i);
+ var count = this.count();
+ var Ctor = getIndicesCtor(this);
+ var newIndices = new Ctor(count);
+ var value = [];
+ var dimSize = dimNames.length;
+
+ var offset = 0;
+ var dim0 = dimNames[0];
+
+ for (var i = 0; i < count; i++) {
+ var keep;
+ var rawIdx = this.getRawIndex(i);
+ // Simple optimization
+ if (dimSize === 0) {
+ keep = (cb as FilterCb0<Ctx>).call(fCtx, i);
+ }
+ else if (dimSize === 1) {
+ var val = this._getFast(dim0, rawIdx);
+ keep = (cb as FilterCb1<Ctx>).call(fCtx, val, i);
+ }
+ else {
+ for (var k = 0; k < dimSize; k++) {
+ value[k] = this._getFast(dim0, rawIdx);
}
- // Index
value[k] = i;
- cb.apply(context, value);
+ keep = (cb as FilterCb<Ctx>).apply(fCtx, value);
+ }
+ if (keep) {
+ newIndices[offset++] = rawIdx;
+ }
}
- }
-};
-
-/**
- * Data filter
- * @param {string|Array.<string>}
- * @param {Function} cb
- * @param {*} [context=this]
- */
-listProto.filterSelf = function (dimensions, cb, context, contextCompat) {
- 'use strict';
- if (!this._count) {
- return;
- }
-
- if (typeof dimensions === 'function') {
- contextCompat = context;
- context = cb;
- cb = dimensions;
- dimensions = [];
- }
-
- // contextCompat just for compat echarts3
- context = context || contextCompat || this;
+ // Set indices after filtered.
+ if (offset < count) {
+ this._indices = newIndices;
+ }
+ this._count = offset;
+ // Reset data extent
+ this._extent = {};
- dimensions = zrUtil.map(
- normalizeDimensions(dimensions), this.getDimension, this
- );
+ this.getRawIndex = this._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices;
- if (__DEV__) {
- validateDimensions(this, dimensions);
+ return this;
}
+ /**
+ * Select data in range. (For optimization of filter)
+ * (Manually inline code, support 5 million data filtering in data zoom.)
+ */
+ selectRange(range: {[dimName: string]: string}): List {
+ 'use strict';
- var count = this.count();
- var Ctor = getIndicesCtor(this);
- var newIndices = new Ctor(count);
- var value = [];
- var dimSize = dimensions.length;
-
- var offset = 0;
- var dim0 = dimensions[0];
-
- for (var i = 0; i < count; i++) {
- var keep;
- var rawIdx = this.getRawIndex(i);
- // Simple optimization
- if (dimSize === 0) {
- keep = cb.call(context, i);
+ if (!this._count) {
+ return;
}
- else if (dimSize === 1) {
- var val = this._getFast(dim0, rawIdx);
- keep = cb.call(context, val, i);
- }
- else {
- for (var k = 0; k < dimSize; k++) {
- value[k] = this._getFast(dim0, rawIdx);
+
+ var dimensions = [];
+ for (var dim in range) {
+ if (range.hasOwnProperty(dim)) {
+ dimensions.push(dim);
}
- value[k] = i;
- keep = cb.apply(context, value);
}
- if (keep) {
- newIndices[offset++] = rawIdx;
- }
- }
-
- // Set indices after filtered.
- if (offset < count) {
- this._indices = newIndices;
- }
- this._count = offset;
- // Reset data extent
- this._extent = {};
-
- this.getRawIndex = this._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices;
-
- return this;
-};
-
-/**
- * Select data in range. (For optimization of filter)
- * (Manually inline code, support 5 million data filtering in data zoom.)
- */
-listProto.selectRange = function (range) {
- 'use strict';
- if (!this._count) {
- return;
- }
-
- var dimensions = [];
- for (var dim in range) {
- if (range.hasOwnProperty(dim)) {
- dimensions.push(dim);
+ if (__DEV__) {
+ validateDimensions(this, dimensions);
}
- }
- if (__DEV__) {
- validateDimensions(this, dimensions);
- }
-
- var dimSize = dimensions.length;
- if (!dimSize) {
- return;
- }
+ var dimSize = dimensions.length;
+ if (!dimSize) {
+ return;
+ }
- var originalCount = this.count();
- var Ctor = getIndicesCtor(this);
- var newIndices = new Ctor(originalCount);
-
- var offset = 0;
- var dim0 = dimensions[0];
-
- var min = range[dim0][0];
- var max = range[dim0][1];
-
- var quickFinished = false;
- if (!this._indices) {
- // Extreme optimization for common case. About 2x faster in chrome.
- var idx = 0;
- if (dimSize === 1) {
- var dimStorage = this._storage[dimensions[0]];
- for (var k = 0; k < this._chunkCount; k++) {
- var chunkStorage = dimStorage[k];
- var len = Math.min(this._count - k * this._chunkSize, this._chunkSize);
- for (var i = 0; i < len; i++) {
- var val = chunkStorage[i];
- // NaN will not be filtered. Consider the case, in line chart, empty
- // value indicates the line should be broken. But for the case like
- // scatter plot, a data item with empty value will not be rendered,
- // but the axis extent may be effected if some other dim of the data
- // item has value. Fortunately it is not a significant negative effect.
- if (
- (val >= min && val <= max) || isNaN(val)
- ) {
- newIndices[offset++] = idx;
+ var originalCount = this.count();
+ var Ctor = getIndicesCtor(this);
+ var newIndices = new Ctor(originalCount);
+
+ var offset = 0;
+ var dim0 = dimensions[0];
+
+ var min = range[dim0][0];
+ var max = range[dim0][1];
+
+ var quickFinished = false;
+ if (!this._indices) {
+ // Extreme optimization for common case. About 2x faster in chrome.
+ var idx = 0;
+ if (dimSize === 1) {
+ var dimStorage = this._storage[dimensions[0]];
+ for (var k = 0; k < this._chunkCount; k++) {
+ var chunkStorage = dimStorage[k];
+ var len = Math.min(this._count - k * this._chunkSize, this._chunkSize);
+ for (var i = 0; i < len; i++) {
+ var val = chunkStorage[i];
+ // NaN will not be filtered. Consider the case, in line chart, empty
+ // value indicates the line should be broken. But for the case like
+ // scatter plot, a data item with empty value will not be rendered,
+ // but the axis extent may be effected if some other dim of the data
+ // item has value. Fortunately it is not a significant negative effect.
+ if (
+ (val >= min && val <= max) || isNaN(val as any)
+ ) {
+ newIndices[offset++] = idx;
+ }
+ idx++;
}
- idx++;
}
+ quickFinished = true;
}
- quickFinished = true;
- }
- else if (dimSize === 2) {
- var dimStorage = this._storage[dim0];
- var dimStorage2 = this._storage[dimensions[1]];
- var min2 = range[dimensions[1]][0];
- var max2 = range[dimensions[1]][1];
- for (var k = 0; k < this._chunkCount; k++) {
- var chunkStorage = dimStorage[k];
- var chunkStorage2 = dimStorage2[k];
- var len = Math.min(this._count - k * this._chunkSize, this._chunkSize);
- for (var i = 0; i < len; i++) {
- var val = chunkStorage[i];
- var val2 = chunkStorage2[i];
- // Do not filter NaN, see comment above.
- if ((
- (val >= min && val <= max) || isNaN(val)
- )
- && (
- (val2 >= min2 && val2 <= max2) || isNaN(val2)
- )
- ) {
- newIndices[offset++] = idx;
+ else if (dimSize === 2) {
+ var dimStorage = this._storage[dim0];
+ var dimStorage2 = this._storage[dimensions[1]];
+ var min2 = range[dimensions[1]][0];
+ var max2 = range[dimensions[1]][1];
+ for (var k = 0; k < this._chunkCount; k++) {
+ var chunkStorage = dimStorage[k];
+ var chunkStorage2 = dimStorage2[k];
+ var len = Math.min(this._count - k * this._chunkSize, this._chunkSize);
+ for (var i = 0; i < len; i++) {
+ var val = chunkStorage[i];
+ var val2 = chunkStorage2[i];
+ // Do not filter NaN, see comment above.
+ if ((
+ (val >= min && val <= max) || isNaN(val as any)
+ )
+ && (
+ (val2 >= min2 && val2 <= max2) || isNaN(val2 as any)
+ )
+ ) {
+ newIndices[offset++] = idx;
+ }
+ idx++;
}
- idx++;
- }
- }
- quickFinished = true;
- }
- }
- if (!quickFinished) {
- if (dimSize === 1) {
- for (var i = 0; i < originalCount; i++) {
- var rawIndex = this.getRawIndex(i);
- var val = this._getFast(dim0, rawIndex);
- // Do not filter NaN, see comment above.
- if (
- (val >= min && val <= max) || isNaN(val)
- ) {
- newIndices[offset++] = rawIndex;
}
+ quickFinished = true;
}
}
- else {
- for (var i = 0; i < originalCount; i++) {
- var keep = true;
- var rawIndex = this.getRawIndex(i);
- for (var k = 0; k < dimSize; k++) {
- var dimk = dimensions[k];
- var val = this._getFast(dim, rawIndex);
+ if (!quickFinished) {
+ if (dimSize === 1) {
+ for (var i = 0; i < originalCount; i++) {
+ var rawIndex = this.getRawIndex(i);
+ var val = this._getFast(dim0, rawIndex);
// Do not filter NaN, see comment above.
- if (val < range[dimk][0] || val > range[dimk][1]) {
- keep = false;
+ if (
+ (val >= min && val <= max) || isNaN(val as any)
+ ) {
+ newIndices[offset++] = rawIndex;
}
}
- if (keep) {
- newIndices[offset++] = this.getRawIndex(i);
+ }
+ else {
+ for (var i = 0; i < originalCount; i++) {
+ var keep = true;
+ var rawIndex = this.getRawIndex(i);
+ for (var k = 0; k < dimSize; k++) {
+ var dimk = dimensions[k];
+ var val = this._getFast(dim, rawIndex);
+ // Do not filter NaN, see comment above.
+ if (val < range[dimk][0] || val > range[dimk][1]) {
+ keep = false;
+ }
+ }
+ if (keep) {
+ newIndices[offset++] = this.getRawIndex(i);
+ }
}
}
}
- }
- // Set indices after filtered.
- if (offset < originalCount) {
- this._indices = newIndices;
+ // Set indices after filtered.
+ if (offset < originalCount) {
+ this._indices = newIndices;
+ }
+ this._count = offset;
+ // Reset data extent
+ this._extent = {};
+
+ this.getRawIndex = this._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices;
+
+ return this;
}
- this._count = offset;
- // Reset data extent
- this._extent = {};
- this.getRawIndex = this._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices;
+ /**
+ * Data mapping to a plain array
+ */
+ mapArray<Ctx>(cb: MapArrayCb0<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): any[];
+ mapArray<Ctx>(dims: DimensionLoose, cb: MapArrayCb1<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): any[];
+ mapArray<Ctx>(dims: [DimensionLoose], cb: MapArrayCb1<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): any[];
+ mapArray<Ctx>(dims: [DimensionLoose, DimensionLoose], cb: MapArrayCb2<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): any[];
+ mapArray<Ctx>(dims: ItrParamDims, cb: MapArrayCb<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): any[];
+ mapArray<Ctx>(
+ dims: ItrParamDims | MapArrayCb<Ctx>,
+ cb: MapArrayCb<Ctx> | Ctx,
+ ctx?: Ctx,
+ ctxCompat?: Ctx
+ ): any[] {
+ 'use strict';
+
+ if (typeof dims === 'function') {
+ ctxCompat = ctx;
+ ctx = cb as Ctx;
+ cb = dims;
+ dims = [];
+ }
- return this;
-};
+ // ctxCompat just for compat echarts3
+ ctx = (ctx || ctxCompat || this) as Ctx;
-/**
- * Data mapping to a plain array
- * @param {string|Array.<string>} [dimensions]
- * @param {Function} cb
- * @param {*} [context=this]
- * @return {Array}
- */
-listProto.mapArray = function (dimensions, cb, context, contextCompat) {
- 'use strict';
-
- if (typeof dimensions === 'function') {
- contextCompat = context;
- context = cb;
- cb = dimensions;
- dimensions = [];
+ var result: any[] = [];
+ this.each(dims, function () {
+ result.push(cb && (cb as MapArrayCb<Ctx>).apply(this, arguments));
+ }, ctx);
+ return result;
}
- // contextCompat just for compat echarts3
- context = context || contextCompat || this;
+ /**
+ * Data mapping to a new List with given dimensions
+ */
+ map<Ctx>(dims: DimensionLoose, cb: MapCb1<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): List;
+ map<Ctx>(dims: [DimensionLoose], cb: MapCb1<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): List;
+ map<Ctx>(dims: [DimensionLoose, DimensionLoose], cb: MapCb2<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): List;
+ map<Ctx>(
+ dims: ItrParamDims,
+ cb: MapCb<Ctx>,
+ ctx?: Ctx,
+ ctxCompat?: Ctx
+ ): List {
+ 'use strict';
+
+ // ctxCompat just for compat echarts3
+ var fCtx = (ctx || ctxCompat || this) as CtxOrList<Ctx>;
+
+ var dimNames = zrUtil.map(
+ normalizeDimensions(dims), this.getDimension, this
+ );
+
+ if (__DEV__) {
+ validateDimensions(this, dimNames);
+ }
+
+ var list = cloneListForMapAndSample(this, dimNames);
- var result = [];
- this.each(dimensions, function () {
- result.push(cb && cb.apply(this, arguments));
- }, context);
- return result;
-};
+ // Following properties are all immutable.
+ // So we can reference to the same value
+ list._indices = this._indices;
+ list.getRawIndex = list._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices;
+
+ var storage = list._storage;
-// Data in excludeDimensions is copied, otherwise transfered.
-function cloneListForMapAndSample(original, excludeDimensions) {
- var allDimensions = original.dimensions;
- var list = new List(
- zrUtil.map(allDimensions, original.getDimensionInfo, original),
- original.hostModel
- );
- // FIXME If needs stackedOn, value may already been stacked
- transferProperties(list, original);
-
- var storage = list._storage = {};
- var originalStorage = original._storage;
-
- // Init storage
- for (var i = 0; i < allDimensions.length; i++) {
- var dim = allDimensions[i];
- if (originalStorage[dim]) {
- // Notice that we do not reset invertedIndicesMap here, becuase
- // there is no scenario of mapping or sampling ordinal dimension.
- if (zrUtil.indexOf(excludeDimensions, dim) >= 0) {
- storage[dim] = cloneDimStore(originalStorage[dim]);
- list._rawExtent[dim] = getInitialExtent();
- list._extent[dim] = null;
+ var tmpRetValue = [];
+ var chunkSize = this._chunkSize;
+ var dimSize = dimNames.length;
+ var dataCount = this.count();
+ var values = [];
+ var rawExtent = list._rawExtent;
+
+ for (var dataIndex = 0; dataIndex < dataCount; dataIndex++) {
+ for (var dimIndex = 0; dimIndex < dimSize; dimIndex++) {
+ values[dimIndex] = this.get(dimNames[dimIndex], dataIndex);
}
- else {
- // Direct reference for other dimensions
- storage[dim] = originalStorage[dim];
+ values[dimSize] = dataIndex;
+
+ var retValue = cb && cb.apply(fCtx, values);
+ if (retValue != null) {
+ // a number or string (in oridinal dimension)?
+ if (typeof retValue !== 'object') {
+ tmpRetValue[0] = retValue;
+ retValue = tmpRetValue;
+ }
+
+ var rawIndex = this.getRawIndex(dataIndex);
+ var chunkIndex = Math.floor(rawIndex / chunkSize);
+ var chunkOffset = rawIndex % chunkSize;
+
+ for (var i = 0; i < retValue.length; i++) {
+ var dim = dimNames[i];
+ var val = retValue[i];
+ var rawExtentOnDim = rawExtent[dim];
+
+ var dimStore = storage[dim];
+ if (dimStore) {
+ dimStore[chunkIndex][chunkOffset] = val;
+ }
+
+ if (val < rawExtentOnDim[0]) {
+ rawExtentOnDim[0] = val as number;
+ }
+ if (val > rawExtentOnDim[1]) {
+ rawExtentOnDim[1] = val as number;
+ }
+ }
}
}
- }
- return list;
-}
-function cloneDimStore(originalDimStore) {
- var newDimStore = new Array(originalDimStore.length);
- for (var j = 0; j < originalDimStore.length; j++) {
- newDimStore[j] = cloneChunk(originalDimStore[j]);
+ return list;
}
- return newDimStore;
-}
-function getInitialExtent() {
- return [Infinity, -Infinity];
-}
+ /**
+ * Large data down sampling on given dimension
+ * @param sampleIndex Sample index for name and id
+ */
+ downSample(
+ dimension: DimensionName,
+ rate: number,
+ sampleValue: (frameValues: ParsedDataValue[]) => ParsedDataNumeric,
+ sampleIndex: (frameValues: ParsedDataValue[], value: ParsedDataNumeric) => number
+ ): List {
+ var list = cloneListForMapAndSample(this, [dimension]);
+ var targetStorage = list._storage;
+
+ var frameValues = [];
+ var frameSize = Math.floor(1 / rate);
+
+ var dimStore = targetStorage[dimension];
+ var len = this.count();
+ var chunkSize = this._chunkSize;
+ var rawExtentOnDim = list._rawExtent[dimension];
+
+ var newIndices = new (getIndicesCtor(this))(len);
+
+ var offset = 0;
+ for (var i = 0; i < len; i += frameSize) {
+ // Last frame
+ if (frameSize > len - i) {
+ frameSize = len - i;
+ frameValues.length = frameSize;
+ }
+ for (var k = 0; k < frameSize; k++) {
+ var dataIdx = this.getRawIndex(i + k);
+ var originalChunkIndex = Math.floor(dataIdx / chunkSize);
+ var originalChunkOffset = dataIdx % chunkSize;
+ frameValues[k] = dimStore[originalChunkIndex][originalChunkOffset];
+ }
+ var value = sampleValue(frameValues);
+ var sampleFrameIdx = this.getRawIndex(
+ Math.min(i + sampleIndex(frameValues, value) || 0, len - 1)
+ );
+ var sampleChunkIndex = Math.floor(sampleFrameIdx / chunkSize);
+ var sampleChunkOffset = sampleFrameIdx % chunkSize;
+ // Only write value on the filtered data
+ dimStore[sampleChunkIndex][sampleChunkOffset] = value;
-/**
- * Data mapping to a new List with given dimensions
- * @param {string|Array.<string>} dimensions
- * @param {Function} cb
- * @param {*} [context=this]
- * @return {Array}
- */
-listProto.map = function (dimensions, cb, context, contextCompat) {
- 'use strict';
+ if (value < rawExtentOnDim[0]) {
+ rawExtentOnDim[0] = value;
+ }
+ if (value > rawExtentOnDim[1]) {
+ rawExtentOnDim[1] = value;
+ }
+
+ newIndices[offset++] = sampleFrameIdx;
+ }
- // contextCompat just for compat echarts3
- context = context || contextCompat || this;
+ list._count = offset;
+ list._indices = newIndices;
- dimensions = zrUtil.map(
- normalizeDimensions(dimensions), this.getDimension, this
- );
+ list.getRawIndex = getRawIndexWithIndices;
- if (__DEV__) {
- validateDimensions(this, dimensions);
+ return list;
}
- var list = cloneListForMapAndSample(this, dimensions);
-
- // Following properties are all immutable.
- // So we can reference to the same value
- list._indices = this._indices;
- list.getRawIndex = list._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices;
+ /**
+ * Get model of one data item.
+ */
+ // FIXME Model proxy ?
+ getItemModel(idx: number): Model {
+ var hostModel = this.hostModel;
+ return new Model(this.getRawDataItem(idx), hostModel, hostModel && hostModel.ecModel);
+ }
- var storage = list._storage;
+ /**
+ * Create a data differ
+ */
+ diff(otherList: List): DataDiffer {
+ var thisList = this;
+
+ return new DataDiffer(
+ otherList ? otherList.getIndices() : [],
+ this.getIndices(),
+ function (idx) {
+ return getId(otherList, idx);
+ },
+ function (idx) {
+ return getId(thisList, idx);
+ }
+ );
+ }
- var tmpRetValue = [];
- var chunkSize = this._chunkSize;
- var dimSize = dimensions.length;
- var dataCount = this.count();
- var values = [];
- var rawExtent = list._rawExtent;
+ /**
+ * Get visual property.
+ */
+ getVisual(key: string): any {
+ var visual = this._visual;
+ return visual && visual[key];
+ }
- for (var dataIndex = 0; dataIndex < dataCount; dataIndex++) {
- for (var dimIndex = 0; dimIndex < dimSize; dimIndex++) {
- values[dimIndex] = this.get(dimensions[dimIndex], dataIndex /*, stack */);
+ /**
+ * Set visual property
+ *
+ * @example
+ * setVisual('color', color);
+ * setVisual({
+ * 'color': color
+ * });
+ */
+ setVisual(key: string, val: any): void;
+ setVisual(kvObj: Dictionary<any>): void;
+ setVisual(key: string | Dictionary<any>, val?: any): void {
+ if (isObject<Dictionary<any>>(key)) {
+ for (var name in key) {
+ if (key.hasOwnProperty(name)) {
+ this.setVisual(name, key[name]);
+ }
+ }
+ return;
}
- values[dimSize] = dataIndex;
+ this._visual = this._visual || {};
+ this._visual[key] = val;
+ }
- var retValue = cb && cb.apply(context, values);
- if (retValue != null) {
- // a number or string (in oridinal dimension)?
- if (typeof retValue !== 'object') {
- tmpRetValue[0] = retValue;
- retValue = tmpRetValue;
+ /**
+ * Set layout property.
+ */
+ setLayout(key: string, val: any): void;
+ setLayout(kvObj: Dictionary<any>): void;
+ setLayout(key: string | Dictionary<any>, val?: any): void {
+ if (isObject<Dictionary<any>>(key)) {
+ for (var name in key) {
+ if (key.hasOwnProperty(name)) {
+ this.setLayout(name, key[name]);
+ }
}
+ return;
+ }
+ this._layout[key] = val;
+ }
+
+ /**
+ * Get layout property.
+ */
+ getLayout(key: string): any {
+ return this._layout[key];
+ }
- var rawIndex = this.getRawIndex(dataIndex);
- var chunkIndex = Math.floor(rawIndex / chunkSize);
- var chunkOffset = rawIndex % chunkSize;
+ /**
+ * Get layout of single data item
+ */
+ getItemLayout(idx: number): Dictionary<any> {
+ return this._itemLayouts[idx];
+ }
- for (var i = 0; i < retValue.length; i++) {
- var dim = dimensions[i];
- var val = retValue[i];
- var rawExtentOnDim = rawExtent[dim];
+ /**
+ * Set layout of single data item
+ */
+ setItemLayout<M = false>(
+ idx: number,
+ layout: (M extends true ? Dictionary<any> : any),
+ merge?: M
+ ): void {
+ this._itemLayouts[idx] = merge
+ ? zrUtil.extend(this._itemLayouts[idx] || {}, layout)
+ : layout;
+ }
- var dimStore = storage[dim];
- if (dimStore) {
- dimStore[chunkIndex][chunkOffset] = val;
- }
+ /**
+ * Clear all layout of single data item
+ */
+ clearItemLayouts(): void {
+ this._itemLayouts.length = 0;
+ }
- if (val < rawExtentOnDim[0]) {
- rawExtentOnDim[0] = val;
- }
- if (val > rawExtentOnDim[1]) {
- rawExtentOnDim[1] = val;
+ /**
+ * Get visual property of single data item
+ */
+ getItemVisual(idx: number, key: string, ignoreParent?: boolean): any {
+ var itemVisual = this._itemVisuals[idx];
+ var val = itemVisual && itemVisual[key];
+ if (val == null && !ignoreParent) {
+ // Use global visual property
+ return this.getVisual(key);
+ }
+ return val;
+ }
+
+ /**
+ * Set visual property of single data item
+ *
+ * @param {number} idx
+ * @param {string|Object} key
+ * @param {*} [value]
+ *
+ * @example
+ * setItemVisual(0, 'color', color);
+ * setItemVisual(0, {
+ * 'color': color
+ * });
+ */
+ setItemVisual(idx: number, key: string, value: any): void;
+ setItemVisual(idx: number, kvObject: Dictionary<any>): void;
+ setItemVisual(idx: number, key: string | Dictionary<any>, value?: any): void {
+ var itemVisual = this._itemVisuals[idx] || {};
+ var hasItemVisual = this.hasItemVisual;
+ this._itemVisuals[idx] = itemVisual;
+
+ if (isObject<Dictionary<any>>(key)) {
+ for (var name in key) {
+ if (key.hasOwnProperty(name)) {
+ itemVisual[name] = key[name];
+ hasItemVisual[name] = true;
}
}
+ return;
}
+ itemVisual[key] = value;
+ hasItemVisual[key] = true;
}
- return list;
-};
+ /**
+ * Clear itemVisuals and list visual.
+ */
+ clearAllVisual(): void {
+ this._visual = {};
+ this._itemVisuals = [];
+ this.hasItemVisual = {};
+ }
-/**
- * Large data down sampling on given dimension
- * @param {string} dimension
- * @param {number} rate
- * @param {Function} sampleValue
- * @param {Function} sampleIndex Sample index for name and id
- */
-listProto.downSample = function (dimension, rate, sampleValue, sampleIndex) {
- var list = cloneListForMapAndSample(this, [dimension]);
- var targetStorage = list._storage;
-
- var frameValues = [];
- var frameSize = Math.floor(1 / rate);
-
- var dimStore = targetStorage[dimension];
- var len = this.count();
- var chunkSize = this._chunkSize;
- var rawExtentOnDim = list._rawExtent[dimension];
-
- var newIndices = new (getIndicesCtor(this))(len);
-
- var offset = 0;
- for (var i = 0; i < len; i += frameSize) {
- // Last frame
- if (frameSize > len - i) {
- frameSize = len - i;
- frameValues.length = frameSize;
- }
- for (var k = 0; k < frameSize; k++) {
- var dataIdx = this.getRawIndex(i + k);
- var originalChunkIndex = Math.floor(dataIdx / chunkSize);
- var originalChunkOffset = dataIdx % chunkSize;
- frameValues[k] = dimStore[originalChunkIndex][originalChunkOffset];
- }
- var value = sampleValue(frameValues);
- var sampleFrameIdx = this.getRawIndex(
- Math.min(i + sampleIndex(frameValues, value) || 0, len - 1)
- );
- var sampleChunkIndex = Math.floor(sampleFrameIdx / chunkSize);
- var sampleChunkOffset = sampleFrameIdx % chunkSize;
- // Only write value on the filtered data
- dimStore[sampleChunkIndex][sampleChunkOffset] = value;
+ /**
+ * Set graphic element relative to data. It can be set as null
+ */
+ setItemGraphicEl(idx: number, el: Element): void {
+ var hostModel = this.hostModel;
- if (value < rawExtentOnDim[0]) {
- rawExtentOnDim[0] = value;
- }
- if (value > rawExtentOnDim[1]) {
- rawExtentOnDim[1] = value;
+ if (el) {
+ // Add data index and series index for indexing the data by element
+ // Useful in tooltip
+ (el as ECElement).dataIndex = idx;
+ (el as ECElement).dataType = this.dataType;
+ (el as ECElement).seriesIndex = hostModel && (hostModel as any).seriesIndex;
+ if (el.type === 'group') {
+ el.traverse(setItemDataAndSeriesIndex, el);
+ }
}
- newIndices[offset++] = sampleFrameIdx;
+ this._graphicEls[idx] = el;
}
- list._count = offset;
- list._indices = newIndices;
+ getItemGraphicEl(idx: number): Element {
+ return this._graphicEls[idx];
+ }
- list.getRawIndex = getRawIndexWithIndices;
+ eachItemGraphicEl<Ctx = unknown>(
+ cb: (this: Ctx, el: Element, idx: number) => void,
+ context?: Ctx
+ ): void {
+ zrUtil.each(this._graphicEls, function (el, idx) {
+ if (el) {
+ cb && cb.call(context, el, idx);
+ }
+ });
+ }
- return list;
-};
+ /**
+ * Shallow clone a new list except visual and layout properties, and graph elements.
+ * New list only change the indices.
+ */
+ cloneShallow(list?: List): List {
+ if (!list) {
+ var dimensionInfoList = zrUtil.map(this.dimensions, this.getDimensionInfo, this);
+ list = new List(dimensionInfoList, this.hostModel);
+ }
-/**
- * Get model of one data item.
- *
- * @param {number} idx
- */
-// FIXME Model proxy ?
-listProto.getItemModel = function (idx) {
- var hostModel = this.hostModel;
- return new Model(this.getRawDataItem(idx), hostModel, hostModel && hostModel.ecModel);
-};
+ // FIXME
+ list._storage = this._storage;
-/**
- * Create a data differ
- * @param {module:echarts/data/List} otherList
- * @return {module:echarts/data/DataDiffer}
- */
-listProto.diff = function (otherList) {
- var thisList = this;
-
- return new DataDiffer(
- otherList ? otherList.getIndices() : [],
- this.getIndices(),
- function (idx) {
- return getId(otherList, idx);
- },
- function (idx) {
- return getId(thisList, idx);
- }
- );
-};
-/**
- * Get visual property.
- * @param {string} key
- */
-listProto.getVisual = function (key) {
- var visual = this._visual;
- return visual && visual[key];
-};
+ transferProperties(list, this);
-/**
- * Set visual property
- * @param {string|Object} key
- * @param {*} [value]
- *
- * @example
- * setVisual('color', color);
- * setVisual({
- * 'color': color
- * });
- */
-listProto.setVisual = function (key, val) {
- if (isObject(key)) {
- for (var name in key) {
- if (key.hasOwnProperty(name)) {
- this.setVisual(name, key[name]);
+ // Clone will not change the data extent and indices
+ if (this._indices) {
+ var Ctor = this._indices.constructor as DataArrayLikeConstructor;
+ if (Ctor === Array) {
+ var thisCount = this._indices.length;
+ list._indices = new Ctor(thisCount);
+ for (var i = 0; i < thisCount; i++) {
+ list._indices[i] = this._indices[i];
+ }
+ }
+ else {
+ list._indices = new (Ctor as DataTypedArrayConstructor)(this._indices);
}
}
- return;
+ else {
+ list._indices = null;
+ }
+ list.getRawIndex = list._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices;
+
+ return list;
}
- this._visual = this._visual || {};
- this._visual[key] = val;
-};
-/**
- * Set layout property.
- * @param {string|Object} key
- * @param {*} [val]
- */
-listProto.setLayout = function (key, val) {
- if (isObject(key)) {
- for (var name in key) {
- if (key.hasOwnProperty(name)) {
- this.setLayout(name, key[name]);
- }
+ /**
+ * Wrap some method to add more feature
+ */
+ wrapMethod(
+ methodName: FunctionPropertyNames<List>,
+ injectFunction: (...args: any) => any
+ ): void {
+ var originalMethod = this[methodName];
+ if (typeof originalMethod !== 'function') {
+ return;
}
- return;
+ this.__wrappedMethods = this.__wrappedMethods || [];
+ this.__wrappedMethods.push(methodName);
+ this[methodName] = function () {
+ var res = (originalMethod as any).apply(this, arguments);
+ return injectFunction.apply(this, [res].concat(zrUtil.slice(arguments)));
+ };
}
- this._layout[key] = val;
-};
-/**
- * Get layout property.
- * @param {string} key.
- * @return {*}
- */
-listProto.getLayout = function (key) {
- return this._layout[key];
-};
-/**
- * Get layout of single data item
- * @param {number} idx
- */
-listProto.getItemLayout = function (idx) {
- return this._itemLayouts[idx];
-};
+ // ----------------------------------------------------------
+ // A work around for internal method visiting private member.
+ // ----------------------------------------------------------
+ static internalField = (function () {
-/**
- * Set layout of single data item
- * @param {number} idx
- * @param {Object} layout
- * @param {boolean=} [merge=false]
- */
-listProto.setItemLayout = function (idx, layout, merge) {
- this._itemLayouts[idx] = merge
- ? zrUtil.extend(this._itemLayouts[idx] || {}, layout)
- : layout;
-};
+ defaultDimValueGetters = {
-/**
- * Clear all layout of single data item
- */
-listProto.clearItemLayouts = function () {
- this._itemLayouts.length = 0;
-};
+ arrayRows: getDimValueSimply,
-/**
- * Get visual property of single data item
- * @param {number} idx
- * @param {string} key
- * @param {boolean} [ignoreParent=false]
- */
-listProto.getItemVisual = function (idx, key, ignoreParent) {
- var itemVisual = this._itemVisuals[idx];
- var val = itemVisual && itemVisual[key];
- if (val == null && !ignoreParent) {
- // Use global visual property
- return this.getVisual(key);
- }
- return val;
-};
+ objectRows: function (
+ this: List, dataItem: Dictionary<any>, dimName: string, dataIndex: number, dimIndex: number
+ ): ParsedDataValue {
+ return convertDataValue(dataItem[dimName], this._dimensionInfos[dimName]);
+ },
-/**
- * Set visual property of single data item
- *
- * @param {number} idx
- * @param {string|Object} key
- * @param {*} [value]
- *
- * @example
- * setItemVisual(0, 'color', color);
- * setItemVisual(0, {
- * 'color': color
- * });
- */
-listProto.setItemVisual = function (idx, key, value) {
- var itemVisual = this._itemVisuals[idx] || {};
- var hasItemVisual = this.hasItemVisual;
- this._itemVisuals[idx] = itemVisual;
-
- if (isObject(key)) {
- for (var name in key) {
- if (key.hasOwnProperty(name)) {
- itemVisual[name] = key[name];
- hasItemVisual[name] = true;
+ keyedColumns: getDimValueSimply,
+
+ original: function (
+ this: List, dataItem: any, dimName: string, dataIndex: number, dimIndex: number
+ ): ParsedDataValue {
+ // Performance sensitive, do not use modelUtil.getDataItemValue.
+ // If dataItem is an plain object with no value field, the var `value`
+ // will be assigned with the object, but it will be tread correctly
+ // in the `convertDataValue`.
+ var value = dataItem && (dataItem.value == null ? dataItem : dataItem.value);
+
+ // If any dataItem is like { value: 10 }
+ if (!this._rawData.pure && isDataItemOption(dataItem)) {
+ this.hasItemOption = true;
+ }
+ return convertDataValue(
+ (value instanceof Array)
+ ? value[dimIndex]
+ // If value is a single number or something else not array.
+ : value,
+ this._dimensionInfos[dimName]
+ );
+ },
+
+ typedArray: function (
+ this: List, dataItem: any, dimName: string, dataIndex: number, dimIndex: number
+ ): ParsedDataValue {
+ return dataItem[dimIndex];
}
- }
- return;
- }
- itemVisual[key] = value;
- hasItemVisual[key] = true;
-};
-/**
- * Clear itemVisuals and list visual.
- */
-listProto.clearAllVisual = function () {
- this._visual = {};
- this._itemVisuals = [];
- this.hasItemVisual = {};
-};
+ };
-var setItemDataAndSeriesIndex = function (child) {
- child.seriesIndex = this.seriesIndex;
- child.dataIndex = this.dataIndex;
- child.dataType = this.dataType;
-};
-/**
- * Set graphic element relative to data. It can be set as null
- * @param {number} idx
- * @param {module:zrender/Element} [el]
- */
-listProto.setItemGraphicEl = function (idx, el) {
- var hostModel = this.hostModel;
-
- if (el) {
- // Add data index and series index for indexing the data by element
- // Useful in tooltip
- el.dataIndex = idx;
- el.dataType = this.dataType;
- el.seriesIndex = hostModel && hostModel.seriesIndex;
- if (el.type === 'group') {
- el.traverse(setItemDataAndSeriesIndex, el);
+ function getDimValueSimply(
+ this: List, dataItem: any, dimName: string, dataIndex: number, dimIndex: number
+ ): ParsedDataValue {
+ return convertDataValue(dataItem[dimIndex], this._dimensionInfos[dimName]);
}
- }
- this._graphicEls[idx] = el;
-};
+ /**
+ * Convert raw the value in to inner value in List.
+ * [Caution]: this is the key logic of user value parser.
+ * For backward compatibiliy, do not modify it until have to.
+ */
+ function convertDataValue(value: any, dimInfo: DataDimensionInfo): ParsedDataValue {
+ // Performance sensitive.
+ var dimType = dimInfo && dimInfo.type;
+ if (dimType === 'ordinal') {
+ // If given value is a category string
+ var ordinalMeta = dimInfo && dimInfo.ordinalMeta;
+ return ordinalMeta
+ ? ordinalMeta.parseAndCollect(value)
+ : value;
+ }
-/**
- * @param {number} idx
- * @return {module:zrender/Element}
- */
-listProto.getItemGraphicEl = function (idx) {
- return this._graphicEls[idx];
-};
+ if (dimType === 'time'
+ // spead up when using timestamp
+ && typeof value !== 'number'
+ && value != null
+ && value !== '-'
+ ) {
+ value = +parseDate(value);
+ }
-/**
- * @param {Function} cb
- * @param {*} context
- */
-listProto.eachItemGraphicEl = function (cb, context) {
- zrUtil.each(this._graphicEls, function (el, idx) {
- if (el) {
- cb && cb.call(context, el, idx);
+ // dimType defaults 'number'.
+ // If dimType is not ordinal and value is null or undefined or NaN or '-',
+ // parse to NaN.
+ return (value == null || value === '')
+ ? NaN
+ // If string (like '-'), using '+' parse to NaN
+ // If object, also parse to NaN
+ : +value;
+ };
+
+ prepareInvertedIndex = function (list: List): void {
+ var invertedIndicesMap = list._invertedIndicesMap;
+ zrUtil.each(invertedIndicesMap, function (invertedIndices, dim) {
+ var dimInfo = list._dimensionInfos[dim];
+
+ // Currently, only dimensions that has ordinalMeta can create inverted indices.
+ var ordinalMeta = dimInfo.ordinalMeta;
+ if (ordinalMeta) {
+ invertedIndices = invertedIndicesMap[dim] = new CtorInt32Array(
+ ordinalMeta.categories.length
+ );
+ // The default value of TypedArray is 0. To avoid miss
+ // mapping to 0, we should set it as INDEX_NOT_FOUND.
+ for (var i = 0; i < invertedIndices.length; i++) {
+ invertedIndices[i] = INDEX_NOT_FOUND;
+ }
+ for (var i = 0; i < list._count; i++) {
+ // Only support the case that all values are distinct.
+ invertedIndices[list.get(dim, i) as number] = i;
+ }
+ }
+ });
+ };
+
+ getRawValueFromStore = function (list: List, dimIndex: number, rawIndex: number): any {
+ var val;
+ if (dimIndex != null) {
+ var chunkSize = list._chunkSize;
+ var chunkIndex = Math.floor(rawIndex / chunkSize);
+ var chunkOffset = rawIndex % chunkSize;
+ var dim = list.dimensions[dimIndex];
+ var chunk = list._storage[dim][chunkIndex];
+ if (chunk) {
+ val = chunk[chunkOffset];
+ var ordinalMeta = list._dimensionInfos[dim].ordinalMeta;
+ if (ordinalMeta && ordinalMeta.categories.length) {
+ val = ordinalMeta.categories[val as OrdinalRawValueIndex];
+ }
+ }
+ }
+ return val;
+ };
+
+ getIndicesCtor = function (list: List): DataArrayLikeConstructor {
+ // The possible max value in this._indicies is always this._rawCount despite of filtering.
+ return list._rawCount > 65535 ? CtorUint32Array : CtorUint16Array;
+ };
+
+ prepareChunks = function (
+ storage: DataStorage,
+ dimInfo: DataDimensionInfo,
+ chunkSize: number,
+ chunkCount: number,
+ end: number
+ ): void {
+ var DataCtor = dataCtors[dimInfo.type];
+ var lastChunkIndex = chunkCount - 1;
+ var dim = dimInfo.name;
+ var resizeChunkArray = storage[dim][lastChunkIndex];
+ if (resizeChunkArray && resizeChunkArray.length < chunkSize) {
+ var newStore = new DataCtor(Math.min(end - lastChunkIndex * chunkSize, chunkSize));
+ // The cost of the copy is probably inconsiderable
+ // within the initial chunkSize.
+ for (var j = 0; j < resizeChunkArray.length; j++) {
+ newStore[j] = resizeChunkArray[j];
+ }
+ storage[dim][lastChunkIndex] = newStore;
+ }
+
+ // Create new chunks.
+ for (var k = chunkCount * chunkSize; k < end; k += chunkSize) {
+ storage[dim].push(new DataCtor(Math.min(end - k, chunkSize)));
+ }
+ };
+
+ getRawIndexWithoutIndices = function (this: List, idx: number): number {
+ return idx;
+ };
+
+ getRawIndexWithIndices = function (this: List, idx: number): number {
+ if (idx < this._count && idx >= 0) {
+ return this._indices[idx];
+ }
+ return -1;
+ };
+
+ getId = function (list: List, rawIndex: number): string {
+ var id = list._idList[rawIndex];
+ if (id == null) {
+ id = getRawValueFromStore(list, list._idDimIdx, rawIndex);
+ }
+ if (id == null) {
+ // FIXME Check the usage in graph, should not use prefix.
+ id = ID_PREFIX + rawIndex;
+ }
+ return id;
+ };
+
+ normalizeDimensions = function (
+ dimensions: ItrParamDims
+ ): Array<DimensionLoose> {
+ if (!zrUtil.isArray(dimensions)) {
+ dimensions = [dimensions];
+ }
+ return dimensions;
+ };
+
+ validateDimensions = function (list: List, dims: DimensionName[]): void {
+ for (var i = 0; i < dims.length; i++) {
+ // stroage may be empty when no data, so use
+ // dimensionInfos to check.
+ if (!list._dimensionInfos[dims[i]]) {
+ console.error('Unkown dimension ' + dims[i]);
+ }
+ }
+ };
+
+ // Data in excludeDimensions is copied, otherwise transfered.
+ cloneListForMapAndSample = function (
+ original: List, excludeDimensions: DimensionName[]
+ ): List {
+ var allDimensions = original.dimensions;
+ var list = new List(
+ zrUtil.map(allDimensions, original.getDimensionInfo, original),
+ original.hostModel
+ );
+ // FIXME If needs stackedOn, value may already been stacked
+ transferProperties(list, original);
+
+ var storage = list._storage = {} as DataStorage;
+ var originalStorage = original._storage;
+
+ // Init storage
+ for (var i = 0; i < allDimensions.length; i++) {
+ var dim = allDimensions[i];
+ if (originalStorage[dim]) {
+ // Notice that we do not reset invertedIndicesMap here, becuase
+ // there is no scenario of mapping or sampling ordinal dimension.
+ if (zrUtil.indexOf(excludeDimensions, dim) >= 0) {
+ storage[dim] = cloneDimStore(originalStorage[dim]);
+ list._rawExtent[dim] = getInitialExtent();
+ list._extent[dim] = null;
+ }
+ else {
+ // Direct reference for other dimensions
+ storage[dim] = originalStorage[dim];
+ }
+ }
+ }
+ return list;
+ };
+
+ cloneDimStore = function (originalDimStore: DataValueChunk[]): DataValueChunk[] {
+ var newDimStore = new Array(originalDimStore.length);
+ for (var j = 0; j < originalDimStore.length; j++) {
+ newDimStore[j] = cloneChunk(originalDimStore[j]);
+ }
+ return newDimStore;
+ };
+
+ function cloneChunk(originalChunk: DataValueChunk): DataValueChunk {
+ var Ctor = originalChunk.constructor;
+ // Only shallow clone is enough when Array.
+ return Ctor === Array
+ ? (originalChunk as Array<ParsedDataValue>).slice()
+ : new (Ctor as DataTypedArrayConstructor)(originalChunk as DataTypedArray);
}
- });
-};
-/**
- * Shallow clone a new list except visual and layout properties, and graph elements.
- * New list only change the indices.
- */
-listProto.cloneShallow = function (list) {
- if (!list) {
- var dimensionInfoList = zrUtil.map(this.dimensions, this.getDimensionInfo, this);
- list = new List(dimensionInfoList, this.hostModel);
- }
+ getInitialExtent = function (): [number, number] {
+ return [Infinity, -Infinity];
+ };
+
+ setItemDataAndSeriesIndex = function (this: Element, child: Element): void {
+ (child as ECElement).seriesIndex = (this as ECElement).seriesIndex;
+ (child as ECElement).dataIndex = (this as ECElement).dataIndex;
+ (child as ECElement).dataType = (this as ECElement).dataType;
+ };
+
+ transferProperties = function (target: List, source: List): void {
+ zrUtil.each(
+ TRANSFERABLE_PROPERTIES.concat(source.__wrappedMethods || []),
+ function (propName) {
+ if (source.hasOwnProperty(propName)) {
+ (target as any)[propName] = (source as any)[propName];
+ }
+ }
+ );
- // FIXME
- list._storage = this._storage;
+ target.__wrappedMethods = source.__wrappedMethods;
- transferProperties(list, this);
+ zrUtil.each(CLONE_PROPERTIES, function (propName) {
+ (target as any)[propName] = zrUtil.clone((source as any)[propName]);
+ });
- // Clone will not change the data extent and indices
- if (this._indices) {
- var Ctor = this._indices.constructor;
- list._indices = new Ctor(this._indices);
- }
- else {
- list._indices = null;
- }
- list.getRawIndex = list._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices;
+ target._calculationInfo = zrUtil.extend({}, source._calculationInfo);
+ };
- return list;
-};
+ })();
-/**
- * Wrap some method to add more feature
- * @param {string} methodName
- * @param {Function} injectFunction
- */
-listProto.wrapMethod = function (methodName, injectFunction) {
- var originalMethod = this[methodName];
- if (typeof originalMethod !== 'function') {
- return;
- }
- this.__wrappedMethods = this.__wrappedMethods || [];
- this.__wrappedMethods.push(methodName);
- this[methodName] = function () {
- var res = originalMethod.apply(this, arguments);
- return injectFunction.apply(this, [res].concat(zrUtil.slice(arguments)));
- };
-};
+}
+
+// -----------------------------
+// Internal method declarations:
+// -----------------------------
+var defaultDimValueGetters: {[sourceFormat: string]: DimValueGetter};
+var prepareInvertedIndex: (list: List) => void;
+var getRawValueFromStore: (list: List, dimIndex: number, rawIndex: number) => any;
+var getIndicesCtor: (list: List) => DataArrayLikeConstructor;
+var prepareChunks: (
+ storage: DataStorage, dimInfo: DataDimensionInfo, chunkSize: number, chunkCount: number, end: number
+) => void;
+var getRawIndexWithoutIndices: (this: List, idx: number) => number;
+var getRawIndexWithIndices: (this: List, idx: number) => number;
+var getId: (list: List, rawIndex: number) => string;
+var normalizeDimensions: (dimensions: ItrParamDims) => Array<DimensionLoose>;
+var validateDimensions: (list: List, dims: DimensionName[]) => void;
+var cloneListForMapAndSample: (original: List, excludeDimensions: DimensionName[]) => List;
+var cloneDimStore: (originalDimStore: DataValueChunk[]) => DataValueChunk[];
+var getInitialExtent: () => [number, number];
+var setItemDataAndSeriesIndex: (this: Element, child: Element) => void;
+var transferProperties: (target: List, source: List) => void;
-// Methods that create a new list based on this list should be listed here.
-// Notice that those method should `RETURN` the new list.
-listProto.TRANSFERABLE_METHODS = ['cloneShallow', 'downSample', 'map'];
-// Methods that change indices of this list should be listed here.
-listProto.CHANGABLE_METHODS = ['filterSelf', 'selectRange'];
export default List;
diff --git a/src/data/OrdinalMeta.ts b/src/data/OrdinalMeta.ts
index d20c1d1..5beb8a1 100644
--- a/src/data/OrdinalMeta.ts
+++ b/src/data/OrdinalMeta.ts
@@ -17,125 +17,103 @@
* under the License.
*/
-import {createHashMap, isObject, map} from 'zrender/src/core/util';
+import {createHashMap, isObject, map, HashMap} from 'zrender/src/core/util';
+import Model from '../model/Model';
-/**
- * @constructor
- * @param {Object} [opt]
- * @param {Object} [opt.categories=[]]
- * @param {Object} [opt.needCollect=false]
- * @param {Object} [opt.deduplication=false]
- */
-function OrdinalMeta(opt) {
+class OrdinalMeta {
- /**
- * @readOnly
- * @type {Array.<string>}
- */
- this.categories = opt.categories || [];
+ readonly categories: string[];
- /**
- * @private
- * @type {boolean}
- */
- this._needCollect = opt.needCollect;
+ private _needCollect: boolean;
- /**
- * @private
- * @type {boolean}
- */
- this._deduplication = opt.deduplication;
+ private _deduplication: boolean;
+
+ private _map: HashMap<number>;
- /**
- * @private
- * @type {boolean}
- */
- this._map;
-}
-/**
- * @param {module:echarts/model/Model} axisModel
- * @return {module:echarts/data/OrdinalMeta}
- */
-OrdinalMeta.createByAxisModel = function (axisModel) {
- var option = axisModel.option;
- var data = option.data;
- var categories = data && map(data, getName);
-
- return new OrdinalMeta({
- categories: categories,
- needCollect: !categories,
- // deduplication is default in axis.
- deduplication: option.dedplication !== false
- });
-};
-
-var proto = OrdinalMeta.prototype;
-
-/**
- * @param {string} category
- * @return {number} ordinal
- */
-proto.getOrdinal = function (category) {
- return getOrCreateMap(this).get(category);
-};
-
-/**
- * @param {*} category
- * @return {number} The ordinal. If not found, return NaN.
- */
-proto.parseAndCollect = function (category) {
- var index;
- var needCollect = this._needCollect;
-
- // The value of category dim can be the index of the given category set.
- // This feature is only supported when !needCollect, because we should
- // consider a common case: a value is 2017, which is a number but is
- // expected to be tread as a category. This case usually happen in dataset,
- // where it happent to be no need of the index feature.
- if (typeof category !== 'string' && !needCollect) {
- return category;
+ constructor(opt: {
+ categories?: string[],
+ needCollect?: boolean
+ deduplication?: boolean
+ }) {
+ this.categories = opt.categories || [];
+ this._needCollect = opt.needCollect;
+ this._deduplication = opt.deduplication;
}
- // Optimize for the scenario:
- // category is ['2012-01-01', '2012-01-02', ...], where the input
- // data has been ensured not duplicate and is large data.
- // Notice, if a dataset dimension provide categroies, usually echarts
- // should remove duplication except user tell echarts dont do that
- // (set axis.deduplication = false), because echarts do not know whether
- // the values in the category dimension has duplication (consider the
- // parallel-aqi example)
- if (needCollect && !this._deduplication) {
- index = this.categories.length;
- this.categories[index] = category;
- return index;
+ static createByAxisModel(axisModel: Model): OrdinalMeta {
+ var option = axisModel.option;
+ var data = option.data;
+ var categories = data && map(data, getName);
+
+ return new OrdinalMeta({
+ categories: categories,
+ needCollect: !categories,
+ // deduplication is default in axis.
+ deduplication: option.dedplication !== false
+ });
+ };
+
+ getOrdinal(category: string): number {
+ return this._getOrCreateMap().get(category);
}
- var map = getOrCreateMap(this);
- index = map.get(category);
+ /**
+ * @return The ordinal. If not found, return NaN.
+ */
+ parseAndCollect(category: any): number {
+ var index;
+ var needCollect = this._needCollect;
+
+ // The value of category dim can be the index of the given category set.
+ // This feature is only supported when !needCollect, because we should
+ // consider a common case: a value is 2017, which is a number but is
+ // expected to be tread as a category. This case usually happen in dataset,
+ // where it happent to be no need of the index feature.
+ if (typeof category !== 'string' && !needCollect) {
+ return category;
+ }
- if (index == null) {
- if (needCollect) {
+ // Optimize for the scenario:
+ // category is ['2012-01-01', '2012-01-02', ...], where the input
+ // data has been ensured not duplicate and is large data.
+ // Notice, if a dataset dimension provide categroies, usually echarts
+ // should remove duplication except user tell echarts dont do that
+ // (set axis.deduplication = false), because echarts do not know whether
+ // the values in the category dimension has duplication (consider the
+ // parallel-aqi example)
+ if (needCollect && !this._deduplication) {
index = this.categories.length;
this.categories[index] = category;
- map.set(category, index);
+ return index;
}
- else {
- index = NaN;
+
+ var map = this._getOrCreateMap();
+ index = map.get(category);
+
+ if (index == null) {
+ if (needCollect) {
+ index = this.categories.length;
+ this.categories[index] = category;
+ map.set(category, index);
+ }
+ else {
+ index = NaN;
+ }
}
- }
- return index;
-};
+ return index;
+ }
-// Consider big data, do not create map until needed.
-function getOrCreateMap(ordinalMeta) {
- return ordinalMeta._map || (
- ordinalMeta._map = createHashMap(ordinalMeta.categories)
- );
+ // Consider big data, do not create map until needed.
+ private _getOrCreateMap(): HashMap<number> {
+ return this._map || (
+ this._map = createHashMap<number>(this.categories)
+ );
+ }
}
-function getName(obj) {
+function getName(obj: any): string {
if (isObject(obj) && obj.value != null) {
return obj.value;
}
diff --git a/src/data/Source.ts b/src/data/Source.ts
index 55acf3a..4d1b5fb 100644
--- a/src/data/Source.ts
+++ b/src/data/Source.ts
@@ -17,15 +17,17 @@
* under the License.
*/
-import {createHashMap, isTypedArray} from 'zrender/src/core/util';
-import {enableClassCheck} from '../util/clazz';
+import {createHashMap, isTypedArray, HashMap} from 'zrender/src/core/util';
+import {enableClassCheck, CheckableConstructor} from '../util/clazz';
import {
+ SourceFormat, SeriesLayoutBy, DimensionDefinition,
+ OptionEncodeValue, OptionSourceData, OptionEncode,
SOURCE_FORMAT_ORIGINAL,
SERIES_LAYOUT_BY_COLUMN,
SOURCE_FORMAT_UNKNOWN,
- SOURCE_FORMAT_TYPED_ARRAY,
- SOURCE_FORMAT_KEYED_COLUMNS
-} from './helper/sourceType';
+ SOURCE_FORMAT_KEYED_COLUMNS,
+ SOURCE_FORMAT_TYPED_ARRAY
+} from '../util/types';
/**
* [sourceFormat]
@@ -63,87 +65,89 @@ import {
* + "unknown"
*/
-/**
- * @constructor
- * @param {Object} fields
- * @param {string} fields.sourceFormat
- * @param {Array|Object} fields.fromDataset
- * @param {Array|Object} [fields.data]
- * @param {string} [seriesLayoutBy='column']
- * @param {Array.<Object|string>} [dimensionsDefine]
- * @param {Objet|HashMap} [encodeDefine]
- * @param {number} [startIndex=0]
- * @param {number} [dimensionsDetectCount]
- */
-function Source(fields) {
+class Source {
- /**
- * @type {boolean}
- */
- this.fromDataset = fields.fromDataset;
+ readonly fromDataset: boolean;
/**
* Not null/undefined.
* @type {Array|Object}
*/
- this.data = fields.data || (
- fields.sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS ? {} : []
- );
+ readonly data: OptionSourceData;
/**
* See also "detectSourceFormat".
* Not null/undefined.
- * @type {string}
*/
- this.sourceFormat = fields.sourceFormat || SOURCE_FORMAT_UNKNOWN;
+ readonly sourceFormat: SourceFormat;
/**
* 'row' or 'column'
* Not null/undefined.
- * @type {string} seriesLayoutBy
*/
- this.seriesLayoutBy = fields.seriesLayoutBy || SERIES_LAYOUT_BY_COLUMN;
+ readonly seriesLayoutBy: SeriesLayoutBy;
/**
* dimensions definition in option.
* can be null/undefined.
- * @type {Array.<Object|string>}
*/
- this.dimensionsDefine = fields.dimensionsDefine;
+ readonly dimensionsDefine: DimensionDefinition[];
/**
* encode definition in option.
* can be null/undefined.
- * @type {Objet|HashMap}
*/
- this.encodeDefine = fields.encodeDefine && createHashMap(fields.encodeDefine);
+ readonly encodeDefine: HashMap<OptionEncodeValue>;
/**
* Not null/undefined, uint.
- * @type {number}
*/
- this.startIndex = fields.startIndex || 0;
+ readonly startIndex: number;
/**
* Can be null/undefined (when unknown), uint.
- * @type {number}
*/
- this.dimensionsDetectCount = fields.dimensionsDetectCount;
+ readonly dimensionsDetectCount: number;
+
+
+ constructor(fields: {
+ fromDataset: boolean,
+ data?: OptionSourceData,
+ sourceFormat?: SourceFormat, // default: SOURCE_FORMAT_UNKNOWN
+ seriesLayoutBy?: SeriesLayoutBy, // default: 'column'
+ dimensionsDefine?: DimensionDefinition[],
+ encodeDefine?: OptionEncode,
+ startIndex?: number, // default: 0
+ dimensionsDetectCount?: number
+ }) {
+
+ this.fromDataset = fields.fromDataset;
+ this.data = fields.data || (
+ fields.sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS ? {} : []
+ );
+ this.sourceFormat = fields.sourceFormat || SOURCE_FORMAT_UNKNOWN;
+ this.seriesLayoutBy = fields.seriesLayoutBy || SERIES_LAYOUT_BY_COLUMN;
+ this.dimensionsDefine = fields.dimensionsDefine;
+ this.encodeDefine = fields.encodeDefine && createHashMap(fields.encodeDefine);
+ this.startIndex = fields.startIndex || 0;
+ this.dimensionsDetectCount = fields.dimensionsDetectCount;
+ }
+
+ /**
+ * Wrap original series data for some compatibility cases.
+ */
+ static seriesDataToSource(data: OptionSourceData): Source {
+ return new Source({
+ data: data,
+ sourceFormat: isTypedArray(data)
+ ? SOURCE_FORMAT_TYPED_ARRAY
+ : SOURCE_FORMAT_ORIGINAL,
+ fromDataset: false
+ });
+ };
}
-/**
- * Wrap original series data for some compatibility cases.
- */
-Source.seriesDataToSource = function (data) {
- return new Source({
- data: data,
- sourceFormat: isTypedArray(data)
- ? SOURCE_FORMAT_TYPED_ARRAY
- : SOURCE_FORMAT_ORIGINAL,
- fromDataset: false
- });
-};
-
-enableClassCheck(Source);
+export type SourceConstructor = typeof Source & CheckableConstructor;
+enableClassCheck(Source as SourceConstructor);
export default Source;
\ No newline at end of file
diff --git a/src/data/helper/completeDimensions.ts b/src/data/helper/completeDimensions.ts
index 41fa8eb..24e766a 100644
--- a/src/data/helper/completeDimensions.ts
+++ b/src/data/helper/completeDimensions.ts
@@ -17,6 +17,8 @@
* under the License.
*/
+// @ts-nocheck
+
/**
* @deprecated
* Use `echarts/data/helper/createDimensions` instead.
@@ -26,7 +28,7 @@ import {createHashMap, each, isString, defaults, extend, isObject, clone} from '
import {normalizeToArray} from '../../util/model';
import {guessOrdinal, BE_ORDINAL} from './sourceHelper';
import Source from '../Source';
-import {OTHER_DIMENSIONS} from './dimensionHelper';
+import {VISUAL_DIMENSIONS} from '../../util/types';
import DataDimensionInfo from '../DataDimensionInfo';
/**
@@ -190,7 +192,7 @@ function completeDimensions(sysDims, source, opt) {
});
function applyDim(resultItem, coordDim, coordDimIndex) {
- if (OTHER_DIMENSIONS.get(coordDim) != null) {
+ if (VISUAL_DIMENSIONS.get(coordDim) != null) {
resultItem.otherDims[coordDim] = coordDimIndex;
}
else {
diff --git a/src/data/helper/dataProvider.ts b/src/data/helper/dataProvider.ts
index bac6936..c6a9e04 100644
--- a/src/data/helper/dataProvider.ts
+++ b/src/data/helper/dataProvider.ts
@@ -23,292 +23,289 @@
import {__DEV__} from '../../config';
import {isTypedArray, extend, assert, each, isObject} from 'zrender/src/core/util';
-import {getDataItemValue, isDataItemOption} from '../../util/model';
-import {parseDate} from '../../util/number';
-import Source from '../Source';
+import {getDataItemValue} from '../../util/model';
+import Source, { SourceConstructor } from '../Source';
+import {ArrayLike, Dictionary} from 'zrender/src/core/types';
import {
+ SOURCE_FORMAT_ORIGINAL,
+ SOURCE_FORMAT_OBJECT_ROWS,
+ SOURCE_FORMAT_KEYED_COLUMNS,
SOURCE_FORMAT_TYPED_ARRAY,
SOURCE_FORMAT_ARRAY_ROWS,
- SOURCE_FORMAT_ORIGINAL,
- SOURCE_FORMAT_OBJECT_ROWS
-} from './sourceType';
+ SERIES_LAYOUT_BY_COLUMN,
+ SERIES_LAYOUT_BY_ROW,
+ DimensionName, DimensionIndex, OptionSourceData,
+ DimensionIndexLoose, OptionDataItem, OptionDataPrimitive
+} from '../../util/types';
+import List from '../List';
+
+
+export interface DataProvider {
+ // If data is pure without style configuration
+ pure: boolean;
+ // If data is persistent and will not be released after use.
+ persistent: boolean;
+
+ getSource(): Source;
+ count(): number;
+ getItem(idx: number, out?: OptionDataItem): OptionDataItem;
+ appendData(newData: ArrayLike<OptionDataItem>): void;
+ clean(): void;
+}
/**
* If normal array used, mutable chunk size is supported.
* If typed array used, chunk size must be fixed.
*/
-export function DefaultDataProvider(source, dimSize) {
- if (!Source.isInstance(source)) {
- source = Source.seriesDataToSource(source);
- }
- this._source = source;
+export class DefaultDataProvider implements DataProvider {
- var data = this._data = source.data;
- var sourceFormat = source.sourceFormat;
+ private _source: Source;
- // Typed array. TODO IE10+?
- if (sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) {
- if (__DEV__) {
- if (dimSize == null) {
- throw new Error('Typed array data must specify dimension size');
- }
- }
- this._offset = 0;
- this._dimSize = dimSize;
- this._data = data;
- }
+ private _data: OptionSourceData;
- var methods = providerMethods[
- sourceFormat === SOURCE_FORMAT_ARRAY_ROWS
- ? sourceFormat + '_' + source.seriesLayoutBy
- : sourceFormat
- ];
+ private _offset: number;
- if (__DEV__) {
- assert(methods, 'Invalide sourceFormat: ' + sourceFormat);
- }
+ private _dimSize: number;
- extend(this, methods);
-}
+ pure: boolean;
-var providerProto = DefaultDataProvider.prototype;
-// If data is pure without style configuration
-providerProto.pure = false;
-// If data is persistent and will not be released after use.
-providerProto.persistent = true;
+ persistent: boolean;
-// ???! FIXME legacy data provider do not has method getSource
-providerProto.getSource = function () {
- return this._source;
-};
+ static protoInitialize = (function () {
+ // PENDING: To avoid potential incompat (e.g., prototype
+ // is visited somewhere), still init them on prototype.
+ var proto = DefaultDataProvider.prototype;
+ proto.pure = false;
+ proto.persistent = true;
+ })();
-var providerMethods = {
-
- 'arrayRows_column': {
- pure: true,
- count: function () {
- return Math.max(0, this._data.length - this._source.startIndex);
- },
- getItem: function (idx) {
- return this._data[idx + this._source.startIndex];
- },
- appendData: appendDataSimply
- },
- 'arrayRows_row': {
- pure: true,
- count: function () {
- var row = this._data[0];
- return row ? Math.max(0, row.length - this._source.startIndex) : 0;
- },
- getItem: function (idx) {
- idx += this._source.startIndex;
- var item = [];
- var data = this._data;
- for (var i = 0; i < data.length; i++) {
- var row = data[i];
- item.push(row ? row[idx] : null);
+ constructor(sourceParam: Source | OptionSourceData, dimSize?: number) {
+ // var source: Source;
+ var source: Source = !(Source as SourceConstructor).isInstance(sourceParam)
+ ? Source.seriesDataToSource(sourceParam as OptionSourceData)
+ : sourceParam as Source;
+
+ // declare source is Source;
+ this._source = source;
+
+ var data = this._data = source.data;
+ var sourceFormat = source.sourceFormat;
+
+ // Typed array. TODO IE10+?
+ if (sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) {
+ if (__DEV__) {
+ if (dimSize == null) {
+ throw new Error('Typed array data must specify dimension size');
+ }
}
- return item;
- },
- appendData: function () {
- throw new Error('Do not support appendData when set seriesLayoutBy: "row".');
+ this._offset = 0;
+ this._dimSize = dimSize;
+ this._data = data;
}
- },
- 'objectRows': {
- pure: true,
- count: countSimply,
- getItem: getItemSimply,
- appendData: appendDataSimply
- },
+ var methods = providerMethods[
+ sourceFormat === SOURCE_FORMAT_ARRAY_ROWS
+ ? sourceFormat + '_' + source.seriesLayoutBy
+ : sourceFormat
+ ];
- 'keyedColumns': {
- pure: true,
- count: function () {
- var dimName = this._source.dimensionsDefine[0].name;
- var col = this._data[dimName];
- return col ? col.length : 0;
- },
- getItem: function (idx) {
- var item = [];
- var dims = this._source.dimensionsDefine;
- for (var i = 0; i < dims.length; i++) {
- var col = this._data[dims[i].name];
- item.push(col ? col[idx] : null);
- }
- return item;
- },
- appendData: function (newData) {
- var data = this._data;
- each(newData, function (newCol, key) {
- var oldCol = data[key] || (data[key] = []);
- for (var i = 0; i < (newCol || []).length; i++) {
- oldCol.push(newCol[i]);
- }
- });
+ if (__DEV__) {
+ assert(methods, 'Invalide sourceFormat: ' + sourceFormat);
}
- },
- 'original': {
- count: countSimply,
- getItem: getItemSimply,
- appendData: appendDataSimply
- },
+ extend(this, methods);
+ }
- 'typedArray': {
- persistent: false,
- pure: true,
- count: function () {
- return this._data ? (this._data.length / this._dimSize) : 0;
- },
- getItem: function (idx, out) {
- idx = idx - this._offset;
- out = out || [];
- var offset = this._dimSize * idx;
- for (var i = 0; i < this._dimSize; i++) {
- out[i] = this._data[offset + i];
- }
- return out;
- },
- appendData: function (newData) {
- if (__DEV__) {
- assert(
- isTypedArray(newData),
- 'Added data must be TypedArray if data in initialization is TypedArray'
- );
- }
+ getSource(): Source {
+ return this._source;
+ }
- this._data = newData;
- },
+ count(): number {
+ return 0;
+ }
- // Clean self if data is already used.
- clean: function () {
- // PENDING
- this._offset += this.count();
- this._data = null;
- }
+ getItem(idx: number): OptionDataItem {
+ return;
}
-};
-function countSimply() {
- return this._data.length;
-}
-function getItemSimply(idx) {
- return this._data[idx];
-}
-function appendDataSimply(newData) {
- for (var i = 0; i < newData.length; i++) {
- this._data.push(newData[i]);
+ appendData(newData: OptionSourceData): void {
}
-}
+ clean(): void {
+ }
+ static internalField = (function () {
+
+ providerMethods = {
+
+ [SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_COLUMN]: {
+ pure: true,
+ count: function (this: DefaultDataProvider): number {
+ return Math.max(0, (this._data as OptionDataItem[][]).length - this._source.startIndex);
+ },
+ getItem: function (this: DefaultDataProvider, idx: number): OptionDataPrimitive[] {
+ return (this._data as OptionDataPrimitive[][])[idx + this._source.startIndex];
+ },
+ appendData: appendDataSimply
+ },
+
+ [SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_ROW]: {
+ pure: true,
+ count: function (this: DefaultDataProvider): number {
+ var row = (this._data as OptionDataPrimitive[][])[0];
+ return row ? Math.max(0, row.length - this._source.startIndex) : 0;
+ },
+ getItem: function (this: DefaultDataProvider, idx: number): OptionDataPrimitive[] {
+ idx += this._source.startIndex;
+ var item = [];
+ var data = this._data as OptionDataPrimitive[][];
+ for (var i = 0; i < data.length; i++) {
+ var row = data[i];
+ item.push(row ? row[idx] : null);
+ }
+ return item;
+ },
+ appendData: function () {
+ throw new Error('Do not support appendData when set seriesLayoutBy: "row".');
+ }
+ },
+
+ [SOURCE_FORMAT_OBJECT_ROWS]: {
+ pure: true,
+ count: countSimply,
+ getItem: getItemSimply,
+ appendData: appendDataSimply
+ },
+
+ [SOURCE_FORMAT_KEYED_COLUMNS]: {
+ pure: true,
+ count: function (this: DefaultDataProvider): number {
+ var dimName = this._source.dimensionsDefine[0].name;
+ var col = (this._data as Dictionary<OptionDataPrimitive[]>)[dimName];
+ return col ? col.length : 0;
+ },
+ getItem: function (this: DefaultDataProvider, idx: number): OptionDataPrimitive[] {
+ var item = [];
+ var dims = this._source.dimensionsDefine;
+ for (var i = 0; i < dims.length; i++) {
+ var col = (this._data as Dictionary<OptionDataPrimitive[]>)[dims[i].name];
+ item.push(col ? col[idx] : null);
+ }
+ return item;
+ },
+ appendData: function (this: DefaultDataProvider, newData: OptionSourceData) {
+ var data = this._data as Dictionary<OptionDataPrimitive[]>;
+ each(newData, function (newCol, key) {
+ var oldCol = data[key] || (data[key] = []);
+ for (var i = 0; i < (newCol || []).length; i++) {
+ oldCol.push(newCol[i]);
+ }
+ });
+ }
+ },
+
+ [SOURCE_FORMAT_ORIGINAL]: {
+ count: countSimply,
+ getItem: getItemSimply,
+ appendData: appendDataSimply
+ },
+
+ [SOURCE_FORMAT_TYPED_ARRAY]: {
+ persistent: false,
+ pure: true,
+ count: function (this: DefaultDataProvider): number {
+ return this._data ? ((this._data as ArrayLike<number>).length / this._dimSize) : 0;
+ },
+ getItem: function (this: DefaultDataProvider, idx: number, out: ArrayLike<number>): ArrayLike<number> {
+ idx = idx - this._offset;
+ out = out || [];
+ var offset = this._dimSize * idx;
+ for (var i = 0; i < this._dimSize; i++) {
+ out[i] = (this._data as ArrayLike<number>)[offset + i];
+ }
+ return out;
+ },
+ appendData: function (this: DefaultDataProvider, newData: ArrayLike<number>): void {
+ if (__DEV__) {
+ assert(
+ isTypedArray(newData),
+ 'Added data must be TypedArray if data in initialization is TypedArray'
+ );
+ }
+
+ this._data = newData;
+ },
+
+ // Clean self if data is already used.
+ clean: function (this: DefaultDataProvider): void {
+ // PENDING
+ this._offset += this.count();
+ this._data = null;
+ }
+ }
+ };
+
+ function countSimply(this: DefaultDataProvider): number {
+ return (this._data as []).length;
+ }
+ function getItemSimply(this: DefaultDataProvider, idx: number): OptionDataItem {
+ return (this._data as [])[idx];
+ }
+ function appendDataSimply(this: DefaultDataProvider, newData: ArrayLike<OptionDataItem>): void {
+ for (var i = 0; i < newData.length; i++) {
+ (this._data as any[]).push(newData[i]);
+ }
+ }
-var rawValueGetters = {
+ })()
+}
- arrayRows: getRawValueSimply,
+var providerMethods: Dictionary<any>;
- objectRows: function (dataItem, dataIndex, dimIndex, dimName) {
+// TODO
+// merge it to dataProvider?
+type RawValueGetter = (
+ dataItem: OptionDataItem,
+ dataIndex: number,
+ dimIndex: DimensionIndex,
+ dimName: DimensionName
+ // If dimIndex not provided, return OptionDataItem.
+ // If dimIndex provided, return OptionDataPrimitive.
+) => OptionDataPrimitive | OptionDataItem;
+
+var rawValueGetters: {[sourceFormat: string]: RawValueGetter} = {
+
+ [SOURCE_FORMAT_ARRAY_ROWS]: getRawValueSimply,
+
+ [SOURCE_FORMAT_OBJECT_ROWS]: function (
+ dataItem: Dictionary<OptionDataPrimitive>, dataIndex: number, dimIndex: number, dimName: string
+ ): OptionDataPrimitive | Dictionary<OptionDataPrimitive> {
return dimIndex != null ? dataItem[dimName] : dataItem;
},
- keyedColumns: getRawValueSimply,
+ [SOURCE_FORMAT_KEYED_COLUMNS]: getRawValueSimply,
- original: function (dataItem, dataIndex, dimIndex, dimName) {
- // FIXME
- // In some case (markpoint in geo (geo-map.html)), dataItem
- // is {coord: [...]}
+ [SOURCE_FORMAT_ORIGINAL]: function (
+ dataItem: OptionDataItem, dataIndex: number, dimIndex: number, dimName: string
+ ): OptionDataPrimitive | OptionDataItem {
+ // FIXME: In some case (markpoint in geo (geo-map.html)),
+ // dataItem is {coord: [...]}
var value = getDataItemValue(dataItem);
return (dimIndex == null || !(value instanceof Array))
? value
: value[dimIndex];
},
- typedArray: getRawValueSimply
+ [SOURCE_FORMAT_TYPED_ARRAY]: getRawValueSimply
};
-function getRawValueSimply(dataItem, dataIndex, dimIndex, dimName) {
+function getRawValueSimply(
+ dataItem: ArrayLike<OptionDataPrimitive>, dataIndex: number, dimIndex: number, dimName: string
+): OptionDataPrimitive | ArrayLike<OptionDataPrimitive> {
return dimIndex != null ? dataItem[dimIndex] : dataItem;
}
-
-export var defaultDimValueGetters = {
-
- arrayRows: getDimValueSimply,
-
- objectRows: function (dataItem, dimName, dataIndex, dimIndex) {
- return converDataValue(dataItem[dimName], this._dimensionInfos[dimName]);
- },
-
- keyedColumns: getDimValueSimply,
-
- original: function (dataItem, dimName, dataIndex, dimIndex) {
- // Performance sensitive, do not use modelUtil.getDataItemValue.
- // If dataItem is an plain object with no value field, the var `value`
- // will be assigned with the object, but it will be tread correctly
- // in the `convertDataValue`.
- var value = dataItem && (dataItem.value == null ? dataItem : dataItem.value);
-
- // If any dataItem is like { value: 10 }
- if (!this._rawData.pure && isDataItemOption(dataItem)) {
- this.hasItemOption = true;
- }
- return converDataValue(
- (value instanceof Array)
- ? value[dimIndex]
- // If value is a single number or something else not array.
- : value,
- this._dimensionInfos[dimName]
- );
- },
-
- typedArray: function (dataItem, dimName, dataIndex, dimIndex) {
- return dataItem[dimIndex];
- }
-
-};
-
-function getDimValueSimply(dataItem, dimName, dataIndex, dimIndex) {
- return converDataValue(dataItem[dimIndex], this._dimensionInfos[dimName]);
-}
-
-/**
- * This helper method convert value in data.
- * @param {string|number|Date} value
- * @param {Object|string} [dimInfo] If string (like 'x'), dimType defaults 'number'.
- * If "dimInfo.ordinalParseAndSave", ordinal value can be parsed.
- */
-function converDataValue(value, dimInfo) {
- // Performance sensitive.
- var dimType = dimInfo && dimInfo.type;
- if (dimType === 'ordinal') {
- // If given value is a category string
- var ordinalMeta = dimInfo && dimInfo.ordinalMeta;
- return ordinalMeta
- ? ordinalMeta.parseAndCollect(value)
- : value;
- }
-
- if (dimType === 'time'
- // spead up when using timestamp
- && typeof value !== 'number'
- && value != null
- && value !== '-'
- ) {
- value = +parseDate(value);
- }
-
- // dimType defaults 'number'.
- // If dimType is not ordinal and value is null or undefined or NaN or '-',
- // parse to NaN.
- return (value == null || value === '')
- ? NaN
- // If string (like '-'), using '+' parse to NaN
- // If object, also parse to NaN
- : +value;
-}
-
// ??? FIXME can these logic be more neat: getRawValue, getRawDataItem,
// Consider persistent.
// Caution: why use raw value to display on label or tooltip?
@@ -316,13 +313,9 @@ function converDataValue(value, dimInfo) {
// how to format is expected. More over, if stack is used, calculated
// value may be 0.91000000001, which have brings trouble to display.
// TODO: consider how to treat null/undefined/NaN when display?
-/**
- * @param {module:echarts/data/List} data
- * @param {number} dataIndex
- * @param {string|number} [dim] dimName or dimIndex
- * @return {Array.<number>|string|number} can be null/undefined.
- */
-export function retrieveRawValue(data, dataIndex, dim) {
+export function retrieveRawValue(
+ data: List, dataIndex: number, dim?: DimensionName | DimensionIndexLoose
+): any {
if (!data) {
return;
}
@@ -352,14 +345,14 @@ export function retrieveRawValue(data, dataIndex, dim) {
* data: [{name: 'xx', value: 5, selected: true}, ...]
* where only sourceFormat is 'original' and 'objectRows' supported.
*
- * ??? TODO
+ * // TODO
* Supported detail options in data item when using 'arrayRows'.
*
- * @param {module:echarts/data/List} data
- * @param {number} dataIndex
- * @param {string} attr like 'selected'
+ * @param data
+ * @param dataIndex
+ * @param attr like 'selected'
*/
-export function retrieveRawAttr(data, dataIndex, attr) {
+export function retrieveRawAttr(data: List, dataIndex: number, attr: string): any {
if (!data) {
return;
}
@@ -377,6 +370,6 @@ export function retrieveRawAttr(data, dataIndex, attr) {
dataItem = null;
}
if (dataItem) {
- return dataItem[attr];
+ return (dataItem as Dictionary<OptionDataPrimitive>)[attr];
}
}
diff --git a/src/data/helper/dimensionHelper.ts b/src/data/helper/dimensionHelper.ts
index b0e616c..1db36ed 100644
--- a/src/data/helper/dimensionHelper.ts
+++ b/src/data/helper/dimensionHelper.ts
@@ -17,16 +17,44 @@
* under the License.
*/
+// @ts-nocheck
+
import {each, createHashMap, assert} from 'zrender/src/core/util';
import { __DEV__ } from '../../config';
+import List from '../List';
+import {
+ DimensionName, DimensionIndex, VISUAL_DIMENSIONS, DimensionType
+} from '../../util/types';
+
+export type DimensionSummaryEncode = {
+ defaultedLabel: DimensionName[],
+ defaultedTooltip: DimensionName[],
+ [coordOrVisualDimName: string]:
+ // index: coordDimIndex, value: dataDimName
+ DimensionName[]
+};
+export type DimensionUserOuputEncode = {
+ [coordOrVisualDimName: string]:
+ // index: coordDimIndex, value: dataDimIndex
+ DimensionIndex[]
+};
+export type DimensionUserOuput = {
+ // The same as `data.dimensions`
+ dimensionNames: DimensionName[]
+ encode: DimensionUserOuputEncode
+};
+export type DimensionSummary = {
+ encode: DimensionSummaryEncode,
+ // Those details that can be expose to users are put int `userOutput`.
+ userOutput: DimensionUserOuput,
+ // All of the data dim names that mapped by coordDim.
+ dataDimsOnCoord: DimensionName[],
+ encodeFirstDimNotExtra: {[coordDim: string]: DimensionName},
+}
-export var OTHER_DIMENSIONS = createHashMap([
- 'tooltip', 'label', 'itemName', 'itemId', 'seriesName'
-]);
-
-export function summarizeDimensions(data) {
- var summary = {};
- var encode = summary.encode = {};
+export function summarizeDimensions(data: List): DimensionSummary {
+ var summary: DimensionSummary = {} as DimensionSummary;
+ var encode = summary.encode = {} as DimensionSummaryEncode;
var notExtraCoordDimMap = createHashMap();
var defaultedLabel = [];
var defaultedTooltip = [];
@@ -43,7 +71,7 @@ export function summarizeDimensions(data) {
var coordDim = dimItem.coordDim;
if (coordDim) {
if (__DEV__) {
- assert(OTHER_DIMENSIONS.get(coordDim) == null);
+ assert(VISUAL_DIMENSIONS.get(coordDim) == null);
}
var coordDimIndex = dimItem.coordDimIndex;
@@ -69,7 +97,7 @@ export function summarizeDimensions(data) {
}
}
- OTHER_DIMENSIONS.each(function (v, otherDim) {
+ VISUAL_DIMENSIONS.each(function (v, otherDim) {
var encodeArr = getOrCreateEncodeArr(encode, otherDim);
var dimIndex = dimItem.otherDims[otherDim];
@@ -117,14 +145,17 @@ export function summarizeDimensions(data) {
return summary;
}
-function getOrCreateEncodeArr(encode, dim) {
+function getOrCreateEncodeArr(
+ encode: DimensionSummaryEncode, dim: DimensionName
+): DimensionName[] {
if (!encode.hasOwnProperty(dim)) {
encode[dim] = [];
}
return encode[dim];
}
-export function getDimensionTypeByAxis(axisType) {
+// FIXME:TS should be type `AxisType`
+export function getDimensionTypeByAxis(axisType: string) {
return axisType === 'category'
? 'ordinal'
: axisType === 'time'
@@ -132,7 +163,7 @@ export function getDimensionTypeByAxis(axisType) {
: 'float';
}
-function mayLabelDimType(dimType) {
+function mayLabelDimType(dimType: DimensionType): boolean {
// In most cases, ordinal and time do not suitable for label.
// Ordinal info can be displayed on axis. Time is too long.
return !(dimType === 'ordinal' || dimType === 'time');
diff --git a/src/data/helper/sourceHelper.ts b/src/data/helper/sourceHelper.ts
index b58b01f..db59b9e 100644
--- a/src/data/helper/sourceHelper.ts
+++ b/src/data/helper/sourceHelper.ts
@@ -17,6 +17,8 @@
* under the License.
*/
+// @ts-nocheck
+
import {__DEV__} from '../../config';
import {makeInner, getDataItemValue} from '../../util/model';
import {
@@ -37,11 +39,11 @@ import {
SOURCE_FORMAT_ORIGINAL,
SOURCE_FORMAT_ARRAY_ROWS,
SOURCE_FORMAT_OBJECT_ROWS,
+ SERIES_LAYOUT_BY_ROW,
SOURCE_FORMAT_KEYED_COLUMNS,
- SOURCE_FORMAT_UNKNOWN,
SOURCE_FORMAT_TYPED_ARRAY,
- SERIES_LAYOUT_BY_ROW
-} from './sourceType';
+ SOURCE_FORMAT_UNKNOWN
+} from '../../util/types';
// The result of `guessOrdinal`.
export var BE_ORDINAL = {
diff --git a/src/data/helper/sourceType.ts b/src/data/helper/sourceType.ts
deleted file mode 100644
index 537f671..0000000
--- a/src/data/helper/sourceType.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
-* 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.
-*/
-
-// Avoid typo.
-export var SOURCE_FORMAT_ORIGINAL = 'original';
-export var SOURCE_FORMAT_ARRAY_ROWS = 'arrayRows';
-export var SOURCE_FORMAT_OBJECT_ROWS = 'objectRows';
-export var SOURCE_FORMAT_KEYED_COLUMNS = 'keyedColumns';
-export var SOURCE_FORMAT_UNKNOWN = 'unknown';
-// ??? CHANGE A NAME
-export var SOURCE_FORMAT_TYPED_ARRAY = 'typedArray';
-
-export var SERIES_LAYOUT_BY_COLUMN = 'column';
-export var SERIES_LAYOUT_BY_ROW = 'row';
diff --git a/src/echarts.ts b/src/echarts.ts
index 36a29cf..32fc419 100644
--- a/src/echarts.ts
+++ b/src/echarts.ts
@@ -16,23 +16,27 @@
* specific language governing permissions and limitations
* under the License.
*/
+
import {__DEV__} from './config';
import * as zrender from 'zrender/src/zrender';
import * as zrUtil from 'zrender/src/core/util';
import * as colorTool from 'zrender/src/tool/color';
import env from 'zrender/src/core/env';
import timsort from 'zrender/src/core/timsort';
-import Eventful from 'zrender/src/mixin/Eventful';
-import GlobalModel from './model/Global';
+import Eventful from 'zrender/src/core/Eventful';
+import Element, { ElementEvent } from 'zrender/src/Element';
+import CanvasPainter from 'zrender/src/canvas/Painter';
+import SVGPainter from 'zrender/src/svg/Painter';
+import GlobalModel, {QueryConditionKindA} 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 from './view/Component';
-import ChartView from './view/Chart';
+import ComponentModel, { ComponentModelConstructor } from './model/Component';
+import SeriesModel, { SeriesModelConstructor } from './model/Series';
+import ComponentView, {ComponentViewConstructor} from './view/Component';
+import ChartView, {ChartViewConstructor} from './view/Chart';
import * as graphic from './util/graphic';
import * as modelUtil from './util/model';
import {throttle} from './util/throttle';
@@ -43,13 +47,33 @@ import Scheduler from './stream/Scheduler';
import lightTheme from './theme/light';
import darkTheme from './theme/dark';
import './component/dataset';
-import mapDataStorage from './coord/geo/mapDataStorage';
+import mapDataStorage, { GeoMapDefinition, GeoMapGeoJSONSource, GeoSpecialAreas } from './coord/geo/mapDataStorage';
+import {CoordinateSystem, CoordinateSystemCreator} from './coord/CoordinateSystem';
+import { parseClassType } from './util/clazz';
+import {ECEventProcessor} from './util/ECEventProcessor';
+import {
+ Payload, PayloadItem, ECElement, RendererType, ECEvent,
+ ActionHandler, ActionInfo, OptionPreprocessor, PostUpdater,
+ LoadingEffect, LoadingEffectCreator, StageHandler,
+ StageHandlerOverallReset, VisualType, StageHandlerInput,
+ ViewRootGroup, DimensionDefinitionLoose, ECEventData, ThemeOption,
+ ECOption,
+ ECUnitOption,
+ ZRColor,
+ ComponentMainType,
+ ComponentSubType
+} from './util/types';
+import Displayable from 'zrender/src/graphic/Displayable';
+import IncrementalDisplayable from 'zrender/src/graphic/IncrementalDisplayable';
+
+
+declare var global: any;
+type ModelFinder = modelUtil.ModelFinder;
var assert = zrUtil.assert;
var each = zrUtil.each;
var isFunction = zrUtil.isFunction;
var isObject = zrUtil.isObject;
-var parseClassType = ComponentModel.parseClassType;
export var version = '4.6.0';
@@ -96,1950 +120,1792 @@ export var PRIORITY = {
// 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]).
-var IN_MAIN_PROCESS = '__flagInMainProcess';
-var OPTION_UPDATED = '__optionUpdated';
+var IN_MAIN_PROCESS = '__flagInMainProcess' as const;
+var OPTION_UPDATED = '__optionUpdated' as const;
var ACTION_REG = /^[a-zA-Z0-9_]+$/;
+var CONNECT_STATUS_KEY = '__connectUpdateStatus' as const;
+var CONNECT_STATUS_PENDING = 0 as const;
+var CONNECT_STATUS_UPDATING = 1 as const;
+var CONNECT_STATUS_UPDATED = 2 as const;
+type ConnectStatus =
+ typeof CONNECT_STATUS_PENDING
+ | typeof CONNECT_STATUS_UPDATING
+ | typeof CONNECT_STATUS_UPDATED;
+
+type SetOptionOpts = {
+ notMerge?: boolean,
+ lazyUpdate?: boolean,
+ silent?: boolean
+};
-function createRegisterEventWithLowercaseName(method, ignoreDisposed) {
- return function (eventName, handler, context) {
- if (!ignoreDisposed && this._disposed) {
+type EventMethodName = 'on' | 'off';
+function createRegisterEventWithLowercaseECharts(method: EventMethodName) {
+ return function (this: ECharts, ...args: any): ECharts {
+ if (this.isDisposed()) {
disposedWarning(this.id);
return;
}
-
- // Event name is all lowercase
- eventName = eventName && eventName.toLowerCase();
- Eventful.prototype[method].call(this, eventName, handler, context);
+ return toLowercaseNameAndCallEventful<ECharts>(this, method, args);
};
}
-
-/**
- * @module echarts~MessageCenter
- */
-function MessageCenter() {
- Eventful.call(this);
+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;
}
-MessageCenter.prototype.on = createRegisterEventWithLowercaseName('on', true);
-MessageCenter.prototype.off = createRegisterEventWithLowercaseName('off', true);
-MessageCenter.prototype.one = createRegisterEventWithLowercaseName('one', true);
-zrUtil.mixin(MessageCenter, Eventful);
-/**
- * @module echarts~ECharts
- */
-function ECharts(dom, theme, opts) {
- opts = opts || {};
- // Get theme by name
- if (typeof theme === 'string') {
- theme = themeStorage[theme];
- }
+class MessageCenter extends Eventful<MessageCenter> {}
+var messageCenterProto = MessageCenter.prototype;
+messageCenterProto.on = createRegisterEventWithLowercaseMessageCenter('on');
+messageCenterProto.off = createRegisterEventWithLowercaseMessageCenter('off');
+// messageCenterProto.one = createRegisterEventWithLowercaseMessageCenter('one');
+
+
+class ECharts {
/**
- * @type {string}
+ * @readonly
*/
- this.id;
+ id: string;
/**
* Group id
- * @type {string}
+ * @readonly
*/
- this.group;
+ group: string;
- /**
- * @type {HTMLElement}
- * @private
- */
- this._dom = dom;
+ private _zr: zrender.ZRenderType;
- var defaultRenderer = 'canvas';
- if (__DEV__) {
- defaultRenderer = (
- typeof window === 'undefined' ? global : window
- ).__ECHARTS__DEFAULT__RENDERER__ || defaultRenderer;
- }
+ private _dom: HTMLElement;
- /**
- * @type {module:zrender/ZRender}
- * @private
- */
- var zr = this._zr = zrender.init(dom, {
- renderer: opts.renderer || defaultRenderer,
- devicePixelRatio: opts.devicePixelRatio,
- width: opts.width,
- height: opts.height
- });
+ private _model: GlobalModel;
- /**
- * Expect 60 fps.
- * @type {Function}
- * @private
- */
- this._throttledZrFlush = throttle(zrUtil.bind(zr.flush, zr), 17);
+ private _throttledZrFlush: zrender.ZRenderType extends {flush: infer R} ? R : never;
- var theme = zrUtil.clone(theme);
- theme && backwardCompat(theme, true);
- /**
- * @type {Object}
- * @private
- */
- this._theme = theme;
+ private _theme: ThemeOption;
- /**
- * @type {Array.<module:echarts/view/Chart>}
- * @private
- */
- this._chartsViews = [];
+ private _chartsViews: ChartView[] = [];
- /**
- * @type {Object.<string, module:echarts/view/Chart>}
- * @private
- */
- this._chartsMap = {};
+ private _chartsMap: {[viewId: string]: ChartView} = {};
- /**
- * @type {Array.<module:echarts/view/Component>}
- * @private
- */
- this._componentsViews = [];
+ private _componentsViews: ComponentView[] = [];
- /**
- * @type {Object.<string, module:echarts/view/Component>}
- * @private
- */
- this._componentsMap = {};
+ private _componentsMap: {[viewId: string]: ComponentView} = {};
- /**
- * @type {module:echarts/CoordinateSystem}
- * @private
- */
- this._coordSysMgr = new CoordinateSystemManager();
+ private _coordSysMgr: CoordinateSystemManager;
- /**
- * @type {module:echarts/ExtensionAPI}
- * @private
- */
- var api = this._api = createExtensionAPI(this);
+ private _api: ExtensionAPI;
- // Sort on demand
- function prioritySortFunc(a, b) {
- return a.__prio - b.__prio;
- }
- timsort(visualFuncs, prioritySortFunc);
- timsort(dataProcessorFuncs, prioritySortFunc);
+ private _scheduler: Scheduler;
- /**
- * @type {module:echarts/stream/Scheduler}
- */
- this._scheduler = new Scheduler(this, api, dataProcessorFuncs, visualFuncs);
+ private _messageCenter: MessageCenter;
- Eventful.call(this, this._ecEventProcessor = new EventProcessor());
+ // Can't dispatch action during rendering procedure
+ private _pendingActions: Payload[] = [];
- /**
- * @type {module:echarts~MessageCenter}
- * @private
- */
- this._messageCenter = new MessageCenter();
+ private _ecEventProcessor: ECEventProcessor;
- // Init mouse events
- this._initEvents();
+ private _disposed: boolean;
- // In case some people write `window.onresize = chart.resize`
- this.resize = zrUtil.bind(this.resize, this);
+ private _loadingFX: LoadingEffect;
- // Can't dispatch action during rendering procedure
- this._pendingActions = [];
+ private [OPTION_UPDATED]: boolean | {silent: boolean};
+ private [IN_MAIN_PROCESS]: boolean;
+ private [CONNECT_STATUS_KEY]: ConnectStatus;
- zr.animation.on('frame', this._onframe, this);
- bindRenderedEvent(zr, this);
+ constructor(
+ dom: HTMLElement,
+ // Theme name or themeOption.
+ theme?: string | ThemeOption,
+ opts?: {
+ renderer?: RendererType,
+ devicePixelRatio?: number,
+ width?: number,
+ height?: number
+ }
+ ) {
+ opts = opts || {};
- // ECharts instance can be used as value.
- zrUtil.setAsPrimitive(this);
-}
+ // Get theme by name
+ if (typeof theme === 'string') {
+ theme = themeStorage[theme] as object;
+ }
-var echartsProto = ECharts.prototype;
+ this._dom = dom;
-echartsProto._onframe = function () {
- if (this._disposed) {
- return;
- }
+ var defaultRenderer = 'canvas';
+ if (__DEV__) {
+ defaultRenderer = ((
+ typeof window === 'undefined' ? global : window
+ ) as any).__ECHARTS__DEFAULT__RENDERER__ || defaultRenderer;
+ }
- var scheduler = this._scheduler;
+ var zr = this._zr = zrender.init(dom, {
+ renderer: opts.renderer || defaultRenderer,
+ devicePixelRatio: opts.devicePixelRatio,
+ width: opts.width,
+ height: opts.height
+ });
- // Lazy update
- if (this[OPTION_UPDATED]) {
- var silent = this[OPTION_UPDATED].silent;
+ // Expect 60 fps.
+ this._throttledZrFlush = throttle(zrUtil.bind(zr.flush, zr), 17);
- this[IN_MAIN_PROCESS] = true;
+ theme = zrUtil.clone(theme);
+ theme && backwardCompat(theme, true);
- prepare(this);
- updateMethods.update.call(this);
+ this._theme = theme;
- this[IN_MAIN_PROCESS] = false;
+ this._coordSysMgr = new CoordinateSystemManager();
- this[OPTION_UPDATED] = false;
+ var api = this._api = createExtensionAPI(this);
- flushPendingActions.call(this, silent);
+ // Sort on demand
+ function prioritySortFunc(a: StageHandler, b: StageHandler): number {
+ return a.__prio - b.__prio;
+ }
+ timsort(visualFuncs, prioritySortFunc);
+ timsort(dataProcessorFuncs, prioritySortFunc);
- triggerUpdatedEvent.call(this, silent);
- }
- // Avoid do both lazy update and progress in one frame.
- else if (scheduler.unfinished) {
- // Stream progress.
- var remainTime = TEST_FRAME_REMAIN_TIME;
- var ecModel = this._model;
- var api = this._api;
- scheduler.unfinished = false;
- do {
- var startTime = +new Date();
+ this._scheduler = new Scheduler(this, api, dataProcessorFuncs, visualFuncs);
- scheduler.performSeriesTasks(ecModel);
+ this._ecEventProcessor = new ECEventProcessor();
+ Eventful.call(this, this._ecEventProcessor);
- // Currently dataProcessorFuncs do not check threshold.
- scheduler.performDataProcessorTasks(ecModel);
+ this._messageCenter = new MessageCenter();
- updateStreamModes(this, ecModel);
+ // Init mouse events
+ this._initEvents();
- // 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 determin in the first frame (the
- // frame is executed immedietely after task reset.
- // this._coordSysMgr.update(ecModel, api);
+ // In case some people write `window.onresize = chart.resize`
+ this.resize = zrUtil.bind(this.resize, this);
- // console.log('--- ec frame visual ---', remainTime);
- scheduler.performVisualTasks(ecModel);
+ zr.animation.on('frame', this._onframe, this);
- renderSeries(this, this._model, api, 'remain');
+ bindRenderedEvent(zr, this);
- remainTime -= (+new Date() - startTime);
- }
- while (remainTime > 0 && scheduler.unfinished);
+ // ECharts instance can be used as value.
+ zrUtil.setAsPrimitive(this);
+ }
- // Call flush explicitly for trigger finished event.
- if (!scheduler.unfinished) {
- this._zr.flush();
+ private _onframe(): void {
+ if (this._disposed) {
+ return;
}
- // Else, zr flushing be ensue within the same frame,
- // because zr flushing is after onframe event.
- }
-};
-/**
- * @return {HTMLElement}
- */
-echartsProto.getDom = function () {
- return this._dom;
-};
+ var scheduler = this._scheduler;
-/**
- * @return {module:zrender~ZRender}
- */
-echartsProto.getZr = function () {
- return this._zr;
-};
+ // Lazy update
+ if (this[OPTION_UPDATED]) {
+ var silent = (this[OPTION_UPDATED] as any).silent;
-/**
- * Usage:
- * chart.setOption(option, notMerge, lazyUpdate);
- * chart.setOption(option, {
- * notMerge: ...,
- * lazyUpdate: ...,
- * silent: ...
- * });
- *
- * @param {Object} option
- * @param {Object|boolean} [opts] opts or notMerge.
- * @param {boolean} [opts.notMerge=false]
- * @param {boolean} [opts.lazyUpdate=false] Useful when setOption frequently.
- */
-echartsProto.setOption = function (option, notMerge, lazyUpdate) {
- if (__DEV__) {
- assert(!this[IN_MAIN_PROCESS], '`setOption` should not be called during main process.');
- }
- if (this._disposed) {
- disposedWarning(this.id);
- return;
- }
+ this[IN_MAIN_PROCESS] = true;
- var silent;
- if (isObject(notMerge)) {
- lazyUpdate = notMerge.lazyUpdate;
- silent = notMerge.silent;
- notMerge = notMerge.notMerge;
- }
+ prepare(this);
+ updateMethods.update.call(this);
- this[IN_MAIN_PROCESS] = true;
+ this[IN_MAIN_PROCESS] = false;
- if (!this._model || notMerge) {
- var optionManager = new OptionManager(this._api);
- var theme = this._theme;
- var ecModel = this._model = new GlobalModel();
- ecModel.scheduler = this._scheduler;
- ecModel.init(null, null, theme, optionManager);
- }
+ this[OPTION_UPDATED] = false;
- this._model.setOption(option, optionPreprocessorFuncs);
+ flushPendingActions.call(this, silent);
- if (lazyUpdate) {
- this[OPTION_UPDATED] = {silent: silent};
- this[IN_MAIN_PROCESS] = false;
- }
- else {
- prepare(this);
+ triggerUpdatedEvent.call(this, silent);
+ }
+ // Avoid do both lazy update and progress in one frame.
+ else if (scheduler.unfinished) {
+ // Stream progress.
+ var remainTime = TEST_FRAME_REMAIN_TIME;
+ var ecModel = this._model;
+ var api = this._api;
+ scheduler.unfinished = +false;
+ do {
+ var startTime = +new Date();
- updateMethods.update.call(this);
+ scheduler.performSeriesTasks(ecModel);
- // Ensure zr refresh sychronously, and then pixel in canvas can be
- // fetched after `setOption`.
- this._zr.flush();
+ // Currently dataProcessorFuncs do not check threshold.
+ scheduler.performDataProcessorTasks(ecModel);
- this[OPTION_UPDATED] = false;
- this[IN_MAIN_PROCESS] = false;
+ updateStreamModes(this, ecModel);
- flushPendingActions.call(this, silent);
- triggerUpdatedEvent.call(this, silent);
+ // 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 determin in the first frame (the
+ // frame is executed immedietely 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.
+ }
}
-};
-/**
- * @DEPRECATED
- */
-echartsProto.setTheme = function () {
- console.error('ECharts#setTheme() is DEPRECATED in ECharts 3.0');
-};
+ getDom(): HTMLElement {
+ return this._dom;
+ }
-/**
- * @return {module:echarts/model/Global}
- */
-echartsProto.getModel = function () {
- return this._model;
-};
+ getZr(): zrender.ZRenderType {
+ return this._zr;
+ }
-/**
- * @return {Object}
- */
-echartsProto.getOption = function () {
- return this._model && this._model.getOption();
-};
+ /**
+ * 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`.
+ */
+ setOption(option: ECOption, notMerge?: boolean, lazyUpdate?: boolean): void;
+ setOption(option: ECOption, opts?: SetOptionOpts): void;
+ setOption(option: ECOption, notMerge?: boolean | SetOptionOpts, lazyUpdate?: boolean): void {
+ if (__DEV__) {
+ assert(!this[IN_MAIN_PROCESS], '`setOption` should not be called during main process.');
+ }
+ if (this._disposed) {
+ disposedWarning(this.id);
+ return;
+ }
-/**
- * @return {number}
- */
-echartsProto.getWidth = function () {
- return this._zr.getWidth();
-};
+ var silent;
+ if (isObject<SetOptionOpts>(notMerge)) {
+ lazyUpdate = notMerge.lazyUpdate;
+ silent = notMerge.silent;
+ notMerge = notMerge.notMerge;
+ }
-/**
- * @return {number}
- */
-echartsProto.getHeight = function () {
- return this._zr.getHeight();
-};
+ this[IN_MAIN_PROCESS] = true;
-/**
- * @return {number}
- */
-echartsProto.getDevicePixelRatio = function () {
- return this._zr.painter.dpr || window.devicePixelRatio || 1;
-};
+ if (!this._model || notMerge) {
+ var optionManager = new OptionManager(this._api);
+ var theme = this._theme;
+ var ecModel = this._model = new GlobalModel();
+ ecModel.scheduler = this._scheduler;
+ ecModel.init(null, null, null, theme, optionManager);
+ }
-/**
- * Get canvas which has all thing rendered
- * @param {Object} opts
- * @param {string} [opts.backgroundColor]
- * @return {string}
- */
-echartsProto.getRenderedCanvas = function (opts) {
- if (!env.canvasSupported) {
- return;
- }
- opts = opts || {};
- opts.pixelRatio = opts.pixelRatio || 1;
- opts.backgroundColor = opts.backgroundColor
- || this._model.get('backgroundColor');
- var zr = this._zr;
- // var list = zr.storage.getDisplayList();
- // Stop animations
- // Never works before in init animation, so remove it.
- // zrUtil.each(list, function (el) {
- // el.stopAnimation(true);
- // });
- return zr.painter.getRenderedCanvas(opts);
-};
+ this._model.setOption(option, optionPreprocessorFuncs);
-/**
- * Get svg data url
- * @return {string}
- */
-echartsProto.getSvgDataUrl = function () {
- if (!env.svgSupported) {
- return;
- }
+ if (lazyUpdate) {
+ this[OPTION_UPDATED] = {silent: silent};
+ this[IN_MAIN_PROCESS] = false;
+ }
+ else {
+ prepare(this);
- var zr = this._zr;
- var list = zr.storage.getDisplayList();
- // Stop animations
- zrUtil.each(list, function (el) {
- el.stopAnimation(true);
- });
+ updateMethods.update.call(this);
- return zr.painter.pathToDataUrl();
-};
+ // Ensure zr refresh sychronously, and then pixel in canvas can be
+ // fetched after `setOption`.
+ this._zr.flush();
-/**
- * @return {string}
- * @param {Object} opts
- * @param {string} [opts.type='png']
- * @param {string} [opts.pixelRatio=1]
- * @param {string} [opts.backgroundColor]
- * @param {string} [opts.excludeComponents]
- */
-echartsProto.getDataURL = function (opts) {
- if (this._disposed) {
- disposedWarning(this.id);
- return;
+ this[OPTION_UPDATED] = false;
+ this[IN_MAIN_PROCESS] = false;
+
+ flushPendingActions.call(this, silent);
+ triggerUpdatedEvent.call(this, silent);
+ }
}
- opts = opts || {};
- var excludeComponents = opts.excludeComponents;
- var ecModel = this._model;
- var excludesComponentViews = [];
- var self = this;
-
- each(excludeComponents, function (componentType) {
- ecModel.eachComponent({
- mainType: componentType
- }, function (component) {
- var view = self._componentsMap[component.__viewId];
- if (!view.group.ignore) {
- excludesComponentViews.push(view);
- view.group.ignore = true;
- }
- });
- });
+ /**
+ * @DEPRECATED
+ */
+ setTheme(): void {
+ console.error('ECharts#setTheme() is DEPRECATED in ECharts 3.0');
+ }
- var url = this._zr.painter.getType() === 'svg'
- ? this.getSvgDataUrl()
- : this.getRenderedCanvas(opts).toDataURL(
- 'image/' + (opts && opts.type || 'png')
- );
+ getModel(): GlobalModel {
+ return this._model;
+ }
- each(excludesComponentViews, function (view) {
- view.group.ignore = false;
- });
+ getOption(): ECUnitOption {
+ return this._model && this._model.getOption();
+ }
- return url;
-};
+ getWidth(): number {
+ return this._zr.getWidth();
+ }
+ getHeight(): number {
+ return this._zr.getHeight();
+ }
-/**
- * @return {string}
- * @param {Object} opts
- * @param {string} [opts.type='png']
- * @param {string} [opts.pixelRatio=1]
- * @param {string} [opts.backgroundColor]
- */
-echartsProto.getConnectedDataURL = function (opts) {
- if (this._disposed) {
- disposedWarning(this.id);
- return;
+ getDevicePixelRatio(): number {
+ return (this._zr.painter as CanvasPainter).dpr || window.devicePixelRatio || 1;
}
- if (!env.canvasSupported) {
- return;
+ /**
+ * Get canvas which has all thing rendered
+ */
+ getRenderedCanvas(opts?: {
+ backgroundColor?: ZRColor
+ pixelRatio?: number
+ }): HTMLCanvasElement {
+ if (!env.canvasSupported) {
+ return;
+ }
+ opts = zrUtil.extend({}, opts || {});
+ opts.pixelRatio = opts.pixelRatio || 1;
+ opts.backgroundColor = opts.backgroundColor
+ || this._model.get('backgroundColor');
+ var zr = this._zr;
+ // var list = zr.storage.getDisplayList();
+ // Stop animations
+ // Never works before in init animation, so remove it.
+ // zrUtil.each(list, function (el) {
+ // el.stopAnimation(true);
+ // });
+ return (zr.painter as CanvasPainter).getRenderedCanvas(opts);
}
- var groupId = this.group;
- var mathMin = Math.min;
- var mathMax = Math.max;
- var MAX_NUMBER = Infinity;
- if (connectedGroups[groupId]) {
- var left = MAX_NUMBER;
- var top = MAX_NUMBER;
- var right = -MAX_NUMBER;
- var bottom = -MAX_NUMBER;
- var canvasList = [];
- var dpr = (opts && opts.pixelRatio) || 1;
-
- zrUtil.each(instances, function (chart, id) {
- if (chart.group === groupId) {
- var canvas = chart.getRenderedCanvas(
- zrUtil.clone(opts)
- );
- var 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
- });
- }
+
+ /**
+ * Get svg data url
+ */
+ getSvgDataUrl(): string {
+ if (!env.svgSupported) {
+ return;
+ }
+
+ var zr = this._zr;
+ var list = zr.storage.getDisplayList();
+ // Stop animations
+ zrUtil.each(list, function (el: Element) {
+ el.stopAnimation(true);
});
- left *= dpr;
- top *= dpr;
- right *= dpr;
- bottom *= dpr;
- var width = right - left;
- var height = bottom - top;
- var targetCanvas = zrUtil.createCanvas();
- targetCanvas.width = width;
- targetCanvas.height = height;
- var zr = zrender.init(targetCanvas);
-
- // 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
- }
- }));
+ return (zr.painter as SVGPainter).pathToDataUrl();
+ }
+
+ getDataURL(opts?: {
+ // file type 'png' by defualt
+ type?: string,
+ pixelRatio?: number,
+ backgroundColor?: ZRColor,
+ // component type array
+ excludeComponents?: ComponentMainType[]
+ }): string {
+ if (this._disposed) {
+ disposedWarning(this.id);
+ return;
}
- each(canvasList, function (item) {
- var img = new graphic.Image({
- style: {
- x: item.left * dpr - left,
- y: item.top * dpr - top,
- image: item.dom
+ opts = opts || {};
+ var excludeComponents = opts.excludeComponents;
+ var ecModel = this._model;
+ var excludesComponentViews: ComponentView[] = [];
+ var self = this;
+
+ each(excludeComponents, function (componentType) {
+ ecModel.eachComponent({
+ mainType: componentType
+ }, function (component) {
+ var view = self._componentsMap[component.__viewId];
+ if (!view.group.ignore) {
+ excludesComponentViews.push(view);
+ view.group.ignore = true;
}
});
- 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.
- * @param {string|Object} finder
- * If string, e.g., 'geo', means {geoIndex: 0}.
- * If Object, could contain some of these properties below:
- * {
- * seriesIndex / seriesId / seriesName,
- * geoIndex / geoId, geoName,
- * bmapIndex / bmapId / bmapName,
- * xAxisIndex / xAxisId / xAxisName,
- * yAxisIndex / yAxisId / yAxisName,
- * gridIndex / gridId / gridName,
- * ... (can be extended)
- * }
- * @param {Array|number} value
- * @return {Array|number} result
- */
-echartsProto.convertToPixel = zrUtil.curry(doConvertPixel, 'convertToPixel');
+ var url = this._zr.painter.getType() === 'svg'
+ ? this.getSvgDataUrl()
+ : this.getRenderedCanvas(opts).toDataURL(
+ 'image/' + (opts && opts.type || 'png')
+ );
-/**
- * Convert from pixel coordinate system to logical coordinate system.
- * See CoordinateSystem#convertFromPixel.
- * @param {string|Object} finder
- * If string, e.g., 'geo', means {geoIndex: 0}.
- * If Object, could contain some of these properties below:
- * {
- * seriesIndex / seriesId / seriesName,
- * geoIndex / geoId / geoName,
- * bmapIndex / bmapId / bmapName,
- * xAxisIndex / xAxisId / xAxisName,
- * yAxisIndex / yAxisId / yAxisName
- * gridIndex / gridId / gridName,
- * ... (can be extended)
- * }
- * @param {Array|number} value
- * @return {Array|number} result
- */
-echartsProto.convertFromPixel = zrUtil.curry(doConvertPixel, 'convertFromPixel');
+ each(excludesComponentViews, function (view) {
+ view.group.ignore = false;
+ });
-function doConvertPixel(methodName, finder, value) {
- if (this._disposed) {
- disposedWarning(this.id);
- return;
+ return url;
}
- var ecModel = this._model;
- var coordSysList = this._coordSysMgr.getCoordinateSystems();
- var result;
+ /**
+ * @return {string}
+ * @param {Object} opts
+ * @param {string} [opts.type='png']
+ * @param {string} [opts.pixelRatio=1]
+ * @param {string} [opts.backgroundColor]
+ */
+ getConnectedDataURL(opts?: {
+ // file type 'png' by defualt
+ type?: string,
+ pixelRatio?: number,
+ backgroundColor?: ZRColor,
+ connectedBackgroundColor?: string
+ }): string {
+ if (this._disposed) {
+ disposedWarning(this.id);
+ return;
+ }
+
+ if (!env.canvasSupported) {
+ return;
+ }
+ var groupId = this.group;
+ var mathMin = Math.min;
+ var mathMax = Math.max;
+ var MAX_NUMBER = Infinity;
+ if (connectedGroups[groupId]) {
+ var left = MAX_NUMBER;
+ var top = MAX_NUMBER;
+ var right = -MAX_NUMBER;
+ var bottom = -MAX_NUMBER;
+ var canvasList: {dom: HTMLCanvasElement, left: number, top: number}[] = [];
+ var dpr = (opts && opts.pixelRatio) || 1;
+
+ zrUtil.each(instances, function (chart, id) {
+ if (chart.group === groupId) {
+ var canvas = chart.getRenderedCanvas(
+ zrUtil.clone(opts)
+ );
+ var 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;
+ var width = right - left;
+ var height = bottom - top;
+ var targetCanvas = zrUtil.createCanvas();
+ targetCanvas.width = width;
+ targetCanvas.height = height;
+ var zr = zrender.init(targetCanvas);
+
+ // 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
+ }
+ }));
+ }
- finder = modelUtil.parseFinder(ecModel, finder);
+ each(canvasList, function (item) {
+ var img = new graphic.Image({
+ style: {
+ x: item.left * dpr - left,
+ y: item.top * dpr - top,
+ image: item.dom
+ }
+ });
+ zr.add(img);
+ });
+ zr.refreshImmediately();
- for (var i = 0; i < coordSysList.length; i++) {
- var coordSys = coordSysList[i];
- if (coordSys[methodName]
- && (result = coordSys[methodName](ecModel, finder, value)) != null
- ) {
- return result;
+ return targetCanvas.toDataURL('image/' + (opts && opts.type || 'png'));
+ }
+ else {
+ return this.getDataURL(opts);
}
}
- if (__DEV__) {
- console.warn(
- 'No coordinate system that supports ' + methodName + ' found by the given finder.'
- );
+ /**
+ * Convert from logical coordinate system to pixel coordinate system.
+ * See CoordinateSystem#convertToPixel.
+ */
+ convertToPixel(finder: ModelFinder, value: any): number[] {
+ return doConvertPixel(this, 'convertToPixel', finder, value);
}
-}
-/**
- * Is the specified coordinate systems or components contain the given pixel point.
- * @param {string|Object} finder
- * If string, e.g., 'geo', means {geoIndex: 0}.
- * If Object, could contain some of these properties below:
- * {
- * seriesIndex / seriesId / seriesName,
- * geoIndex / geoId / geoName,
- * bmapIndex / bmapId / bmapName,
- * xAxisIndex / xAxisId / xAxisName,
- * yAxisIndex / yAxisId / yAxisName,
- * gridIndex / gridId / gridName,
- * ... (can be extended)
- * }
- * @param {Array|number} value
- * @return {boolean} result
- */
-echartsProto.containPixel = function (finder, value) {
- if (this._disposed) {
- disposedWarning(this.id);
- return;
+ /**
+ * Convert from pixel coordinate system to logical coordinate system.
+ * See CoordinateSystem#convertFromPixel.
+ */
+ convertFromPixel(finder: ModelFinder, value: number[]): any {
+ return doConvertPixel(this, 'convertFromPixel', finder, value);
}
- var ecModel = this._model;
- var result;
+ /**
+ * 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;
+ }
- finder = modelUtil.parseFinder(ecModel, finder);
+ var ecModel = this._model;
+ var result: boolean;
- zrUtil.each(finder, function (models, key) {
- key.indexOf('Models') >= 0 && zrUtil.each(models, function (model) {
- var coordSys = model.coordinateSystem;
- if (coordSys && coordSys.containPoint) {
- result |= !!coordSys.containPoint(value);
- }
- else if (key === 'seriesModels') {
- var view = this._chartsMap[model.__viewId];
- if (view && view.containPoint) {
- result |= view.containPoint(value, model);
+ var findResult = modelUtil.parseFinder(ecModel, finder);
+
+ zrUtil.each(findResult, function (models, key) {
+ key.indexOf('Models') >= 0 && zrUtil.each(models as ComponentModel, function (model) {
+ var coordSys = model.coordinateSystem;
+ if (coordSys && coordSys.containPoint) {
+ result = result || !!coordSys.containPoint(value);
+ }
+ else if (key === 'seriesModels') {
+ var 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 + ': ' + (view
- ? 'The found component do not support containPoint.'
- : 'No view mapping to the found component.'
- ));
+ console.warn(key + ': containPoint is not supported');
}
}
- }
- else {
- if (__DEV__) {
- console.warn(key + ': containPoint is not supported');
- }
- }
+ }, this);
}, this);
- }, this);
- return !!result;
-};
+ return !!result;
+ }
-/**
- * Get visual from series or data.
- * @param {string|Object} 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 {string} visualType 'color', 'symbol', 'symbolSize'
- */
-echartsProto.getVisual = function (finder, visualType) {
- var ecModel = this._model;
+ /**
+ * 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) {
+ var ecModel = this._model;
- finder = modelUtil.parseFinder(ecModel, finder, {defaultMainType: 'series'});
+ finder = modelUtil.parseFinder(ecModel, finder, {defaultMainType: 'series'});
- var seriesModel = finder.seriesModel;
+ var seriesModel = finder.seriesModel;
- if (__DEV__) {
- if (!seriesModel) {
- console.warn('There is no specified seires model');
+ if (__DEV__) {
+ if (!seriesModel) {
+ console.warn('There is no specified seires model');
+ }
}
- }
- var data = seriesModel.getData();
+ var data = seriesModel.getData();
- var dataIndexInside = finder.hasOwnProperty('dataIndexInside')
- ? finder.dataIndexInside
- : finder.hasOwnProperty('dataIndex')
- ? data.indexOfRawIndex(finder.dataIndex)
- : null;
+ var dataIndexInside = finder.hasOwnProperty('dataIndexInside')
+ ? finder.dataIndexInside
+ : finder.hasOwnProperty('dataIndex')
+ ? data.indexOfRawIndex(finder.dataIndex)
+ : null;
- return dataIndexInside != null
- ? data.getItemVisual(dataIndexInside, visualType)
- : data.getVisual(visualType);
-};
-
-/**
- * Get view of corresponding component model
- * @param {module:echarts/model/Component} componentModel
- * @return {module:echarts/view/Component}
- */
-echartsProto.getViewOfComponentModel = function (componentModel) {
- return this._componentsMap[componentModel.__viewId];
-};
+ return dataIndexInside != null
+ ? data.getItemVisual(dataIndexInside, visualType)
+ : data.getVisual(visualType);
+ }
-/**
- * Get view of corresponding series model
- * @param {module:echarts/model/Series} seriesModel
- * @return {module:echarts/view/Chart}
- */
-echartsProto.getViewOfSeriesModel = function (seriesModel) {
- return this._chartsMap[seriesModel.__viewId];
-};
-
-var updateMethods = {
-
- prepareAndUpdate: function (payload) {
- prepare(this);
- updateMethods.update.call(this, payload);
- },
+ /**
+ * Get view of corresponding component model
+ */
+ getViewOfComponentModel(componentModel: ComponentModel): ComponentView {
+ return this._componentsMap[componentModel.__viewId];
+ }
/**
- * @param {Object} payload
- * @private
+ * Get view of corresponding series model
*/
- update: function (payload) {
- // console.profile && console.profile('update');
-
- var ecModel = this._model;
- var api = this._api;
- var zr = this._zr;
- var coordSysMgr = this._coordSysMgr;
- var scheduler = this._scheduler;
-
- // update before setOption
- if (!ecModel) {
- return;
- }
-
- 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.
+ getViewOfSeriesModel(seriesModel: SeriesModel): ChartView {
+ return this._chartsMap[seriesModel.__viewId];
+ }
+
+ private _initEvents(): void {
+ each(MOUSE_EVENT_NAMES, function (eveName) {
+ var handler = function (this: ECharts, e: ElementEvent) {
+ var ecModel = this.getModel();
+ var targetEl = e.target;
+ var el = targetEl as ECElement;
+ var params: ECEvent;
+ var isGlobalOut = eveName === 'globalout';
+
+ // no e.target when 'globalout'.
+ if (isGlobalOut) {
+ params = {} as ECEvent;
+ }
+ else if (el && el.dataIndex != null) {
+ var dataModel = el.dataModel || ecModel.getSeriesByIndex(el.seriesIndex);
+ params = (
+ dataModel && dataModel.getDataParams(el.dataIndex, el.dataType, targetEl) || {}
+ ) as ECEvent;
+ }
+ // If element has custom eventData of components
+ else if (el && el.eventData) {
+ params = zrUtil.extend({}, el.eventData) as ECEvent;
+ }
- // Create new coordinate system each update
- // In LineView may save the old coordinate system and use it to get the orignal point
- coordSysMgr.create(ecModel, api);
+ // 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) {
+ var componentType = params.componentType;
+ var 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;
+ }
+ var model = componentType && componentIndex != null
+ && ecModel.getComponent(componentType, componentIndex);
+ var view = model && this[
+ model.mainType === 'series' ? '_chartsMap' : '_componentsMap'
+ ][model.__viewId];
- scheduler.performDataProcessorTasks(ecModel, payload);
+ 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');
+ }
+ }
- // 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
- // deteming whether use progressive rendering.
- updateStreamModes(this, ecModel);
+ params.event = e;
+ params.type = eveName;
- // 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 requied in stream mode updater currently.
- coordSysMgr.update(ecModel, api);
+ this._ecEventProcessor.eventInfo = {
+ targetEl: el,
+ packedEvent: params,
+ model: model,
+ view: view
+ };
- clearColorPalette(ecModel);
- scheduler.performVisualTasks(ecModel, payload);
+ 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);
+ }, this);
- render(this, ecModel, api, payload);
+ each(eventActionMap, function (actionType, eventType) {
+ this._messageCenter.on(eventType, function (event) {
+ this.trigger(eventType, event);
+ }, this);
+ }, this);
+ }
- // Set background
- var backgroundColor = ecModel.get('backgroundColor') || 'transparent';
+ isDisposed(): boolean {
+ return this._disposed;
+ }
- // In IE8
- if (!env.canvasSupported) {
- var colorArr = colorTool.parse(backgroundColor);
- backgroundColor = colorTool.stringify(colorArr, 'rgb');
- if (colorArr[3] === 0) {
- backgroundColor = 'transparent';
- }
- }
- else {
- zr.setBackgroundColor(backgroundColor);
+ clear(): void {
+ if (this._disposed) {
+ disposedWarning(this.id);
+ return;
}
+ this.setOption({ series: [] }, true);
+ }
- performPostUpdateFuncs(ecModel, api);
-
- // console.profile && console.profileEnd('update');
- },
-
- /**
- * @param {Object} payload
- * @private
- */
- updateTransform: function (payload) {
- var ecModel = this._model;
- var ecIns = this;
- var api = this._api;
-
- // update before setOption
- if (!ecModel) {
+ dispose(): void {
+ if (this._disposed) {
+ disposedWarning(this.id);
return;
}
+ this._disposed = true;
- // ChartView.markUpdateMethod(payload, 'updateTransform');
+ modelUtil.setAttribute(this.getDom(), DOM_ATTRIBUTE_KEY, '');
- var componentDirtyList = [];
- ecModel.eachComponent(function (componentType, componentModel) {
- var componentView = ecIns.getViewOfComponentModel(componentModel);
- if (componentView && componentView.__alive) {
- if (componentView.updateTransform) {
- var result = componentView.updateTransform(componentModel, ecModel, api, payload);
- result && result.update && componentDirtyList.push(componentView);
- }
- else {
- componentDirtyList.push(componentView);
- }
- }
- });
+ var api = this._api;
+ var ecModel = this._model;
- var seriesDirtyMap = zrUtil.createHashMap();
- ecModel.eachSeries(function (seriesModel) {
- var chartView = ecIns._chartsMap[seriesModel.__viewId];
- if (chartView.updateTransform) {
- var result = chartView.updateTransform(seriesModel, ecModel, api, payload);
- result && result.update && seriesDirtyMap.set(seriesModel.uid, 1);
- }
- else {
- seriesDirtyMap.set(seriesModel.uid, 1);
- }
+ each(this._componentsViews, function (component) {
+ component.dispose(ecModel, api);
+ });
+ each(this._chartsViews, function (chart) {
+ chart.dispose(ecModel, api);
});
- 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(ecIns, ecModel, api, payload, seriesDirtyMap);
+ // Dispose after all views disposed
+ this._zr.dispose();
- performPostUpdateFuncs(ecModel, this._api);
- },
+ delete instances[this.id];
+ }
/**
- * @param {Object} payload
- * @private
+ * Resize the chart
*/
- updateView: function (payload) {
- var ecModel = this._model;
-
- // update before setOption
- if (!ecModel) {
+ resize(opts?: {
+ width?: number | 'auto', // Can be 'auto' (the same as null/undefined)
+ height?: number | 'auto', // Can be 'auto' (the same as null/undefined)
+ silent?: boolean // by default false.
+ }): void {
+ if (__DEV__) {
+ assert(!this[IN_MAIN_PROCESS], '`resize` should not be called during main process.');
+ }
+ if (this._disposed) {
+ disposedWarning(this.id);
return;
}
- ChartView.markUpdateMethod(payload, 'updateView');
-
- clearColorPalette(ecModel);
+ this._zr.resize(opts);
- // 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, this._model, this._api, payload);
+ var ecModel = this._model;
- performPostUpdateFuncs(ecModel, this._api);
- },
+ // Resize loading effect
+ this._loadingFX && this._loadingFX.resize();
- /**
- * @param {Object} payload
- * @private
- */
- updateVisual: function (payload) {
- updateMethods.update.call(this, payload);
+ if (!ecModel) {
+ return;
+ }
- // var ecModel = this._model;
+ var optionChanged = ecModel.resetOption('media');
- // // update before setOption
- // if (!ecModel) {
- // return;
- // }
+ var silent = opts && opts.silent;
- // ChartView.markUpdateMethod(payload, 'updateVisual');
+ this[IN_MAIN_PROCESS] = true;
- // clearColorPalette(ecModel);
+ optionChanged && prepare(this);
+ updateMethods.update.call(this);
- // // 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});
+ this[IN_MAIN_PROCESS] = false;
- // render(this, this._model, this._api, payload);
+ flushPendingActions.call(this, silent);
- // performPostUpdateFuncs(ecModel, this._api);
- },
+ triggerUpdatedEvent.call(this, silent);
+ }
/**
- * @param {Object} payload
- * @private
+ * Show loading effect
+ * @param name 'default' by default
+ * @param cfg cfg of registered loading effect
*/
- updateLayout: function (payload) {
- updateMethods.update.call(this, payload);
-
- // var ecModel = this._model;
-
- // // update before setOption
- // if (!ecModel) {
- // return;
- // }
-
- // ChartView.markUpdateMethod(payload, 'updateLayout');
+ showLoading(cfg?: object): void;
+ showLoading(name?: string, cfg?: object): void;
+ showLoading(name?: string | object, cfg?: object): void {
+ if (this._disposed) {
+ disposedWarning(this.id);
+ return;
+ }
- // // 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});
+ if (isObject(name)) {
+ cfg = name as object;
+ name = '';
+ }
+ name = name || 'default';
- // render(this, this._model, this._api, payload);
+ this.hideLoading();
+ if (!loadingEffects[name]) {
+ if (__DEV__) {
+ console.warn('Loading effects ' + name + ' not exists.');
+ }
+ return;
+ }
+ var el = loadingEffects[name](this._api, cfg);
+ var zr = this._zr;
+ this._loadingFX = el;
- // performPostUpdateFuncs(ecModel, this._api);
+ zr.add(el);
}
-};
-function prepare(ecIns) {
- var ecModel = ecIns._model;
- var scheduler = ecIns._scheduler;
-
- scheduler.restorePipelines(ecModel);
-
- scheduler.prepareStageTasks();
-
- prepareView(ecIns, 'component', ecModel, scheduler);
-
- prepareView(ecIns, 'chart', ecModel, scheduler);
-
- scheduler.plan();
-}
+ /**
+ * Hide loading effect
+ */
+ hideLoading(): void {
+ if (this._disposed) {
+ disposedWarning(this.id);
+ return;
+ }
-/**
- * @private
- */
-function updateDirectly(ecIns, method, payload, mainType, subType) {
- var ecModel = ecIns._model;
-
- // broadcast
- if (!mainType) {
- // FIXME
- // Chart will not be update directly here, except set dirty.
- // But there is no such scenario now.
- each(ecIns._componentsViews.concat(ecIns._chartsViews), callView);
- return;
+ this._loadingFX && this._zr.remove(this._loadingFX);
+ this._loadingFX = null;
}
- var query = {};
- query[mainType + 'Id'] = payload[mainType + 'Id'];
- query[mainType + 'Index'] = payload[mainType + 'Index'];
- query[mainType + 'Name'] = payload[mainType + 'Name'];
+ makeActionFromEvent(eventObj: ECEvent): Payload {
+ var payload = zrUtil.extend({}, eventObj) as Payload;
+ payload.type = eventActionMap[eventObj.type];
+ return payload;
+ }
- var condition = {mainType: mainType, query: query};
- subType && (condition.subType = subType); // subType may be '' by parseClassType;
+ /**
+ * @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;
+ }
- var excludeSeriesId = payload.excludeSeriesId;
- if (excludeSeriesId != null) {
- excludeSeriesId = zrUtil.createHashMap(modelUtil.normalizeToArray(excludeSeriesId));
- }
+ if (!isObject(opt)) {
+ opt = {silent: !!opt};
+ }
- // If dispatchAction before setOption, do nothing.
- ecModel && ecModel.eachComponent(condition, function (model) {
- if (!excludeSeriesId || excludeSeriesId.get(model.id) == null) {
- callView(ecIns[
- mainType === 'series' ? '_chartsMap' : '_componentsMap'
- ][model.__viewId]);
+ if (!actions[payload.type]) {
+ return;
}
- }, ecIns);
- function callView(view) {
- view && view.__alive && view[method] && view[method](
- view.__model, ecModel, ecIns._api, payload
- );
- }
-}
+ // Avoid dispatch action before setOption. Especially in `connect`.
+ if (!this._model) {
+ return;
+ }
-/**
- * Resize the chart
- * @param {Object} opts
- * @param {number} [opts.width] Can be 'auto' (the same as null/undefined)
- * @param {number} [opts.height] Can be 'auto' (the same as null/undefined)
- * @param {boolean} [opts.silent=false]
- */
-echartsProto.resize = function (opts) {
- if (__DEV__) {
- assert(!this[IN_MAIN_PROCESS], '`resize` should not be called during main process.');
- }
- if (this._disposed) {
- disposedWarning(this.id);
- return;
- }
+ // May dispatchAction in rendering procedure
+ if (this[IN_MAIN_PROCESS]) {
+ this._pendingActions.push(payload);
+ return;
+ }
- this._zr.resize(opts);
+ var silent = (opt as any).silent;
+ doDispatchAction.call(this, payload, silent);
- var ecModel = this._model;
+ var flush = (opt as any).flush;
+ if (flush) {
+ this._zr.flush();
+ }
+ else if (flush !== false && env.browser.weChat) {
+ // In WeChat embeded browser, `requestAnimationFrame` and `setInterval`
+ // hang when sliding page (on touch event), which cause that zr does not
+ // refresh util 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();
+ }
- // Resize loading effect
- this._loadingFX && this._loadingFX.resize();
+ flushPendingActions.call(this, silent);
- if (!ecModel) {
- return;
+ triggerUpdatedEvent.call(this, silent);
}
- var optionChanged = ecModel.resetOption('media');
+ appendData(params: {
+ seriesIndex: number,
+ data: any
+ }): void {
+ if (this._disposed) {
+ disposedWarning(this.id);
+ return;
+ }
- var silent = opts && opts.silent;
+ var seriesIndex = params.seriesIndex;
+ var ecModel = this.getModel();
+ var seriesModel = ecModel.getSeriesByIndex(seriesIndex) as SeriesModel;
- this[IN_MAIN_PROCESS] = true;
+ if (__DEV__) {
+ assert(params.data && seriesModel);
+ }
- optionChanged && prepare(this);
- updateMethods.update.call(this);
+ seriesModel.appendData(params);
- this[IN_MAIN_PROCESS] = false;
+ // 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.
- flushPendingActions.call(this, silent);
+ this._scheduler.unfinished = +true;
+ }
- triggerUpdatedEvent.call(this, silent);
-};
-function updateStreamModes(ecIns, ecModel) {
- var chartsMap = ecIns._chartsMap;
- var scheduler = ecIns._scheduler;
- ecModel.eachSeries(function (seriesModel) {
- scheduler.updateStreamModes(seriesModel, chartsMap[seriesModel.__viewId]);
- });
-}
+ // A work around for no `internal` modifier in ts yet but
+ // need to strictly hide private methods to JS users.
+ static internalField = (function () {
-/**
- * Show loading effect
- * @param {string} [name='default']
- * @param {Object} [cfg]
- */
-echartsProto.showLoading = function (name, cfg) {
- if (this._disposed) {
- disposedWarning(this.id);
- return;
- }
+ prepare = function (ecIns: ECharts): void {
+ var scheduler = ecIns._scheduler;
- if (isObject(name)) {
- cfg = name;
- name = '';
- }
- name = name || 'default';
+ scheduler.restorePipelines(ecIns._model);
+ scheduler.prepareStageTasks();
- this.hideLoading();
- if (!loadingEffects[name]) {
- if (__DEV__) {
- console.warn('Loading effects ' + name + ' not exists.');
- }
- return;
- }
- var el = loadingEffects[name](this._api, cfg);
- var zr = this._zr;
- this._loadingFX = el;
+ prepareView(ecIns, true);
+ prepareView(ecIns, false);
- zr.add(el);
-};
+ scheduler.plan();
+ };
-/**
- * Hide loading effect
- */
-echartsProto.hideLoading = function () {
- if (this._disposed) {
- disposedWarning(this.id);
- return;
- }
+ /**
+ * Prepare view instances of charts and components
+ */
+ prepareView = function (ecIns: ECharts, isComponent: boolean): void {
+ var ecModel = ecIns._model;
+ var scheduler = ecIns._scheduler;
+ var viewList = isComponent ? ecIns._componentsViews : ecIns._chartsViews;
+ var viewMap = isComponent ? ecIns._componentsMap : ecIns._chartsMap;
+ var zr = ecIns._zr;
+ var api = ecIns._api;
+
+ for (var i = 0; i < viewList.length; i++) {
+ viewList[i].__alive = false;
+ }
- this._loadingFX && this._zr.remove(this._loadingFX);
- this._loadingFX = null;
-};
+ isComponent
+ ? ecModel.eachComponent(function (componentType, model) {
+ componentType !== 'series' && doPrepare(model);
+ })
+ : ecModel.eachSeries(doPrepare);
+
+ function doPrepare(model: ComponentModel): void {
+ // Consider: id same and type changed.
+ var viewId = '_ec_' + model.id + '_' + model.type;
+ var view = viewMap[viewId];
+ if (!view) {
+ var classType = parseClassType(model.type);
+ var Clazz = isComponent
+ ? (ComponentView as ComponentViewConstructor).getClass(classType.main, classType.sub)
+ : (ChartView as ChartViewConstructor).getClass(classType.sub);
-/**
- * @param {Object} eventObj
- * @return {Object}
- */
-echartsProto.makeActionFromEvent = function (eventObj) {
- var payload = zrUtil.extend({}, eventObj);
- payload.type = eventActionMap[eventObj.type];
- return payload;
-};
+ if (__DEV__) {
+ assert(Clazz, classType.sub + ' does not exist.');
+ }
-/**
- * @pubilc
- * @param {Object} payload
- * @param {string} [payload.type] Action type
- * @param {Object|boolean} [opt] If pass boolean, means opt.silent
- * @param {boolean} [opt.silent=false] Whether trigger events.
- * @param {boolean} [opt.flush=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.
- */
-echartsProto.dispatchAction = function (payload, opt) {
- if (this._disposed) {
- disposedWarning(this.id);
- return;
- }
+ view = new Clazz();
+ view.init(ecModel, api);
+ viewMap[viewId] = view;
+ viewList.push(view as any);
+ zr.add(view.group);
+ }
- if (!isObject(opt)) {
- opt = {silent: !!opt};
- }
+ 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
+ );
+ }
- if (!actions[payload.type]) {
- return;
- }
+ for (var i = 0; i < viewList.length;) {
+ var view = viewList[i];
+ if (!view.__alive) {
+ !isComponent && (view as ChartView).renderTask.dispose();
+ zr.remove(view.group);
+ view.dispose(ecModel, api);
+ viewList.splice(i, 1);
+ delete viewMap[view.__id];
+ view.__id = view.group.__ecComponentInfo = null;
+ }
+ else {
+ i++;
+ }
+ }
+ };
- // Avoid dispatch action before setOption. Especially in `connect`.
- if (!this._model) {
- return;
- }
+ updateDirectly = function (
+ ecIns: ECharts,
+ method: string,
+ payload: Payload,
+ mainType: ComponentMainType,
+ subType?: ComponentSubType
+ ): void {
+ var ecModel = ecIns._model;
+
+ // 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;
+ }
- // May dispatchAction in rendering procedure
- if (this[IN_MAIN_PROCESS]) {
- this._pendingActions.push(payload);
- return;
- }
+ var query: QueryConditionKindA['query'] = {};
+ query[mainType + 'Id'] = payload[mainType + 'Id'];
+ query[mainType + 'Index'] = payload[mainType + 'Index'];
+ query[mainType + 'Name'] = payload[mainType + 'Name'];
- doDispatchAction.call(this, payload, opt.silent);
+ var condition = {mainType: mainType, query: query} as QueryConditionKindA;
+ subType && (condition.subType = subType); // subType may be '' by parseClassType;
- if (opt.flush) {
- this._zr.flush(true);
- }
- else if (opt.flush !== false && env.browser.weChat) {
- // In WeChat embeded browser, `requestAnimationFrame` and `setInterval`
- // hang when sliding page (on touch event), which cause that zr does not
- // refresh util 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();
- }
+ var excludeSeriesId = payload.excludeSeriesId;
+ var excludeSeriesIdMap: zrUtil.HashMap<string[]>;
+ if (excludeSeriesId != null) {
+ excludeSeriesIdMap = zrUtil.createHashMap(modelUtil.normalizeToArray(excludeSeriesId));
+ }
- flushPendingActions.call(this, opt.silent);
+ // If dispatchAction before setOption, do nothing.
+ ecModel && ecModel.eachComponent(condition, function (model) {
+ if (!excludeSeriesIdMap || excludeSeriesIdMap.get(model.id) == null) {
+ callView(ecIns[
+ mainType === 'series' ? '_chartsMap' : '_componentsMap'
+ ][model.__viewId]);
+ }
+ }, ecIns);
- triggerUpdatedEvent.call(this, opt.silent);
-};
+ function callView(view: ComponentView | ChartView) {
+ view && view.__alive && (view as any)[method] && (view as any)[method](
+ view.__model, ecModel, ecIns._api, payload
+ );
+ }
+ };
-function doDispatchAction(payload, silent) {
- var payloadType = payload.type;
- var escapeConnect = payload.escapeConnect;
- var actionWrap = actions[payloadType];
- var actionInfo = actionWrap.actionInfo;
-
- var cptType = (actionInfo.update || 'update').split(':');
- var updateMethod = cptType.pop();
- cptType = cptType[0] != null && parseClassType(cptType[0]);
-
- this[IN_MAIN_PROCESS] = true;
-
- var payloads = [payload];
- var batched = false;
- // Batch action
- if (payload.batch) {
- batched = true;
- payloads = zrUtil.map(payload.batch, function (item) {
- item = zrUtil.defaults(zrUtil.extend({}, item), payload);
- item.batch = null;
- return item;
- });
- }
+ updateMethods = {
- var eventObjBatch = [];
- var eventObj;
- var isHighDown = payloadType === 'highlight' || payloadType === 'downplay';
-
- each(payloads, function (batchItem) {
- // Action can specify the event by return it.
- eventObj = actionWrap.action(batchItem, this._model, this._api);
- // Emit event outside
- eventObj = eventObj || zrUtil.extend({}, 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) {
- // method, payload, mainType, subType
- updateDirectly(this, updateMethod, batchItem, 'series');
- }
- else if (cptType) {
- updateDirectly(this, updateMethod, batchItem, cptType.main, cptType.sub);
- }
- }, this);
+ prepareAndUpdate: function (this: ECharts, payload: Payload): void {
+ prepare(this);
+ updateMethods.update.call(this, payload);
+ },
- if (updateMethod !== 'none' && !isHighDown && !cptType) {
- // Still dirty
- if (this[OPTION_UPDATED]) {
- // FIXME Pass payload ?
- prepare(this);
- updateMethods.update.call(this, payload);
- this[OPTION_UPDATED] = false;
- }
- else {
- updateMethods[updateMethod].call(this, payload);
- }
- }
+ update: function (this: ECharts, payload: Payload): void {
+ // console.profile && console.profile('update');
- // Follow the rule of action batch
- if (batched) {
- eventObj = {
- type: actionInfo.event || payloadType,
- escapeConnect: escapeConnect,
- batch: eventObjBatch
- };
- }
- else {
- eventObj = eventObjBatch[0];
- }
+ var ecModel = this._model;
+ var api = this._api;
+ var zr = this._zr;
+ var coordSysMgr = this._coordSysMgr;
+ var scheduler = this._scheduler;
- this[IN_MAIN_PROCESS] = false;
+ // update before setOption
+ if (!ecModel) {
+ return;
+ }
- !silent && this._messageCenter.trigger(eventObj.type, eventObj);
-}
+ scheduler.restoreData(ecModel, payload);
-function flushPendingActions(silent) {
- var pendingActions = this._pendingActions;
- while (pendingActions.length) {
- var payload = pendingActions.shift();
- doDispatchAction.call(this, payload, silent);
- }
-}
+ scheduler.performSeriesTasks(ecModel);
-function triggerUpdatedEvent(silent) {
- !silent && this.trigger('updated');
-}
+ // 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.
-/**
- * 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.
- */
-function bindRenderedEvent(zr, ecIns) {
- zr.on('rendered', function () {
-
- ecIns.trigger('rendered');
-
- // The `finished` event should not be triggered repeatly,
- // so it should only be triggered when rendering indeed happend
- // 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[OPTION_UPDATED]
- && !ecIns._scheduler.unfinished
- && !ecIns._pendingActions.length
- ) {
- ecIns.trigger('finished');
- }
- });
-}
+ // Create new coordinate system each update
+ // In LineView may save the old coordinate system and use it to get the orignal point
+ coordSysMgr.create(ecModel, api);
-/**
- * @param {Object} params
- * @param {number} params.seriesIndex
- * @param {Array|TypedArray} params.data
- */
-echartsProto.appendData = function (params) {
- if (this._disposed) {
- disposedWarning(this.id);
- return;
- }
+ scheduler.performDataProcessorTasks(ecModel, payload);
- var seriesIndex = params.seriesIndex;
- var ecModel = this.getModel();
- var seriesModel = ecModel.getSeriesByIndex(seriesIndex);
+ // 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
+ // deteming whether use progressive rendering.
+ updateStreamModes(this, ecModel);
- if (__DEV__) {
- assert(params.data && seriesModel);
- }
+ // 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 requied in stream mode updater currently.
+ coordSysMgr.update(ecModel, api);
- seriesModel.appendData(params);
+ clearColorPalette(ecModel);
+ scheduler.performVisualTasks(ecModel, payload);
- // 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.
+ render(this, ecModel, api, payload);
- this._scheduler.unfinished = true;
-};
+ // Set background
+ var backgroundColor = ecModel.get('backgroundColor') || 'transparent';
-/**
- * Register event
- * @method
- */
-echartsProto.on = createRegisterEventWithLowercaseName('on', false);
-echartsProto.off = createRegisterEventWithLowercaseName('off', false);
-echartsProto.one = createRegisterEventWithLowercaseName('one', false);
+ // In IE8
+ if (!env.canvasSupported) {
+ var colorArr = colorTool.parse(backgroundColor);
+ backgroundColor = colorTool.stringify(colorArr, 'rgb');
+ if (colorArr[3] === 0) {
+ backgroundColor = 'transparent';
+ }
+ }
+ else {
+ zr.setBackgroundColor(backgroundColor);
+ }
-/**
- * Prepare view instances of charts and components
- * @param {module:echarts/model/Global} ecModel
- * @private
- */
-function prepareView(ecIns, type, ecModel, scheduler) {
- var isComponent = type === 'component';
- var viewList = isComponent ? ecIns._componentsViews : ecIns._chartsViews;
- var viewMap = isComponent ? ecIns._componentsMap : ecIns._chartsMap;
- var zr = ecIns._zr;
- var api = ecIns._api;
-
- for (var i = 0; i < viewList.length; i++) {
- viewList[i].__alive = false;
- }
+ performPostUpdateFuncs(ecModel, api);
- isComponent
- ? ecModel.eachComponent(function (componentType, model) {
- componentType !== 'series' && doPrepare(model);
- })
- : ecModel.eachSeries(doPrepare);
-
- function doPrepare(model) {
- // Consider: id same and type changed.
- var viewId = '_ec_' + model.id + '_' + model.type;
- var view = viewMap[viewId];
- if (!view) {
- var classType = parseClassType(model.type);
- var Clazz = isComponent
- ? ComponentView.getClass(classType.main, classType.sub)
- : ChartView.getClass(classType.sub);
+ // console.profile && console.profileEnd('update');
+ },
- if (__DEV__) {
- assert(Clazz, classType.sub + ' does not exist.');
- }
+ updateTransform: function (this: ECharts, payload: Payload): void {
+ var ecModel = this._model;
+ var ecIns = this;
+ var api = this._api;
- view = new Clazz();
- view.init(ecModel, api);
- viewMap[viewId] = view;
- viewList.push(view);
- zr.add(view.group);
- }
+ // update before setOption
+ if (!ecModel) {
+ return;
+ }
- model.__viewId = view.__id = viewId;
- view.__alive = true;
- view.__model = model;
- view.group.__ecComponentInfo = {
- mainType: model.mainType,
- index: model.componentIndex
- };
- !isComponent && scheduler.prepareView(view, model, ecModel, api);
- }
+ // ChartView.markUpdateMethod(payload, 'updateTransform');
- for (var i = 0; i < viewList.length;) {
- var view = viewList[i];
- if (!view.__alive) {
- !isComponent && view.renderTask.dispose();
- zr.remove(view.group);
- view.dispose(ecModel, api);
- viewList.splice(i, 1);
- delete viewMap[view.__id];
- view.__id = view.group.__ecComponentInfo = null;
- }
- else {
- i++;
- }
- }
-}
+ var componentDirtyList = [];
+ ecModel.eachComponent(function (componentType, componentModel) {
+ var componentView = ecIns.getViewOfComponentModel(componentModel);
+ if (componentView && componentView.__alive) {
+ if (componentView.updateTransform) {
+ var result = componentView.updateTransform(componentModel, ecModel, api, payload);
+ result && result.update && componentDirtyList.push(componentView);
+ }
+ else {
+ componentDirtyList.push(componentView);
+ }
+ }
+ });
-// /**
-// * Encode visual infomation from data after data processing
-// *
-// * @param {module:echarts/model/Global} ecModel
-// * @param {object} layout
-// * @param {boolean} [layoutFilter] `true`: only layout,
-// * `false`: only not layout,
-// * `null`/`undefined`: all.
-// * @param {string} taskBaseTag
-// * @private
-// */
-// function startVisualEncoding(ecIns, ecModel, api, payload, layoutFilter) {
-// each(visualFuncs, function (visual, index) {
-// var isLayout = visual.isLayout;
-// if (layoutFilter == null
-// || (layoutFilter === false && !isLayout)
-// || (layoutFilter === true && isLayout)
-// ) {
-// visual.func(ecModel, api, payload);
-// }
-// });
-// }
+ var seriesDirtyMap = zrUtil.createHashMap();
+ ecModel.eachSeries(function (seriesModel) {
+ var chartView = ecIns._chartsMap[seriesModel.__viewId];
+ if (chartView.updateTransform) {
+ var result = chartView.updateTransform(seriesModel, ecModel, api, payload);
+ result && result.update && seriesDirtyMap.set(seriesModel.uid, 1);
+ }
+ else {
+ seriesDirtyMap.set(seriesModel.uid, 1);
+ }
+ });
-function clearColorPalette(ecModel) {
- ecModel.clearColorPalette();
- ecModel.eachSeries(function (seriesModel) {
- seriesModel.clearColorPalette();
- });
-}
+ 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}
+ );
-function render(ecIns, ecModel, api, payload) {
+ // Currently, not call render of components. Geo render cost a lot.
+ // renderComponents(ecIns, ecModel, api, payload, componentDirtyList);
+ renderSeries(ecIns, ecModel, api, payload, seriesDirtyMap);
- renderComponents(ecIns, ecModel, api, payload);
+ performPostUpdateFuncs(ecModel, this._api);
+ },
- each(ecIns._chartsViews, function (chart) {
- chart.__alive = false;
- });
+ updateView: function (this: ECharts, payload: Payload): void {
+ var ecModel = this._model;
- renderSeries(ecIns, ecModel, api, payload);
+ // update before setOption
+ if (!ecModel) {
+ return;
+ }
- // Remove groups of unrendered charts
- each(ecIns._chartsViews, function (chart) {
- if (!chart.__alive) {
- chart.remove(ecModel, api);
- }
- });
-}
+ ChartView.markUpdateMethod(payload, 'updateView');
-function renderComponents(ecIns, ecModel, api, payload, dirtyList) {
- each(dirtyList || ecIns._componentsViews, function (componentView) {
- var componentModel = componentView.__model;
- componentView.render(componentModel, ecModel, api, payload);
+ clearColorPalette(ecModel);
- updateZ(componentModel, componentView);
- });
-}
+ // 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 each chart and component
- * @private
- */
-function renderSeries(ecIns, ecModel, api, payload, dirtyMap) {
- // Render all charts
- var scheduler = ecIns._scheduler;
- var unfinished;
- ecModel.eachSeries(function (seriesModel) {
- var chartView = ecIns._chartsMap[seriesModel.__viewId];
- chartView.__alive = true;
-
- var renderTask = chartView.renderTask;
- scheduler.updatePayload(renderTask, payload);
-
- if (dirtyMap && dirtyMap.get(seriesModel.uid)) {
- renderTask.dirty();
- }
+ render(this, this._model, this._api, payload);
- unfinished |= renderTask.perform(scheduler.getPerformArgs(renderTask));
+ performPostUpdateFuncs(ecModel, this._api);
+ },
- chartView.group.silent = !!seriesModel.get('silent');
+ updateVisual: function (this: ECharts, payload: Payload): void {
+ updateMethods.update.call(this, payload);
- updateZ(seriesModel, chartView);
+ // var ecModel = this._model;
- updateBlend(seriesModel, chartView);
- });
- scheduler.unfinished |= unfinished;
+ // // update before setOption
+ // if (!ecModel) {
+ // return;
+ // }
- // If use hover layer
- updateHoverLayerStatus(ecIns, ecModel);
+ // ChartView.markUpdateMethod(payload, 'updateVisual');
- // Add aria
- aria(ecIns._zr.dom, ecModel);
-}
+ // clearColorPalette(ecModel);
-function performPostUpdateFuncs(ecModel, api) {
- each(postUpdateFuncs, function (func) {
- func(ecModel, api);
- });
-}
+ // // 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});
+ // render(this, this._model, this._api, payload);
-var MOUSE_EVENT_NAMES = [
- 'click', 'dblclick', 'mouseover', 'mouseout', 'mousemove',
- 'mousedown', 'mouseup', 'globalout', 'contextmenu'
-];
+ // performPostUpdateFuncs(ecModel, this._api);
+ },
-/**
- * @private
- */
-echartsProto._initEvents = function () {
- each(MOUSE_EVENT_NAMES, function (eveName) {
- var handler = function (e) {
- var ecModel = this.getModel();
- var el = e.target;
- var params;
- var isGlobalOut = eveName === 'globalout';
-
- // no e.target when 'globalout'.
- if (isGlobalOut) {
- params = {};
- }
- else if (el && el.dataIndex != null) {
- var dataModel = el.dataModel || ecModel.getSeriesByIndex(el.seriesIndex);
- params = dataModel && dataModel.getDataParams(el.dataIndex, el.dataType, el) || {};
+ updateLayout: function (this: ECharts, payload: Payload): void {
+ updateMethods.update.call(this, payload);
+
+ // var ecModel = this._model;
+
+ // // update before setOption
+ // if (!ecModel) {
+ // return;
+ // }
+
+ // ChartView.markUpdateMethod(payload, 'updateLayout');
+
+ // // 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});
+
+ // render(this, this._model, this._api, payload);
+
+ // performPostUpdateFuncs(ecModel, this._api);
}
- // If element has custom eventData of components
- else if (el && el.eventData) {
- params = zrUtil.extend({}, el.eventData);
+ };
+
+ doConvertPixel = function (
+ ecIns: ECharts,
+ methodName: 'convertFromPixel' | 'convertToPixel',
+ finder: ModelFinder,
+ value: any
+ ): any {
+ if (ecIns._disposed) {
+ disposedWarning(ecIns.id);
+ return;
}
+ var ecModel = ecIns._model;
+ var coordSysList = ecIns._coordSysMgr.getCoordinateSystems();
+ var result;
- // 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) {
- var componentType = params.componentType;
- var 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'
+ finder = modelUtil.parseFinder(ecModel, finder);
+
+ for (var i = 0; i < coordSysList.length; i++) {
+ var coordSys = coordSysList[i];
+ if (coordSys[methodName]
+ && (result = coordSys[methodName](ecModel, finder, value)) != null
) {
- componentType = 'series';
- componentIndex = params.seriesIndex;
- }
- var model = componentType && componentIndex != null
- && ecModel.getComponent(componentType, componentIndex);
- var 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');
- }
+ return result;
}
+ }
+
+ if (__DEV__) {
+ console.warn(
+ 'No coordinate system that supports ' + methodName + ' found by the given finder.'
+ );
+ }
+ };
+
+ updateStreamModes = function (ecIns: ECharts, ecModel: GlobalModel): void {
+ var chartsMap = ecIns._chartsMap;
+ var scheduler = ecIns._scheduler;
+ ecModel.eachSeries(function (seriesModel) {
+ scheduler.updateStreamModes(seriesModel, chartsMap[seriesModel.__viewId]);
+ });
+ };
+
+ doDispatchAction = function (this: ECharts, payload: Payload, silent: boolean): void {
+ var payloadType = payload.type;
+ var escapeConnect = payload.escapeConnect;
+ var actionWrap = actions[payloadType];
+ var actionInfo = actionWrap.actionInfo;
+
+ var cptTypeTmp = (actionInfo.update || 'update').split(':');
+ var updateMethod = cptTypeTmp.pop();
+ var cptType = cptTypeTmp[0] != null && parseClassType(cptTypeTmp[0]);
+
+ this[IN_MAIN_PROCESS] = true;
+
+ var payloads: Payload[] = [payload];
+ var batched = false;
+ // Batch action
+ if (payload.batch) {
+ batched = true;
+ payloads = zrUtil.map<PayloadItem, Payload, unknown>(payload.batch, function (item) {
+ item = zrUtil.defaults(zrUtil.extend({}, item), payload);
+ item.batch = null;
+ return item as Payload;
+ });
+ }
- params.event = e;
- params.type = eveName;
+ var eventObjBatch: ECEventData[] = [];
+ var eventObj: ECEvent;
+ var isHighDown = payloadType === 'highlight' || payloadType === 'downplay';
+
+ each(payloads, function (batchItem) {
+ // Action can specify the event by return it.
+ eventObj = actionWrap.action(batchItem, this._model, this._api) as ECEvent;
+ // Emit event outside
+ eventObj = eventObj || zrUtil.extend({} as ECEvent, 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) {
+ // method, payload, mainType, subType
+ updateDirectly(this, updateMethod, batchItem as Payload, 'series');
+ }
+ else if (cptType) {
+ updateDirectly(this, updateMethod, batchItem as Payload, cptType.main, cptType.sub);
+ }
+ }, this);
+
+ if (updateMethod !== 'none' && !isHighDown && !cptType) {
+ // Still dirty
+ if (this[OPTION_UPDATED]) {
+ // FIXME Pass payload ?
+ prepare(this);
+ updateMethods.update.call(this, payload);
+ this[OPTION_UPDATED] = false;
+ }
+ else {
+ updateMethods[updateMethod as keyof typeof updateMethods].call(this, payload);
+ }
+ }
- this._ecEventProcessor.eventInfo = {
- targetEl: el,
- packedEvent: params,
- model: model,
- view: view
+ // Follow the rule of action batch
+ if (batched) {
+ eventObj = {
+ type: actionInfo.event || payloadType,
+ escapeConnect: escapeConnect,
+ batch: eventObjBatch
};
+ }
+ else {
+ eventObj = eventObjBatch[0] as ECEvent;
+ }
+
+ this[IN_MAIN_PROCESS] = false;
+
+ !silent && this._messageCenter.trigger(eventObj.type, eventObj);
+ };
- this.trigger(eveName, params);
+ flushPendingActions = function (this: ECharts, silent: boolean): void {
+ var pendingActions = this._pendingActions;
+ while (pendingActions.length) {
+ var payload = pendingActions.shift();
+ doDispatchAction.call(this, payload, silent);
}
};
- // 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.zrEventfulCallAtLast = true;
- this._zr.on(eveName, handler, this);
- }, this);
-
- each(eventActionMap, function (actionType, eventType) {
- this._messageCenter.on(eventType, function (event) {
- this.trigger(eventType, event);
- }, this);
- }, this);
-};
-/**
- * @return {boolean}
- */
-echartsProto.isDisposed = function () {
- return this._disposed;
-};
+ triggerUpdatedEvent = function (this: ECharts, silent): void {
+ !silent && this.trigger('updated');
+ };
-/**
- * Clear
- */
-echartsProto.clear = function () {
- if (this._disposed) {
- disposedWarning(this.id);
- return;
- }
- this.setOption({ series: [] }, true);
-};
+ /**
+ * 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 () {
+
+ ecIns.trigger('rendered');
+
+ // The `finished` event should not be triggered repeatly,
+ // so it should only be triggered when rendering indeed happend
+ // 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[OPTION_UPDATED]
+ && !ecIns._scheduler.unfinished
+ && !ecIns._pendingActions.length
+ ) {
+ ecIns.trigger('finished');
+ }
+ });
+ };
-/**
- * Dispose instance
- */
-echartsProto.dispose = function () {
- if (this._disposed) {
- disposedWarning(this.id);
- return;
- }
- this._disposed = true;
+ clearColorPalette = function (ecModel: GlobalModel): void {
+ ecModel.clearColorPalette();
+ ecModel.eachSeries(function (seriesModel) {
+ seriesModel.clearColorPalette();
+ });
+ };
- modelUtil.setAttribute(this.getDom(), DOM_ATTRIBUTE_KEY, '');
+ render = function (ecIns: ECharts, ecModel: GlobalModel, api: ExtensionAPI, payload: Payload): void {
- var api = this._api;
- var ecModel = this._model;
+ renderComponents(ecIns, ecModel, api, payload);
- each(this._componentsViews, function (component) {
- component.dispose(ecModel, api);
- });
- each(this._chartsViews, function (chart) {
- chart.dispose(ecModel, api);
- });
+ each(ecIns._chartsViews, function (chart: ChartView) {
+ chart.__alive = false;
+ });
- // Dispose after all views disposed
- this._zr.dispose();
+ renderSeries(ecIns, ecModel, api, payload);
- delete instances[this.id];
-};
+ // Remove groups of unrendered charts
+ each(ecIns._chartsViews, function (chart: ChartView) {
+ if (!chart.__alive) {
+ chart.remove(ecModel, api);
+ }
+ });
+ };
-zrUtil.mixin(ECharts, Eventful);
+ renderComponents = function (
+ ecIns: ECharts, ecModel: GlobalModel, api: ExtensionAPI, payload: Payload, dirtyList?: ComponentView[]
+ ): void {
+ each(dirtyList || ecIns._componentsViews, function (componentView: ComponentView) {
+ var componentModel = componentView.__model;
+ componentView.render(componentModel, ecModel, api, payload);
-function disposedWarning(id) {
- if (__DEV__) {
- console.warn('Instance ' + id + ' has been disposed');
- }
-}
+ updateZ(componentModel, componentView);
+ });
+ };
-function updateHoverLayerStatus(ecIns, ecModel) {
- var zr = ecIns._zr;
- var storage = zr.storage;
- var elCount = 0;
+ /**
+ * Render each chart and component
+ */
+ renderSeries = function (
+ ecIns: ECharts,
+ ecModel: GlobalModel,
+ api: ExtensionAPI,
+ payload: Payload | 'remain',
+ dirtyMap?: {[uid: string]: any}
+ ): void {
+ // Render all charts
+ var scheduler = ecIns._scheduler;
+ var unfinished: number;
+ ecModel.eachSeries(function (seriesModel) {
+ var chartView = ecIns._chartsMap[seriesModel.__viewId];
+ chartView.__alive = true;
+
+ var renderTask = chartView.renderTask;
+ scheduler.updatePayload(renderTask, payload);
+
+ if (dirtyMap && dirtyMap.get(seriesModel.uid)) {
+ renderTask.dirty();
+ }
- storage.traverse(function (el) {
- elCount++;
- });
+ unfinished |= +renderTask.perform(scheduler.getPerformArgs(renderTask));
- if (elCount > ecModel.get('hoverLayerThreshold') && !env.node) {
- ecModel.eachSeries(function (seriesModel) {
- if (seriesModel.preventUsingHoverLayer) {
- return;
- }
- var chartView = ecIns._chartsMap[seriesModel.__viewId];
- if (chartView.__alive) {
- chartView.group.traverse(function (el) {
- // Don't switch back.
- el.useHoverLayer = true;
- });
- }
- });
- }
-}
+ chartView.group.silent = !!seriesModel.get('silent');
-/**
- * Update chart progressive and blend.
- * @param {module:echarts/model/Series|module:echarts/model/Component} model
- * @param {module:echarts/view/Component|module:echarts/view/Chart} view
- */
-function updateBlend(seriesModel, chartView) {
- var blendMode = seriesModel.get('blendMode') || null;
- if (__DEV__) {
- if (!env.canvasSupported && blendMode && blendMode !== 'source-over') {
- console.warn('Only canvas support blendMode');
- }
- }
- chartView.group.traverse(function (el) {
- // FIXME marker and other components
- if (!el.isGroup) {
- // Only set if blendMode is changed. In case element is incremental and don't wan't to rerender.
- if (el.style.blend !== blendMode) {
- el.setStyle('blend', blendMode);
- }
- }
- if (el.eachPendingDisplayable) {
- el.eachPendingDisplayable(function (displayable) {
- displayable.setStyle('blend', blendMode);
+ updateZ(seriesModel, chartView);
+
+ updateBlend(seriesModel, chartView);
});
- }
- });
-}
+ scheduler.unfinished |= unfinished;
-/**
- * @param {module:echarts/model/Series|module:echarts/model/Component} model
- * @param {module:echarts/view/Component|module:echarts/view/Chart} view
- */
-function updateZ(model, view) {
- var z = model.get('z');
- var zlevel = model.get('zlevel');
- // Set z and zlevel
- view.group.traverse(function (el) {
- if (el.type !== 'group') {
- z != null && (el.z = z);
- zlevel != null && (el.zlevel = zlevel);
- }
- });
-}
+ // If use hover layer
+ updateHoverLayerStatus(ecIns, ecModel);
-function createExtensionAPI(ecInstance) {
- var coordSysMgr = ecInstance._coordSysMgr;
- return zrUtil.extend(new ExtensionAPI(ecInstance), {
- // Inject methods
- getCoordinateSystems: zrUtil.bind(
- coordSysMgr.getCoordinateSystems, coordSysMgr
- ),
- getComponentByElement: function (el) {
- while (el) {
- var modelInfo = el.__ecComponentInfo;
- if (modelInfo != null) {
- return ecInstance._model.getComponent(modelInfo.mainType, modelInfo.index);
- }
- el = el.parent;
- }
- }
- });
-}
+ // Add aria
+ aria(ecIns._zr.dom, ecModel);
+ };
+ performPostUpdateFuncs = function (ecModel: GlobalModel, api: ExtensionAPI): void {
+ each(postUpdateFuncs, function (func) {
+ func(ecModel, api);
+ });
+ };
-/**
- * @class
- * Usage of query:
- * `chart.on('click', query, handler);`
- * The `query` can be:
- * + The component type query string, only `mainType` or `mainType.subType`,
- * like: 'xAxis', 'series', 'xAxis.category' or 'series.line'.
- * + The component query object, like:
- * `{seriesIndex: 2}`, `{seriesName: 'xx'}`, `{seriesId: 'some'}`,
- * `{xAxisIndex: 2}`, `{xAxisName: 'xx'}`, `{xAxisId: 'some'}`.
- * + The data query object, like:
- * `{dataIndex: 123}`, `{dataType: 'link'}`, `{name: 'some'}`.
- * + The other query object (cmponent customized query), like:
- * `{element: 'some'}` (only available in custom series).
- *
- * Caveat: If a prop in the `query` object is `null/undefined`, it is the
- * same as there is no such prop in the `query` object.
- */
-function EventProcessor() {
- // These info required: targetEl, packedEvent, model, view
- this.eventInfo;
-}
-EventProcessor.prototype = {
- constructor: EventProcessor,
-
- normalizeQuery: function (query) {
- var cptQuery = {};
- var dataQuery = {};
- var otherQuery = {};
-
- // `query` is `mainType` or `mainType.subType` of component.
- if (zrUtil.isString(query)) {
- var condCptType = parseClassType(query);
- // `.main` and `.sub` may be ''.
- cptQuery.mainType = condCptType.main || null;
- cptQuery.subType = condCptType.sub || null;
- }
- // `query` is an object, convert to {mainType, index, name, id}.
- else {
- // `xxxIndex`, `xxxName`, `xxxId`, `name`, `dataIndex`, `dataType` is reserved,
- // can not be used in `compomentModel.filterForExposedEvent`.
- var suffixes = ['Index', 'Name', 'Id'];
- var dataKeys = {name: 1, dataIndex: 1, dataType: 1};
- zrUtil.each(query, function (val, key) {
- var reserved = false;
- for (var i = 0; i < suffixes.length; i++) {
- var propSuffix = suffixes[i];
- var suffixPos = key.lastIndexOf(propSuffix);
- if (suffixPos > 0 && suffixPos === key.length - propSuffix.length) {
- var mainType = key.slice(0, suffixPos);
- // Consider `dataIndex`.
- if (mainType !== 'data') {
- cptQuery.mainType = mainType;
- cptQuery[propSuffix.toLowerCase()] = val;
- reserved = true;
- }
+ updateHoverLayerStatus = function (ecIns: ECharts, ecModel: GlobalModel): void {
+ var zr = ecIns._zr;
+ var storage = zr.storage;
+ var elCount = 0;
+
+ storage.traverse(function (el) {
+ elCount++;
+ });
+
+ if (elCount > ecModel.get('hoverLayerThreshold') && !env.node) {
+ ecModel.eachSeries(function (seriesModel) {
+ if (seriesModel.preventUsingHoverLayer) {
+ return;
+ }
+ var chartView = ecIns._chartsMap[seriesModel.__viewId];
+ if (chartView.__alive) {
+ chartView.group.traverse(function (el: ECElement) {
+ // Don't switch back.
+ el.useHoverLayer = true;
+ });
+ }
+ });
+ }
+ };
+
+ /**
+ * Update chart progressive and blend.
+ */
+ updateBlend = function (seriesModel: SeriesModel, chartView: ChartView): void {
+ var blendMode = seriesModel.get('blendMode') || null;
+ if (__DEV__) {
+ if (!env.canvasSupported && blendMode && blendMode !== 'source-over') {
+ console.warn('Only canvas support blendMode');
+ }
+ }
+ chartView.group.traverse(function (el: Displayable) {
+ // FIXME marker and other components
+ if (!el.isGroup) {
+ // Only set if blendMode is changed. In case element is incremental and don't wan't to rerender.
+ if (el.style.blend !== blendMode) {
+ el.setStyle('blend', blendMode);
}
}
- if (dataKeys.hasOwnProperty(key)) {
- dataQuery[key] = val;
- reserved = true;
+ if ((el as IncrementalDisplayable).eachPendingDisplayable) {
+ (el as IncrementalDisplayable).eachPendingDisplayable(function (displayable) {
+ displayable.setStyle('blend', blendMode);
+ });
}
- if (!reserved) {
- otherQuery[key] = val;
+ });
+ };
+
+ updateZ = function (model: ComponentModel, view: ComponentView | ChartView): void {
+ var z = model.get('z');
+ var zlevel = model.get('zlevel');
+ // Set z and zlevel
+ view.group.traverse(function (el: Displayable) {
+ if (el.type !== 'group') {
+ z != null && (el.z = z);
+ zlevel != null && (el.zlevel = zlevel);
}
});
- }
+ };
- return {
- cptQuery: cptQuery,
- dataQuery: dataQuery,
- otherQuery: otherQuery
+ createExtensionAPI = function (ecIns: ECharts): ExtensionAPI {
+ return new (class extends ExtensionAPI {
+ getCoordinateSystems(): CoordinateSystem[] {
+ return ecIns._coordSysMgr.getCoordinateSystems();
+ }
+ getComponentByElement(el: Element) {
+ while (el) {
+ var modelInfo = (el as ViewRootGroup).__ecComponentInfo;
+ if (modelInfo != null) {
+ return ecIns._model.getComponent(modelInfo.mainType, modelInfo.index);
+ }
+ el = el.parent;
+ }
+ }
+ })(ecIns);
};
- },
- filter: function (eventType, query, args) {
- // They should be assigned before each trigger call.
- var eventInfo = this.eventInfo;
+ enableConnect = function (chart: ECharts): void {
- if (!eventInfo) {
- return true;
- }
+ function updateConnectedChartsStatus(charts: ECharts[], status: ConnectStatus) {
+ for (var i = 0; i < charts.length; i++) {
+ var otherChart = charts[i];
+ otherChart[CONNECT_STATUS_KEY] = status;
+ }
+ }
- var targetEl = eventInfo.targetEl;
- var packedEvent = eventInfo.packedEvent;
- var model = eventInfo.model;
- var view = eventInfo.view;
+ each(eventActionMap, function (actionType, eventType) {
+ chart._messageCenter.on(eventType, function (event) {
+ if (connectedGroups[chart.group] && chart[CONNECT_STATUS_KEY] !== CONNECT_STATUS_PENDING) {
+ if (event && event.escapeConnect) {
+ return;
+ }
- // For event like 'globalout'.
- if (!model || !view) {
- return true;
- }
+ var action = chart.makeActionFromEvent(event);
+ var 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);
+ }
+ });
+ });
+ };
- var cptQuery = query.cptQuery;
- var dataQuery = query.dataQuery;
-
- return check(cptQuery, model, 'mainType')
- && check(cptQuery, model, 'subType')
- && check(cptQuery, model, 'index', 'componentIndex')
- && check(cptQuery, model, 'name')
- && check(cptQuery, model, 'id')
- && check(dataQuery, packedEvent, 'name')
- && check(dataQuery, packedEvent, 'dataIndex')
- && check(dataQuery, packedEvent, 'dataType')
- && (!view.filterForExposedEvent || view.filterForExposedEvent(
- eventType, query.otherQuery, targetEl, packedEvent
- ));
-
- function check(query, host, prop, propOnHost) {
- return query[prop] == null || host[propOnHost || prop] === query[prop];
- }
- },
+ })()
+}
- afterTrigger: function () {
- // Make sure the eventInfo wont be used in next trigger.
- this.eventInfo = null;
- }
+
+// ---------------------------------------
+// Internal method names for class ECharts
+// ---------------------------------------
+var prepare: (ecIns: ECharts) => void;
+var prepareView: (ecIns: ECharts, isComponent: boolean) => void;
+var updateDirectly: (
+ ecIns: ECharts, method: string, payload: Payload, mainType: ComponentMainType, subType?: ComponentSubType
+) => void;
+type UpdateMethod = (this: ECharts, payload?: Payload) => void
+var updateMethods: {
+ prepareAndUpdate: UpdateMethod,
+ update: UpdateMethod,
+ updateTransform: UpdateMethod,
+ updateView: UpdateMethod,
+ updateVisual: UpdateMethod,
+ updateLayout: UpdateMethod
};
+var doConvertPixel: (ecIns: ECharts, methodName: string, finder: ModelFinder, value: any) => any;
+var updateStreamModes: (ecIns: ECharts, ecModel: GlobalModel) => void;
+var doDispatchAction: (this: ECharts, payload: Payload, silent: boolean) => void;
+var flushPendingActions: (this: ECharts, silent: boolean) => void;
+var triggerUpdatedEvent: (this: ECharts, silent: boolean) => void;
+var bindRenderedEvent: (zr: zrender.ZRenderType, ecIns: ECharts) => void;
+var clearColorPalette: (ecModel: GlobalModel) => void;
+var render: (ecIns: ECharts, ecModel: GlobalModel, api: ExtensionAPI, payload: Payload) => void;
+var renderComponents: (
+ ecIns: ECharts, ecModel: GlobalModel, api: ExtensionAPI, payload: Payload, dirtyList?: ComponentView[]
+) => void;
+var renderSeries: (
+ ecIns: ECharts,
+ ecModel: GlobalModel,
+ api: ExtensionAPI,
+ payload: Payload | 'remain',
+ dirtyMap?: {[uid: string]: any}
+) => void;
+var performPostUpdateFuncs: (ecModel: GlobalModel, api: ExtensionAPI) => void;
+var updateHoverLayerStatus: (ecIns: ECharts, ecModel: GlobalModel) => void;
+var updateBlend: (seriesModel: SeriesModel, chartView: ChartView) => void;
+var updateZ: (model: ComponentModel, view: ComponentView | ChartView) => void;
+var createExtensionAPI: (ecIns: ECharts) => ExtensionAPI;
+var enableConnect: (chart: ECharts) => void;
+
+
+
+interface ECharts extends Eventful {}
+zrUtil.tsMixin(ECharts, Eventful);
+var echartsProto = ECharts.prototype;
+echartsProto.on = createRegisterEventWithLowercaseECharts('on');
+echartsProto.off = createRegisterEventWithLowercaseECharts('off');
+// echartsProto.one = createRegisterEventWithLowercaseECharts('one');
-/**
- * @type {Object} key: actionType.
- * @inner
- */
-var actions = {};
+// /**
+// * Encode visual infomation from data after data processing
+// *
+// * @param {module:echarts/model/Global} ecModel
+// * @param {object} layout
+// * @param {boolean} [layoutFilter] `true`: only layout,
+// * `false`: only not layout,
+// * `null`/`undefined`: all.
+// * @param {string} taskBaseTag
+// * @private
+// */
+// function startVisualEncoding(ecIns, ecModel, api, payload, layoutFilter) {
+// each(visualFuncs, function (visual, index) {
+// var isLayout = visual.isLayout;
+// if (layoutFilter == null
+// || (layoutFilter === false && !isLayout)
+// || (layoutFilter === true && isLayout)
+// ) {
+// visual.func(ecModel, api, payload);
+// }
+// });
+// }
-/**
- * Map eventType to actionType
- * @type {Object}
- */
-var eventActionMap = {};
-/**
- * Data processor functions of each stage
- * @type {Array.<Object.<string, Function>>}
- * @inner
- */
-var dataProcessorFuncs = [];
+var MOUSE_EVENT_NAMES = [
+ 'click', 'dblclick', 'mouseover', 'mouseout', 'mousemove',
+ 'mousedown', 'mouseup', 'globalout', 'contextmenu'
+];
-/**
- * @type {Array.<Function>}
- * @inner
- */
-var optionPreprocessorFuncs = [];
+function disposedWarning(id: string): void {
+ if (__DEV__) {
+ console.warn('Instance ' + id + ' has been disposed');
+ }
+}
-/**
- * @type {Array.<Function>}
- * @inner
- */
-var postUpdateFuncs = [];
-/**
- * Visual encoding functions of each stage
- * @type {Array.<Object.<string, Function>>}
- */
-var visualFuncs = [];
+var actions: {
+ [actionType: string]: {
+ action: ActionHandler,
+ actionInfo: ActionInfo
+ }
+} = {};
/**
- * Theme storage
- * @type {Object.<key, Object>}
- */
-var themeStorage = {};
-/**
- * Loading effects
+ * Map eventType to actionType
*/
-var loadingEffects = {};
+var eventActionMap: {[eventType: string]: string} = {};
-var instances = {};
-var connectedGroups = {};
+var dataProcessorFuncs: StageHandler[] = [];
-var idBase = new Date() - 0;
-var groupIdBase = new Date() - 0;
-var DOM_ATTRIBUTE_KEY = '_echarts_instance_';
+var optionPreprocessorFuncs: OptionPreprocessor[] = [];
-function enableConnect(chart) {
- var STATUS_PENDING = 0;
- var STATUS_UPDATING = 1;
- var STATUS_UPDATED = 2;
- var STATUS_KEY = '__connectUpdateStatus';
+var postUpdateFuncs: PostUpdater[] = [];
- function updateConnectedChartsStatus(charts, status) {
- for (var i = 0; i < charts.length; i++) {
- var otherChart = charts[i];
- otherChart[STATUS_KEY] = status;
- }
- }
+var visualFuncs: StageHandler[] = [];
- each(eventActionMap, function (actionType, eventType) {
- chart._messageCenter.on(eventType, function (event) {
- if (connectedGroups[chart.group] && chart[STATUS_KEY] !== STATUS_PENDING) {
- if (event && event.escapeConnect) {
- return;
- }
+var themeStorage: {[themeName: string]: ThemeOption} = {};
- var action = chart.makeActionFromEvent(event);
- var otherCharts = [];
+var loadingEffects: {[effectName: string]: LoadingEffectCreator} = {};
- each(instances, function (otherChart) {
- if (otherChart !== chart && otherChart.group === chart.group) {
- otherCharts.push(otherChart);
- }
- });
+var instances: {[id: string]: ECharts} = {};
+var connectedGroups: {[groupId: string]: boolean} = {};
+
+var idBase: number = +(new Date()) - 0;
+var groupIdBase: number = +(new Date()) - 0;
+var DOM_ATTRIBUTE_KEY = '_echarts_instance_';
- updateConnectedChartsStatus(otherCharts, STATUS_PENDING);
- each(otherCharts, function (otherChart) {
- if (otherChart[STATUS_KEY] !== STATUS_UPDATING) {
- otherChart.dispatchAction(action);
- }
- });
- updateConnectedChartsStatus(otherCharts, STATUS_UPDATED);
- }
- });
- });
-}
/**
- * @param {HTMLElement} dom
- * @param {Object} [theme]
- * @param {Object} opts
- * @param {number} [opts.devicePixelRatio] Use window.devicePixelRatio by default
- * @param {string} [opts.renderer] Can choose 'canvas' or 'svg' to render the chart.
- * @param {number} [opts.width] Use clientWidth of the input `dom` by default.
- * Can be 'auto' (the same as null/undefined)
- * @param {number} [opts.height] Use clientHeight of the input `dom` by default.
- * Can be 'auto' (the same as null/undefined)
+ * @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)
*/
-export function init(dom, theme, opts) {
+export function init(
+ dom: HTMLElement,
+ theme?: string | object,
+ opts?: {
+ renderer?: RendererType,
+ devicePixelRatio?: number,
+ width?: number,
+ height?: number
+ }
+): ECharts {
if (__DEV__) {
// Check version
- if ((zrender.version.replace('.', '') - 0) < (dependencies.zrender.replace('.', '') - 0)) {
+ if (+zrender.version.replace('.', '') < +dependencies.zrender.replace('.', '')) {
throw new Error(
'zrender/src ' + zrender.version
+ ' is too old for ECharts ' + version
@@ -2088,9 +1954,23 @@ export function init(dom, theme, opts) {
}
/**
- * @return {string|Array.<module:echarts~ECharts>} groupId
- */
-export function connect(groupId) {
+ * @usage
+ * (A)
+ * ```js
+ * var chart1 = echarts.init(dom1);
+ * var chart2 = echarts.init(dom2);
+ * chart1.group = 'xxx';
+ * chart2.group = 'xxx';
+ * echarts.connect('xxx');
+ * ```
+ * (B)
+ * ```js
+ * var chart1 = echarts.init(dom1);
+ * var chart2 = echarts.init(dom2);
+ * echarts.connect('xxx', [chart1, chart2]);
+ * ```
+ */
+export function connect(groupId: string | ECharts[]): string {
// Is array of charts
if (zrUtil.isArray(groupId)) {
var charts = groupId;
@@ -2103,31 +1983,29 @@ export function connect(groupId) {
});
groupId = groupId || ('g_' + groupIdBase++);
each(charts, function (chart) {
- chart.group = groupId;
+ chart.group = groupId as string;
});
}
- connectedGroups[groupId] = true;
- return groupId;
+ connectedGroups[groupId as string] = true;
+ return groupId as string;
}
/**
- * @DEPRECATED
- * @return {string} groupId
+ * @deprecated
*/
-export function disConnect(groupId) {
+export function disConnect(groupId: string): void {
connectedGroups[groupId] = false;
}
/**
- * @return {string} groupId
+ * Alias and backword compat
*/
export var disconnect = disConnect;
/**
* Dispose a chart instance
- * @param {module:echarts~ECharts|HTMLDomElement|string} chart
*/
-export function dispose(chart) {
+export function dispose(chart: ECharts | HTMLElement | string): void {
if (typeof chart === 'string') {
chart = instances[chart];
}
@@ -2140,42 +2018,32 @@ export function dispose(chart) {
}
}
-/**
- * @param {HTMLElement} dom
- * @return {echarts~ECharts}
- */
-export function getInstanceByDom(dom) {
+export function getInstanceByDom(dom: HTMLElement): ECharts {
return instances[modelUtil.getAttribute(dom, DOM_ATTRIBUTE_KEY)];
}
-/**
- * @param {string} key
- * @return {echarts~ECharts}
- */
-export function getInstanceById(key) {
+export function getInstanceById(key: string): ECharts {
return instances[key];
}
/**
* Register theme
*/
-export function registerTheme(name, theme) {
+export function registerTheme(name: string, theme: ThemeOption): void {
themeStorage[name] = theme;
}
/**
* Register option preprocessor
- * @param {Function} preprocessorFunc
*/
-export function registerPreprocessor(preprocessorFunc) {
+export function registerPreprocessor(preprocessorFunc: OptionPreprocessor): void {
optionPreprocessorFuncs.push(preprocessorFunc);
}
-/**
- * @param {number} [priority=1000]
- * @param {Object|Function} processor
- */
-export function registerProcessor(priority, processor) {
+export function registerProcessor(
+ priority: number | StageHandlerInput | StageHandlerOverallReset,
+ processor?: StageHandlerInput | StageHandlerOverallReset
+): void {
normalizeRegister(dataProcessorFuncs, priority, processor, PRIORITY_PROCESSOR_FILTER);
}
@@ -2183,12 +2051,12 @@ export function registerProcessor(priority, processor) {
* Register postUpdater
* @param {Function} postUpdateFunc
*/
-export function registerPostUpdate(postUpdateFunc) {
+export function registerPostUpdate(postUpdateFunc: PostUpdater): void {
postUpdateFuncs.push(postUpdateFunc);
}
/**
- * Usage:
+ * @usage
* registerAction('someAction', 'someEvent', function () { ... });
* registerAction('someAction', function () { ... });
* registerAction(
@@ -2203,36 +2071,44 @@ export function registerPostUpdate(postUpdateFunc) {
* @param {string} [eventName]
* @param {Function} action
*/
-export function registerAction(actionInfo, eventName, 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 (typeof eventName === 'function') {
action = eventName;
eventName = '';
}
var actionType = isObject(actionInfo)
- ? actionInfo.type
+ ? (actionInfo as ActionInfo).type
: ([actionInfo, actionInfo = {
event: eventName
- }][0]);
+ } as ActionInfo][0]);
// Event name is all lowercase
- actionInfo.event = (actionInfo.event || actionType).toLowerCase();
- eventName = actionInfo.event;
+ (actionInfo as ActionInfo).event = (
+ (actionInfo as ActionInfo).event || actionType as string
+ ).toLowerCase();
+ eventName = (actionInfo as ActionInfo).event;
// Validate action type and event name.
- assert(ACTION_REG.test(actionType) && ACTION_REG.test(eventName));
+ assert(ACTION_REG.test(actionType as string) && ACTION_REG.test(eventName));
- if (!actions[actionType]) {
- actions[actionType] = {action: action, actionInfo: actionInfo};
+ if (!actions[actionType as string]) {
+ actions[actionType as string] = {action: action, actionInfo: actionInfo as ActionInfo};
}
- eventActionMap[eventName] = actionType;
+ eventActionMap[eventName as string] = actionType as string;
}
-/**
- * @param {string} type
- * @param {*} CoordinateSystem
- */
-export function registerCoordinateSystem(type, CoordinateSystem) {
- CoordinateSystemManager.register(type, CoordinateSystem);
+export function registerCoordinateSystem(
+ type: string,
+ coordSysCreator: CoordinateSystemCreator
+): void {
+ CoordinateSystemManager.register(type, coordSysCreator);
}
/**
@@ -2240,12 +2116,12 @@ export function registerCoordinateSystem(type, CoordinateSystem) {
* @param {string} type
* @return {Array.<string|Object>}
*/
-export function getCoordinateSystemDimensions(type) {
+export function getCoordinateSystemDimensions(type: string): DimensionDefinitionLoose[] {
var coordSysCreator = CoordinateSystemManager.get(type);
if (coordSysCreator) {
return coordSysCreator.getDimensionsInfo
- ? coordSysCreator.getDimensionsInfo()
- : coordSysCreator.dimensions.slice();
+ ? coordSysCreator.getDimensionsInfo()
+ : coordSysCreator.dimensions.slice();
}
}
@@ -2253,28 +2129,30 @@ export function getCoordinateSystemDimensions(type) {
* 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
- *
- * @param {number} [priority=1000]
- * @param {Function} layoutTask
*/
-export function registerLayout(priority, layoutTask) {
+export function registerLayout(
+ priority: number | StageHandlerInput | StageHandlerOverallReset,
+ layoutTask?: StageHandlerInput | StageHandlerOverallReset
+): void {
normalizeRegister(visualFuncs, priority, layoutTask, PRIORITY_VISUAL_LAYOUT, 'layout');
}
-/**
- * @param {number} [priority=3000]
- * @param {module:echarts/stream/Task} visualTask
- */
-export function registerVisual(priority, visualTask) {
+export function registerVisual(
+ priority: number | StageHandlerInput | StageHandlerOverallReset,
+ visualTask?: StageHandlerInput | StageHandlerOverallReset
+): void {
normalizeRegister(visualFuncs, priority, visualTask, PRIORITY_VISUAL_CHART, 'visual');
}
-/**
- * @param {Object|Function} fn: {seriesType, createOnAllSeries, performRawSeries, reset}
- */
-function normalizeRegister(targetList, priority, fn, defaultPriority, visualType) {
+function normalizeRegister(
+ targetList: StageHandlerInput[],
+ priority: number | StageHandlerInput | StageHandlerOverallReset,
+ fn: StageHandlerInput | StageHandlerOverallReset,
+ defaultPriority: number,
+ visualType?: VisualType
+): void {
if (isFunction(priority) || isObject(priority)) {
- fn = priority;
+ fn = priority as (StageHandlerInput | StageHandlerOverallReset);
priority = defaultPriority;
}
@@ -2284,7 +2162,7 @@ function normalizeRegister(targetList, priority, fn, defaultPriority, visualType
}
// Check duplicate
each(targetList, function (wrap) {
- assert(wrap.__raw !== fn);
+ assert((wrap as StageHandler).__raw !== fn);
});
}
@@ -2293,69 +2171,51 @@ function normalizeRegister(targetList, priority, fn, defaultPriority, visualType
stageHandler.__prio = priority;
stageHandler.__raw = fn;
targetList.push(stageHandler);
-
- return stageHandler;
}
-/**
- * @param {string} name
- */
-export function registerLoading(name, loadingFx) {
+export function registerLoading(
+ name: string,
+ loadingFx: LoadingEffectCreator
+): void {
loadingEffects[name] = loadingFx;
}
-/**
- * @param {Object} opts
- * @param {string} [superClass]
- */
-export function extendComponentModel(opts/*, superClass*/) {
+export function extendComponentModel(proto: object): ComponentModel {
// var Clazz = ComponentModel;
// if (superClass) {
// var classType = parseClassType(superClass);
// Clazz = ComponentModel.getClass(classType.main, classType.sub, true);
// }
- return ComponentModel.extend(opts);
+ return (ComponentModel as ComponentModelConstructor).extend(proto) as any;
}
-/**
- * @param {Object} opts
- * @param {string} [superClass]
- */
-export function extendComponentView(opts/*, superClass*/) {
+export function extendComponentView(proto: object): ChartView {
// var Clazz = ComponentView;
// if (superClass) {
// var classType = parseClassType(superClass);
// Clazz = ComponentView.getClass(classType.main, classType.sub, true);
// }
- return ComponentView.extend(opts);
+ return (ComponentView as ComponentViewConstructor).extend(proto) as any;
}
-/**
- * @param {Object} opts
- * @param {string} [superClass]
- */
-export function extendSeriesModel(opts/*, superClass*/) {
+export function extendSeriesModel(proto: object): SeriesModel {
// var Clazz = SeriesModel;
// if (superClass) {
// superClass = 'series.' + superClass.replace('series.', '');
// var classType = parseClassType(superClass);
// Clazz = ComponentModel.getClass(classType.main, classType.sub, true);
// }
- return SeriesModel.extend(opts);
+ return (SeriesModel as SeriesModelConstructor).extend(proto) as any;
}
-/**
- * @param {Object} opts
- * @param {string} [superClass]
- */
-export function extendChartView(opts/*, superClass*/) {
+export function extendChartView(proto: object): ChartView {
// var Clazz = ChartView;
// if (superClass) {
// superClass = superClass.replace('series.', '');
// var classType = parseClassType(superClass);
// Clazz = ChartView.getClass(classType.main, true);
// }
- return ChartView.extend(opts);
+ return (ChartView as ChartViewConstructor).extend(proto) as any;
}
/**
@@ -2365,7 +2225,6 @@ export function extendChartView(opts/*, superClass*/) {
*
* Be careful of using it in the browser.
*
- * @param {Function} creator
* @example
* var Canvas = require('canvas');
* var echarts = require('echarts');
@@ -2374,47 +2233,26 @@ export function extendChartView(opts/*, superClass*/) {
* return new Canvas(32, 32);
* });
*/
-export function setCanvasCreator(creator) {
+export function setCanvasCreator(creator: () => HTMLCanvasElement): void {
zrUtil.$override('createCanvas', creator);
}
/**
- * @param {string} mapName
- * @param {Array.<Object>|Object|string} geoJson
- * @param {Object} [specialAreas]
- *
- * @example GeoJSON
- * $.get('USA.json', function (geoJson) {
- * echarts.registerMap('USA', geoJson);
- * // Or
- * echarts.registerMap('USA', {
- * geoJson: geoJson,
- * specialAreas: {}
- * })
- * });
- *
- * $.get('airport.svg', function (svg) {
- * echarts.registerMap('airport', {
- * svg: svg
- * }
- * });
- *
- * echarts.registerMap('eu', [
- * {svg: eu-topographic.svg},
- * {geoJSON: eu.json}
- * ])
+ * The parameters and usage: see `mapDataStorage.registerMap`.
+ * Compatible with previous `echarts.registerMap`.
*/
-export function registerMap(mapName, geoJson, specialAreas) {
+export function registerMap(
+ mapName: string,
+ geoJson: GeoMapDefinition | GeoMapDefinition[] | GeoMapGeoJSONSource,
+ specialAreas?: GeoSpecialAreas
+): void {
mapDataStorage.registerMap(mapName, geoJson, specialAreas);
}
-/**
- * @param {string} mapName
- * @return {Object}
- */
-export function getMap(mapName) {
+export function getMap(mapName: string) {
// For backward compatibility, only return the first one.
var records = mapDataStorage.retrieveMap(mapName);
+ // FIXME support SVG
return records && records[0] && {
geoJson: records[0].geoJSON,
specialAreas: records[0].specialAreas
@@ -2447,3 +2285,5 @@ registerTheme('dark', darkTheme);
// For backward compatibility, where the namespace `dataTool` will
// be mounted on `echarts` is the extension `dataTool` is imported.
export var dataTool = {};
+
+export interface EChartsType extends ECharts {}
diff --git a/src/export.ts b/src/export.ts
index 6ddd62e..8313fc8 100644
--- a/src/export.ts
+++ b/src/export.ts
@@ -58,7 +58,7 @@ zrUtil.each(
'extend', 'defaults', 'clone', 'merge'
],
function (name) {
- ecUtil[name] = zrUtil[name];
+ (ecUtil as any)[name] = (zrUtil as any)[name];
}
);
export {ecUtil as util};
@@ -91,7 +91,7 @@ zrUtil.each(
'BoundingRect'
],
function (name) {
- graphic[name] = graphicUtil[name];
+ (graphic as any)[name] = (graphicUtil as any)[name];
}
);
export {graphic};
diff --git a/src/loading/default.ts b/src/loading/default.ts
index 13d90d5..3ace478 100644
--- a/src/loading/default.ts
+++ b/src/loading/default.ts
@@ -19,6 +19,8 @@
import * as zrUtil from 'zrender/src/core/util';
import * as graphic from '../util/graphic';
+import { LoadingEffect } from '../util/types';
... 6793 lines suppressed ...
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org