diff --git a/src/action/createDataSelectAction.js b/src/action/createDataSelectAction.js index bb7ecccdd487b64df46ae5f77d35a9427d99ef79..d9703daf1738f681bbe8613ddac70b10fdc76388 100644 --- a/src/action/createDataSelectAction.js +++ b/src/action/createDataSelectAction.js @@ -7,11 +7,16 @@ define(function (require) { * @property {string} name */ echarts.registerAction(actionInfo, function (payload, ecModel) { - ecModel.eachSeriesByType(seriesType, function (seriesModel) { - if (seriesModel.name === payload.seriesName && seriesModel.toggleSelected) { - seriesModel.toggleSelected(payload.name); + + ecModel.eachComponent( + {mainType: 'series', subType: seriesType, payload: payload}, + function (seriesModel) { + if (seriesModel.toggleSelected) { + seriesModel.toggleSelected(payload.name); + } } - }); + ); + }); }; }); \ No newline at end of file diff --git a/src/component/dataRange/dataRangeAction.js b/src/component/dataRange/dataRangeAction.js index 545249823ebe302b9c70dc0ba98063589209ee30..636ade547ff7614d18e71a25983d91441c8191f8 100644 --- a/src/component/dataRange/dataRangeAction.js +++ b/src/component/dataRange/dataRangeAction.js @@ -3,9 +3,7 @@ */ define(function(require) { - // var zrUtil = require('zrender/core/util'); var echarts = require('../../echarts'); - // var modelUtil = require('../../util/model'); var actionInfo = { type: 'selectDataRange', @@ -16,37 +14,10 @@ define(function(require) { echarts.registerAction(actionInfo, function (payload, ecModel) { - var fromDataRangeModel = ecModel.queryComponent({ - mainType: 'dataRange', - name: payload.dataRangeName, - index: payload.dataRangeIndex + ecModel.eachComponent({mainType: 'dataRange', payload: payload}, function (model) { + model.setSelected(payload.selected); }); - fromDataRangeModel && fromDataRangeModel.setSelected(payload.selected); - - // Find all dataRangeModel that has the same visualType and controls the same series. - // var linkedNodesFinder = modelUtil.createLinkedNodesFinder( - // function (callback, context) { - // ecModel.eachComponent('dataRange', function (dataRangeModel) { - // var fromMappings = dataRangeModel.visualMappings; - // var toMappings = dataRangeModel.visualMappings; - // if (toMappings.selected.type === fromMappings.selected.type - // || toMappings.selected.type === fromMappings.selected.type - // ) { - // callback.call(context, dataRangeModel); - // } - // }); - // }, - // modelUtil.createNameEach(['series'], ['index']), - // function (model, nameObj) { - // return model.get(nameObj.index); - // } - // ); - // var effectedModels = linkedNodesFinder(fromDataRangeModel).nodes; - - // zrUtil.each(effectedModels, function (dataRangeModel) { - // fromDataRangeModel.setSelected(payload.selected); - // }); }); }); \ No newline at end of file diff --git a/src/component/dataZoom/dataZoomAction.js b/src/component/dataZoom/dataZoomAction.js index a99a968342e8f33ebac175ab648f3bdafaeecabf..27e9e34d03dd31e11d89afe4ab1476503c0a2cd7 100644 --- a/src/component/dataZoom/dataZoomAction.js +++ b/src/component/dataZoom/dataZoomAction.js @@ -10,14 +10,6 @@ define(function(require) { echarts.registerAction('dataZoom', function (payload, ecModel) { - var fromDataZoomModel = ecModel.queryComponent({ - mainType: 'dataZoom', - name: payload.dataZoomName, - index: payload.dataZoomIndex - }); - if (!fromDataZoomModel) { - return; - } var linkedNodesFinder = modelUtil.createLinkedNodesFinder( zrUtil.bind(ecModel.eachComponent, ecModel, 'dataZoom'), @@ -27,11 +19,35 @@ define(function(require) { } ); - var effectedModels = linkedNodesFinder(fromDataZoomModel).nodes; + var effectedModels = []; + + ecModel.eachComponent({mainType: 'dataZoom', payload: payload}, function (model) { + distinctPush(effectedModels, linkedNodesFinder(model).nodes); + }); zrUtil.each(effectedModels, function (dataZoomModel) { dataZoomModel.setRange(payload.range); }); + }); + function distinctPush(target, source) { + var targetLen = target.length; + + for (var i = 0, len = source.length; i < len; i++) { + var src = source[i]; + + var has = false; + for (var j = 0; j < targetLen; j++) { + if (src === target[j]) { + has = true; + } + } + + if (!has) { + target.push(src); + } + } + } + }); \ No newline at end of file diff --git a/src/model/Component.js b/src/model/Component.js index 77a2a51eb4f722784ce3275aaf1b707f7ce79b9c..ff57317c291bf902eb072bc661a14eb4f3a678ef 100644 --- a/src/model/Component.js +++ b/src/model/Component.js @@ -22,11 +22,35 @@ define(function(require) { type: 'component', + /** + * @readOnly + * @type {string} + */ + id: '', + /** * @readOnly */ name: '', + /** + * @readOnly + * @type {string} + */ + mainType: '', + + /** + * @readOnly + * @type {string} + */ + subType: '', + + /** + * @readOnly + * @type {number} + */ + componentIndex: null, + /** * @type {Object} * @protected @@ -83,16 +107,11 @@ define(function(require) { }, /** - * Two ids are different if and only if their component types - * or names are different. We use this id to hash component models - * and view instances in echarts. When setOption are called in - * no-merge mode, new models are able to replace old model, and - * view instances are able to mapped to previous. * @public * @return {string} id */ getId: function () { - return this.name + '__' + this.type; + return this.id; } }); @@ -101,6 +120,7 @@ define(function(require) { clazzUtil.enableClassExtend( ComponentModel, function (option, parentModel, ecModel, dependentModels, index) { + this.ecModel = ecModel; this.dependentModels = dependentModels; this.componentIndex = index; @@ -112,13 +132,10 @@ define(function(require) { this.subType = type.sub; } - // FIXME - // 如果name重复,要进行提示。 - var componentName = option.name; - if (componentName == null) { - componentName = this.type + '' + index; - } - this.name = componentName + ''; + // option.name and option.id has been completed and validated + // in module:echarts/model/Global + this.name = option.name; + this.id = option.id; this.uid = componentUtil.getUID('componentModel'); } diff --git a/src/model/Global.js b/src/model/Global.js index f46e53df080a6608724260be941e7ddb6ff6000a..3cc00b88eff84aa802baad9d4752618c7f2ecbc2 100644 --- a/src/model/Global.js +++ b/src/model/Global.js @@ -12,6 +12,7 @@ define(function (require) { var zrUtil = require('zrender/core/util'); var Model = require('./Model'); var each = zrUtil.each; + var filter = zrUtil.filter; var ComponentModel = require('./Component'); @@ -40,12 +41,6 @@ define(function (require) { */ this._componentsMap = {}; - /** - * @type {Object.>} - * @private - */ - this._componentsMapByName = {}; - /** * All components before processing * @type {Object.} @@ -92,7 +87,6 @@ define(function (require) { mergeOption: function (newOption) { var option = this.option; var componentsMap = this._componentsMap; - var componentsMapByName = this._componentsMapByName; var newCptTypes = []; // 如果不存在对应的 component model 则直接 merge @@ -126,14 +120,16 @@ define(function (require) { } if (!componentsMap[componentType]) { componentsMap[componentType] = []; - componentsMapByName[componentType] = []; } var existComponents = this._mappingToExists(componentType, newCptOptionList); - for (var i = 0; i < newCptOptionList.length; i++) { - var componentModel = existComponents[i]; - var newCptOption = newCptOptionList[i]; + this._completeOptionKeys( + componentType, newCptOptionList, existComponents + ); + + each(newCptOptionList, function (newCptOption, index) { + var componentModel = existComponents[index]; var subType = this._determineSubType( componentType, newCptOption, componentModel @@ -148,14 +144,12 @@ define(function (require) { else { // PENDING Global as parent ? componentModel = new ComponentModelClass( - newCptOptionList[i], this, this, - this._getComponentsByTypes(dependencies), i + newCptOption, this, this, + this._getComponentsByTypes(dependencies), index ); - componentsMap[componentType][i] = - componentsMapByName[componentType][componentModel.name] = - componentModel; + componentsMap[componentType][index] = componentModel; } - } + }, this); } // Backup data @@ -188,6 +182,19 @@ define(function (require) { var result = []; var existComponents = (this._componentsMap[componentType] || []).slice(); + // Mapping by id if specified. + each(newComponentOptionList, function (componentOption, index) { + if (!componentOption.id) { + return; + } + for (var i = 0, len = existComponents.length; i < len; i++) { + if (existComponents[i].getId() === componentOption.id) { + result[index] = existComponents.splice(i, 1)[0]; + return; + } + } + }); + // Mapping by name if specified. each(newComponentOptionList, function (componentOption, index) { if (!componentOption.name) { @@ -196,7 +203,7 @@ define(function (require) { for (var i = 0, len = existComponents.length; i < len; i++) { if (existComponents[i].name === componentOption.name) { result[index] = existComponents.splice(i, 1)[0]; - break; + return; } } }); @@ -211,6 +218,66 @@ define(function (require) { return result; }, + /** + * @private + */ + _completeOptionKeys: function (mainType, newCptOptionList, existComponents) { + // We use this id to hash component models and view instances + // in echarts. id can be specified by user, or auto generated. + + // The id generation rule ensures when setOption are called in + // no-merge mode, new model is able to replace old model, and + // new view instance are able to mapped to old instance. + // So we generate id by name and type. + + // name can be duplicated among components, which is convenient + // to specify multi components (like series) by one name. + + // We use a prefix when generating name or id to prevent + // user using the generated name or id directly. + var prefix = '\0'; + + // Ensure that each id is distinct. + var idSet = {}; + + each(newCptOptionList, function (opt, index) { + + var existCpt = existComponents[index]; + + // Complete subType + var subType = this._determineSubType(mainType, opt, existCpt); + var type = mainType + '.' + subType; + var id; + var name; + + if (existCpt) { + id = opt.id = existCpt.id; + opt.name = existCpt.name; + } + else { + name = opt.name; + if (name == null) { + // Using delimiter to escapse dulipcation. + name = opt.name = [prefix, type, index].join('-'); + } + id = opt.id; + if (id == null) { + // The delimiter should not be the same as name delimeter, + // ohterwise duplication might occurs. + id = opt.id = [prefix, name, type, index].join('|'); + } + } + + if (idSet[id]) { + // FIXME + // how to throw + throw new Error('id duplicates: ' + id); + } + idSet[id] = 1; + + }, this); + }, + /** * @return {module:echarts/model/Model} */ @@ -232,26 +299,53 @@ define(function (require) { /** * @param {Object} condition - * @param {number} [condition.index] Either input name or index. - * @param {string} [condition.name] Either input name or index. - * @param {string} [condition.mainType] Main type (not include subType). - * @return {module:echarts/model/Component} + * @param {string} condition.mainType + * @param {string} [condition.subType] If ignore, only query by mainType + * @param {number} [condition.index] Either input index or id or name. + * @param {string} [condition.id] Either input index or id or name. + * @param {string} [condition.name] Either input index or id or name. + * @return {Array.} */ - queryComponent: function (condition) { + queryComponents: function (condition) { var mainType = condition.mainType; if (!mainType) { - return; + return []; } - var byIndex = this._componentsMap[mainType]; - var byName = this._componentsMapByName[mainType]; + var index = condition.index; + var id = condition.id; + var name = condition.name; - if (byIndex && condition.index != null) { - return byIndex[condition.index]; + var cpts = this._componentsMap[mainType]; + + if (!cpts || !cpts.length) { + return []; + } + + var result; + + if (index != null) { + var cpt = cpts[index]; + result = cpt ? [cpt] : []; + } + else if (id != null) { + result = filter(cpts, function (cpt) { + return cpt.id === id; + }); } - if (byName && condition.name != null) { - return byName[condition.name]; + else if (name != null) { + result = filter(cpts, function (cpt) { + return cpt.name === name; + }); } + + var subType = condition.subType; + + return subType == null + ? result + : filter(result, function (cpt) { + return cpt.subType === subType; + }); }, /** @@ -263,6 +357,14 @@ define(function (require) { * // componentType does not include subType * // (componentType is 'xxx' but not 'xxx.aa') * }); + * eachComponent( + * {mainType: 'dataZoom', payload: {dataZoomId: 'abc'}}, + * function (model, index) {...} + * ); + * eachComponent( + * {mainType: 'series', subType: 'pie', payload: {seriesName: 'uio'}}, + * function (model, index) {...} + * ); * * @param {string=} mainType * @param {Function} cb @@ -278,9 +380,25 @@ define(function (require) { }, this); }, context); } - else { + + else if (zrUtil.isString(mainType)) { each(this._componentsMap[mainType], cb, context); } + + // Query by payload. + else if (zrUtil.isObject(mainType)) { + var condition = zrUtil.extend({}, mainType); + var payload = condition.payload; + var mainType = condition.mainType; + + // Style in payload: xxxIndex, xxxId, xxxName, + // where xxx is mainType. + condition.index = payload[mainType + 'Index']; + condition.id = payload[mainType + 'Id']; + condition.name = payload[mainType + 'Name']; + + each(this.queryComponents(condition), cb, context); + } }, /**