Tree.js 12 KB

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