component.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. var zrUtil = require("zrender/lib/core/util");
  2. var _clazz = require("./clazz");
  3. var parseClassType = _clazz.parseClassType;
  4. var base = 0;
  5. var DELIMITER = '_';
  6. /**
  7. * @public
  8. * @param {string} type
  9. * @return {string}
  10. */
  11. function getUID(type) {
  12. // Considering the case of crossing js context,
  13. // use Math.random to make id as unique as possible.
  14. return [type || '', base++, Math.random()].join(DELIMITER);
  15. }
  16. /**
  17. * @inner
  18. */
  19. function enableSubTypeDefaulter(entity) {
  20. var subTypeDefaulters = {};
  21. entity.registerSubTypeDefaulter = function (componentType, defaulter) {
  22. componentType = parseClassType(componentType);
  23. subTypeDefaulters[componentType.main] = defaulter;
  24. };
  25. entity.determineSubType = function (componentType, option) {
  26. var type = option.type;
  27. if (!type) {
  28. var componentTypeMain = parseClassType(componentType).main;
  29. if (entity.hasSubTypes(componentType) && subTypeDefaulters[componentTypeMain]) {
  30. type = subTypeDefaulters[componentTypeMain](option);
  31. }
  32. }
  33. return type;
  34. };
  35. return entity;
  36. }
  37. /**
  38. * Topological travel on Activity Network (Activity On Vertices).
  39. * Dependencies is defined in Model.prototype.dependencies, like ['xAxis', 'yAxis'].
  40. *
  41. * If 'xAxis' or 'yAxis' is absent in componentTypeList, just ignore it in topology.
  42. *
  43. * If there is circle dependencey, Error will be thrown.
  44. *
  45. */
  46. function enableTopologicalTravel(entity, dependencyGetter) {
  47. /**
  48. * @public
  49. * @param {Array.<string>} targetNameList Target Component type list.
  50. * Can be ['aa', 'bb', 'aa.xx']
  51. * @param {Array.<string>} fullNameList By which we can build dependency graph.
  52. * @param {Function} callback Params: componentType, dependencies.
  53. * @param {Object} context Scope of callback.
  54. */
  55. entity.topologicalTravel = function (targetNameList, fullNameList, callback, context) {
  56. if (!targetNameList.length) {
  57. return;
  58. }
  59. var result = makeDepndencyGraph(fullNameList);
  60. var graph = result.graph;
  61. var stack = result.noEntryList;
  62. var targetNameSet = {};
  63. zrUtil.each(targetNameList, function (name) {
  64. targetNameSet[name] = true;
  65. });
  66. while (stack.length) {
  67. var currComponentType = stack.pop();
  68. var currVertex = graph[currComponentType];
  69. var isInTargetNameSet = !!targetNameSet[currComponentType];
  70. if (isInTargetNameSet) {
  71. callback.call(context, currComponentType, currVertex.originalDeps.slice());
  72. delete targetNameSet[currComponentType];
  73. }
  74. zrUtil.each(currVertex.successor, isInTargetNameSet ? removeEdgeAndAdd : removeEdge);
  75. }
  76. zrUtil.each(targetNameSet, function () {
  77. throw new Error('Circle dependency may exists');
  78. });
  79. function removeEdge(succComponentType) {
  80. graph[succComponentType].entryCount--;
  81. if (graph[succComponentType].entryCount === 0) {
  82. stack.push(succComponentType);
  83. }
  84. } // Consider this case: legend depends on series, and we call
  85. // chart.setOption({series: [...]}), where only series is in option.
  86. // If we do not have 'removeEdgeAndAdd', legendModel.mergeOption will
  87. // not be called, but only sereis.mergeOption is called. Thus legend
  88. // have no chance to update its local record about series (like which
  89. // name of series is available in legend).
  90. function removeEdgeAndAdd(succComponentType) {
  91. targetNameSet[succComponentType] = true;
  92. removeEdge(succComponentType);
  93. }
  94. };
  95. /**
  96. * DepndencyGraph: {Object}
  97. * key: conponentType,
  98. * value: {
  99. * successor: [conponentTypes...],
  100. * originalDeps: [conponentTypes...],
  101. * entryCount: {number}
  102. * }
  103. */
  104. function makeDepndencyGraph(fullNameList) {
  105. var graph = {};
  106. var noEntryList = [];
  107. zrUtil.each(fullNameList, function (name) {
  108. var thisItem = createDependencyGraphItem(graph, name);
  109. var originalDeps = thisItem.originalDeps = dependencyGetter(name);
  110. var availableDeps = getAvailableDependencies(originalDeps, fullNameList);
  111. thisItem.entryCount = availableDeps.length;
  112. if (thisItem.entryCount === 0) {
  113. noEntryList.push(name);
  114. }
  115. zrUtil.each(availableDeps, function (dependentName) {
  116. if (zrUtil.indexOf(thisItem.predecessor, dependentName) < 0) {
  117. thisItem.predecessor.push(dependentName);
  118. }
  119. var thatItem = createDependencyGraphItem(graph, dependentName);
  120. if (zrUtil.indexOf(thatItem.successor, dependentName) < 0) {
  121. thatItem.successor.push(name);
  122. }
  123. });
  124. });
  125. return {
  126. graph: graph,
  127. noEntryList: noEntryList
  128. };
  129. }
  130. function createDependencyGraphItem(graph, name) {
  131. if (!graph[name]) {
  132. graph[name] = {
  133. predecessor: [],
  134. successor: []
  135. };
  136. }
  137. return graph[name];
  138. }
  139. function getAvailableDependencies(originalDeps, fullNameList) {
  140. var availableDeps = [];
  141. zrUtil.each(originalDeps, function (dep) {
  142. zrUtil.indexOf(fullNameList, dep) >= 0 && availableDeps.push(dep);
  143. });
  144. return availableDeps;
  145. }
  146. }
  147. exports.getUID = getUID;
  148. exports.enableSubTypeDefaulter = enableSubTypeDefaulter;
  149. exports.enableTopologicalTravel = enableTopologicalTravel;