You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
861 lines
28 KiB
861 lines
28 KiB
|
|
/* |
|
* 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. |
|
*/ |
|
|
|
|
|
/** |
|
* AUTO-GENERATED FILE. DO NOT MODIFY. |
|
*/ |
|
|
|
/* |
|
* Licensed to the Apache Software Foundation (ASF) under one |
|
* or more contributor license agreements. See the NOTICE file |
|
* distributed with this work for additional information |
|
* regarding copyright ownership. The ASF licenses this file |
|
* to you under the Apache License, Version 2.0 (the |
|
* "License"); you may not use this file except in compliance |
|
* with the License. You may obtain a copy of the License at |
|
* |
|
* http://www.apache.org/licenses/LICENSE-2.0 |
|
* |
|
* Unless required by applicable law or agreed to in writing, |
|
* software distributed under the License is distributed on an |
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
|
* KIND, either express or implied. See the License for the |
|
* specific language governing permissions and limitations |
|
* under the License. |
|
*/ |
|
import { __extends } from "tslib"; |
|
/** |
|
* Caution: If the mechanism should be changed some day, these cases |
|
* should be considered: |
|
* |
|
* (1) In `merge option` mode, if using the same option to call `setOption` |
|
* many times, the result should be the same (try our best to ensure that). |
|
* (2) In `merge option` mode, if a component has no id/name specified, it |
|
* will be merged by index, and the result sequence of the components is |
|
* consistent to the original sequence. |
|
* (3) In `replaceMerge` mode, keep the result sequence of the components is |
|
* consistent to the original sequence, even though there might result in "hole". |
|
* (4) `reset` feature (in toolbox). Find detailed info in comments about |
|
* `mergeOption` in module:echarts/model/OptionManager. |
|
*/ |
|
|
|
import { each, filter, isArray, isObject, isString, createHashMap, assert, clone, merge, extend, mixin, isFunction } from 'zrender/lib/core/util.js'; |
|
import * as modelUtil from '../util/model.js'; |
|
import Model from './Model.js'; |
|
import ComponentModel from './Component.js'; |
|
import globalDefault from './globalDefault.js'; |
|
import { resetSourceDefaulter } from '../data/helper/sourceHelper.js'; |
|
import { concatInternalOptions } from './internalComponentCreator.js'; |
|
import { PaletteMixin } from './mixin/palette.js'; |
|
import { error, warn } from '../util/log.js'; // ----------------------- |
|
// Internal method names: |
|
// ----------------------- |
|
|
|
var reCreateSeriesIndices; |
|
var assertSeriesInitialized; |
|
var initBase; |
|
var OPTION_INNER_KEY = '\0_ec_inner'; |
|
var OPTION_INNER_VALUE = 1; |
|
var BUITIN_COMPONENTS_MAP = { |
|
grid: 'GridComponent', |
|
polar: 'PolarComponent', |
|
geo: 'GeoComponent', |
|
singleAxis: 'SingleAxisComponent', |
|
parallel: 'ParallelComponent', |
|
calendar: 'CalendarComponent', |
|
graphic: 'GraphicComponent', |
|
toolbox: 'ToolboxComponent', |
|
tooltip: 'TooltipComponent', |
|
axisPointer: 'AxisPointerComponent', |
|
brush: 'BrushComponent', |
|
title: 'TitleComponent', |
|
timeline: 'TimelineComponent', |
|
markPoint: 'MarkPointComponent', |
|
markLine: 'MarkLineComponent', |
|
markArea: 'MarkAreaComponent', |
|
legend: 'LegendComponent', |
|
dataZoom: 'DataZoomComponent', |
|
visualMap: 'VisualMapComponent', |
|
// aria: 'AriaComponent', |
|
// dataset: 'DatasetComponent', |
|
// Dependencies |
|
xAxis: 'GridComponent', |
|
yAxis: 'GridComponent', |
|
angleAxis: 'PolarComponent', |
|
radiusAxis: 'PolarComponent' |
|
}; |
|
var BUILTIN_CHARTS_MAP = { |
|
line: 'LineChart', |
|
bar: 'BarChart', |
|
pie: 'PieChart', |
|
scatter: 'ScatterChart', |
|
radar: 'RadarChart', |
|
map: 'MapChart', |
|
tree: 'TreeChart', |
|
treemap: 'TreemapChart', |
|
graph: 'GraphChart', |
|
gauge: 'GaugeChart', |
|
funnel: 'FunnelChart', |
|
parallel: 'ParallelChart', |
|
sankey: 'SankeyChart', |
|
boxplot: 'BoxplotChart', |
|
candlestick: 'CandlestickChart', |
|
effectScatter: 'EffectScatterChart', |
|
lines: 'LinesChart', |
|
heatmap: 'HeatmapChart', |
|
pictorialBar: 'PictorialBarChart', |
|
themeRiver: 'ThemeRiverChart', |
|
sunburst: 'SunburstChart', |
|
custom: 'CustomChart' |
|
}; |
|
var componetsMissingLogPrinted = {}; |
|
|
|
function checkMissingComponents(option) { |
|
each(option, function (componentOption, mainType) { |
|
if (!ComponentModel.hasClass(mainType)) { |
|
var componentImportName = BUITIN_COMPONENTS_MAP[mainType]; |
|
|
|
if (componentImportName && !componetsMissingLogPrinted[componentImportName]) { |
|
error("Component " + mainType + " is used but not imported.\nimport { " + componentImportName + " } from 'echarts/components';\necharts.use([" + componentImportName + "]);"); |
|
componetsMissingLogPrinted[componentImportName] = true; |
|
} |
|
} |
|
}); |
|
} |
|
|
|
var GlobalModel = |
|
/** @class */ |
|
function (_super) { |
|
__extends(GlobalModel, _super); |
|
|
|
function GlobalModel() { |
|
return _super !== null && _super.apply(this, arguments) || this; |
|
} |
|
|
|
GlobalModel.prototype.init = function (option, parentModel, ecModel, theme, locale, optionManager) { |
|
theme = theme || {}; |
|
this.option = null; // Mark as not initialized. |
|
|
|
this._theme = new Model(theme); |
|
this._locale = new Model(locale); |
|
this._optionManager = optionManager; |
|
}; |
|
|
|
GlobalModel.prototype.setOption = function (option, opts, optionPreprocessorFuncs) { |
|
if (process.env.NODE_ENV !== 'production') { |
|
assert(option != null, 'option is null/undefined'); |
|
assert(option[OPTION_INNER_KEY] !== OPTION_INNER_VALUE, 'please use chart.getOption()'); |
|
} |
|
|
|
var innerOpt = normalizeSetOptionInput(opts); |
|
|
|
this._optionManager.setOption(option, optionPreprocessorFuncs, innerOpt); |
|
|
|
this._resetOption(null, innerOpt); |
|
}; |
|
/** |
|
* @param type null/undefined: reset all. |
|
* 'recreate': force recreate all. |
|
* 'timeline': only reset timeline option |
|
* 'media': only reset media query option |
|
* @return Whether option changed. |
|
*/ |
|
|
|
|
|
GlobalModel.prototype.resetOption = function (type, opt) { |
|
return this._resetOption(type, normalizeSetOptionInput(opt)); |
|
}; |
|
|
|
GlobalModel.prototype._resetOption = function (type, opt) { |
|
var optionChanged = false; |
|
var optionManager = this._optionManager; |
|
|
|
if (!type || type === 'recreate') { |
|
var baseOption = optionManager.mountOption(type === 'recreate'); |
|
|
|
if (process.env.NODE_ENV !== 'production') { |
|
checkMissingComponents(baseOption); |
|
} |
|
|
|
if (!this.option || type === 'recreate') { |
|
initBase(this, baseOption); |
|
} else { |
|
this.restoreData(); |
|
|
|
this._mergeOption(baseOption, opt); |
|
} |
|
|
|
optionChanged = true; |
|
} |
|
|
|
if (type === 'timeline' || type === 'media') { |
|
this.restoreData(); |
|
} // By design, if `setOption(option2)` at the second time, and `option2` is a `ECUnitOption`, |
|
// it should better not have the same props with `MediaUnit['option']`. |
|
// Because either `option2` or `MediaUnit['option']` will be always merged to "current option" |
|
// rather than original "baseOption". If they both override a prop, the result might be |
|
// unexpected when media state changed after `setOption` called. |
|
// If we really need to modify a props in each `MediaUnit['option']`, use the full version |
|
// (`{baseOption, media}`) in `setOption`. |
|
// For `timeline`, the case is the same. |
|
|
|
|
|
if (!type || type === 'recreate' || type === 'timeline') { |
|
var timelineOption = optionManager.getTimelineOption(this); |
|
|
|
if (timelineOption) { |
|
optionChanged = true; |
|
|
|
this._mergeOption(timelineOption, opt); |
|
} |
|
} |
|
|
|
if (!type || type === 'recreate' || type === 'media') { |
|
var mediaOptions = optionManager.getMediaOption(this); |
|
|
|
if (mediaOptions.length) { |
|
each(mediaOptions, function (mediaOption) { |
|
optionChanged = true; |
|
|
|
this._mergeOption(mediaOption, opt); |
|
}, this); |
|
} |
|
} |
|
|
|
return optionChanged; |
|
}; |
|
|
|
GlobalModel.prototype.mergeOption = function (option) { |
|
this._mergeOption(option, null); |
|
}; |
|
|
|
GlobalModel.prototype._mergeOption = function (newOption, opt) { |
|
var option = this.option; |
|
var componentsMap = this._componentsMap; |
|
var componentsCount = this._componentsCount; |
|
var newCmptTypes = []; |
|
var newCmptTypeMap = createHashMap(); |
|
var replaceMergeMainTypeMap = opt && opt.replaceMergeMainTypeMap; |
|
resetSourceDefaulter(this); // If no component class, merge directly. |
|
// For example: color, animaiton options, etc. |
|
|
|
each(newOption, function (componentOption, mainType) { |
|
if (componentOption == null) { |
|
return; |
|
} |
|
|
|
if (!ComponentModel.hasClass(mainType)) { |
|
// globalSettingTask.dirty(); |
|
option[mainType] = option[mainType] == null ? clone(componentOption) : merge(option[mainType], componentOption, true); |
|
} else if (mainType) { |
|
newCmptTypes.push(mainType); |
|
newCmptTypeMap.set(mainType, true); |
|
} |
|
}); |
|
|
|
if (replaceMergeMainTypeMap) { |
|
// If there is a mainType `xxx` in `replaceMerge` but not declared in option, |
|
// we trade it as it is declared in option as `{xxx: []}`. Because: |
|
// (1) for normal merge, `{xxx: null/undefined}` are the same meaning as `{xxx: []}`. |
|
// (2) some preprocessor may convert some of `{xxx: null/undefined}` to `{xxx: []}`. |
|
replaceMergeMainTypeMap.each(function (val, mainTypeInReplaceMerge) { |
|
if (ComponentModel.hasClass(mainTypeInReplaceMerge) && !newCmptTypeMap.get(mainTypeInReplaceMerge)) { |
|
newCmptTypes.push(mainTypeInReplaceMerge); |
|
newCmptTypeMap.set(mainTypeInReplaceMerge, true); |
|
} |
|
}); |
|
} |
|
|
|
ComponentModel.topologicalTravel(newCmptTypes, ComponentModel.getAllClassMainTypes(), visitComponent, this); |
|
|
|
function visitComponent(mainType) { |
|
var newCmptOptionList = concatInternalOptions(this, mainType, modelUtil.normalizeToArray(newOption[mainType])); |
|
var oldCmptList = componentsMap.get(mainType); |
|
var mergeMode = // `!oldCmptList` means init. See the comment in `mappingToExists` |
|
!oldCmptList ? 'replaceAll' : replaceMergeMainTypeMap && replaceMergeMainTypeMap.get(mainType) ? 'replaceMerge' : 'normalMerge'; |
|
var mappingResult = modelUtil.mappingToExists(oldCmptList, newCmptOptionList, mergeMode); // Set mainType and complete subType. |
|
|
|
modelUtil.setComponentTypeToKeyInfo(mappingResult, mainType, ComponentModel); // Empty it before the travel, in order to prevent `this._componentsMap` |
|
// from being used in the `init`/`mergeOption`/`optionUpdated` of some |
|
// components, which is probably incorrect logic. |
|
|
|
option[mainType] = null; |
|
componentsMap.set(mainType, null); |
|
componentsCount.set(mainType, 0); |
|
var optionsByMainType = []; |
|
var cmptsByMainType = []; |
|
var cmptsCountByMainType = 0; |
|
var tooltipExists; |
|
var tooltipWarningLogged; |
|
each(mappingResult, function (resultItem, index) { |
|
var componentModel = resultItem.existing; |
|
var newCmptOption = resultItem.newOption; |
|
|
|
if (!newCmptOption) { |
|
if (componentModel) { |
|
// Consider where is no new option and should be merged using {}, |
|
// see removeEdgeAndAdd in topologicalTravel and |
|
// ComponentModel.getAllClassMainTypes. |
|
componentModel.mergeOption({}, this); |
|
componentModel.optionUpdated({}, false); |
|
} // If no both `resultItem.exist` and `resultItem.option`, |
|
// either it is in `replaceMerge` and not matched by any id, |
|
// or it has been removed in previous `replaceMerge` and left a "hole" in this component index. |
|
|
|
} else { |
|
var isSeriesType = mainType === 'series'; |
|
var ComponentModelClass = ComponentModel.getClass(mainType, resultItem.keyInfo.subType, !isSeriesType // Give a more detailed warn later if series don't exists |
|
); |
|
|
|
if (!ComponentModelClass) { |
|
if (process.env.NODE_ENV !== 'production') { |
|
var subType = resultItem.keyInfo.subType; |
|
var seriesImportName = BUILTIN_CHARTS_MAP[subType]; |
|
|
|
if (!componetsMissingLogPrinted[subType]) { |
|
componetsMissingLogPrinted[subType] = true; |
|
|
|
if (seriesImportName) { |
|
error("Series " + subType + " is used but not imported.\nimport { " + seriesImportName + " } from 'echarts/charts';\necharts.use([" + seriesImportName + "]);"); |
|
} else { |
|
error("Unknown series " + subType); |
|
} |
|
} |
|
} |
|
|
|
return; |
|
} // TODO Before multiple tooltips get supported, we do this check to avoid unexpected exception. |
|
|
|
|
|
if (mainType === 'tooltip') { |
|
if (tooltipExists) { |
|
if (process.env.NODE_ENV !== 'production') { |
|
if (!tooltipWarningLogged) { |
|
warn('Currently only one tooltip component is allowed.'); |
|
tooltipWarningLogged = true; |
|
} |
|
} |
|
|
|
return; |
|
} |
|
|
|
tooltipExists = true; |
|
} |
|
|
|
if (componentModel && componentModel.constructor === ComponentModelClass) { |
|
componentModel.name = resultItem.keyInfo.name; // componentModel.settingTask && componentModel.settingTask.dirty(); |
|
|
|
componentModel.mergeOption(newCmptOption, this); |
|
componentModel.optionUpdated(newCmptOption, false); |
|
} else { |
|
// PENDING Global as parent ? |
|
var extraOpt = extend({ |
|
componentIndex: index |
|
}, resultItem.keyInfo); |
|
componentModel = new ComponentModelClass(newCmptOption, this, this, extraOpt); // Assign `keyInfo` |
|
|
|
extend(componentModel, extraOpt); |
|
|
|
if (resultItem.brandNew) { |
|
componentModel.__requireNewView = true; |
|
} |
|
|
|
componentModel.init(newCmptOption, this, this); // Call optionUpdated after init. |
|
// newCmptOption has been used as componentModel.option |
|
// and may be merged with theme and default, so pass null |
|
// to avoid confusion. |
|
|
|
componentModel.optionUpdated(null, true); |
|
} |
|
} |
|
|
|
if (componentModel) { |
|
optionsByMainType.push(componentModel.option); |
|
cmptsByMainType.push(componentModel); |
|
cmptsCountByMainType++; |
|
} else { |
|
// Always do assign to avoid elided item in array. |
|
optionsByMainType.push(void 0); |
|
cmptsByMainType.push(void 0); |
|
} |
|
}, this); |
|
option[mainType] = optionsByMainType; |
|
componentsMap.set(mainType, cmptsByMainType); |
|
componentsCount.set(mainType, cmptsCountByMainType); // Backup series for filtering. |
|
|
|
if (mainType === 'series') { |
|
reCreateSeriesIndices(this); |
|
} |
|
} // If no series declared, ensure `_seriesIndices` initialized. |
|
|
|
|
|
if (!this._seriesIndices) { |
|
reCreateSeriesIndices(this); |
|
} |
|
}; |
|
/** |
|
* Get option for output (cloned option and inner info removed) |
|
*/ |
|
|
|
|
|
GlobalModel.prototype.getOption = function () { |
|
var option = clone(this.option); |
|
each(option, function (optInMainType, mainType) { |
|
if (ComponentModel.hasClass(mainType)) { |
|
var opts = modelUtil.normalizeToArray(optInMainType); // Inner cmpts need to be removed. |
|
// Inner cmpts might not be at last since ec5.0, but still |
|
// compatible for users: if inner cmpt at last, splice the returned array. |
|
|
|
var realLen = opts.length; |
|
var metNonInner = false; |
|
|
|
for (var i = realLen - 1; i >= 0; i--) { |
|
// Remove options with inner id. |
|
if (opts[i] && !modelUtil.isComponentIdInternal(opts[i])) { |
|
metNonInner = true; |
|
} else { |
|
opts[i] = null; |
|
!metNonInner && realLen--; |
|
} |
|
} |
|
|
|
opts.length = realLen; |
|
option[mainType] = opts; |
|
} |
|
}); |
|
delete option[OPTION_INNER_KEY]; |
|
return option; |
|
}; |
|
|
|
GlobalModel.prototype.getTheme = function () { |
|
return this._theme; |
|
}; |
|
|
|
GlobalModel.prototype.getLocaleModel = function () { |
|
return this._locale; |
|
}; |
|
|
|
GlobalModel.prototype.setUpdatePayload = function (payload) { |
|
this._payload = payload; |
|
}; |
|
|
|
GlobalModel.prototype.getUpdatePayload = function () { |
|
return this._payload; |
|
}; |
|
/** |
|
* @param idx If not specified, return the first one. |
|
*/ |
|
|
|
|
|
GlobalModel.prototype.getComponent = function (mainType, idx) { |
|
var list = this._componentsMap.get(mainType); |
|
|
|
if (list) { |
|
var cmpt = list[idx || 0]; |
|
|
|
if (cmpt) { |
|
return cmpt; |
|
} else if (idx == null) { |
|
for (var i = 0; i < list.length; i++) { |
|
if (list[i]) { |
|
return list[i]; |
|
} |
|
} |
|
} |
|
} |
|
}; |
|
/** |
|
* @return Never be null/undefined. |
|
*/ |
|
|
|
|
|
GlobalModel.prototype.queryComponents = function (condition) { |
|
var mainType = condition.mainType; |
|
|
|
if (!mainType) { |
|
return []; |
|
} |
|
|
|
var index = condition.index; |
|
var id = condition.id; |
|
var name = condition.name; |
|
|
|
var cmpts = this._componentsMap.get(mainType); |
|
|
|
if (!cmpts || !cmpts.length) { |
|
return []; |
|
} |
|
|
|
var result; |
|
|
|
if (index != null) { |
|
result = []; |
|
each(modelUtil.normalizeToArray(index), function (idx) { |
|
cmpts[idx] && result.push(cmpts[idx]); |
|
}); |
|
} else if (id != null) { |
|
result = queryByIdOrName('id', id, cmpts); |
|
} else if (name != null) { |
|
result = queryByIdOrName('name', name, cmpts); |
|
} else { |
|
// Return all non-empty components in that mainType |
|
result = filter(cmpts, function (cmpt) { |
|
return !!cmpt; |
|
}); |
|
} |
|
|
|
return filterBySubType(result, condition); |
|
}; |
|
/** |
|
* The interface is different from queryComponents, |
|
* which is convenient for inner usage. |
|
* |
|
* @usage |
|
* let result = findComponents( |
|
* {mainType: 'dataZoom', query: {dataZoomId: 'abc'}} |
|
* ); |
|
* let result = findComponents( |
|
* {mainType: 'series', subType: 'pie', query: {seriesName: 'uio'}} |
|
* ); |
|
* let result = findComponents( |
|
* {mainType: 'series', |
|
* filter: function (model, index) {...}} |
|
* ); |
|
* // result like [component0, componnet1, ...] |
|
*/ |
|
|
|
|
|
GlobalModel.prototype.findComponents = function (condition) { |
|
var query = condition.query; |
|
var mainType = condition.mainType; |
|
var queryCond = getQueryCond(query); |
|
var result = queryCond ? this.queryComponents(queryCond) // Retrieve all non-empty components. |
|
: filter(this._componentsMap.get(mainType), function (cmpt) { |
|
return !!cmpt; |
|
}); |
|
return doFilter(filterBySubType(result, condition)); |
|
|
|
function getQueryCond(q) { |
|
var indexAttr = mainType + 'Index'; |
|
var idAttr = mainType + 'Id'; |
|
var nameAttr = mainType + 'Name'; |
|
return q && (q[indexAttr] != null || q[idAttr] != null || q[nameAttr] != null) ? { |
|
mainType: mainType, |
|
// subType will be filtered finally. |
|
index: q[indexAttr], |
|
id: q[idAttr], |
|
name: q[nameAttr] |
|
} : null; |
|
} |
|
|
|
function doFilter(res) { |
|
return condition.filter ? filter(res, condition.filter) : res; |
|
} |
|
}; |
|
|
|
GlobalModel.prototype.eachComponent = function (mainType, cb, context) { |
|
var componentsMap = this._componentsMap; |
|
|
|
if (isFunction(mainType)) { |
|
var ctxForAll_1 = cb; |
|
var cbForAll_1 = mainType; |
|
componentsMap.each(function (cmpts, componentType) { |
|
for (var i = 0; cmpts && i < cmpts.length; i++) { |
|
var cmpt = cmpts[i]; |
|
cmpt && cbForAll_1.call(ctxForAll_1, componentType, cmpt, cmpt.componentIndex); |
|
} |
|
}); |
|
} else { |
|
var cmpts = isString(mainType) ? componentsMap.get(mainType) : isObject(mainType) ? this.findComponents(mainType) : null; |
|
|
|
for (var i = 0; cmpts && i < cmpts.length; i++) { |
|
var cmpt = cmpts[i]; |
|
cmpt && cb.call(context, cmpt, cmpt.componentIndex); |
|
} |
|
} |
|
}; |
|
/** |
|
* Get series list before filtered by name. |
|
*/ |
|
|
|
|
|
GlobalModel.prototype.getSeriesByName = function (name) { |
|
var nameStr = modelUtil.convertOptionIdName(name, null); |
|
return filter(this._componentsMap.get('series'), function (oneSeries) { |
|
return !!oneSeries && nameStr != null && oneSeries.name === nameStr; |
|
}); |
|
}; |
|
/** |
|
* Get series list before filtered by index. |
|
*/ |
|
|
|
|
|
GlobalModel.prototype.getSeriesByIndex = function (seriesIndex) { |
|
return this._componentsMap.get('series')[seriesIndex]; |
|
}; |
|
/** |
|
* Get series list before filtered by type. |
|
* FIXME: rename to getRawSeriesByType? |
|
*/ |
|
|
|
|
|
GlobalModel.prototype.getSeriesByType = function (subType) { |
|
return filter(this._componentsMap.get('series'), function (oneSeries) { |
|
return !!oneSeries && oneSeries.subType === subType; |
|
}); |
|
}; |
|
/** |
|
* Get all series before filtered. |
|
*/ |
|
|
|
|
|
GlobalModel.prototype.getSeries = function () { |
|
return filter(this._componentsMap.get('series'), function (oneSeries) { |
|
return !!oneSeries; |
|
}); |
|
}; |
|
/** |
|
* Count series before filtered. |
|
*/ |
|
|
|
|
|
GlobalModel.prototype.getSeriesCount = function () { |
|
return this._componentsCount.get('series'); |
|
}; |
|
/** |
|
* After filtering, series may be different |
|
* from raw series. |
|
*/ |
|
|
|
|
|
GlobalModel.prototype.eachSeries = function (cb, context) { |
|
assertSeriesInitialized(this); |
|
each(this._seriesIndices, function (rawSeriesIndex) { |
|
var series = this._componentsMap.get('series')[rawSeriesIndex]; |
|
|
|
cb.call(context, series, rawSeriesIndex); |
|
}, this); |
|
}; |
|
/** |
|
* Iterate raw series before filtered. |
|
* |
|
* @param {Function} cb |
|
* @param {*} context |
|
*/ |
|
|
|
|
|
GlobalModel.prototype.eachRawSeries = function (cb, context) { |
|
each(this._componentsMap.get('series'), function (series) { |
|
series && cb.call(context, series, series.componentIndex); |
|
}); |
|
}; |
|
/** |
|
* After filtering, series may be different. |
|
* from raw series. |
|
*/ |
|
|
|
|
|
GlobalModel.prototype.eachSeriesByType = function (subType, cb, context) { |
|
assertSeriesInitialized(this); |
|
each(this._seriesIndices, function (rawSeriesIndex) { |
|
var series = this._componentsMap.get('series')[rawSeriesIndex]; |
|
|
|
if (series.subType === subType) { |
|
cb.call(context, series, rawSeriesIndex); |
|
} |
|
}, this); |
|
}; |
|
/** |
|
* Iterate raw series before filtered of given type. |
|
*/ |
|
|
|
|
|
GlobalModel.prototype.eachRawSeriesByType = function (subType, cb, context) { |
|
return each(this.getSeriesByType(subType), cb, context); |
|
}; |
|
|
|
GlobalModel.prototype.isSeriesFiltered = function (seriesModel) { |
|
assertSeriesInitialized(this); |
|
return this._seriesIndicesMap.get(seriesModel.componentIndex) == null; |
|
}; |
|
|
|
GlobalModel.prototype.getCurrentSeriesIndices = function () { |
|
return (this._seriesIndices || []).slice(); |
|
}; |
|
|
|
GlobalModel.prototype.filterSeries = function (cb, context) { |
|
assertSeriesInitialized(this); |
|
var newSeriesIndices = []; |
|
each(this._seriesIndices, function (seriesRawIdx) { |
|
var series = this._componentsMap.get('series')[seriesRawIdx]; |
|
|
|
cb.call(context, series, seriesRawIdx) && newSeriesIndices.push(seriesRawIdx); |
|
}, this); |
|
this._seriesIndices = newSeriesIndices; |
|
this._seriesIndicesMap = createHashMap(newSeriesIndices); |
|
}; |
|
|
|
GlobalModel.prototype.restoreData = function (payload) { |
|
reCreateSeriesIndices(this); |
|
var componentsMap = this._componentsMap; |
|
var componentTypes = []; |
|
componentsMap.each(function (components, componentType) { |
|
if (ComponentModel.hasClass(componentType)) { |
|
componentTypes.push(componentType); |
|
} |
|
}); |
|
ComponentModel.topologicalTravel(componentTypes, ComponentModel.getAllClassMainTypes(), function (componentType) { |
|
each(componentsMap.get(componentType), function (component) { |
|
if (component && (componentType !== 'series' || !isNotTargetSeries(component, payload))) { |
|
component.restoreData(); |
|
} |
|
}); |
|
}); |
|
}; |
|
|
|
GlobalModel.internalField = function () { |
|
reCreateSeriesIndices = function (ecModel) { |
|
var seriesIndices = ecModel._seriesIndices = []; |
|
each(ecModel._componentsMap.get('series'), function (series) { |
|
// series may have been removed by `replaceMerge`. |
|
series && seriesIndices.push(series.componentIndex); |
|
}); |
|
ecModel._seriesIndicesMap = createHashMap(seriesIndices); |
|
}; |
|
|
|
assertSeriesInitialized = function (ecModel) { |
|
// Components that use _seriesIndices should depends on series component, |
|
// which make sure that their initialization is after series. |
|
if (process.env.NODE_ENV !== 'production') { |
|
if (!ecModel._seriesIndices) { |
|
throw new Error('Option should contains series.'); |
|
} |
|
} |
|
}; |
|
|
|
initBase = function (ecModel, baseOption) { |
|
// Using OPTION_INNER_KEY to mark that this option cannot be used outside, |
|
// i.e. `chart.setOption(chart.getModel().option);` is forbidden. |
|
ecModel.option = {}; |
|
ecModel.option[OPTION_INNER_KEY] = OPTION_INNER_VALUE; // Init with series: [], in case of calling findSeries method |
|
// before series initialized. |
|
|
|
ecModel._componentsMap = createHashMap({ |
|
series: [] |
|
}); |
|
ecModel._componentsCount = createHashMap(); // If user spefied `option.aria`, aria will be enable. This detection should be |
|
// performed before theme and globalDefault merge. |
|
|
|
var airaOption = baseOption.aria; |
|
|
|
if (isObject(airaOption) && airaOption.enabled == null) { |
|
airaOption.enabled = true; |
|
} |
|
|
|
mergeTheme(baseOption, ecModel._theme.option); // TODO Needs clone when merging to the unexisted property |
|
|
|
merge(baseOption, globalDefault, false); |
|
|
|
ecModel._mergeOption(baseOption, null); |
|
}; |
|
}(); |
|
|
|
return GlobalModel; |
|
}(Model); |
|
|
|
function isNotTargetSeries(seriesModel, payload) { |
|
if (payload) { |
|
var index = payload.seriesIndex; |
|
var id = payload.seriesId; |
|
var name_1 = payload.seriesName; |
|
return index != null && seriesModel.componentIndex !== index || id != null && seriesModel.id !== id || name_1 != null && seriesModel.name !== name_1; |
|
} |
|
} |
|
|
|
function mergeTheme(option, theme) { |
|
// PENDING |
|
// NOT use `colorLayer` in theme if option has `color` |
|
var notMergeColorLayer = option.color && !option.colorLayer; |
|
each(theme, function (themeItem, name) { |
|
if (name === 'colorLayer' && notMergeColorLayer) { |
|
return; |
|
} // If it is component model mainType, the model handles that merge later. |
|
// otherwise, merge them here. |
|
|
|
|
|
if (!ComponentModel.hasClass(name)) { |
|
if (typeof themeItem === 'object') { |
|
option[name] = !option[name] ? clone(themeItem) : merge(option[name], themeItem, false); |
|
} else { |
|
if (option[name] == null) { |
|
option[name] = themeItem; |
|
} |
|
} |
|
} |
|
}); |
|
} |
|
|
|
function queryByIdOrName(attr, idOrName, cmpts) { |
|
// Here is a break from echarts4: string and number are |
|
// treated as equal. |
|
if (isArray(idOrName)) { |
|
var keyMap_1 = createHashMap(); |
|
each(idOrName, function (idOrNameItem) { |
|
if (idOrNameItem != null) { |
|
var idName = modelUtil.convertOptionIdName(idOrNameItem, null); |
|
idName != null && keyMap_1.set(idOrNameItem, true); |
|
} |
|
}); |
|
return filter(cmpts, function (cmpt) { |
|
return cmpt && keyMap_1.get(cmpt[attr]); |
|
}); |
|
} else { |
|
var idName_1 = modelUtil.convertOptionIdName(idOrName, null); |
|
return filter(cmpts, function (cmpt) { |
|
return cmpt && idName_1 != null && cmpt[attr] === idName_1; |
|
}); |
|
} |
|
} |
|
|
|
function filterBySubType(components, condition) { |
|
// Using hasOwnProperty for restrict. Consider |
|
// subType is undefined in user payload. |
|
return condition.hasOwnProperty('subType') ? filter(components, function (cmpt) { |
|
return cmpt && cmpt.subType === condition.subType; |
|
}) : components; |
|
} |
|
|
|
function normalizeSetOptionInput(opts) { |
|
var replaceMergeMainTypeMap = createHashMap(); |
|
opts && each(modelUtil.normalizeToArray(opts.replaceMerge), function (mainType) { |
|
if (process.env.NODE_ENV !== 'production') { |
|
assert(ComponentModel.hasClass(mainType), '"' + mainType + '" is not valid component main type in "replaceMerge"'); |
|
} |
|
|
|
replaceMergeMainTypeMap.set(mainType, true); |
|
}); |
|
return { |
|
replaceMergeMainTypeMap: replaceMergeMainTypeMap |
|
}; |
|
} |
|
|
|
mixin(GlobalModel, PaletteMixin); |
|
export default GlobalModel; |