Tree.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  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(); // FIXME: refactor levelModel to "beforeLink", and remove levelModel here.
  243. if (levelModel) {
  244. return itemModel.getModel(path, levelModel.getModel(path));
  245. } else {
  246. return itemModel.getModel(path);
  247. }
  248. },
  249. /**
  250. * @return {module:echarts/model/Model}
  251. */
  252. getLevelModel: function () {
  253. return (this.hostTree.levelModels || [])[this.depth];
  254. },
  255. /**
  256. * @example
  257. * setItemVisual('color', color);
  258. * setItemVisual({
  259. * 'color': color
  260. * });
  261. */
  262. setVisual: function (key, value) {
  263. this.dataIndex >= 0 && this.hostTree.data.setItemVisual(this.dataIndex, key, value);
  264. },
  265. /**
  266. * Get item visual
  267. */
  268. getVisual: function (key, ignoreParent) {
  269. return this.hostTree.data.getItemVisual(this.dataIndex, key, ignoreParent);
  270. },
  271. /**
  272. * @public
  273. * @return {number}
  274. */
  275. getRawIndex: function () {
  276. return this.hostTree.data.getRawIndex(this.dataIndex);
  277. },
  278. /**
  279. * @public
  280. * @return {string}
  281. */
  282. getId: function () {
  283. return this.hostTree.data.getId(this.dataIndex);
  284. },
  285. /**
  286. * if this is an ancestor of another node
  287. *
  288. * @public
  289. * @param {TreeNode} node another node
  290. * @return {boolean} if is ancestor
  291. */
  292. isAncestorOf: function (node) {
  293. var parent = node.parentNode;
  294. while (parent) {
  295. if (parent === this) {
  296. return true;
  297. }
  298. parent = parent.parentNode;
  299. }
  300. return false;
  301. },
  302. /**
  303. * if this is an descendant of another node
  304. *
  305. * @public
  306. * @param {TreeNode} node another node
  307. * @return {boolean} if is descendant
  308. */
  309. isDescendantOf: function (node) {
  310. return node !== this && node.isAncestorOf(this);
  311. }
  312. };
  313. /**
  314. * @constructor
  315. * @alias module:echarts/data/Tree
  316. * @param {module:echarts/model/Model} hostModel
  317. * @param {Array.<Object>} levelOptions
  318. */
  319. function Tree(hostModel, levelOptions) {
  320. /**
  321. * @type {module:echarts/data/Tree~TreeNode}
  322. * @readOnly
  323. */
  324. this.root;
  325. /**
  326. * @type {module:echarts/data/List}
  327. * @readOnly
  328. */
  329. this.data;
  330. /**
  331. * Index of each item is the same as the raw index of coresponding list item.
  332. * @private
  333. * @type {Array.<module:echarts/data/Tree~TreeNode}
  334. */
  335. this._nodes = [];
  336. /**
  337. * @private
  338. * @readOnly
  339. * @type {module:echarts/model/Model}
  340. */
  341. this.hostModel = hostModel;
  342. /**
  343. * @private
  344. * @readOnly
  345. * @type {Array.<module:echarts/model/Model}
  346. */
  347. this.levelModels = zrUtil.map(levelOptions || [], function (levelDefine) {
  348. return new Model(levelDefine, hostModel, hostModel.ecModel);
  349. });
  350. }
  351. Tree.prototype = {
  352. constructor: Tree,
  353. type: 'tree',
  354. /**
  355. * Travel this subtree (include this node).
  356. * Usage:
  357. * node.eachNode(function () { ... }); // preorder
  358. * node.eachNode('preorder', function () { ... }); // preorder
  359. * node.eachNode('postorder', function () { ... }); // postorder
  360. * node.eachNode(
  361. * {order: 'postorder', attr: 'viewChildren'},
  362. * function () { ... }
  363. * ); // postorder
  364. *
  365. * @param {(Object|string)} options If string, means order.
  366. * @param {string=} options.order 'preorder' or 'postorder'
  367. * @param {string=} options.attr 'children' or 'viewChildren'
  368. * @param {Function} cb
  369. * @param {Object} [context]
  370. */
  371. eachNode: function (options, cb, context) {
  372. this.root.eachNode(options, cb, context);
  373. },
  374. /**
  375. * @param {number} dataIndex
  376. * @return {module:echarts/data/Tree~TreeNode}
  377. */
  378. getNodeByDataIndex: function (dataIndex) {
  379. var rawIndex = this.data.getRawIndex(dataIndex);
  380. return this._nodes[rawIndex];
  381. },
  382. /**
  383. * @param {string} name
  384. * @return {module:echarts/data/Tree~TreeNode}
  385. */
  386. getNodeByName: function (name) {
  387. return this.root.getNodeByName(name);
  388. },
  389. /**
  390. * Update item available by list,
  391. * when list has been performed options like 'filterSelf' or 'map'.
  392. */
  393. update: function () {
  394. var data = this.data;
  395. var nodes = this._nodes;
  396. for (var i = 0, len = nodes.length; i < len; i++) {
  397. nodes[i].dataIndex = -1;
  398. }
  399. for (var i = 0, len = data.count(); i < len; i++) {
  400. nodes[data.getRawIndex(i)].dataIndex = i;
  401. }
  402. },
  403. /**
  404. * Clear all layouts
  405. */
  406. clearLayouts: function () {
  407. this.data.clearItemLayouts();
  408. }
  409. };
  410. /**
  411. * data node format:
  412. * {
  413. * name: ...
  414. * value: ...
  415. * children: [
  416. * {
  417. * name: ...
  418. * value: ...
  419. * children: ...
  420. * },
  421. * ...
  422. * ]
  423. * }
  424. *
  425. * @static
  426. * @param {Object} dataRoot Root node.
  427. * @param {module:echarts/model/Model} hostModel
  428. * @param {Object} treeOptions
  429. * @param {Array.<Object>} treeOptions.levels
  430. * @return module:echarts/data/Tree
  431. */
  432. Tree.createTree = function (dataRoot, hostModel, treeOptions, beforeLink) {
  433. var tree = new Tree(hostModel, treeOptions && treeOptions.levels);
  434. var listData = [];
  435. var dimMax = 1;
  436. buildHierarchy(dataRoot);
  437. function buildHierarchy(dataNode, parentNode) {
  438. var value = dataNode.value;
  439. dimMax = Math.max(dimMax, zrUtil.isArray(value) ? value.length : 1);
  440. listData.push(dataNode);
  441. var node = new TreeNode(dataNode.name, tree);
  442. parentNode ? addChild(node, parentNode) : tree.root = node;
  443. tree._nodes.push(node);
  444. var children = dataNode.children;
  445. if (children) {
  446. for (var i = 0; i < children.length; i++) {
  447. buildHierarchy(children[i], node);
  448. }
  449. }
  450. }
  451. tree.root.updateDepthAndHeight(0);
  452. var dimensionsInfo = createDimensions(listData, {
  453. coordDimensions: ['value'],
  454. dimensionsCount: dimMax
  455. });
  456. var list = new List(dimensionsInfo, hostModel);
  457. list.initData(listData);
  458. beforeLink && beforeLink(list);
  459. linkList({
  460. mainData: list,
  461. struct: tree,
  462. structAttr: 'tree'
  463. });
  464. tree.update();
  465. return tree;
  466. };
  467. /**
  468. * It is needed to consider the mess of 'list', 'hostModel' when creating a TreeNote,
  469. * so this function is not ready and not necessary to be public.
  470. *
  471. * @param {(module:echarts/data/Tree~TreeNode|Object)} child
  472. */
  473. function addChild(child, node) {
  474. var children = node.children;
  475. if (child.parentNode === node) {
  476. return;
  477. }
  478. children.push(child);
  479. child.parentNode = node;
  480. }
  481. var _default = Tree;
  482. module.exports = _default;