text: define(function (require) {
  
      var VisualMapping = require('../../visual/VisualMapping');
      var zrColor = require('zrender/tool/color');
      var zrUtil = require('zrender/core/util');
      var isArray = zrUtil.isArray;
  
      var ITEM_STYLE_NORMAL = 'itemStyle.normal';
  
      return function (ecModel, payload) {
  
          var condition = {mainType: 'series', subType: 'treemap', query: payload};
          ecModel.eachComponent(condition, function (seriesModel) {
  
              var tree = seriesModel.getData().tree;
              var root = tree.root;
              var seriesItemStyleModel = seriesModel.getModel(ITEM_STYLE_NORMAL);
  
              if (root.isRemoved()) {
                  return;
              }
  
              var levelItemStyles = zrUtil.map(tree.levelModels, function (levelModel) {
                  return levelModel ? levelModel.get(ITEM_STYLE_NORMAL) : null;
              });
  
              travelTree(
                  root,
                  {},
                  levelItemStyles,
                  seriesItemStyleModel,
                  seriesModel.getViewRoot().getAncestors(),
                  seriesModel
              );
          });
      };
  
      function travelTree(
          node, designatedVisual, levelItemStyles, seriesItemStyleModel,
          viewRootAncestors, seriesModel
      ) {
          var nodeModel = node.getModel();
          var nodeLayout = node.getLayout();
  
          // Optimize
          if (nodeLayout.invisible) {
              return;
          }
  
          var nodeItemStyleModel = node.getModel(ITEM_STYLE_NORMAL);
          var levelItemStyle = levelItemStyles[node.depth];
          var visuals = buildVisuals(
              nodeItemStyleModel, designatedVisual, levelItemStyle, seriesItemStyleModel
          );
  
          // calculate border color
          var borderColor = nodeItemStyleModel.get('borderColor');
          var borderColorSaturation = nodeItemStyleModel.get('borderColorSaturation');
          var thisNodeColor;
          if (borderColorSaturation != null) {
              // For performance, do not always execute 'calculateColor'.
              thisNodeColor = calculateColor(visuals, node);
              borderColor = calculateBorderColor(borderColorSaturation, thisNodeColor);
          }
          node.setVisual('borderColor', borderColor);
  
          var viewChildren = node.viewChildren;
          if (!viewChildren || !viewChildren.length) {
              thisNodeColor = calculateColor(visuals, node);
              // Apply visual to this node.
              node.setVisual('color', thisNodeColor);
          }
          else {
              var mapping = buildVisualMapping(
                  node, nodeModel, nodeLayout, nodeItemStyleModel, visuals, viewChildren
              );
              // Designate visual to children.
              zrUtil.each(viewChildren, function (child, index) {
                  // If higher than viewRoot, only ancestors of viewRoot is needed to visit.
                  if (child.depth >= viewRootAncestors.length
                      || child === viewRootAncestors[child.depth]
                  ) {
                      var childVisual = mapVisual(
                          nodeModel, visuals, child, index, mapping, seriesModel
                      );
                      travelTree(
                          child, childVisual, levelItemStyles, seriesItemStyleModel,
                          viewRootAncestors, seriesModel
                      );
                  }
              });
          }
      }
  
      function buildVisuals(
          nodeItemStyleModel, designatedVisual, levelItemStyle, seriesItemStyleModel
      ) {
          var visuals = zrUtil.extend({}, designatedVisual);
  
          zrUtil.each(['color', 'colorAlpha', 'colorSaturation'], function (visualName) {
              // Priority: thisNode > thisLevel > parentNodeDesignated > seriesModel
              var val = nodeItemStyleModel.get(visualName, true); // Ignore parent
              val == null && levelItemStyle && (val = levelItemStyle[visualName]);
              val == null && (val = designatedVisual[visualName]);
              val == null && (val = seriesItemStyleModel.get(visualName));
  
              val != null && (visuals[visualName] = val);
          });
  
          return visuals;
      }
  
      function calculateColor(visuals) {
          var color = getValueVisualDefine(visuals, 'color');
  
          if (color) {
              var colorAlpha = getValueVisualDefine(visuals, 'colorAlpha');
              var colorSaturation = getValueVisualDefine(visuals, 'colorSaturation');
              if (colorSaturation) {
                  color = zrColor.modifyHSL(color, null, null, colorSaturation);
              }
              if (colorAlpha) {
                  color = zrColor.modifyAlpha(color, colorAlpha);
              }
  
              return color;
          }
      }
  
      function calculateBorderColor(borderColorSaturation, thisNodeColor) {
          return thisNodeColor != null
               ? zrColor.modifyHSL(thisNodeColor, null, null, borderColorSaturation)
               : null;
      }
  
      function getValueVisualDefine(visuals, name) {
          var value = visuals[name];
          if (value != null && value !== 'none') {
              return value;
          }
      }
  
      function buildVisualMapping(
          node, nodeModel, nodeLayout, nodeItemStyleModel, visuals, viewChildren
      ) {
          if (!viewChildren || !viewChildren.length) {
              return;
          }
  
          var rangeVisual = getRangeVisual(nodeModel, 'color')
              || (
                  visuals.color != null
                  && visuals.color !== 'none'
                  && (
                      getRangeVisual(nodeModel, 'colorAlpha')
                      || getRangeVisual(nodeModel, 'colorSaturation')
                  )
              );
  
          if (!rangeVisual) {
              return;
          }
  
          var colorMappingBy = nodeModel.get('colorMappingBy');
          var opt = {
              type: rangeVisual.name,
              dataExtent: nodeLayout.dataExtent,
              visual: rangeVisual.range
          };
          if (opt.type === 'color'
              && (colorMappingBy === 'index' || colorMappingBy === 'id')
          ) {
              opt.mappingMethod = 'category';
              opt.loop = true;
              // categories is ordinal, so do not set opt.categories.
          }
          else {
              opt.mappingMethod = 'linear';
          }
  
          var mapping = new VisualMapping(opt);
          mapping.__drColorMappingBy = colorMappingBy;
  
          return mapping;
      }
  
      // Notice: If we dont have the attribute 'colorRange', but only use
      // attribute 'color' to represent both concepts of 'colorRange' and 'color',
      // (It means 'colorRange' when 'color' is Array, means 'color' when not array),
      // this problem will be encountered:
      // If a level-1 node dont have children, and its siblings has children,
      // and colorRange is set on level-1, then the node can not be colored.
      // So we separate 'colorRange' and 'color' to different attributes.
      function getRangeVisual(nodeModel, name) {
          // 'colorRange', 'colorARange', 'colorSRange'.
          // If not exsits on this node, fetch from levels and series.
          var range = nodeModel.get(name);
          return (isArray(range) && range.length) ? {name: name, range: range} : null;
      }
  
      function mapVisual(nodeModel, visuals, child, index, mapping, seriesModel) {
          var childVisuals = zrUtil.extend({}, visuals);
  
          if (mapping) {
              var mappingType = mapping.type;
              var colorMappingBy = mappingType === 'color' && mapping.__drColorMappingBy;
              var value =
                  colorMappingBy === 'index'
                  ? index
                  : colorMappingBy === 'id'
                  ? seriesModel.mapIdToIndex(child.getId())
                  : child.getValue(nodeModel.get('visualDimension'));
  
              childVisuals[mappingType] = mapping.mapValueToVisual(value);
          }
  
          return childVisuals;
      }
  
  });