lepu-test-platform-web/node_modules/echarts/lib/component/dataZoom/DataZoomModel.js

632 lines
20 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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来判断考虑timelog等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;