Component.js 8.2 KB
Newer Older
L
lang 已提交
1 2 3 4 5
/**
 * Component model
 *
 * @module echarts/model/Component
 */
L
lang 已提交
6 7 8 9 10
define(function(require) {

    'use strict';

    var Model = require('./Model');
L
lang 已提交
11
    var zrUtil = require('zrender/core/util');
12 13 14 15
    var arrayPush = Array.prototype.push;

    var TYPE_DELIMITER = '.';
    var IS_CONTAINER = '___EC__COMPONENT__CONTAINER___';
L
lang 已提交
16

P
pah100 已提交
17 18
    /**
     * Component model classes
19 20 21 22
     * key: componentType,
     * value:
     *     componentClass, when componentType is 'xxx'
     *     or Array.<componentClass>, when componentType is 'xxx.yy'
P
pah100 已提交
23 24 25 26
     * @type {Object}
     */
    var componentModelClasses = {};

L
lang 已提交
27 28 29 30 31 32 33
    /**
     * @alias module:echarts/model/Component
     * @constructor
     * @param {Object} option
     * @param {module:echarts/model/Model} parentModel
     * @param {module:echarts/model/Model} ecModel
     */
L
lang 已提交
34
    var ComponentModel = Model.extend({
L
lang 已提交
35 36 37 38 39 40 41 42 43

        type: 'component',

        /**
         * @type {Object}
         * @protected
         */
        defaultOption: null,

P
pah100 已提交
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
        /**
         * @type {Object}
         * @readOnly
         */
        ecModel: null,

        /**
         * key: componentType
         * value:  Component model list, can not be null.
         * @type {Object.<string, Array.<module:echarts/model/Model>>}
         * @readOnly
         */
        dependentModel: null,

        init: function () {
            this.mergeDefaultAndTheme(this.option, this.ecModel);
L
lang 已提交
60 61 62 63 64 65
        },

        mergeDefaultAndTheme: function (option, ecModel) {
            zrUtil.merge(option, ecModel.getTheme().get(this.type));
            zrUtil.merge(option, this.defaultOption);
        }
66

L
lang 已提交
67 68
    });

P
pah100 已提交
69 70 71 72 73 74
    ComponentModel.extend = function (proto) {
        var SubComponentModel = function (option, parentModel, ecModel, dependentModels) {
            this.ecModel = ecModel;
            this.dependentModels = dependentModels;
            ComponentModel.apply(this, arguments);
        };
P
pah100 已提交
75

P
pah100 已提交
76 77 78 79 80 81 82 83 84 85 86 87 88 89
        for (var name in proto) {
            if (proto.hasOwnProperty(name)) {
                SubComponentModel.prototype[name] = proto[name];
            }
        }

        var Super = this;
        SubComponentModel.extend = Super.extend;
        zrUtil.inherits(SubComponentModel, Super);

        return registerComponentClass(SubComponentModel, proto.type);
    };

    function registerComponentClass(SubComponentModel, componentType) {
L
lang 已提交
90
        if (componentType) {
91 92 93 94 95 96 97 98 99 100 101
            componentType = ComponentModel.parseComponentType(componentType);

            if (!componentType.sub) {
                if (componentModelClasses[componentType.main]) {
                    throw new Error(componentType.main + 'exists');
                }
                componentModelClasses[componentType.main] = SubComponentModel;
            }
            else if (componentType.sub !== IS_CONTAINER) {
                var container = makeContainer(componentType);
                container[componentType.sub] = SubComponentModel;
L
lang 已提交
102 103 104
            }
        }
        return SubComponentModel;
P
pah100 已提交
105
    }
L
lang 已提交
106

107 108 109 110 111 112 113 114
    ComponentModel.getComponentModelClass = function (componentType, option) {
        var fullComponentType = componentType;
        if (option && option.type) {
            fullComponentType = componentType + TYPE_DELIMITER + option.type;
        }
        var ComponentClass = getClassOrContainer(fullComponentType);
        if (ComponentClass[IS_CONTAINER]) {
            ComponentClass = ComponentClass[option.type];
L
lang 已提交
115
        }
116
        return ComponentClass;
L
lang 已提交
117 118
    };

119 120
    ComponentModel.has = function (componentType) {
        return !!getClassOrContainer(componentType);
L
lang 已提交
121
    };
L
lang 已提交
122

123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
    ComponentModel.parseComponentType = function (componentType) {
        var ret = {main: '', sub: ''};
        if (componentType) {
            componentType = componentType.split(TYPE_DELIMITER);
            ret.main = componentType[0] || '';
            ret.sub = componentType[1] || '';
        }
        return ret;
    };

    function makeContainer(componentType) {
        var container = componentModelClasses[componentType.main];
        if (!container || !container[IS_CONTAINER]) {
            container = componentModelClasses[componentType.main] = {};
            container[IS_CONTAINER] = true;
        }
        return container;
    }

    function getClassOrContainer(componentType) {
        componentType = ComponentModel.parseComponentType(componentType);
        return componentModelClasses[componentType.main];
    }

P
pah100 已提交
147 148
    /**
     * Topological travel on Activity Network (Activity On Vertices).
P
pah100 已提交
149 150
     * Dependencies is defined in Model.prototype.dependencies, like ['xAxis', 'yAxis'].
     * If 'xAxis' or 'yAxis' is absent in componentTypeList, just ignore it in topology.
P
pah100 已提交
151 152 153
     *
     * @public
     * @param {Array.<string>} componentTypeList Target Component type list.
P
pah100 已提交
154
     * @param {Function} callback Params: componentType, dependencies.
P
pah100 已提交
155
     */
P
pah100 已提交
156
    ComponentModel.topologicalTravel = function (componentTypeList, callback, scope) {
P
pah100 已提交
157 158 159
        if (!componentTypeList.length) {
            return;
        }
P
pah100 已提交
160 161 162
        var result = makeDepndencyGraph(componentTypeList);
        var graph = result.graph;
        var stack = result.noEntryList;
P
pah100 已提交
163 164 165 166 167 168 169

        if (!stack.length) {
            throw new Error('Circle exists in dependency graph.');
        }

        while (stack.length) {
            var currComponentType = stack.pop();
P
pah100 已提交
170 171
            var currVertex = graph[currComponentType];
            callback.call(scope, currComponentType, currVertex.originalDeps.slice());
P
pah100 已提交
172 173 174 175
            zrUtil.each(currVertex.successor, removeEdge);
        }

        function removeEdge(succComponentType) {
P
pah100 已提交
176 177
            graph[succComponentType].entryCount--;
            if (graph[succComponentType].entryCount === 0) {
P
pah100 已提交
178 179 180 181 182
                stack.push(succComponentType);
            }
        }
    };

P
pah100 已提交
183 184 185 186
    /**
     * DepndencyGraph: {Object}
     * key: conponentType,
     * value: {
P
pah100 已提交
187 188 189
     *     successor: [conponentTypes...],
     *     originalDeps: [conponentTypes...],
     *     entryCount: {number}
P
pah100 已提交
190 191 192
     * }
     */
    function makeDepndencyGraph(componentTypeList) {
P
pah100 已提交
193 194
        var graph = {};
        var noEntryList = [];
P
pah100 已提交
195 196 197

        zrUtil.each(componentTypeList, function (componentType) {

P
pah100 已提交
198
            var thisItem = createDependencyGraphItem(graph, componentType);
199
            var originalDeps = thisItem.originalDeps = getDependencies(componentType);
P
pah100 已提交
200 201 202 203 204 205 206 207

            var availableDeps = getAvailableDependencies(originalDeps, componentTypeList);
            thisItem.entryCount = availableDeps.length;
            if (thisItem.entryCount === 0) {
                noEntryList.push(componentType);
            }

            zrUtil.each(availableDeps, function (depComponentType) {
P
pah100 已提交
208 209 210
                if (zrUtil.indexOf(thisItem.predecessor, depComponentType) < 0) {
                    thisItem.predecessor.push(depComponentType);
                }
P
pah100 已提交
211
                var thatItem = createDependencyGraphItem(graph, depComponentType);
P
pah100 已提交
212 213 214 215
                if (zrUtil.indexOf(thatItem.successor, depComponentType) < 0) {
                    thatItem.successor.push(componentType);
                }
            });
P
pah100 已提交
216
        });
P
pah100 已提交
217

P
pah100 已提交
218
        return {graph: graph, noEntryList: noEntryList};
P
pah100 已提交
219 220
    }

221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
    function getDependencies(componentType) {
        componentType = ComponentModel.parseComponentType(componentType);
        var deps = [];
        var obj = componentModelClasses[componentType.main];
        if (obj && obj[IS_CONTAINER]) {
            zrUtil.each(obj, function (ComponentClass, componentType) {
                if (componentType !== IS_CONTAINER) {
                    arrayPush.apply(deps, ComponentClass.prototype.dependencies || []);
                }
            });
        }
        else if (obj) {
            arrayPush.apply(deps, obj.prototype.dependencies || []);
        }
        return deps;
    }

P
pah100 已提交
238 239 240
    function createDependencyGraphItem(graph, componentType) {
        if (!graph[componentType]) {
            graph[componentType] = {predecessor: [], successor: []};
P
pah100 已提交
241
        }
P
pah100 已提交
242 243 244 245 246 247 248 249 250
        return graph[componentType];
    }

    function getAvailableDependencies(originalDeps, componentTypeList) {
        var availableDeps = [];
        zrUtil.each(originalDeps, function (dep) {
            zrUtil.indexOf(componentTypeList, dep) >= 0 && availableDeps.push(dep);
        });
        return availableDeps;
P
pah100 已提交
251 252
    }

L
lang 已提交
253 254
    return ComponentModel;
});