| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532 | 
/** Licensed to the Apache Software Foundation (ASF) under one* or more contributor license agreements.  See the NOTICE file* distributed with this work for additional information* regarding copyright ownership.  The ASF licenses this file* to you under the Apache License, Version 2.0 (the* "License"); you may not use this file except in compliance* with the License.  You may obtain a copy of the License at**   http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing,* software distributed under the License is distributed on an* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY* KIND, either express or implied.  See the License for the* specific language governing permissions and limitations* under the License.*/var zrUtil = require("zrender/lib/core/util");var linkList = require("./helper/linkList");var List = require("./List");var createDimensions = require("./helper/createDimensions");/** Licensed to the Apache Software Foundation (ASF) under one* or more contributor license agreements.  See the NOTICE file* distributed with this work for additional information* regarding copyright ownership.  The ASF licenses this file* to you under the Apache License, Version 2.0 (the* "License"); you may not use this file except in compliance* with the License.  You may obtain a copy of the License at**   http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing,* software distributed under the License is distributed on an* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY* KIND, either express or implied.  See the License for the* specific language governing permissions and limitations* under the License.*//** * Tree data structure * * @module echarts/data/Tree *//** * @constructor module:echarts/data/Tree~TreeNode * @param {string} name * @param {module:echarts/data/Tree} hostTree */var TreeNode = function (name, hostTree) {  /**   * @type {string}   */  this.name = name || '';  /**   * Depth of node   *   * @type {number}   * @readOnly   */  this.depth = 0;  /**   * Height of the subtree rooted at this node.   * @type {number}   * @readOnly   */  this.height = 0;  /**   * @type {module:echarts/data/Tree~TreeNode}   * @readOnly   */  this.parentNode = null;  /**   * Reference to list item.   * Do not persistent dataIndex outside,   * besause it may be changed by list.   * If dataIndex -1,   * this node is logical deleted (filtered) in list.   *   * @type {Object}   * @readOnly   */  this.dataIndex = -1;  /**   * @type {Array.<module:echarts/data/Tree~TreeNode>}   * @readOnly   */  this.children = [];  /**   * @type {Array.<module:echarts/data/Tree~TreeNode>}   * @pubilc   */  this.viewChildren = [];  /**   * @type {moduel:echarts/data/Tree}   * @readOnly   */  this.hostTree = hostTree;};TreeNode.prototype = {  constructor: TreeNode,  /**   * The node is removed.   * @return {boolean} is removed.   */  isRemoved: function () {    return this.dataIndex < 0;  },  /**   * Travel this subtree (include this node).   * Usage:   *    node.eachNode(function () { ... }); // preorder   *    node.eachNode('preorder', function () { ... }); // preorder   *    node.eachNode('postorder', function () { ... }); // postorder   *    node.eachNode(   *        {order: 'postorder', attr: 'viewChildren'},   *        function () { ... }   *    ); // postorder   *   * @param {(Object|string)} options If string, means order.   * @param {string=} options.order 'preorder' or 'postorder'   * @param {string=} options.attr 'children' or 'viewChildren'   * @param {Function} cb If in preorder and return false,   *                      its subtree will not be visited.   * @param {Object} [context]   */  eachNode: function (options, cb, context) {    if (typeof options === 'function') {      context = cb;      cb = options;      options = null;    }    options = options || {};    if (zrUtil.isString(options)) {      options = {        order: options      };    }    var order = options.order || 'preorder';    var children = this[options.attr || 'children'];    var suppressVisitSub;    order === 'preorder' && (suppressVisitSub = cb.call(context, this));    for (var i = 0; !suppressVisitSub && i < children.length; i++) {      children[i].eachNode(options, cb, context);    }    order === 'postorder' && cb.call(context, this);  },  /**   * Update depth and height of this subtree.   *   * @param  {number} depth   */  updateDepthAndHeight: function (depth) {    var height = 0;    this.depth = depth;    for (var i = 0; i < this.children.length; i++) {      var child = this.children[i];      child.updateDepthAndHeight(depth + 1);      if (child.height > height) {        height = child.height;      }    }    this.height = height + 1;  },  /**   * @param  {string} id   * @return {module:echarts/data/Tree~TreeNode}   */  getNodeById: function (id) {    if (this.getId() === id) {      return this;    }    for (var i = 0, children = this.children, len = children.length; i < len; i++) {      var res = children[i].getNodeById(id);      if (res) {        return res;      }    }  },  /**   * @param {module:echarts/data/Tree~TreeNode} node   * @return {boolean}   */  contains: function (node) {    if (node === this) {      return true;    }    for (var i = 0, children = this.children, len = children.length; i < len; i++) {      var res = children[i].contains(node);      if (res) {        return res;      }    }  },  /**   * @param {boolean} includeSelf Default false.   * @return {Array.<module:echarts/data/Tree~TreeNode>} order: [root, child, grandchild, ...]   */  getAncestors: function (includeSelf) {    var ancestors = [];    var node = includeSelf ? this : this.parentNode;    while (node) {      ancestors.push(node);      node = node.parentNode;    }    ancestors.reverse();    return ancestors;  },  /**   * @param {string|Array=} [dimension='value'] Default 'value'. can be 0, 1, 2, 3   * @return {number} Value.   */  getValue: function (dimension) {    var data = this.hostTree.data;    return data.get(data.getDimension(dimension || 'value'), this.dataIndex);  },  /**   * @param {Object} layout   * @param {boolean=} [merge=false]   */  setLayout: function (layout, merge) {    this.dataIndex >= 0 && this.hostTree.data.setItemLayout(this.dataIndex, layout, merge);  },  /**   * @return {Object} layout   */  getLayout: function () {    return this.hostTree.data.getItemLayout(this.dataIndex);  },  /**   * @param {string} [path]   * @return {module:echarts/model/Model}   */  getModel: function (path) {    if (this.dataIndex < 0) {      return;    }    var hostTree = this.hostTree;    var itemModel = hostTree.data.getItemModel(this.dataIndex);    return itemModel.getModel(path);  },  /**   * @example   *  setItemVisual('color', color);   *  setItemVisual({   *      'color': color   *  });   */  setVisual: function (key, value) {    this.dataIndex >= 0 && this.hostTree.data.setItemVisual(this.dataIndex, key, value);  },  /**   * Get item visual   */  getVisual: function (key, ignoreParent) {    return this.hostTree.data.getItemVisual(this.dataIndex, key, ignoreParent);  },  /**   * @public   * @return {number}   */  getRawIndex: function () {    return this.hostTree.data.getRawIndex(this.dataIndex);  },  /**   * @public   * @return {string}   */  getId: function () {    return this.hostTree.data.getId(this.dataIndex);  },  /**   * if this is an ancestor of another node   *   * @public   * @param {TreeNode} node another node   * @return {boolean} if is ancestor   */  isAncestorOf: function (node) {    var parent = node.parentNode;    while (parent) {      if (parent === this) {        return true;      }      parent = parent.parentNode;    }    return false;  },  /**   * if this is an descendant of another node   *   * @public   * @param {TreeNode} node another node   * @return {boolean} if is descendant   */  isDescendantOf: function (node) {    return node !== this && node.isAncestorOf(this);  }};/** * @constructor * @alias module:echarts/data/Tree * @param {module:echarts/model/Model} hostModel */function Tree(hostModel) {  /**   * @type {module:echarts/data/Tree~TreeNode}   * @readOnly   */  this.root;  /**   * @type {module:echarts/data/List}   * @readOnly   */  this.data;  /**   * Index of each item is the same as the raw index of coresponding list item.   * @private   * @type {Array.<module:echarts/data/Tree~TreeNode}   */  this._nodes = [];  /**   * @private   * @readOnly   * @type {module:echarts/model/Model}   */  this.hostModel = hostModel;}Tree.prototype = {  constructor: Tree,  type: 'tree',  /**   * Travel this subtree (include this node).   * Usage:   *    node.eachNode(function () { ... }); // preorder   *    node.eachNode('preorder', function () { ... }); // preorder   *    node.eachNode('postorder', function () { ... }); // postorder   *    node.eachNode(   *        {order: 'postorder', attr: 'viewChildren'},   *        function () { ... }   *    ); // postorder   *   * @param {(Object|string)} options If string, means order.   * @param {string=} options.order 'preorder' or 'postorder'   * @param {string=} options.attr 'children' or 'viewChildren'   * @param {Function} cb   * @param {Object}   [context]   */  eachNode: function (options, cb, context) {    this.root.eachNode(options, cb, context);  },  /**   * @param {number} dataIndex   * @return {module:echarts/data/Tree~TreeNode}   */  getNodeByDataIndex: function (dataIndex) {    var rawIndex = this.data.getRawIndex(dataIndex);    return this._nodes[rawIndex];  },  /**   * @param {string} name   * @return {module:echarts/data/Tree~TreeNode}   */  getNodeByName: function (name) {    return this.root.getNodeByName(name);  },  /**   * Update item available by list,   * when list has been performed options like 'filterSelf' or 'map'.   */  update: function () {    var data = this.data;    var nodes = this._nodes;    for (var i = 0, len = nodes.length; i < len; i++) {      nodes[i].dataIndex = -1;    }    for (var i = 0, len = data.count(); i < len; i++) {      nodes[data.getRawIndex(i)].dataIndex = i;    }  },  /**   * Clear all layouts   */  clearLayouts: function () {    this.data.clearItemLayouts();  }};/** * data node format: * { *     name: ... *     value: ... *     children: [ *         { *             name: ... *             value: ... *             children: ... *         }, *         ... *     ] * } * * @static * @param {Object} dataRoot Root node. * @param {module:echarts/model/Model} hostModel * @return module:echarts/data/Tree */Tree.createTree = function (dataRoot, hostModel, beforeLink) {  var tree = new Tree(hostModel);  var listData = [];  var dimMax = 1;  buildHierarchy(dataRoot);  function buildHierarchy(dataNode, parentNode) {    var value = dataNode.value;    dimMax = Math.max(dimMax, zrUtil.isArray(value) ? value.length : 1);    listData.push(dataNode);    var node = new TreeNode(dataNode.name, tree);    parentNode ? addChild(node, parentNode) : tree.root = node;    tree._nodes.push(node);    var children = dataNode.children;    if (children) {      for (var i = 0; i < children.length; i++) {        buildHierarchy(children[i], node);      }    }  }  tree.root.updateDepthAndHeight(0);  var dimensionsInfo = createDimensions(listData, {    coordDimensions: ['value'],    dimensionsCount: dimMax  });  var list = new List(dimensionsInfo, hostModel);  list.initData(listData);  beforeLink && beforeLink(list);  linkList({    mainData: list,    struct: tree,    structAttr: 'tree'  });  tree.update();  return tree;};/** * It is needed to consider the mess of 'list', 'hostModel' when creating a TreeNote, * so this function is not ready and not necessary to be public. * * @param {(module:echarts/data/Tree~TreeNode|Object)} child */function addChild(child, node) {  var children = node.children;  if (child.parentNode === node) {    return;  }  children.push(child);  child.parentNode = node;}var _default = Tree;module.exports = _default;
 |