Tree.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. var zrUtil = require("zrender/lib/core/util");
  2. var Model = require("../model/Model");
  3. var List = require("./List");
  4. var linkList = require("./helper/linkList");
  5. var completeDimensions = require("./helper/completeDimensions");
  6. /**
  7. * Tree data structure
  8. *
  9. * @module echarts/data/Tree
  10. */
  11. /**
  12. * @constructor module:echarts/data/Tree~TreeNode
  13. * @param {string} name
  14. * @param {module:echarts/data/Tree} hostTree
  15. */
  16. var TreeNode = function (name, hostTree) {
  17. /**
  18. * @type {string}
  19. */
  20. this.name = name || '';
  21. /**
  22. * Depth of node
  23. *
  24. * @type {number}
  25. * @readOnly
  26. */
  27. this.depth = 0;
  28. /**
  29. * Height of the subtree rooted at this node.
  30. * @type {number}
  31. * @readOnly
  32. */
  33. this.height = 0;
  34. /**
  35. * @type {module:echarts/data/Tree~TreeNode}
  36. * @readOnly
  37. */
  38. this.parentNode = null;
  39. /**
  40. * Reference to list item.
  41. * Do not persistent dataIndex outside,
  42. * besause it may be changed by list.
  43. * If dataIndex -1,
  44. * this node is logical deleted (filtered) in list.
  45. *
  46. * @type {Object}
  47. * @readOnly
  48. */
  49. this.dataIndex = -1;
  50. /**
  51. * @type {Array.<module:echarts/data/Tree~TreeNode>}
  52. * @readOnly
  53. */
  54. this.children = [];
  55. /**
  56. * @type {Array.<module:echarts/data/Tree~TreeNode>}
  57. * @pubilc
  58. */
  59. this.viewChildren = [];
  60. /**
  61. * @type {moduel:echarts/data/Tree}
  62. * @readOnly
  63. */
  64. this.hostTree = hostTree;
  65. };
  66. TreeNode.prototype = {
  67. constructor: TreeNode,
  68. /**
  69. * The node is removed.
  70. * @return {boolean} is removed.
  71. */
  72. isRemoved: function () {
  73. return this.dataIndex < 0;
  74. },
  75. /**
  76. * Travel this subtree (include this node).
  77. * Usage:
  78. * node.eachNode(function () { ... }); // preorder
  79. * node.eachNode('preorder', function () { ... }); // preorder
  80. * node.eachNode('postorder', function () { ... }); // postorder
  81. * node.eachNode(
  82. * {order: 'postorder', attr: 'viewChildren'},
  83. * function () { ... }
  84. * ); // postorder
  85. *
  86. * @param {(Object|string)} options If string, means order.
  87. * @param {string=} options.order 'preorder' or 'postorder'
  88. * @param {string=} options.attr 'children' or 'viewChildren'
  89. * @param {Function} cb If in preorder and return false,
  90. * its subtree will not be visited.
  91. * @param {Object} [context]
  92. */
  93. eachNode: function (options, cb, context) {
  94. if (typeof options === 'function') {
  95. context = cb;
  96. cb = options;
  97. options = null;
  98. }
  99. options = options || {};
  100. if (zrUtil.isString(options)) {
  101. options = {
  102. order: options
  103. };
  104. }
  105. var order = options.order || 'preorder';
  106. var children = this[options.attr || 'children'];
  107. var suppressVisitSub;
  108. order === 'preorder' && (suppressVisitSub = cb.call(context, this));
  109. for (var i = 0; !suppressVisitSub && i < children.length; i++) {
  110. children[i].eachNode(options, cb, context);
  111. }
  112. order === 'postorder' && cb.call(context, this);
  113. },
  114. /**
  115. * Update depth and height of this subtree.
  116. *
  117. * @param {number} depth
  118. */
  119. updateDepthAndHeight: function (depth) {
  120. var height = 0;
  121. this.depth = depth;
  122. for (var i = 0; i < this.children.length; i++) {
  123. var child = this.children[i];
  124. child.updateDepthAndHeight(depth + 1);
  125. if (child.height > height) {
  126. height = child.height;
  127. }
  128. }
  129. this.height = height + 1;
  130. },
  131. /**
  132. * @param {string} id
  133. * @return {module:echarts/data/Tree~TreeNode}
  134. */
  135. getNodeById: function (id) {
  136. if (this.getId() === id) {
  137. return this;
  138. }
  139. for (var i = 0, children = this.children, len = children.length; i < len; i++) {
  140. var res = children[i].getNodeById(id);
  141. if (res) {
  142. return res;
  143. }
  144. }
  145. },
  146. /**
  147. * @param {module:echarts/data/Tree~TreeNode} node
  148. * @return {boolean}
  149. */
  150. contains: function (node) {
  151. if (node === this) {
  152. return true;
  153. }
  154. for (var i = 0, children = this.children, len = children.length; i < len; i++) {
  155. var res = children[i].contains(node);
  156. if (res) {
  157. return res;
  158. }
  159. }
  160. },
  161. /**
  162. * @param {boolean} includeSelf Default false.
  163. * @return {Array.<module:echarts/data/Tree~TreeNode>} order: [root, child, grandchild, ...]
  164. */
  165. getAncestors: function (includeSelf) {
  166. var ancestors = [];
  167. var node = includeSelf ? this : this.parentNode;
  168. while (node) {
  169. ancestors.push(node);
  170. node = node.parentNode;
  171. }
  172. ancestors.reverse();
  173. return ancestors;
  174. },
  175. /**
  176. * @param {string|Array=} [dimension='value'] Default 'value'. can be 0, 1, 2, 3
  177. * @return {number} Value.
  178. */
  179. getValue: function (dimension) {
  180. var data = this.hostTree.data;
  181. return data.get(data.getDimension(dimension || 'value'), this.dataIndex);
  182. },
  183. /**
  184. * @param {Object} layout
  185. * @param {boolean=} [merge=false]
  186. */
  187. setLayout: function (layout, merge) {
  188. this.dataIndex >= 0 && this.hostTree.data.setItemLayout(this.dataIndex, layout, merge);
  189. },
  190. /**
  191. * @return {Object} layout
  192. */
  193. getLayout: function () {
  194. return this.hostTree.data.getItemLayout(this.dataIndex);
  195. },
  196. /**
  197. * @param {string} [path]
  198. * @return {module:echarts/model/Model}
  199. */
  200. getModel: function (path) {
  201. if (this.dataIndex < 0) {
  202. return;
  203. }
  204. var hostTree = this.hostTree;
  205. var itemModel = hostTree.data.getItemModel(this.dataIndex);
  206. var levelModel = this.getLevelModel();
  207. var leavesModel;
  208. if (!levelModel && (this.children.length === 0 || this.children.length !== 0 && this.isExpand === false)) {
  209. leavesModel = this.getLeavesModel();
  210. }
  211. return itemModel.getModel(path, (levelModel || leavesModel || hostTree.hostModel).getModel(path));
  212. },
  213. /**
  214. * @return {module:echarts/model/Model}
  215. */
  216. getLevelModel: function () {
  217. return (this.hostTree.levelModels || [])[this.depth];
  218. },
  219. /**
  220. * @return {module:echarts/model/Model}
  221. */
  222. getLeavesModel: function () {
  223. return this.hostTree.leavesModel;
  224. },
  225. /**
  226. * @example
  227. * setItemVisual('color', color);
  228. * setItemVisual({
  229. * 'color': color
  230. * });
  231. */
  232. setVisual: function (key, value) {
  233. this.dataIndex >= 0 && this.hostTree.data.setItemVisual(this.dataIndex, key, value);
  234. },
  235. /**
  236. * Get item visual
  237. */
  238. getVisual: function (key, ignoreParent) {
  239. return this.hostTree.data.getItemVisual(this.dataIndex, key, ignoreParent);
  240. },
  241. /**
  242. * @public
  243. * @return {number}
  244. */
  245. getRawIndex: function () {
  246. return this.hostTree.data.getRawIndex(this.dataIndex);
  247. },
  248. /**
  249. * @public
  250. * @return {string}
  251. */
  252. getId: function () {
  253. return this.hostTree.data.getId(this.dataIndex);
  254. }
  255. };
  256. /**
  257. * @constructor
  258. * @alias module:echarts/data/Tree
  259. * @param {module:echarts/model/Model} hostModel
  260. * @param {Array.<Object>} levelOptions
  261. * @param {Object} leavesOption
  262. */
  263. function Tree(hostModel, levelOptions, leavesOption) {
  264. /**
  265. * @type {module:echarts/data/Tree~TreeNode}
  266. * @readOnly
  267. */
  268. this.root;
  269. /**
  270. * @type {module:echarts/data/List}
  271. * @readOnly
  272. */
  273. this.data;
  274. /**
  275. * Index of each item is the same as the raw index of coresponding list item.
  276. * @private
  277. * @type {Array.<module:echarts/data/Tree~TreeNode}
  278. */
  279. this._nodes = [];
  280. /**
  281. * @private
  282. * @readOnly
  283. * @type {module:echarts/model/Model}
  284. */
  285. this.hostModel = hostModel;
  286. /**
  287. * @private
  288. * @readOnly
  289. * @type {Array.<module:echarts/model/Model}
  290. */
  291. this.levelModels = zrUtil.map(levelOptions || [], function (levelDefine) {
  292. return new Model(levelDefine, hostModel, hostModel.ecModel);
  293. });
  294. this.leavesModel = new Model(leavesOption || {}, hostModel, hostModel.ecModel);
  295. }
  296. Tree.prototype = {
  297. constructor: Tree,
  298. type: 'tree',
  299. /**
  300. * Travel this subtree (include this node).
  301. * Usage:
  302. * node.eachNode(function () { ... }); // preorder
  303. * node.eachNode('preorder', function () { ... }); // preorder
  304. * node.eachNode('postorder', function () { ... }); // postorder
  305. * node.eachNode(
  306. * {order: 'postorder', attr: 'viewChildren'},
  307. * function () { ... }
  308. * ); // postorder
  309. *
  310. * @param {(Object|string)} options If string, means order.
  311. * @param {string=} options.order 'preorder' or 'postorder'
  312. * @param {string=} options.attr 'children' or 'viewChildren'
  313. * @param {Function} cb
  314. * @param {Object} [context]
  315. */
  316. eachNode: function (options, cb, context) {
  317. this.root.eachNode(options, cb, context);
  318. },
  319. /**
  320. * @param {number} dataIndex
  321. * @return {module:echarts/data/Tree~TreeNode}
  322. */
  323. getNodeByDataIndex: function (dataIndex) {
  324. var rawIndex = this.data.getRawIndex(dataIndex);
  325. return this._nodes[rawIndex];
  326. },
  327. /**
  328. * @param {string} name
  329. * @return {module:echarts/data/Tree~TreeNode}
  330. */
  331. getNodeByName: function (name) {
  332. return this.root.getNodeByName(name);
  333. },
  334. /**
  335. * Update item available by list,
  336. * when list has been performed options like 'filterSelf' or 'map'.
  337. */
  338. update: function () {
  339. var data = this.data;
  340. var nodes = this._nodes;
  341. for (var i = 0, len = nodes.length; i < len; i++) {
  342. nodes[i].dataIndex = -1;
  343. }
  344. for (var i = 0, len = data.count(); i < len; i++) {
  345. nodes[data.getRawIndex(i)].dataIndex = i;
  346. }
  347. },
  348. /**
  349. * Clear all layouts
  350. */
  351. clearLayouts: function () {
  352. this.data.clearItemLayouts();
  353. }
  354. };
  355. /**
  356. * data node format:
  357. * {
  358. * name: ...
  359. * value: ...
  360. * children: [
  361. * {
  362. * name: ...
  363. * value: ...
  364. * children: ...
  365. * },
  366. * ...
  367. * ]
  368. * }
  369. *
  370. * @static
  371. * @param {Object} dataRoot Root node.
  372. * @param {module:echarts/model/Model} hostModel
  373. * @param {Object} treeOptions
  374. * @param {Array.<Object>} treeOptions.levels
  375. * @param {Array.<Object>} treeOptions.leaves
  376. * @return module:echarts/data/Tree
  377. */
  378. Tree.createTree = function (dataRoot, hostModel, treeOptions) {
  379. var tree = new Tree(hostModel, treeOptions.levels, treeOptions.leaves);
  380. var listData = [];
  381. var dimMax = 1;
  382. buildHierarchy(dataRoot);
  383. function buildHierarchy(dataNode, parentNode) {
  384. var value = dataNode.value;
  385. dimMax = Math.max(dimMax, zrUtil.isArray(value) ? value.length : 1);
  386. listData.push(dataNode);
  387. var node = new TreeNode(dataNode.name, tree);
  388. parentNode ? addChild(node, parentNode) : tree.root = node;
  389. tree._nodes.push(node);
  390. var children = dataNode.children;
  391. if (children) {
  392. for (var i = 0; i < children.length; i++) {
  393. buildHierarchy(children[i], node);
  394. }
  395. }
  396. }
  397. tree.root.updateDepthAndHeight(0);
  398. var dimensions = completeDimensions([{
  399. name: 'value'
  400. }], listData, {
  401. dimCount: dimMax
  402. });
  403. var list = new List(dimensions, hostModel);
  404. list.initData(listData);
  405. linkList({
  406. mainData: list,
  407. struct: tree,
  408. structAttr: 'tree'
  409. });
  410. tree.update();
  411. return tree;
  412. };
  413. /**
  414. * It is needed to consider the mess of 'list', 'hostModel' when creating a TreeNote,
  415. * so this function is not ready and not necessary to be public.
  416. *
  417. * @param {(module:echarts/data/Tree~TreeNode|Object)} child
  418. */
  419. function addChild(child, node) {
  420. var children = node.children;
  421. if (child.parentNode === node) {
  422. return;
  423. }
  424. children.push(child);
  425. child.parentNode = node;
  426. }
  427. var _default = Tree;
  428. module.exports = _default;