diff --git a/src/component/dataZoom/dataZoomProcessor.js b/src/component/dataZoom/dataZoomProcessor.js new file mode 100644 index 0000000000000000000000000000000000000000..f7f64d66d32365d8a39a631c0856d90d884f6603 --- /dev/null +++ b/src/component/dataZoom/dataZoomProcessor.js @@ -0,0 +1,86 @@ +/** + * @file Data zoom processor + */ +define(function (require) { + + var echarts = require('../../echarts'); + var helper = require('./helper'); + var zrUtil = require('zrender/core/util'); + + echarts.registerProcessor(function (ecModel) { + ecModel.eachComponent('dataZoom', zrUtil.curry(processSingleDataZoom, ecModel)); + }); + + // FIXME + // originalData是放在原来的data中(这样不会被接下来的processor处理) + // 还是新创建个series(type="dataZoom")专门存originalData(这样会被processor处理,但是一些地方得特别判断type=='dataZoom') + // 同样,Axis的originalData放在哪里。 + // 其中: + // originalData在ec2中的用途: + // (1)画dataZoom组件(包括具体数值) + // (2)在packData时会用getRealDataIndex + + // TODO + // undo redo + + function processSingleDataZoom(ecModel, dataZoomModel) { + helper.eachAxisDim(function (dimNames) { + zrUtil.each( + dataZoomModel.get(dimNames.axisIndex), + zrUtil.curry(processSingleAxis, ecModel, dataZoomModel, dimNames) + ); + }); + } + + function processSingleAxis(ecModel, dataZoomModel, dimNames, axisIndex) { + // TODO + // backup axis data + var axisModel = ecModel.getComponent(dimNames.axis, axisIndex); + var axisData = axisModel.get('data'); + if (axisData) { + var dataLength = axisData.length; + var start = Math.floor(axisModel.get('dataZoomStart') / 100 * dataLength); + var end = Math.ceil(axisModel.get('dataZoomEnd') / 100 * dataLength); + + // Only category axis has property 'data's. + axisData = axisData.slice(start, end); + + var seriesModels = getTargetSeriesModelsByAxis( + ecModel, dataZoomModel, axisIndex, dimNames + ); + zrUtil.each(seriesModels, function (seriesModel) { + // FIXME + // data的backup + + // FIXME + // 如何filter, + // 是根据data自己存的信息(如dimension)来判断(这比较直接,但是现在list里存的信息没清楚), + // 还是根据axis type来判断(比较枚举不太好) + // var axisType = axisModel.get('type'); + + // FIXME + // 这里仅仅处理了list类型 + var seriesData = seriesModel.getData(); + seriesModel.setData( + seriesData['filter' + dimNames.dim.toUpperCase()](start, end) + ); + + // FIXME + // 对于数值轴,还要考虑log等情况 + // FIXME + // 对于时间河流图,还要考虑是否须整块移除。 + }); + } + } + + function getTargetSeriesModelsByAxis(ecModel, dataZoomModel, axisIndex, dimNames) { + var seriesModels = []; + ecModel.eachSeries(function (seriesModel) { + if (axisIndex === seriesModel.get(dimNames.axisIndex)) { + seriesModels.push(seriesModel); + } + }); + return seriesModels; + } + +}); diff --git a/src/component/dataZoom/helper.js b/src/component/dataZoom/helper.js new file mode 100644 index 0000000000000000000000000000000000000000..246446f2160e8bf50be4ac96e4eaf0d657c3acf8 --- /dev/null +++ b/src/component/dataZoom/helper.js @@ -0,0 +1,52 @@ +/** + * @file Data zoom helper + */ +define(function(require) { + + var zrUtil = require('zrender/core/util'); + + var helper = {}; + + var AXIS_DIMS = ['x', 'y']; + + // FIXME + // 公用? + helper.eachAxisDim = function (callback, scope) { + zrUtil.each(AXIS_DIMS, function (axisDim) { + var names = { + axisIndex: axisDim + 'AxisIndex', + axis: axisDim + 'Axis', + dim: axisDim + }; + callback.call(scope, names); + }); + }; + + // FIXME + // 公用? + /** + * If value1 is not null, then return value1, otherwise judget rest of values. + * @param {*...} values + * @return {*} Final value + */ + helper.retrieveValue = function (values) { + for (var i = 0, len = arguments.length; i < len; i++) { + if (arguments[i] != null) { + return arguments[i]; + } + } + }; + + // FIXME + // 公用? + /** + * If value is not array, then translate it to array. + * @param {*} value + * @return {Array} [value] or value + */ + helper.toArray = function (value) { + return zrUtil.isArray(value) ? value : (value == null ? [] : [value]); + }; + + return helper; +}); \ No newline at end of file diff --git a/src/model/Component.js b/src/model/Component.js index b3d7354e1fcac2663d7666685a082a1e5d43a727..1c517d7ee9adc18698c65d31860ecc140f2fe4a8 100644 --- a/src/model/Component.js +++ b/src/model/Component.js @@ -44,10 +44,9 @@ define(function(require) { }); ComponentModel.extend = function (opts) { - var SubComponentModel = Model.extend.call(this, opts); - var componentType = opts.type; + if (componentType) { if (componentModelClasses[componentType]) { throw new Error('Component model "' + componentType + '" exists.'); @@ -57,9 +56,9 @@ define(function(require) { return SubComponentModel; }; - ComponentModel.create = function (name, option, ecModel) { + ComponentModel.create = function (name, option, ecModel, dependentModels) { if (componentModelClasses[name]) { - return new componentModelClasses[name](option, null, ecModel); + return new componentModelClasses[name](option, null, ecModel, dependentModels); } }; @@ -69,25 +68,20 @@ define(function(require) { /** * Topological travel on Activity Network (Activity On Vertices). + * Dependencies is defined in Model.prototype.dependencies, like ['xAxis', 'yAxis']. + * If 'xAxis' or 'yAxis' is absent in componentTypeList, just ignore it in topology. * * @public * @param {Array.} componentTypeList Target Component type list. - * @param {Function} callback Params: componentType, depends. + * @param {Function} callback Params: componentType, dependencies. */ - ComponentModel.topologicalTavel = function (componentTypeList, callback, scope) { + ComponentModel.topologicalTravel = function (componentTypeList, callback, scope) { if (!componentTypeList.length) { return; } - var dependencyGraph = makeDepndencyGraph(componentTypeList); - var stack = []; - var entryCount = []; - - zrUtil.each(componentTypeList, function (componentType) { - entryCount[componentType] = dependencyGraph[componentType].predecessor.length; - if (entryCount[componentType] === 0) { - stack.push(componentType); - } - }); + var result = makeDepndencyGraph(componentTypeList); + var graph = result.graph; + var stack = result.noEntryList; if (!stack.length) { throw new Error('Circle exists in dependency graph.'); @@ -95,14 +89,14 @@ define(function(require) { while (stack.length) { var currComponentType = stack.pop(); - var currVertex = dependencyGraph[currComponentType]; - callback.call(scope, currComponentType, currVertex.predecessor.slice()); + var currVertex = graph[currComponentType]; + callback.call(scope, currComponentType, currVertex.originalDeps.slice()); zrUtil.each(currVertex.successor, removeEdge); } function removeEdge(succComponentType) { - entryCount[succComponentType]--; - if (entryCount[succComponentType] === 0) { + graph[succComponentType].entryCount--; + if (graph[succComponentType].entryCount === 0) { stack.push(succComponentType); } } @@ -112,37 +106,54 @@ define(function(require) { * DepndencyGraph: {Object} * key: conponentType, * value: { - * predecessor: [conponentTypes...] - * successor: [conponentTypes...] + * successor: [conponentTypes...], + * originalDeps: [conponentTypes...], + * entryCount: {number} * } */ function makeDepndencyGraph(componentTypeList) { - var dependencyGraph = {}; + var graph = {}; + var noEntryList = []; zrUtil.each(componentTypeList, function (componentType) { - var thisItem = createDependencyGraphItem(dependencyGraph, componentType); - var ModelClass = componentModelClasses[componentType]; - var depends = ModelClass.prototype.depends || []; - zrUtil.each(depends, function (depComponentType) { + var thisItem = createDependencyGraphItem(graph, componentType); + var originalDeps = thisItem.originalDeps = + (componentModelClasses[componentType].prototype.dependencies || []).slice(); + + var availableDeps = getAvailableDependencies(originalDeps, componentTypeList); + thisItem.entryCount = availableDeps.length; + if (thisItem.entryCount === 0) { + noEntryList.push(componentType); + } + + zrUtil.each(availableDeps, function (depComponentType) { if (zrUtil.indexOf(thisItem.predecessor, depComponentType) < 0) { thisItem.predecessor.push(depComponentType); } - var thatItem = createDependencyGraphItem(dependencyGraph, depComponentType); + var thatItem = createDependencyGraphItem(graph, depComponentType); if (zrUtil.indexOf(thatItem.successor, depComponentType) < 0) { thatItem.successor.push(componentType); } }); }); - return dependencyGraph; + return {graph: graph, noEntryList: noEntryList}; } - function createDependencyGraphItem(dependencyGraph, componentType) { - if (!dependencyGraph[componentType]) { - dependencyGraph[componentType] = {predecessor: [], successor: []}; + function createDependencyGraphItem(graph, componentType) { + if (!graph[componentType]) { + graph[componentType] = {predecessor: [], successor: []}; } - return dependencyGraph[componentType]; + return graph[componentType]; + } + + function getAvailableDependencies(originalDeps, componentTypeList) { + var availableDeps = []; + zrUtil.each(originalDeps, function (dep) { + zrUtil.indexOf(componentTypeList, dep) >= 0 && availableDeps.push(dep); + }); + return availableDeps; } return ComponentModel; diff --git a/test/ut/spec/model/Component.js b/test/ut/spec/model/Component.js index f876b55096d8e4cfd492b68899974e258ddaf468..79552e9e888c3aee91d5e38e7b4112ddb0c0c4ba 100755 --- a/test/ut/spec/model/Component.js +++ b/test/ut/spec/model/Component.js @@ -19,23 +19,33 @@ describe('Component', function() { function xtestCase() {} // jshint ignore:line testCase('topologicalTavel_base', function (ComponentModel) { - ComponentModel.extend({type: 'm1', depends: ['a1', 'a2']}); + ComponentModel.extend({type: 'm1', dependencies: ['a1', 'a2']}); ComponentModel.extend({type: 'a1'}); ComponentModel.extend({type: 'a2'}); var result = []; - ComponentModel.topologicalTavel(['m1', 'a1', 'a2'], function (componentType, depends) { - result.push([componentType, depends]); + ComponentModel.topologicalTavel(['m1', 'a1', 'a2'], function (componentType, dependencies) { + result.push([componentType, dependencies]); }); expect(result).toEqual([['a2', []], ['a1', []], ['m1', ['a1', 'a2']]]); }); + testCase('topologicalTavel_a1IsAbsent', function (ComponentModel) { + ComponentModel.extend({type: 'm1', dependencies: ['a1', 'a2']}); + ComponentModel.extend({type: 'a2'}); + var result = []; + ComponentModel.topologicalTavel(['m1', 'a2'], function (componentType, dependencies) { + result.push([componentType, dependencies]); + }); + expect(result).toEqual([['a2', []], ['m1', ['a1', 'a2']]]); + }); + testCase('topologicalTavel_empty', function (ComponentModel) { - ComponentModel.extend({type: 'm1', depends: ['a1', 'a2']}); + ComponentModel.extend({type: 'm1', dependencies: ['a1', 'a2']}); ComponentModel.extend({type: 'a1'}); ComponentModel.extend({type: 'a2'}); var result = []; - ComponentModel.topologicalTavel([], function (componentType, depends) { - result.push([componentType, depends]); + ComponentModel.topologicalTavel([], function (componentType, dependencies) { + result.push([componentType, dependencies]); }); expect(result).toEqual([]); }); @@ -43,50 +53,50 @@ describe('Component', function() { testCase('topologicalTavel_isolate', function (ComponentModel) { ComponentModel.extend({type: 'a2'}); ComponentModel.extend({type: 'a1'}); - ComponentModel.extend({type: 'm1', depends: ['a2']}); + ComponentModel.extend({type: 'm1', dependencies: ['a2']}); var result = []; - ComponentModel.topologicalTavel(['a1', 'a2', 'm1'], function (componentType, depends) { - result.push([componentType, depends]); + ComponentModel.topologicalTavel(['a1', 'a2', 'm1'], function (componentType, dependencies) { + result.push([componentType, dependencies]); }); expect(result).toEqual([['a2', []], ['m1', ['a2']], ['a1', []]]); }); testCase('topologicalTavel_diamond', function (ComponentModel) { - ComponentModel.extend({type: 'a1', depends: []}); - ComponentModel.extend({type: 'a2', depends: ['a1']}); - ComponentModel.extend({type: 'a3', depends: ['a1']}); - ComponentModel.extend({type: 'm1', depends: ['a2', 'a3']}); + ComponentModel.extend({type: 'a1', dependencies: []}); + ComponentModel.extend({type: 'a2', dependencies: ['a1']}); + ComponentModel.extend({type: 'a3', dependencies: ['a1']}); + ComponentModel.extend({type: 'm1', dependencies: ['a2', 'a3']}); var result = []; - ComponentModel.topologicalTavel(['m1', 'a1', 'a2', 'a3'], function (componentType, depends) { - result.push([componentType, depends]); + ComponentModel.topologicalTavel(['m1', 'a1', 'a2', 'a3'], function (componentType, dependencies) { + result.push([componentType, dependencies]); }); expect(result).toEqual([['a1', []], ['a3', ['a1']], ['a2', ['a1']], ['m1', ['a2', 'a3']]]); }); testCase('topologicalTavel_loop', function (ComponentModel) { - ComponentModel.extend({type: 'm1', depends: ['a1', 'a2']}); - ComponentModel.extend({type: 'm2', depends: ['m1', 'a2']}); - ComponentModel.extend({type: 'a1', depends: ['m2', 'a2']}); + ComponentModel.extend({type: 'm1', dependencies: ['a1', 'a2']}); + ComponentModel.extend({type: 'm2', dependencies: ['m1', 'a2']}); + ComponentModel.extend({type: 'a1', dependencies: ['m2', 'a2']}); ComponentModel.extend({type: 'a2'}); expect(function () { ComponentModel.topologicalTavel(['m1', 'm2', 'a1']); }).toThrowError(/Circl/); }); - testCase('topologicalTavel_re', function (ComponentModel) { - ComponentModel.extend({type: 'm1', depends: ['a1', 'a2']}); + testCase('topologicalTavel_multipleEchartsInstance', function (ComponentModel) { + ComponentModel.extend({type: 'm1', dependencies: ['a1', 'a2']}); ComponentModel.extend({type: 'a1'}); ComponentModel.extend({type: 'a2'}); var result = []; - ComponentModel.topologicalTavel(['m1', 'a1', 'a2'], function (componentType, depends) { - result.push([componentType, depends]); + ComponentModel.topologicalTavel(['m1', 'a1', 'a2'], function (componentType, dependencies) { + result.push([componentType, dependencies]); }); expect(result).toEqual([['a2', []], ['a1', []], ['m1', ['a1', 'a2']]]); result = []; - ComponentModel.extend({type: 'm2', depends: ['a1', 'm1']}); - ComponentModel.topologicalTavel(['m2', 'm1', 'a1', 'a2'], function (componentType, depends) { - result.push([componentType, depends]); + ComponentModel.extend({type: 'm2', dependencies: ['a1', 'm1']}); + ComponentModel.topologicalTavel(['m2', 'm1', 'a1', 'a2'], function (componentType, dependencies) { + result.push([componentType, dependencies]); }); expect(result).toEqual([['a2', []], ['a1', []], ['m1', ['a1', 'a2']], ['m2', ['a1', 'm1']]]); });