632 lines
20 KiB
JavaScript
632 lines
20 KiB
JavaScript
|
||
/*
|
||
* 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.
|
||
*/
|
||
|
||
var _config = require("../../config");
|
||
|
||
var __DEV__ = _config.__DEV__;
|
||
|
||
var echarts = require("../../echarts");
|
||
|
||
var zrUtil = require("zrender/lib/core/util");
|
||
|
||
var env = require("zrender/lib/core/env");
|
||
|
||
var modelUtil = require("../../util/model");
|
||
|
||
var helper = require("./helper");
|
||
|
||
var AxisProxy = require("./AxisProxy");
|
||
|
||
/*
|
||
* 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.
|
||
*/
|
||
var each = zrUtil.each;
|
||
var eachAxisDim = helper.eachAxisDim;
|
||
var DataZoomModel = echarts.extendComponentModel({
|
||
type: 'dataZoom',
|
||
dependencies: ['xAxis', 'yAxis', 'zAxis', 'radiusAxis', 'angleAxis', 'singleAxis', 'series'],
|
||
|
||
/**
|
||
* @protected
|
||
*/
|
||
defaultOption: {
|
||
zlevel: 0,
|
||
z: 4,
|
||
// Higher than normal component (z: 2).
|
||
orient: null,
|
||
// Default auto by axisIndex. Possible value: 'horizontal', 'vertical'.
|
||
xAxisIndex: null,
|
||
// Default the first horizontal category axis.
|
||
yAxisIndex: null,
|
||
// Default the first vertical category axis.
|
||
filterMode: 'filter',
|
||
// Possible values: 'filter' or 'empty' or 'weakFilter'.
|
||
// 'filter': data items which are out of window will be removed. This option is
|
||
// applicable when filtering outliers. For each data item, it will be
|
||
// filtered if one of the relevant dimensions is out of the window.
|
||
// 'weakFilter': data items which are out of window will be removed. This option
|
||
// is applicable when filtering outliers. For each data item, it will be
|
||
// filtered only if all of the relevant dimensions are out of the same
|
||
// side of the window.
|
||
// 'empty': data items which are out of window will be set to empty.
|
||
// This option is applicable when user should not neglect
|
||
// that there are some data items out of window.
|
||
// 'none': Do not filter.
|
||
// Taking line chart as an example, line will be broken in
|
||
// the filtered points when filterModel is set to 'empty', but
|
||
// be connected when set to 'filter'.
|
||
throttle: null,
|
||
// Dispatch action by the fixed rate, avoid frequency.
|
||
// default 100. Do not throttle when use null/undefined.
|
||
// If animation === true and animationDurationUpdate > 0,
|
||
// default value is 100, otherwise 20.
|
||
start: 0,
|
||
// Start percent. 0 ~ 100
|
||
end: 100,
|
||
// End percent. 0 ~ 100
|
||
startValue: null,
|
||
// Start value. If startValue specified, start is ignored.
|
||
endValue: null,
|
||
// End value. If endValue specified, end is ignored.
|
||
minSpan: null,
|
||
// 0 ~ 100
|
||
maxSpan: null,
|
||
// 0 ~ 100
|
||
minValueSpan: null,
|
||
// The range of dataZoom can not be smaller than that.
|
||
maxValueSpan: null,
|
||
// The range of dataZoom can not be larger than that.
|
||
rangeMode: null // Array, can be 'value' or 'percent'.
|
||
|
||
},
|
||
|
||
/**
|
||
* @override
|
||
*/
|
||
init: function (option, parentModel, ecModel) {
|
||
/**
|
||
* key like x_0, y_1
|
||
* @private
|
||
* @type {Object}
|
||
*/
|
||
this._dataIntervalByAxis = {};
|
||
/**
|
||
* @private
|
||
*/
|
||
|
||
this._dataInfo = {};
|
||
/**
|
||
* key like x_0, y_1
|
||
* @private
|
||
*/
|
||
|
||
this._axisProxies = {};
|
||
/**
|
||
* @readOnly
|
||
*/
|
||
|
||
this.textStyleModel;
|
||
/**
|
||
* @private
|
||
*/
|
||
|
||
this._autoThrottle = true;
|
||
/**
|
||
* It is `[rangeModeForMin, rangeModeForMax]`.
|
||
* The optional values for `rangeMode`:
|
||
* + `'value'` mode: the axis extent will always be determined by
|
||
* `dataZoom.startValue` and `dataZoom.endValue`, despite
|
||
* how data like and how `axis.min` and `axis.max` are.
|
||
* + `'percent'` mode: `100` represents 100% of the `[dMin, dMax]`,
|
||
* where `dMin` is `axis.min` if `axis.min` specified, otherwise `data.extent[0]`,
|
||
* and `dMax` is `axis.max` if `axis.max` specified, otherwise `data.extent[1]`.
|
||
* Axis extent will be determined by the result of the percent of `[dMin, dMax]`.
|
||
*
|
||
* For example, when users are using dynamic data (update data periodically via `setOption`),
|
||
* if in `'value`' mode, the window will be kept in a fixed value range despite how
|
||
* data are appended, while if in `'percent'` mode, whe window range will be changed alone with
|
||
* the appended data (suppose `axis.min` and `axis.max` are not specified).
|
||
*
|
||
* @private
|
||
*/
|
||
|
||
this._rangePropMode = ['percent', 'percent'];
|
||
var inputRawOption = retrieveRawOption(option);
|
||
/**
|
||
* Suppose a "main process" start at the point that model prepared (that is,
|
||
* model initialized or merged or method called in `action`).
|
||
* We should keep the `main process` idempotent, that is, given a set of values
|
||
* on `option`, we get the same result.
|
||
*
|
||
* But sometimes, values on `option` will be updated for providing users
|
||
* a "final calculated value" (`dataZoomProcessor` will do that). Those value
|
||
* should not be the base/input of the `main process`.
|
||
*
|
||
* So in that case we should save and keep the input of the `main process`
|
||
* separately, called `settledOption`.
|
||
*
|
||
* For example, consider the case:
|
||
* (Step_1) brush zoom the grid by `toolbox.dataZoom`,
|
||
* where the original input `option.startValue`, `option.endValue` are earsed by
|
||
* calculated value.
|
||
* (Step)2) click the legend to hide and show a series,
|
||
* where the new range is calculated by the earsed `startValue` and `endValue`,
|
||
* which brings incorrect result.
|
||
*
|
||
* @readOnly
|
||
*/
|
||
|
||
this.settledOption = inputRawOption;
|
||
this.mergeDefaultAndTheme(option, ecModel);
|
||
this.doInit(inputRawOption);
|
||
},
|
||
|
||
/**
|
||
* @override
|
||
*/
|
||
mergeOption: function (newOption) {
|
||
var inputRawOption = retrieveRawOption(newOption); //FIX #2591
|
||
|
||
zrUtil.merge(this.option, newOption, true);
|
||
zrUtil.merge(this.settledOption, inputRawOption, true);
|
||
this.doInit(inputRawOption);
|
||
},
|
||
|
||
/**
|
||
* @protected
|
||
*/
|
||
doInit: function (inputRawOption) {
|
||
var thisOption = this.option; // Disable realtime view update if canvas is not supported.
|
||
|
||
if (!env.canvasSupported) {
|
||
thisOption.realtime = false;
|
||
}
|
||
|
||
this._setDefaultThrottle(inputRawOption);
|
||
|
||
updateRangeUse(this, inputRawOption);
|
||
var settledOption = this.settledOption;
|
||
each([['start', 'startValue'], ['end', 'endValue']], function (names, index) {
|
||
// start/end has higher priority over startValue/endValue if they
|
||
// both set, but we should make chart.setOption({endValue: 1000})
|
||
// effective, rather than chart.setOption({endValue: 1000, end: null}).
|
||
if (this._rangePropMode[index] === 'value') {
|
||
thisOption[names[0]] = settledOption[names[0]] = null;
|
||
} // Otherwise do nothing and use the merge result.
|
||
|
||
}, this);
|
||
this.textStyleModel = this.getModel('textStyle');
|
||
|
||
this._resetTarget();
|
||
|
||
this._giveAxisProxies();
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_giveAxisProxies: function () {
|
||
var axisProxies = this._axisProxies;
|
||
this.eachTargetAxis(function (dimNames, axisIndex, dataZoomModel, ecModel) {
|
||
var axisModel = this.dependentModels[dimNames.axis][axisIndex]; // If exists, share axisProxy with other dataZoomModels.
|
||
|
||
var axisProxy = axisModel.__dzAxisProxy || ( // Use the first dataZoomModel as the main model of axisProxy.
|
||
axisModel.__dzAxisProxy = new AxisProxy(dimNames.name, axisIndex, this, ecModel)); // FIXME
|
||
// dispose __dzAxisProxy
|
||
|
||
axisProxies[dimNames.name + '_' + axisIndex] = axisProxy;
|
||
}, this);
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_resetTarget: function () {
|
||
var thisOption = this.option;
|
||
|
||
var autoMode = this._judgeAutoMode();
|
||
|
||
eachAxisDim(function (dimNames) {
|
||
var axisIndexName = dimNames.axisIndex;
|
||
thisOption[axisIndexName] = modelUtil.normalizeToArray(thisOption[axisIndexName]);
|
||
}, this);
|
||
|
||
if (autoMode === 'axisIndex') {
|
||
this._autoSetAxisIndex();
|
||
} else if (autoMode === 'orient') {
|
||
this._autoSetOrient();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_judgeAutoMode: function () {
|
||
// Auto set only works for setOption at the first time.
|
||
// The following is user's reponsibility. So using merged
|
||
// option is OK.
|
||
var thisOption = this.option;
|
||
var hasIndexSpecified = false;
|
||
eachAxisDim(function (dimNames) {
|
||
// When user set axisIndex as a empty array, we think that user specify axisIndex
|
||
// but do not want use auto mode. Because empty array may be encountered when
|
||
// some error occured.
|
||
if (thisOption[dimNames.axisIndex] != null) {
|
||
hasIndexSpecified = true;
|
||
}
|
||
}, this);
|
||
var orient = thisOption.orient;
|
||
|
||
if (orient == null && hasIndexSpecified) {
|
||
return 'orient';
|
||
} else if (!hasIndexSpecified) {
|
||
if (orient == null) {
|
||
thisOption.orient = 'horizontal';
|
||
}
|
||
|
||
return 'axisIndex';
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_autoSetAxisIndex: function () {
|
||
var autoAxisIndex = true;
|
||
var orient = this.get('orient', true);
|
||
var thisOption = this.option;
|
||
var dependentModels = this.dependentModels;
|
||
|
||
if (autoAxisIndex) {
|
||
// Find axis that parallel to dataZoom as default.
|
||
var dimName = orient === 'vertical' ? 'y' : 'x';
|
||
|
||
if (dependentModels[dimName + 'Axis'].length) {
|
||
thisOption[dimName + 'AxisIndex'] = [0];
|
||
autoAxisIndex = false;
|
||
} else {
|
||
each(dependentModels.singleAxis, function (singleAxisModel) {
|
||
if (autoAxisIndex && singleAxisModel.get('orient', true) === orient) {
|
||
thisOption.singleAxisIndex = [singleAxisModel.componentIndex];
|
||
autoAxisIndex = false;
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
if (autoAxisIndex) {
|
||
// Find the first category axis as default. (consider polar)
|
||
eachAxisDim(function (dimNames) {
|
||
if (!autoAxisIndex) {
|
||
return;
|
||
}
|
||
|
||
var axisIndices = [];
|
||
var axisModels = this.dependentModels[dimNames.axis];
|
||
|
||
if (axisModels.length && !axisIndices.length) {
|
||
for (var i = 0, len = axisModels.length; i < len; i++) {
|
||
if (axisModels[i].get('type') === 'category') {
|
||
axisIndices.push(i);
|
||
}
|
||
}
|
||
}
|
||
|
||
thisOption[dimNames.axisIndex] = axisIndices;
|
||
|
||
if (axisIndices.length) {
|
||
autoAxisIndex = false;
|
||
}
|
||
}, this);
|
||
}
|
||
|
||
if (autoAxisIndex) {
|
||
// FIXME
|
||
// 这里是兼容ec2的写法(没指定xAxisIndex和yAxisIndex时把scatter和双数值轴折柱纳入dataZoom控制),
|
||
// 但是实际是否需要Grid.js#getScaleByOption来判断(考虑time,log等axis type)?
|
||
// If both dataZoom.xAxisIndex and dataZoom.yAxisIndex is not specified,
|
||
// dataZoom component auto adopts series that reference to
|
||
// both xAxis and yAxis which type is 'value'.
|
||
this.ecModel.eachSeries(function (seriesModel) {
|
||
if (this._isSeriesHasAllAxesTypeOf(seriesModel, 'value')) {
|
||
eachAxisDim(function (dimNames) {
|
||
var axisIndices = thisOption[dimNames.axisIndex];
|
||
var axisIndex = seriesModel.get(dimNames.axisIndex);
|
||
var axisId = seriesModel.get(dimNames.axisId);
|
||
var axisModel = seriesModel.ecModel.queryComponents({
|
||
mainType: dimNames.axis,
|
||
index: axisIndex,
|
||
id: axisId
|
||
})[0];
|
||
axisIndex = axisModel.componentIndex;
|
||
|
||
if (zrUtil.indexOf(axisIndices, axisIndex) < 0) {
|
||
axisIndices.push(axisIndex);
|
||
}
|
||
});
|
||
}
|
||
}, this);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_autoSetOrient: function () {
|
||
var dim; // Find the first axis
|
||
|
||
this.eachTargetAxis(function (dimNames) {
|
||
!dim && (dim = dimNames.name);
|
||
}, this);
|
||
this.option.orient = dim === 'y' ? 'vertical' : 'horizontal';
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_isSeriesHasAllAxesTypeOf: function (seriesModel, axisType) {
|
||
// FIXME
|
||
// 需要series的xAxisIndex和yAxisIndex都首先自动设置上。
|
||
// 例如series.type === scatter时。
|
||
var is = true;
|
||
eachAxisDim(function (dimNames) {
|
||
var seriesAxisIndex = seriesModel.get(dimNames.axisIndex);
|
||
var axisModel = this.dependentModels[dimNames.axis][seriesAxisIndex];
|
||
|
||
if (!axisModel || axisModel.get('type') !== axisType) {
|
||
is = false;
|
||
}
|
||
}, this);
|
||
return is;
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_setDefaultThrottle: function (inputRawOption) {
|
||
// When first time user set throttle, auto throttle ends.
|
||
if (inputRawOption.hasOwnProperty('throttle')) {
|
||
this._autoThrottle = false;
|
||
}
|
||
|
||
if (this._autoThrottle) {
|
||
var globalOption = this.ecModel.option;
|
||
this.option.throttle = globalOption.animation && globalOption.animationDurationUpdate > 0 ? 100 : 20;
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @public
|
||
*/
|
||
getFirstTargetAxisModel: function () {
|
||
var firstAxisModel;
|
||
eachAxisDim(function (dimNames) {
|
||
if (firstAxisModel == null) {
|
||
var indices = this.get(dimNames.axisIndex);
|
||
|
||
if (indices.length) {
|
||
firstAxisModel = this.dependentModels[dimNames.axis][indices[0]];
|
||
}
|
||
}
|
||
}, this);
|
||
return firstAxisModel;
|
||
},
|
||
|
||
/**
|
||
* @public
|
||
* @param {Function} callback param: axisModel, dimNames, axisIndex, dataZoomModel, ecModel
|
||
*/
|
||
eachTargetAxis: function (callback, context) {
|
||
var ecModel = this.ecModel;
|
||
eachAxisDim(function (dimNames) {
|
||
each(this.get(dimNames.axisIndex), function (axisIndex) {
|
||
callback.call(context, dimNames, axisIndex, this, ecModel);
|
||
}, this);
|
||
}, this);
|
||
},
|
||
|
||
/**
|
||
* @param {string} dimName
|
||
* @param {number} axisIndex
|
||
* @return {module:echarts/component/dataZoom/AxisProxy} If not found, return null/undefined.
|
||
*/
|
||
getAxisProxy: function (dimName, axisIndex) {
|
||
return this._axisProxies[dimName + '_' + axisIndex];
|
||
},
|
||
|
||
/**
|
||
* @param {string} dimName
|
||
* @param {number} axisIndex
|
||
* @return {module:echarts/model/Model} If not found, return null/undefined.
|
||
*/
|
||
getAxisModel: function (dimName, axisIndex) {
|
||
var axisProxy = this.getAxisProxy(dimName, axisIndex);
|
||
return axisProxy && axisProxy.getAxisModel();
|
||
},
|
||
|
||
/**
|
||
* If not specified, set to undefined.
|
||
*
|
||
* @public
|
||
* @param {Object} opt
|
||
* @param {number} [opt.start]
|
||
* @param {number} [opt.end]
|
||
* @param {number} [opt.startValue]
|
||
* @param {number} [opt.endValue]
|
||
*/
|
||
setRawRange: function (opt) {
|
||
var thisOption = this.option;
|
||
var settledOption = this.settledOption;
|
||
each([['start', 'startValue'], ['end', 'endValue']], function (names) {
|
||
// Consider the pair <start, startValue>:
|
||
// If one has value and the other one is `null/undefined`, we both set them
|
||
// to `settledOption`. This strategy enables the feature to clear the original
|
||
// value in `settledOption` to `null/undefined`.
|
||
// But if both of them are `null/undefined`, we do not set them to `settledOption`
|
||
// and keep `settledOption` with the original value. This strategy enables users to
|
||
// only set <end or endValue> but not set <start or startValue> when calling
|
||
// `dispatchAction`.
|
||
// The pair <end, endValue> is treated in the same way.
|
||
if (opt[names[0]] != null || opt[names[1]] != null) {
|
||
thisOption[names[0]] = settledOption[names[0]] = opt[names[0]];
|
||
thisOption[names[1]] = settledOption[names[1]] = opt[names[1]];
|
||
}
|
||
}, this);
|
||
updateRangeUse(this, opt);
|
||
},
|
||
|
||
/**
|
||
* @public
|
||
* @param {Object} opt
|
||
* @param {number} [opt.start]
|
||
* @param {number} [opt.end]
|
||
* @param {number} [opt.startValue]
|
||
* @param {number} [opt.endValue]
|
||
*/
|
||
setCalculatedRange: function (opt) {
|
||
var option = this.option;
|
||
each(['start', 'startValue', 'end', 'endValue'], function (name) {
|
||
option[name] = opt[name];
|
||
});
|
||
},
|
||
|
||
/**
|
||
* @public
|
||
* @return {Array.<number>} [startPercent, endPercent]
|
||
*/
|
||
getPercentRange: function () {
|
||
var axisProxy = this.findRepresentativeAxisProxy();
|
||
|
||
if (axisProxy) {
|
||
return axisProxy.getDataPercentWindow();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @public
|
||
* For example, chart.getModel().getComponent('dataZoom').getValueRange('y', 0);
|
||
*
|
||
* @param {string} [axisDimName]
|
||
* @param {number} [axisIndex]
|
||
* @return {Array.<number>} [startValue, endValue] value can only be '-' or finite number.
|
||
*/
|
||
getValueRange: function (axisDimName, axisIndex) {
|
||
if (axisDimName == null && axisIndex == null) {
|
||
var axisProxy = this.findRepresentativeAxisProxy();
|
||
|
||
if (axisProxy) {
|
||
return axisProxy.getDataValueWindow();
|
||
}
|
||
} else {
|
||
return this.getAxisProxy(axisDimName, axisIndex).getDataValueWindow();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @public
|
||
* @param {module:echarts/model/Model} [axisModel] If axisModel given, find axisProxy
|
||
* corresponding to the axisModel
|
||
* @return {module:echarts/component/dataZoom/AxisProxy}
|
||
*/
|
||
findRepresentativeAxisProxy: function (axisModel) {
|
||
if (axisModel) {
|
||
return axisModel.__dzAxisProxy;
|
||
} // Find the first hosted axisProxy
|
||
|
||
|
||
var axisProxies = this._axisProxies;
|
||
|
||
for (var key in axisProxies) {
|
||
if (axisProxies.hasOwnProperty(key) && axisProxies[key].hostedBy(this)) {
|
||
return axisProxies[key];
|
||
}
|
||
} // If no hosted axis find not hosted axisProxy.
|
||
// Consider this case: dataZoomModel1 and dataZoomModel2 control the same axis,
|
||
// and the option.start or option.end settings are different. The percentRange
|
||
// should follow axisProxy.
|
||
// (We encounter this problem in toolbox data zoom.)
|
||
|
||
|
||
for (var key in axisProxies) {
|
||
if (axisProxies.hasOwnProperty(key) && !axisProxies[key].hostedBy(this)) {
|
||
return axisProxies[key];
|
||
}
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @return {Array.<string>}
|
||
*/
|
||
getRangePropMode: function () {
|
||
return this._rangePropMode.slice();
|
||
}
|
||
});
|
||
/**
|
||
* Retrieve the those raw params from option, which will be cached separately.
|
||
* becasue they will be overwritten by normalized/calculated values in the main
|
||
* process.
|
||
*/
|
||
|
||
function retrieveRawOption(option) {
|
||
var ret = {};
|
||
each(['start', 'end', 'startValue', 'endValue', 'throttle'], function (name) {
|
||
option.hasOwnProperty(name) && (ret[name] = option[name]);
|
||
});
|
||
return ret;
|
||
}
|
||
|
||
function updateRangeUse(dataZoomModel, inputRawOption) {
|
||
var rangePropMode = dataZoomModel._rangePropMode;
|
||
var rangeModeInOption = dataZoomModel.get('rangeMode');
|
||
each([['start', 'startValue'], ['end', 'endValue']], function (names, index) {
|
||
var percentSpecified = inputRawOption[names[0]] != null;
|
||
var valueSpecified = inputRawOption[names[1]] != null;
|
||
|
||
if (percentSpecified && !valueSpecified) {
|
||
rangePropMode[index] = 'percent';
|
||
} else if (!percentSpecified && valueSpecified) {
|
||
rangePropMode[index] = 'value';
|
||
} else if (rangeModeInOption) {
|
||
rangePropMode[index] = rangeModeInOption[index];
|
||
} else if (percentSpecified) {
|
||
// percentSpecified && valueSpecified
|
||
rangePropMode[index] = 'percent';
|
||
} // else remain its original setting.
|
||
|
||
});
|
||
}
|
||
|
||
var _default = DataZoomModel;
|
||
module.exports = _default; |