Tree.js 13 KB

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