You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							1294 lines
						
					
					
						
							43 KiB
						
					
					
				
			
		
		
	
	
							1294 lines
						
					
					
						
							43 KiB
						
					
					
				
 | 
						|
/* | 
						|
* 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 pathTool = require("zrender/lib/tool/path"); | 
						|
 | 
						|
var colorTool = require("zrender/lib/tool/color"); | 
						|
 | 
						|
var matrix = require("zrender/lib/core/matrix"); | 
						|
 | 
						|
var vector = require("zrender/lib/core/vector"); | 
						|
 | 
						|
var Path = require("zrender/lib/graphic/Path"); | 
						|
 | 
						|
var Transformable = require("zrender/lib/mixin/Transformable"); | 
						|
 | 
						|
var ZImage = require("zrender/lib/graphic/Image"); | 
						|
 | 
						|
exports.Image = ZImage; | 
						|
 | 
						|
var Group = require("zrender/lib/container/Group"); | 
						|
 | 
						|
exports.Group = Group; | 
						|
 | 
						|
var Text = require("zrender/lib/graphic/Text"); | 
						|
 | 
						|
exports.Text = Text; | 
						|
 | 
						|
var Circle = require("zrender/lib/graphic/shape/Circle"); | 
						|
 | 
						|
exports.Circle = Circle; | 
						|
 | 
						|
var Sector = require("zrender/lib/graphic/shape/Sector"); | 
						|
 | 
						|
exports.Sector = Sector; | 
						|
 | 
						|
var Ring = require("zrender/lib/graphic/shape/Ring"); | 
						|
 | 
						|
exports.Ring = Ring; | 
						|
 | 
						|
var Polygon = require("zrender/lib/graphic/shape/Polygon"); | 
						|
 | 
						|
exports.Polygon = Polygon; | 
						|
 | 
						|
var Polyline = require("zrender/lib/graphic/shape/Polyline"); | 
						|
 | 
						|
exports.Polyline = Polyline; | 
						|
 | 
						|
var Rect = require("zrender/lib/graphic/shape/Rect"); | 
						|
 | 
						|
exports.Rect = Rect; | 
						|
 | 
						|
var Line = require("zrender/lib/graphic/shape/Line"); | 
						|
 | 
						|
exports.Line = Line; | 
						|
 | 
						|
var BezierCurve = require("zrender/lib/graphic/shape/BezierCurve"); | 
						|
 | 
						|
exports.BezierCurve = BezierCurve; | 
						|
 | 
						|
var Arc = require("zrender/lib/graphic/shape/Arc"); | 
						|
 | 
						|
exports.Arc = Arc; | 
						|
 | 
						|
var CompoundPath = require("zrender/lib/graphic/CompoundPath"); | 
						|
 | 
						|
exports.CompoundPath = CompoundPath; | 
						|
 | 
						|
var LinearGradient = require("zrender/lib/graphic/LinearGradient"); | 
						|
 | 
						|
exports.LinearGradient = LinearGradient; | 
						|
 | 
						|
var RadialGradient = require("zrender/lib/graphic/RadialGradient"); | 
						|
 | 
						|
exports.RadialGradient = RadialGradient; | 
						|
 | 
						|
var BoundingRect = require("zrender/lib/core/BoundingRect"); | 
						|
 | 
						|
exports.BoundingRect = BoundingRect; | 
						|
 | 
						|
var IncrementalDisplayable = require("zrender/lib/graphic/IncrementalDisplayable"); | 
						|
 | 
						|
exports.IncrementalDisplayable = IncrementalDisplayable; | 
						|
 | 
						|
/* | 
						|
* 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 round = Math.round; | 
						|
var mathMax = Math.max; | 
						|
var mathMin = Math.min; | 
						|
var EMPTY_OBJ = {}; | 
						|
var Z2_EMPHASIS_LIFT = 1; | 
						|
/** | 
						|
 * Extend shape with parameters | 
						|
 */ | 
						|
 | 
						|
function extendShape(opts) { | 
						|
  return Path.extend(opts); | 
						|
} | 
						|
/** | 
						|
 * Extend path | 
						|
 */ | 
						|
 | 
						|
 | 
						|
function extendPath(pathData, opts) { | 
						|
  return pathTool.extendFromString(pathData, opts); | 
						|
} | 
						|
/** | 
						|
 * Create a path element from path data string | 
						|
 * @param {string} pathData | 
						|
 * @param {Object} opts | 
						|
 * @param {module:zrender/core/BoundingRect} rect | 
						|
 * @param {string} [layout=cover] 'center' or 'cover' | 
						|
 */ | 
						|
 | 
						|
 | 
						|
function makePath(pathData, opts, rect, layout) { | 
						|
  var path = pathTool.createFromString(pathData, opts); | 
						|
 | 
						|
  if (rect) { | 
						|
    if (layout === 'center') { | 
						|
      rect = centerGraphic(rect, path.getBoundingRect()); | 
						|
    } | 
						|
 | 
						|
    resizePath(path, rect); | 
						|
  } | 
						|
 | 
						|
  return path; | 
						|
} | 
						|
/** | 
						|
 * Create a image element from image url | 
						|
 * @param {string} imageUrl image url | 
						|
 * @param {Object} opts options | 
						|
 * @param {module:zrender/core/BoundingRect} rect constrain rect | 
						|
 * @param {string} [layout=cover] 'center' or 'cover' | 
						|
 */ | 
						|
 | 
						|
 | 
						|
function makeImage(imageUrl, rect, layout) { | 
						|
  var path = new ZImage({ | 
						|
    style: { | 
						|
      image: imageUrl, | 
						|
      x: rect.x, | 
						|
      y: rect.y, | 
						|
      width: rect.width, | 
						|
      height: rect.height | 
						|
    }, | 
						|
    onload: function (img) { | 
						|
      if (layout === 'center') { | 
						|
        var boundingRect = { | 
						|
          width: img.width, | 
						|
          height: img.height | 
						|
        }; | 
						|
        path.setStyle(centerGraphic(rect, boundingRect)); | 
						|
      } | 
						|
    } | 
						|
  }); | 
						|
  return path; | 
						|
} | 
						|
/** | 
						|
 * Get position of centered element in bounding box. | 
						|
 * | 
						|
 * @param  {Object} rect         element local bounding box | 
						|
 * @param  {Object} boundingRect constraint bounding box | 
						|
 * @return {Object} element position containing x, y, width, and height | 
						|
 */ | 
						|
 | 
						|
 | 
						|
function centerGraphic(rect, boundingRect) { | 
						|
  // Set rect to center, keep width / height ratio. | 
						|
  var aspect = boundingRect.width / boundingRect.height; | 
						|
  var width = rect.height * aspect; | 
						|
  var height; | 
						|
 | 
						|
  if (width <= rect.width) { | 
						|
    height = rect.height; | 
						|
  } else { | 
						|
    width = rect.width; | 
						|
    height = width / aspect; | 
						|
  } | 
						|
 | 
						|
  var cx = rect.x + rect.width / 2; | 
						|
  var cy = rect.y + rect.height / 2; | 
						|
  return { | 
						|
    x: cx - width / 2, | 
						|
    y: cy - height / 2, | 
						|
    width: width, | 
						|
    height: height | 
						|
  }; | 
						|
} | 
						|
 | 
						|
var mergePath = pathTool.mergePath; | 
						|
/** | 
						|
 * Resize a path to fit the rect | 
						|
 * @param {module:zrender/graphic/Path} path | 
						|
 * @param {Object} rect | 
						|
 */ | 
						|
 | 
						|
function resizePath(path, rect) { | 
						|
  if (!path.applyTransform) { | 
						|
    return; | 
						|
  } | 
						|
 | 
						|
  var pathRect = path.getBoundingRect(); | 
						|
  var m = pathRect.calculateTransform(rect); | 
						|
  path.applyTransform(m); | 
						|
} | 
						|
/** | 
						|
 * Sub pixel optimize line for canvas | 
						|
 * | 
						|
 * @param {Object} param | 
						|
 * @param {Object} [param.shape] | 
						|
 * @param {number} [param.shape.x1] | 
						|
 * @param {number} [param.shape.y1] | 
						|
 * @param {number} [param.shape.x2] | 
						|
 * @param {number} [param.shape.y2] | 
						|
 * @param {Object} [param.style] | 
						|
 * @param {number} [param.style.lineWidth] | 
						|
 * @return {Object} Modified param | 
						|
 */ | 
						|
 | 
						|
 | 
						|
function subPixelOptimizeLine(param) { | 
						|
  var shape = param.shape; | 
						|
  var lineWidth = param.style.lineWidth; | 
						|
 | 
						|
  if (round(shape.x1 * 2) === round(shape.x2 * 2)) { | 
						|
    shape.x1 = shape.x2 = subPixelOptimize(shape.x1, lineWidth, true); | 
						|
  } | 
						|
 | 
						|
  if (round(shape.y1 * 2) === round(shape.y2 * 2)) { | 
						|
    shape.y1 = shape.y2 = subPixelOptimize(shape.y1, lineWidth, true); | 
						|
  } | 
						|
 | 
						|
  return param; | 
						|
} | 
						|
/** | 
						|
 * Sub pixel optimize rect for canvas | 
						|
 * | 
						|
 * @param {Object} param | 
						|
 * @param {Object} [param.shape] | 
						|
 * @param {number} [param.shape.x] | 
						|
 * @param {number} [param.shape.y] | 
						|
 * @param {number} [param.shape.width] | 
						|
 * @param {number} [param.shape.height] | 
						|
 * @param {Object} [param.style] | 
						|
 * @param {number} [param.style.lineWidth] | 
						|
 * @return {Object} Modified param | 
						|
 */ | 
						|
 | 
						|
 | 
						|
function subPixelOptimizeRect(param) { | 
						|
  var shape = param.shape; | 
						|
  var lineWidth = param.style.lineWidth; | 
						|
  var originX = shape.x; | 
						|
  var originY = shape.y; | 
						|
  var originWidth = shape.width; | 
						|
  var originHeight = shape.height; | 
						|
  shape.x = subPixelOptimize(shape.x, lineWidth, true); | 
						|
  shape.y = subPixelOptimize(shape.y, lineWidth, true); | 
						|
  shape.width = Math.max(subPixelOptimize(originX + originWidth, lineWidth, false) - shape.x, originWidth === 0 ? 0 : 1); | 
						|
  shape.height = Math.max(subPixelOptimize(originY + originHeight, lineWidth, false) - shape.y, originHeight === 0 ? 0 : 1); | 
						|
  return param; | 
						|
} | 
						|
/** | 
						|
 * Sub pixel optimize for canvas | 
						|
 * | 
						|
 * @param {number} position Coordinate, such as x, y | 
						|
 * @param {number} lineWidth Should be nonnegative integer. | 
						|
 * @param {boolean=} positiveOrNegative Default false (negative). | 
						|
 * @return {number} Optimized position. | 
						|
 */ | 
						|
 | 
						|
 | 
						|
function subPixelOptimize(position, lineWidth, positiveOrNegative) { | 
						|
  // Assure that (position + lineWidth / 2) is near integer edge, | 
						|
  // otherwise line will be fuzzy in canvas. | 
						|
  var doubledPosition = round(position * 2); | 
						|
  return (doubledPosition + round(lineWidth)) % 2 === 0 ? doubledPosition / 2 : (doubledPosition + (positiveOrNegative ? 1 : -1)) / 2; | 
						|
} | 
						|
 | 
						|
function hasFillOrStroke(fillOrStroke) { | 
						|
  return fillOrStroke != null && fillOrStroke !== 'none'; | 
						|
} // Most lifted color are duplicated. | 
						|
 | 
						|
 | 
						|
var liftedColorMap = zrUtil.createHashMap(); | 
						|
var liftedColorCount = 0; | 
						|
 | 
						|
function liftColor(color) { | 
						|
  if (typeof color !== 'string') { | 
						|
    return color; | 
						|
  } | 
						|
 | 
						|
  var liftedColor = liftedColorMap.get(color); | 
						|
 | 
						|
  if (!liftedColor) { | 
						|
    liftedColor = colorTool.lift(color, -0.1); | 
						|
 | 
						|
    if (liftedColorCount < 10000) { | 
						|
      liftedColorMap.set(color, liftedColor); | 
						|
      liftedColorCount++; | 
						|
    } | 
						|
  } | 
						|
 | 
						|
  return liftedColor; | 
						|
} | 
						|
 | 
						|
function cacheElementStl(el) { | 
						|
  if (!el.__hoverStlDirty) { | 
						|
    return; | 
						|
  } | 
						|
 | 
						|
  el.__hoverStlDirty = false; | 
						|
  var hoverStyle = el.__hoverStl; | 
						|
 | 
						|
  if (!hoverStyle) { | 
						|
    el.__cachedNormalStl = el.__cachedNormalZ2 = null; | 
						|
    return; | 
						|
  } | 
						|
 | 
						|
  var normalStyle = el.__cachedNormalStl = {}; | 
						|
  el.__cachedNormalZ2 = el.z2; | 
						|
  var elStyle = el.style; | 
						|
 | 
						|
  for (var name in hoverStyle) { | 
						|
    // See comment in `doSingleEnterHover`. | 
						|
    if (hoverStyle[name] != null) { | 
						|
      normalStyle[name] = elStyle[name]; | 
						|
    } | 
						|
  } // Always cache fill and stroke to normalStyle for lifting color. | 
						|
 | 
						|
 | 
						|
  normalStyle.fill = elStyle.fill; | 
						|
  normalStyle.stroke = elStyle.stroke; | 
						|
} | 
						|
 | 
						|
function doSingleEnterHover(el) { | 
						|
  var hoverStl = el.__hoverStl; | 
						|
 | 
						|
  if (!hoverStl || el.__highlighted) { | 
						|
    return; | 
						|
  } | 
						|
 | 
						|
  var useHoverLayer = el.useHoverLayer; | 
						|
  el.__highlighted = useHoverLayer ? 'layer' : 'plain'; | 
						|
  var zr = el.__zr; | 
						|
 | 
						|
  if (!zr && useHoverLayer) { | 
						|
    return; | 
						|
  } | 
						|
 | 
						|
  var elTarget = el; | 
						|
  var targetStyle = el.style; | 
						|
 | 
						|
  if (useHoverLayer) { | 
						|
    elTarget = zr.addHover(el); | 
						|
    targetStyle = elTarget.style; | 
						|
  } | 
						|
 | 
						|
  rollbackDefaultTextStyle(targetStyle); | 
						|
 | 
						|
  if (!useHoverLayer) { | 
						|
    cacheElementStl(elTarget); | 
						|
  } // styles can be: | 
						|
  // { | 
						|
  //    label: { | 
						|
  //        show: false, | 
						|
  //        position: 'outside', | 
						|
  //        fontSize: 18 | 
						|
  //    }, | 
						|
  //    emphasis: { | 
						|
  //        label: { | 
						|
  //            show: true | 
						|
  //        } | 
						|
  //    } | 
						|
  // }, | 
						|
  // where properties of `emphasis` may not appear in `normal`. We previously use | 
						|
  // module:echarts/util/model#defaultEmphasis to merge `normal` to `emphasis`. | 
						|
  // But consider rich text and setOption in merge mode, it is impossible to cover | 
						|
  // all properties in merge. So we use merge mode when setting style here, where | 
						|
  // only properties that is not `null/undefined` can be set. The disadventage: | 
						|
  // null/undefined can not be used to remove style any more in `emphasis`. | 
						|
 | 
						|
 | 
						|
  targetStyle.extendFrom(hoverStl); | 
						|
  setDefaultHoverFillStroke(targetStyle, hoverStl, 'fill'); | 
						|
  setDefaultHoverFillStroke(targetStyle, hoverStl, 'stroke'); | 
						|
  applyDefaultTextStyle(targetStyle); | 
						|
 | 
						|
  if (!useHoverLayer) { | 
						|
    el.dirty(false); | 
						|
    el.z2 += Z2_EMPHASIS_LIFT; | 
						|
  } | 
						|
} | 
						|
 | 
						|
function setDefaultHoverFillStroke(targetStyle, hoverStyle, prop) { | 
						|
  if (!hasFillOrStroke(hoverStyle[prop]) && hasFillOrStroke(targetStyle[prop])) { | 
						|
    targetStyle[prop] = liftColor(targetStyle[prop]); | 
						|
  } | 
						|
} | 
						|
 | 
						|
function doSingleLeaveHover(el) { | 
						|
  var highlighted = el.__highlighted; | 
						|
 | 
						|
  if (!highlighted) { | 
						|
    return; | 
						|
  } | 
						|
 | 
						|
  el.__highlighted = false; | 
						|
 | 
						|
  if (highlighted === 'layer') { | 
						|
    el.__zr && el.__zr.removeHover(el); | 
						|
  } else if (highlighted) { | 
						|
    var style = el.style; | 
						|
    var normalStl = el.__cachedNormalStl; | 
						|
 | 
						|
    if (normalStl) { | 
						|
      rollbackDefaultTextStyle(style); // Consider null/undefined value, should use | 
						|
      // `setStyle` but not `extendFrom(stl, true)`. | 
						|
 | 
						|
      el.setStyle(normalStl); | 
						|
      applyDefaultTextStyle(style); | 
						|
    } // `__cachedNormalZ2` will not be reset if calling `setElementHoverStyle` | 
						|
    // when `el` is on emphasis state. So here by comparing with 1, we try | 
						|
    // hard to make the bug case rare. | 
						|
 | 
						|
 | 
						|
    var normalZ2 = el.__cachedNormalZ2; | 
						|
 | 
						|
    if (normalZ2 != null && el.z2 - normalZ2 === Z2_EMPHASIS_LIFT) { | 
						|
      el.z2 = normalZ2; | 
						|
    } | 
						|
  } | 
						|
} | 
						|
 | 
						|
function traverseCall(el, method) { | 
						|
  el.isGroup ? el.traverse(function (child) { | 
						|
    !child.isGroup && method(child); | 
						|
  }) : method(el); | 
						|
} | 
						|
/** | 
						|
 * Set hover style (namely "emphasis style") of element, based on the current | 
						|
 * style of the given `el`. | 
						|
 * This method should be called after all of the normal styles have been adopted | 
						|
 * to the `el`. See the reason on `setHoverStyle`. | 
						|
 * | 
						|
 * @param {module:zrender/Element} el Should not be `zrender/container/Group`. | 
						|
 * @param {Object|boolean} [hoverStl] The specified hover style. | 
						|
 *        If set as `false`, disable the hover style. | 
						|
 *        Similarly, The `el.hoverStyle` can alse be set | 
						|
 *        as `false` to disable the hover style. | 
						|
 *        Otherwise, use the default hover style if not provided. | 
						|
 * @param {Object} [opt] | 
						|
 * @param {boolean} [opt.hoverSilentOnTouch=false] See `graphic.setAsHoverStyleTrigger` | 
						|
 */ | 
						|
 | 
						|
 | 
						|
function setElementHoverStyle(el, hoverStl) { | 
						|
  // For performance consideration, it might be better to make the "hover style" only the | 
						|
  // difference properties from the "normal style", but not a entire copy of all styles. | 
						|
  hoverStl = el.__hoverStl = hoverStl !== false && (hoverStl || {}); | 
						|
  el.__hoverStlDirty = true; // FIXME | 
						|
  // It is not completely right to save "normal"/"emphasis" flag on elements. | 
						|
  // It probably should be saved on `data` of series. Consider the cases: | 
						|
  // (1) A highlighted elements are moved out of the view port and re-enter | 
						|
  // again by dataZoom. | 
						|
  // (2) call `setOption` and replace elements totally when they are highlighted. | 
						|
 | 
						|
  if (el.__highlighted) { | 
						|
    // Consider the case: | 
						|
    // The styles of a highlighted `el` is being updated. The new "emphasis style" | 
						|
    // should be adapted to the `el`. Notice here new "normal styles" should have | 
						|
    // been set outside and the cached "normal style" is out of date. | 
						|
    el.__cachedNormalStl = null; // Do not clear `__cachedNormalZ2` here, because setting `z2` is not a constraint | 
						|
    // of this method. In most cases, `z2` is not set and hover style should be able | 
						|
    // to rollback. Of course, that would bring bug, but only in a rare case, see | 
						|
    // `doSingleLeaveHover` for details. | 
						|
 | 
						|
    doSingleLeaveHover(el); | 
						|
    doSingleEnterHover(el); | 
						|
  } | 
						|
} | 
						|
/** | 
						|
 * Emphasis (called by API) has higher priority than `mouseover`. | 
						|
 * When element has been called to be entered emphasis, mouse over | 
						|
 * should not trigger the highlight effect (for example, animation | 
						|
 * scale) again, and `mouseout` should not downplay the highlight | 
						|
 * effect. So the listener of `mouseover` and `mouseout` should | 
						|
 * check `isInEmphasis`. | 
						|
 * | 
						|
 * @param {module:zrender/Element} el | 
						|
 * @return {boolean} | 
						|
 */ | 
						|
 | 
						|
 | 
						|
function isInEmphasis(el) { | 
						|
  return el && el.__isEmphasisEntered; | 
						|
} | 
						|
 | 
						|
function onElementMouseOver(e) { | 
						|
  if (this.__hoverSilentOnTouch && e.zrByTouch) { | 
						|
    return; | 
						|
  } // Only if element is not in emphasis status | 
						|
 | 
						|
 | 
						|
  !this.__isEmphasisEntered && traverseCall(this, doSingleEnterHover); | 
						|
} | 
						|
 | 
						|
function onElementMouseOut(e) { | 
						|
  if (this.__hoverSilentOnTouch && e.zrByTouch) { | 
						|
    return; | 
						|
  } // Only if element is not in emphasis status | 
						|
 | 
						|
 | 
						|
  !this.__isEmphasisEntered && traverseCall(this, doSingleLeaveHover); | 
						|
} | 
						|
 | 
						|
function enterEmphasis() { | 
						|
  this.__isEmphasisEntered = true; | 
						|
  traverseCall(this, doSingleEnterHover); | 
						|
} | 
						|
 | 
						|
function leaveEmphasis() { | 
						|
  this.__isEmphasisEntered = false; | 
						|
  traverseCall(this, doSingleLeaveHover); | 
						|
} | 
						|
/** | 
						|
 * Set hover style (namely "emphasis style") of element, | 
						|
 * based on the current style of the given `el`. | 
						|
 * | 
						|
 * (1) | 
						|
 * **CONSTRAINTS** for this method: | 
						|
 * <A> This method MUST be called after all of the normal styles having been adopted | 
						|
 * to the `el`. | 
						|
 * <B> The input `hoverStyle` (that is, "emphasis style") MUST be the subset of the | 
						|
 * "normal style" having been set to the el. | 
						|
 * <C> `color` MUST be one of the "normal styles" (because color might be lifted as | 
						|
 * a default hover style). | 
						|
 * | 
						|
 * The reason: this method treat the current style of the `el` as the "normal style" | 
						|
 * and cache them when enter/update the "emphasis style". Consider the case: the `el` | 
						|
 * is in "emphasis" state and `setOption`/`dispatchAction` trigger the style updating | 
						|
 * logic, where the el should shift from the original emphasis style to the new | 
						|
 * "emphasis style" and should be able to "downplay" back to the new "normal style". | 
						|
 * | 
						|
 * Indeed, it is error-prone to make a interface has so many constraints, but I have | 
						|
 * not found a better solution yet to fit the backward compatibility, performance and | 
						|
 * the current programming style. | 
						|
 * | 
						|
 * (2) | 
						|
 * Call the method for a "root" element once. Do not call it for each descendants. | 
						|
 * If the descendants elemenets of a group has itself hover style different from the | 
						|
 * root group, we can simply mount the style on `el.hoverStyle` for them, but should | 
						|
 * not call this method for them. | 
						|
 * | 
						|
 * @param {module:zrender/Element} el | 
						|
 * @param {Object|boolean} [hoverStyle] See `graphic.setElementHoverStyle`. | 
						|
 * @param {Object} [opt] | 
						|
 * @param {boolean} [opt.hoverSilentOnTouch=false] See `graphic.setAsHoverStyleTrigger`. | 
						|
 */ | 
						|
 | 
						|
 | 
						|
function setHoverStyle(el, hoverStyle, opt) { | 
						|
  el.isGroup ? el.traverse(function (child) { | 
						|
    // If element has sepcified hoverStyle, then use it instead of given hoverStyle | 
						|
    // Often used when item group has a label element and it's hoverStyle is different | 
						|
    !child.isGroup && setElementHoverStyle(child, child.hoverStyle || hoverStyle); | 
						|
  }) : setElementHoverStyle(el, el.hoverStyle || hoverStyle); | 
						|
  setAsHoverStyleTrigger(el, opt); | 
						|
} | 
						|
/** | 
						|
 * @param {Object|boolean} [opt] If `false`, means disable trigger. | 
						|
 * @param {boolean} [opt.hoverSilentOnTouch=false] | 
						|
 *        In touch device, mouseover event will be trigger on touchstart event | 
						|
 *        (see module:zrender/dom/HandlerProxy). By this mechanism, we can | 
						|
 *        conveniently use hoverStyle when tap on touch screen without additional | 
						|
 *        code for compatibility. | 
						|
 *        But if the chart/component has select feature, which usually also use | 
						|
 *        hoverStyle, there might be conflict between 'select-highlight' and | 
						|
 *        'hover-highlight' especially when roam is enabled (see geo for example). | 
						|
 *        In this case, hoverSilentOnTouch should be used to disable hover-highlight | 
						|
 *        on touch device. | 
						|
 */ | 
						|
 | 
						|
 | 
						|
function setAsHoverStyleTrigger(el, opt) { | 
						|
  var disable = opt === false; | 
						|
  el.__hoverSilentOnTouch = opt != null && opt.hoverSilentOnTouch; // Simple optimize, since this method might be | 
						|
  // called for each elements of a group in some cases. | 
						|
 | 
						|
  if (!disable || el.__hoverStyleTrigger) { | 
						|
    var method = disable ? 'off' : 'on'; // Duplicated function will be auto-ignored, see Eventful.js. | 
						|
 | 
						|
    el[method]('mouseover', onElementMouseOver)[method]('mouseout', onElementMouseOut); // Emphasis, normal can be triggered manually | 
						|
 | 
						|
    el[method]('emphasis', enterEmphasis)[method]('normal', leaveEmphasis); | 
						|
    el.__hoverStyleTrigger = !disable; | 
						|
  } | 
						|
} | 
						|
/** | 
						|
 * See more info in `setTextStyleCommon`. | 
						|
 * @param {Object|module:zrender/graphic/Style} normalStyle | 
						|
 * @param {Object} emphasisStyle | 
						|
 * @param {module:echarts/model/Model} normalModel | 
						|
 * @param {module:echarts/model/Model} emphasisModel | 
						|
 * @param {Object} opt Check `opt` of `setTextStyleCommon` to find other props. | 
						|
 * @param {string|Function} [opt.defaultText] | 
						|
 * @param {module:echarts/model/Model} [opt.labelFetcher] Fetch text by | 
						|
 *      `opt.labelFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex)` | 
						|
 * @param {module:echarts/model/Model} [opt.labelDataIndex] Fetch text by | 
						|
 *      `opt.textFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex)` | 
						|
 * @param {module:echarts/model/Model} [opt.labelDimIndex] Fetch text by | 
						|
 *      `opt.textFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex)` | 
						|
 * @param {Object} [normalSpecified] | 
						|
 * @param {Object} [emphasisSpecified] | 
						|
 */ | 
						|
 | 
						|
 | 
						|
function setLabelStyle(normalStyle, emphasisStyle, normalModel, emphasisModel, opt, normalSpecified, emphasisSpecified) { | 
						|
  opt = opt || EMPTY_OBJ; | 
						|
  var labelFetcher = opt.labelFetcher; | 
						|
  var labelDataIndex = opt.labelDataIndex; | 
						|
  var labelDimIndex = opt.labelDimIndex; // This scenario, `label.normal.show = true; label.emphasis.show = false`, | 
						|
  // is not supported util someone requests. | 
						|
 | 
						|
  var showNormal = normalModel.getShallow('show'); | 
						|
  var showEmphasis = emphasisModel.getShallow('show'); // Consider performance, only fetch label when necessary. | 
						|
  // If `normal.show` is `false` and `emphasis.show` is `true` and `emphasis.formatter` is not set, | 
						|
  // label should be displayed, where text is fetched by `normal.formatter` or `opt.defaultText`. | 
						|
 | 
						|
  var baseText; | 
						|
 | 
						|
  if (showNormal || showEmphasis) { | 
						|
    if (labelFetcher) { | 
						|
      baseText = labelFetcher.getFormattedLabel(labelDataIndex, 'normal', null, labelDimIndex); | 
						|
    } | 
						|
 | 
						|
    if (baseText == null) { | 
						|
      baseText = zrUtil.isFunction(opt.defaultText) ? opt.defaultText(labelDataIndex, opt) : opt.defaultText; | 
						|
    } | 
						|
  } | 
						|
 | 
						|
  var normalStyleText = showNormal ? baseText : null; | 
						|
  var emphasisStyleText = showEmphasis ? zrUtil.retrieve2(labelFetcher ? labelFetcher.getFormattedLabel(labelDataIndex, 'emphasis', null, labelDimIndex) : null, baseText) : null; // Optimize: If style.text is null, text will not be drawn. | 
						|
 | 
						|
  if (normalStyleText != null || emphasisStyleText != null) { | 
						|
    // Always set `textStyle` even if `normalStyle.text` is null, because default | 
						|
    // values have to be set on `normalStyle`. | 
						|
    // If we set default values on `emphasisStyle`, consider case: | 
						|
    // Firstly, `setOption(... label: {normal: {text: null}, emphasis: {show: true}} ...);` | 
						|
    // Secondly, `setOption(... label: {noraml: {show: true, text: 'abc', color: 'red'} ...);` | 
						|
    // Then the 'red' will not work on emphasis. | 
						|
    setTextStyle(normalStyle, normalModel, normalSpecified, opt); | 
						|
    setTextStyle(emphasisStyle, emphasisModel, emphasisSpecified, opt, true); | 
						|
  } | 
						|
 | 
						|
  normalStyle.text = normalStyleText; | 
						|
  emphasisStyle.text = emphasisStyleText; | 
						|
} | 
						|
/** | 
						|
 * Set basic textStyle properties. | 
						|
 * See more info in `setTextStyleCommon`. | 
						|
 * @param {Object|module:zrender/graphic/Style} textStyle | 
						|
 * @param {module:echarts/model/Model} model | 
						|
 * @param {Object} [specifiedTextStyle] Can be overrided by settings in model. | 
						|
 * @param {Object} [opt] See `opt` of `setTextStyleCommon`. | 
						|
 * @param {boolean} [isEmphasis] | 
						|
 */ | 
						|
 | 
						|
 | 
						|
function setTextStyle(textStyle, textStyleModel, specifiedTextStyle, opt, isEmphasis) { | 
						|
  setTextStyleCommon(textStyle, textStyleModel, opt, isEmphasis); | 
						|
  specifiedTextStyle && zrUtil.extend(textStyle, specifiedTextStyle); // textStyle.host && textStyle.host.dirty && textStyle.host.dirty(false); | 
						|
 | 
						|
  return textStyle; | 
						|
} | 
						|
/** | 
						|
 * Set text option in the style. | 
						|
 * See more info in `setTextStyleCommon`. | 
						|
 * @deprecated | 
						|
 * @param {Object} textStyle | 
						|
 * @param {module:echarts/model/Model} labelModel | 
						|
 * @param {string|boolean} defaultColor Default text color. | 
						|
 *        If set as false, it will be processed as a emphasis style. | 
						|
 */ | 
						|
 | 
						|
 | 
						|
function setText(textStyle, labelModel, defaultColor) { | 
						|
  var opt = { | 
						|
    isRectText: true | 
						|
  }; | 
						|
  var isEmphasis; | 
						|
 | 
						|
  if (defaultColor === false) { | 
						|
    isEmphasis = true; | 
						|
  } else { | 
						|
    // Support setting color as 'auto' to get visual color. | 
						|
    opt.autoColor = defaultColor; | 
						|
  } | 
						|
 | 
						|
  setTextStyleCommon(textStyle, labelModel, opt, isEmphasis); // textStyle.host && textStyle.host.dirty && textStyle.host.dirty(false); | 
						|
} | 
						|
/** | 
						|
 * The uniform entry of set text style, that is, retrieve style definitions | 
						|
 * from `model` and set to `textStyle` object. | 
						|
 * | 
						|
 * Never in merge mode, but in overwrite mode, that is, all of the text style | 
						|
 * properties will be set. (Consider the states of normal and emphasis and | 
						|
 * default value can be adopted, merge would make the logic too complicated | 
						|
 * to manage.) | 
						|
 * | 
						|
 * The `textStyle` object can either be a plain object or an instance of | 
						|
 * `zrender/src/graphic/Style`, and either be the style of normal or emphasis. | 
						|
 * After this mothod called, the `textStyle` object can then be used in | 
						|
 * `el.setStyle(textStyle)` or `el.hoverStyle = textStyle`. | 
						|
 * | 
						|
 * Default value will be adopted and `insideRollbackOpt` will be created. | 
						|
 * See `applyDefaultTextStyle` `rollbackDefaultTextStyle` for more details. | 
						|
 * | 
						|
 * opt: { | 
						|
 *      disableBox: boolean, Whether diable drawing box of block (outer most). | 
						|
 *      isRectText: boolean, | 
						|
 *      autoColor: string, specify a color when color is 'auto', | 
						|
 *              for textFill, textStroke, textBackgroundColor, and textBorderColor. | 
						|
 *              If autoColor specified, it is used as default textFill. | 
						|
 *      useInsideStyle: | 
						|
 *              `true`: Use inside style (textFill, textStroke, textStrokeWidth) | 
						|
 *                  if `textFill` is not specified. | 
						|
 *              `false`: Do not use inside style. | 
						|
 *              `null/undefined`: use inside style if `isRectText` is true and | 
						|
 *                  `textFill` is not specified and textPosition contains `'inside'`. | 
						|
 *      forceRich: boolean | 
						|
 * } | 
						|
 */ | 
						|
 | 
						|
 | 
						|
function setTextStyleCommon(textStyle, textStyleModel, opt, isEmphasis) { | 
						|
  // Consider there will be abnormal when merge hover style to normal style if given default value. | 
						|
  opt = opt || EMPTY_OBJ; | 
						|
 | 
						|
  if (opt.isRectText) { | 
						|
    var textPosition = textStyleModel.getShallow('position') || (isEmphasis ? null : 'inside'); // 'outside' is not a valid zr textPostion value, but used | 
						|
    // in bar series, and magric type should be considered. | 
						|
 | 
						|
    textPosition === 'outside' && (textPosition = 'top'); | 
						|
    textStyle.textPosition = textPosition; | 
						|
    textStyle.textOffset = textStyleModel.getShallow('offset'); | 
						|
    var labelRotate = textStyleModel.getShallow('rotate'); | 
						|
    labelRotate != null && (labelRotate *= Math.PI / 180); | 
						|
    textStyle.textRotation = labelRotate; | 
						|
    textStyle.textDistance = zrUtil.retrieve2(textStyleModel.getShallow('distance'), isEmphasis ? null : 5); | 
						|
  } | 
						|
 | 
						|
  var ecModel = textStyleModel.ecModel; | 
						|
  var globalTextStyle = ecModel && ecModel.option.textStyle; // Consider case: | 
						|
  // { | 
						|
  //     data: [{ | 
						|
  //         value: 12, | 
						|
  //         label: { | 
						|
  //             rich: { | 
						|
  //                 // no 'a' here but using parent 'a'. | 
						|
  //             } | 
						|
  //         } | 
						|
  //     }], | 
						|
  //     rich: { | 
						|
  //         a: { ... } | 
						|
  //     } | 
						|
  // } | 
						|
 | 
						|
  var richItemNames = getRichItemNames(textStyleModel); | 
						|
  var richResult; | 
						|
 | 
						|
  if (richItemNames) { | 
						|
    richResult = {}; | 
						|
 | 
						|
    for (var name in richItemNames) { | 
						|
      if (richItemNames.hasOwnProperty(name)) { | 
						|
        // Cascade is supported in rich. | 
						|
        var richTextStyle = textStyleModel.getModel(['rich', name]); // In rich, never `disableBox`. | 
						|
 | 
						|
        setTokenTextStyle(richResult[name] = {}, richTextStyle, globalTextStyle, opt, isEmphasis); | 
						|
      } | 
						|
    } | 
						|
  } | 
						|
 | 
						|
  textStyle.rich = richResult; | 
						|
  setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isEmphasis, true); | 
						|
 | 
						|
  if (opt.forceRich && !opt.textStyle) { | 
						|
    opt.textStyle = {}; | 
						|
  } | 
						|
 | 
						|
  return textStyle; | 
						|
} // Consider case: | 
						|
// { | 
						|
//     data: [{ | 
						|
//         value: 12, | 
						|
//         label: { | 
						|
//             rich: { | 
						|
//                 // no 'a' here but using parent 'a'. | 
						|
//             } | 
						|
//         } | 
						|
//     }], | 
						|
//     rich: { | 
						|
//         a: { ... } | 
						|
//     } | 
						|
// } | 
						|
 | 
						|
 | 
						|
function getRichItemNames(textStyleModel) { | 
						|
  // Use object to remove duplicated names. | 
						|
  var richItemNameMap; | 
						|
 | 
						|
  while (textStyleModel && textStyleModel !== textStyleModel.ecModel) { | 
						|
    var rich = (textStyleModel.option || EMPTY_OBJ).rich; | 
						|
 | 
						|
    if (rich) { | 
						|
      richItemNameMap = richItemNameMap || {}; | 
						|
 | 
						|
      for (var name in rich) { | 
						|
        if (rich.hasOwnProperty(name)) { | 
						|
          richItemNameMap[name] = 1; | 
						|
        } | 
						|
      } | 
						|
    } | 
						|
 | 
						|
    textStyleModel = textStyleModel.parentModel; | 
						|
  } | 
						|
 | 
						|
  return richItemNameMap; | 
						|
} | 
						|
 | 
						|
function setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isEmphasis, isBlock) { | 
						|
  // In merge mode, default value should not be given. | 
						|
  globalTextStyle = !isEmphasis && globalTextStyle || EMPTY_OBJ; | 
						|
  textStyle.textFill = getAutoColor(textStyleModel.getShallow('color'), opt) || globalTextStyle.color; | 
						|
  textStyle.textStroke = getAutoColor(textStyleModel.getShallow('textBorderColor'), opt) || globalTextStyle.textBorderColor; | 
						|
  textStyle.textStrokeWidth = zrUtil.retrieve2(textStyleModel.getShallow('textBorderWidth'), globalTextStyle.textBorderWidth); // Save original textPosition, because style.textPosition will be repalced by | 
						|
  // real location (like [10, 30]) in zrender. | 
						|
 | 
						|
  textStyle.insideRawTextPosition = textStyle.textPosition; | 
						|
 | 
						|
  if (!isEmphasis) { | 
						|
    if (isBlock) { | 
						|
      textStyle.insideRollbackOpt = opt; | 
						|
      applyDefaultTextStyle(textStyle); | 
						|
    } // Set default finally. | 
						|
 | 
						|
 | 
						|
    if (textStyle.textFill == null) { | 
						|
      textStyle.textFill = opt.autoColor; | 
						|
    } | 
						|
  } // Do not use `getFont` here, because merge should be supported, where | 
						|
  // part of these properties may be changed in emphasis style, and the | 
						|
  // others should remain their original value got from normal style. | 
						|
 | 
						|
 | 
						|
  textStyle.fontStyle = textStyleModel.getShallow('fontStyle') || globalTextStyle.fontStyle; | 
						|
  textStyle.fontWeight = textStyleModel.getShallow('fontWeight') || globalTextStyle.fontWeight; | 
						|
  textStyle.fontSize = textStyleModel.getShallow('fontSize') || globalTextStyle.fontSize; | 
						|
  textStyle.fontFamily = textStyleModel.getShallow('fontFamily') || globalTextStyle.fontFamily; | 
						|
  textStyle.textAlign = textStyleModel.getShallow('align'); | 
						|
  textStyle.textVerticalAlign = textStyleModel.getShallow('verticalAlign') || textStyleModel.getShallow('baseline'); | 
						|
  textStyle.textLineHeight = textStyleModel.getShallow('lineHeight'); | 
						|
  textStyle.textWidth = textStyleModel.getShallow('width'); | 
						|
  textStyle.textHeight = textStyleModel.getShallow('height'); | 
						|
  textStyle.textTag = textStyleModel.getShallow('tag'); | 
						|
 | 
						|
  if (!isBlock || !opt.disableBox) { | 
						|
    textStyle.textBackgroundColor = getAutoColor(textStyleModel.getShallow('backgroundColor'), opt); | 
						|
    textStyle.textPadding = textStyleModel.getShallow('padding'); | 
						|
    textStyle.textBorderColor = getAutoColor(textStyleModel.getShallow('borderColor'), opt); | 
						|
    textStyle.textBorderWidth = textStyleModel.getShallow('borderWidth'); | 
						|
    textStyle.textBorderRadius = textStyleModel.getShallow('borderRadius'); | 
						|
    textStyle.textBoxShadowColor = textStyleModel.getShallow('shadowColor'); | 
						|
    textStyle.textBoxShadowBlur = textStyleModel.getShallow('shadowBlur'); | 
						|
    textStyle.textBoxShadowOffsetX = textStyleModel.getShallow('shadowOffsetX'); | 
						|
    textStyle.textBoxShadowOffsetY = textStyleModel.getShallow('shadowOffsetY'); | 
						|
  } | 
						|
 | 
						|
  textStyle.textShadowColor = textStyleModel.getShallow('textShadowColor') || globalTextStyle.textShadowColor; | 
						|
  textStyle.textShadowBlur = textStyleModel.getShallow('textShadowBlur') || globalTextStyle.textShadowBlur; | 
						|
  textStyle.textShadowOffsetX = textStyleModel.getShallow('textShadowOffsetX') || globalTextStyle.textShadowOffsetX; | 
						|
  textStyle.textShadowOffsetY = textStyleModel.getShallow('textShadowOffsetY') || globalTextStyle.textShadowOffsetY; | 
						|
} | 
						|
 | 
						|
function getAutoColor(color, opt) { | 
						|
  return color !== 'auto' ? color : opt && opt.autoColor ? opt.autoColor : null; | 
						|
} | 
						|
/** | 
						|
 * Give some default value to the input `textStyle` object, based on the current settings | 
						|
 * in this `textStyle` object. | 
						|
 * | 
						|
 * The Scenario: | 
						|
 * when text position is `inside` and `textFill` is not specified, we show | 
						|
 * text border by default for better view. But it should be considered that text position | 
						|
 * might be changed when hovering or being emphasis, where the `insideRollback` is used to | 
						|
 * restore the style. | 
						|
 * | 
						|
 * Usage (& NOTICE): | 
						|
 * When a style object (eithor plain object or instance of `zrender/src/graphic/Style`) is | 
						|
 * about to be modified on its text related properties, `rollbackDefaultTextStyle` should | 
						|
 * be called before the modification and `applyDefaultTextStyle` should be called after that. | 
						|
 * (For the case that all of the text related properties is reset, like `setTextStyleCommon` | 
						|
 * does, `rollbackDefaultTextStyle` is not needed to be called). | 
						|
 */ | 
						|
 | 
						|
 | 
						|
function applyDefaultTextStyle(textStyle) { | 
						|
  var opt = textStyle.insideRollbackOpt; // Only `insideRollbackOpt` created (in `setTextStyleCommon`), | 
						|
  // applyDefaultTextStyle works. | 
						|
 | 
						|
  if (!opt || textStyle.textFill != null) { | 
						|
    return; | 
						|
  } | 
						|
 | 
						|
  var useInsideStyle = opt.useInsideStyle; | 
						|
  var textPosition = textStyle.insideRawTextPosition; | 
						|
  var insideRollback; | 
						|
  var autoColor = opt.autoColor; | 
						|
 | 
						|
  if (useInsideStyle !== false && (useInsideStyle === true || opt.isRectText && textPosition // textPosition can be [10, 30] | 
						|
  && typeof textPosition === 'string' && textPosition.indexOf('inside') >= 0)) { | 
						|
    insideRollback = { | 
						|
      textFill: null, | 
						|
      textStroke: textStyle.textStroke, | 
						|
      textStrokeWidth: textStyle.textStrokeWidth | 
						|
    }; | 
						|
    textStyle.textFill = '#fff'; // Consider text with #fff overflow its container. | 
						|
 | 
						|
    if (textStyle.textStroke == null) { | 
						|
      textStyle.textStroke = autoColor; | 
						|
      textStyle.textStrokeWidth == null && (textStyle.textStrokeWidth = 2); | 
						|
    } | 
						|
  } else if (autoColor != null) { | 
						|
    insideRollback = { | 
						|
      textFill: null | 
						|
    }; | 
						|
    textStyle.textFill = autoColor; | 
						|
  } // Always set `insideRollback`, for clearing previous. | 
						|
 | 
						|
 | 
						|
  if (insideRollback) { | 
						|
    textStyle.insideRollback = insideRollback; | 
						|
  } | 
						|
} | 
						|
/** | 
						|
 * Consider the case: in a scatter, | 
						|
 * label: { | 
						|
 *     normal: {position: 'inside'}, | 
						|
 *     emphasis: {position: 'top'} | 
						|
 * } | 
						|
 * In the normal state, the `textFill` will be set as '#fff' for pretty view (see | 
						|
 * `applyDefaultTextStyle`), but when switching to emphasis state, the `textFill` | 
						|
 * should be retured to 'autoColor', but not keep '#fff'. | 
						|
 */ | 
						|
 | 
						|
 | 
						|
function rollbackDefaultTextStyle(style) { | 
						|
  var insideRollback = style.insideRollback; | 
						|
 | 
						|
  if (insideRollback) { | 
						|
    style.textFill = insideRollback.textFill; | 
						|
    style.textStroke = insideRollback.textStroke; | 
						|
    style.textStrokeWidth = insideRollback.textStrokeWidth; | 
						|
    style.insideRollback = null; | 
						|
  } | 
						|
} | 
						|
 | 
						|
function getFont(opt, ecModel) { | 
						|
  // ecModel or default text style model. | 
						|
  var gTextStyleModel = ecModel || ecModel.getModel('textStyle'); | 
						|
  return zrUtil.trim([// FIXME in node-canvas fontWeight is before fontStyle | 
						|
  opt.fontStyle || gTextStyleModel && gTextStyleModel.getShallow('fontStyle') || '', opt.fontWeight || gTextStyleModel && gTextStyleModel.getShallow('fontWeight') || '', (opt.fontSize || gTextStyleModel && gTextStyleModel.getShallow('fontSize') || 12) + 'px', opt.fontFamily || gTextStyleModel && gTextStyleModel.getShallow('fontFamily') || 'sans-serif'].join(' ')); | 
						|
} | 
						|
 | 
						|
function animateOrSetProps(isUpdate, el, props, animatableModel, dataIndex, cb) { | 
						|
  if (typeof dataIndex === 'function') { | 
						|
    cb = dataIndex; | 
						|
    dataIndex = null; | 
						|
  } // Do not check 'animation' property directly here. Consider this case: | 
						|
  // animation model is an `itemModel`, whose does not have `isAnimationEnabled` | 
						|
  // but its parent model (`seriesModel`) does. | 
						|
 | 
						|
 | 
						|
  var animationEnabled = animatableModel && animatableModel.isAnimationEnabled(); | 
						|
 | 
						|
  if (animationEnabled) { | 
						|
    var postfix = isUpdate ? 'Update' : ''; | 
						|
    var duration = animatableModel.getShallow('animationDuration' + postfix); | 
						|
    var animationEasing = animatableModel.getShallow('animationEasing' + postfix); | 
						|
    var animationDelay = animatableModel.getShallow('animationDelay' + postfix); | 
						|
 | 
						|
    if (typeof animationDelay === 'function') { | 
						|
      animationDelay = animationDelay(dataIndex, animatableModel.getAnimationDelayParams ? animatableModel.getAnimationDelayParams(el, dataIndex) : null); | 
						|
    } | 
						|
 | 
						|
    if (typeof duration === 'function') { | 
						|
      duration = duration(dataIndex); | 
						|
    } | 
						|
 | 
						|
    duration > 0 ? el.animateTo(props, duration, animationDelay || 0, animationEasing, cb, !!cb) : (el.stopAnimation(), el.attr(props), cb && cb()); | 
						|
  } else { | 
						|
    el.stopAnimation(); | 
						|
    el.attr(props); | 
						|
    cb && cb(); | 
						|
  } | 
						|
} | 
						|
/** | 
						|
 * Update graphic element properties with or without animation according to the | 
						|
 * configuration in series. | 
						|
 * | 
						|
 * Caution: this method will stop previous animation. | 
						|
 * So if do not use this method to one element twice before | 
						|
 * animation starts, unless you know what you are doing. | 
						|
 * | 
						|
 * @param {module:zrender/Element} el | 
						|
 * @param {Object} props | 
						|
 * @param {module:echarts/model/Model} [animatableModel] | 
						|
 * @param {number} [dataIndex] | 
						|
 * @param {Function} [cb] | 
						|
 * @example | 
						|
 *     graphic.updateProps(el, { | 
						|
 *         position: [100, 100] | 
						|
 *     }, seriesModel, dataIndex, function () { console.log('Animation done!'); }); | 
						|
 *     // Or | 
						|
 *     graphic.updateProps(el, { | 
						|
 *         position: [100, 100] | 
						|
 *     }, seriesModel, function () { console.log('Animation done!'); }); | 
						|
 */ | 
						|
 | 
						|
 | 
						|
function updateProps(el, props, animatableModel, dataIndex, cb) { | 
						|
  animateOrSetProps(true, el, props, animatableModel, dataIndex, cb); | 
						|
} | 
						|
/** | 
						|
 * Init graphic element properties with or without animation according to the | 
						|
 * configuration in series. | 
						|
 * | 
						|
 * Caution: this method will stop previous animation. | 
						|
 * So if do not use this method to one element twice before | 
						|
 * animation starts, unless you know what you are doing. | 
						|
 * | 
						|
 * @param {module:zrender/Element} el | 
						|
 * @param {Object} props | 
						|
 * @param {module:echarts/model/Model} [animatableModel] | 
						|
 * @param {number} [dataIndex] | 
						|
 * @param {Function} cb | 
						|
 */ | 
						|
 | 
						|
 | 
						|
function initProps(el, props, animatableModel, dataIndex, cb) { | 
						|
  animateOrSetProps(false, el, props, animatableModel, dataIndex, cb); | 
						|
} | 
						|
/** | 
						|
 * Get transform matrix of target (param target), | 
						|
 * in coordinate of its ancestor (param ancestor) | 
						|
 * | 
						|
 * @param {module:zrender/mixin/Transformable} target | 
						|
 * @param {module:zrender/mixin/Transformable} [ancestor] | 
						|
 */ | 
						|
 | 
						|
 | 
						|
function getTransform(target, ancestor) { | 
						|
  var mat = matrix.identity([]); | 
						|
 | 
						|
  while (target && target !== ancestor) { | 
						|
    matrix.mul(mat, target.getLocalTransform(), mat); | 
						|
    target = target.parent; | 
						|
  } | 
						|
 | 
						|
  return mat; | 
						|
} | 
						|
/** | 
						|
 * Apply transform to an vertex. | 
						|
 * @param {Array.<number>} target [x, y] | 
						|
 * @param {Array.<number>|TypedArray.<number>|Object} transform Can be: | 
						|
 *      + Transform matrix: like [1, 0, 0, 1, 0, 0] | 
						|
 *      + {position, rotation, scale}, the same as `zrender/Transformable`. | 
						|
 * @param {boolean=} invert Whether use invert matrix. | 
						|
 * @return {Array.<number>} [x, y] | 
						|
 */ | 
						|
 | 
						|
 | 
						|
function applyTransform(target, transform, invert) { | 
						|
  if (transform && !zrUtil.isArrayLike(transform)) { | 
						|
    transform = Transformable.getLocalTransform(transform); | 
						|
  } | 
						|
 | 
						|
  if (invert) { | 
						|
    transform = matrix.invert([], transform); | 
						|
  } | 
						|
 | 
						|
  return vector.applyTransform([], target, transform); | 
						|
} | 
						|
/** | 
						|
 * @param {string} direction 'left' 'right' 'top' 'bottom' | 
						|
 * @param {Array.<number>} transform Transform matrix: like [1, 0, 0, 1, 0, 0] | 
						|
 * @param {boolean=} invert Whether use invert matrix. | 
						|
 * @return {string} Transformed direction. 'left' 'right' 'top' 'bottom' | 
						|
 */ | 
						|
 | 
						|
 | 
						|
function transformDirection(direction, transform, invert) { | 
						|
  // Pick a base, ensure that transform result will not be (0, 0). | 
						|
  var hBase = transform[4] === 0 || transform[5] === 0 || transform[0] === 0 ? 1 : Math.abs(2 * transform[4] / transform[0]); | 
						|
  var vBase = transform[4] === 0 || transform[5] === 0 || transform[2] === 0 ? 1 : Math.abs(2 * transform[4] / transform[2]); | 
						|
  var vertex = [direction === 'left' ? -hBase : direction === 'right' ? hBase : 0, direction === 'top' ? -vBase : direction === 'bottom' ? vBase : 0]; | 
						|
  vertex = applyTransform(vertex, transform, invert); | 
						|
  return Math.abs(vertex[0]) > Math.abs(vertex[1]) ? vertex[0] > 0 ? 'right' : 'left' : vertex[1] > 0 ? 'bottom' : 'top'; | 
						|
} | 
						|
/** | 
						|
 * Apply group transition animation from g1 to g2. | 
						|
 * If no animatableModel, no animation. | 
						|
 */ | 
						|
 | 
						|
 | 
						|
function groupTransition(g1, g2, animatableModel, cb) { | 
						|
  if (!g1 || !g2) { | 
						|
    return; | 
						|
  } | 
						|
 | 
						|
  function getElMap(g) { | 
						|
    var elMap = {}; | 
						|
    g.traverse(function (el) { | 
						|
      if (!el.isGroup && el.anid) { | 
						|
        elMap[el.anid] = el; | 
						|
      } | 
						|
    }); | 
						|
    return elMap; | 
						|
  } | 
						|
 | 
						|
  function getAnimatableProps(el) { | 
						|
    var obj = { | 
						|
      position: vector.clone(el.position), | 
						|
      rotation: el.rotation | 
						|
    }; | 
						|
 | 
						|
    if (el.shape) { | 
						|
      obj.shape = zrUtil.extend({}, el.shape); | 
						|
    } | 
						|
 | 
						|
    return obj; | 
						|
  } | 
						|
 | 
						|
  var elMap1 = getElMap(g1); | 
						|
  g2.traverse(function (el) { | 
						|
    if (!el.isGroup && el.anid) { | 
						|
      var oldEl = elMap1[el.anid]; | 
						|
 | 
						|
      if (oldEl) { | 
						|
        var newProp = getAnimatableProps(el); | 
						|
        el.attr(getAnimatableProps(oldEl)); | 
						|
        updateProps(el, newProp, animatableModel, el.dataIndex); | 
						|
      } // else { | 
						|
      //     if (el.previousProps) { | 
						|
      //         graphic.updateProps | 
						|
      //     } | 
						|
      // } | 
						|
 | 
						|
    } | 
						|
  }); | 
						|
} | 
						|
/** | 
						|
 * @param {Array.<Array.<number>>} points Like: [[23, 44], [53, 66], ...] | 
						|
 * @param {Object} rect {x, y, width, height} | 
						|
 * @return {Array.<Array.<number>>} A new clipped points. | 
						|
 */ | 
						|
 | 
						|
 | 
						|
function clipPointsByRect(points, rect) { | 
						|
  // FIXME: this way migth be incorrect when grpahic clipped by a corner. | 
						|
  // and when element have border. | 
						|
  return zrUtil.map(points, function (point) { | 
						|
    var x = point[0]; | 
						|
    x = mathMax(x, rect.x); | 
						|
    x = mathMin(x, rect.x + rect.width); | 
						|
    var y = point[1]; | 
						|
    y = mathMax(y, rect.y); | 
						|
    y = mathMin(y, rect.y + rect.height); | 
						|
    return [x, y]; | 
						|
  }); | 
						|
} | 
						|
/** | 
						|
 * @param {Object} targetRect {x, y, width, height} | 
						|
 * @param {Object} rect {x, y, width, height} | 
						|
 * @return {Object} A new clipped rect. If rect size are negative, return undefined. | 
						|
 */ | 
						|
 | 
						|
 | 
						|
function clipRectByRect(targetRect, rect) { | 
						|
  var x = mathMax(targetRect.x, rect.x); | 
						|
  var x2 = mathMin(targetRect.x + targetRect.width, rect.x + rect.width); | 
						|
  var y = mathMax(targetRect.y, rect.y); | 
						|
  var y2 = mathMin(targetRect.y + targetRect.height, rect.y + rect.height); // If the total rect is cliped, nothing, including the border, | 
						|
  // should be painted. So return undefined. | 
						|
 | 
						|
  if (x2 >= x && y2 >= y) { | 
						|
    return { | 
						|
      x: x, | 
						|
      y: y, | 
						|
      width: x2 - x, | 
						|
      height: y2 - y | 
						|
    }; | 
						|
  } | 
						|
} | 
						|
/** | 
						|
 * @param {string} iconStr Support 'image://' or 'path://' or direct svg path. | 
						|
 * @param {Object} [opt] Properties of `module:zrender/Element`, except `style`. | 
						|
 * @param {Object} [rect] {x, y, width, height} | 
						|
 * @return {module:zrender/Element} Icon path or image element. | 
						|
 */ | 
						|
 | 
						|
 | 
						|
function createIcon(iconStr, opt, rect) { | 
						|
  opt = zrUtil.extend({ | 
						|
    rectHover: true | 
						|
  }, opt); | 
						|
  var style = opt.style = { | 
						|
    strokeNoScale: true | 
						|
  }; | 
						|
  rect = rect || { | 
						|
    x: -1, | 
						|
    y: -1, | 
						|
    width: 2, | 
						|
    height: 2 | 
						|
  }; | 
						|
 | 
						|
  if (iconStr) { | 
						|
    return iconStr.indexOf('image://') === 0 ? (style.image = iconStr.slice(8), zrUtil.defaults(style, rect), new ZImage(opt)) : makePath(iconStr.replace('path://', ''), opt, rect, 'center'); | 
						|
  } | 
						|
} | 
						|
 | 
						|
exports.Z2_EMPHASIS_LIFT = Z2_EMPHASIS_LIFT; | 
						|
exports.extendShape = extendShape; | 
						|
exports.extendPath = extendPath; | 
						|
exports.makePath = makePath; | 
						|
exports.makeImage = makeImage; | 
						|
exports.mergePath = mergePath; | 
						|
exports.resizePath = resizePath; | 
						|
exports.subPixelOptimizeLine = subPixelOptimizeLine; | 
						|
exports.subPixelOptimizeRect = subPixelOptimizeRect; | 
						|
exports.subPixelOptimize = subPixelOptimize; | 
						|
exports.setElementHoverStyle = setElementHoverStyle; | 
						|
exports.isInEmphasis = isInEmphasis; | 
						|
exports.setHoverStyle = setHoverStyle; | 
						|
exports.setAsHoverStyleTrigger = setAsHoverStyleTrigger; | 
						|
exports.setLabelStyle = setLabelStyle; | 
						|
exports.setTextStyle = setTextStyle; | 
						|
exports.setText = setText; | 
						|
exports.getFont = getFont; | 
						|
exports.updateProps = updateProps; | 
						|
exports.initProps = initProps; | 
						|
exports.getTransform = getTransform; | 
						|
exports.applyTransform = applyTransform; | 
						|
exports.transformDirection = transformDirection; | 
						|
exports.groupTransition = groupTransition; | 
						|
exports.clipPointsByRect = clipPointsByRect; | 
						|
exports.clipRectByRect = clipRectByRect; | 
						|
exports.createIcon = createIcon; |