text: define(function(require) {
  
      var SeriesModel = require('../../model/Series');
      var Tree = require('../../data/Tree');
      var zrUtil = require('zrender/core/util');
      var Model = require('../../model/Model');
      var formatUtil = require('../../util/format');
      var encodeHTML = formatUtil.encodeHTML;
      var addCommas = formatUtil.addCommas;
  
  
      return SeriesModel.extend({
  
          type: 'series.treemap',
  
          dependencies: ['grid', 'polar'],
  
          /**
           * @type {module:echarts/data/Tree~Node}
           */
          _viewRoot: null,
  
          defaultOption: {
              // Disable progressive rendering
              progressive: 0,
              hoverLayerThreshold: Infinity,
              // center: ['50%', '50%'],          // not supported in ec3.
              // size: ['80%', '80%'],            // deprecated, compatible with ec2.
              left: 'center',
              top: 'middle',
              right: null,
              bottom: null,
              width: '80%',
              height: '80%',
              sort: true,                         // Can be null or false or true
                                                  // (order by desc default, asc not supported yet (strange effect))
              clipWindow: 'origin',               // Size of clipped window when zooming. 'origin' or 'fullscreen'
              squareRatio: 0.5 * (1 + Math.sqrt(5)), // golden ratio
              leafDepth: null,                    // Nodes on depth from root are regarded as leaves.
                                                  // Count from zero (zero represents only view root).
              drillDownIcon: '▶',                 // Use html character temporarily because it is complicated
                                                  // to align specialized icon. ▷▶❒❐▼✚
              visualDimension: 0,                 // Can be 0, 1, 2, 3.
              zoomToNodeRatio: 0.32 * 0.32,       // Be effective when using zoomToNode. Specify the proportion of the
                                                  // target node area in the view area.
              roam: true,                         // true, false, 'scale' or 'zoom', 'move'.
              nodeClick: 'zoomToNode',            // Leaf node click behaviour: 'zoomToNode', 'link', false.
                                                  // If leafDepth is set and clicking a node which has children but
                                                  // be on left depth, the behaviour would be changing root. Otherwise
                                                  // use behavious defined above.
              animation: true,
              animationDurationUpdate: 900,
              animationEasing: 'quinticInOut',
              breadcrumb: {
                  show: true,
                  height: 22,
                  left: 'center',
                  top: 'bottom',
                  // right
                  // bottom
                  emptyItemWidth: 25,             // Width of empty node.
                  itemStyle: {
                      normal: {
                          color: 'rgba(0,0,0,0.7)', //'#5793f3',
                          borderColor: 'rgba(255,255,255,0.7)',
                          borderWidth: 1,
                          shadowColor: 'rgba(150,150,150,1)',
                          shadowBlur: 3,
                          shadowOffsetX: 0,
                          shadowOffsetY: 0,
                          textStyle: {
                              color: '#fff'
                          }
                      },
                      emphasis: {
                          textStyle: {}
                      }
                  }
              },
              label: {
                  normal: {
                      show: true,
                      position: 'inside', // Can be [5, '5%'] or position stirng like 'insideTopLeft', ...
                      textStyle: {
                          color: '#fff',
                          ellipsis: true
                      }
                  }
              },
              itemStyle: {
                  normal: {
                      color: null,            // Can be 'none' if not necessary.
                      colorAlpha: null,       // Can be 'none' if not necessary.
                      colorSaturation: null,  // Can be 'none' if not necessary.
                      borderWidth: 0,
                      gapWidth: 0,
                      borderColor: '#fff',
                      borderColorSaturation: null // If specified, borderColor will be ineffective, and the
                                                  // border color is evaluated by color of current node and
                                                  // borderColorSaturation.
                  },
                  emphasis: {
  
                  }
              },
              color: 'none',              // Array. Specify color list of each level.
                                          // level[0].color would be global color list.
              colorAlpha: null,           // Array. Specify color alpha range of each level, like [0.2, 0.8]
              colorSaturation: null,      // Array. Specify color saturation of each level, like [0.2, 0.5]
              colorMappingBy: 'index',    // 'value' or 'index' or 'id'.
              visibleMin: 10,             // If area less than this threshold (unit: pixel^2), node will not
                                          // be rendered. Only works when sort is 'asc' or 'desc'.
              childrenVisibleMin: null,   // If area of a node less than this threshold (unit: pixel^2),
                                          // grandchildren will not show.
                                          // Why grandchildren? If not grandchildren but children,
                                          // some siblings show children and some not,
                                          // the appearance may be mess and not consistent,
              levels: []                  // Each item: {
                                          //     visibleMin, itemStyle, visualDimension, label
                                          // }
              // data: {
              //      value: [],
              //      children: [],
              //      link: 'http://xxx.xxx.xxx',
              //      target: 'blank' or 'self'
              // }
          },
  
          /**
           * @override
           */
          getInitialData: function (option, ecModel) {
              var data = option.data || [];
              var rootName = option.name;
              rootName == null && (rootName = option.name);
  
              // Create a virtual root.
              var root = {name: rootName, children: option.data};
              var value0 = (data[0] || {}).value;
  
              completeTreeValue(root, zrUtil.isArray(value0) ? value0.length : -1);
  
              // FIXME
              // sereis.mergeOption 的 getInitData是否放在merge后，从而能直接获取merege后的结果而非手动判断。
              var levels = option.levels || [];
  
              levels = option.levels = setDefault(levels, ecModel);
  
              // Make sure always a new tree is created when setOption,
              // in TreemapView, we check whether oldTree === newTree
              // to choose mappings approach among old shapes and new shapes.
              return Tree.createTree(root, this, levels).data;
          },
  
          optionUpdated: function () {
              this.resetViewRoot();
          },
  
          /**
           * @override
           * @param {number} dataIndex
           * @param {boolean} [mutipleSeries=false]
           */
          formatTooltip: function (dataIndex) {
              var data = this.getData();
              var value = this.getRawValue(dataIndex);
              var formattedValue = zrUtil.isArray(value)
                  ? addCommas(value[0]) : addCommas(value);
              var name = data.getName(dataIndex);
  
              return encodeHTML(name) + ': ' + formattedValue;
          },
  
          /**
           * Add tree path to tooltip param
           *
           * @override
           * @param {number} dataIndex
           * @return {Object}
           */
          getDataParams: function (dataIndex) {
              var params = SeriesModel.prototype.getDataParams.apply(this, arguments);
  
              var data = this.getData();
              var node = data.tree.getNodeByDataIndex(dataIndex);
              var treePathInfo = params.treePathInfo = [];
  
              while (node) {
                  var nodeDataIndex = node.dataIndex;
                  treePathInfo.push({
                      name: node.name,
                      dataIndex: nodeDataIndex,
                      value: this.getRawValue(nodeDataIndex)
                  });
                  node = node.parentNode;
              }
  
              treePathInfo.reverse();
  
              return params;
          },
  
          /**
           * @public
           * @param {Object} layoutInfo {
           *                                x: containerGroup x
           *                                y: containerGroup y
           *                                width: containerGroup width
           *                                height: containerGroup height
           *                            }
           */
          setLayoutInfo: function (layoutInfo) {
              /**
               * @readOnly
               * @type {Object}
               */
              this.layoutInfo = this.layoutInfo || {};
              zrUtil.extend(this.layoutInfo, layoutInfo);
          },
  
          /**
           * @param  {string} id
           * @return {number} index
           */
          mapIdToIndex: function (id) {
              // A feature is implemented:
              // index is monotone increasing with the sequence of
              // input id at the first time.
              // This feature can make sure that each data item and its
              // mapped color have the same index between data list and
              // color list at the beginning, which is useful for user
              // to adjust data-color mapping.
  
              /**
               * @private
               * @type {Object}
               */
              var idIndexMap = this._idIndexMap;
  
              if (!idIndexMap) {
                  idIndexMap = this._idIndexMap = {};
                  /**
                   * @private
                   * @type {number}
                   */
                  this._idIndexMapCount = 0;
              }
  
              var index = idIndexMap[id];
              if (index == null) {
                  idIndexMap[id] = index = this._idIndexMapCount++;
              }
  
              return index;
          },
  
          getViewRoot: function () {
              return this._viewRoot;
          },
  
          /**
           * @param {module:echarts/data/Tree~Node} [viewRoot]
           */
          resetViewRoot: function (viewRoot) {
              viewRoot
                  ? (this._viewRoot = viewRoot)
                  : (viewRoot = this._viewRoot);
  
              var root = this.getData().tree.root;
  
              if (!viewRoot
                  || (viewRoot !== root && !root.contains(viewRoot))
              ) {
                  this._viewRoot = root;
              }
          }
      });
  
      /**
       * @param {Object} dataNode
       */
      function completeTreeValue(dataNode, arrValueLength) {
          // Postorder travel tree.
          // If value of none-leaf node is not set,
          // calculate it by suming up the value of all children.
          var sum = 0;
  
          zrUtil.each(dataNode.children, function (child) {
  
              completeTreeValue(child, arrValueLength);
  
              var childValue = child.value;
              zrUtil.isArray(childValue) && (childValue = childValue[0]);
  
              sum += childValue;
          });
  
          var thisValue = dataNode.value;
  
          if (arrValueLength >= 0) {
              if (!zrUtil.isArray(thisValue)) {
                  dataNode.value = new Array(arrValueLength);
              }
              else {
                  thisValue = thisValue[0];
              }
          }
  
          if (thisValue == null || isNaN(thisValue)) {
              thisValue = sum;
          }
          // Value should not less than 0.
          if (thisValue < 0) {
              thisValue = 0;
          }
  
          arrValueLength >= 0
              ? (dataNode.value[0] = thisValue)
              : (dataNode.value = thisValue);
      }
  
      /**
       * set default to level configuration
       */
      function setDefault(levels, ecModel) {
          var globalColorList = ecModel.get('color');
  
          if (!globalColorList) {
              return;
          }
  
          levels = levels || [];
          var hasColorDefine;
          zrUtil.each(levels, function (levelDefine) {
              var model = new Model(levelDefine);
              var modelColor = model.get('color');
              if (model.get('itemStyle.normal.color')
                  || (modelColor && modelColor !== 'none')
              ) {
                  hasColorDefine = true;
              }
          });
  
          if (!hasColorDefine) {
              var level0 = levels[0] || (levels[0] = {});
              level0.color = globalColorList.slice();
          }
  
          return levels;
      }
  
  });