{"version":3,"file":"echarts.js","sources":["../src/config.js","../../zrender/src/core/guid.js","../../zrender/src/core/env.js","../../zrender/src/core/util.js","../../zrender/src/core/vector.js","../../zrender/src/mixin/Draggable.js","../../zrender/src/mixin/Eventful.js","../../zrender/src/Handler.js","../../zrender/src/core/matrix.js","../../zrender/src/mixin/Transformable.js","../../zrender/src/animation/easing.js","../../zrender/src/animation/Clip.js","../../zrender/src/core/LRU.js","../../zrender/src/tool/color.js","../../zrender/src/animation/Animator.js","../../zrender/src/config.js","../../zrender/src/core/log.js","../../zrender/src/mixin/Animatable.js","../../zrender/src/Element.js","../../zrender/src/core/BoundingRect.js","../../zrender/src/container/Group.js","../../zrender/src/core/timsort.js","../../zrender/src/Storage.js","../../zrender/src/graphic/Style.js","../../zrender/src/graphic/Pattern.js","../../zrender/src/Layer.js","../../zrender/src/animation/requestAnimationFrame.js","../../zrender/src/graphic/helper/image.js","../../zrender/src/contain/text.js","../../zrender/src/graphic/helper/roundRect.js","../../zrender/src/graphic/helper/text.js","../../zrender/src/graphic/mixin/RectText.js","../../zrender/src/graphic/Displayable.js","../../zrender/src/graphic/Image.js","../../zrender/src/Painter.js","../../zrender/src/core/event.js","../../zrender/src/animation/Animation.js","../../zrender/src/core/GestureMgr.js","../../zrender/src/dom/HandlerProxy.js","../../zrender/src/zrender.js","../src/util/number.js","../src/util/format.js","../src/util/clazz.js","../src/model/mixin/makeStyleMapper.js","../src/model/mixin/lineStyle.js","../src/model/mixin/areaStyle.js","../../zrender/src/core/curve.js","../../zrender/src/core/bbox.js","../../zrender/src/core/PathProxy.js","../../zrender/src/contain/line.js","../../zrender/src/contain/cubic.js","../../zrender/src/contain/quadratic.js","../../zrender/src/contain/util.js","../../zrender/src/contain/arc.js","../../zrender/src/contain/windingLine.js","../../zrender/src/contain/path.js","../../zrender/src/graphic/Path.js","../../zrender/src/tool/transformPath.js","../../zrender/src/tool/path.js","../../zrender/src/graphic/Text.js","../../zrender/src/graphic/shape/Circle.js","../../zrender/src/graphic/helper/fixClipWithShadow.js","../../zrender/src/graphic/shape/Sector.js","../../zrender/src/graphic/shape/Ring.js","../../zrender/src/graphic/helper/smoothSpline.js","../../zrender/src/graphic/helper/smoothBezier.js","../../zrender/src/graphic/helper/poly.js","../../zrender/src/graphic/shape/Polygon.js","../../zrender/src/graphic/shape/Polyline.js","../../zrender/src/graphic/shape/Rect.js","../../zrender/src/graphic/shape/Line.js","../../zrender/src/graphic/shape/BezierCurve.js","../../zrender/src/graphic/shape/Arc.js","../../zrender/src/graphic/CompoundPath.js","../../zrender/src/graphic/Gradient.js","../../zrender/src/graphic/LinearGradient.js","../../zrender/src/graphic/RadialGradient.js","../src/util/graphic.js","../src/model/mixin/textStyle.js","../src/model/mixin/itemStyle.js","../src/model/Model.js","../src/util/model.js","../src/util/component.js","../src/util/layout.js","../src/model/mixin/boxLayout.js","../src/model/Component.js","../src/model/globalDefault.js","../src/model/mixin/colorPalette.js","../src/model/Global.js","../src/ExtensionAPI.js","../src/CoordinateSystem.js","../src/model/OptionManager.js","../src/preprocessor/helper/compatStyle.js","../src/preprocessor/backwardCompat.js","../src/model/Series.js","../src/view/Component.js","../src/view/Chart.js","../src/util/throttle.js","../src/visual/seriesColor.js","../src/loading/default.js","../src/echarts.js","../src/data/DataDiffer.js","../src/data/List.js","../src/data/helper/completeDimensions.js","../src/chart/helper/createListFromArray.js","../src/scale/Scale.js","../src/scale/Ordinal.js","../src/scale/helper.js","../src/scale/Interval.js","../src/scale/Time.js","../src/scale/Log.js","../src/coord/axisHelper.js","../src/coord/axisModelCommonMixin.js","../src/util/symbol.js","../src/helper.js","../src/coord/Axis.js","../../zrender/src/contain/polygon.js","../src/coord/geo/Region.js","../src/coord/geo/parseGeoJson.js","../src/export.js","../src/chart/line/LineSeries.js","../src/chart/helper/labelHelper.js","../src/chart/helper/Symbol.js","../src/chart/helper/SymbolDraw.js","../src/chart/line/lineAnimationDiff.js","../src/chart/line/poly.js","../src/chart/line/LineView.js","../src/visual/symbol.js","../src/layout/points.js","../src/processor/dataSample.js","../src/coord/cartesian/Cartesian.js","../src/coord/cartesian/Cartesian2D.js","../src/coord/cartesian/Axis2D.js","../src/coord/axisDefault.js","../src/coord/axisModelCreator.js","../src/coord/cartesian/AxisModel.js","../src/coord/cartesian/GridModel.js","../src/coord/cartesian/Grid.js","../src/component/axis/AxisBuilder.js","../src/component/axisPointer/modelHelper.js","../src/component/axis/AxisView.js","../src/component/axis/cartesianAxisHelper.js","../src/component/axis/CartesianAxisView.js","../src/component/gridSimple.js","../src/chart/line.js","../src/layout/barGrid.js","../src/chart/bar/BaseBarSeries.js","../src/chart/bar/BarSeries.js","../src/chart/bar/helper.js","../src/chart/bar/barItemStyle.js","../src/chart/bar/BarView.js","../src/chart/bar.js","../src/component/helper/selectableMixin.js","../src/chart/pie/PieSeries.js","../src/chart/pie/PieView.js","../src/action/createDataSelectAction.js","../src/visual/dataColor.js","../src/chart/pie/labelLayout.js","../src/chart/pie/pieLayout.js","../src/processor/dataFilter.js","../src/chart/pie.js","../src/chart/scatter/ScatterSeries.js","../src/chart/helper/LargeSymbolDraw.js","../src/chart/scatter/ScatterView.js","../src/chart/scatter.js","../src/coord/radar/IndicatorAxis.js","../src/coord/radar/Radar.js","../src/coord/radar/RadarModel.js","../src/component/radar/RadarView.js","../src/chart/radar/RadarSeries.js","../src/chart/radar/RadarView.js","../src/chart/radar/radarLayout.js","../src/chart/radar/backwardCompat.js","../src/chart/radar.js","../src/coord/View.js","../src/coord/geo/fix/nanhai.js","../src/coord/geo/fix/textCoord.js","../src/coord/geo/fix/geoCoord.js","../src/coord/geo/fix/diaoyuIsland.js","../src/coord/geo/Geo.js","../src/coord/geo/geoCreator.js","../src/chart/map/MapSeries.js","../src/component/helper/interactionMutex.js","../src/component/helper/RoamController.js","../src/component/helper/roamHelper.js","../src/component/helper/cursorHelper.js","../src/component/helper/MapDraw.js","../src/chart/map/MapView.js","../src/action/roamHelper.js","../src/action/geoRoam.js","../src/chart/map/mapSymbolLayout.js","../src/chart/map/mapVisual.js","../src/chart/map/mapDataStatistic.js","../src/chart/map/backwardCompat.js","../src/chart/map.js","../src/data/helper/linkList.js","../src/data/Tree.js","../src/chart/tree/TreeSeries.js","../src/chart/tree/layoutHelper.js","../src/chart/tree/TreeView.js","../src/chart/tree/treeAction.js","../src/chart/tree/traversalHelper.js","../src/chart/tree/commonLayout.js","../src/chart/tree/orthogonalLayout.js","../src/chart/tree/radialLayout.js","../src/chart/tree.js","../src/chart/treemap/helper.js","../src/chart/treemap/TreemapSeries.js","../src/chart/treemap/Breadcrumb.js","../src/util/animation.js","../src/chart/treemap/TreemapView.js","../src/chart/treemap/treemapAction.js","../src/visual/VisualMapping.js","../src/chart/treemap/treemapVisual.js","../src/chart/treemap/treemapLayout.js","../src/chart/treemap.js","../src/data/Graph.js","../src/chart/helper/createGraphFromNodeEdge.js","../src/chart/graph/GraphSeries.js","../src/chart/helper/LinePath.js","../src/chart/helper/Line.js","../src/chart/helper/LineDraw.js","../src/chart/graph/adjustEdge.js","../src/chart/graph/GraphView.js","../src/chart/graph/graphAction.js","../src/chart/graph/categoryFilter.js","../src/chart/graph/categoryVisual.js","../src/chart/graph/edgeVisual.js","../src/chart/graph/simpleLayoutHelper.js","../src/chart/graph/simpleLayout.js","../src/chart/graph/circularLayoutHelper.js","../src/chart/graph/circularLayout.js","../src/chart/graph/forceHelper.js","../src/chart/graph/forceLayout.js","../src/chart/graph/createView.js","../src/chart/graph.js","../src/chart/gauge/GaugeSeries.js","../src/chart/gauge/PointerPath.js","../src/chart/gauge/GaugeView.js","../src/chart/funnel/FunnelSeries.js","../src/chart/funnel/FunnelView.js","../src/chart/funnel/funnelLayout.js","../src/chart/funnel.js","../src/coord/parallel/parallelPreprocessor.js","../src/coord/parallel/ParallelAxis.js","../src/component/helper/sliderMove.js","../src/coord/parallel/Parallel.js","../src/coord/parallel/parallelCreator.js","../src/coord/parallel/AxisModel.js","../src/coord/parallel/ParallelModel.js","../src/component/axis/parallelAxisAction.js","../src/component/helper/BrushController.js","../src/component/helper/brushHelper.js","../src/component/axis/ParallelAxisView.js","../src/component/parallel.js","../src/chart/parallel/ParallelSeries.js","../src/chart/parallel/ParallelView.js","../src/chart/parallel/parallelVisual.js","../src/chart/parallel.js","../src/chart/sankey/SankeySeries.js","../src/chart/sankey/SankeyView.js","../src/util/array/nest.js","../src/chart/sankey/sankeyLayout.js","../src/chart/sankey/sankeyVisual.js","../src/chart/sankey.js","../src/chart/helper/WhiskerBoxDraw.js","../src/chart/helper/whiskerBoxCommon.js","../src/chart/boxplot/BoxplotSeries.js","../src/chart/boxplot/BoxplotView.js","../src/chart/boxplot/boxplotVisual.js","../src/chart/boxplot/boxplotLayout.js","../src/chart/boxplot.js","../src/chart/candlestick/CandlestickSeries.js","../src/chart/candlestick/CandlestickView.js","../src/chart/candlestick/preprocessor.js","../src/chart/candlestick/candlestickVisual.js","../src/chart/candlestick/candlestickLayout.js","../src/chart/candlestick.js","../src/chart/effectScatter/EffectScatterSeries.js","../src/chart/helper/EffectSymbol.js","../src/chart/effectScatter/EffectScatterView.js","../src/chart/effectScatter.js","../src/chart/lines/LinesSeries.js","../src/chart/helper/EffectLine.js","../src/chart/helper/Polyline.js","../src/chart/helper/EffectPolyline.js","../src/chart/helper/LargeLineDraw.js","../src/chart/lines/LinesView.js","../src/chart/lines/linesLayout.js","../src/chart/lines/linesVisual.js","../src/chart/lines.js","../src/chart/heatmap/HeatmapSeries.js","../src/chart/heatmap/HeatmapLayer.js","../src/chart/heatmap/HeatmapView.js","../src/chart/bar/PictorialBarSeries.js","../src/chart/bar/PictorialBarView.js","../src/chart/pictorialBar.js","../src/coord/single/SingleAxis.js","../src/coord/single/Single.js","../src/coord/single/singleCreator.js","../src/component/axis/singleAxisHelper.js","../src/component/axis/SingleAxisView.js","../src/coord/single/AxisModel.js","../src/component/axisPointer/findPointFromSeries.js","../src/component/axisPointer/axisTrigger.js","../src/component/axisPointer/AxisPointerModel.js","../src/component/axisPointer/globalListener.js","../src/component/axisPointer/AxisPointerView.js","../src/component/axisPointer/BaseAxisPointer.js","../src/component/axisPointer/viewHelper.js","../src/component/axisPointer/CartesianAxisPointer.js","../src/component/axisPointer.js","../src/component/axisPointer/SingleAxisPointer.js","../src/component/singleAxis.js","../src/chart/themeRiver/ThemeRiverSeries.js","../src/chart/themeRiver/ThemeRiverView.js","../src/chart/themeRiver/themeRiverLayout.js","../src/chart/themeRiver/themeRiverVisual.js","../src/chart/themeRiver.js","../src/coord/cartesian/prepareCustom.js","../src/coord/geo/prepareCustom.js","../src/coord/single/prepareCustom.js","../src/coord/polar/prepareCustom.js","../src/coord/calendar/prepareCustom.js","../src/chart/custom.js","../src/component/graphic.js","../src/component/legend/LegendModel.js","../src/component/legend/legendAction.js","../src/component/helper/listComponent.js","../src/component/legend/LegendView.js","../src/component/legend/legendFilter.js","../src/component/legend.js","../src/component/legend/ScrollableLegendModel.js","../src/component/legend/ScrollableLegendView.js","../src/component/legend/scrollableLegendAction.js","../src/component/legendScroll.js","../src/component/tooltip/TooltipModel.js","../src/component/tooltip/TooltipContent.js","../src/component/tooltip/TooltipView.js","../src/component/tooltip.js","../src/layout/barPolar.js","../src/coord/polar/RadiusAxis.js","../src/coord/polar/AngleAxis.js","../src/coord/polar/Polar.js","../src/coord/polar/AxisModel.js","../src/coord/polar/PolarModel.js","../src/coord/polar/polarCreator.js","../src/component/axis/AngleAxisView.js","../src/component/axis/RadiusAxisView.js","../src/component/axisPointer/PolarAxisPointer.js","../src/component/polar.js","../src/coord/geo/GeoModel.js","../src/component/geo/GeoView.js","../src/component/geo.js","../src/component/brush/preprocessor.js","../src/visual/visualSolution.js","../src/component/brush/selector.js","../src/component/helper/BrushTargetManager.js","../src/component/brush/visualEncoding.js","../src/component/brush/BrushModel.js","../src/component/brush/BrushView.js","../src/component/brush/brushAction.js","../src/component/toolbox/featureManager.js","../src/lang.js","../src/component/toolbox/feature/Brush.js","../src/component/brush.js","../src/coord/calendar/Calendar.js","../src/coord/calendar/CalendarModel.js","../src/component/calendar/CalendarView.js","../src/component/calendar.js","../src/component/title.js","../src/component/dataZoom/typeDefaulter.js","../src/component/dataZoom/helper.js","../src/component/dataZoom/AxisProxy.js","../src/component/dataZoom/DataZoomModel.js","../src/component/dataZoom/DataZoomView.js","../src/component/dataZoom/SliderZoomModel.js","../src/component/dataZoom/SliderZoomView.js","../src/component/dataZoom/InsideZoomModel.js","../src/component/dataZoom/roams.js","../src/component/dataZoom/InsideZoomView.js","../src/component/dataZoom/dataZoomProcessor.js","../src/component/dataZoom/dataZoomAction.js","../src/component/dataZoom.js","../src/component/visualMap/preprocessor.js","../src/component/visualMap/typeDefaulter.js","../src/component/visualMap/visualEncoding.js","../src/visual/visualDefault.js","../src/component/visualMap/VisualMapModel.js","../src/component/visualMap/ContinuousModel.js","../src/component/visualMap/VisualMapView.js","../src/component/visualMap/helper.js","../src/component/visualMap/ContinuousView.js","../src/component/visualMap/visualMapAction.js","../src/component/visualMapContinuous.js","../src/component/visualMap/PiecewiseModel.js","../src/component/visualMap/PiecewiseView.js","../src/component/visualMapPiecewise.js","../src/component/visualMap.js","../src/component/marker/MarkerModel.js","../src/component/marker/MarkPointModel.js","../src/component/marker/markerHelper.js","../src/component/marker/MarkerView.js","../src/component/marker/MarkPointView.js","../src/component/markPoint.js","../src/component/marker/MarkLineModel.js","../src/component/marker/MarkLineView.js","../src/component/markLine.js","../src/component/marker/MarkAreaModel.js","../src/component/marker/MarkAreaView.js","../src/component/markArea.js","../src/component/timeline/preprocessor.js","../src/component/timeline/typeDefaulter.js","../src/component/timeline/timelineAction.js","../src/component/timeline/TimelineModel.js","../src/component/timeline/SliderTimelineModel.js","../src/component/timeline/TimelineView.js","../src/component/timeline/TimelineAxis.js","../src/component/timeline/SliderTimelineView.js","../src/component/timeline.js","../src/component/toolbox/ToolboxModel.js","../src/component/toolbox/ToolboxView.js","../src/component/toolbox/feature/SaveAsImage.js","../src/component/toolbox/feature/MagicType.js","../src/component/toolbox/feature/DataView.js","../src/component/dataZoom/history.js","../src/component/dataZoom/SelectZoomModel.js","../src/component/dataZoom/SelectZoomView.js","../src/component/dataZoomSelect.js","../src/component/toolbox/feature/DataZoom.js","../src/component/toolbox/feature/Restore.js","../../zrender/src/vml/core.js","../../zrender/src/vml/graphic.js","../../zrender/src/vml/Painter.js","../../zrender/src/vml/vml.js","../../zrender/src/svg/core.js","../../zrender/src/svg/graphic.js","../../zrender/src/core/arrayDiff2.js","../../zrender/src/svg/helper/Definable.js","../../zrender/src/svg/helper/GradientManager.js","../../zrender/src/svg/helper/ClippathManager.js","../../zrender/src/svg/Painter.js","../../zrender/src/svg/svg.js","../echarts.all.js"],"sourcesContent":["// (1) The code `if (__DEV__) ...` can be removed by build tool.\n// (2) If intend to use `__DEV__`, this module should be imported. Use a global\n// variable `__DEV__` may cause that miss the declaration (see #6535), or the\n// declaration is behind of the using position (for example in `Model.extent`,\n// And tools like rollup can not analysis the dependency if not import).\n\nvar dev;\n\n// In browser\nif (typeof window !== 'undefined') {\n dev = window.__DEV__;\n}\n// In node\nelse if (typeof global !== 'undefined') {\n dev = global.__DEV__;\n}\n\nif (typeof dev === 'undefined') {\n dev = true;\n}\n\nexport var __DEV__ = dev;\n","/**\n * zrender: 生成唯一id\n *\n * @author errorrik (errorrik@gmail.com)\n */\n\nvar idStart = 0x0907;\n\nexport default function () {\n return idStart++;\n}","/**\n * echarts设备环境识别\n *\n * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。\n * @author firede[firede@firede.us]\n * @desc thanks zepto.\n */\n\nvar env = {};\n\nif (typeof navigator === 'undefined') {\n // In node\n env = {\n browser: {},\n os: {},\n node: true,\n // Assume canvas is supported\n canvasSupported: true,\n svgSupported: true\n };\n}\nelse {\n env = detect(navigator.userAgent);\n}\n\nexport default env;\n\n// Zepto.js\n// (c) 2010-2013 Thomas Fuchs\n// Zepto.js may be freely distributed under the MIT license.\n\nfunction detect(ua) {\n var os = {};\n var browser = {};\n // var webkit = ua.match(/Web[kK]it[\\/]{0,1}([\\d.]+)/);\n // var android = ua.match(/(Android);?[\\s\\/]+([\\d.]+)?/);\n // var ipad = ua.match(/(iPad).*OS\\s([\\d_]+)/);\n // var ipod = ua.match(/(iPod)(.*OS\\s([\\d_]+))?/);\n // var iphone = !ipad && ua.match(/(iPhone\\sOS)\\s([\\d_]+)/);\n // var webos = ua.match(/(webOS|hpwOS)[\\s\\/]([\\d.]+)/);\n // var touchpad = webos && ua.match(/TouchPad/);\n // var kindle = ua.match(/Kindle\\/([\\d.]+)/);\n // var silk = ua.match(/Silk\\/([\\d._]+)/);\n // var blackberry = ua.match(/(BlackBerry).*Version\\/([\\d.]+)/);\n // var bb10 = ua.match(/(BB10).*Version\\/([\\d.]+)/);\n // var rimtabletos = ua.match(/(RIM\\sTablet\\sOS)\\s([\\d.]+)/);\n // var playbook = ua.match(/PlayBook/);\n // var chrome = ua.match(/Chrome\\/([\\d.]+)/) || ua.match(/CriOS\\/([\\d.]+)/);\n var firefox = ua.match(/Firefox\\/([\\d.]+)/);\n // var safari = webkit && ua.match(/Mobile\\//) && !chrome;\n // var webview = ua.match(/(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/) && !chrome;\n var ie = ua.match(/MSIE\\s([\\d.]+)/)\n // IE 11 Trident/7.0; rv:11.0\n || ua.match(/Trident\\/.+?rv:(([\\d.]+))/);\n var edge = ua.match(/Edge\\/([\\d.]+)/); // IE 12 and 12+\n\n var weChat = (/micromessenger/i).test(ua);\n\n // Todo: clean this up with a better OS/browser seperation:\n // - discern (more) between multiple browsers on android\n // - decide if kindle fire in silk mode is android or not\n // - Firefox on Android doesn't specify the Android version\n // - possibly devide in os, device and browser hashes\n\n // if (browser.webkit = !!webkit) browser.version = webkit[1];\n\n // if (android) os.android = true, os.version = android[2];\n // if (iphone && !ipod) os.ios = os.iphone = true, os.version = iphone[2].replace(/_/g, '.');\n // if (ipad) os.ios = os.ipad = true, os.version = ipad[2].replace(/_/g, '.');\n // if (ipod) os.ios = os.ipod = true, os.version = ipod[3] ? ipod[3].replace(/_/g, '.') : null;\n // if (webos) os.webos = true, os.version = webos[2];\n // if (touchpad) os.touchpad = true;\n // if (blackberry) os.blackberry = true, os.version = blackberry[2];\n // if (bb10) os.bb10 = true, os.version = bb10[2];\n // if (rimtabletos) os.rimtabletos = true, os.version = rimtabletos[2];\n // if (playbook) browser.playbook = true;\n // if (kindle) os.kindle = true, os.version = kindle[1];\n // if (silk) browser.silk = true, browser.version = silk[1];\n // if (!silk && os.android && ua.match(/Kindle Fire/)) browser.silk = true;\n // if (chrome) browser.chrome = true, browser.version = chrome[1];\n if (firefox) {\n browser.firefox = true;\n browser.version = firefox[1];\n }\n // if (safari && (ua.match(/Safari/) || !!os.ios)) browser.safari = true;\n // if (webview) browser.webview = true;\n\n if (ie) {\n browser.ie = true;\n browser.version = ie[1];\n }\n\n if (edge) {\n browser.edge = true;\n browser.version = edge[1];\n }\n\n // It is difficult to detect WeChat in Win Phone precisely, because ua can\n // not be set on win phone. So we do not consider Win Phone.\n if (weChat) {\n browser.weChat = true;\n }\n\n // os.tablet = !!(ipad || playbook || (android && !ua.match(/Mobile/)) ||\n // (firefox && ua.match(/Tablet/)) || (ie && !ua.match(/Phone/) && ua.match(/Touch/)));\n // os.phone = !!(!os.tablet && !os.ipod && (android || iphone || webos ||\n // (chrome && ua.match(/Android/)) || (chrome && ua.match(/CriOS\\/([\\d.]+)/)) ||\n // (firefox && ua.match(/Mobile/)) || (ie && ua.match(/Touch/))));\n\n return {\n browser: browser,\n os: os,\n node: false,\n // 原生canvas支持,改极端点了\n // canvasSupported : !(browser.ie && parseFloat(browser.version) < 9)\n canvasSupported: !!document.createElement('canvas').getContext,\n svgSupported: typeof SVGRect !== 'undefined',\n // @see <http://stackoverflow.com/questions/4817029/whats-the-best-way-to-detect-a-touch-screen-device-using-javascript>\n // works on most browsers\n // IE10/11 does not support touch event, and MS Edge supports them but not by\n // default, so we dont check navigator.maxTouchPoints for them here.\n touchEventsSupported: 'ontouchstart' in window && !browser.ie && !browser.edge,\n // <http://caniuse.com/#search=pointer%20event>.\n pointerEventsSupported: 'onpointerdown' in window\n // Firefox supports pointer but not by default, only MS browsers are reliable on pointer\n // events currently. So we dont use that on other browsers unless tested sufficiently.\n // Although IE 10 supports pointer event, it use old style and is different from the\n // standard. So we exclude that. (IE 10 is hardly used on touch device)\n && (browser.edge || (browser.ie && browser.version >= 11))\n };\n}","/**\n * @module zrender/core/util\n */\n\n// 用于处理merge时无法遍历Date等对象的问题\nvar BUILTIN_OBJECT = {\n '[object Function]': 1,\n '[object RegExp]': 1,\n '[object Date]': 1,\n '[object Error]': 1,\n '[object CanvasGradient]': 1,\n '[object CanvasPattern]': 1,\n // For node-canvas\n '[object Image]': 1,\n '[object Canvas]': 1\n};\n\nvar TYPED_ARRAY = {\n '[object Int8Array]': 1,\n '[object Uint8Array]': 1,\n '[object Uint8ClampedArray]': 1,\n '[object Int16Array]': 1,\n '[object Uint16Array]': 1,\n '[object Int32Array]': 1,\n '[object Uint32Array]': 1,\n '[object Float32Array]': 1,\n '[object Float64Array]': 1\n};\n\nvar objToString = Object.prototype.toString;\n\nvar arrayProto = Array.prototype;\nvar nativeForEach = arrayProto.forEach;\nvar nativeFilter = arrayProto.filter;\nvar nativeSlice = arrayProto.slice;\nvar nativeMap = arrayProto.map;\nvar nativeReduce = arrayProto.reduce;\n\n// Avoid assign to an exported variable, for transforming to cjs.\nvar methods = {};\n\nexport function $override(name, fn) {\n methods[name] = fn;\n}\n\n/**\n * Those data types can be cloned:\n * Plain object, Array, TypedArray, number, string, null, undefined.\n * Those data types will be assgined using the orginal data:\n * BUILTIN_OBJECT\n * Instance of user defined class will be cloned to a plain object, without\n * properties in prototype.\n * Other data types is not supported (not sure what will happen).\n *\n * Caution: do not support clone Date, for performance consideration.\n * (There might be a large number of date in `series.data`).\n * So date should not be modified in and out of echarts.\n *\n * @param {*} source\n * @return {*} new\n */\nexport function clone(source) {\n if (source == null || typeof source != 'object') {\n return source;\n }\n\n var result = source;\n var typeStr = objToString.call(source);\n\n if (typeStr === '[object Array]') {\n result = [];\n for (var i = 0, len = source.length; i < len; i++) {\n result[i] = clone(source[i]);\n }\n }\n else if (TYPED_ARRAY[typeStr]) {\n var Ctor = source.constructor;\n if (source.constructor.from) {\n result = Ctor.from(source);\n }\n else {\n result = new Ctor(source.length);\n for (var i = 0, len = source.length; i < len; i++) {\n result[i] = clone(source[i]);\n }\n }\n }\n else if (!BUILTIN_OBJECT[typeStr] && !isPrimitive(source) && !isDom(source)) {\n result = {};\n for (var key in source) {\n if (source.hasOwnProperty(key)) {\n result[key] = clone(source[key]);\n }\n }\n }\n\n return result;\n}\n\n/**\n * @memberOf module:zrender/core/util\n * @param {*} target\n * @param {*} source\n * @param {boolean} [overwrite=false]\n */\nexport function merge(target, source, overwrite) {\n // We should escapse that source is string\n // and enter for ... in ...\n if (!isObject(source) || !isObject(target)) {\n return overwrite ? clone(source) : target;\n }\n\n for (var key in source) {\n if (source.hasOwnProperty(key)) {\n var targetProp = target[key];\n var sourceProp = source[key];\n\n if (isObject(sourceProp)\n && isObject(targetProp)\n && !isArray(sourceProp)\n && !isArray(targetProp)\n && !isDom(sourceProp)\n && !isDom(targetProp)\n && !isBuiltInObject(sourceProp)\n && !isBuiltInObject(targetProp)\n && !isPrimitive(sourceProp)\n && !isPrimitive(targetProp)\n ) {\n // 如果需要递归覆盖,就递归调用merge\n merge(targetProp, sourceProp, overwrite);\n }\n else if (overwrite || !(key in target)) {\n // 否则只处理overwrite为true,或者在目标对象中没有此属性的情况\n // NOTE,在 target[key] 不存在的时候也是直接覆盖\n target[key] = clone(source[key], true);\n }\n }\n }\n\n return target;\n}\n\n/**\n * @param {Array} targetAndSources The first item is target, and the rests are source.\n * @param {boolean} [overwrite=false]\n * @return {*} target\n */\nexport function mergeAll(targetAndSources, overwrite) {\n var result = targetAndSources[0];\n for (var i = 1, len = targetAndSources.length; i < len; i++) {\n result = merge(result, targetAndSources[i], overwrite);\n }\n return result;\n}\n\n/**\n * @param {*} target\n * @param {*} source\n * @memberOf module:zrender/core/util\n */\nexport function extend(target, source) {\n for (var key in source) {\n if (source.hasOwnProperty(key)) {\n target[key] = source[key];\n }\n }\n return target;\n}\n\n/**\n * @param {*} target\n * @param {*} source\n * @param {boolean} [overlay=false]\n * @memberOf module:zrender/core/util\n */\nexport function defaults(target, source, overlay) {\n for (var key in source) {\n if (source.hasOwnProperty(key)\n && (overlay ? source[key] != null : target[key] == null)\n ) {\n target[key] = source[key];\n }\n }\n return target;\n}\n\nexport var createCanvas = function () {\n return methods.createCanvas();\n};\n\nmethods.createCanvas = function () {\n return document.createElement('canvas');\n};\n\n// FIXME\nvar _ctx;\n\nexport function getContext() {\n if (!_ctx) {\n // Use util.createCanvas instead of createCanvas\n // because createCanvas may be overwritten in different environment\n _ctx = createCanvas().getContext('2d');\n }\n return _ctx;\n}\n\n/**\n * 查询数组中元素的index\n * @memberOf module:zrender/core/util\n */\nexport function indexOf(array, value) {\n if (array) {\n if (array.indexOf) {\n return array.indexOf(value);\n }\n for (var i = 0, len = array.length; i < len; i++) {\n if (array[i] === value) {\n return i;\n }\n }\n }\n return -1;\n}\n\n/**\n * 构造类继承关系\n *\n * @memberOf module:zrender/core/util\n * @param {Function} clazz 源类\n * @param {Function} baseClazz 基类\n */\nexport function inherits(clazz, baseClazz) {\n var clazzPrototype = clazz.prototype;\n function F() {}\n F.prototype = baseClazz.prototype;\n clazz.prototype = new F();\n\n for (var prop in clazzPrototype) {\n clazz.prototype[prop] = clazzPrototype[prop];\n }\n clazz.prototype.constructor = clazz;\n clazz.superClass = baseClazz;\n}\n\n/**\n * @memberOf module:zrender/core/util\n * @param {Object|Function} target\n * @param {Object|Function} sorce\n * @param {boolean} overlay\n */\nexport function mixin(target, source, overlay) {\n target = 'prototype' in target ? target.prototype : target;\n source = 'prototype' in source ? source.prototype : source;\n\n defaults(target, source, overlay);\n}\n\n/**\n * Consider typed array.\n * @param {Array|TypedArray} data\n */\nexport function isArrayLike(data) {\n if (! data) {\n return;\n }\n if (typeof data == 'string') {\n return false;\n }\n return typeof data.length == 'number';\n}\n\n/**\n * 数组或对象遍历\n * @memberOf module:zrender/core/util\n * @param {Object|Array} obj\n * @param {Function} cb\n * @param {*} [context]\n */\nexport function each(obj, cb, context) {\n if (!(obj && cb)) {\n return;\n }\n if (obj.forEach && obj.forEach === nativeForEach) {\n obj.forEach(cb, context);\n }\n else if (obj.length === +obj.length) {\n for (var i = 0, len = obj.length; i < len; i++) {\n cb.call(context, obj[i], i, obj);\n }\n }\n else {\n for (var key in obj) {\n if (obj.hasOwnProperty(key)) {\n cb.call(context, obj[key], key, obj);\n }\n }\n }\n}\n\n/**\n * 数组映射\n * @memberOf module:zrender/core/util\n * @param {Array} obj\n * @param {Function} cb\n * @param {*} [context]\n * @return {Array}\n */\nexport function map(obj, cb, context) {\n if (!(obj && cb)) {\n return;\n }\n if (obj.map && obj.map === nativeMap) {\n return obj.map(cb, context);\n }\n else {\n var result = [];\n for (var i = 0, len = obj.length; i < len; i++) {\n result.push(cb.call(context, obj[i], i, obj));\n }\n return result;\n }\n}\n\n/**\n * @memberOf module:zrender/core/util\n * @param {Array} obj\n * @param {Function} cb\n * @param {Object} [memo]\n * @param {*} [context]\n * @return {Array}\n */\nexport function reduce(obj, cb, memo, context) {\n if (!(obj && cb)) {\n return;\n }\n if (obj.reduce && obj.reduce === nativeReduce) {\n return obj.reduce(cb, memo, context);\n }\n else {\n for (var i = 0, len = obj.length; i < len; i++) {\n memo = cb.call(context, memo, obj[i], i, obj);\n }\n return memo;\n }\n}\n\n/**\n * 数组过滤\n * @memberOf module:zrender/core/util\n * @param {Array} obj\n * @param {Function} cb\n * @param {*} [context]\n * @return {Array}\n */\nexport function filter(obj, cb, context) {\n if (!(obj && cb)) {\n return;\n }\n if (obj.filter && obj.filter === nativeFilter) {\n return obj.filter(cb, context);\n }\n else {\n var result = [];\n for (var i = 0, len = obj.length; i < len; i++) {\n if (cb.call(context, obj[i], i, obj)) {\n result.push(obj[i]);\n }\n }\n return result;\n }\n}\n\n/**\n * 数组项查找\n * @memberOf module:zrender/core/util\n * @param {Array} obj\n * @param {Function} cb\n * @param {*} [context]\n * @return {*}\n */\nexport function find(obj, cb, context) {\n if (!(obj && cb)) {\n return;\n }\n for (var i = 0, len = obj.length; i < len; i++) {\n if (cb.call(context, obj[i], i, obj)) {\n return obj[i];\n }\n }\n}\n\n/**\n * @memberOf module:zrender/core/util\n * @param {Function} func\n * @param {*} context\n * @return {Function}\n */\nexport function bind(func, context) {\n var args = nativeSlice.call(arguments, 2);\n return function () {\n return func.apply(context, args.concat(nativeSlice.call(arguments)));\n };\n}\n\n/**\n * @memberOf module:zrender/core/util\n * @param {Function} func\n * @return {Function}\n */\nexport function curry(func) {\n var args = nativeSlice.call(arguments, 1);\n return function () {\n return func.apply(this, args.concat(nativeSlice.call(arguments)));\n };\n}\n\n/**\n * @memberOf module:zrender/core/util\n * @param {*} value\n * @return {boolean}\n */\nexport function isArray(value) {\n return objToString.call(value) === '[object Array]';\n}\n\n/**\n * @memberOf module:zrender/core/util\n * @param {*} value\n * @return {boolean}\n */\nexport function isFunction(value) {\n return typeof value === 'function';\n}\n\n/**\n * @memberOf module:zrender/core/util\n * @param {*} value\n * @return {boolean}\n */\nexport function isString(value) {\n return objToString.call(value) === '[object String]';\n}\n\n/**\n * @memberOf module:zrender/core/util\n * @param {*} value\n * @return {boolean}\n */\nexport function isObject(value) {\n // Avoid a V8 JIT bug in Chrome 19-20.\n // See https://code.google.com/p/v8/issues/detail?id=2291 for more details.\n var type = typeof value;\n return type === 'function' || (!!value && type == 'object');\n}\n\n/**\n * @memberOf module:zrender/core/util\n * @param {*} value\n * @return {boolean}\n */\nexport function isBuiltInObject(value) {\n return !!BUILTIN_OBJECT[objToString.call(value)];\n}\n\n/**\n * @memberOf module:zrender/core/util\n * @param {*} value\n * @return {boolean}\n */\nexport function isDom(value) {\n return typeof value === 'object'\n && typeof value.nodeType === 'number'\n && typeof value.ownerDocument === 'object';\n}\n\n/**\n * Whether is exactly NaN. Notice isNaN('a') returns true.\n * @param {*} value\n * @return {boolean}\n */\nexport function eqNaN(value) {\n return value !== value;\n}\n\n/**\n * If value1 is not null, then return value1, otherwise judget rest of values.\n * Low performance.\n * @memberOf module:zrender/core/util\n * @return {*} Final value\n */\nexport function retrieve(values) {\n for (var i = 0, len = arguments.length; i < len; i++) {\n if (arguments[i] != null) {\n return arguments[i];\n }\n }\n}\n\nexport function retrieve2(value0, value1) {\n return value0 != null\n ? value0\n : value1;\n}\n\nexport function retrieve3(value0, value1, value2) {\n return value0 != null\n ? value0\n : value1 != null\n ? value1\n : value2;\n}\n\n/**\n * @memberOf module:zrender/core/util\n * @param {Array} arr\n * @param {number} startIndex\n * @param {number} endIndex\n * @return {Array}\n */\nexport function slice() {\n return Function.call.apply(nativeSlice, arguments);\n}\n\n/**\n * Normalize css liked array configuration\n * e.g.\n * 3 => [3, 3, 3, 3]\n * [4, 2] => [4, 2, 4, 2]\n * [4, 3, 2] => [4, 3, 2, 3]\n * @param {number|Array.<number>} val\n * @return {Array.<number>}\n */\nexport function normalizeCssArray(val) {\n if (typeof (val) === 'number') {\n return [val, val, val, val];\n }\n var len = val.length;\n if (len === 2) {\n // vertical | horizontal\n return [val[0], val[1], val[0], val[1]];\n }\n else if (len === 3) {\n // top | horizontal | bottom\n return [val[0], val[1], val[2], val[1]];\n }\n return val;\n}\n\n/**\n * @memberOf module:zrender/core/util\n * @param {boolean} condition\n * @param {string} message\n */\nexport function assert(condition, message) {\n if (!condition) {\n throw new Error(message);\n }\n}\n\nvar primitiveKey = '__ec_primitive__';\n/**\n * Set an object as primitive to be ignored traversing children in clone or merge\n */\nexport function setAsPrimitive(obj) {\n obj[primitiveKey] = true;\n}\n\nexport function isPrimitive(obj) {\n return obj[primitiveKey];\n}\n\n/**\n * @constructor\n * @param {Object} obj Only apply `ownProperty`.\n */\nfunction HashMap(obj) {\n obj && each(obj, function (value, key) {\n this.set(key, value);\n }, this);\n}\n\n// Add prefix to avoid conflict with Object.prototype.\nvar HASH_MAP_PREFIX = '_ec_';\nvar HASH_MAP_PREFIX_LENGTH = 4;\n\nHashMap.prototype = {\n constructor: HashMap,\n // Do not provide `has` method to avoid defining what is `has`.\n // (We usually treat `null` and `undefined` as the same, different\n // from ES6 Map).\n get: function (key) {\n return this[HASH_MAP_PREFIX + key];\n },\n set: function (key, value) {\n this[HASH_MAP_PREFIX + key] = value;\n // Comparing with invocation chaining, `return value` is more commonly\n // used in this case: `var someVal = map.set('a', genVal());`\n return value;\n },\n // Although util.each can be performed on this hashMap directly, user\n // should not use the exposed keys, who are prefixed.\n each: function (cb, context) {\n context !== void 0 && (cb = bind(cb, context));\n for (var prefixedKey in this) {\n this.hasOwnProperty(prefixedKey)\n && cb(this[prefixedKey], prefixedKey.slice(HASH_MAP_PREFIX_LENGTH));\n }\n },\n // Do not use this method if performance sensitive.\n removeKey: function (key) {\n delete this[HASH_MAP_PREFIX + key];\n }\n};\n\nexport function createHashMap(obj) {\n return new HashMap(obj);\n}\n\nexport function noop() {}\n","\nvar ArrayCtor = typeof Float32Array === 'undefined'\n ? Array\n : Float32Array;\n\n/**\n * 创建一个向量\n * @param {number} [x=0]\n * @param {number} [y=0]\n * @return {Vector2}\n */\nexport function create(x, y) {\n var out = new ArrayCtor(2);\n if (x == null) {\n x = 0;\n }\n if (y == null) {\n y = 0;\n }\n out[0] = x;\n out[1] = y;\n return out;\n}\n\n/**\n * 复制向量数据\n * @param {Vector2} out\n * @param {Vector2} v\n * @return {Vector2}\n */\nexport function copy(out, v) {\n out[0] = v[0];\n out[1] = v[1];\n return out;\n}\n\n/**\n * 克隆一个向量\n * @param {Vector2} v\n * @return {Vector2}\n */\nexport function clone(v) {\n var out = new ArrayCtor(2);\n out[0] = v[0];\n out[1] = v[1];\n return out;\n}\n\n/**\n * 设置向量的两个项\n * @param {Vector2} out\n * @param {number} a\n * @param {number} b\n * @return {Vector2} 结果\n */\nexport function set(out, a, b) {\n out[0] = a;\n out[1] = b;\n return out;\n}\n\n/**\n * 向量相加\n * @param {Vector2} out\n * @param {Vector2} v1\n * @param {Vector2} v2\n */\nexport function add(out, v1, v2) {\n out[0] = v1[0] + v2[0];\n out[1] = v1[1] + v2[1];\n return out;\n}\n\n/**\n * 向量缩放后相加\n * @param {Vector2} out\n * @param {Vector2} v1\n * @param {Vector2} v2\n * @param {number} a\n */\nexport function scaleAndAdd(out, v1, v2, a) {\n out[0] = v1[0] + v2[0] * a;\n out[1] = v1[1] + v2[1] * a;\n return out;\n}\n\n/**\n * 向量相减\n * @param {Vector2} out\n * @param {Vector2} v1\n * @param {Vector2} v2\n */\nexport function sub(out, v1, v2) {\n out[0] = v1[0] - v2[0];\n out[1] = v1[1] - v2[1];\n return out;\n}\n\n/**\n * 向量长度\n * @param {Vector2} v\n * @return {number}\n */\nexport function len(v) {\n return Math.sqrt(lenSquare(v));\n}\nexport var length = len; // jshint ignore:line\n\n/**\n * 向量长度平方\n * @param {Vector2} v\n * @return {number}\n */\nexport function lenSquare(v) {\n return v[0] * v[0] + v[1] * v[1];\n}\nexport var lengthSquare = lenSquare;\n\n/**\n * 向量乘法\n * @param {Vector2} out\n * @param {Vector2} v1\n * @param {Vector2} v2\n */\nexport function mul(out, v1, v2) {\n out[0] = v1[0] * v2[0];\n out[1] = v1[1] * v2[1];\n return out;\n}\n\n/**\n * 向量除法\n * @param {Vector2} out\n * @param {Vector2} v1\n * @param {Vector2} v2\n */\nexport function div(out, v1, v2) {\n out[0] = v1[0] / v2[0];\n out[1] = v1[1] / v2[1];\n return out;\n}\n\n/**\n * 向量点乘\n * @param {Vector2} v1\n * @param {Vector2} v2\n * @return {number}\n */\nexport function dot(v1, v2) {\n return v1[0] * v2[0] + v1[1] * v2[1];\n}\n\n/**\n * 向量缩放\n * @param {Vector2} out\n * @param {Vector2} v\n * @param {number} s\n */\nexport function scale(out, v, s) {\n out[0] = v[0] * s;\n out[1] = v[1] * s;\n return out;\n}\n\n/**\n * 向量归一化\n * @param {Vector2} out\n * @param {Vector2} v\n */\nexport function normalize(out, v) {\n var d = len(v);\n if (d === 0) {\n out[0] = 0;\n out[1] = 0;\n }\n else {\n out[0] = v[0] / d;\n out[1] = v[1] / d;\n }\n return out;\n}\n\n/**\n * 计算向量间距离\n * @param {Vector2} v1\n * @param {Vector2} v2\n * @return {number}\n */\nexport function distance(v1, v2) {\n return Math.sqrt(\n (v1[0] - v2[0]) * (v1[0] - v2[0])\n + (v1[1] - v2[1]) * (v1[1] - v2[1])\n );\n}\nexport var dist = distance;\n\n/**\n * 向量距离平方\n * @param {Vector2} v1\n * @param {Vector2} v2\n * @return {number}\n */\nexport function distanceSquare(v1, v2) {\n return (v1[0] - v2[0]) * (v1[0] - v2[0])\n + (v1[1] - v2[1]) * (v1[1] - v2[1]);\n}\nexport var distSquare = distanceSquare;\n\n/**\n * 求负向量\n * @param {Vector2} out\n * @param {Vector2} v\n */\nexport function negate(out, v) {\n out[0] = -v[0];\n out[1] = -v[1];\n return out;\n}\n\n/**\n * 插值两个点\n * @param {Vector2} out\n * @param {Vector2} v1\n * @param {Vector2} v2\n * @param {number} t\n */\nexport function lerp(out, v1, v2, t) {\n out[0] = v1[0] + t * (v2[0] - v1[0]);\n out[1] = v1[1] + t * (v2[1] - v1[1]);\n return out;\n}\n\n/**\n * 矩阵左乘向量\n * @param {Vector2} out\n * @param {Vector2} v\n * @param {Vector2} m\n */\nexport function applyTransform(out, v, m) {\n var x = v[0];\n var y = v[1];\n out[0] = m[0] * x + m[2] * y + m[4];\n out[1] = m[1] * x + m[3] * y + m[5];\n return out;\n}\n\n/**\n * 求两个向量最小值\n * @param {Vector2} out\n * @param {Vector2} v1\n * @param {Vector2} v2\n */\nexport function min(out, v1, v2) {\n out[0] = Math.min(v1[0], v2[0]);\n out[1] = Math.min(v1[1], v2[1]);\n return out;\n}\n\n/**\n * 求两个向量最大值\n * @param {Vector2} out\n * @param {Vector2} v1\n * @param {Vector2} v2\n */\nexport function max(out, v1, v2) {\n out[0] = Math.max(v1[0], v2[0]);\n out[1] = Math.max(v1[1], v2[1]);\n return out;\n}\n","// TODO Draggable for group\n// FIXME Draggable on element which has parent rotation or scale\nfunction Draggable() {\n\n this.on('mousedown', this._dragStart, this);\n this.on('mousemove', this._drag, this);\n this.on('mouseup', this._dragEnd, this);\n this.on('globalout', this._dragEnd, this);\n // this._dropTarget = null;\n // this._draggingTarget = null;\n\n // this._x = 0;\n // this._y = 0;\n}\n\nDraggable.prototype = {\n\n constructor: Draggable,\n\n _dragStart: function (e) {\n var draggingTarget = e.target;\n if (draggingTarget && draggingTarget.draggable) {\n this._draggingTarget = draggingTarget;\n draggingTarget.dragging = true;\n this._x = e.offsetX;\n this._y = e.offsetY;\n\n this.dispatchToElement(param(draggingTarget, e), 'dragstart', e.event);\n }\n },\n\n _drag: function (e) {\n var draggingTarget = this._draggingTarget;\n if (draggingTarget) {\n\n var x = e.offsetX;\n var y = e.offsetY;\n\n var dx = x - this._x;\n var dy = y - this._y;\n this._x = x;\n this._y = y;\n\n draggingTarget.drift(dx, dy, e);\n this.dispatchToElement(param(draggingTarget, e), 'drag', e.event);\n\n var dropTarget = this.findHover(x, y, draggingTarget).target;\n var lastDropTarget = this._dropTarget;\n this._dropTarget = dropTarget;\n\n if (draggingTarget !== dropTarget) {\n if (lastDropTarget && dropTarget !== lastDropTarget) {\n this.dispatchToElement(param(lastDropTarget, e), 'dragleave', e.event);\n }\n if (dropTarget && dropTarget !== lastDropTarget) {\n this.dispatchToElement(param(dropTarget, e), 'dragenter', e.event);\n }\n }\n }\n },\n\n _dragEnd: function (e) {\n var draggingTarget = this._draggingTarget;\n\n if (draggingTarget) {\n draggingTarget.dragging = false;\n }\n\n this.dispatchToElement(param(draggingTarget, e), 'dragend', e.event);\n\n if (this._dropTarget) {\n this.dispatchToElement(param(this._dropTarget, e), 'drop', e.event);\n }\n\n this._draggingTarget = null;\n this._dropTarget = null;\n }\n\n};\n\nfunction param(target, e) {\n return {target: target, topTarget: e && e.topTarget};\n}\n\nexport default Draggable;","/**\n * 事件扩展\n * @module zrender/mixin/Eventful\n * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)\n * pissang (https://www.github.com/pissang)\n */\n\nvar arrySlice = Array.prototype.slice;\n\n/**\n * 事件分发器\n * @alias module:zrender/mixin/Eventful\n * @constructor\n */\nvar Eventful = function () {\n this._$handlers = {};\n};\n\nEventful.prototype = {\n\n constructor: Eventful,\n\n /**\n * 单次触发绑定,trigger后销毁\n *\n * @param {string} event 事件名\n * @param {Function} handler 响应函数\n * @param {Object} context\n */\n one: function (event, handler, context) {\n var _h = this._$handlers;\n\n if (!handler || !event) {\n return this;\n }\n\n if (!_h[event]) {\n _h[event] = [];\n }\n\n for (var i = 0; i < _h[event].length; i++) {\n if (_h[event][i].h === handler) {\n return this;\n }\n }\n\n _h[event].push({\n h: handler,\n one: true,\n ctx: context || this\n });\n\n return this;\n },\n\n /**\n * 绑定事件\n * @param {string} event 事件名\n * @param {Function} handler 事件处理函数\n * @param {Object} [context]\n */\n on: function (event, handler, context) {\n var _h = this._$handlers;\n\n if (!handler || !event) {\n return this;\n }\n\n if (!_h[event]) {\n _h[event] = [];\n }\n\n for (var i = 0; i < _h[event].length; i++) {\n if (_h[event][i].h === handler) {\n return this;\n }\n }\n\n _h[event].push({\n h: handler,\n one: false,\n ctx: context || this\n });\n\n return this;\n },\n\n /**\n * 是否绑定了事件\n * @param {string} event\n * @return {boolean}\n */\n isSilent: function (event) {\n var _h = this._$handlers;\n return _h[event] && _h[event].length;\n },\n\n /**\n * 解绑事件\n * @param {string} event 事件名\n * @param {Function} [handler] 事件处理函数\n */\n off: function (event, handler) {\n var _h = this._$handlers;\n\n if (!event) {\n this._$handlers = {};\n return this;\n }\n\n if (handler) {\n if (_h[event]) {\n var newList = [];\n for (var i = 0, l = _h[event].length; i < l; i++) {\n if (_h[event][i]['h'] != handler) {\n newList.push(_h[event][i]);\n }\n }\n _h[event] = newList;\n }\n\n if (_h[event] && _h[event].length === 0) {\n delete _h[event];\n }\n }\n else {\n delete _h[event];\n }\n\n return this;\n },\n\n /**\n * 事件分发\n *\n * @param {string} type 事件类型\n */\n trigger: function (type) {\n if (this._$handlers[type]) {\n var args = arguments;\n var argLen = args.length;\n\n if (argLen > 3) {\n args = arrySlice.call(args, 1);\n }\n\n var _h = this._$handlers[type];\n var len = _h.length;\n for (var i = 0; i < len;) {\n // Optimize advise from backbone\n switch (argLen) {\n case 1:\n _h[i]['h'].call(_h[i]['ctx']);\n break;\n case 2:\n _h[i]['h'].call(_h[i]['ctx'], args[1]);\n break;\n case 3:\n _h[i]['h'].call(_h[i]['ctx'], args[1], args[2]);\n break;\n default:\n // have more than 2 given arguments\n _h[i]['h'].apply(_h[i]['ctx'], args);\n break;\n }\n\n if (_h[i]['one']) {\n _h.splice(i, 1);\n len--;\n }\n else {\n i++;\n }\n }\n }\n\n return this;\n },\n\n /**\n * 带有context的事件分发, 最后一个参数是事件回调的context\n * @param {string} type 事件类型\n */\n triggerWithContext: function (type) {\n if (this._$handlers[type]) {\n var args = arguments;\n var argLen = args.length;\n\n if (argLen > 4) {\n args = arrySlice.call(args, 1, args.length - 1);\n }\n var ctx = args[args.length - 1];\n\n var _h = this._$handlers[type];\n var len = _h.length;\n for (var i = 0; i < len;) {\n // Optimize advise from backbone\n switch (argLen) {\n case 1:\n _h[i]['h'].call(ctx);\n break;\n case 2:\n _h[i]['h'].call(ctx, args[1]);\n break;\n case 3:\n _h[i]['h'].call(ctx, args[1], args[2]);\n break;\n default:\n // have more than 2 given arguments\n _h[i]['h'].apply(ctx, args);\n break;\n }\n\n if (_h[i]['one']) {\n _h.splice(i, 1);\n len--;\n }\n else {\n i++;\n }\n }\n }\n\n return this;\n }\n};\n\n// 对象可以通过 onxxxx 绑定事件\n/**\n * @event module:zrender/mixin/Eventful#onclick\n * @type {Function}\n * @default null\n */\n/**\n * @event module:zrender/mixin/Eventful#onmouseover\n * @type {Function}\n * @default null\n */\n/**\n * @event module:zrender/mixin/Eventful#onmouseout\n * @type {Function}\n * @default null\n */\n/**\n * @event module:zrender/mixin/Eventful#onmousemove\n * @type {Function}\n * @default null\n */\n/**\n * @event module:zrender/mixin/Eventful#onmousewheel\n * @type {Function}\n * @default null\n */\n/**\n * @event module:zrender/mixin/Eventful#onmousedown\n * @type {Function}\n * @default null\n */\n/**\n * @event module:zrender/mixin/Eventful#onmouseup\n * @type {Function}\n * @default null\n */\n/**\n * @event module:zrender/mixin/Eventful#ondrag\n * @type {Function}\n * @default null\n */\n/**\n * @event module:zrender/mixin/Eventful#ondragstart\n * @type {Function}\n * @default null\n */\n/**\n * @event module:zrender/mixin/Eventful#ondragend\n * @type {Function}\n * @default null\n */\n/**\n * @event module:zrender/mixin/Eventful#ondragenter\n * @type {Function}\n * @default null\n */\n/**\n * @event module:zrender/mixin/Eventful#ondragleave\n * @type {Function}\n * @default null\n */\n/**\n * @event module:zrender/mixin/Eventful#ondragover\n * @type {Function}\n * @default null\n */\n/**\n * @event module:zrender/mixin/Eventful#ondrop\n * @type {Function}\n * @default null\n */\n\nexport default Eventful;","/**\n * Handler\n * @module zrender/Handler\n * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)\n * errorrik (errorrik@gmail.com)\n * pissang (shenyi.914@gmail.com)\n */\n\nimport * as util from './core/util';\nimport * as vec2 from './core/vector';\nimport Draggable from './mixin/Draggable';\nimport Eventful from './mixin/Eventful';\n\nvar SILENT = 'silent';\n\nfunction makeEventPacket(eveType, targetInfo, event) {\n return {\n type: eveType,\n event: event,\n // target can only be an element that is not silent.\n target: targetInfo.target,\n // topTarget can be a silent element.\n topTarget: targetInfo.topTarget,\n cancelBubble: false,\n offsetX: event.zrX,\n offsetY: event.zrY,\n gestureEvent: event.gestureEvent,\n pinchX: event.pinchX,\n pinchY: event.pinchY,\n pinchScale: event.pinchScale,\n wheelDelta: event.zrDelta,\n zrByTouch: event.zrByTouch,\n which: event.which\n };\n}\n\nfunction EmptyProxy () {}\nEmptyProxy.prototype.dispose = function () {};\n\nvar handlerNames = [\n 'click', 'dblclick', 'mousewheel', 'mouseout',\n 'mouseup', 'mousedown', 'mousemove', 'contextmenu'\n];\n/**\n * @alias module:zrender/Handler\n * @constructor\n * @extends module:zrender/mixin/Eventful\n * @param {module:zrender/Storage} storage Storage instance.\n * @param {module:zrender/Painter} painter Painter instance.\n * @param {module:zrender/dom/HandlerProxy} proxy HandlerProxy instance.\n * @param {HTMLElement} painterRoot painter.root (not painter.getViewportRoot()).\n */\nvar Handler = function(storage, painter, proxy, painterRoot) {\n Eventful.call(this);\n\n this.storage = storage;\n\n this.painter = painter;\n\n this.painterRoot = painterRoot;\n\n proxy = proxy || new EmptyProxy();\n\n /**\n * Proxy of event. can be Dom, WebGLSurface, etc.\n */\n this.proxy = proxy;\n\n // Attach handler\n proxy.handler = this;\n\n /**\n * {target, topTarget, x, y}\n * @private\n * @type {Object}\n */\n this._hovered = {};\n\n /**\n * @private\n * @type {Date}\n */\n this._lastTouchMoment;\n\n /**\n * @private\n * @type {number}\n */\n this._lastX;\n\n /**\n * @private\n * @type {number}\n */\n this._lastY;\n\n\n Draggable.call(this);\n\n util.each(handlerNames, function (name) {\n proxy.on && proxy.on(name, this[name], this);\n }, this);\n};\n\nHandler.prototype = {\n\n constructor: Handler,\n\n mousemove: function (event) {\n var x = event.zrX;\n var y = event.zrY;\n\n var lastHovered = this._hovered;\n var lastHoveredTarget = lastHovered.target;\n\n // If lastHoveredTarget is removed from zr (detected by '__zr') by some API call\n // (like 'setOption' or 'dispatchAction') in event handlers, we should find\n // lastHovered again here. Otherwise 'mouseout' can not be triggered normally.\n // See #6198.\n if (lastHoveredTarget && !lastHoveredTarget.__zr) {\n lastHovered = this.findHover(lastHovered.x, lastHovered.y);\n lastHoveredTarget = lastHovered.target;\n }\n\n var hovered = this._hovered = this.findHover(x, y);\n var hoveredTarget = hovered.target;\n\n var proxy = this.proxy;\n proxy.setCursor && proxy.setCursor(hoveredTarget ? hoveredTarget.cursor : 'default');\n\n // Mouse out on previous hovered element\n if (lastHoveredTarget && hoveredTarget !== lastHoveredTarget) {\n this.dispatchToElement(lastHovered, 'mouseout', event);\n }\n\n // Mouse moving on one element\n this.dispatchToElement(hovered, 'mousemove', event);\n\n // Mouse over on a new element\n if (hoveredTarget && hoveredTarget !== lastHoveredTarget) {\n this.dispatchToElement(hovered, 'mouseover', event);\n }\n },\n\n mouseout: function (event) {\n this.dispatchToElement(this._hovered, 'mouseout', event);\n\n // There might be some doms created by upper layer application\n // at the same level of painter.getViewportRoot() (e.g., tooltip\n // dom created by echarts), where 'globalout' event should not\n // be triggered when mouse enters these doms. (But 'mouseout'\n // should be triggered at the original hovered element as usual).\n var element = event.toElement || event.relatedTarget;\n var innerDom;\n do {\n element = element && element.parentNode;\n }\n while (element && element.nodeType != 9 && !(\n innerDom = element === this.painterRoot\n ));\n\n !innerDom && this.trigger('globalout', {event: event});\n },\n\n /**\n * Resize\n */\n resize: function (event) {\n this._hovered = {};\n },\n\n /**\n * Dispatch event\n * @param {string} eventName\n * @param {event=} eventArgs\n */\n dispatch: function (eventName, eventArgs) {\n var handler = this[eventName];\n handler && handler.call(this, eventArgs);\n },\n\n /**\n * Dispose\n */\n dispose: function () {\n\n this.proxy.dispose();\n\n this.storage =\n this.proxy =\n this.painter = null;\n },\n\n /**\n * 设置默认的cursor style\n * @param {string} [cursorStyle='default'] 例如 crosshair\n */\n setCursorStyle: function (cursorStyle) {\n var proxy = this.proxy;\n proxy.setCursor && proxy.setCursor(cursorStyle);\n },\n\n /**\n * 事件分发代理\n *\n * @private\n * @param {Object} targetInfo {target, topTarget} 目标图形元素\n * @param {string} eventName 事件名称\n * @param {Object} event 事件对象\n */\n dispatchToElement: function (targetInfo, eventName, event) {\n targetInfo = targetInfo || {};\n var el = targetInfo.target;\n if (el && el.silent) {\n return;\n }\n var eventHandler = 'on' + eventName;\n var eventPacket = makeEventPacket(eventName, targetInfo, event);\n\n while (el) {\n el[eventHandler]\n && (eventPacket.cancelBubble = el[eventHandler].call(el, eventPacket));\n\n el.trigger(eventName, eventPacket);\n\n el = el.parent;\n\n if (eventPacket.cancelBubble) {\n break;\n }\n }\n\n if (!eventPacket.cancelBubble) {\n // 冒泡到顶级 zrender 对象\n this.trigger(eventName, eventPacket);\n // 分发事件到用户自定义层\n // 用户有可能在全局 click 事件中 dispose,所以需要判断下 painter 是否存在\n this.painter && this.painter.eachOtherLayer(function (layer) {\n if (typeof(layer[eventHandler]) == 'function') {\n layer[eventHandler].call(layer, eventPacket);\n }\n if (layer.trigger) {\n layer.trigger(eventName, eventPacket);\n }\n });\n }\n },\n\n /**\n * @private\n * @param {number} x\n * @param {number} y\n * @param {module:zrender/graphic/Displayable} exclude\n * @return {model:zrender/Element}\n * @method\n */\n findHover: function(x, y, exclude) {\n var list = this.storage.getDisplayList();\n var out = {x: x, y: y};\n\n for (var i = list.length - 1; i >= 0 ; i--) {\n var hoverCheckResult;\n if (list[i] !== exclude\n // getDisplayList may include ignored item in VML mode\n && !list[i].ignore\n && (hoverCheckResult = isHover(list[i], x, y))\n ) {\n !out.topTarget && (out.topTarget = list[i]);\n if (hoverCheckResult !== SILENT) {\n out.target = list[i];\n break;\n }\n }\n }\n\n return out;\n }\n};\n\n// Common handlers\nutil.each(['click', 'mousedown', 'mouseup', 'mousewheel', 'dblclick', 'contextmenu'], function (name) {\n Handler.prototype[name] = function (event) {\n // Find hover again to avoid click event is dispatched manually. Or click is triggered without mouseover\n var hovered = this.findHover(event.zrX, event.zrY);\n var hoveredTarget = hovered.target;\n\n if (name === 'mousedown') {\n this._downEl = hoveredTarget;\n this._downPoint = [event.zrX, event.zrY];\n // In case click triggered before mouseup\n this._upEl = hoveredTarget;\n }\n else if (name === 'mosueup') {\n this._upEl = hoveredTarget;\n }\n else if (name === 'click') {\n if (this._downEl !== this._upEl\n // Original click event is triggered on the whole canvas element,\n // including the case that `mousedown` - `mousemove` - `mouseup`,\n // which should be filtered, otherwise it will bring trouble to\n // pan and zoom.\n || !this._downPoint\n // Arbitrary value\n || vec2.dist(this._downPoint, [event.zrX, event.zrY]) > 4\n ) {\n return;\n }\n this._downPoint = null;\n }\n\n this.dispatchToElement(hovered, name, event);\n };\n});\n\nfunction isHover(displayable, x, y) {\n if (displayable[displayable.rectHover ? 'rectContain' : 'contain'](x, y)) {\n var el = displayable;\n var isSilent;\n while (el) {\n // If clipped by ancestor.\n // FIXME: If clipPath has neither stroke nor fill,\n // el.clipPath.contain(x, y) will always return false.\n if (el.clipPath && !el.clipPath.contain(x, y)) {\n return false;\n }\n if (el.silent) {\n isSilent = true;\n }\n el = el.parent;\n }\n return isSilent ? SILENT : true;\n }\n\n return false;\n}\n\nutil.mixin(Handler, Eventful);\nutil.mixin(Handler, Draggable);\n\nexport default Handler;","/**\n * 3x2矩阵操作类\n * @exports zrender/tool/matrix\n */\n\nvar ArrayCtor = typeof Float32Array === 'undefined'\n ? Array\n : Float32Array;\n\n/**\n * 创建一个单位矩阵\n * @return {Float32Array|Array.<number>}\n */\nexport function create() {\n var out = new ArrayCtor(6);\n identity(out);\n\n return out;\n}\n\n/**\n * 设置矩阵为单位矩阵\n * @param {Float32Array|Array.<number>} out\n */\nexport function identity(out) {\n out[0] = 1;\n out[1] = 0;\n out[2] = 0;\n out[3] = 1;\n out[4] = 0;\n out[5] = 0;\n return out;\n}\n\n/**\n * 复制矩阵\n * @param {Float32Array|Array.<number>} out\n * @param {Float32Array|Array.<number>} m\n */\nexport function copy(out, m) {\n out[0] = m[0];\n out[1] = m[1];\n out[2] = m[2];\n out[3] = m[3];\n out[4] = m[4];\n out[5] = m[5];\n return out;\n}\n\n/**\n * 矩阵相乘\n * @param {Float32Array|Array.<number>} out\n * @param {Float32Array|Array.<number>} m1\n * @param {Float32Array|Array.<number>} m2\n */\nexport function mul(out, m1, m2) {\n // Consider matrix.mul(m, m2, m);\n // where out is the same as m2.\n // So use temp variable to escape error.\n var out0 = m1[0] * m2[0] + m1[2] * m2[1];\n var out1 = m1[1] * m2[0] + m1[3] * m2[1];\n var out2 = m1[0] * m2[2] + m1[2] * m2[3];\n var out3 = m1[1] * m2[2] + m1[3] * m2[3];\n var out4 = m1[0] * m2[4] + m1[2] * m2[5] + m1[4];\n var out5 = m1[1] * m2[4] + m1[3] * m2[5] + m1[5];\n out[0] = out0;\n out[1] = out1;\n out[2] = out2;\n out[3] = out3;\n out[4] = out4;\n out[5] = out5;\n return out;\n}\n\n/**\n * 平移变换\n * @param {Float32Array|Array.<number>} out\n * @param {Float32Array|Array.<number>} a\n * @param {Float32Array|Array.<number>} v\n */\nexport function translate(out, a, v) {\n out[0] = a[0];\n out[1] = a[1];\n out[2] = a[2];\n out[3] = a[3];\n out[4] = a[4] + v[0];\n out[5] = a[5] + v[1];\n return out;\n}\n\n/**\n * 旋转变换\n * @param {Float32Array|Array.<number>} out\n * @param {Float32Array|Array.<number>} a\n * @param {number} rad\n */\nexport function rotate(out, a, rad) {\n var aa = a[0];\n var ac = a[2];\n var atx = a[4];\n var ab = a[1];\n var ad = a[3];\n var aty = a[5];\n var st = Math.sin(rad);\n var ct = Math.cos(rad);\n\n out[0] = aa * ct + ab * st;\n out[1] = -aa * st + ab * ct;\n out[2] = ac * ct + ad * st;\n out[3] = -ac * st + ct * ad;\n out[4] = ct * atx + st * aty;\n out[5] = ct * aty - st * atx;\n return out;\n}\n\n/**\n * 缩放变换\n * @param {Float32Array|Array.<number>} out\n * @param {Float32Array|Array.<number>} a\n * @param {Float32Array|Array.<number>} v\n */\nexport function scale(out, a, v) {\n var vx = v[0];\n var vy = v[1];\n out[0] = a[0] * vx;\n out[1] = a[1] * vy;\n out[2] = a[2] * vx;\n out[3] = a[3] * vy;\n out[4] = a[4] * vx;\n out[5] = a[5] * vy;\n return out;\n}\n\n/**\n * 求逆矩阵\n * @param {Float32Array|Array.<number>} out\n * @param {Float32Array|Array.<number>} a\n */\nexport function invert(out, a) {\n\n var aa = a[0];\n var ac = a[2];\n var atx = a[4];\n var ab = a[1];\n var ad = a[3];\n var aty = a[5];\n\n var det = aa * ad - ab * ac;\n if (!det) {\n return null;\n }\n det = 1.0 / det;\n\n out[0] = ad * det;\n out[1] = -ab * det;\n out[2] = -ac * det;\n out[3] = aa * det;\n out[4] = (ac * aty - ad * atx) * det;\n out[5] = (ab * atx - aa * aty) * det;\n return out;\n}\n","/**\n * 提供变换扩展\n * @module zrender/mixin/Transformable\n * @author pissang (https://www.github.com/pissang)\n */\n\nimport * as matrix from '../core/matrix';\nimport * as vector from '../core/vector';\n\nvar mIdentity = matrix.identity;\n\nvar EPSILON = 5e-5;\n\nfunction isNotAroundZero(val) {\n return val > EPSILON || val < -EPSILON;\n}\n\n/**\n * @alias module:zrender/mixin/Transformable\n * @constructor\n */\nvar Transformable = function (opts) {\n opts = opts || {};\n // If there are no given position, rotation, scale\n if (!opts.position) {\n /**\n * 平移\n * @type {Array.<number>}\n * @default [0, 0]\n */\n this.position = [0, 0];\n }\n if (opts.rotation == null) {\n /**\n * 旋转\n * @type {Array.<number>}\n * @default 0\n */\n this.rotation = 0;\n }\n if (!opts.scale) {\n /**\n * 缩放\n * @type {Array.<number>}\n * @default [1, 1]\n */\n this.scale = [1, 1];\n }\n /**\n * 旋转和缩放的原点\n * @type {Array.<number>}\n * @default null\n */\n this.origin = this.origin || null;\n};\n\nvar transformableProto = Transformable.prototype;\ntransformableProto.transform = null;\n\n/**\n * 判断是否需要有坐标变换\n * 如果有坐标变换, 则从position, rotation, scale以及父节点的transform计算出自身的transform矩阵\n */\ntransformableProto.needLocalTransform = function () {\n return isNotAroundZero(this.rotation)\n || isNotAroundZero(this.position[0])\n || isNotAroundZero(this.position[1])\n || isNotAroundZero(this.scale[0] - 1)\n || isNotAroundZero(this.scale[1] - 1);\n};\n\ntransformableProto.updateTransform = function () {\n var parent = this.parent;\n var parentHasTransform = parent && parent.transform;\n var needLocalTransform = this.needLocalTransform();\n\n var m = this.transform;\n if (!(needLocalTransform || parentHasTransform)) {\n m && mIdentity(m);\n return;\n }\n\n m = m || matrix.create();\n\n if (needLocalTransform) {\n this.getLocalTransform(m);\n }\n else {\n mIdentity(m);\n }\n\n // 应用父节点变换\n if (parentHasTransform) {\n if (needLocalTransform) {\n matrix.mul(m, parent.transform, m);\n }\n else {\n matrix.copy(m, parent.transform);\n }\n }\n // 保存这个变换矩阵\n this.transform = m;\n\n this.invTransform = this.invTransform || matrix.create();\n matrix.invert(this.invTransform, m);\n};\n\ntransformableProto.getLocalTransform = function (m) {\n return Transformable.getLocalTransform(this, m);\n};\n\n/**\n * 将自己的transform应用到context上\n * @param {CanvasRenderingContext2D} ctx\n */\ntransformableProto.setTransform = function (ctx) {\n var m = this.transform;\n var dpr = ctx.dpr || 1;\n if (m) {\n ctx.setTransform(dpr * m[0], dpr * m[1], dpr * m[2], dpr * m[3], dpr * m[4], dpr * m[5]);\n }\n else {\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n }\n};\n\ntransformableProto.restoreTransform = function (ctx) {\n var dpr = ctx.dpr || 1;\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n};\n\nvar tmpTransform = [];\n\n/**\n * 分解`transform`矩阵到`position`, `rotation`, `scale`\n */\ntransformableProto.decomposeTransform = function () {\n if (!this.transform) {\n return;\n }\n var parent = this.parent;\n var m = this.transform;\n if (parent && parent.transform) {\n // Get local transform and decompose them to position, scale, rotation\n matrix.mul(tmpTransform, parent.invTransform, m);\n m = tmpTransform;\n }\n var sx = m[0] * m[0] + m[1] * m[1];\n var sy = m[2] * m[2] + m[3] * m[3];\n var position = this.position;\n var scale = this.scale;\n if (isNotAroundZero(sx - 1)) {\n sx = Math.sqrt(sx);\n }\n if (isNotAroundZero(sy - 1)) {\n sy = Math.sqrt(sy);\n }\n if (m[0] < 0) {\n sx = -sx;\n }\n if (m[3] < 0) {\n sy = -sy;\n }\n position[0] = m[4];\n position[1] = m[5];\n scale[0] = sx;\n scale[1] = sy;\n this.rotation = Math.atan2(-m[1] / sy, m[0] / sx);\n};\n\n/**\n * Get global scale\n * @return {Array.<number>}\n */\ntransformableProto.getGlobalScale = function () {\n var m = this.transform;\n if (!m) {\n return [1, 1];\n }\n var sx = Math.sqrt(m[0] * m[0] + m[1] * m[1]);\n var sy = Math.sqrt(m[2] * m[2] + m[3] * m[3]);\n if (m[0] < 0) {\n sx = -sx;\n }\n if (m[3] < 0) {\n sy = -sy;\n }\n return [sx, sy];\n};\n/**\n * 变换坐标位置到 shape 的局部坐标空间\n * @method\n * @param {number} x\n * @param {number} y\n * @return {Array.<number>}\n */\ntransformableProto.transformCoordToLocal = function (x, y) {\n var v2 = [x, y];\n var invTransform = this.invTransform;\n if (invTransform) {\n vector.applyTransform(v2, v2, invTransform);\n }\n return v2;\n};\n\n/**\n * 变换局部坐标位置到全局坐标空间\n * @method\n * @param {number} x\n * @param {number} y\n * @return {Array.<number>}\n */\ntransformableProto.transformCoordToGlobal = function (x, y) {\n var v2 = [x, y];\n var transform = this.transform;\n if (transform) {\n vector.applyTransform(v2, v2, transform);\n }\n return v2;\n};\n\n/**\n * @static\n * @param {Object} target\n * @param {Array.<number>} target.origin\n * @param {number} target.rotation\n * @param {Array.<number>} target.position\n * @param {Array.<number>} [m]\n */\nTransformable.getLocalTransform = function (target, m) {\n m = m || [];\n mIdentity(m);\n\n var origin = target.origin;\n var scale = target.scale || [1, 1];\n var rotation = target.rotation || 0;\n var position = target.position || [0, 0];\n\n if (origin) {\n // Translate to origin\n m[4] -= origin[0];\n m[5] -= origin[1];\n }\n matrix.scale(m, m, scale);\n if (rotation) {\n matrix.rotate(m, m, rotation);\n }\n if (origin) {\n // Translate back from origin\n m[4] += origin[0];\n m[5] += origin[1];\n }\n\n m[4] += position[0];\n m[5] += position[1];\n\n return m;\n};\n\nexport default Transformable;","/**\n * 缓动代码来自 https://github.com/sole/tween.js/blob/master/src/Tween.js\n * @see http://sole.github.io/tween.js/examples/03_graphs.html\n * @exports zrender/animation/easing\n */\nvar easing = {\n /**\n * @param {number} k\n * @return {number}\n */\n linear: function (k) {\n return k;\n },\n\n /**\n * @param {number} k\n * @return {number}\n */\n quadraticIn: function (k) {\n return k * k;\n },\n /**\n * @param {number} k\n * @return {number}\n */\n quadraticOut: function (k) {\n return k * (2 - k);\n },\n /**\n * @param {number} k\n * @return {number}\n */\n quadraticInOut: function (k) {\n if ((k *= 2) < 1) {\n return 0.5 * k * k;\n }\n return -0.5 * (--k * (k - 2) - 1);\n },\n\n // 三次方的缓动(t^3)\n /**\n * @param {number} k\n * @return {number}\n */\n cubicIn: function (k) {\n return k * k * k;\n },\n /**\n * @param {number} k\n * @return {number}\n */\n cubicOut: function (k) {\n return --k * k * k + 1;\n },\n /**\n * @param {number} k\n * @return {number}\n */\n cubicInOut: function (k) {\n if ((k *= 2) < 1) {\n return 0.5 * k * k * k;\n }\n return 0.5 * ((k -= 2) * k * k + 2);\n },\n\n // 四次方的缓动(t^4)\n /**\n * @param {number} k\n * @return {number}\n */\n quarticIn: function (k) {\n return k * k * k * k;\n },\n /**\n * @param {number} k\n * @return {number}\n */\n quarticOut: function (k) {\n return 1 - (--k * k * k * k);\n },\n /**\n * @param {number} k\n * @return {number}\n */\n quarticInOut: function (k) {\n if ((k *= 2) < 1) {\n return 0.5 * k * k * k * k;\n }\n return -0.5 * ((k -= 2) * k * k * k - 2);\n },\n\n // 五次方的缓动(t^5)\n /**\n * @param {number} k\n * @return {number}\n */\n quinticIn: function (k) {\n return k * k * k * k * k;\n },\n /**\n * @param {number} k\n * @return {number}\n */\n quinticOut: function (k) {\n return --k * k * k * k * k + 1;\n },\n /**\n * @param {number} k\n * @return {number}\n */\n quinticInOut: function (k) {\n if ((k *= 2) < 1) {\n return 0.5 * k * k * k * k * k;\n }\n return 0.5 * ((k -= 2) * k * k * k * k + 2);\n },\n\n // 正弦曲线的缓动(sin(t))\n /**\n * @param {number} k\n * @return {number}\n */\n sinusoidalIn: function (k) {\n return 1 - Math.cos(k * Math.PI / 2);\n },\n /**\n * @param {number} k\n * @return {number}\n */\n sinusoidalOut: function (k) {\n return Math.sin(k * Math.PI / 2);\n },\n /**\n * @param {number} k\n * @return {number}\n */\n sinusoidalInOut: function (k) {\n return 0.5 * (1 - Math.cos(Math.PI * k));\n },\n\n // 指数曲线的缓动(2^t)\n /**\n * @param {number} k\n * @return {number}\n */\n exponentialIn: function (k) {\n return k === 0 ? 0 : Math.pow(1024, k - 1);\n },\n /**\n * @param {number} k\n * @return {number}\n */\n exponentialOut: function (k) {\n return k === 1 ? 1 : 1 - Math.pow(2, -10 * k);\n },\n /**\n * @param {number} k\n * @return {number}\n */\n exponentialInOut: function (k) {\n if (k === 0) {\n return 0;\n }\n if (k === 1) {\n return 1;\n }\n if ((k *= 2) < 1) {\n return 0.5 * Math.pow(1024, k - 1);\n }\n return 0.5 * (-Math.pow(2, -10 * (k - 1)) + 2);\n },\n\n // 圆形曲线的缓动(sqrt(1-t^2))\n /**\n * @param {number} k\n * @return {number}\n */\n circularIn: function (k) {\n return 1 - Math.sqrt(1 - k * k);\n },\n /**\n * @param {number} k\n * @return {number}\n */\n circularOut: function (k) {\n return Math.sqrt(1 - (--k * k));\n },\n /**\n * @param {number} k\n * @return {number}\n */\n circularInOut: function (k) {\n if ((k *= 2) < 1) {\n return -0.5 * (Math.sqrt(1 - k * k) - 1);\n }\n return 0.5 * (Math.sqrt(1 - (k -= 2) * k) + 1);\n },\n\n // 创建类似于弹簧在停止前来回振荡的动画\n /**\n * @param {number} k\n * @return {number}\n */\n elasticIn: function (k) {\n var s;\n var a = 0.1;\n var p = 0.4;\n if (k === 0) {\n return 0;\n }\n if (k === 1) {\n return 1;\n }\n if (!a || a < 1) {\n a = 1; s = p / 4;\n }\n else {\n s = p * Math.asin(1 / a) / (2 * Math.PI);\n }\n return -(a * Math.pow(2, 10 * (k -= 1)) *\n Math.sin((k - s) * (2 * Math.PI) / p));\n },\n /**\n * @param {number} k\n * @return {number}\n */\n elasticOut: function (k) {\n var s;\n var a = 0.1;\n var p = 0.4;\n if (k === 0) {\n return 0;\n }\n if (k === 1) {\n return 1;\n }\n if (!a || a < 1) {\n a = 1; s = p / 4;\n }\n else {\n s = p * Math.asin(1 / a) / (2 * Math.PI);\n }\n return (a * Math.pow(2, -10 * k) *\n Math.sin((k - s) * (2 * Math.PI) / p) + 1);\n },\n /**\n * @param {number} k\n * @return {number}\n */\n elasticInOut: function (k) {\n var s;\n var a = 0.1;\n var p = 0.4;\n if (k === 0) {\n return 0;\n }\n if (k === 1) {\n return 1;\n }\n if (!a || a < 1) {\n a = 1; s = p / 4;\n }\n else {\n s = p * Math.asin(1 / a) / (2 * Math.PI);\n }\n if ((k *= 2) < 1) {\n return -0.5 * (a * Math.pow(2, 10 * (k -= 1))\n * Math.sin((k - s) * (2 * Math.PI) / p));\n }\n return a * Math.pow(2, -10 * (k -= 1))\n * Math.sin((k - s) * (2 * Math.PI) / p) * 0.5 + 1;\n\n },\n\n // 在某一动画开始沿指示的路径进行动画处理前稍稍收回该动画的移动\n /**\n * @param {number} k\n * @return {number}\n */\n backIn: function (k) {\n var s = 1.70158;\n return k * k * ((s + 1) * k - s);\n },\n /**\n * @param {number} k\n * @return {number}\n */\n backOut: function (k) {\n var s = 1.70158;\n return --k * k * ((s + 1) * k + s) + 1;\n },\n /**\n * @param {number} k\n * @return {number}\n */\n backInOut: function (k) {\n var s = 1.70158 * 1.525;\n if ((k *= 2) < 1) {\n return 0.5 * (k * k * ((s + 1) * k - s));\n }\n return 0.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2);\n },\n\n // 创建弹跳效果\n /**\n * @param {number} k\n * @return {number}\n */\n bounceIn: function (k) {\n return 1 - easing.bounceOut(1 - k);\n },\n /**\n * @param {number} k\n * @return {number}\n */\n bounceOut: function (k) {\n if (k < (1 / 2.75)) {\n return 7.5625 * k * k;\n }\n else if (k < (2 / 2.75)) {\n return 7.5625 * (k -= (1.5 / 2.75)) * k + 0.75;\n }\n else if (k < (2.5 / 2.75)) {\n return 7.5625 * (k -= (2.25 / 2.75)) * k + 0.9375;\n }\n else {\n return 7.5625 * (k -= (2.625 / 2.75)) * k + 0.984375;\n }\n },\n /**\n * @param {number} k\n * @return {number}\n */\n bounceInOut: function (k) {\n if (k < 0.5) {\n return easing.bounceIn(k * 2) * 0.5;\n }\n return easing.bounceOut(k * 2 - 1) * 0.5 + 0.5;\n }\n};\n\nexport default easing;","/**\n * 动画主控制器\n * @config target 动画对象,可以是数组,如果是数组的话会批量分发onframe等事件\n * @config life(1000) 动画时长\n * @config delay(0) 动画延迟时间\n * @config loop(true)\n * @config gap(0) 循环的间隔时间\n * @config onframe\n * @config easing(optional)\n * @config ondestroy(optional)\n * @config onrestart(optional)\n *\n * TODO pause\n */\n\nimport easingFuncs from './easing';\n\nfunction Clip(options) {\n\n this._target = options.target;\n\n // 生命周期\n this._life = options.life || 1000;\n // 延时\n this._delay = options.delay || 0;\n // 开始时间\n // this._startTime = new Date().getTime() + this._delay;// 单位毫秒\n this._initialized = false;\n\n // 是否循环\n this.loop = options.loop == null ? false : options.loop;\n\n this.gap = options.gap || 0;\n\n this.easing = options.easing || 'Linear';\n\n this.onframe = options.onframe;\n this.ondestroy = options.ondestroy;\n this.onrestart = options.onrestart;\n\n this._pausedTime = 0;\n this._paused = false;\n}\n\nClip.prototype = {\n\n constructor: Clip,\n\n step: function (globalTime, deltaTime) {\n // Set startTime on first step, or _startTime may has milleseconds different between clips\n // PENDING\n if (!this._initialized) {\n this._startTime = globalTime + this._delay;\n this._initialized = true;\n }\n\n if (this._paused) {\n this._pausedTime += deltaTime;\n return;\n }\n\n var percent = (globalTime - this._startTime - this._pausedTime) / this._life;\n\n // 还没开始\n if (percent < 0) {\n return;\n }\n\n percent = Math.min(percent, 1);\n\n var easing = this.easing;\n var easingFunc = typeof easing == 'string' ? easingFuncs[easing] : easing;\n var schedule = typeof easingFunc === 'function'\n ? easingFunc(percent)\n : percent;\n\n this.fire('frame', schedule);\n\n // 结束\n if (percent == 1) {\n if (this.loop) {\n this.restart (globalTime);\n // 重新开始周期\n // 抛出而不是直接调用事件直到 stage.update 后再统一调用这些事件\n return 'restart';\n }\n\n // 动画完成将这个控制器标识为待删除\n // 在Animation.update中进行批量删除\n this._needsRemove = true;\n return 'destroy';\n }\n\n return null;\n },\n\n restart: function (globalTime) {\n var remainder = (globalTime - this._startTime - this._pausedTime) % this._life;\n this._startTime = globalTime - remainder + this.gap;\n this._pausedTime = 0;\n\n this._needsRemove = false;\n },\n\n fire: function (eventType, arg) {\n eventType = 'on' + eventType;\n if (this[eventType]) {\n this[eventType](this._target, arg);\n }\n },\n\n pause: function () {\n this._paused = true;\n },\n\n resume: function () {\n this._paused = false;\n }\n};\n\nexport default Clip;","// Simple LRU cache use doubly linked list\n// @module zrender/core/LRU\n\n/**\n * Simple double linked list. Compared with array, it has O(1) remove operation.\n * @constructor\n */\nvar LinkedList = function () {\n\n /**\n * @type {module:zrender/core/LRU~Entry}\n */\n this.head = null;\n\n /**\n * @type {module:zrender/core/LRU~Entry}\n */\n this.tail = null;\n\n this._len = 0;\n};\n\nvar linkedListProto = LinkedList.prototype;\n/**\n * Insert a new value at the tail\n * @param {} val\n * @return {module:zrender/core/LRU~Entry}\n */\nlinkedListProto.insert = function (val) {\n var entry = new Entry(val);\n this.insertEntry(entry);\n return entry;\n};\n\n/**\n * Insert an entry at the tail\n * @param {module:zrender/core/LRU~Entry} entry\n */\nlinkedListProto.insertEntry = function (entry) {\n if (!this.head) {\n this.head = this.tail = entry;\n }\n else {\n this.tail.next = entry;\n entry.prev = this.tail;\n entry.next = null;\n this.tail = entry;\n }\n this._len++;\n};\n\n/**\n * Remove entry.\n * @param {module:zrender/core/LRU~Entry} entry\n */\nlinkedListProto.remove = function (entry) {\n var prev = entry.prev;\n var next = entry.next;\n if (prev) {\n prev.next = next;\n }\n else {\n // Is head\n this.head = next;\n }\n if (next) {\n next.prev = prev;\n }\n else {\n // Is tail\n this.tail = prev;\n }\n entry.next = entry.prev = null;\n this._len--;\n};\n\n/**\n * @return {number}\n */\nlinkedListProto.len = function () {\n return this._len;\n};\n\n/**\n * Clear list\n */\nlinkedListProto.clear = function () {\n this.head = this.tail = null;\n this._len = 0;\n};\n\n/**\n * @constructor\n * @param {} val\n */\nvar Entry = function (val) {\n /**\n * @type {}\n */\n this.value = val;\n\n /**\n * @type {module:zrender/core/LRU~Entry}\n */\n this.next;\n\n /**\n * @type {module:zrender/core/LRU~Entry}\n */\n this.prev;\n};\n\n/**\n * LRU Cache\n * @constructor\n * @alias module:zrender/core/LRU\n */\nvar LRU = function (maxSize) {\n\n this._list = new LinkedList();\n\n this._map = {};\n\n this._maxSize = maxSize || 10;\n\n this._lastRemovedEntry = null;\n};\n\nvar LRUProto = LRU.prototype;\n\n/**\n * @param {string} key\n * @param {} value\n * @return {} Removed value\n */\nLRUProto.put = function (key, value) {\n var list = this._list;\n var map = this._map;\n var removed = null;\n if (map[key] == null) {\n var len = list.len();\n // Reuse last removed entry\n var entry = this._lastRemovedEntry;\n\n if (len >= this._maxSize && len > 0) {\n // Remove the least recently used\n var leastUsedEntry = list.head;\n list.remove(leastUsedEntry);\n delete map[leastUsedEntry.key];\n\n removed = leastUsedEntry.value;\n this._lastRemovedEntry = leastUsedEntry;\n }\n\n if (entry) {\n entry.value = value;\n }\n else {\n entry = new Entry(value);\n }\n entry.key = key;\n list.insertEntry(entry);\n map[key] = entry;\n }\n\n return removed;\n};\n\n/**\n * @param {string} key\n * @return {}\n */\nLRUProto.get = function (key) {\n var entry = this._map[key];\n var list = this._list;\n if (entry != null) {\n // Put the latest used entry in the tail\n if (entry !== list.tail) {\n list.remove(entry);\n list.insertEntry(entry);\n }\n\n return entry.value;\n }\n};\n\n/**\n * Clear the cache\n */\nLRUProto.clear = function () {\n this._list.clear();\n this._map = {};\n};\n\nexport default LRU;","import LRU from '../core/LRU';\n\nvar kCSSColorTable = {\n 'transparent': [0,0,0,0], 'aliceblue': [240,248,255,1],\n 'antiquewhite': [250,235,215,1], 'aqua': [0,255,255,1],\n 'aquamarine': [127,255,212,1], 'azure': [240,255,255,1],\n 'beige': [245,245,220,1], 'bisque': [255,228,196,1],\n 'black': [0,0,0,1], 'blanchedalmond': [255,235,205,1],\n 'blue': [0,0,255,1], 'blueviolet': [138,43,226,1],\n 'brown': [165,42,42,1], 'burlywood': [222,184,135,1],\n 'cadetblue': [95,158,160,1], 'chartreuse': [127,255,0,1],\n 'chocolate': [210,105,30,1], 'coral': [255,127,80,1],\n 'cornflowerblue': [100,149,237,1], 'cornsilk': [255,248,220,1],\n 'crimson': [220,20,60,1], 'cyan': [0,255,255,1],\n 'darkblue': [0,0,139,1], 'darkcyan': [0,139,139,1],\n 'darkgoldenrod': [184,134,11,1], 'darkgray': [169,169,169,1],\n 'darkgreen': [0,100,0,1], 'darkgrey': [169,169,169,1],\n 'darkkhaki': [189,183,107,1], 'darkmagenta': [139,0,139,1],\n 'darkolivegreen': [85,107,47,1], 'darkorange': [255,140,0,1],\n 'darkorchid': [153,50,204,1], 'darkred': [139,0,0,1],\n 'darksalmon': [233,150,122,1], 'darkseagreen': [143,188,143,1],\n 'darkslateblue': [72,61,139,1], 'darkslategray': [47,79,79,1],\n 'darkslategrey': [47,79,79,1], 'darkturquoise': [0,206,209,1],\n 'darkviolet': [148,0,211,1], 'deeppink': [255,20,147,1],\n 'deepskyblue': [0,191,255,1], 'dimgray': [105,105,105,1],\n 'dimgrey': [105,105,105,1], 'dodgerblue': [30,144,255,1],\n 'firebrick': [178,34,34,1], 'floralwhite': [255,250,240,1],\n 'forestgreen': [34,139,34,1], 'fuchsia': [255,0,255,1],\n 'gainsboro': [220,220,220,1], 'ghostwhite': [248,248,255,1],\n 'gold': [255,215,0,1], 'goldenrod': [218,165,32,1],\n 'gray': [128,128,128,1], 'green': [0,128,0,1],\n 'greenyellow': [173,255,47,1], 'grey': [128,128,128,1],\n 'honeydew': [240,255,240,1], 'hotpink': [255,105,180,1],\n 'indianred': [205,92,92,1], 'indigo': [75,0,130,1],\n 'ivory': [255,255,240,1], 'khaki': [240,230,140,1],\n 'lavender': [230,230,250,1], 'lavenderblush': [255,240,245,1],\n 'lawngreen': [124,252,0,1], 'lemonchiffon': [255,250,205,1],\n 'lightblue': [173,216,230,1], 'lightcoral': [240,128,128,1],\n 'lightcyan': [224,255,255,1], 'lightgoldenrodyellow': [250,250,210,1],\n 'lightgray': [211,211,211,1], 'lightgreen': [144,238,144,1],\n 'lightgrey': [211,211,211,1], 'lightpink': [255,182,193,1],\n 'lightsalmon': [255,160,122,1], 'lightseagreen': [32,178,170,1],\n 'lightskyblue': [135,206,250,1], 'lightslategray': [119,136,153,1],\n 'lightslategrey': [119,136,153,1], 'lightsteelblue': [176,196,222,1],\n 'lightyellow': [255,255,224,1], 'lime': [0,255,0,1],\n 'limegreen': [50,205,50,1], 'linen': [250,240,230,1],\n 'magenta': [255,0,255,1], 'maroon': [128,0,0,1],\n 'mediumaquamarine': [102,205,170,1], 'mediumblue': [0,0,205,1],\n 'mediumorchid': [186,85,211,1], 'mediumpurple': [147,112,219,1],\n 'mediumseagreen': [60,179,113,1], 'mediumslateblue': [123,104,238,1],\n 'mediumspringgreen': [0,250,154,1], 'mediumturquoise': [72,209,204,1],\n 'mediumvioletred': [199,21,133,1], 'midnightblue': [25,25,112,1],\n 'mintcream': [245,255,250,1], 'mistyrose': [255,228,225,1],\n 'moccasin': [255,228,181,1], 'navajowhite': [255,222,173,1],\n 'navy': [0,0,128,1], 'oldlace': [253,245,230,1],\n 'olive': [128,128,0,1], 'olivedrab': [107,142,35,1],\n 'orange': [255,165,0,1], 'orangered': [255,69,0,1],\n 'orchid': [218,112,214,1], 'palegoldenrod': [238,232,170,1],\n 'palegreen': [152,251,152,1], 'paleturquoise': [175,238,238,1],\n 'palevioletred': [219,112,147,1], 'papayawhip': [255,239,213,1],\n 'peachpuff': [255,218,185,1], 'peru': [205,133,63,1],\n 'pink': [255,192,203,1], 'plum': [221,160,221,1],\n 'powderblue': [176,224,230,1], 'purple': [128,0,128,1],\n 'red': [255,0,0,1], 'rosybrown': [188,143,143,1],\n 'royalblue': [65,105,225,1], 'saddlebrown': [139,69,19,1],\n 'salmon': [250,128,114,1], 'sandybrown': [244,164,96,1],\n 'seagreen': [46,139,87,1], 'seashell': [255,245,238,1],\n 'sienna': [160,82,45,1], 'silver': [192,192,192,1],\n 'skyblue': [135,206,235,1], 'slateblue': [106,90,205,1],\n 'slategray': [112,128,144,1], 'slategrey': [112,128,144,1],\n 'snow': [255,250,250,1], 'springgreen': [0,255,127,1],\n 'steelblue': [70,130,180,1], 'tan': [210,180,140,1],\n 'teal': [0,128,128,1], 'thistle': [216,191,216,1],\n 'tomato': [255,99,71,1], 'turquoise': [64,224,208,1],\n 'violet': [238,130,238,1], 'wheat': [245,222,179,1],\n 'white': [255,255,255,1], 'whitesmoke': [245,245,245,1],\n 'yellow': [255,255,0,1], 'yellowgreen': [154,205,50,1]\n};\n\nfunction clampCssByte(i) { // Clamp to integer 0 .. 255.\n i = Math.round(i); // Seems to be what Chrome does (vs truncation).\n return i < 0 ? 0 : i > 255 ? 255 : i;\n}\n\nfunction clampCssAngle(i) { // Clamp to integer 0 .. 360.\n i = Math.round(i); // Seems to be what Chrome does (vs truncation).\n return i < 0 ? 0 : i > 360 ? 360 : i;\n}\n\nfunction clampCssFloat(f) { // Clamp to float 0.0 .. 1.0.\n return f < 0 ? 0 : f > 1 ? 1 : f;\n}\n\nfunction parseCssInt(str) { // int or percentage.\n if (str.length && str.charAt(str.length - 1) === '%') {\n return clampCssByte(parseFloat(str) / 100 * 255);\n }\n return clampCssByte(parseInt(str, 10));\n}\n\nfunction parseCssFloat(str) { // float or percentage.\n if (str.length && str.charAt(str.length - 1) === '%') {\n return clampCssFloat(parseFloat(str) / 100);\n }\n return clampCssFloat(parseFloat(str));\n}\n\nfunction cssHueToRgb(m1, m2, h) {\n if (h < 0) {\n h += 1;\n }\n else if (h > 1) {\n h -= 1;\n }\n\n if (h * 6 < 1) {\n return m1 + (m2 - m1) * h * 6;\n }\n if (h * 2 < 1) {\n return m2;\n }\n if (h * 3 < 2) {\n return m1 + (m2 - m1) * (2/3 - h) * 6;\n }\n return m1;\n}\n\nfunction lerpNumber(a, b, p) {\n return a + (b - a) * p;\n}\n\nfunction setRgba(out, r, g, b, a) {\n out[0] = r; out[1] = g; out[2] = b; out[3] = a;\n return out;\n}\nfunction copyRgba(out, a) {\n out[0] = a[0]; out[1] = a[1]; out[2] = a[2]; out[3] = a[3];\n return out;\n}\n\nvar colorCache = new LRU(20);\nvar lastRemovedArr = null;\n\nfunction putToCache(colorStr, rgbaArr) {\n // Reuse removed array\n if (lastRemovedArr) {\n copyRgba(lastRemovedArr, rgbaArr);\n }\n lastRemovedArr = colorCache.put(colorStr, lastRemovedArr || (rgbaArr.slice()));\n}\n\n/**\n * @param {string} colorStr\n * @param {Array.<number>} out\n * @return {Array.<number>}\n * @memberOf module:zrender/util/color\n */\nexport function parse(colorStr, rgbaArr) {\n if (!colorStr) {\n return;\n }\n rgbaArr = rgbaArr || [];\n\n var cached = colorCache.get(colorStr);\n if (cached) {\n return copyRgba(rgbaArr, cached);\n }\n\n // colorStr may be not string\n colorStr = colorStr + '';\n // Remove all whitespace, not compliant, but should just be more accepting.\n var str = colorStr.replace(/ /g, '').toLowerCase();\n\n // Color keywords (and transparent) lookup.\n if (str in kCSSColorTable) {\n copyRgba(rgbaArr, kCSSColorTable[str]);\n putToCache(colorStr, rgbaArr);\n return rgbaArr;\n }\n\n // #abc and #abc123 syntax.\n if (str.charAt(0) === '#') {\n if (str.length === 4) {\n var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing.\n if (!(iv >= 0 && iv <= 0xfff)) {\n setRgba(rgbaArr, 0, 0, 0, 1);\n return; // Covers NaN.\n }\n setRgba(rgbaArr,\n ((iv & 0xf00) >> 4) | ((iv & 0xf00) >> 8),\n (iv & 0xf0) | ((iv & 0xf0) >> 4),\n (iv & 0xf) | ((iv & 0xf) << 4),\n 1\n );\n putToCache(colorStr, rgbaArr);\n return rgbaArr;\n }\n else if (str.length === 7) {\n var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing.\n if (!(iv >= 0 && iv <= 0xffffff)) {\n setRgba(rgbaArr, 0, 0, 0, 1);\n return; // Covers NaN.\n }\n setRgba(rgbaArr,\n (iv & 0xff0000) >> 16,\n (iv & 0xff00) >> 8,\n iv & 0xff,\n 1\n );\n putToCache(colorStr, rgbaArr);\n return rgbaArr;\n }\n\n return;\n }\n var op = str.indexOf('('), ep = str.indexOf(')');\n if (op !== -1 && ep + 1 === str.length) {\n var fname = str.substr(0, op);\n var params = str.substr(op + 1, ep - (op + 1)).split(',');\n var alpha = 1; // To allow case fallthrough.\n switch (fname) {\n case 'rgba':\n if (params.length !== 4) {\n setRgba(rgbaArr, 0, 0, 0, 1);\n return;\n }\n alpha = parseCssFloat(params.pop()); // jshint ignore:line\n // Fall through.\n case 'rgb':\n if (params.length !== 3) {\n setRgba(rgbaArr, 0, 0, 0, 1);\n return;\n }\n setRgba(rgbaArr,\n parseCssInt(params[0]),\n parseCssInt(params[1]),\n parseCssInt(params[2]),\n alpha\n );\n putToCache(colorStr, rgbaArr);\n return rgbaArr;\n case 'hsla':\n if (params.length !== 4) {\n setRgba(rgbaArr, 0, 0, 0, 1);\n return;\n }\n params[3] = parseCssFloat(params[3]);\n hsla2rgba(params, rgbaArr);\n putToCache(colorStr, rgbaArr);\n return rgbaArr;\n case 'hsl':\n if (params.length !== 3) {\n setRgba(rgbaArr, 0, 0, 0, 1);\n return;\n }\n hsla2rgba(params, rgbaArr);\n putToCache(colorStr, rgbaArr);\n return rgbaArr;\n default:\n return;\n }\n }\n\n setRgba(rgbaArr, 0, 0, 0, 1);\n return;\n}\n\n/**\n * @param {Array.<number>} hsla\n * @param {Array.<number>} rgba\n * @return {Array.<number>} rgba\n */\nfunction hsla2rgba(hsla, rgba) {\n var h = (((parseFloat(hsla[0]) % 360) + 360) % 360) / 360; // 0 .. 1\n // NOTE(deanm): According to the CSS spec s/l should only be\n // percentages, but we don't bother and let float or percentage.\n var s = parseCssFloat(hsla[1]);\n var l = parseCssFloat(hsla[2]);\n var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;\n var m1 = l * 2 - m2;\n\n rgba = rgba || [];\n setRgba(rgba,\n clampCssByte(cssHueToRgb(m1, m2, h + 1 / 3) * 255),\n clampCssByte(cssHueToRgb(m1, m2, h) * 255),\n clampCssByte(cssHueToRgb(m1, m2, h - 1 / 3) * 255),\n 1\n );\n\n if (hsla.length === 4) {\n rgba[3] = hsla[3];\n }\n\n return rgba;\n}\n\n/**\n * @param {Array.<number>} rgba\n * @return {Array.<number>} hsla\n */\nfunction rgba2hsla(rgba) {\n if (!rgba) {\n return;\n }\n\n // RGB from 0 to 255\n var R = rgba[0] / 255;\n var G = rgba[1] / 255;\n var B = rgba[2] / 255;\n\n var vMin = Math.min(R, G, B); // Min. value of RGB\n var vMax = Math.max(R, G, B); // Max. value of RGB\n var delta = vMax - vMin; // Delta RGB value\n\n var L = (vMax + vMin) / 2;\n var H;\n var S;\n // HSL results from 0 to 1\n if (delta === 0) {\n H = 0;\n S = 0;\n }\n else {\n if (L < 0.5) {\n S = delta / (vMax + vMin);\n }\n else {\n S = delta / (2 - vMax - vMin);\n }\n\n var deltaR = (((vMax - R) / 6) + (delta / 2)) / delta;\n var deltaG = (((vMax - G) / 6) + (delta / 2)) / delta;\n var deltaB = (((vMax - B) / 6) + (delta / 2)) / delta;\n\n if (R === vMax) {\n H = deltaB - deltaG;\n }\n else if (G === vMax) {\n H = (1 / 3) + deltaR - deltaB;\n }\n else if (B === vMax) {\n H = (2 / 3) + deltaG - deltaR;\n }\n\n if (H < 0) {\n H += 1;\n }\n\n if (H > 1) {\n H -= 1;\n }\n }\n\n var hsla = [H * 360, S, L];\n\n if (rgba[3] != null) {\n hsla.push(rgba[3]);\n }\n\n return hsla;\n}\n\n/**\n * @param {string} color\n * @param {number} level\n * @return {string}\n * @memberOf module:zrender/util/color\n */\nexport function lift(color, level) {\n var colorArr = parse(color);\n if (colorArr) {\n for (var i = 0; i < 3; i++) {\n if (level < 0) {\n colorArr[i] = colorArr[i] * (1 - level) | 0;\n }\n else {\n colorArr[i] = ((255 - colorArr[i]) * level + colorArr[i]) | 0;\n }\n }\n return stringify(colorArr, colorArr.length === 4 ? 'rgba' : 'rgb');\n }\n}\n\n/**\n * @param {string} color\n * @return {string}\n * @memberOf module:zrender/util/color\n */\nexport function toHex(color) {\n var colorArr = parse(color);\n if (colorArr) {\n return ((1 << 24) + (colorArr[0] << 16) + (colorArr[1] << 8) + (+colorArr[2])).toString(16).slice(1);\n }\n}\n\n/**\n * Map value to color. Faster than lerp methods because color is represented by rgba array.\n * @param {number} normalizedValue A float between 0 and 1.\n * @param {Array.<Array.<number>>} colors List of rgba color array\n * @param {Array.<number>} [out] Mapped gba color array\n * @return {Array.<number>} will be null/undefined if input illegal.\n */\nexport function fastLerp(normalizedValue, colors, out) {\n if (!(colors && colors.length)\n || !(normalizedValue >= 0 && normalizedValue <= 1)\n ) {\n return;\n }\n\n out = out || [];\n\n var value = normalizedValue * (colors.length - 1);\n var leftIndex = Math.floor(value);\n var rightIndex = Math.ceil(value);\n var leftColor = colors[leftIndex];\n var rightColor = colors[rightIndex];\n var dv = value - leftIndex;\n out[0] = clampCssByte(lerpNumber(leftColor[0], rightColor[0], dv));\n out[1] = clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv));\n out[2] = clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv));\n out[3] = clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv));\n\n return out;\n}\n\n/**\n * @deprecated\n */\nexport var fastMapToColor = fastLerp;\n\n/**\n * @param {number} normalizedValue A float between 0 and 1.\n * @param {Array.<string>} colors Color list.\n * @param {boolean=} fullOutput Default false.\n * @return {(string|Object)} Result color. If fullOutput,\n * return {color: ..., leftIndex: ..., rightIndex: ..., value: ...},\n * @memberOf module:zrender/util/color\n */\nexport function lerp(normalizedValue, colors, fullOutput) {\n if (!(colors && colors.length)\n || !(normalizedValue >= 0 && normalizedValue <= 1)\n ) {\n return;\n }\n\n var value = normalizedValue * (colors.length - 1);\n var leftIndex = Math.floor(value);\n var rightIndex = Math.ceil(value);\n var leftColor = parse(colors[leftIndex]);\n var rightColor = parse(colors[rightIndex]);\n var dv = value - leftIndex;\n\n var color = stringify(\n [\n clampCssByte(lerpNumber(leftColor[0], rightColor[0], dv)),\n clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv)),\n clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv)),\n clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv))\n ],\n 'rgba'\n );\n\n return fullOutput\n ? {\n color: color,\n leftIndex: leftIndex,\n rightIndex: rightIndex,\n value: value\n }\n : color;\n}\n\n/**\n * @deprecated\n */\nexport var mapToColor = lerp;\n\n/**\n * @param {string} color\n * @param {number=} h 0 ~ 360, ignore when null.\n * @param {number=} s 0 ~ 1, ignore when null.\n * @param {number=} l 0 ~ 1, ignore when null.\n * @return {string} Color string in rgba format.\n * @memberOf module:zrender/util/color\n */\nexport function modifyHSL(color, h, s, l) {\n color = parse(color);\n\n if (color) {\n color = rgba2hsla(color);\n h != null && (color[0] = clampCssAngle(h));\n s != null && (color[1] = parseCssFloat(s));\n l != null && (color[2] = parseCssFloat(l));\n\n return stringify(hsla2rgba(color), 'rgba');\n }\n}\n\n/**\n * @param {string} color\n * @param {number=} alpha 0 ~ 1\n * @return {string} Color string in rgba format.\n * @memberOf module:zrender/util/color\n */\nexport function modifyAlpha(color, alpha) {\n color = parse(color);\n\n if (color && alpha != null) {\n color[3] = clampCssFloat(alpha);\n return stringify(color, 'rgba');\n }\n}\n\n/**\n * @param {Array.<number>} arrColor like [12,33,44,0.4]\n * @param {string} type 'rgba', 'hsva', ...\n * @return {string} Result color. (If input illegal, return undefined).\n */\nexport function stringify(arrColor, type) {\n if (!arrColor || !arrColor.length) {\n return;\n }\n var colorStr = arrColor[0] + ',' + arrColor[1] + ',' + arrColor[2];\n if (type === 'rgba' || type === 'hsva' || type === 'hsla') {\n colorStr += ',' + arrColor[3];\n }\n return type + '(' + colorStr + ')';\n}\n","/**\n * @module echarts/animation/Animator\n */\n\nimport Clip from './Clip';\nimport * as color from '../tool/color';\nimport {isArrayLike} from '../core/util';\n\nvar arraySlice = Array.prototype.slice;\n\nfunction defaultGetter(target, key) {\n return target[key];\n}\n\nfunction defaultSetter(target, key, value) {\n target[key] = value;\n}\n\n/**\n * @param {number} p0\n * @param {number} p1\n * @param {number} percent\n * @return {number}\n */\nfunction interpolateNumber(p0, p1, percent) {\n return (p1 - p0) * percent + p0;\n}\n\n/**\n * @param {string} p0\n * @param {string} p1\n * @param {number} percent\n * @return {string}\n */\nfunction interpolateString(p0, p1, percent) {\n return percent > 0.5 ? p1 : p0;\n}\n\n/**\n * @param {Array} p0\n * @param {Array} p1\n * @param {number} percent\n * @param {Array} out\n * @param {number} arrDim\n */\nfunction interpolateArray(p0, p1, percent, out, arrDim) {\n var len = p0.length;\n if (arrDim == 1) {\n for (var i = 0; i < len; i++) {\n out[i] = interpolateNumber(p0[i], p1[i], percent);\n }\n }\n else {\n var len2 = len && p0[0].length;\n for (var i = 0; i < len; i++) {\n for (var j = 0; j < len2; j++) {\n out[i][j] = interpolateNumber(\n p0[i][j], p1[i][j], percent\n );\n }\n }\n }\n}\n\n// arr0 is source array, arr1 is target array.\n// Do some preprocess to avoid error happened when interpolating from arr0 to arr1\nfunction fillArr(arr0, arr1, arrDim) {\n var arr0Len = arr0.length;\n var arr1Len = arr1.length;\n if (arr0Len !== arr1Len) {\n // FIXME Not work for TypedArray\n var isPreviousLarger = arr0Len > arr1Len;\n if (isPreviousLarger) {\n // Cut the previous\n arr0.length = arr1Len;\n }\n else {\n // Fill the previous\n for (var i = arr0Len; i < arr1Len; i++) {\n arr0.push(\n arrDim === 1 ? arr1[i] : arraySlice.call(arr1[i])\n );\n }\n }\n }\n // Handling NaN value\n var len2 = arr0[0] && arr0[0].length;\n for (var i = 0; i < arr0.length; i++) {\n if (arrDim === 1) {\n if (isNaN(arr0[i])) {\n arr0[i] = arr1[i];\n }\n }\n else {\n for (var j = 0; j < len2; j++) {\n if (isNaN(arr0[i][j])) {\n arr0[i][j] = arr1[i][j];\n }\n }\n }\n }\n}\n\n/**\n * @param {Array} arr0\n * @param {Array} arr1\n * @param {number} arrDim\n * @return {boolean}\n */\nfunction isArraySame(arr0, arr1, arrDim) {\n if (arr0 === arr1) {\n return true;\n }\n var len = arr0.length;\n if (len !== arr1.length) {\n return false;\n }\n if (arrDim === 1) {\n for (var i = 0; i < len; i++) {\n if (arr0[i] !== arr1[i]) {\n return false;\n }\n }\n }\n else {\n var len2 = arr0[0].length;\n for (var i = 0; i < len; i++) {\n for (var j = 0; j < len2; j++) {\n if (arr0[i][j] !== arr1[i][j]) {\n return false;\n }\n }\n }\n }\n return true;\n}\n\n/**\n * Catmull Rom interpolate array\n * @param {Array} p0\n * @param {Array} p1\n * @param {Array} p2\n * @param {Array} p3\n * @param {number} t\n * @param {number} t2\n * @param {number} t3\n * @param {Array} out\n * @param {number} arrDim\n */\nfunction catmullRomInterpolateArray(\n p0, p1, p2, p3, t, t2, t3, out, arrDim\n) {\n var len = p0.length;\n if (arrDim == 1) {\n for (var i = 0; i < len; i++) {\n out[i] = catmullRomInterpolate(\n p0[i], p1[i], p2[i], p3[i], t, t2, t3\n );\n }\n }\n else {\n var len2 = p0[0].length;\n for (var i = 0; i < len; i++) {\n for (var j = 0; j < len2; j++) {\n out[i][j] = catmullRomInterpolate(\n p0[i][j], p1[i][j], p2[i][j], p3[i][j],\n t, t2, t3\n );\n }\n }\n }\n}\n\n/**\n * Catmull Rom interpolate number\n * @param {number} p0\n * @param {number} p1\n * @param {number} p2\n * @param {number} p3\n * @param {number} t\n * @param {number} t2\n * @param {number} t3\n * @return {number}\n */\nfunction catmullRomInterpolate(p0, p1, p2, p3, t, t2, t3) {\n var v0 = (p2 - p0) * 0.5;\n var v1 = (p3 - p1) * 0.5;\n return (2 * (p1 - p2) + v0 + v1) * t3\n + (-3 * (p1 - p2) - 2 * v0 - v1) * t2\n + v0 * t + p1;\n}\n\nfunction cloneValue(value) {\n if (isArrayLike(value)) {\n var len = value.length;\n if (isArrayLike(value[0])) {\n var ret = [];\n for (var i = 0; i < len; i++) {\n ret.push(arraySlice.call(value[i]));\n }\n return ret;\n }\n\n return arraySlice.call(value);\n }\n\n return value;\n}\n\nfunction rgba2String(rgba) {\n rgba[0] = Math.floor(rgba[0]);\n rgba[1] = Math.floor(rgba[1]);\n rgba[2] = Math.floor(rgba[2]);\n\n return 'rgba(' + rgba.join(',') + ')';\n}\n\nfunction getArrayDim(keyframes) {\n var lastValue = keyframes[keyframes.length - 1].value;\n return isArrayLike(lastValue && lastValue[0]) ? 2 : 1;\n}\n\nfunction createTrackClip(animator, easing, oneTrackDone, keyframes, propName, forceAnimate) {\n var getter = animator._getter;\n var setter = animator._setter;\n var useSpline = easing === 'spline';\n\n var trackLen = keyframes.length;\n if (!trackLen) {\n return;\n }\n // Guess data type\n var firstVal = keyframes[0].value;\n var isValueArray = isArrayLike(firstVal);\n var isValueColor = false;\n var isValueString = false;\n\n // For vertices morphing\n var arrDim = isValueArray ? getArrayDim(keyframes) : 0;\n\n var trackMaxTime;\n // Sort keyframe as ascending\n keyframes.sort(function(a, b) {\n return a.time - b.time;\n });\n\n trackMaxTime = keyframes[trackLen - 1].time;\n // Percents of each keyframe\n var kfPercents = [];\n // Value of each keyframe\n var kfValues = [];\n var prevValue = keyframes[0].value;\n var isAllValueEqual = true;\n for (var i = 0; i < trackLen; i++) {\n kfPercents.push(keyframes[i].time / trackMaxTime);\n // Assume value is a color when it is a string\n var value = keyframes[i].value;\n\n // Check if value is equal, deep check if value is array\n if (!((isValueArray && isArraySame(value, prevValue, arrDim))\n || (!isValueArray && value === prevValue))) {\n isAllValueEqual = false;\n }\n prevValue = value;\n\n // Try converting a string to a color array\n if (typeof value == 'string') {\n var colorArray = color.parse(value);\n if (colorArray) {\n value = colorArray;\n isValueColor = true;\n }\n else {\n isValueString = true;\n }\n }\n kfValues.push(value);\n }\n if (!forceAnimate && isAllValueEqual) {\n return;\n }\n\n var lastValue = kfValues[trackLen - 1];\n // Polyfill array and NaN value\n for (var i = 0; i < trackLen - 1; i++) {\n if (isValueArray) {\n fillArr(kfValues[i], lastValue, arrDim);\n }\n else {\n if (isNaN(kfValues[i]) && !isNaN(lastValue) && !isValueString && !isValueColor) {\n kfValues[i] = lastValue;\n }\n }\n }\n isValueArray && fillArr(getter(animator._target, propName), lastValue, arrDim);\n\n // Cache the key of last frame to speed up when\n // animation playback is sequency\n var lastFrame = 0;\n var lastFramePercent = 0;\n var start;\n var w;\n var p0;\n var p1;\n var p2;\n var p3;\n\n if (isValueColor) {\n var rgba = [0, 0, 0, 0];\n }\n\n var onframe = function (target, percent) {\n // Find the range keyframes\n // kf1-----kf2---------current--------kf3\n // find kf2 and kf3 and do interpolation\n var frame;\n // In the easing function like elasticOut, percent may less than 0\n if (percent < 0) {\n frame = 0;\n }\n else if (percent < lastFramePercent) {\n // Start from next key\n // PENDING start from lastFrame ?\n start = Math.min(lastFrame + 1, trackLen - 1);\n for (frame = start; frame >= 0; frame--) {\n if (kfPercents[frame] <= percent) {\n break;\n }\n }\n // PENDING really need to do this ?\n frame = Math.min(frame, trackLen - 2);\n }\n else {\n for (frame = lastFrame; frame < trackLen; frame++) {\n if (kfPercents[frame] > percent) {\n break;\n }\n }\n frame = Math.min(frame - 1, trackLen - 2);\n }\n lastFrame = frame;\n lastFramePercent = percent;\n\n var range = (kfPercents[frame + 1] - kfPercents[frame]);\n if (range === 0) {\n return;\n }\n else {\n w = (percent - kfPercents[frame]) / range;\n }\n if (useSpline) {\n p1 = kfValues[frame];\n p0 = kfValues[frame === 0 ? frame : frame - 1];\n p2 = kfValues[frame > trackLen - 2 ? trackLen - 1 : frame + 1];\n p3 = kfValues[frame > trackLen - 3 ? trackLen - 1 : frame + 2];\n if (isValueArray) {\n catmullRomInterpolateArray(\n p0, p1, p2, p3, w, w * w, w * w * w,\n getter(target, propName),\n arrDim\n );\n }\n else {\n var value;\n if (isValueColor) {\n value = catmullRomInterpolateArray(\n p0, p1, p2, p3, w, w * w, w * w * w,\n rgba, 1\n );\n value = rgba2String(rgba);\n }\n else if (isValueString) {\n // String is step(0.5)\n return interpolateString(p1, p2, w);\n }\n else {\n value = catmullRomInterpolate(\n p0, p1, p2, p3, w, w * w, w * w * w\n );\n }\n setter(\n target,\n propName,\n value\n );\n }\n }\n else {\n if (isValueArray) {\n interpolateArray(\n kfValues[frame], kfValues[frame + 1], w,\n getter(target, propName),\n arrDim\n );\n }\n else {\n var value;\n if (isValueColor) {\n interpolateArray(\n kfValues[frame], kfValues[frame + 1], w,\n rgba, 1\n );\n value = rgba2String(rgba);\n }\n else if (isValueString) {\n // String is step(0.5)\n return interpolateString(kfValues[frame], kfValues[frame + 1], w);\n }\n else {\n value = interpolateNumber(kfValues[frame], kfValues[frame + 1], w);\n }\n setter(\n target,\n propName,\n value\n );\n }\n }\n };\n\n var clip = new Clip({\n target: animator._target,\n life: trackMaxTime,\n loop: animator._loop,\n delay: animator._delay,\n onframe: onframe,\n ondestroy: oneTrackDone\n });\n\n if (easing && easing !== 'spline') {\n clip.easing = easing;\n }\n\n return clip;\n}\n\n/**\n * @alias module:zrender/animation/Animator\n * @constructor\n * @param {Object} target\n * @param {boolean} loop\n * @param {Function} getter\n * @param {Function} setter\n */\nvar Animator = function(target, loop, getter, setter) {\n this._tracks = {};\n this._target = target;\n\n this._loop = loop || false;\n\n this._getter = getter || defaultGetter;\n this._setter = setter || defaultSetter;\n\n this._clipCount = 0;\n\n this._delay = 0;\n\n this._doneList = [];\n\n this._onframeList = [];\n\n this._clipList = [];\n};\n\nAnimator.prototype = {\n /**\n * 设置动画关键帧\n * @param {number} time 关键帧时间,单位是ms\n * @param {Object} props 关键帧的属性值,key-value表示\n * @return {module:zrender/animation/Animator}\n */\n when: function(time /* ms */, props) {\n var tracks = this._tracks;\n for (var propName in props) {\n if (!props.hasOwnProperty(propName)) {\n continue;\n }\n\n if (!tracks[propName]) {\n tracks[propName] = [];\n // Invalid value\n var value = this._getter(this._target, propName);\n if (value == null) {\n // zrLog('Invalid property ' + propName);\n continue;\n }\n // If time is 0\n // Then props is given initialize value\n // Else\n // Initialize value from current prop value\n if (time !== 0) {\n tracks[propName].push({\n time: 0,\n value: cloneValue(value)\n });\n }\n }\n tracks[propName].push({\n time: time,\n value: props[propName]\n });\n }\n return this;\n },\n /**\n * 添加动画每一帧的回调函数\n * @param {Function} callback\n * @return {module:zrender/animation/Animator}\n */\n during: function (callback) {\n this._onframeList.push(callback);\n return this;\n },\n\n pause: function () {\n for (var i = 0; i < this._clipList.length; i++) {\n this._clipList[i].pause();\n }\n this._paused = true;\n },\n\n resume: function () {\n for (var i = 0; i < this._clipList.length; i++) {\n this._clipList[i].resume();\n }\n this._paused = false;\n },\n\n isPaused: function () {\n return !!this._paused;\n },\n\n _doneCallback: function () {\n // Clear all tracks\n this._tracks = {};\n // Clear all clips\n this._clipList.length = 0;\n\n var doneList = this._doneList;\n var len = doneList.length;\n for (var i = 0; i < len; i++) {\n doneList[i].call(this);\n }\n },\n /**\n * 开始执行动画\n * @param {string|Function} [easing]\n * 动画缓动函数,详见{@link module:zrender/animation/easing}\n * @param {boolean} forceAnimate\n * @return {module:zrender/animation/Animator}\n */\n start: function (easing, forceAnimate) {\n\n var self = this;\n var clipCount = 0;\n\n var oneTrackDone = function() {\n clipCount--;\n if (!clipCount) {\n self._doneCallback();\n }\n };\n\n var lastClip;\n for (var propName in this._tracks) {\n if (!this._tracks.hasOwnProperty(propName)) {\n continue;\n }\n var clip = createTrackClip(\n this, easing, oneTrackDone,\n this._tracks[propName], propName, forceAnimate\n );\n if (clip) {\n this._clipList.push(clip);\n clipCount++;\n\n // If start after added to animation\n if (this.animation) {\n this.animation.addClip(clip);\n }\n\n lastClip = clip;\n }\n }\n\n // Add during callback on the last clip\n if (lastClip) {\n var oldOnFrame = lastClip.onframe;\n lastClip.onframe = function (target, percent) {\n oldOnFrame(target, percent);\n\n for (var i = 0; i < self._onframeList.length; i++) {\n self._onframeList[i](target, percent);\n }\n };\n }\n\n // This optimization will help the case that in the upper application\n // the view may be refreshed frequently, where animation will be\n // called repeatly but nothing changed.\n if (!clipCount) {\n this._doneCallback();\n }\n return this;\n },\n /**\n * 停止动画\n * @param {boolean} forwardToLast If move to last frame before stop\n */\n stop: function (forwardToLast) {\n var clipList = this._clipList;\n var animation = this.animation;\n for (var i = 0; i < clipList.length; i++) {\n var clip = clipList[i];\n if (forwardToLast) {\n // Move to last frame before stop\n clip.onframe(this._target, 1);\n }\n animation && animation.removeClip(clip);\n }\n clipList.length = 0;\n },\n /**\n * 设置动画延迟开始的时间\n * @param {number} time 单位ms\n * @return {module:zrender/animation/Animator}\n */\n delay: function (time) {\n this._delay = time;\n return this;\n },\n /**\n * 添加动画结束的回调\n * @param {Function} cb\n * @return {module:zrender/animation/Animator}\n */\n done: function(cb) {\n if (cb) {\n this._doneList.push(cb);\n }\n return this;\n },\n\n /**\n * @return {Array.<module:zrender/animation/Clip>}\n */\n getClips: function () {\n return this._clipList;\n }\n};\n\nexport default Animator;","var dpr = 1;\n\n// If in browser environment\nif (typeof window !== 'undefined') {\n dpr = Math.max(window.devicePixelRatio || 1, 1);\n}\n\n/**\n * config默认配置项\n * @exports zrender/config\n * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)\n */\n\n/**\n * debug日志选项:catchBrushException为true下有效\n * 0 : 不生成debug数据,发布用\n * 1 : 异常抛出,调试用\n * 2 : 控制台输出,调试用\n */\nexport var debugMode = 0;\n\n// retina 屏幕优化\nexport var devicePixelRatio = dpr;\n","import {debugMode} from '../config';\n\nvar log = function () {\n};\n\nif (debugMode === 1) {\n log = function () {\n for (var k in arguments) {\n throw new Error(arguments[k]);\n }\n };\n}\nelse if (debugMode > 1) {\n log = function () {\n for (var k in arguments) {\n console.log(arguments[k]);\n }\n };\n}\n\nexport default log;","import Animator from '../animation/Animator';\nimport log from '../core/log';\nimport {\n isString,\n isFunction,\n isObject,\n isArrayLike,\n indexOf\n} from '../core/util';\n\n/**\n * @alias modue:zrender/mixin/Animatable\n * @constructor\n */\nvar Animatable = function () {\n\n /**\n * @type {Array.<module:zrender/animation/Animator>}\n * @readOnly\n */\n this.animators = [];\n};\n\nAnimatable.prototype = {\n\n constructor: Animatable,\n\n /**\n * 动画\n *\n * @param {string} path The path to fetch value from object, like 'a.b.c'.\n * @param {boolean} [loop] Whether to loop animation.\n * @return {module:zrender/animation/Animator}\n * @example:\n * el.animate('style', false)\n * .when(1000, {x: 10} )\n * .done(function(){ // Animation done })\n * .start()\n */\n animate: function (path, loop) {\n var target;\n var animatingShape = false;\n var el = this;\n var zr = this.__zr;\n if (path) {\n var pathSplitted = path.split('.');\n var prop = el;\n // If animating shape\n animatingShape = pathSplitted[0] === 'shape';\n for (var i = 0, l = pathSplitted.length; i < l; i++) {\n if (!prop) {\n continue;\n }\n prop = prop[pathSplitted[i]];\n }\n if (prop) {\n target = prop;\n }\n }\n else {\n target = el;\n }\n\n if (!target) {\n log(\n 'Property \"'\n + path\n + '\" is not existed in element '\n + el.id\n );\n return;\n }\n\n var animators = el.animators;\n\n var animator = new Animator(target, loop);\n\n animator.during(function (target) {\n el.dirty(animatingShape);\n })\n .done(function () {\n // FIXME Animator will not be removed if use `Animator#stop` to stop animation\n animators.splice(indexOf(animators, animator), 1);\n });\n\n animators.push(animator);\n\n // If animate after added to the zrender\n if (zr) {\n zr.animation.addAnimator(animator);\n }\n\n return animator;\n },\n\n /**\n * 停止动画\n * @param {boolean} forwardToLast If move to last frame before stop\n */\n stopAnimation: function (forwardToLast) {\n var animators = this.animators;\n var len = animators.length;\n for (var i = 0; i < len; i++) {\n animators[i].stop(forwardToLast);\n }\n animators.length = 0;\n\n return this;\n },\n\n /**\n * Caution: this method will stop previous animation.\n * So do not use this method to one element twice before\n * animation starts, unless you know what you are doing.\n * @param {Object} target\n * @param {number} [time=500] Time in ms\n * @param {string} [easing='linear']\n * @param {number} [delay=0]\n * @param {Function} [callback]\n * @param {Function} [forceAnimate] Prevent stop animation and callback\n * immediently when target values are the same as current values.\n *\n * @example\n * // Animate position\n * el.animateTo({\n * position: [10, 10]\n * }, function () { // done })\n *\n * // Animate shape, style and position in 100ms, delayed 100ms, with cubicOut easing\n * el.animateTo({\n * shape: {\n * width: 500\n * },\n * style: {\n * fill: 'red'\n * }\n * position: [10, 10]\n * }, 100, 100, 'cubicOut', function () { // done })\n */\n // TODO Return animation key\n animateTo: function (target, time, delay, easing, callback, forceAnimate) {\n // animateTo(target, time, easing, callback);\n if (isString(delay)) {\n callback = easing;\n easing = delay;\n delay = 0;\n }\n // animateTo(target, time, delay, callback);\n else if (isFunction(easing)) {\n callback = easing;\n easing = 'linear';\n delay = 0;\n }\n // animateTo(target, time, callback);\n else if (isFunction(delay)) {\n callback = delay;\n delay = 0;\n }\n // animateTo(target, callback)\n else if (isFunction(time)) {\n callback = time;\n time = 500;\n }\n // animateTo(target)\n else if (!time) {\n time = 500;\n }\n // Stop all previous animations\n this.stopAnimation();\n this._animateToShallow('', this, target, time, delay);\n\n // Animators may be removed immediately after start\n // if there is nothing to animate\n var animators = this.animators.slice();\n var count = animators.length;\n function done() {\n count--;\n if (!count) {\n callback && callback();\n }\n }\n\n // No animators. This should be checked before animators[i].start(),\n // because 'done' may be executed immediately if no need to animate.\n if (!count) {\n callback && callback();\n }\n // Start after all animators created\n // Incase any animator is done immediately when all animation properties are not changed\n for (var i = 0; i < animators.length; i++) {\n animators[i]\n .done(done)\n .start(easing, forceAnimate);\n }\n },\n\n /**\n * @private\n * @param {string} path=''\n * @param {Object} source=this\n * @param {Object} target\n * @param {number} [time=500]\n * @param {number} [delay=0]\n *\n * @example\n * // Animate position\n * el._animateToShallow({\n * position: [10, 10]\n * })\n *\n * // Animate shape, style and position in 100ms, delayed 100ms\n * el._animateToShallow({\n * shape: {\n * width: 500\n * },\n * style: {\n * fill: 'red'\n * }\n * position: [10, 10]\n * }, 100, 100)\n */\n _animateToShallow: function (path, source, target, time, delay) {\n var objShallow = {};\n var propertyCount = 0;\n for (var name in target) {\n if (!target.hasOwnProperty(name)) {\n continue;\n }\n\n if (source[name] != null) {\n if (isObject(target[name]) && !isArrayLike(target[name])) {\n this._animateToShallow(\n path ? path + '.' + name : name,\n source[name],\n target[name],\n time,\n delay\n );\n }\n else {\n objShallow[name] = target[name];\n propertyCount++;\n }\n }\n else if (target[name] != null) {\n // Attr directly if not has property\n // FIXME, if some property not needed for element ?\n if (!path) {\n this.attr(name, target[name]);\n }\n else { // Shape or style\n var props = {};\n props[path] = {};\n props[path][name] = target[name];\n this.attr(props);\n }\n }\n }\n\n if (propertyCount > 0) {\n this.animate(path, false)\n .when(time == null ? 500 : time, objShallow)\n .delay(delay || 0);\n }\n\n return this;\n }\n};\n\nexport default Animatable;","import guid from './core/guid';\nimport Eventful from './mixin/Eventful';\nimport Transformable from './mixin/Transformable';\nimport Animatable from './mixin/Animatable';\nimport * as zrUtil from './core/util';\n\n/**\n * @alias module:zrender/Element\n * @constructor\n * @extends {module:zrender/mixin/Animatable}\n * @extends {module:zrender/mixin/Transformable}\n * @extends {module:zrender/mixin/Eventful}\n */\nvar Element = function (opts) { // jshint ignore:line\n\n Transformable.call(this, opts);\n Eventful.call(this, opts);\n Animatable.call(this, opts);\n\n /**\n * 画布元素ID\n * @type {string}\n */\n this.id = opts.id || guid();\n};\n\nElement.prototype = {\n\n /**\n * 元素类型\n * Element type\n * @type {string}\n */\n type: 'element',\n\n /**\n * 元素名字\n * Element name\n * @type {string}\n */\n name: '',\n\n /**\n * ZRender 实例对象,会在 element 添加到 zrender 实例中后自动赋值\n * ZRender instance will be assigned when element is associated with zrender\n * @name module:/zrender/Element#__zr\n * @type {module:zrender/ZRender}\n */\n __zr: null,\n\n /**\n * 图形是否忽略,为true时忽略图形的绘制以及事件触发\n * If ignore drawing and events of the element object\n * @name module:/zrender/Element#ignore\n * @type {boolean}\n * @default false\n */\n ignore: false,\n\n /**\n * 用于裁剪的路径(shape),所有 Group 内的路径在绘制时都会被这个路径裁剪\n * 该路径会继承被裁减对象的变换\n * @type {module:zrender/graphic/Path}\n * @see http://www.w3.org/TR/2dcontext/#clipping-region\n * @readOnly\n */\n clipPath: null,\n\n /**\n * Drift element\n * @param {number} dx dx on the global space\n * @param {number} dy dy on the global space\n */\n drift: function (dx, dy) {\n switch (this.draggable) {\n case 'horizontal':\n dy = 0;\n break;\n case 'vertical':\n dx = 0;\n break;\n }\n\n var m = this.transform;\n if (!m) {\n m = this.transform = [1, 0, 0, 1, 0, 0];\n }\n m[4] += dx;\n m[5] += dy;\n\n this.decomposeTransform();\n this.dirty(false);\n },\n\n /**\n * Hook before update\n */\n beforeUpdate: function () {},\n /**\n * Hook after update\n */\n afterUpdate: function () {},\n /**\n * Update each frame\n */\n update: function () {\n this.updateTransform();\n },\n\n /**\n * @param {Function} cb\n * @param {} context\n */\n traverse: function (cb, context) {},\n\n /**\n * @protected\n */\n attrKV: function (key, value) {\n if (key === 'position' || key === 'scale' || key === 'origin') {\n // Copy the array\n if (value) {\n var target = this[key];\n if (!target) {\n target = this[key] = [];\n }\n target[0] = value[0];\n target[1] = value[1];\n }\n }\n else {\n this[key] = value;\n }\n },\n\n /**\n * Hide the element\n */\n hide: function () {\n this.ignore = true;\n this.__zr && this.__zr.refresh();\n },\n\n /**\n * Show the element\n */\n show: function () {\n this.ignore = false;\n this.__zr && this.__zr.refresh();\n },\n\n /**\n * @param {string|Object} key\n * @param {*} value\n */\n attr: function (key, value) {\n if (typeof key === 'string') {\n this.attrKV(key, value);\n }\n else if (zrUtil.isObject(key)) {\n for (var name in key) {\n if (key.hasOwnProperty(name)) {\n this.attrKV(name, key[name]);\n }\n }\n }\n\n this.dirty(false);\n\n return this;\n },\n\n /**\n * @param {module:zrender/graphic/Path} clipPath\n */\n setClipPath: function (clipPath) {\n var zr = this.__zr;\n if (zr) {\n clipPath.addSelfToZr(zr);\n }\n\n // Remove previous clip path\n if (this.clipPath && this.clipPath !== clipPath) {\n this.removeClipPath();\n }\n\n this.clipPath = clipPath;\n clipPath.__zr = zr;\n clipPath.__clipTarget = this;\n\n this.dirty(false);\n },\n\n /**\n */\n removeClipPath: function () {\n var clipPath = this.clipPath;\n if (clipPath) {\n if (clipPath.__zr) {\n clipPath.removeSelfFromZr(clipPath.__zr);\n }\n\n clipPath.__zr = null;\n clipPath.__clipTarget = null;\n this.clipPath = null;\n\n this.dirty(false);\n }\n },\n\n /**\n * Add self from zrender instance.\n * Not recursively because it will be invoked when element added to storage.\n * @param {module:zrender/ZRender} zr\n */\n addSelfToZr: function (zr) {\n this.__zr = zr;\n // 添加动画\n var animators = this.animators;\n if (animators) {\n for (var i = 0; i < animators.length; i++) {\n zr.animation.addAnimator(animators[i]);\n }\n }\n\n if (this.clipPath) {\n this.clipPath.addSelfToZr(zr);\n }\n },\n\n /**\n * Remove self from zrender instance.\n * Not recursively because it will be invoked when element added to storage.\n * @param {module:zrender/ZRender} zr\n */\n removeSelfFromZr: function (zr) {\n this.__zr = null;\n // 移除动画\n var animators = this.animators;\n if (animators) {\n for (var i = 0; i < animators.length; i++) {\n zr.animation.removeAnimator(animators[i]);\n }\n }\n\n if (this.clipPath) {\n this.clipPath.removeSelfFromZr(zr);\n }\n }\n};\n\nzrUtil.mixin(Element, Animatable);\nzrUtil.mixin(Element, Transformable);\nzrUtil.mixin(Element, Eventful);\n\nexport default Element;","/**\n * @module echarts/core/BoundingRect\n */\n\nimport * as vec2 from './vector';\nimport * as matrix from './matrix';\n\nvar v2ApplyTransform = vec2.applyTransform;\nvar mathMin = Math.min;\nvar mathMax = Math.max;\n\n/**\n * @alias module:echarts/core/BoundingRect\n */\nfunction BoundingRect(x, y, width, height) {\n\n if (width < 0) {\n x = x + width;\n width = -width;\n }\n if (height < 0) {\n y = y + height;\n height = -height;\n }\n\n /**\n * @type {number}\n */\n this.x = x;\n /**\n * @type {number}\n */\n this.y = y;\n /**\n * @type {number}\n */\n this.width = width;\n /**\n * @type {number}\n */\n this.height = height;\n}\n\nBoundingRect.prototype = {\n\n constructor: BoundingRect,\n\n /**\n * @param {module:echarts/core/BoundingRect} other\n */\n union: function (other) {\n var x = mathMin(other.x, this.x);\n var y = mathMin(other.y, this.y);\n\n this.width = mathMax(\n other.x + other.width,\n this.x + this.width\n ) - x;\n this.height = mathMax(\n other.y + other.height,\n this.y + this.height\n ) - y;\n this.x = x;\n this.y = y;\n },\n\n /**\n * @param {Array.<number>} m\n * @methods\n */\n applyTransform: (function () {\n var lt = [];\n var rb = [];\n var lb = [];\n var rt = [];\n return function (m) {\n // In case usage like this\n // el.getBoundingRect().applyTransform(el.transform)\n // And element has no transform\n if (!m) {\n return;\n }\n lt[0] = lb[0] = this.x;\n lt[1] = rt[1] = this.y;\n rb[0] = rt[0] = this.x + this.width;\n rb[1] = lb[1] = this.y + this.height;\n\n v2ApplyTransform(lt, lt, m);\n v2ApplyTransform(rb, rb, m);\n v2ApplyTransform(lb, lb, m);\n v2ApplyTransform(rt, rt, m);\n\n this.x = mathMin(lt[0], rb[0], lb[0], rt[0]);\n this.y = mathMin(lt[1], rb[1], lb[1], rt[1]);\n var maxX = mathMax(lt[0], rb[0], lb[0], rt[0]);\n var maxY = mathMax(lt[1], rb[1], lb[1], rt[1]);\n this.width = maxX - this.x;\n this.height = maxY - this.y;\n };\n })(),\n\n /**\n * Calculate matrix of transforming from self to target rect\n * @param {module:zrender/core/BoundingRect} b\n * @return {Array.<number>}\n */\n calculateTransform: function (b) {\n var a = this;\n var sx = b.width / a.width;\n var sy = b.height / a.height;\n\n var m = matrix.create();\n\n // 矩阵右乘\n matrix.translate(m, m, [-a.x, -a.y]);\n matrix.scale(m, m, [sx, sy]);\n matrix.translate(m, m, [b.x, b.y]);\n\n return m;\n },\n\n /**\n * @param {(module:echarts/core/BoundingRect|Object)} b\n * @return {boolean}\n */\n intersect: function (b) {\n if (!b) {\n return false;\n }\n\n if (!(b instanceof BoundingRect)) {\n // Normalize negative width/height.\n b = BoundingRect.create(b);\n }\n\n var a = this;\n var ax0 = a.x;\n var ax1 = a.x + a.width;\n var ay0 = a.y;\n var ay1 = a.y + a.height;\n\n var bx0 = b.x;\n var bx1 = b.x + b.width;\n var by0 = b.y;\n var by1 = b.y + b.height;\n\n return ! (ax1 < bx0 || bx1 < ax0 || ay1 < by0 || by1 < ay0);\n },\n\n contain: function (x, y) {\n var rect = this;\n return x >= rect.x\n && x <= (rect.x + rect.width)\n && y >= rect.y\n && y <= (rect.y + rect.height);\n },\n\n /**\n * @return {module:echarts/core/BoundingRect}\n */\n clone: function () {\n return new BoundingRect(this.x, this.y, this.width, this.height);\n },\n\n /**\n * Copy from another rect\n */\n copy: function (other) {\n this.x = other.x;\n this.y = other.y;\n this.width = other.width;\n this.height = other.height;\n },\n\n plain: function () {\n return {\n x: this.x,\n y: this.y,\n width: this.width,\n height: this.height\n };\n }\n};\n\n/**\n * @param {Object|module:zrender/core/BoundingRect} rect\n * @param {number} rect.x\n * @param {number} rect.y\n * @param {number} rect.width\n * @param {number} rect.height\n * @return {module:zrender/core/BoundingRect}\n */\nBoundingRect.create = function (rect) {\n return new BoundingRect(rect.x, rect.y, rect.width, rect.height);\n};\n\nexport default BoundingRect;","/**\n * Group是一个容器,可以插入子节点,Group的变换也会被应用到子节点上\n * @module zrender/graphic/Group\n * @example\n * var Group = require('zrender/container/Group');\n * var Circle = require('zrender/graphic/shape/Circle');\n * var g = new Group();\n * g.position[0] = 100;\n * g.position[1] = 100;\n * g.add(new Circle({\n * style: {\n * x: 100,\n * y: 100,\n * r: 20,\n * }\n * }));\n * zr.add(g);\n */\n\nimport * as zrUtil from '../core/util';\nimport Element from '../Element';\nimport BoundingRect from '../core/BoundingRect';\n\n/**\n * @alias module:zrender/graphic/Group\n * @constructor\n * @extends module:zrender/mixin/Transformable\n * @extends module:zrender/mixin/Eventful\n */\nvar Group = function (opts) {\n\n opts = opts || {};\n\n Element.call(this, opts);\n\n for (var key in opts) {\n if (opts.hasOwnProperty(key)) {\n this[key] = opts[key];\n }\n }\n\n this._children = [];\n\n this.__storage = null;\n\n this.__dirty = true;\n};\n\nGroup.prototype = {\n\n constructor: Group,\n\n isGroup: true,\n\n /**\n * @type {string}\n */\n type: 'group',\n\n /**\n * 所有子孙元素是否响应鼠标事件\n * @name module:/zrender/container/Group#silent\n * @type {boolean}\n * @default false\n */\n silent: false,\n\n /**\n * @return {Array.<module:zrender/Element>}\n */\n children: function () {\n return this._children.slice();\n },\n\n /**\n * 获取指定 index 的儿子节点\n * @param {number} idx\n * @return {module:zrender/Element}\n */\n childAt: function (idx) {\n return this._children[idx];\n },\n\n /**\n * 获取指定名字的儿子节点\n * @param {string} name\n * @return {module:zrender/Element}\n */\n childOfName: function (name) {\n var children = this._children;\n for (var i = 0; i < children.length; i++) {\n if (children[i].name === name) {\n return children[i];\n }\n }\n },\n\n /**\n * @return {number}\n */\n childCount: function () {\n return this._children.length;\n },\n\n /**\n * 添加子节点到最后\n * @param {module:zrender/Element} child\n */\n add: function (child) {\n if (child && child !== this && child.parent !== this) {\n\n this._children.push(child);\n\n this._doAdd(child);\n }\n\n return this;\n },\n\n /**\n * 添加子节点在 nextSibling 之前\n * @param {module:zrender/Element} child\n * @param {module:zrender/Element} nextSibling\n */\n addBefore: function (child, nextSibling) {\n if (child && child !== this && child.parent !== this\n && nextSibling && nextSibling.parent === this) {\n\n var children = this._children;\n var idx = children.indexOf(nextSibling);\n\n if (idx >= 0) {\n children.splice(idx, 0, child);\n this._doAdd(child);\n }\n }\n\n return this;\n },\n\n _doAdd: function (child) {\n if (child.parent) {\n child.parent.remove(child);\n }\n\n child.parent = this;\n\n var storage = this.__storage;\n var zr = this.__zr;\n if (storage && storage !== child.__storage) {\n\n storage.addToStorage(child);\n\n if (child instanceof Group) {\n child.addChildrenToStorage(storage);\n }\n }\n\n zr && zr.refresh();\n },\n\n /**\n * 移除子节点\n * @param {module:zrender/Element} child\n */\n remove: function (child) {\n var zr = this.__zr;\n var storage = this.__storage;\n var children = this._children;\n\n var idx = zrUtil.indexOf(children, child);\n if (idx < 0) {\n return this;\n }\n children.splice(idx, 1);\n\n child.parent = null;\n\n if (storage) {\n\n storage.delFromStorage(child);\n\n if (child instanceof Group) {\n child.delChildrenFromStorage(storage);\n }\n }\n\n zr && zr.refresh();\n\n return this;\n },\n\n /**\n * 移除所有子节点\n */\n removeAll: function () {\n var children = this._children;\n var storage = this.__storage;\n var child;\n var i;\n for (i = 0; i < children.length; i++) {\n child = children[i];\n if (storage) {\n storage.delFromStorage(child);\n if (child instanceof Group) {\n child.delChildrenFromStorage(storage);\n }\n }\n child.parent = null;\n }\n children.length = 0;\n\n return this;\n },\n\n /**\n * 遍历所有子节点\n * @param {Function} cb\n * @param {} context\n */\n eachChild: function (cb, context) {\n var children = this._children;\n for (var i = 0; i < children.length; i++) {\n var child = children[i];\n cb.call(context, child, i);\n }\n return this;\n },\n\n /**\n * 深度优先遍历所有子孙节点\n * @param {Function} cb\n * @param {} context\n */\n traverse: function (cb, context) {\n for (var i = 0; i < this._children.length; i++) {\n var child = this._children[i];\n cb.call(context, child);\n\n if (child.type === 'group') {\n child.traverse(cb, context);\n }\n }\n return this;\n },\n\n addChildrenToStorage: function (storage) {\n for (var i = 0; i < this._children.length; i++) {\n var child = this._children[i];\n storage.addToStorage(child);\n if (child instanceof Group) {\n child.addChildrenToStorage(storage);\n }\n }\n },\n\n delChildrenFromStorage: function (storage) {\n for (var i = 0; i < this._children.length; i++) {\n var child = this._children[i];\n storage.delFromStorage(child);\n if (child instanceof Group) {\n child.delChildrenFromStorage(storage);\n }\n }\n },\n\n dirty: function () {\n this.__dirty = true;\n this.__zr && this.__zr.refresh();\n return this;\n },\n\n /**\n * @return {module:zrender/core/BoundingRect}\n */\n getBoundingRect: function (includeChildren) {\n // TODO Caching\n var rect = null;\n var tmpRect = new BoundingRect(0, 0, 0, 0);\n var children = includeChildren || this._children;\n var tmpMat = [];\n\n for (var i = 0; i < children.length; i++) {\n var child = children[i];\n if (child.ignore || child.invisible) {\n continue;\n }\n\n var childRect = child.getBoundingRect();\n var transform = child.getLocalTransform(tmpMat);\n // TODO\n // The boundingRect cacluated by transforming original\n // rect may be bigger than the actual bundingRect when rotation\n // is used. (Consider a circle rotated aginst its center, where\n // the actual boundingRect should be the same as that not be\n // rotated.) But we can not find better approach to calculate\n // actual boundingRect yet, considering performance.\n if (transform) {\n tmpRect.copy(childRect);\n tmpRect.applyTransform(transform);\n rect = rect || tmpRect.clone();\n rect.union(tmpRect);\n }\n else {\n rect = rect || childRect.clone();\n rect.union(childRect);\n }\n }\n return rect || tmpRect;\n }\n};\n\nzrUtil.inherits(Group, Element);\n\nexport default Group;","// https://github.com/mziccard/node-timsort\nvar DEFAULT_MIN_MERGE = 32;\n\nvar DEFAULT_MIN_GALLOPING = 7;\n\nvar DEFAULT_TMP_STORAGE_LENGTH = 256;\n\nfunction minRunLength(n) {\n var r = 0;\n\n while (n >= DEFAULT_MIN_MERGE) {\n r |= n & 1;\n n >>= 1;\n }\n\n return n + r;\n}\n\nfunction makeAscendingRun(array, lo, hi, compare) {\n var runHi = lo + 1;\n\n if (runHi === hi) {\n return 1;\n }\n\n if (compare(array[runHi++], array[lo]) < 0) {\n while (runHi < hi && compare(array[runHi], array[runHi - 1]) < 0) {\n runHi++;\n }\n\n reverseRun(array, lo, runHi);\n }\n else {\n while (runHi < hi && compare(array[runHi], array[runHi - 1]) >= 0) {\n runHi++;\n }\n }\n\n return runHi - lo;\n}\n\nfunction reverseRun(array, lo, hi) {\n hi--;\n\n while (lo < hi) {\n var t = array[lo];\n array[lo++] = array[hi];\n array[hi--] = t;\n }\n}\n\nfunction binaryInsertionSort(array, lo, hi, start, compare) {\n if (start === lo) {\n start++;\n }\n\n for (; start < hi; start++) {\n var pivot = array[start];\n\n var left = lo;\n var right = start;\n var mid;\n\n while (left < right) {\n mid = left + right >>> 1;\n\n if (compare(pivot, array[mid]) < 0) {\n right = mid;\n }\n else {\n left = mid + 1;\n }\n }\n\n var n = start - left;\n\n switch (n) {\n case 3:\n array[left + 3] = array[left + 2];\n\n case 2:\n array[left + 2] = array[left + 1];\n\n case 1:\n array[left + 1] = array[left];\n break;\n default:\n while (n > 0) {\n array[left + n] = array[left + n - 1];\n n--;\n }\n }\n\n array[left] = pivot;\n }\n}\n\nfunction gallopLeft(value, array, start, length, hint, compare) {\n var lastOffset = 0;\n var maxOffset = 0;\n var offset = 1;\n\n if (compare(value, array[start + hint]) > 0) {\n maxOffset = length - hint;\n\n while (offset < maxOffset && compare(value, array[start + hint + offset]) > 0) {\n lastOffset = offset;\n offset = (offset << 1) + 1;\n\n if (offset <= 0) {\n offset = maxOffset;\n }\n }\n\n if (offset > maxOffset) {\n offset = maxOffset;\n }\n\n lastOffset += hint;\n offset += hint;\n }\n else {\n maxOffset = hint + 1;\n while (offset < maxOffset && compare(value, array[start + hint - offset]) <= 0) {\n lastOffset = offset;\n offset = (offset << 1) + 1;\n\n if (offset <= 0) {\n offset = maxOffset;\n }\n }\n if (offset > maxOffset) {\n offset = maxOffset;\n }\n\n var tmp = lastOffset;\n lastOffset = hint - offset;\n offset = hint - tmp;\n }\n\n lastOffset++;\n while (lastOffset < offset) {\n var m = lastOffset + (offset - lastOffset >>> 1);\n\n if (compare(value, array[start + m]) > 0) {\n lastOffset = m + 1;\n }\n else {\n offset = m;\n }\n }\n return offset;\n}\n\nfunction gallopRight(value, array, start, length, hint, compare) {\n var lastOffset = 0;\n var maxOffset = 0;\n var offset = 1;\n\n if (compare(value, array[start + hint]) < 0) {\n maxOffset = hint + 1;\n\n while (offset < maxOffset && compare(value, array[start + hint - offset]) < 0) {\n lastOffset = offset;\n offset = (offset << 1) + 1;\n\n if (offset <= 0) {\n offset = maxOffset;\n }\n }\n\n if (offset > maxOffset) {\n offset = maxOffset;\n }\n\n var tmp = lastOffset;\n lastOffset = hint - offset;\n offset = hint - tmp;\n }\n else {\n maxOffset = length - hint;\n\n while (offset < maxOffset && compare(value, array[start + hint + offset]) >= 0) {\n lastOffset = offset;\n offset = (offset << 1) + 1;\n\n if (offset <= 0) {\n offset = maxOffset;\n }\n }\n\n if (offset > maxOffset) {\n offset = maxOffset;\n }\n\n lastOffset += hint;\n offset += hint;\n }\n\n lastOffset++;\n\n while (lastOffset < offset) {\n var m = lastOffset + (offset - lastOffset >>> 1);\n\n if (compare(value, array[start + m]) < 0) {\n offset = m;\n }\n else {\n lastOffset = m + 1;\n }\n }\n\n return offset;\n}\n\nfunction TimSort(array, compare) {\n var minGallop = DEFAULT_MIN_GALLOPING;\n var length = 0;\n var tmpStorageLength = DEFAULT_TMP_STORAGE_LENGTH;\n var stackLength = 0;\n var runStart;\n var runLength;\n var stackSize = 0;\n\n length = array.length;\n\n if (length < 2 * DEFAULT_TMP_STORAGE_LENGTH) {\n tmpStorageLength = length >>> 1;\n }\n\n var tmp = [];\n\n stackLength = length < 120 ? 5 : length < 1542 ? 10 : length < 119151 ? 19 : 40;\n\n runStart = [];\n runLength = [];\n\n function pushRun(_runStart, _runLength) {\n runStart[stackSize] = _runStart;\n runLength[stackSize] = _runLength;\n stackSize += 1;\n }\n\n function mergeRuns() {\n while (stackSize > 1) {\n var n = stackSize - 2;\n\n if (n >= 1 && runLength[n - 1] <= runLength[n] + runLength[n + 1] || n >= 2 && runLength[n - 2] <= runLength[n] + runLength[n - 1]) {\n if (runLength[n - 1] < runLength[n + 1]) {\n n--;\n }\n }\n else if (runLength[n] > runLength[n + 1]) {\n break;\n }\n mergeAt(n);\n }\n }\n\n function forceMergeRuns() {\n while (stackSize > 1) {\n var n = stackSize - 2;\n\n if (n > 0 && runLength[n - 1] < runLength[n + 1]) {\n n--;\n }\n\n mergeAt(n);\n }\n }\n\n function mergeAt(i) {\n var start1 = runStart[i];\n var length1 = runLength[i];\n var start2 = runStart[i + 1];\n var length2 = runLength[i + 1];\n\n runLength[i] = length1 + length2;\n\n if (i === stackSize - 3) {\n runStart[i + 1] = runStart[i + 2];\n runLength[i + 1] = runLength[i + 2];\n }\n\n stackSize--;\n\n var k = gallopRight(array[start2], array, start1, length1, 0, compare);\n start1 += k;\n length1 -= k;\n\n if (length1 === 0) {\n return;\n }\n\n length2 = gallopLeft(array[start1 + length1 - 1], array, start2, length2, length2 - 1, compare);\n\n if (length2 === 0) {\n return;\n }\n\n if (length1 <= length2) {\n mergeLow(start1, length1, start2, length2);\n }\n else {\n mergeHigh(start1, length1, start2, length2);\n }\n }\n\n function mergeLow(start1, length1, start2, length2) {\n var i = 0;\n\n for (i = 0; i < length1; i++) {\n tmp[i] = array[start1 + i];\n }\n\n var cursor1 = 0;\n var cursor2 = start2;\n var dest = start1;\n\n array[dest++] = array[cursor2++];\n\n if (--length2 === 0) {\n for (i = 0; i < length1; i++) {\n array[dest + i] = tmp[cursor1 + i];\n }\n return;\n }\n\n if (length1 === 1) {\n for (i = 0; i < length2; i++) {\n array[dest + i] = array[cursor2 + i];\n }\n array[dest + length2] = tmp[cursor1];\n return;\n }\n\n var _minGallop = minGallop;\n var count1, count2, exit;\n\n while (1) {\n count1 = 0;\n count2 = 0;\n exit = false;\n\n do {\n if (compare(array[cursor2], tmp[cursor1]) < 0) {\n array[dest++] = array[cursor2++];\n count2++;\n count1 = 0;\n\n if (--length2 === 0) {\n exit = true;\n break;\n }\n }\n else {\n array[dest++] = tmp[cursor1++];\n count1++;\n count2 = 0;\n if (--length1 === 1) {\n exit = true;\n break;\n }\n }\n } while ((count1 | count2) < _minGallop);\n\n if (exit) {\n break;\n }\n\n do {\n count1 = gallopRight(array[cursor2], tmp, cursor1, length1, 0, compare);\n\n if (count1 !== 0) {\n for (i = 0; i < count1; i++) {\n array[dest + i] = tmp[cursor1 + i];\n }\n\n dest += count1;\n cursor1 += count1;\n length1 -= count1;\n if (length1 <= 1) {\n exit = true;\n break;\n }\n }\n\n array[dest++] = array[cursor2++];\n\n if (--length2 === 0) {\n exit = true;\n break;\n }\n\n count2 = gallopLeft(tmp[cursor1], array, cursor2, length2, 0, compare);\n\n if (count2 !== 0) {\n for (i = 0; i < count2; i++) {\n array[dest + i] = array[cursor2 + i];\n }\n\n dest += count2;\n cursor2 += count2;\n length2 -= count2;\n\n if (length2 === 0) {\n exit = true;\n break;\n }\n }\n array[dest++] = tmp[cursor1++];\n\n if (--length1 === 1) {\n exit = true;\n break;\n }\n\n _minGallop--;\n } while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING);\n\n if (exit) {\n break;\n }\n\n if (_minGallop < 0) {\n _minGallop = 0;\n }\n\n _minGallop += 2;\n }\n\n minGallop = _minGallop;\n\n minGallop < 1 && (minGallop = 1);\n\n if (length1 === 1) {\n for (i = 0; i < length2; i++) {\n array[dest + i] = array[cursor2 + i];\n }\n array[dest + length2] = tmp[cursor1];\n }\n else if (length1 === 0) {\n throw new Error();\n // throw new Error('mergeLow preconditions were not respected');\n }\n else {\n for (i = 0; i < length1; i++) {\n array[dest + i] = tmp[cursor1 + i];\n }\n }\n }\n\n function mergeHigh (start1, length1, start2, length2) {\n var i = 0;\n\n for (i = 0; i < length2; i++) {\n tmp[i] = array[start2 + i];\n }\n\n var cursor1 = start1 + length1 - 1;\n var cursor2 = length2 - 1;\n var dest = start2 + length2 - 1;\n var customCursor = 0;\n var customDest = 0;\n\n array[dest--] = array[cursor1--];\n\n if (--length1 === 0) {\n customCursor = dest - (length2 - 1);\n\n for (i = 0; i < length2; i++) {\n array[customCursor + i] = tmp[i];\n }\n\n return;\n }\n\n if (length2 === 1) {\n dest -= length1;\n cursor1 -= length1;\n customDest = dest + 1;\n customCursor = cursor1 + 1;\n\n for (i = length1 - 1; i >= 0; i--) {\n array[customDest + i] = array[customCursor + i];\n }\n\n array[dest] = tmp[cursor2];\n return;\n }\n\n var _minGallop = minGallop;\n\n while (true) {\n var count1 = 0;\n var count2 = 0;\n var exit = false;\n\n do {\n if (compare(tmp[cursor2], array[cursor1]) < 0) {\n array[dest--] = array[cursor1--];\n count1++;\n count2 = 0;\n if (--length1 === 0) {\n exit = true;\n break;\n }\n }\n else {\n array[dest--] = tmp[cursor2--];\n count2++;\n count1 = 0;\n if (--length2 === 1) {\n exit = true;\n break;\n }\n }\n } while ((count1 | count2) < _minGallop);\n\n if (exit) {\n break;\n }\n\n do {\n count1 = length1 - gallopRight(tmp[cursor2], array, start1, length1, length1 - 1, compare);\n\n if (count1 !== 0) {\n dest -= count1;\n cursor1 -= count1;\n length1 -= count1;\n customDest = dest + 1;\n customCursor = cursor1 + 1;\n\n for (i = count1 - 1; i >= 0; i--) {\n array[customDest + i] = array[customCursor + i];\n }\n\n if (length1 === 0) {\n exit = true;\n break;\n }\n }\n\n array[dest--] = tmp[cursor2--];\n\n if (--length2 === 1) {\n exit = true;\n break;\n }\n\n count2 = length2 - gallopLeft(array[cursor1], tmp, 0, length2, length2 - 1, compare);\n\n if (count2 !== 0) {\n dest -= count2;\n cursor2 -= count2;\n length2 -= count2;\n customDest = dest + 1;\n customCursor = cursor2 + 1;\n\n for (i = 0; i < count2; i++) {\n array[customDest + i] = tmp[customCursor + i];\n }\n\n if (length2 <= 1) {\n exit = true;\n break;\n }\n }\n\n array[dest--] = array[cursor1--];\n\n if (--length1 === 0) {\n exit = true;\n break;\n }\n\n _minGallop--;\n } while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING);\n\n if (exit) {\n break;\n }\n\n if (_minGallop < 0) {\n _minGallop = 0;\n }\n\n _minGallop += 2;\n }\n\n minGallop = _minGallop;\n\n if (minGallop < 1) {\n minGallop = 1;\n }\n\n if (length2 === 1) {\n dest -= length1;\n cursor1 -= length1;\n customDest = dest + 1;\n customCursor = cursor1 + 1;\n\n for (i = length1 - 1; i >= 0; i--) {\n array[customDest + i] = array[customCursor + i];\n }\n\n array[dest] = tmp[cursor2];\n }\n else if (length2 === 0) {\n throw new Error();\n // throw new Error('mergeHigh preconditions were not respected');\n }\n else {\n customCursor = dest - (length2 - 1);\n for (i = 0; i < length2; i++) {\n array[customCursor + i] = tmp[i];\n }\n }\n }\n\n this.mergeRuns = mergeRuns;\n this.forceMergeRuns = forceMergeRuns;\n this.pushRun = pushRun;\n}\n\nexport default function sort(array, compare, lo, hi) {\n if (!lo) {\n lo = 0;\n }\n if (!hi) {\n hi = array.length;\n }\n\n var remaining = hi - lo;\n\n if (remaining < 2) {\n return;\n }\n\n var runLength = 0;\n\n if (remaining < DEFAULT_MIN_MERGE) {\n runLength = makeAscendingRun(array, lo, hi, compare);\n binaryInsertionSort(array, lo, hi, lo + runLength, compare);\n return;\n }\n\n var ts = new TimSort(array, compare);\n\n var minRun = minRunLength(remaining);\n\n do {\n runLength = makeAscendingRun(array, lo, hi, compare);\n if (runLength < minRun) {\n var force = remaining;\n if (force > minRun) {\n force = minRun;\n }\n\n binaryInsertionSort(array, lo, lo + force, lo + runLength, compare);\n runLength = force;\n }\n\n ts.pushRun(lo, runLength);\n ts.mergeRuns();\n\n remaining -= runLength;\n lo += runLength;\n } while (remaining !== 0);\n\n ts.forceMergeRuns();\n}\n","/**\n * Storage内容仓库模块\n * @module zrender/Storage\n * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)\n * @author errorrik (errorrik@gmail.com)\n * @author pissang (https://github.com/pissang/)\n */\n\nimport * as util from './core/util';\nimport env from './core/env';\nimport Group from './container/Group';\n\n// Use timsort because in most case elements are partially sorted\n// https://jsfiddle.net/pissang/jr4x7mdm/8/\nimport timsort from './core/timsort';\n\nfunction shapeCompareFunc(a, b) {\n if (a.zlevel === b.zlevel) {\n if (a.z === b.z) {\n // if (a.z2 === b.z2) {\n // // FIXME Slow has renderidx compare\n // // http://stackoverflow.com/questions/20883421/sorting-in-javascript-should-every-compare-function-have-a-return-0-statement\n // // https://github.com/v8/v8/blob/47cce544a31ed5577ffe2963f67acb4144ee0232/src/js/array.js#L1012\n // return a.__renderidx - b.__renderidx;\n // }\n return a.z2 - b.z2;\n }\n return a.z - b.z;\n }\n return a.zlevel - b.zlevel;\n}\n/**\n * 内容仓库 (M)\n * @alias module:zrender/Storage\n * @constructor\n */\nvar Storage = function () { // jshint ignore:line\n this._roots = [];\n\n this._displayList = [];\n\n this._displayListLen = 0;\n};\n\nStorage.prototype = {\n\n constructor: Storage,\n\n /**\n * @param {Function} cb\n *\n */\n traverse: function (cb, context) {\n for (var i = 0; i < this._roots.length; i++) {\n this._roots[i].traverse(cb, context);\n }\n },\n\n /**\n * 返回所有图形的绘制队列\n * @param {boolean} [update=false] 是否在返回前更新该数组\n * @param {boolean} [includeIgnore=false] 是否包含 ignore 的数组, 在 update 为 true 的时候有效\n *\n * 详见{@link module:zrender/graphic/Displayable.prototype.updateDisplayList}\n * @return {Array.<module:zrender/graphic/Displayable>}\n */\n getDisplayList: function (update, includeIgnore) {\n includeIgnore = includeIgnore || false;\n if (update) {\n this.updateDisplayList(includeIgnore);\n }\n return this._displayList;\n },\n\n /**\n * 更新图形的绘制队列。\n * 每次绘制前都会调用,该方法会先深度优先遍历整个树,更新所有Group和Shape的变换并且把所有可见的Shape保存到数组中,\n * 最后根据绘制的优先级(zlevel > z > 插入顺序)排序得到绘制队列\n * @param {boolean} [includeIgnore=false] 是否包含 ignore 的数组\n */\n updateDisplayList: function (includeIgnore) {\n this._displayListLen = 0;\n var roots = this._roots;\n var displayList = this._displayList;\n for (var i = 0, len = roots.length; i < len; i++) {\n this._updateAndAddDisplayable(roots[i], null, includeIgnore);\n }\n displayList.length = this._displayListLen;\n\n // for (var i = 0, len = displayList.length; i < len; i++) {\n // displayList[i].__renderidx = i;\n // }\n\n // displayList.sort(shapeCompareFunc);\n env.canvasSupported && timsort(displayList, shapeCompareFunc);\n },\n\n _updateAndAddDisplayable: function (el, clipPaths, includeIgnore) {\n\n if (el.ignore && !includeIgnore) {\n return;\n }\n\n el.beforeUpdate();\n\n if (el.__dirty) {\n\n el.update();\n\n }\n\n el.afterUpdate();\n\n var userSetClipPath = el.clipPath;\n if (userSetClipPath) {\n\n // FIXME 效率影响\n if (clipPaths) {\n clipPaths = clipPaths.slice();\n }\n else {\n clipPaths = [];\n }\n\n var currentClipPath = userSetClipPath;\n var parentClipPath = el;\n // Recursively add clip path\n while (currentClipPath) {\n // clipPath 的变换是基于使用这个 clipPath 的元素\n currentClipPath.parent = parentClipPath;\n currentClipPath.updateTransform();\n\n clipPaths.push(currentClipPath);\n\n parentClipPath = currentClipPath;\n currentClipPath = currentClipPath.clipPath;\n }\n }\n\n if (el.isGroup) {\n var children = el._children;\n\n for (var i = 0; i < children.length; i++) {\n var child = children[i];\n\n // Force to mark as dirty if group is dirty\n // FIXME __dirtyPath ?\n if (el.__dirty) {\n child.__dirty = true;\n }\n\n this._updateAndAddDisplayable(child, clipPaths, includeIgnore);\n }\n\n // Mark group clean here\n el.__dirty = false;\n\n }\n else {\n el.__clipPaths = clipPaths;\n\n this._displayList[this._displayListLen++] = el;\n }\n },\n\n /**\n * 添加图形(Shape)或者组(Group)到根节点\n * @param {module:zrender/Element} el\n */\n addRoot: function (el) {\n if (el.__storage === this) {\n return;\n }\n\n if (el instanceof Group) {\n el.addChildrenToStorage(this);\n }\n\n this.addToStorage(el);\n this._roots.push(el);\n },\n\n /**\n * 删除指定的图形(Shape)或者组(Group)\n * @param {string|Array.<string>} [el] 如果为空清空整个Storage\n */\n delRoot: function (el) {\n if (el == null) {\n // 不指定el清空\n for (var i = 0; i < this._roots.length; i++) {\n var root = this._roots[i];\n if (root instanceof Group) {\n root.delChildrenFromStorage(this);\n }\n }\n\n this._roots = [];\n this._displayList = [];\n this._displayListLen = 0;\n\n return;\n }\n\n if (el instanceof Array) {\n for (var i = 0, l = el.length; i < l; i++) {\n this.delRoot(el[i]);\n }\n return;\n }\n\n\n var idx = util.indexOf(this._roots, el);\n if (idx >= 0) {\n this.delFromStorage(el);\n this._roots.splice(idx, 1);\n if (el instanceof Group) {\n el.delChildrenFromStorage(this);\n }\n }\n },\n\n addToStorage: function (el) {\n el.__storage = this;\n el.dirty(false);\n\n return this;\n },\n\n delFromStorage: function (el) {\n if (el) {\n el.__storage = null;\n }\n\n return this;\n },\n\n /**\n * 清空并且释放Storage\n */\n dispose: function () {\n this._renderList =\n this._roots = null;\n },\n\n displayableSortFunc: shapeCompareFunc\n};\n\nexport default Storage;","\nvar STYLE_COMMON_PROPS = [\n ['shadowBlur', 0], ['shadowOffsetX', 0], ['shadowOffsetY', 0], ['shadowColor', '#000'],\n ['lineCap', 'butt'], ['lineJoin', 'miter'], ['miterLimit', 10]\n];\n\n// var SHADOW_PROPS = STYLE_COMMON_PROPS.slice(0, 4);\n// var LINE_PROPS = STYLE_COMMON_PROPS.slice(4);\n\nvar Style = function (opts, host) {\n this.extendFrom(opts, false);\n this.host = host;\n};\n\nfunction createLinearGradient(ctx, obj, rect) {\n var x = obj.x == null ? 0 : obj.x;\n var x2 = obj.x2 == null ? 1 : obj.x2;\n var y = obj.y == null ? 0 : obj.y;\n var y2 = obj.y2 == null ? 0 : obj.y2;\n\n if (!obj.global) {\n x = x * rect.width + rect.x;\n x2 = x2 * rect.width + rect.x;\n y = y * rect.height + rect.y;\n y2 = y2 * rect.height + rect.y;\n }\n\n var canvasGradient = ctx.createLinearGradient(x, y, x2, y2);\n\n return canvasGradient;\n}\n\nfunction createRadialGradient(ctx, obj, rect) {\n var width = rect.width;\n var height = rect.height;\n var min = Math.min(width, height);\n\n var x = obj.x == null ? 0.5 : obj.x;\n var y = obj.y == null ? 0.5 : obj.y;\n var r = obj.r == null ? 0.5 : obj.r;\n if (!obj.global) {\n x = x * width + rect.x;\n y = y * height + rect.y;\n r = r * min;\n }\n\n var canvasGradient = ctx.createRadialGradient(x, y, 0, x, y, r);\n\n return canvasGradient;\n}\n\n\nStyle.prototype = {\n\n constructor: Style,\n\n /**\n * @type {module:zrender/graphic/Displayable}\n */\n host: null,\n\n /**\n * @type {string}\n */\n fill: '#000',\n\n /**\n * @type {string}\n */\n stroke: null,\n\n /**\n * @type {number}\n */\n opacity: 1,\n\n /**\n * @type {Array.<number>}\n */\n lineDash: null,\n\n /**\n * @type {number}\n */\n lineDashOffset: 0,\n\n /**\n * @type {number}\n */\n shadowBlur: 0,\n\n /**\n * @type {number}\n */\n shadowOffsetX: 0,\n\n /**\n * @type {number}\n */\n shadowOffsetY: 0,\n\n /**\n * @type {number}\n */\n lineWidth: 1,\n\n /**\n * If stroke ignore scale\n * @type {Boolean}\n */\n strokeNoScale: false,\n\n // Bounding rect text configuration\n // Not affected by element transform\n /**\n * @type {string}\n */\n text: null,\n\n /**\n * If `fontSize` or `fontFamily` exists, `font` will be reset by\n * `fontSize`, `fontStyle`, `fontWeight`, `fontFamily`.\n * So do not visit it directly in upper application (like echarts),\n * but use `contain/text#makeFont` instead.\n * @type {string}\n */\n font: null,\n\n /**\n * The same as font. Use font please.\n * @deprecated\n * @type {string}\n */\n textFont: null,\n\n /**\n * It helps merging respectively, rather than parsing an entire font string.\n * @type {string}\n */\n fontStyle: null,\n\n /**\n * It helps merging respectively, rather than parsing an entire font string.\n * @type {string}\n */\n fontWeight: null,\n\n /**\n * It helps merging respectively, rather than parsing an entire font string.\n * Should be 12 but not '12px'.\n * @type {number}\n */\n fontSize: null,\n\n /**\n * It helps merging respectively, rather than parsing an entire font string.\n * @type {string}\n */\n fontFamily: null,\n\n /**\n * Reserved for special functinality, like 'hr'.\n * @type {string}\n */\n textTag: null,\n\n /**\n * @type {string}\n */\n textFill: '#000',\n\n /**\n * @type {string}\n */\n textStroke: null,\n\n /**\n * @type {number}\n */\n textWidth: null,\n\n /**\n * Only for textBackground.\n * @type {number}\n */\n textHeight: null,\n\n /**\n * textStroke may be set as some color as a default\n * value in upper applicaion, where the default value\n * of textStrokeWidth should be 0 to make sure that\n * user can choose to do not use text stroke.\n * @type {number}\n */\n textStrokeWidth: 0,\n\n /**\n * @type {number}\n */\n textLineHeight: null,\n\n /**\n * 'inside', 'left', 'right', 'top', 'bottom'\n * [x, y]\n * Based on x, y of rect.\n * @type {string|Array.<number>}\n * @default 'inside'\n */\n textPosition: 'inside',\n\n /**\n * If not specified, use the boundingRect of a `displayable`.\n * @type {Object}\n */\n textRect: null,\n\n /**\n * [x, y]\n * @type {Array.<number>}\n */\n textOffset: null,\n\n /**\n * @type {string}\n */\n textAlign: null,\n\n /**\n * @type {string}\n */\n textVerticalAlign: null,\n\n /**\n * @type {number}\n */\n textDistance: 5,\n\n /**\n * @type {string}\n */\n textShadowColor: 'transparent',\n\n /**\n * @type {number}\n */\n textShadowBlur: 0,\n\n /**\n * @type {number}\n */\n textShadowOffsetX: 0,\n\n /**\n * @type {number}\n */\n textShadowOffsetY: 0,\n\n /**\n * @type {string}\n */\n textBoxShadowColor: 'transparent',\n\n /**\n * @type {number}\n */\n textBoxShadowBlur: 0,\n\n /**\n * @type {number}\n */\n textBoxShadowOffsetX: 0,\n\n /**\n * @type {number}\n */\n textBoxShadowOffsetY: 0,\n\n /**\n * Whether transform text.\n * Only useful in Path and Image element\n * @type {boolean}\n */\n transformText: false,\n\n /**\n * Text rotate around position of Path or Image\n * Only useful in Path and Image element and transformText is false.\n */\n textRotation: 0,\n\n /**\n * Text origin of text rotation, like [10, 40].\n * Based on x, y of rect.\n * Useful in label rotation of circular symbol.\n * By default, this origin is textPosition.\n * Can be 'center'.\n * @type {string|Array.<number>}\n */\n textOrigin: null,\n\n /**\n * @type {string}\n */\n textBackgroundColor: null,\n\n /**\n * @type {string}\n */\n textBorderColor: null,\n\n /**\n * @type {number}\n */\n textBorderWidth: 0,\n\n /**\n * @type {number}\n */\n textBorderRadius: 0,\n\n /**\n * Can be `2` or `[2, 4]` or `[2, 3, 4, 5]`\n * @type {number|Array.<number>}\n */\n textPadding: null,\n\n /**\n * Text styles for rich text.\n * @type {Object}\n */\n rich: null,\n\n /**\n * {outerWidth, outerHeight, ellipsis, placeholder}\n * @type {Object}\n */\n truncate: null,\n\n /**\n * https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation\n * @type {string}\n */\n blend: null,\n\n /**\n * @param {CanvasRenderingContext2D} ctx\n */\n bind: function (ctx, el, prevEl) {\n var style = this;\n var prevStyle = prevEl && prevEl.style;\n var firstDraw = !prevStyle;\n\n for (var i = 0; i < STYLE_COMMON_PROPS.length; i++) {\n var prop = STYLE_COMMON_PROPS[i];\n var styleName = prop[0];\n\n if (firstDraw || style[styleName] !== prevStyle[styleName]) {\n // FIXME Invalid property value will cause style leak from previous element.\n ctx[styleName] = style[styleName] || prop[1];\n }\n }\n\n if ((firstDraw || style.fill !== prevStyle.fill)) {\n ctx.fillStyle = style.fill;\n }\n if ((firstDraw || style.stroke !== prevStyle.stroke)) {\n ctx.strokeStyle = style.stroke;\n }\n if ((firstDraw || style.opacity !== prevStyle.opacity)) {\n ctx.globalAlpha = style.opacity == null ? 1 : style.opacity;\n }\n\n if ((firstDraw || style.blend !== prevStyle.blend)) {\n ctx.globalCompositeOperation = style.blend || 'source-over';\n }\n if (this.hasStroke()) {\n var lineWidth = style.lineWidth;\n ctx.lineWidth = lineWidth / (\n (this.strokeNoScale && el && el.getLineScale) ? el.getLineScale() : 1\n );\n }\n },\n\n hasFill: function () {\n var fill = this.fill;\n return fill != null && fill !== 'none';\n },\n\n hasStroke: function () {\n var stroke = this.stroke;\n return stroke != null && stroke !== 'none' && this.lineWidth > 0;\n },\n\n /**\n * Extend from other style\n * @param {zrender/graphic/Style} otherStyle\n * @param {boolean} overwrite true: overwrirte any way.\n * false: overwrite only when !target.hasOwnProperty\n * others: overwrite when property is not null/undefined.\n */\n extendFrom: function (otherStyle, overwrite) {\n if (otherStyle) {\n for (var name in otherStyle) {\n if (otherStyle.hasOwnProperty(name)\n && (overwrite === true\n || (\n overwrite === false\n ? !this.hasOwnProperty(name)\n : otherStyle[name] != null\n )\n )\n ) {\n this[name] = otherStyle[name];\n }\n }\n }\n },\n\n /**\n * Batch setting style with a given object\n * @param {Object|string} obj\n * @param {*} [obj]\n */\n set: function (obj, value) {\n if (typeof obj === 'string') {\n this[obj] = value;\n }\n else {\n this.extendFrom(obj, true);\n }\n },\n\n /**\n * Clone\n * @return {zrender/graphic/Style} [description]\n */\n clone: function () {\n var newStyle = new this.constructor();\n newStyle.extendFrom(this, true);\n return newStyle;\n },\n\n getGradient: function (ctx, obj, rect) {\n var method = obj.type === 'radial' ? createRadialGradient : createLinearGradient;\n var canvasGradient = method(ctx, obj, rect);\n var colorStops = obj.colorStops;\n for (var i = 0; i < colorStops.length; i++) {\n canvasGradient.addColorStop(\n colorStops[i].offset, colorStops[i].color\n );\n }\n return canvasGradient;\n }\n\n};\n\nvar styleProto = Style.prototype;\nfor (var i = 0; i < STYLE_COMMON_PROPS.length; i++) {\n var prop = STYLE_COMMON_PROPS[i];\n if (!(prop[0] in styleProto)) {\n styleProto[prop[0]] = prop[1];\n }\n}\n\n// Provide for others\nStyle.getGradient = styleProto.getGradient;\n\nexport default Style;","\nvar Pattern = function (image, repeat) {\n // Should do nothing more in this constructor. Because gradient can be\n // declard by `color: {image: ...}`, where this constructor will not be called.\n\n this.image = image;\n this.repeat = repeat;\n\n // Can be cloned\n this.type = 'pattern';\n};\n\nPattern.prototype.getCanvasPattern = function (ctx) {\n return ctx.createPattern(this.image, this.repeat || 'repeat');\n};\n\nexport default Pattern;","/**\n * @module zrender/Layer\n * @author pissang(https://www.github.com/pissang)\n */\n\nimport * as util from './core/util';\nimport {devicePixelRatio} from './config';\nimport Style from './graphic/Style';\nimport Pattern from './graphic/Pattern';\n\nfunction returnFalse() {\n return false;\n}\n\n/**\n * 创建dom\n *\n * @inner\n * @param {string} id dom id 待用\n * @param {Painter} painter painter instance\n * @param {number} number\n */\nfunction createDom(id, painter, dpr) {\n var newDom = util.createCanvas();\n var width = painter.getWidth();\n var height = painter.getHeight();\n\n var newDomStyle = newDom.style;\n // 没append呢,请原谅我这样写,清晰~\n newDomStyle.position = 'absolute';\n newDomStyle.left = 0;\n newDomStyle.top = 0;\n newDomStyle.width = width + 'px';\n newDomStyle.height = height + 'px';\n newDom.width = width * dpr;\n newDom.height = height * dpr;\n\n // id不作为索引用,避免可能造成的重名,定义为私有属性\n newDom.setAttribute('data-zr-dom-id', id);\n return newDom;\n}\n\n/**\n * @alias module:zrender/Layer\n * @constructor\n * @extends module:zrender/mixin/Transformable\n * @param {string} id\n * @param {module:zrender/Painter} painter\n * @param {number} [dpr]\n */\nvar Layer = function(id, painter, dpr) {\n var dom;\n dpr = dpr || devicePixelRatio;\n if (typeof id === 'string') {\n dom = createDom(id, painter, dpr);\n }\n // Not using isDom because in node it will return false\n else if (util.isObject(id)) {\n dom = id;\n id = dom.id;\n }\n this.id = id;\n this.dom = dom;\n\n var domStyle = dom.style;\n if (domStyle) { // Not in node\n dom.onselectstart = returnFalse; // 避免页面选中的尴尬\n domStyle['-webkit-user-select'] = 'none';\n domStyle['user-select'] = 'none';\n domStyle['-webkit-touch-callout'] = 'none';\n domStyle['-webkit-tap-highlight-color'] = 'rgba(0,0,0,0)';\n domStyle['padding'] = 0;\n domStyle['margin'] = 0;\n domStyle['border-width'] = 0;\n }\n\n this.domBack = null;\n this.ctxBack = null;\n\n this.painter = painter;\n\n this.config = null;\n\n // Configs\n /**\n * 每次清空画布的颜色\n * @type {string}\n * @default 0\n */\n this.clearColor = 0;\n /**\n * 是否开启动态模糊\n * @type {boolean}\n * @default false\n */\n this.motionBlur = false;\n /**\n * 在开启动态模糊的时候使用,与上一帧混合的alpha值,值越大尾迹越明显\n * @type {number}\n * @default 0.7\n */\n this.lastFrameAlpha = 0.7;\n\n /**\n * Layer dpr\n * @type {number}\n */\n this.dpr = dpr;\n};\n\nLayer.prototype = {\n\n constructor: Layer,\n\n elCount: 0,\n\n __dirty: true,\n\n initContext: function () {\n this.ctx = this.dom.getContext('2d');\n this.ctx.__currentValues = {};\n this.ctx.dpr = this.dpr;\n },\n\n createBackBuffer: function () {\n var dpr = this.dpr;\n\n this.domBack = createDom('back-' + this.id, this.painter, dpr);\n this.ctxBack = this.domBack.getContext('2d');\n this.ctxBack.__currentValues = {};\n\n if (dpr != 1) {\n this.ctxBack.scale(dpr, dpr);\n }\n },\n\n /**\n * @param {number} width\n * @param {number} height\n */\n resize: function (width, height) {\n var dpr = this.dpr;\n\n var dom = this.dom;\n var domStyle = dom.style;\n var domBack = this.domBack;\n\n domStyle.width = width + 'px';\n domStyle.height = height + 'px';\n\n dom.width = width * dpr;\n dom.height = height * dpr;\n\n if (domBack) {\n domBack.width = width * dpr;\n domBack.height = height * dpr;\n\n if (dpr != 1) {\n this.ctxBack.scale(dpr, dpr);\n }\n }\n },\n\n /**\n * 清空该层画布\n * @param {boolean} clearAll Clear all with out motion blur\n */\n clear: function (clearAll) {\n var dom = this.dom;\n var ctx = this.ctx;\n var width = dom.width;\n var height = dom.height;\n\n var clearColor = this.clearColor;\n var haveMotionBLur = this.motionBlur && !clearAll;\n var lastFrameAlpha = this.lastFrameAlpha;\n\n var dpr = this.dpr;\n\n if (haveMotionBLur) {\n if (!this.domBack) {\n this.createBackBuffer();\n }\n\n this.ctxBack.globalCompositeOperation = 'copy';\n this.ctxBack.drawImage(\n dom, 0, 0,\n width / dpr,\n height / dpr\n );\n }\n\n ctx.clearRect(0, 0, width, height);\n if (clearColor) {\n var clearColorGradientOrPattern;\n // Gradient\n if (clearColor.colorStops) {\n // Cache canvas gradient\n clearColorGradientOrPattern = clearColor.__canvasGradient || Style.getGradient(ctx, clearColor, {\n x: 0,\n y: 0,\n width: width,\n height: height\n });\n\n clearColor.__canvasGradient = clearColorGradientOrPattern;\n }\n // Pattern\n else if (clearColor.image) {\n clearColorGradientOrPattern = Pattern.prototype.getCanvasPattern.call(clearColor, ctx);\n }\n ctx.save();\n ctx.fillStyle = clearColorGradientOrPattern || clearColor;\n ctx.fillRect(0, 0, width, height);\n ctx.restore();\n }\n\n if (haveMotionBLur) {\n var domBack = this.domBack;\n ctx.save();\n ctx.globalAlpha = lastFrameAlpha;\n ctx.drawImage(domBack, 0, 0, width, height);\n ctx.restore();\n }\n }\n};\n\nexport default Layer;","\nexport default (\n typeof window !== 'undefined'\n && (\n (window.requestAnimationFrame && window.requestAnimationFrame.bind(window))\n // https://github.com/ecomfe/zrender/issues/189#issuecomment-224919809\n || (window.msRequestAnimationFrame && window.msRequestAnimationFrame.bind(window))\n || window.mozRequestAnimationFrame\n || window.webkitRequestAnimationFrame\n )\n) || function (func) {\n setTimeout(func, 16);\n};","\nimport LRU from '../../core/LRU';\n\nvar globalImageCache = new LRU(50);\n\n/**\n * @param {string|HTMLImageElement|HTMLCanvasElement|Canvas} newImageOrSrc\n * @return {HTMLImageElement|HTMLCanvasElement|Canvas} image\n */\nexport function findExistImage(newImageOrSrc) {\n if (typeof newImageOrSrc === 'string') {\n var cachedImgObj = globalImageCache.get(newImageOrSrc);\n return cachedImgObj && cachedImgObj.image;\n }\n else {\n return newImageOrSrc;\n }\n}\n\n/**\n * Caution: User should cache loaded images, but not just count on LRU.\n * Consider if required images more than LRU size, will dead loop occur?\n *\n * @param {string|HTMLImageElement|HTMLCanvasElement|Canvas} newImageOrSrc\n * @param {HTMLImageElement|HTMLCanvasElement|Canvas} image Existent image.\n * @param {module:zrender/Element} [hostEl] For calling `dirty`.\n * @param {Function} [cb] params: (image, cbPayload)\n * @param {Object} [cbPayload] Payload on cb calling.\n * @return {HTMLImageElement|HTMLCanvasElement|Canvas} image\n */\nexport function createOrUpdateImage(newImageOrSrc, image, hostEl, cb, cbPayload) {\n if (!newImageOrSrc) {\n return image;\n }\n else if (typeof newImageOrSrc === 'string') {\n\n // Image should not be loaded repeatly.\n if ((image && image.__zrImageSrc === newImageOrSrc) || !hostEl) {\n return image;\n }\n\n // Only when there is no existent image or existent image src\n // is different, this method is responsible for load.\n var cachedImgObj = globalImageCache.get(newImageOrSrc);\n\n var pendingWrap = {hostEl: hostEl, cb: cb, cbPayload: cbPayload};\n\n if (cachedImgObj) {\n image = cachedImgObj.image;\n !isImageReady(image) && cachedImgObj.pending.push(pendingWrap);\n }\n else {\n !image && (image = new Image());\n image.onload = imageOnLoad;\n\n globalImageCache.put(\n newImageOrSrc,\n image.__cachedImgObj = {\n image: image,\n pending: [pendingWrap]\n }\n );\n\n image.src = image.__zrImageSrc = newImageOrSrc;\n }\n\n return image;\n }\n // newImageOrSrc is an HTMLImageElement or HTMLCanvasElement or Canvas\n else {\n return newImageOrSrc;\n }\n}\n\nfunction imageOnLoad() {\n var cachedImgObj = this.__cachedImgObj;\n this.onload = this.__cachedImgObj = null;\n\n for (var i = 0; i < cachedImgObj.pending.length; i++) {\n var pendingWrap = cachedImgObj.pending[i];\n var cb = pendingWrap.cb;\n cb && cb(this, pendingWrap.cbPayload);\n pendingWrap.hostEl.dirty();\n }\n cachedImgObj.pending.length = 0;\n}\n\nexport function isImageReady(image) {\n return image && image.width && image.height;\n}\n\n","import BoundingRect from '../core/BoundingRect';\nimport * as imageHelper from '../graphic/helper/image';\nimport {\n getContext,\n extend,\n retrieve2,\n retrieve3\n} from '../core/util';\n\nvar textWidthCache = {};\nvar textWidthCacheCounter = 0;\n\nvar TEXT_CACHE_MAX = 5000;\nvar STYLE_REG = /\\{([a-zA-Z0-9_]+)\\|([^}]*)\\}/g;\n\nexport var DEFAULT_FONT = '12px sans-serif';\n\n// Avoid assign to an exported variable, for transforming to cjs.\nvar methods = {};\n\nexport function $override(name, fn) {\n methods[name] = fn;\n}\n\n/**\n * @public\n * @param {string} text\n * @param {string} font\n * @return {number} width\n */\nexport function getWidth(text, font) {\n font = font || DEFAULT_FONT;\n var key = text + ':' + font;\n if (textWidthCache[key]) {\n return textWidthCache[key];\n }\n\n var textLines = (text + '').split('\\n');\n var width = 0;\n\n for (var i = 0, l = textLines.length; i < l; i++) {\n // textContain.measureText may be overrided in SVG or VML\n width = Math.max(measureText(textLines[i], font).width, width);\n }\n\n if (textWidthCacheCounter > TEXT_CACHE_MAX) {\n textWidthCacheCounter = 0;\n textWidthCache = {};\n }\n textWidthCacheCounter++;\n textWidthCache[key] = width;\n\n return width;\n}\n\n/**\n * @public\n * @param {string} text\n * @param {string} font\n * @param {string} [textAlign='left']\n * @param {string} [textVerticalAlign='top']\n * @param {Array.<number>} [textPadding]\n * @param {Object} [rich]\n * @param {Object} [truncate]\n * @return {Object} {x, y, width, height, lineHeight}\n */\nexport function getBoundingRect(text, font, textAlign, textVerticalAlign, textPadding, rich, truncate) {\n return rich\n ? getRichTextRect(text, font, textAlign, textVerticalAlign, textPadding, rich, truncate)\n : getPlainTextRect(text, font, textAlign, textVerticalAlign, textPadding, truncate);\n}\n\nfunction getPlainTextRect(text, font, textAlign, textVerticalAlign, textPadding, truncate) {\n var contentBlock = parsePlainText(text, font, textPadding, truncate);\n var outerWidth = getWidth(text, font);\n if (textPadding) {\n outerWidth += textPadding[1] + textPadding[3];\n }\n var outerHeight = contentBlock.outerHeight;\n\n var x = adjustTextX(0, outerWidth, textAlign);\n var y = adjustTextY(0, outerHeight, textVerticalAlign);\n\n var rect = new BoundingRect(x, y, outerWidth, outerHeight);\n rect.lineHeight = contentBlock.lineHeight;\n\n return rect;\n}\n\nfunction getRichTextRect(text, font, textAlign, textVerticalAlign, textPadding, rich, truncate) {\n var contentBlock = parseRichText(text, {\n rich: rich,\n truncate: truncate,\n font: font,\n textAlign: textAlign,\n textPadding: textPadding\n });\n var outerWidth = contentBlock.outerWidth;\n var outerHeight = contentBlock.outerHeight;\n\n var x = adjustTextX(0, outerWidth, textAlign);\n var y = adjustTextY(0, outerHeight, textVerticalAlign);\n\n return new BoundingRect(x, y, outerWidth, outerHeight);\n}\n\n/**\n * @public\n * @param {number} x\n * @param {number} width\n * @param {string} [textAlign='left']\n * @return {number} Adjusted x.\n */\nexport function adjustTextX(x, width, textAlign) {\n // FIXME Right to left language\n if (textAlign === 'right') {\n x -= width;\n }\n else if (textAlign === 'center') {\n x -= width / 2;\n }\n return x;\n}\n\n/**\n * @public\n * @param {number} y\n * @param {number} height\n * @param {string} [textVerticalAlign='top']\n * @return {number} Adjusted y.\n */\nexport function adjustTextY(y, height, textVerticalAlign) {\n if (textVerticalAlign === 'middle') {\n y -= height / 2;\n }\n else if (textVerticalAlign === 'bottom') {\n y -= height;\n }\n return y;\n}\n\n/**\n * @public\n * @param {stirng} textPosition\n * @param {Object} rect {x, y, width, height}\n * @param {number} distance\n * @return {Object} {x, y, textAlign, textVerticalAlign}\n */\nexport function adjustTextPositionOnRect(textPosition, rect, distance) {\n\n var x = rect.x;\n var y = rect.y;\n\n var height = rect.height;\n var width = rect.width;\n var halfHeight = height / 2;\n\n var textAlign = 'left';\n var textVerticalAlign = 'top';\n\n switch (textPosition) {\n case 'left':\n x -= distance;\n y += halfHeight;\n textAlign = 'right';\n textVerticalAlign = 'middle';\n break;\n case 'right':\n x += distance + width;\n y += halfHeight;\n textVerticalAlign = 'middle';\n break;\n case 'top':\n x += width / 2;\n y -= distance;\n textAlign = 'center';\n textVerticalAlign = 'bottom';\n break;\n case 'bottom':\n x += width / 2;\n y += height + distance;\n textAlign = 'center';\n break;\n case 'inside':\n x += width / 2;\n y += halfHeight;\n textAlign = 'center';\n textVerticalAlign = 'middle';\n break;\n case 'insideLeft':\n x += distance;\n y += halfHeight;\n textVerticalAlign = 'middle';\n break;\n case 'insideRight':\n x += width - distance;\n y += halfHeight;\n textAlign = 'right';\n textVerticalAlign = 'middle';\n break;\n case 'insideTop':\n x += width / 2;\n y += distance;\n textAlign = 'center';\n break;\n case 'insideBottom':\n x += width / 2;\n y += height - distance;\n textAlign = 'center';\n textVerticalAlign = 'bottom';\n break;\n case 'insideTopLeft':\n x += distance;\n y += distance;\n break;\n case 'insideTopRight':\n x += width - distance;\n y += distance;\n textAlign = 'right';\n break;\n case 'insideBottomLeft':\n x += distance;\n y += height - distance;\n textVerticalAlign = 'bottom';\n break;\n case 'insideBottomRight':\n x += width - distance;\n y += height - distance;\n textAlign = 'right';\n textVerticalAlign = 'bottom';\n break;\n }\n\n return {\n x: x,\n y: y,\n textAlign: textAlign,\n textVerticalAlign: textVerticalAlign\n };\n}\n\n/**\n * Show ellipsis if overflow.\n *\n * @public\n * @param {string} text\n * @param {string} containerWidth\n * @param {string} font\n * @param {number} [ellipsis='...']\n * @param {Object} [options]\n * @param {number} [options.maxIterations=3]\n * @param {number} [options.minChar=0] If truncate result are less\n * then minChar, ellipsis will not show, which is\n * better for user hint in some cases.\n * @param {number} [options.placeholder=''] When all truncated, use the placeholder.\n * @return {string}\n */\nexport function truncateText(text, containerWidth, font, ellipsis, options) {\n if (!containerWidth) {\n return '';\n }\n\n var textLines = (text + '').split('\\n');\n options = prepareTruncateOptions(containerWidth, font, ellipsis, options);\n\n // FIXME\n // It is not appropriate that every line has '...' when truncate multiple lines.\n for (var i = 0, len = textLines.length; i < len; i++) {\n textLines[i] = truncateSingleLine(textLines[i], options);\n }\n\n return textLines.join('\\n');\n}\n\nfunction prepareTruncateOptions(containerWidth, font, ellipsis, options) {\n options = extend({}, options);\n\n options.font = font;\n var ellipsis = retrieve2(ellipsis, '...');\n options.maxIterations = retrieve2(options.maxIterations, 2);\n var minChar = options.minChar = retrieve2(options.minChar, 0);\n // FIXME\n // Other languages?\n options.cnCharWidth = getWidth('国', font);\n // FIXME\n // Consider proportional font?\n var ascCharWidth = options.ascCharWidth = getWidth('a', font);\n options.placeholder = retrieve2(options.placeholder, '');\n\n // Example 1: minChar: 3, text: 'asdfzxcv', truncate result: 'asdf', but not: 'a...'.\n // Example 2: minChar: 3, text: '维度', truncate result: '维', but not: '...'.\n var contentWidth = containerWidth = Math.max(0, containerWidth - 1); // Reserve some gap.\n for (var i = 0; i < minChar && contentWidth >= ascCharWidth; i++) {\n contentWidth -= ascCharWidth;\n }\n\n var ellipsisWidth = getWidth(ellipsis);\n if (ellipsisWidth > contentWidth) {\n ellipsis = '';\n ellipsisWidth = 0;\n }\n\n contentWidth = containerWidth - ellipsisWidth;\n\n options.ellipsis = ellipsis;\n options.ellipsisWidth = ellipsisWidth;\n options.contentWidth = contentWidth;\n options.containerWidth = containerWidth;\n\n return options;\n}\n\nfunction truncateSingleLine(textLine, options) {\n var containerWidth = options.containerWidth;\n var font = options.font;\n var contentWidth = options.contentWidth;\n\n if (!containerWidth) {\n return '';\n }\n\n var lineWidth = getWidth(textLine, font);\n\n if (lineWidth <= containerWidth) {\n return textLine;\n }\n\n for (var j = 0;; j++) {\n if (lineWidth <= contentWidth || j >= options.maxIterations) {\n textLine += options.ellipsis;\n break;\n }\n\n var subLength = j === 0\n ? estimateLength(textLine, contentWidth, options.ascCharWidth, options.cnCharWidth)\n : lineWidth > 0\n ? Math.floor(textLine.length * contentWidth / lineWidth)\n : 0;\n\n textLine = textLine.substr(0, subLength);\n lineWidth = getWidth(textLine, font);\n }\n\n if (textLine === '') {\n textLine = options.placeholder;\n }\n\n return textLine;\n}\n\nfunction estimateLength(text, contentWidth, ascCharWidth, cnCharWidth) {\n var width = 0;\n var i = 0;\n for (var len = text.length; i < len && width < contentWidth; i++) {\n var charCode = text.charCodeAt(i);\n width += (0 <= charCode && charCode <= 127) ? ascCharWidth : cnCharWidth;\n }\n return i;\n}\n\n/**\n * @public\n * @param {string} font\n * @return {number} line height\n */\nexport function getLineHeight(font) {\n // FIXME A rough approach.\n return getWidth('国', font);\n}\n\n/**\n * @public\n * @param {string} text\n * @param {string} font\n * @return {Object} width\n */\nexport function measureText(text, font) {\n return methods.measureText(text, font);\n}\n\n// Avoid assign to an exported variable, for transforming to cjs.\nmethods.measureText = function (text, font) {\n var ctx = getContext();\n ctx.font = font || DEFAULT_FONT;\n return ctx.measureText(text);\n};\n\n/**\n * @public\n * @param {string} text\n * @param {string} font\n * @param {Object} [truncate]\n * @return {Object} block: {lineHeight, lines, height, outerHeight}\n * Notice: for performance, do not calculate outerWidth util needed.\n */\nexport function parsePlainText(text, font, padding, truncate) {\n text != null && (text += '');\n\n var lineHeight = getLineHeight(font);\n var lines = text ? text.split('\\n') : [];\n var height = lines.length * lineHeight;\n var outerHeight = height;\n\n if (padding) {\n outerHeight += padding[0] + padding[2];\n }\n\n if (text && truncate) {\n var truncOuterHeight = truncate.outerHeight;\n var truncOuterWidth = truncate.outerWidth;\n if (truncOuterHeight != null && outerHeight > truncOuterHeight) {\n text = '';\n lines = [];\n }\n else if (truncOuterWidth != null) {\n var options = prepareTruncateOptions(\n truncOuterWidth - (padding ? padding[1] + padding[3] : 0),\n font,\n truncate.ellipsis,\n {minChar: truncate.minChar, placeholder: truncate.placeholder}\n );\n\n // FIXME\n // It is not appropriate that every line has '...' when truncate multiple lines.\n for (var i = 0, len = lines.length; i < len; i++) {\n lines[i] = truncateSingleLine(lines[i], options);\n }\n }\n }\n\n return {\n lines: lines,\n height: height,\n outerHeight: outerHeight,\n lineHeight: lineHeight\n };\n}\n\n/**\n * For example: 'some text {a|some text}other text{b|some text}xxx{c|}xxx'\n * Also consider 'bbbb{a|xxx\\nzzz}xxxx\\naaaa'.\n *\n * @public\n * @param {string} text\n * @param {Object} style\n * @return {Object} block\n * {\n * width,\n * height,\n * lines: [{\n * lineHeight,\n * width,\n * tokens: [[{\n * styleName,\n * text,\n * width, // include textPadding\n * height, // include textPadding\n * textWidth, // pure text width\n * textHeight, // pure text height\n * lineHeihgt,\n * font,\n * textAlign,\n * textVerticalAlign\n * }], [...], ...]\n * }, ...]\n * }\n * If styleName is undefined, it is plain text.\n */\nexport function parseRichText(text, style) {\n var contentBlock = {lines: [], width: 0, height: 0};\n\n text != null && (text += '');\n if (!text) {\n return contentBlock;\n }\n\n var lastIndex = STYLE_REG.lastIndex = 0;\n var result;\n while ((result = STYLE_REG.exec(text)) != null) {\n var matchedIndex = result.index;\n if (matchedIndex > lastIndex) {\n pushTokens(contentBlock, text.substring(lastIndex, matchedIndex));\n }\n pushTokens(contentBlock, result[2], result[1]);\n lastIndex = STYLE_REG.lastIndex;\n }\n\n if (lastIndex < text.length) {\n pushTokens(contentBlock, text.substring(lastIndex, text.length));\n }\n\n var lines = contentBlock.lines;\n var contentHeight = 0;\n var contentWidth = 0;\n // For `textWidth: 100%`\n var pendingList = [];\n\n var stlPadding = style.textPadding;\n\n var truncate = style.truncate;\n var truncateWidth = truncate && truncate.outerWidth;\n var truncateHeight = truncate && truncate.outerHeight;\n if (stlPadding) {\n truncateWidth != null && (truncateWidth -= stlPadding[1] + stlPadding[3]);\n truncateHeight != null && (truncateHeight -= stlPadding[0] + stlPadding[2]);\n }\n\n // Calculate layout info of tokens.\n for (var i = 0; i < lines.length; i++) {\n var line = lines[i];\n var lineHeight = 0;\n var lineWidth = 0;\n\n for (var j = 0; j < line.tokens.length; j++) {\n var token = line.tokens[j];\n var tokenStyle = token.styleName && style.rich[token.styleName] || {};\n // textPadding should not inherit from style.\n var textPadding = token.textPadding = tokenStyle.textPadding;\n\n // textFont has been asigned to font by `normalizeStyle`.\n var font = token.font = tokenStyle.font || style.font;\n\n // textHeight can be used when textVerticalAlign is specified in token.\n var tokenHeight = token.textHeight = retrieve2(\n // textHeight should not be inherited, consider it can be specified\n // as box height of the block.\n tokenStyle.textHeight, getLineHeight(font)\n );\n textPadding && (tokenHeight += textPadding[0] + textPadding[2]);\n token.height = tokenHeight;\n token.lineHeight = retrieve3(\n tokenStyle.textLineHeight, style.textLineHeight, tokenHeight\n );\n\n token.textAlign = tokenStyle && tokenStyle.textAlign || style.textAlign;\n token.textVerticalAlign = tokenStyle && tokenStyle.textVerticalAlign || 'middle';\n\n if (truncateHeight != null && contentHeight + token.lineHeight > truncateHeight) {\n return {lines: [], width: 0, height: 0};\n }\n\n token.textWidth = getWidth(token.text, font);\n var tokenWidth = tokenStyle.textWidth;\n var tokenWidthNotSpecified = tokenWidth == null || tokenWidth === 'auto';\n\n // Percent width, can be `100%`, can be used in drawing separate\n // line when box width is needed to be auto.\n if (typeof tokenWidth === 'string' && tokenWidth.charAt(tokenWidth.length - 1) === '%') {\n token.percentWidth = tokenWidth;\n pendingList.push(token);\n tokenWidth = 0;\n // Do not truncate in this case, because there is no user case\n // and it is too complicated.\n }\n else {\n if (tokenWidthNotSpecified) {\n tokenWidth = token.textWidth;\n\n // FIXME: If image is not loaded and textWidth is not specified, calling\n // `getBoundingRect()` will not get correct result.\n var textBackgroundColor = tokenStyle.textBackgroundColor;\n var bgImg = textBackgroundColor && textBackgroundColor.image;\n\n // Use cases:\n // (1) If image is not loaded, it will be loaded at render phase and call\n // `dirty()` and `textBackgroundColor.image` will be replaced with the loaded\n // image, and then the right size will be calculated here at the next tick.\n // See `graphic/helper/text.js`.\n // (2) If image loaded, and `textBackgroundColor.image` is image src string,\n // use `imageHelper.findExistImage` to find cached image.\n // `imageHelper.findExistImage` will always be called here before\n // `imageHelper.createOrUpdateImage` in `graphic/helper/text.js#renderRichText`\n // which ensures that image will not be rendered before correct size calcualted.\n if (bgImg) {\n bgImg = imageHelper.findExistImage(bgImg);\n if (imageHelper.isImageReady(bgImg)) {\n tokenWidth = Math.max(tokenWidth, bgImg.width * tokenHeight / bgImg.height);\n }\n }\n }\n\n var paddingW = textPadding ? textPadding[1] + textPadding[3] : 0;\n tokenWidth += paddingW;\n\n var remianTruncWidth = truncateWidth != null ? truncateWidth - lineWidth : null;\n\n if (remianTruncWidth != null && remianTruncWidth < tokenWidth) {\n if (!tokenWidthNotSpecified || remianTruncWidth < paddingW) {\n token.text = '';\n token.textWidth = tokenWidth = 0;\n }\n else {\n token.text = truncateText(\n token.text, remianTruncWidth - paddingW, font, truncate.ellipsis,\n {minChar: truncate.minChar}\n );\n token.textWidth = getWidth(token.text, font);\n tokenWidth = token.textWidth + paddingW;\n }\n }\n }\n\n lineWidth += (token.width = tokenWidth);\n tokenStyle && (lineHeight = Math.max(lineHeight, token.lineHeight));\n }\n\n line.width = lineWidth;\n line.lineHeight = lineHeight;\n contentHeight += lineHeight;\n contentWidth = Math.max(contentWidth, lineWidth);\n }\n\n contentBlock.outerWidth = contentBlock.width = retrieve2(style.textWidth, contentWidth);\n contentBlock.outerHeight = contentBlock.height = retrieve2(style.textHeight, contentHeight);\n\n if (stlPadding) {\n contentBlock.outerWidth += stlPadding[1] + stlPadding[3];\n contentBlock.outerHeight += stlPadding[0] + stlPadding[2];\n }\n\n for (var i = 0; i < pendingList.length; i++) {\n var token = pendingList[i];\n var percentWidth = token.percentWidth;\n // Should not base on outerWidth, because token can not be placed out of padding.\n token.width = parseInt(percentWidth, 10) / 100 * contentWidth;\n }\n\n return contentBlock;\n}\n\nfunction pushTokens(block, str, styleName) {\n var isEmptyStr = str === '';\n var strs = str.split('\\n');\n var lines = block.lines;\n\n for (var i = 0; i < strs.length; i++) {\n var text = strs[i];\n var token = {\n styleName: styleName,\n text: text,\n isLineHolder: !text && !isEmptyStr\n };\n\n // The first token should be appended to the last line.\n if (!i) {\n var tokens = (lines[lines.length - 1] || (lines[0] = {tokens: []})).tokens;\n\n // Consider cases:\n // (1) ''.split('\\n') => ['', '\\n', ''], the '' at the first item\n // (which is a placeholder) should be replaced by new token.\n // (2) A image backage, where token likes {a|}.\n // (3) A redundant '' will affect textAlign in line.\n // (4) tokens with the same tplName should not be merged, because\n // they should be displayed in different box (with border and padding).\n var tokensLen = tokens.length;\n (tokensLen === 1 && tokens[0].isLineHolder)\n ? (tokens[0] = token)\n // Consider text is '', only insert when it is the \"lineHolder\" or\n // \"emptyStr\". Otherwise a redundant '' will affect textAlign in line.\n : ((text || !tokensLen || isEmptyStr) && tokens.push(token));\n }\n // Other tokens always start a new line.\n else {\n // If there is '', insert it as a placeholder.\n lines.push({tokens: [token]});\n }\n }\n}\n\nexport function makeFont(style) {\n // FIXME in node-canvas fontWeight is before fontStyle\n // Use `fontSize` `fontFamily` to check whether font properties are defined.\n return (style.fontSize || style.fontFamily) && [\n style.fontStyle,\n style.fontWeight,\n (style.fontSize || 12) + 'px',\n // If font properties are defined, `fontFamily` should not be ignored.\n style.fontFamily || 'sans-serif'\n ].join(' ') || style.textFont || style.font;\n}\n","\nexport function buildPath(ctx, shape) {\n var x = shape.x;\n var y = shape.y;\n var width = shape.width;\n var height = shape.height;\n var r = shape.r;\n var r1;\n var r2;\n var r3;\n var r4;\n\n // Convert width and height to positive for better borderRadius\n if (width < 0) {\n x = x + width;\n width = -width;\n }\n if (height < 0) {\n y = y + height;\n height = -height;\n }\n\n if (typeof r === 'number') {\n r1 = r2 = r3 = r4 = r;\n }\n else if (r instanceof Array) {\n if (r.length === 1) {\n r1 = r2 = r3 = r4 = r[0];\n }\n else if (r.length === 2) {\n r1 = r3 = r[0];\n r2 = r4 = r[1];\n }\n else if (r.length === 3) {\n r1 = r[0];\n r2 = r4 = r[1];\n r3 = r[2];\n }\n else {\n r1 = r[0];\n r2 = r[1];\n r3 = r[2];\n r4 = r[3];\n }\n }\n else {\n r1 = r2 = r3 = r4 = 0;\n }\n\n var total;\n if (r1 + r2 > width) {\n total = r1 + r2;\n r1 *= width / total;\n r2 *= width / total;\n }\n if (r3 + r4 > width) {\n total = r3 + r4;\n r3 *= width / total;\n r4 *= width / total;\n }\n if (r2 + r3 > height) {\n total = r2 + r3;\n r2 *= height / total;\n r3 *= height / total;\n }\n if (r1 + r4 > height) {\n total = r1 + r4;\n r1 *= height / total;\n r4 *= height / total;\n }\n ctx.moveTo(x + r1, y);\n ctx.lineTo(x + width - r2, y);\n r2 !== 0 && ctx.quadraticCurveTo(\n x + width, y, x + width, y + r2\n );\n ctx.lineTo(x + width, y + height - r3);\n r3 !== 0 && ctx.quadraticCurveTo(\n x + width, y + height, x + width - r3, y + height\n );\n ctx.lineTo(x + r4, y + height);\n r4 !== 0 && ctx.quadraticCurveTo(\n x, y + height, x, y + height - r4\n );\n ctx.lineTo(x, y + r1);\n r1 !== 0 && ctx.quadraticCurveTo(x, y, x + r1, y);\n}\n","\nimport {\n retrieve2,\n retrieve3,\n each,\n normalizeCssArray,\n isString,\n isObject\n} from '../../core/util';\nimport * as textContain from '../../contain/text';\nimport * as roundRectHelper from './roundRect';\nimport * as imageHelper from './image';\n\n// TODO: Have not support 'start', 'end' yet.\nvar VALID_TEXT_ALIGN = {left: 1, right: 1, center: 1};\nvar VALID_TEXT_VERTICAL_ALIGN = {top: 1, bottom: 1, middle: 1};\n\n/**\n * @param {module:zrender/graphic/Style} style\n * @return {module:zrender/graphic/Style} The input style.\n */\nexport function normalizeTextStyle(style) {\n normalizeStyle(style);\n each(style.rich, normalizeStyle);\n return style;\n}\n\nfunction normalizeStyle(style) {\n if (style) {\n\n style.font = textContain.makeFont(style);\n\n var textAlign = style.textAlign;\n textAlign === 'middle' && (textAlign = 'center');\n style.textAlign = (\n textAlign == null || VALID_TEXT_ALIGN[textAlign]\n ) ? textAlign : 'left';\n\n // Compatible with textBaseline.\n var textVerticalAlign = style.textVerticalAlign || style.textBaseline;\n textVerticalAlign === 'center' && (textVerticalAlign = 'middle');\n style.textVerticalAlign = (\n textVerticalAlign == null || VALID_TEXT_VERTICAL_ALIGN[textVerticalAlign]\n ) ? textVerticalAlign : 'top';\n\n var textPadding = style.textPadding;\n if (textPadding) {\n style.textPadding = normalizeCssArray(style.textPadding);\n }\n }\n}\n\n/**\n * @param {CanvasRenderingContext2D} ctx\n * @param {string} text\n * @param {module:zrender/graphic/Style} style\n * @param {Object|boolean} [rect] {x, y, width, height}\n * If set false, rect text is not used.\n */\nexport function renderText(hostEl, ctx, text, style, rect) {\n style.rich\n ? renderRichText(hostEl, ctx, text, style, rect)\n : renderPlainText(hostEl, ctx, text, style, rect);\n}\n\nfunction renderPlainText(hostEl, ctx, text, style, rect) {\n var font = setCtx(ctx, 'font', style.font || textContain.DEFAULT_FONT);\n\n var textPadding = style.textPadding;\n\n var contentBlock = hostEl.__textCotentBlock;\n if (!contentBlock || hostEl.__dirty) {\n contentBlock = hostEl.__textCotentBlock = textContain.parsePlainText(\n text, font, textPadding, style.truncate\n );\n }\n\n var outerHeight = contentBlock.outerHeight;\n\n var textLines = contentBlock.lines;\n var lineHeight = contentBlock.lineHeight;\n\n var boxPos = getBoxPosition(outerHeight, style, rect);\n var baseX = boxPos.baseX;\n var baseY = boxPos.baseY;\n var textAlign = boxPos.textAlign;\n var textVerticalAlign = boxPos.textVerticalAlign;\n\n // Origin of textRotation should be the base point of text drawing.\n applyTextRotation(ctx, style, rect, baseX, baseY);\n\n var boxY = textContain.adjustTextY(baseY, outerHeight, textVerticalAlign);\n var textX = baseX;\n var textY = boxY;\n\n var needDrawBg = needDrawBackground(style);\n if (needDrawBg || textPadding) {\n // Consider performance, do not call getTextWidth util necessary.\n var textWidth = textContain.getWidth(text, font);\n var outerWidth = textWidth;\n textPadding && (outerWidth += textPadding[1] + textPadding[3]);\n var boxX = textContain.adjustTextX(baseX, outerWidth, textAlign);\n\n needDrawBg && drawBackground(hostEl, ctx, style, boxX, boxY, outerWidth, outerHeight);\n\n if (textPadding) {\n textX = getTextXForPadding(baseX, textAlign, textPadding);\n textY += textPadding[0];\n }\n }\n\n setCtx(ctx, 'textAlign', textAlign || 'left');\n // Force baseline to be \"middle\". Otherwise, if using \"top\", the\n // text will offset downward a little bit in font \"Microsoft YaHei\".\n setCtx(ctx, 'textBaseline', 'middle');\n\n // Always set shadowBlur and shadowOffset to avoid leak from displayable.\n setCtx(ctx, 'shadowBlur', style.textShadowBlur || 0);\n setCtx(ctx, 'shadowColor', style.textShadowColor || 'transparent');\n setCtx(ctx, 'shadowOffsetX', style.textShadowOffsetX || 0);\n setCtx(ctx, 'shadowOffsetY', style.textShadowOffsetY || 0);\n\n // `textBaseline` is set as 'middle'.\n textY += lineHeight / 2;\n\n var textStrokeWidth = style.textStrokeWidth;\n var textStroke = getStroke(style.textStroke, textStrokeWidth);\n var textFill = getFill(style.textFill);\n\n if (textStroke) {\n setCtx(ctx, 'lineWidth', textStrokeWidth);\n setCtx(ctx, 'strokeStyle', textStroke);\n }\n if (textFill) {\n setCtx(ctx, 'fillStyle', textFill);\n }\n\n for (var i = 0; i < textLines.length; i++) {\n // Fill after stroke so the outline will not cover the main part.\n textStroke && ctx.strokeText(textLines[i], textX, textY);\n textFill && ctx.fillText(textLines[i], textX, textY);\n textY += lineHeight;\n }\n}\n\nfunction renderRichText(hostEl, ctx, text, style, rect) {\n var contentBlock = hostEl.__textCotentBlock;\n\n if (!contentBlock || hostEl.__dirty) {\n contentBlock = hostEl.__textCotentBlock = textContain.parseRichText(text, style);\n }\n\n drawRichText(hostEl, ctx, contentBlock, style, rect);\n}\n\nfunction drawRichText(hostEl, ctx, contentBlock, style, rect) {\n var contentWidth = contentBlock.width;\n var outerWidth = contentBlock.outerWidth;\n var outerHeight = contentBlock.outerHeight;\n var textPadding = style.textPadding;\n\n var boxPos = getBoxPosition(outerHeight, style, rect);\n var baseX = boxPos.baseX;\n var baseY = boxPos.baseY;\n var textAlign = boxPos.textAlign;\n var textVerticalAlign = boxPos.textVerticalAlign;\n\n // Origin of textRotation should be the base point of text drawing.\n applyTextRotation(ctx, style, rect, baseX, baseY);\n\n var boxX = textContain.adjustTextX(baseX, outerWidth, textAlign);\n var boxY = textContain.adjustTextY(baseY, outerHeight, textVerticalAlign);\n var xLeft = boxX;\n var lineTop = boxY;\n if (textPadding) {\n xLeft += textPadding[3];\n lineTop += textPadding[0];\n }\n var xRight = xLeft + contentWidth;\n\n needDrawBackground(style) && drawBackground(\n hostEl, ctx, style, boxX, boxY, outerWidth, outerHeight\n );\n\n for (var i = 0; i < contentBlock.lines.length; i++) {\n var line = contentBlock.lines[i];\n var tokens = line.tokens;\n var tokenCount = tokens.length;\n var lineHeight = line.lineHeight;\n var usedWidth = line.width;\n\n var leftIndex = 0;\n var lineXLeft = xLeft;\n var lineXRight = xRight;\n var rightIndex = tokenCount - 1;\n var token;\n\n while (\n leftIndex < tokenCount\n && (token = tokens[leftIndex], !token.textAlign || token.textAlign === 'left')\n ) {\n placeToken(hostEl, ctx, token, style, lineHeight, lineTop, lineXLeft, 'left');\n usedWidth -= token.width;\n lineXLeft += token.width;\n leftIndex++;\n }\n\n while (\n rightIndex >= 0\n && (token = tokens[rightIndex], token.textAlign === 'right')\n ) {\n placeToken(hostEl, ctx, token, style, lineHeight, lineTop, lineXRight, 'right');\n usedWidth -= token.width;\n lineXRight -= token.width;\n rightIndex--;\n }\n\n // The other tokens are placed as textAlign 'center' if there is enough space.\n lineXLeft += (contentWidth - (lineXLeft - xLeft) - (xRight - lineXRight) - usedWidth) / 2;\n while (leftIndex <= rightIndex) {\n token = tokens[leftIndex];\n // Consider width specified by user, use 'center' rather than 'left'.\n placeToken(hostEl, ctx, token, style, lineHeight, lineTop, lineXLeft + token.width / 2, 'center');\n lineXLeft += token.width;\n leftIndex++;\n }\n\n lineTop += lineHeight;\n }\n}\n\nfunction applyTextRotation(ctx, style, rect, x, y) {\n // textRotation only apply in RectText.\n if (rect && style.textRotation) {\n var origin = style.textOrigin;\n if (origin === 'center') {\n x = rect.width / 2 + rect.x;\n y = rect.height / 2 + rect.y;\n }\n else if (origin) {\n x = origin[0] + rect.x;\n y = origin[1] + rect.y;\n }\n\n ctx.translate(x, y);\n // Positive: anticlockwise\n ctx.rotate(-style.textRotation);\n ctx.translate(-x, -y);\n }\n}\n\nfunction placeToken(hostEl, ctx, token, style, lineHeight, lineTop, x, textAlign) {\n var tokenStyle = style.rich[token.styleName] || {};\n\n // 'ctx.textBaseline' is always set as 'middle', for sake of\n // the bias of \"Microsoft YaHei\".\n var textVerticalAlign = token.textVerticalAlign;\n var y = lineTop + lineHeight / 2;\n if (textVerticalAlign === 'top') {\n y = lineTop + token.height / 2;\n }\n else if (textVerticalAlign === 'bottom') {\n y = lineTop + lineHeight - token.height / 2;\n }\n\n !token.isLineHolder && needDrawBackground(tokenStyle) && drawBackground(\n hostEl,\n ctx,\n tokenStyle,\n textAlign === 'right'\n ? x - token.width\n : textAlign === 'center'\n ? x - token.width / 2\n : x,\n y - token.height / 2,\n token.width,\n token.height\n );\n\n var textPadding = token.textPadding;\n if (textPadding) {\n x = getTextXForPadding(x, textAlign, textPadding);\n y -= token.height / 2 - textPadding[2] - token.textHeight / 2;\n }\n\n setCtx(ctx, 'shadowBlur', retrieve3(tokenStyle.textShadowBlur, style.textShadowBlur, 0));\n setCtx(ctx, 'shadowColor', tokenStyle.textShadowColor || style.textShadowColor || 'transparent');\n setCtx(ctx, 'shadowOffsetX', retrieve3(tokenStyle.textShadowOffsetX, style.textShadowOffsetX, 0));\n setCtx(ctx, 'shadowOffsetY', retrieve3(tokenStyle.textShadowOffsetY, style.textShadowOffsetY, 0));\n\n setCtx(ctx, 'textAlign', textAlign);\n // Force baseline to be \"middle\". Otherwise, if using \"top\", the\n // text will offset downward a little bit in font \"Microsoft YaHei\".\n setCtx(ctx, 'textBaseline', 'middle');\n\n setCtx(ctx, 'font', token.font || textContain.DEFAULT_FONT);\n\n var textStroke = getStroke(tokenStyle.textStroke || style.textStroke, textStrokeWidth);\n var textFill = getFill(tokenStyle.textFill || style.textFill);\n var textStrokeWidth = retrieve2(tokenStyle.textStrokeWidth, style.textStrokeWidth);\n\n // Fill after stroke so the outline will not cover the main part.\n if (textStroke) {\n setCtx(ctx, 'lineWidth', textStrokeWidth);\n setCtx(ctx, 'strokeStyle', textStroke);\n ctx.strokeText(token.text, x, y);\n }\n if (textFill) {\n setCtx(ctx, 'fillStyle', textFill);\n ctx.fillText(token.text, x, y);\n }\n}\n\nfunction needDrawBackground(style) {\n return style.textBackgroundColor\n || (style.textBorderWidth && style.textBorderColor);\n}\n\n// style: {textBackgroundColor, textBorderWidth, textBorderColor, textBorderRadius}\n// shape: {x, y, width, height}\nfunction drawBackground(hostEl, ctx, style, x, y, width, height) {\n var textBackgroundColor = style.textBackgroundColor;\n var textBorderWidth = style.textBorderWidth;\n var textBorderColor = style.textBorderColor;\n var isPlainBg = isString(textBackgroundColor);\n\n setCtx(ctx, 'shadowBlur', style.textBoxShadowBlur || 0);\n setCtx(ctx, 'shadowColor', style.textBoxShadowColor || 'transparent');\n setCtx(ctx, 'shadowOffsetX', style.textBoxShadowOffsetX || 0);\n setCtx(ctx, 'shadowOffsetY', style.textBoxShadowOffsetY || 0);\n\n if (isPlainBg || (textBorderWidth && textBorderColor)) {\n ctx.beginPath();\n var textBorderRadius = style.textBorderRadius;\n if (!textBorderRadius) {\n ctx.rect(x, y, width, height);\n }\n else {\n roundRectHelper.buildPath(ctx, {\n x: x, y: y, width: width, height: height, r: textBorderRadius\n });\n }\n ctx.closePath();\n }\n\n if (isPlainBg) {\n setCtx(ctx, 'fillStyle', textBackgroundColor);\n ctx.fill();\n }\n else if (isObject(textBackgroundColor)) {\n var image = textBackgroundColor.image;\n\n image = imageHelper.createOrUpdateImage(\n image, null, hostEl, onBgImageLoaded, textBackgroundColor\n );\n if (image && imageHelper.isImageReady(image)) {\n ctx.drawImage(image, x, y, width, height);\n }\n }\n\n if (textBorderWidth && textBorderColor) {\n setCtx(ctx, 'lineWidth', textBorderWidth);\n setCtx(ctx, 'strokeStyle', textBorderColor);\n ctx.stroke();\n }\n}\n\nfunction onBgImageLoaded(image, textBackgroundColor) {\n // Replace image, so that `contain/text.js#parseRichText`\n // will get correct result in next tick.\n textBackgroundColor.image = image;\n}\n\nfunction getBoxPosition(blockHeiht, style, rect) {\n var baseX = style.x || 0;\n var baseY = style.y || 0;\n var textAlign = style.textAlign;\n var textVerticalAlign = style.textVerticalAlign;\n\n // Text position represented by coord\n if (rect) {\n var textPosition = style.textPosition;\n if (textPosition instanceof Array) {\n // Percent\n baseX = rect.x + parsePercent(textPosition[0], rect.width);\n baseY = rect.y + parsePercent(textPosition[1], rect.height);\n }\n else {\n var res = textContain.adjustTextPositionOnRect(\n textPosition, rect, style.textDistance\n );\n baseX = res.x;\n baseY = res.y;\n // Default align and baseline when has textPosition\n textAlign = textAlign || res.textAlign;\n textVerticalAlign = textVerticalAlign || res.textVerticalAlign;\n }\n\n // textOffset is only support in RectText, otherwise\n // we have to adjust boundingRect for textOffset.\n var textOffset = style.textOffset;\n if (textOffset) {\n baseX += textOffset[0];\n baseY += textOffset[1];\n }\n }\n\n return {\n baseX: baseX,\n baseY: baseY,\n textAlign: textAlign,\n textVerticalAlign: textVerticalAlign\n };\n}\n\nfunction setCtx(ctx, prop, value) {\n // FIXME ??? performance try\n // if (ctx.__currentValues[prop] !== value) {\n // ctx[prop] = ctx.__currentValues[prop] = value;\n ctx[prop] = value;\n // }\n return ctx[prop];\n}\n\n/**\n * @param {string} [stroke] If specified, do not check style.textStroke.\n * @param {string} [lineWidth] If specified, do not check style.textStroke.\n * @param {number} style\n */\nexport function getStroke(stroke, lineWidth) {\n return (stroke == null || lineWidth <= 0 || stroke === 'transparent' || stroke === 'none')\n ? null\n // TODO pattern and gradient?\n : (stroke.image || stroke.colorStops)\n ? '#000'\n : stroke;\n}\n\nexport function getFill(fill) {\n return (fill == null || fill === 'none')\n ? null\n // TODO pattern and gradient?\n : (fill.image || fill.colorStops)\n ? '#000'\n : fill;\n}\n\nfunction parsePercent(value, maxValue) {\n if (typeof value === 'string') {\n if (value.lastIndexOf('%') >= 0) {\n return parseFloat(value) / 100 * maxValue;\n }\n return parseFloat(value);\n }\n return value;\n}\n\nfunction getTextXForPadding(x, textAlign, textPadding) {\n return textAlign === 'right'\n ? (x - textPadding[1])\n : textAlign === 'center'\n ? (x + textPadding[3] / 2 - textPadding[1] / 2)\n : (x + textPadding[3]);\n}\n\n/**\n * @param {string} text\n * @param {module:zrender/Style} style\n * @return {boolean}\n */\nexport function needDrawText(text, style) {\n return text != null\n && (text\n || style.textBackgroundColor\n || (style.textBorderWidth && style.textBorderColor)\n || style.textPadding\n );\n}\n","/**\n * Mixin for drawing text in a element bounding rect\n * @module zrender/mixin/RectText\n */\n\nimport * as textHelper from '../helper/text';\nimport BoundingRect from '../../core/BoundingRect';\n\nvar tmpRect = new BoundingRect();\n\nvar RectText = function () {};\n\nRectText.prototype = {\n\n constructor: RectText,\n\n /**\n * Draw text in a rect with specified position.\n * @param {CanvasRenderingContext2D} ctx\n * @param {Object} rect Displayable rect\n */\n drawRectText: function (ctx, rect) {\n var style = this.style;\n\n rect = style.textRect || rect;\n\n // Optimize, avoid normalize every time.\n this.__dirty && textHelper.normalizeTextStyle(style, true);\n\n var text = style.text;\n\n // Convert to string\n text != null && (text += '');\n\n if (!textHelper.needDrawText(text, style)) {\n return;\n }\n\n // FIXME\n ctx.save();\n\n // Transform rect to view space\n var transform = this.transform;\n if (!style.transformText) {\n if (transform) {\n tmpRect.copy(rect);\n tmpRect.applyTransform(transform);\n rect = tmpRect;\n }\n }\n else {\n this.setTransform(ctx);\n }\n\n // transformText and textRotation can not be used at the same time.\n textHelper.renderText(this, ctx, text, style, rect);\n\n ctx.restore();\n }\n};\n\nexport default RectText;","/**\n * 可绘制的图形基类\n * Base class of all displayable graphic objects\n * @module zrender/graphic/Displayable\n */\n\n\nimport * as zrUtil from '../core/util';\nimport Style from './Style';\nimport Element from '../Element';\nimport RectText from './mixin/RectText';\n\n/**\n * @alias module:zrender/graphic/Displayable\n * @extends module:zrender/Element\n * @extends module:zrender/graphic/mixin/RectText\n */\nfunction Displayable(opts) {\n\n opts = opts || {};\n\n Element.call(this, opts);\n\n // Extend properties\n for (var name in opts) {\n if (\n opts.hasOwnProperty(name) &&\n name !== 'style'\n ) {\n this[name] = opts[name];\n }\n }\n\n /**\n * @type {module:zrender/graphic/Style}\n */\n this.style = new Style(opts.style, this);\n\n this._rect = null;\n // Shapes for cascade clipping.\n this.__clipPaths = [];\n\n // FIXME Stateful must be mixined after style is setted\n // Stateful.call(this, opts);\n}\n\nDisplayable.prototype = {\n\n constructor: Displayable,\n\n type: 'displayable',\n\n /**\n * Displayable 是否为脏,Painter 中会根据该标记判断是否需要是否需要重新绘制\n * Dirty flag. From which painter will determine if this displayable object needs brush\n * @name module:zrender/graphic/Displayable#__dirty\n * @type {boolean}\n */\n __dirty: true,\n\n /**\n * 图形是否可见,为true时不绘制图形,但是仍能触发鼠标事件\n * If ignore drawing of the displayable object. Mouse event will still be triggered\n * @name module:/zrender/graphic/Displayable#invisible\n * @type {boolean}\n * @default false\n */\n invisible: false,\n\n /**\n * @name module:/zrender/graphic/Displayable#z\n * @type {number}\n * @default 0\n */\n z: 0,\n\n /**\n * @name module:/zrender/graphic/Displayable#z\n * @type {number}\n * @default 0\n */\n z2: 0,\n\n /**\n * z层level,决定绘画在哪层canvas中\n * @name module:/zrender/graphic/Displayable#zlevel\n * @type {number}\n * @default 0\n */\n zlevel: 0,\n\n /**\n * 是否可拖拽\n * @name module:/zrender/graphic/Displayable#draggable\n * @type {boolean}\n * @default false\n */\n draggable: false,\n\n /**\n * 是否正在拖拽\n * @name module:/zrender/graphic/Displayable#draggable\n * @type {boolean}\n * @default false\n */\n dragging: false,\n\n /**\n * 是否相应鼠标事件\n * @name module:/zrender/graphic/Displayable#silent\n * @type {boolean}\n * @default false\n */\n silent: false,\n\n /**\n * If enable culling\n * @type {boolean}\n * @default false\n */\n culling: false,\n\n /**\n * Mouse cursor when hovered\n * @name module:/zrender/graphic/Displayable#cursor\n * @type {string}\n */\n cursor: 'pointer',\n\n /**\n * If hover area is bounding rect\n * @name module:/zrender/graphic/Displayable#rectHover\n * @type {string}\n */\n rectHover: false,\n\n /**\n * Render the element progressively when the value >= 0,\n * usefull for large data.\n * @type {number}\n */\n progressive: -1,\n\n beforeBrush: function (ctx) {},\n\n afterBrush: function (ctx) {},\n\n /**\n * 图形绘制方法\n * @param {CanvasRenderingContext2D} ctx\n */\n // Interface\n brush: function (ctx, prevEl) {},\n\n /**\n * 获取最小包围盒\n * @return {module:zrender/core/BoundingRect}\n */\n // Interface\n getBoundingRect: function () {},\n\n /**\n * 判断坐标 x, y 是否在图形上\n * If displayable element contain coord x, y\n * @param {number} x\n * @param {number} y\n * @return {boolean}\n */\n contain: function (x, y) {\n return this.rectContain(x, y);\n },\n\n /**\n * @param {Function} cb\n * @param {} context\n */\n traverse: function (cb, context) {\n cb.call(context, this);\n },\n\n /**\n * 判断坐标 x, y 是否在图形的包围盒上\n * If bounding rect of element contain coord x, y\n * @param {number} x\n * @param {number} y\n * @return {boolean}\n */\n rectContain: function (x, y) {\n var coord = this.transformCoordToLocal(x, y);\n var rect = this.getBoundingRect();\n return rect.contain(coord[0], coord[1]);\n },\n\n /**\n * 标记图形元素为脏,并且在下一帧重绘\n * Mark displayable element dirty and refresh next frame\n */\n dirty: function () {\n this.__dirty = true;\n\n this._rect = null;\n\n this.__zr && this.__zr.refresh();\n },\n\n /**\n * 图形是否会触发事件\n * If displayable object binded any event\n * @return {boolean}\n */\n // TODO, 通过 bind 绑定的事件\n // isSilent: function () {\n // return !(\n // this.hoverable || this.draggable\n // || this.onmousemove || this.onmouseover || this.onmouseout\n // || this.onmousedown || this.onmouseup || this.onclick\n // || this.ondragenter || this.ondragover || this.ondragleave\n // || this.ondrop\n // );\n // },\n /**\n * Alias for animate('style')\n * @param {boolean} loop\n */\n animateStyle: function (loop) {\n return this.animate('style', loop);\n },\n\n attrKV: function (key, value) {\n if (key !== 'style') {\n Element.prototype.attrKV.call(this, key, value);\n }\n else {\n this.style.set(value);\n }\n },\n\n /**\n * @param {Object|string} key\n * @param {*} value\n */\n setStyle: function (key, value) {\n this.style.set(key, value);\n this.dirty(false);\n return this;\n },\n\n /**\n * Use given style object\n * @param {Object} obj\n */\n useStyle: function (obj) {\n this.style = new Style(obj, this);\n this.dirty(false);\n return this;\n }\n};\n\nzrUtil.inherits(Displayable, Element);\n\nzrUtil.mixin(Displayable, RectText);\n// zrUtil.mixin(Displayable, Stateful);\n\nexport default Displayable;","import Displayable from './Displayable';\nimport BoundingRect from '../core/BoundingRect';\nimport * as zrUtil from '../core/util';\nimport * as imageHelper from './helper/image';\n\n/**\n * @alias zrender/graphic/Image\n * @extends module:zrender/graphic/Displayable\n * @constructor\n * @param {Object} opts\n */\nfunction ZImage(opts) {\n Displayable.call(this, opts);\n}\n\nZImage.prototype = {\n\n constructor: ZImage,\n\n type: 'image',\n\n brush: function (ctx, prevEl) {\n var style = this.style;\n var src = style.image;\n\n // Must bind each time\n style.bind(ctx, this, prevEl);\n\n var image = this._image = imageHelper.createOrUpdateImage(\n src,\n this._image,\n this,\n this.onload\n );\n\n if (!image || !imageHelper.isImageReady(image)) {\n return;\n }\n\n // 图片已经加载完成\n // if (image.nodeName.toUpperCase() == 'IMG') {\n // if (!image.complete) {\n // return;\n // }\n // }\n // Else is canvas\n\n var x = style.x || 0;\n var y = style.y || 0;\n var width = style.width;\n var height = style.height;\n var aspect = image.width / image.height;\n if (width == null && height != null) {\n // Keep image/height ratio\n width = height * aspect;\n }\n else if (height == null && width != null) {\n height = width / aspect;\n }\n else if (width == null && height == null) {\n width = image.width;\n height = image.height;\n }\n\n // 设置transform\n this.setTransform(ctx);\n\n if (style.sWidth && style.sHeight) {\n var sx = style.sx || 0;\n var sy = style.sy || 0;\n ctx.drawImage(\n image,\n sx, sy, style.sWidth, style.sHeight,\n x, y, width, height\n );\n }\n else if (style.sx && style.sy) {\n var sx = style.sx;\n var sy = style.sy;\n var sWidth = width - sx;\n var sHeight = height - sy;\n ctx.drawImage(\n image,\n sx, sy, sWidth, sHeight,\n x, y, width, height\n );\n }\n else {\n ctx.drawImage(image, x, y, width, height);\n }\n\n this.restoreTransform(ctx);\n\n // Draw rect text\n if (style.text != null) {\n this.drawRectText(ctx, this.getBoundingRect());\n }\n },\n\n getBoundingRect: function () {\n var style = this.style;\n if (! this._rect) {\n this._rect = new BoundingRect(\n style.x || 0, style.y || 0, style.width || 0, style.height || 0\n );\n }\n return this._rect;\n }\n};\n\nzrUtil.inherits(ZImage, Displayable);\n\nexport default ZImage;","/**\n * Default canvas painter\n * @module zrender/Painter\n * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)\n * errorrik (errorrik@gmail.com)\n * pissang (https://www.github.com/pissang)\n */\n\nimport {devicePixelRatio} from './config';\nimport * as util from './core/util';\nimport log from './core/log';\nimport BoundingRect from './core/BoundingRect';\nimport timsort from './core/timsort';\nimport Layer from './Layer';\nimport requestAnimationFrame from './animation/requestAnimationFrame';\nimport Image from './graphic/Image';\n\n// PENDIGN\n// Layer exceeds MAX_PROGRESSIVE_LAYER_NUMBER may have some problem when flush directly second time.\n//\n// Maximum progressive layer. When exceeding this number. All elements will be drawed in the last layer.\nvar MAX_PROGRESSIVE_LAYER_NUMBER = 5;\n\nfunction parseInt10(val) {\n return parseInt(val, 10);\n}\n\nfunction isLayerValid(layer) {\n if (!layer) {\n return false;\n }\n\n if (layer.__builtin__) {\n return true;\n }\n\n if (typeof(layer.resize) !== 'function'\n || typeof(layer.refresh) !== 'function'\n ) {\n return false;\n }\n\n return true;\n}\n\nfunction preProcessLayer(layer) {\n layer.__unusedCount++;\n}\n\nfunction postProcessLayer(layer) {\n if (layer.__unusedCount == 1) {\n layer.clear();\n }\n}\n\nvar tmpRect = new BoundingRect(0, 0, 0, 0);\nvar viewRect = new BoundingRect(0, 0, 0, 0);\nfunction isDisplayableCulled(el, width, height) {\n tmpRect.copy(el.getBoundingRect());\n if (el.transform) {\n tmpRect.applyTransform(el.transform);\n }\n viewRect.width = width;\n viewRect.height = height;\n return !tmpRect.intersect(viewRect);\n}\n\nfunction isClipPathChanged(clipPaths, prevClipPaths) {\n if (clipPaths == prevClipPaths) { // Can both be null or undefined\n return false;\n }\n\n if (!clipPaths || !prevClipPaths || (clipPaths.length !== prevClipPaths.length)) {\n return true;\n }\n for (var i = 0; i < clipPaths.length; i++) {\n if (clipPaths[i] !== prevClipPaths[i]) {\n return true;\n }\n }\n}\n\nfunction doClip(clipPaths, ctx) {\n for (var i = 0; i < clipPaths.length; i++) {\n var clipPath = clipPaths[i];\n\n clipPath.setTransform(ctx);\n ctx.beginPath();\n clipPath.buildPath(ctx, clipPath.shape);\n ctx.clip();\n // Transform back\n clipPath.restoreTransform(ctx);\n }\n}\n\nfunction createRoot(width, height) {\n var domRoot = document.createElement('div');\n\n // domRoot.onselectstart = returnFalse; // 避免页面选中的尴尬\n domRoot.style.cssText = [\n 'position:relative',\n 'overflow:hidden',\n 'width:' + width + 'px',\n 'height:' + height + 'px',\n 'padding:0',\n 'margin:0',\n 'border-width:0'\n ].join(';') + ';';\n\n return domRoot;\n}\n\n/**\n * @alias module:zrender/Painter\n * @constructor\n * @param {HTMLElement} root 绘图容器\n * @param {module:zrender/Storage} storage\n * @param {Object} opts\n */\nvar Painter = function (root, storage, opts) {\n\n this.type = 'canvas';\n\n // In node environment using node-canvas\n var singleCanvas = !root.nodeName // In node ?\n || root.nodeName.toUpperCase() === 'CANVAS';\n\n this._opts = opts = util.extend({}, opts || {});\n\n /**\n * @type {number}\n */\n this.dpr = opts.devicePixelRatio || devicePixelRatio;\n /**\n * @type {boolean}\n * @private\n */\n this._singleCanvas = singleCanvas;\n /**\n * 绘图容器\n * @type {HTMLElement}\n */\n this.root = root;\n\n var rootStyle = root.style;\n\n if (rootStyle) {\n rootStyle['-webkit-tap-highlight-color'] = 'transparent';\n rootStyle['-webkit-user-select'] =\n rootStyle['user-select'] =\n rootStyle['-webkit-touch-callout'] = 'none';\n\n root.innerHTML = '';\n }\n\n /**\n * @type {module:zrender/Storage}\n */\n this.storage = storage;\n\n /**\n * @type {Array.<number>}\n * @private\n */\n var zlevelList = this._zlevelList = [];\n\n /**\n * @type {Object.<string, module:zrender/Layer>}\n * @private\n */\n var layers = this._layers = {};\n\n /**\n * @type {Object.<string, Object>}\n * @type {private}\n */\n this._layerConfig = {};\n\n if (!singleCanvas) {\n this._width = this._getSize(0);\n this._height = this._getSize(1);\n\n var domRoot = this._domRoot = createRoot(\n this._width, this._height\n );\n root.appendChild(domRoot);\n }\n else {\n if (opts.width != null) {\n root.width = opts.width;\n }\n if (opts.height != null) {\n root.height = opts.height;\n }\n // Use canvas width and height directly\n var width = root.width;\n var height = root.height;\n this._width = width;\n this._height = height;\n\n // Create layer if only one given canvas\n // Device pixel ratio is fixed to 1 because given canvas has its specified width and height\n var mainLayer = new Layer(root, this, 1);\n mainLayer.initContext();\n // FIXME Use canvas width and height\n // mainLayer.resize(width, height);\n layers[0] = mainLayer;\n zlevelList.push(0);\n\n this._domRoot = root;\n }\n\n // Layers for progressive rendering\n this._progressiveLayers = [];\n\n /**\n * @type {module:zrender/Layer}\n * @private\n */\n this._hoverlayer;\n\n this._hoverElements = [];\n};\n\nPainter.prototype = {\n\n constructor: Painter,\n\n getType: function () {\n return 'canvas';\n },\n\n /**\n * If painter use a single canvas\n * @return {boolean}\n */\n isSingleCanvas: function () {\n return this._singleCanvas;\n },\n /**\n * @return {HTMLDivElement}\n */\n getViewportRoot: function () {\n return this._domRoot;\n },\n\n getViewportRootOffset: function () {\n var viewportRoot = this.getViewportRoot();\n if (viewportRoot) {\n return {\n offsetLeft: viewportRoot.offsetLeft || 0,\n offsetTop: viewportRoot.offsetTop || 0\n };\n }\n },\n\n /**\n * 刷新\n * @param {boolean} [paintAll=false] 强制绘制所有displayable\n */\n refresh: function (paintAll) {\n\n var list = this.storage.getDisplayList(true);\n\n var zlevelList = this._zlevelList;\n\n this._paintList(list, paintAll);\n\n // Paint custum layers\n for (var i = 0; i < zlevelList.length; i++) {\n var z = zlevelList[i];\n var layer = this._layers[z];\n if (!layer.__builtin__ && layer.refresh) {\n layer.refresh();\n }\n }\n\n this.refreshHover();\n\n if (this._progressiveLayers.length) {\n this._startProgessive();\n }\n\n return this;\n },\n\n addHover: function (el, hoverStyle) {\n if (el.__hoverMir) {\n return;\n }\n var elMirror = new el.constructor({\n style: el.style,\n shape: el.shape\n });\n elMirror.__from = el;\n el.__hoverMir = elMirror;\n elMirror.setStyle(hoverStyle);\n this._hoverElements.push(elMirror);\n },\n\n removeHover: function (el) {\n var elMirror = el.__hoverMir;\n var hoverElements = this._hoverElements;\n var idx = util.indexOf(hoverElements, elMirror);\n if (idx >= 0) {\n hoverElements.splice(idx, 1);\n }\n el.__hoverMir = null;\n },\n\n clearHover: function (el) {\n var hoverElements = this._hoverElements;\n for (var i = 0; i < hoverElements.length; i++) {\n var from = hoverElements[i].__from;\n if (from) {\n from.__hoverMir = null;\n }\n }\n hoverElements.length = 0;\n },\n\n refreshHover: function () {\n var hoverElements = this._hoverElements;\n var len = hoverElements.length;\n var hoverLayer = this._hoverlayer;\n hoverLayer && hoverLayer.clear();\n\n if (!len) {\n return;\n }\n timsort(hoverElements, this.storage.displayableSortFunc);\n\n // Use a extream large zlevel\n // FIXME?\n if (!hoverLayer) {\n hoverLayer = this._hoverlayer = this.getLayer(1e5);\n }\n\n var scope = {};\n hoverLayer.ctx.save();\n for (var i = 0; i < len;) {\n var el = hoverElements[i];\n var originalEl = el.__from;\n // Original el is removed\n // PENDING\n if (!(originalEl && originalEl.__zr)) {\n hoverElements.splice(i, 1);\n originalEl.__hoverMir = null;\n len--;\n continue;\n }\n i++;\n\n // Use transform\n // FIXME style and shape ?\n if (!originalEl.invisible) {\n el.transform = originalEl.transform;\n el.invTransform = originalEl.invTransform;\n el.__clipPaths = originalEl.__clipPaths;\n // el.\n this._doPaintEl(el, hoverLayer, true, scope);\n }\n }\n hoverLayer.ctx.restore();\n },\n\n _startProgessive: function () {\n var self = this;\n\n if (!self._furtherProgressive) {\n return;\n }\n\n // Use a token to stop progress steps triggered by\n // previous zr.refresh calling.\n var token = self._progressiveToken = +new Date();\n\n self._progress++;\n requestAnimationFrame(step);\n\n function step() {\n // In case refreshed or disposed\n if (token === self._progressiveToken && self.storage) {\n\n self._doPaintList(self.storage.getDisplayList());\n\n if (self._furtherProgressive) {\n self._progress++;\n requestAnimationFrame(step);\n }\n else {\n self._progressiveToken = -1;\n }\n }\n }\n },\n\n _clearProgressive: function () {\n this._progressiveToken = -1;\n this._progress = 0;\n util.each(this._progressiveLayers, function (layer) {\n layer.__dirty && layer.clear();\n });\n },\n\n _paintList: function (list, paintAll) {\n\n if (paintAll == null) {\n paintAll = false;\n }\n\n this._updateLayerStatus(list);\n\n this._clearProgressive();\n\n this.eachBuiltinLayer(preProcessLayer);\n\n this._doPaintList(list, paintAll);\n\n this.eachBuiltinLayer(postProcessLayer);\n },\n\n _doPaintList: function (list, paintAll) {\n var currentLayer;\n var currentZLevel;\n var ctx;\n\n // var invTransform = [];\n var scope;\n\n var progressiveLayerIdx = 0;\n var currentProgressiveLayer;\n\n var width = this._width;\n var height = this._height;\n var layerProgress;\n var frame = this._progress;\n function flushProgressiveLayer(layer) {\n var dpr = ctx.dpr || 1;\n ctx.save();\n ctx.globalAlpha = 1;\n ctx.shadowBlur = 0;\n // Avoid layer don't clear in next progressive frame\n currentLayer.__dirty = true;\n ctx.setTransform(1, 0, 0, 1, 0, 0);\n ctx.drawImage(layer.dom, 0, 0, width * dpr, height * dpr);\n ctx.restore();\n }\n\n for (var i = 0, l = list.length; i < l; i++) {\n var el = list[i];\n var elZLevel = this._singleCanvas ? 0 : el.zlevel;\n\n var elFrame = el.__frame;\n\n // Flush at current context\n // PENDING\n if (elFrame < 0 && currentProgressiveLayer) {\n flushProgressiveLayer(currentProgressiveLayer);\n currentProgressiveLayer = null;\n }\n\n // Change draw layer\n if (currentZLevel !== elZLevel) {\n if (ctx) {\n ctx.restore();\n }\n\n // Reset scope\n scope = {};\n\n // Only 0 zlevel if only has one canvas\n currentZLevel = elZLevel;\n currentLayer = this.getLayer(currentZLevel);\n\n if (!currentLayer.__builtin__) {\n log(\n 'ZLevel ' + currentZLevel\n + ' has been used by unkown layer ' + currentLayer.id\n );\n }\n\n ctx = currentLayer.ctx;\n ctx.save();\n\n // Reset the count\n currentLayer.__unusedCount = 0;\n\n if (currentLayer.__dirty || paintAll) {\n currentLayer.clear();\n }\n }\n\n if (!(currentLayer.__dirty || paintAll)) {\n continue;\n }\n\n if (elFrame >= 0) {\n // Progressive layer changed\n if (!currentProgressiveLayer) {\n currentProgressiveLayer = this._progressiveLayers[\n Math.min(progressiveLayerIdx++, MAX_PROGRESSIVE_LAYER_NUMBER - 1)\n ];\n\n currentProgressiveLayer.ctx.save();\n currentProgressiveLayer.renderScope = {};\n\n if (currentProgressiveLayer\n && (currentProgressiveLayer.__progress > currentProgressiveLayer.__maxProgress)\n ) {\n // flushProgressiveLayer(currentProgressiveLayer);\n // Quick jump all progressive elements\n // All progressive element are not dirty, jump over and flush directly\n i = currentProgressiveLayer.__nextIdxNotProg - 1;\n // currentProgressiveLayer = null;\n continue;\n }\n\n layerProgress = currentProgressiveLayer.__progress;\n\n if (!currentProgressiveLayer.__dirty) {\n // Keep rendering\n frame = layerProgress;\n }\n\n currentProgressiveLayer.__progress = frame + 1;\n }\n\n if (elFrame === frame) {\n this._doPaintEl(el, currentProgressiveLayer, true, currentProgressiveLayer.renderScope);\n }\n }\n else {\n this._doPaintEl(el, currentLayer, paintAll, scope);\n }\n\n el.__dirty = false;\n }\n\n if (currentProgressiveLayer) {\n flushProgressiveLayer(currentProgressiveLayer);\n }\n\n // Restore the lastLayer ctx\n ctx && ctx.restore();\n // If still has clipping state\n // if (scope.prevElClipPaths) {\n // ctx.restore();\n // }\n\n this._furtherProgressive = false;\n util.each(this._progressiveLayers, function (layer) {\n if (layer.__maxProgress >= layer.__progress) {\n this._furtherProgressive = true;\n }\n }, this);\n },\n\n _doPaintEl: function (el, currentLayer, forcePaint, scope) {\n var ctx = currentLayer.ctx;\n var m = el.transform;\n if (\n (currentLayer.__dirty || forcePaint)\n // Ignore invisible element\n && !el.invisible\n // Ignore transparent element\n && el.style.opacity !== 0\n // Ignore scale 0 element, in some environment like node-canvas\n // Draw a scale 0 element can cause all following draw wrong\n // And setTransform with scale 0 will cause set back transform failed.\n && !(m && !m[0] && !m[3])\n // Ignore culled element\n && !(el.culling && isDisplayableCulled(el, this._width, this._height))\n ) {\n\n var clipPaths = el.__clipPaths;\n\n // Optimize when clipping on group with several elements\n if (scope.prevClipLayer !== currentLayer\n || isClipPathChanged(clipPaths, scope.prevElClipPaths)\n ) {\n // If has previous clipping state, restore from it\n if (scope.prevElClipPaths) {\n scope.prevClipLayer.ctx.restore();\n scope.prevClipLayer = scope.prevElClipPaths = null;\n\n // Reset prevEl since context has been restored\n scope.prevEl = null;\n }\n // New clipping state\n if (clipPaths) {\n ctx.save();\n doClip(clipPaths, ctx);\n scope.prevClipLayer = currentLayer;\n scope.prevElClipPaths = clipPaths;\n }\n }\n el.beforeBrush && el.beforeBrush(ctx);\n\n el.brush(ctx, scope.prevEl || null);\n scope.prevEl = el;\n\n el.afterBrush && el.afterBrush(ctx);\n }\n },\n\n /**\n * 获取 zlevel 所在层,如果不存在则会创建一个新的层\n * @param {number} zlevel\n * @return {module:zrender/Layer}\n */\n getLayer: function (zlevel) {\n if (this._singleCanvas) {\n return this._layers[0];\n }\n\n var layer = this._layers[zlevel];\n if (!layer) {\n // Create a new layer\n layer = new Layer('zr_' + zlevel, this, this.dpr);\n layer.__builtin__ = true;\n\n if (this._layerConfig[zlevel]) {\n util.merge(layer, this._layerConfig[zlevel], true);\n }\n\n this.insertLayer(zlevel, layer);\n\n // Context is created after dom inserted to document\n // Or excanvas will get 0px clientWidth and clientHeight\n layer.initContext();\n }\n\n return layer;\n },\n\n insertLayer: function (zlevel, layer) {\n\n var layersMap = this._layers;\n var zlevelList = this._zlevelList;\n var len = zlevelList.length;\n var prevLayer = null;\n var i = -1;\n var domRoot = this._domRoot;\n\n if (layersMap[zlevel]) {\n log('ZLevel ' + zlevel + ' has been used already');\n return;\n }\n // Check if is a valid layer\n if (!isLayerValid(layer)) {\n log('Layer of zlevel ' + zlevel + ' is not valid');\n return;\n }\n\n if (len > 0 && zlevel > zlevelList[0]) {\n for (i = 0; i < len - 1; i++) {\n if (\n zlevelList[i] < zlevel\n && zlevelList[i + 1] > zlevel\n ) {\n break;\n }\n }\n prevLayer = layersMap[zlevelList[i]];\n }\n zlevelList.splice(i + 1, 0, zlevel);\n\n layersMap[zlevel] = layer;\n\n // Vitual layer will not directly show on the screen.\n // (It can be a WebGL layer and assigned to a ZImage element)\n // But it still under management of zrender.\n if (!layer.virtual) {\n if (prevLayer) {\n var prevDom = prevLayer.dom;\n if (prevDom.nextSibling) {\n domRoot.insertBefore(\n layer.dom,\n prevDom.nextSibling\n );\n }\n else {\n domRoot.appendChild(layer.dom);\n }\n }\n else {\n if (domRoot.firstChild) {\n domRoot.insertBefore(layer.dom, domRoot.firstChild);\n }\n else {\n domRoot.appendChild(layer.dom);\n }\n }\n }\n },\n\n // Iterate each layer\n eachLayer: function (cb, context) {\n var zlevelList = this._zlevelList;\n var z;\n var i;\n for (i = 0; i < zlevelList.length; i++) {\n z = zlevelList[i];\n cb.call(context, this._layers[z], z);\n }\n },\n\n // Iterate each buildin layer\n eachBuiltinLayer: function (cb, context) {\n var zlevelList = this._zlevelList;\n var layer;\n var z;\n var i;\n for (i = 0; i < zlevelList.length; i++) {\n z = zlevelList[i];\n layer = this._layers[z];\n if (layer.__builtin__) {\n cb.call(context, layer, z);\n }\n }\n },\n\n // Iterate each other layer except buildin layer\n eachOtherLayer: function (cb, context) {\n var zlevelList = this._zlevelList;\n var layer;\n var z;\n var i;\n for (i = 0; i < zlevelList.length; i++) {\n z = zlevelList[i];\n layer = this._layers[z];\n if (!layer.__builtin__) {\n cb.call(context, layer, z);\n }\n }\n },\n\n /**\n * 获取所有已创建的层\n * @param {Array.<module:zrender/Layer>} [prevLayer]\n */\n getLayers: function () {\n return this._layers;\n },\n\n _updateLayerStatus: function (list) {\n\n var layers = this._layers;\n var progressiveLayers = this._progressiveLayers;\n\n var elCountsLastFrame = {};\n var progressiveElCountsLastFrame = {};\n\n this.eachBuiltinLayer(function (layer, z) {\n elCountsLastFrame[z] = layer.elCount;\n layer.elCount = 0;\n layer.__dirty = false;\n });\n\n util.each(progressiveLayers, function (layer, idx) {\n progressiveElCountsLastFrame[idx] = layer.elCount;\n layer.elCount = 0;\n layer.__dirty = false;\n });\n\n var progressiveLayerCount = 0;\n var currentProgressiveLayer;\n var lastProgressiveKey;\n var frameCount = 0;\n for (var i = 0, l = list.length; i < l; i++) {\n var el = list[i];\n var zlevel = this._singleCanvas ? 0 : el.zlevel;\n var layer = layers[zlevel];\n var elProgress = el.progressive;\n if (layer) {\n layer.elCount++;\n layer.__dirty = layer.__dirty || el.__dirty;\n }\n\n /////// Update progressive\n if (elProgress >= 0) {\n // Fix wrong progressive sequence problem.\n if (lastProgressiveKey !== elProgress) {\n lastProgressiveKey = elProgress;\n frameCount++;\n }\n var elFrame = el.__frame = frameCount - 1;\n if (!currentProgressiveLayer) {\n var idx = Math.min(progressiveLayerCount, MAX_PROGRESSIVE_LAYER_NUMBER - 1);\n currentProgressiveLayer = progressiveLayers[idx];\n if (!currentProgressiveLayer) {\n currentProgressiveLayer = progressiveLayers[idx] = new Layer(\n 'progressive', this, this.dpr\n );\n currentProgressiveLayer.initContext();\n }\n currentProgressiveLayer.__maxProgress = 0;\n }\n currentProgressiveLayer.__dirty = currentProgressiveLayer.__dirty || el.__dirty;\n currentProgressiveLayer.elCount++;\n\n currentProgressiveLayer.__maxProgress = Math.max(\n currentProgressiveLayer.__maxProgress, elFrame\n );\n\n if (currentProgressiveLayer.__maxProgress >= currentProgressiveLayer.__progress) {\n // Should keep rendering this layer because progressive rendering is not finished yet\n layer.__dirty = true;\n }\n }\n else {\n el.__frame = -1;\n\n if (currentProgressiveLayer) {\n currentProgressiveLayer.__nextIdxNotProg = i;\n progressiveLayerCount++;\n currentProgressiveLayer = null;\n }\n }\n }\n\n if (currentProgressiveLayer) {\n progressiveLayerCount++;\n currentProgressiveLayer.__nextIdxNotProg = i;\n }\n\n // 层中的元素数量有发生变化\n this.eachBuiltinLayer(function (layer, z) {\n if (elCountsLastFrame[z] !== layer.elCount) {\n layer.__dirty = true;\n }\n });\n\n progressiveLayers.length = Math.min(progressiveLayerCount, MAX_PROGRESSIVE_LAYER_NUMBER);\n util.each(progressiveLayers, function (layer, idx) {\n if (progressiveElCountsLastFrame[idx] !== layer.elCount) {\n el.__dirty = true;\n }\n if (layer.__dirty) {\n layer.__progress = 0;\n }\n });\n },\n\n /**\n * 清除hover层外所有内容\n */\n clear: function () {\n this.eachBuiltinLayer(this._clearLayer);\n return this;\n },\n\n _clearLayer: function (layer) {\n layer.clear();\n },\n\n /**\n * 修改指定zlevel的绘制参数\n *\n * @param {string} zlevel\n * @param {Object} config 配置对象\n * @param {string} [config.clearColor=0] 每次清空画布的颜色\n * @param {string} [config.motionBlur=false] 是否开启动态模糊\n * @param {number} [config.lastFrameAlpha=0.7]\n * 在开启动态模糊的时候使用,与上一帧混合的alpha值,值越大尾迹越明显\n */\n configLayer: function (zlevel, config) {\n if (config) {\n var layerConfig = this._layerConfig;\n if (!layerConfig[zlevel]) {\n layerConfig[zlevel] = config;\n }\n else {\n util.merge(layerConfig[zlevel], config, true);\n }\n\n var layer = this._layers[zlevel];\n\n if (layer) {\n util.merge(layer, layerConfig[zlevel], true);\n }\n }\n },\n\n /**\n * 删除指定层\n * @param {number} zlevel 层所在的zlevel\n */\n delLayer: function (zlevel) {\n var layers = this._layers;\n var zlevelList = this._zlevelList;\n var layer = layers[zlevel];\n if (!layer) {\n return;\n }\n layer.dom.parentNode.removeChild(layer.dom);\n delete layers[zlevel];\n\n zlevelList.splice(util.indexOf(zlevelList, zlevel), 1);\n },\n\n /**\n * 区域大小变化后重绘\n */\n resize: function (width, height) {\n var domRoot = this._domRoot;\n // FIXME Why ?\n domRoot.style.display = 'none';\n\n // Save input w/h\n var opts = this._opts;\n width != null && (opts.width = width);\n height != null && (opts.height = height);\n\n width = this._getSize(0);\n height = this._getSize(1);\n\n domRoot.style.display = '';\n\n // 优化没有实际改变的resize\n if (this._width != width || height != this._height) {\n domRoot.style.width = width + 'px';\n domRoot.style.height = height + 'px';\n\n for (var id in this._layers) {\n if (this._layers.hasOwnProperty(id)) {\n this._layers[id].resize(width, height);\n }\n }\n util.each(this._progressiveLayers, function (layer) {\n layer.resize(width, height);\n });\n\n this.refresh(true);\n }\n\n this._width = width;\n this._height = height;\n\n return this;\n },\n\n /**\n * 清除单独的一个层\n * @param {number} zlevel\n */\n clearLayer: function (zlevel) {\n var layer = this._layers[zlevel];\n if (layer) {\n layer.clear();\n }\n },\n\n /**\n * 释放\n */\n dispose: function () {\n this.root.innerHTML = '';\n\n this.root =\n this.storage =\n\n this._domRoot =\n this._layers = null;\n },\n\n /**\n * Get canvas which has all thing rendered\n * @param {Object} opts\n * @param {string} [opts.backgroundColor]\n * @param {number} [opts.pixelRatio]\n */\n getRenderedCanvas: function (opts) {\n opts = opts || {};\n if (this._singleCanvas) {\n return this._layers[0].dom;\n }\n\n var imageLayer = new Layer('image', this, opts.pixelRatio || this.dpr);\n imageLayer.initContext();\n\n imageLayer.clearColor = opts.backgroundColor;\n imageLayer.clear();\n\n var displayList = this.storage.getDisplayList(true);\n\n var scope = {};\n var zlevel;\n\n var self = this;\n function findAndDrawOtherLayer(smaller, larger) {\n var zlevelList = self._zlevelList;\n if (smaller == null) {\n smaller = -Infinity;\n }\n var intermediateLayer;\n for (var i = 0; i < zlevelList.length; i++) {\n var z = zlevelList[i];\n var layer = self._layers[z];\n if (!layer.__builtin__ && z > smaller && z < larger) {\n intermediateLayer = layer;\n break;\n }\n }\n if (intermediateLayer && intermediateLayer.renderToCanvas) {\n imageLayer.ctx.save();\n intermediateLayer.renderToCanvas(imageLayer.ctx);\n imageLayer.ctx.restore();\n }\n }\n for (var i = 0; i < displayList.length; i++) {\n var el = displayList[i];\n\n if (el.zlevel !== zlevel) {\n findAndDrawOtherLayer(zlevel, el.zlevel);\n zlevel = el.zlevel;\n }\n this._doPaintEl(el, imageLayer, true, scope);\n }\n\n findAndDrawOtherLayer(zlevel, Infinity);\n\n return imageLayer.dom;\n },\n /**\n * 获取绘图区域宽度\n */\n getWidth: function () {\n return this._width;\n },\n\n /**\n * 获取绘图区域高度\n */\n getHeight: function () {\n return this._height;\n },\n\n _getSize: function (whIdx) {\n var opts = this._opts;\n var wh = ['width', 'height'][whIdx];\n var cwh = ['clientWidth', 'clientHeight'][whIdx];\n var plt = ['paddingLeft', 'paddingTop'][whIdx];\n var prb = ['paddingRight', 'paddingBottom'][whIdx];\n\n if (opts[wh] != null && opts[wh] !== 'auto') {\n return parseFloat(opts[wh]);\n }\n\n var root = this.root;\n // IE8 does not support getComputedStyle, but it use VML.\n var stl = document.defaultView.getComputedStyle(root);\n\n return (\n (root[cwh] || parseInt10(stl[wh]) || parseInt10(root.style[wh]))\n - (parseInt10(stl[plt]) || 0)\n - (parseInt10(stl[prb]) || 0)\n ) | 0;\n },\n\n pathToImage: function (path, dpr) {\n dpr = dpr || this.dpr;\n\n var canvas = document.createElement('canvas');\n var ctx = canvas.getContext('2d');\n var rect = path.getBoundingRect();\n var style = path.style;\n var shadowBlurSize = style.shadowBlur;\n var shadowOffsetX = style.shadowOffsetX;\n var shadowOffsetY = style.shadowOffsetY;\n var lineWidth = style.hasStroke() ? style.lineWidth : 0;\n\n var leftMargin = Math.max(lineWidth / 2, -shadowOffsetX + shadowBlurSize);\n var rightMargin = Math.max(lineWidth / 2, shadowOffsetX + shadowBlurSize);\n var topMargin = Math.max(lineWidth / 2, -shadowOffsetY + shadowBlurSize);\n var bottomMargin = Math.max(lineWidth / 2, shadowOffsetY + shadowBlurSize);\n var width = rect.width + leftMargin + rightMargin;\n var height = rect.height + topMargin + bottomMargin;\n\n canvas.width = width * dpr;\n canvas.height = height * dpr;\n\n ctx.scale(dpr, dpr);\n ctx.clearRect(0, 0, width, height);\n ctx.dpr = dpr;\n\n var pathTransform = {\n position: path.position,\n rotation: path.rotation,\n scale: path.scale\n };\n path.position = [leftMargin - rect.x, topMargin - rect.y];\n path.rotation = 0;\n path.scale = [1, 1];\n path.updateTransform();\n if (path) {\n path.brush(ctx);\n }\n\n var ImageShape = Image;\n var imgShape = new ImageShape({\n style: {\n x: 0,\n y: 0,\n image: canvas\n }\n });\n\n if (pathTransform.position != null) {\n imgShape.position = path.position = pathTransform.position;\n }\n\n if (pathTransform.rotation != null) {\n imgShape.rotation = path.rotation = pathTransform.rotation;\n }\n\n if (pathTransform.scale != null) {\n imgShape.scale = path.scale = pathTransform.scale;\n }\n\n return imgShape;\n }\n};\n\nexport default Painter;","/**\n * 事件辅助类\n * @module zrender/core/event\n * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)\n */\n\nimport Eventful from '../mixin/Eventful';\nimport env from './env';\n\nvar isDomLevel2 = (typeof window !== 'undefined') && !!window.addEventListener;\n\nvar MOUSE_EVENT_REG = /^(?:mouse|pointer|contextmenu|drag|drop)|click/;\n\nfunction getBoundingClientRect(el) {\n // BlackBerry 5, iOS 3 (original iPhone) don't have getBoundingRect\n return el.getBoundingClientRect ? el.getBoundingClientRect() : {left: 0, top: 0};\n}\n\n// `calculate` is optional, default false\nexport function clientToLocal(el, e, out, calculate) {\n out = out || {};\n\n // According to the W3C Working Draft, offsetX and offsetY should be relative\n // to the padding edge of the target element. The only browser using this convention\n // is IE. Webkit uses the border edge, Opera uses the content edge, and FireFox does\n // not support the properties.\n // (see http://www.jacklmoore.com/notes/mouse-position/)\n // In zr painter.dom, padding edge equals to border edge.\n\n // FIXME\n // When mousemove event triggered on ec tooltip, target is not zr painter.dom, and\n // offsetX/Y is relative to e.target, where the calculation of zrX/Y via offsetX/Y\n // is too complex. So css-transfrom dont support in this case temporarily.\n if (calculate || !env.canvasSupported) {\n defaultGetZrXY(el, e, out);\n }\n // Caution: In FireFox, layerX/layerY Mouse position relative to the closest positioned\n // ancestor element, so we should make sure el is positioned (e.g., not position:static).\n // BTW1, Webkit don't return the same results as FF in non-simple cases (like add\n // zoom-factor, overflow / opacity layers, transforms ...)\n // BTW2, (ev.offsetY || ev.pageY - $(ev.target).offset().top) is not correct in preserve-3d.\n // <https://bugs.jquery.com/ticket/8523#comment:14>\n // BTW3, In ff, offsetX/offsetY is always 0.\n else if (env.browser.firefox && e.layerX != null && e.layerX !== e.offsetX) {\n out.zrX = e.layerX;\n out.zrY = e.layerY;\n }\n // For IE6+, chrome, safari, opera. (When will ff support offsetX?)\n else if (e.offsetX != null) {\n out.zrX = e.offsetX;\n out.zrY = e.offsetY;\n }\n // For some other device, e.g., IOS safari.\n else {\n defaultGetZrXY(el, e, out);\n }\n\n return out;\n}\n\nfunction defaultGetZrXY(el, e, out) {\n // This well-known method below does not support css transform.\n var box = getBoundingClientRect(el);\n out.zrX = e.clientX - box.left;\n out.zrY = e.clientY - box.top;\n}\n\n/**\n * 如果存在第三方嵌入的一些dom触发的事件,或touch事件,需要转换一下事件坐标.\n * `calculate` is optional, default false.\n */\nexport function normalizeEvent(el, e, calculate) {\n\n e = e || window.event;\n\n if (e.zrX != null) {\n return e;\n }\n\n var eventType = e.type;\n var isTouch = eventType && eventType.indexOf('touch') >= 0;\n\n if (!isTouch) {\n clientToLocal(el, e, e, calculate);\n e.zrDelta = (e.wheelDelta) ? e.wheelDelta / 120 : -(e.detail || 0) / 3;\n }\n else {\n var touch = eventType != 'touchend'\n ? e.targetTouches[0]\n : e.changedTouches[0];\n touch && clientToLocal(el, touch, e, calculate);\n }\n\n // Add which for click: 1 === left; 2 === middle; 3 === right; otherwise: 0;\n // See jQuery: https://github.com/jquery/jquery/blob/master/src/event.js\n // If e.which has been defined, if may be readonly,\n // see: https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/which\n var button = e.button;\n if (e.which == null && button !== undefined && MOUSE_EVENT_REG.test(e.type)) {\n e.which = (button & 1 ? 1 : (button & 2 ? 3 : (button & 4 ? 2 : 0)));\n }\n\n return e;\n}\n\nexport function addEventListener(el, name, handler) {\n if (isDomLevel2) {\n el.addEventListener(name, handler);\n }\n else {\n el.attachEvent('on' + name, handler);\n }\n}\n\nexport function removeEventListener(el, name, handler) {\n if (isDomLevel2) {\n el.removeEventListener(name, handler);\n }\n else {\n el.detachEvent('on' + name, handler);\n }\n}\n\n/**\n * preventDefault and stopPropagation.\n * Notice: do not do that in zrender. Upper application\n * do that if necessary.\n *\n * @memberOf module:zrender/core/event\n * @method\n * @param {Event} e : event对象\n */\nexport var stop = isDomLevel2\n ? function (e) {\n e.preventDefault();\n e.stopPropagation();\n e.cancelBubble = true;\n }\n : function (e) {\n e.returnValue = false;\n e.cancelBubble = true;\n };\n\nexport function notLeftMouse(e) {\n // If e.which is undefined, considered as left mouse event.\n return e.which > 1;\n}\n\n// 做向上兼容\nexport {Eventful as Dispatcher};\n","/**\n * 动画主类, 调度和管理所有动画控制器\n *\n * @module zrender/animation/Animation\n * @author pissang(https://github.com/pissang)\n */\n// TODO Additive animation\n// http://iosoteric.com/additive-animations-animatewithduration-in-ios-8/\n// https://developer.apple.com/videos/wwdc2014/#236\n\nimport * as util from '../core/util';\nimport {Dispatcher} from '../core/event';\nimport requestAnimationFrame from './requestAnimationFrame';\nimport Animator from './Animator';\n\n/**\n * @typedef {Object} IZRenderStage\n * @property {Function} update\n */\n\n/**\n * @alias module:zrender/animation/Animation\n * @constructor\n * @param {Object} [options]\n * @param {Function} [options.onframe]\n * @param {IZRenderStage} [options.stage]\n * @example\n * var animation = new Animation();\n * var obj = {\n * x: 100,\n * y: 100\n * };\n * animation.animate(node.position)\n * .when(1000, {\n * x: 500,\n * y: 500\n * })\n * .when(2000, {\n * x: 100,\n * y: 100\n * })\n * .start('spline');\n */\nvar Animation = function (options) {\n\n options = options || {};\n\n this.stage = options.stage || {};\n\n this.onframe = options.onframe || function() {};\n\n // private properties\n this._clips = [];\n\n this._running = false;\n\n this._time;\n\n this._pausedTime;\n\n this._pauseStart;\n\n this._paused = false;\n\n Dispatcher.call(this);\n};\n\nAnimation.prototype = {\n\n constructor: Animation,\n /**\n * 添加 clip\n * @param {module:zrender/animation/Clip} clip\n */\n addClip: function (clip) {\n this._clips.push(clip);\n },\n /**\n * 添加 animator\n * @param {module:zrender/animation/Animator} animator\n */\n addAnimator: function (animator) {\n animator.animation = this;\n var clips = animator.getClips();\n for (var i = 0; i < clips.length; i++) {\n this.addClip(clips[i]);\n }\n },\n /**\n * 删除动画片段\n * @param {module:zrender/animation/Clip} clip\n */\n removeClip: function(clip) {\n var idx = util.indexOf(this._clips, clip);\n if (idx >= 0) {\n this._clips.splice(idx, 1);\n }\n },\n\n /**\n * 删除动画片段\n * @param {module:zrender/animation/Animator} animator\n */\n removeAnimator: function (animator) {\n var clips = animator.getClips();\n for (var i = 0; i < clips.length; i++) {\n this.removeClip(clips[i]);\n }\n animator.animation = null;\n },\n\n _update: function() {\n\n var time = new Date().getTime() - this._pausedTime;\n var delta = time - this._time;\n var clips = this._clips;\n var len = clips.length;\n\n var deferredEvents = [];\n var deferredClips = [];\n for (var i = 0; i < len; i++) {\n var clip = clips[i];\n var e = clip.step(time, delta);\n // Throw out the events need to be called after\n // stage.update, like destroy\n if (e) {\n deferredEvents.push(e);\n deferredClips.push(clip);\n }\n }\n\n // Remove the finished clip\n for (var i = 0; i < len;) {\n if (clips[i]._needsRemove) {\n clips[i] = clips[len - 1];\n clips.pop();\n len--;\n }\n else {\n i++;\n }\n }\n\n len = deferredEvents.length;\n for (var i = 0; i < len; i++) {\n deferredClips[i].fire(deferredEvents[i]);\n }\n\n this._time = time;\n\n this.onframe(delta);\n\n this.trigger('frame', delta);\n\n if (this.stage.update) {\n this.stage.update();\n }\n },\n\n _startLoop: function () {\n var self = this;\n\n this._running = true;\n\n function step() {\n if (self._running) {\n\n requestAnimationFrame(step);\n\n !self._paused && self._update();\n }\n }\n\n requestAnimationFrame(step);\n },\n\n /**\n * 开始运行动画\n */\n start: function () {\n\n this._time = new Date().getTime();\n this._pausedTime = 0;\n\n this._startLoop();\n },\n /**\n * 停止运行动画\n */\n stop: function () {\n this._running = false;\n },\n\n /**\n * Pause\n */\n pause: function () {\n if (!this._paused) {\n this._pauseStart = new Date().getTime();\n this._paused = true;\n }\n },\n\n /**\n * Resume\n */\n resume: function () {\n if (this._paused) {\n this._pausedTime += (new Date().getTime()) - this._pauseStart;\n this._paused = false;\n }\n },\n\n /**\n * 清除所有动画片段\n */\n clear: function () {\n this._clips = [];\n },\n /**\n * 对一个目标创建一个animator对象,可以指定目标中的属性使用动画\n * @param {Object} target\n * @param {Object} options\n * @param {boolean} [options.loop=false] 是否循环播放动画\n * @param {Function} [options.getter=null]\n * 如果指定getter函数,会通过getter函数取属性值\n * @param {Function} [options.setter=null]\n * 如果指定setter函数,会通过setter函数设置属性值\n * @return {module:zrender/animation/Animation~Animator}\n */\n // TODO Gap\n animate: function (target, options) {\n options = options || {};\n\n var animator = new Animator(\n target,\n options.loop,\n options.getter,\n options.setter\n );\n\n this.addAnimator(animator);\n\n return animator;\n }\n};\n\nutil.mixin(Animation, Dispatcher);\n\nexport default Animation;","/**\n * Only implements needed gestures for mobile.\n */\n\nimport * as eventUtil from './event';\n\nvar GestureMgr = function () {\n\n /**\n * @private\n * @type {Array.<Object>}\n */\n this._track = [];\n};\n\nGestureMgr.prototype = {\n\n constructor: GestureMgr,\n\n recognize: function (event, target, root) {\n this._doTrack(event, target, root);\n return this._recognize(event);\n },\n\n clear: function () {\n this._track.length = 0;\n return this;\n },\n\n _doTrack: function (event, target, root) {\n var touches = event.touches;\n\n if (!touches) {\n return;\n }\n\n var trackItem = {\n points: [],\n touches: [],\n target: target,\n event: event\n };\n\n for (var i = 0, len = touches.length; i < len; i++) {\n var touch = touches[i];\n var pos = eventUtil.clientToLocal(root, touch, {});\n trackItem.points.push([pos.zrX, pos.zrY]);\n trackItem.touches.push(touch);\n }\n\n this._track.push(trackItem);\n },\n\n _recognize: function (event) {\n for (var eventName in recognizers) {\n if (recognizers.hasOwnProperty(eventName)) {\n var gestureInfo = recognizers[eventName](this._track, event);\n if (gestureInfo) {\n return gestureInfo;\n }\n }\n }\n }\n};\n\nfunction dist(pointPair) {\n var dx = pointPair[1][0] - pointPair[0][0];\n var dy = pointPair[1][1] - pointPair[0][1];\n\n return Math.sqrt(dx * dx + dy * dy);\n}\n\nfunction center(pointPair) {\n return [\n (pointPair[0][0] + pointPair[1][0]) / 2,\n (pointPair[0][1] + pointPair[1][1]) / 2\n ];\n}\n\nvar recognizers = {\n\n pinch: function (track, event) {\n var trackLen = track.length;\n\n if (!trackLen) {\n return;\n }\n\n var pinchEnd = (track[trackLen - 1] || {}).points;\n var pinchPre = (track[trackLen - 2] || {}).points || pinchEnd;\n\n if (pinchPre\n && pinchPre.length > 1\n && pinchEnd\n && pinchEnd.length > 1\n ) {\n var pinchScale = dist(pinchEnd) / dist(pinchPre);\n !isFinite(pinchScale) && (pinchScale = 1);\n\n event.pinchScale = pinchScale;\n\n var pinchCenter = center(pinchEnd);\n event.pinchX = pinchCenter[0];\n event.pinchY = pinchCenter[1];\n\n return {\n type: 'pinch',\n target: track[0].target,\n event: event\n };\n }\n }\n\n // Only pinch currently.\n};\n\nexport default GestureMgr;","\nimport {\n addEventListener,\n removeEventListener,\n normalizeEvent\n} from '../core/event';\nimport * as zrUtil from '../core/util';\nimport Eventful from '../mixin/Eventful';\nimport env from '../core/env';\nimport GestureMgr from '../core/GestureMgr';\n\nvar TOUCH_CLICK_DELAY = 300;\n\nvar mouseHandlerNames = [\n 'click', 'dblclick', 'mousewheel', 'mouseout',\n 'mouseup', 'mousedown', 'mousemove', 'contextmenu'\n];\n\nvar touchHandlerNames = [\n 'touchstart', 'touchend', 'touchmove'\n];\n\nvar pointerEventNames = {\n pointerdown: 1, pointerup: 1, pointermove: 1, pointerout: 1\n};\n\nvar pointerHandlerNames = zrUtil.map(mouseHandlerNames, function (name) {\n var nm = name.replace('mouse', 'pointer');\n return pointerEventNames[nm] ? nm : name;\n});\n\nfunction eventNameFix(name) {\n return (name === 'mousewheel' && env.browser.firefox) ? 'DOMMouseScroll' : name;\n}\n\nfunction processGesture(proxy, event, stage) {\n var gestureMgr = proxy._gestureMgr;\n\n stage === 'start' && gestureMgr.clear();\n\n var gestureInfo = gestureMgr.recognize(\n event,\n proxy.handler.findHover(event.zrX, event.zrY, null).target,\n proxy.dom\n );\n\n stage === 'end' && gestureMgr.clear();\n\n // Do not do any preventDefault here. Upper application do that if necessary.\n if (gestureInfo) {\n var type = gestureInfo.type;\n event.gestureEvent = type;\n\n proxy.handler.dispatchToElement({target: gestureInfo.target}, type, gestureInfo.event);\n }\n}\n\n// function onMSGestureChange(proxy, event) {\n// if (event.translationX || event.translationY) {\n// // mousemove is carried by MSGesture to reduce the sensitivity.\n// proxy.handler.dispatchToElement(event.target, 'mousemove', event);\n// }\n// if (event.scale !== 1) {\n// event.pinchX = event.offsetX;\n// event.pinchY = event.offsetY;\n// event.pinchScale = event.scale;\n// proxy.handler.dispatchToElement(event.target, 'pinch', event);\n// }\n// }\n\n/**\n * Prevent mouse event from being dispatched after Touch Events action\n * @see <https://github.com/deltakosh/handjs/blob/master/src/hand.base.js>\n * 1. Mobile browsers dispatch mouse events 300ms after touchend.\n * 2. Chrome for Android dispatch mousedown for long-touch about 650ms\n * Result: Blocking Mouse Events for 700ms.\n */\nfunction setTouchTimer(instance) {\n instance._touching = true;\n clearTimeout(instance._touchTimer);\n instance._touchTimer = setTimeout(function () {\n instance._touching = false;\n }, 700);\n}\n\n\nvar domHandlers = {\n /**\n * Mouse move handler\n * @inner\n * @param {Event} event\n */\n mousemove: function (event) {\n event = normalizeEvent(this.dom, event);\n\n this.trigger('mousemove', event);\n },\n\n /**\n * Mouse out handler\n * @inner\n * @param {Event} event\n */\n mouseout: function (event) {\n event = normalizeEvent(this.dom, event);\n\n var element = event.toElement || event.relatedTarget;\n if (element != this.dom) {\n while (element && element.nodeType != 9) {\n // 忽略包含在root中的dom引起的mouseOut\n if (element === this.dom) {\n return;\n }\n\n element = element.parentNode;\n }\n }\n\n this.trigger('mouseout', event);\n },\n\n /**\n * Touch开始响应函数\n * @inner\n * @param {Event} event\n */\n touchstart: function (event) {\n // Default mouse behaviour should not be disabled here.\n // For example, page may needs to be slided.\n event = normalizeEvent(this.dom, event);\n\n // Mark touch, which is useful in distinguish touch and\n // mouse event in upper applicatoin.\n event.zrByTouch = true;\n\n this._lastTouchMoment = new Date();\n\n processGesture(this, event, 'start');\n\n // In touch device, trigger `mousemove`(`mouseover`) should\n // be triggered, and must before `mousedown` triggered.\n domHandlers.mousemove.call(this, event);\n\n domHandlers.mousedown.call(this, event);\n\n setTouchTimer(this);\n },\n\n /**\n * Touch移动响应函数\n * @inner\n * @param {Event} event\n */\n touchmove: function (event) {\n\n event = normalizeEvent(this.dom, event);\n\n // Mark touch, which is useful in distinguish touch and\n // mouse event in upper applicatoin.\n event.zrByTouch = true;\n\n processGesture(this, event, 'change');\n\n // Mouse move should always be triggered no matter whether\n // there is gestrue event, because mouse move and pinch may\n // be used at the same time.\n domHandlers.mousemove.call(this, event);\n\n setTouchTimer(this);\n },\n\n /**\n * Touch结束响应函数\n * @inner\n * @param {Event} event\n */\n touchend: function (event) {\n\n event = normalizeEvent(this.dom, event);\n\n // Mark touch, which is useful in distinguish touch and\n // mouse event in upper applicatoin.\n event.zrByTouch = true;\n\n processGesture(this, event, 'end');\n\n domHandlers.mouseup.call(this, event);\n\n // Do not trigger `mouseout` here, in spite of `mousemove`(`mouseover`) is\n // triggered in `touchstart`. This seems to be illogical, but by this mechanism,\n // we can conveniently implement \"hover style\" in both PC and touch device just\n // by listening to `mouseover` to add \"hover style\" and listening to `mouseout`\n // to remove \"hover style\" on an element, without any additional code for\n // compatibility. (`mouseout` will not be triggered in `touchend`, so \"hover\n // style\" will remain for user view)\n\n // click event should always be triggered no matter whether\n // there is gestrue event. System click can not be prevented.\n if (+new Date() - this._lastTouchMoment < TOUCH_CLICK_DELAY) {\n domHandlers.click.call(this, event);\n }\n\n setTouchTimer(this);\n },\n\n pointerdown: function (event) {\n domHandlers.mousedown.call(this, event);\n\n // if (useMSGuesture(this, event)) {\n // this._msGesture.addPointer(event.pointerId);\n // }\n },\n\n pointermove: function (event) {\n // FIXME\n // pointermove is so sensitive that it always triggered when\n // tap(click) on touch screen, which affect some judgement in\n // upper application. So, we dont support mousemove on MS touch\n // device yet.\n if (!isPointerFromTouch(event)) {\n domHandlers.mousemove.call(this, event);\n }\n },\n\n pointerup: function (event) {\n domHandlers.mouseup.call(this, event);\n },\n\n pointerout: function (event) {\n // pointerout will be triggered when tap on touch screen\n // (IE11+/Edge on MS Surface) after click event triggered,\n // which is inconsistent with the mousout behavior we defined\n // in touchend. So we unify them.\n // (check domHandlers.touchend for detailed explanation)\n if (!isPointerFromTouch(event)) {\n domHandlers.mouseout.call(this, event);\n }\n }\n};\n\nfunction isPointerFromTouch(event) {\n var pointerType = event.pointerType;\n return pointerType === 'pen' || pointerType === 'touch';\n}\n\n// function useMSGuesture(handlerProxy, event) {\n// return isPointerFromTouch(event) && !!handlerProxy._msGesture;\n// }\n\n// Common handlers\nzrUtil.each(['click', 'mousedown', 'mouseup', 'mousewheel', 'dblclick', 'contextmenu'], function (name) {\n domHandlers[name] = function (event) {\n event = normalizeEvent(this.dom, event);\n this.trigger(name, event);\n };\n});\n\n/**\n * 为控制类实例初始化dom 事件处理函数\n *\n * @inner\n * @param {module:zrender/Handler} instance 控制类实例\n */\nfunction initDomHandler(instance) {\n zrUtil.each(touchHandlerNames, function (name) {\n instance._handlers[name] = zrUtil.bind(domHandlers[name], instance);\n });\n\n zrUtil.each(pointerHandlerNames, function (name) {\n instance._handlers[name] = zrUtil.bind(domHandlers[name], instance);\n });\n\n zrUtil.each(mouseHandlerNames, function (name) {\n instance._handlers[name] = makeMouseHandler(domHandlers[name], instance);\n });\n\n function makeMouseHandler(fn, instance) {\n return function () {\n if (instance._touching) {\n return;\n }\n return fn.apply(instance, arguments);\n };\n }\n}\n\n\nfunction HandlerDomProxy(dom) {\n Eventful.call(this);\n\n this.dom = dom;\n\n /**\n * @private\n * @type {boolean}\n */\n this._touching = false;\n\n /**\n * @private\n * @type {number}\n */\n this._touchTimer;\n\n /**\n * @private\n * @type {module:zrender/core/GestureMgr}\n */\n this._gestureMgr = new GestureMgr();\n\n this._handlers = {};\n\n initDomHandler(this);\n\n if (env.pointerEventsSupported) { // Only IE11+/Edge\n // 1. On devices that both enable touch and mouse (e.g., MS Surface and lenovo X240),\n // IE11+/Edge do not trigger touch event, but trigger pointer event and mouse event\n // at the same time.\n // 2. On MS Surface, it probablely only trigger mousedown but no mouseup when tap on\n // screen, which do not occurs in pointer event.\n // So we use pointer event to both detect touch gesture and mouse behavior.\n mountHandlers(pointerHandlerNames, this);\n\n // FIXME\n // Note: MS Gesture require CSS touch-action set. But touch-action is not reliable,\n // which does not prevent defuault behavior occasionally (which may cause view port\n // zoomed in but use can not zoom it back). And event.preventDefault() does not work.\n // So we have to not to use MSGesture and not to support touchmove and pinch on MS\n // touch screen. And we only support click behavior on MS touch screen now.\n\n // MS Gesture Event is only supported on IE11+/Edge and on Windows 8+.\n // We dont support touch on IE on win7.\n // See <https://msdn.microsoft.com/en-us/library/dn433243(v=vs.85).aspx>\n // if (typeof MSGesture === 'function') {\n // (this._msGesture = new MSGesture()).target = dom; // jshint ignore:line\n // dom.addEventListener('MSGestureChange', onMSGestureChange);\n // }\n }\n else {\n if (env.touchEventsSupported) {\n mountHandlers(touchHandlerNames, this);\n // Handler of 'mouseout' event is needed in touch mode, which will be mounted below.\n // addEventListener(root, 'mouseout', this._mouseoutHandler);\n }\n\n // 1. Considering some devices that both enable touch and mouse event (like on MS Surface\n // and lenovo X240, @see #2350), we make mouse event be always listened, otherwise\n // mouse event can not be handle in those devices.\n // 2. On MS Surface, Chrome will trigger both touch event and mouse event. How to prevent\n // mouseevent after touch event triggered, see `setTouchTimer`.\n mountHandlers(mouseHandlerNames, this);\n }\n\n function mountHandlers(handlerNames, instance) {\n zrUtil.each(handlerNames, function (name) {\n addEventListener(dom, eventNameFix(name), instance._handlers[name]);\n }, instance);\n }\n}\n\nvar handlerDomProxyProto = HandlerDomProxy.prototype;\nhandlerDomProxyProto.dispose = function () {\n var handlerNames = mouseHandlerNames.concat(touchHandlerNames);\n\n for (var i = 0; i < handlerNames.length; i++) {\n var name = handlerNames[i];\n removeEventListener(this.dom, eventNameFix(name), this._handlers[name]);\n }\n};\n\nhandlerDomProxyProto.setCursor = function (cursorStyle) {\n this.dom.style.cursor = cursorStyle || 'default';\n};\n\nzrUtil.mixin(HandlerDomProxy, Eventful);\n\nexport default HandlerDomProxy;","/*!\n* ZRender, a high performance 2d drawing library.\n*\n* Copyright (c) 2013, Baidu Inc.\n* All rights reserved.\n*\n* LICENSE\n* https://github.com/ecomfe/zrender/blob/master/LICENSE.txt\n*/\n\nimport guid from './core/guid';\nimport env from './core/env';\nimport * as zrUtil from './core/util';\nimport Handler from './Handler';\nimport Storage from './Storage';\nimport Painter from './Painter';\nimport Animation from './animation/Animation';\nimport HandlerProxy from './dom/HandlerProxy';\n\nvar useVML = !env.canvasSupported;\n\nvar painterCtors = {\n canvas: Painter\n};\n\nvar instances = {}; // ZRender实例map索引\n\n/**\n * @type {string}\n */\nexport var version = '3.7.3';\n\n/**\n * Initializing a zrender instance\n * @param {HTMLElement} dom\n * @param {Object} opts\n * @param {string} [opts.renderer='canvas'] 'canvas' or 'svg'\n * @param {number} [opts.devicePixelRatio]\n * @param {number|string} [opts.width] Can be 'auto' (the same as null/undefined)\n * @param {number|string} [opts.height] Can be 'auto' (the same as null/undefined)\n * @return {module:zrender/ZRender}\n */\nexport function init(dom, opts) {\n var zr = new ZRender(guid(), dom, opts);\n instances[zr.id] = zr;\n return zr;\n}\n\n/**\n * Dispose zrender instance\n * @param {module:zrender/ZRender} zr\n */\nexport function dispose(zr) {\n if (zr) {\n zr.dispose();\n }\n else {\n for (var key in instances) {\n if (instances.hasOwnProperty(key)) {\n instances[key].dispose();\n }\n }\n instances = {};\n }\n\n return this;\n}\n\n/**\n * Get zrender instance by id\n * @param {string} id zrender instance id\n * @return {module:zrender/ZRender}\n */\nexport function getInstance(id) {\n return instances[id];\n}\n\nexport function registerPainter(name, Ctor) {\n painterCtors[name] = Ctor;\n}\n\nfunction delInstance(id) {\n delete instances[id];\n}\n\n/**\n * @module zrender/ZRender\n */\n/**\n * @constructor\n * @alias module:zrender/ZRender\n * @param {string} id\n * @param {HTMLElement} dom\n * @param {Object} opts\n * @param {string} [opts.renderer='canvas'] 'canvas' or 'svg'\n * @param {number} [opts.devicePixelRatio]\n * @param {number} [opts.width] Can be 'auto' (the same as null/undefined)\n * @param {number} [opts.height] Can be 'auto' (the same as null/undefined)\n */\nvar ZRender = function (id, dom, opts) {\n\n opts = opts || {};\n\n /**\n * @type {HTMLDomElement}\n */\n this.dom = dom;\n\n /**\n * @type {string}\n */\n this.id = id;\n\n var self = this;\n var storage = new Storage();\n\n var rendererType = opts.renderer;\n // TODO WebGL\n if (useVML) {\n if (!painterCtors.vml) {\n throw new Error('You need to require \\'zrender/vml/vml\\' to support IE8');\n }\n rendererType = 'vml';\n }\n else if (!rendererType || !painterCtors[rendererType]) {\n rendererType = 'canvas';\n }\n var painter = new painterCtors[rendererType](dom, storage, opts);\n\n this.storage = storage;\n this.painter = painter;\n\n var handerProxy = !env.node ? new HandlerProxy(painter.getViewportRoot()) : null;\n this.handler = new Handler(storage, painter, handerProxy, painter.root);\n\n /**\n * @type {module:zrender/animation/Animation}\n */\n this.animation = new Animation({\n stage: {\n update: zrUtil.bind(this.flush, this)\n }\n });\n this.animation.start();\n\n /**\n * @type {boolean}\n * @private\n */\n this._needsRefresh;\n\n // 修改 storage.delFromStorage, 每次删除元素之前删除动画\n // FIXME 有点ugly\n var oldDelFromStorage = storage.delFromStorage;\n var oldAddToStorage = storage.addToStorage;\n\n storage.delFromStorage = function (el) {\n oldDelFromStorage.call(storage, el);\n\n el && el.removeSelfFromZr(self);\n };\n\n storage.addToStorage = function (el) {\n oldAddToStorage.call(storage, el);\n\n el.addSelfToZr(self);\n };\n};\n\nZRender.prototype = {\n\n constructor: ZRender,\n /**\n * 获取实例唯一标识\n * @return {string}\n */\n getId: function () {\n return this.id;\n },\n\n /**\n * 添加元素\n * @param {module:zrender/Element} el\n */\n add: function (el) {\n this.storage.addRoot(el);\n this._needsRefresh = true;\n },\n\n /**\n * 删除元素\n * @param {module:zrender/Element} el\n */\n remove: function (el) {\n this.storage.delRoot(el);\n this._needsRefresh = true;\n },\n\n /**\n * Change configuration of layer\n * @param {string} zLevel\n * @param {Object} config\n * @param {string} [config.clearColor=0] Clear color\n * @param {string} [config.motionBlur=false] If enable motion blur\n * @param {number} [config.lastFrameAlpha=0.7] Motion blur factor. Larger value cause longer trailer\n */\n configLayer: function (zLevel, config) {\n this.painter.configLayer(zLevel, config);\n this._needsRefresh = true;\n },\n\n /**\n * Repaint the canvas immediately\n */\n refreshImmediately: function () {\n // var start = new Date();\n // Clear needsRefresh ahead to avoid something wrong happens in refresh\n // Or it will cause zrender refreshes again and again.\n this._needsRefresh = false;\n this.painter.refresh();\n /**\n * Avoid trigger zr.refresh in Element#beforeUpdate hook\n */\n this._needsRefresh = false;\n // var end = new Date();\n\n // var log = document.getElementById('log');\n // if (log) {\n // log.innerHTML = log.innerHTML + '<br>' + (end - start);\n // }\n },\n\n /**\n * Mark and repaint the canvas in the next frame of browser\n */\n refresh: function() {\n this._needsRefresh = true;\n },\n\n /**\n * Perform all refresh\n */\n flush: function () {\n if (this._needsRefresh) {\n this.refreshImmediately();\n }\n if (this._needsRefreshHover) {\n this.refreshHoverImmediately();\n }\n },\n\n /**\n * Add element to hover layer\n * @param {module:zrender/Element} el\n * @param {Object} style\n */\n addHover: function (el, style) {\n if (this.painter.addHover) {\n this.painter.addHover(el, style);\n this.refreshHover();\n }\n },\n\n /**\n * Add element from hover layer\n * @param {module:zrender/Element} el\n */\n removeHover: function (el) {\n if (this.painter.removeHover) {\n this.painter.removeHover(el);\n this.refreshHover();\n }\n },\n\n /**\n * Clear all hover elements in hover layer\n * @param {module:zrender/Element} el\n */\n clearHover: function () {\n if (this.painter.clearHover) {\n this.painter.clearHover();\n this.refreshHover();\n }\n },\n\n /**\n * Refresh hover in next frame\n */\n refreshHover: function () {\n this._needsRefreshHover = true;\n },\n\n /**\n * Refresh hover immediately\n */\n refreshHoverImmediately: function () {\n this._needsRefreshHover = false;\n this.painter.refreshHover && this.painter.refreshHover();\n },\n\n /**\n * Resize the canvas.\n * Should be invoked when container size is changed\n * @param {Object} [opts]\n * @param {number|string} [opts.width] Can be 'auto' (the same as null/undefined)\n * @param {number|string} [opts.height] Can be 'auto' (the same as null/undefined)\n */\n resize: function(opts) {\n opts = opts || {};\n this.painter.resize(opts.width, opts.height);\n this.handler.resize();\n },\n\n /**\n * Stop and clear all animation immediately\n */\n clearAnimation: function () {\n this.animation.clear();\n },\n\n /**\n * Get container width\n */\n getWidth: function() {\n return this.painter.getWidth();\n },\n\n /**\n * Get container height\n */\n getHeight: function() {\n return this.painter.getHeight();\n },\n\n /**\n * Export the canvas as Base64 URL\n * @param {string} type\n * @param {string} [backgroundColor='#fff']\n * @return {string} Base64 URL\n */\n // toDataURL: function(type, backgroundColor) {\n // return this.painter.getRenderedCanvas({\n // backgroundColor: backgroundColor\n // }).toDataURL(type);\n // },\n\n /**\n * Converting a path to image.\n * It has much better performance of drawing image rather than drawing a vector path.\n * @param {module:zrender/graphic/Path} e\n * @param {number} width\n * @param {number} height\n */\n pathToImage: function(e, dpr) {\n return this.painter.pathToImage(e, dpr);\n },\n\n /**\n * Set default cursor\n * @param {string} [cursorStyle='default'] 例如 crosshair\n */\n setCursorStyle: function (cursorStyle) {\n this.handler.setCursorStyle(cursorStyle);\n },\n\n /**\n * Find hovered element\n * @param {number} x\n * @param {number} y\n * @return {Object} {target, topTarget}\n */\n findHover: function (x, y) {\n return this.handler.findHover(x, y);\n },\n\n /**\n * Bind event\n *\n * @param {string} eventName Event name\n * @param {Function} eventHandler Handler function\n * @param {Object} [context] Context object\n */\n on: function(eventName, eventHandler, context) {\n this.handler.on(eventName, eventHandler, context);\n },\n\n /**\n * Unbind event\n * @param {string} eventName Event name\n * @param {Function} [eventHandler] Handler function\n */\n off: function(eventName, eventHandler) {\n this.handler.off(eventName, eventHandler);\n },\n\n /**\n * Trigger event manually\n *\n * @param {string} eventName Event name\n * @param {event=} event Event object\n */\n trigger: function (eventName, event) {\n this.handler.trigger(eventName, event);\n },\n\n\n /**\n * Clear all objects and the canvas.\n */\n clear: function () {\n this.storage.delRoot();\n this.painter.clear();\n },\n\n /**\n * Dispose self.\n */\n dispose: function () {\n this.animation.stop();\n\n this.clear();\n this.storage.dispose();\n this.painter.dispose();\n this.handler.dispose();\n\n this.animation =\n this.storage =\n this.painter =\n this.handler = null;\n\n delInstance(this.id);\n }\n};\n\n","import * as zrUtil from 'zrender/src/core/util';\n\nvar RADIAN_EPSILON = 1e-4;\n\nfunction _trim(str) {\n return str.replace(/^\\s+/, '').replace(/\\s+$/, '');\n}\n\n/**\n * Linear mapping a value from domain to range\n * @memberOf module:echarts/util/number\n * @param {(number|Array.<number>)} val\n * @param {Array.<number>} domain Domain extent domain[0] can be bigger than domain[1]\n * @param {Array.<number>} range Range extent range[0] can be bigger than range[1]\n * @param {boolean} clamp\n * @return {(number|Array.<number>}\n */\nexport function linearMap(val, domain, range, clamp) {\n var subDomain = domain[1] - domain[0];\n var subRange = range[1] - range[0];\n\n if (subDomain === 0) {\n return subRange === 0\n ? range[0]\n : (range[0] + range[1]) / 2;\n }\n\n // Avoid accuracy problem in edge, such as\n // 146.39 - 62.83 === 83.55999999999999.\n // See echarts/test/ut/spec/util/number.js#linearMap#accuracyError\n // It is a little verbose for efficiency considering this method\n // is a hotspot.\n if (clamp) {\n if (subDomain > 0) {\n if (val <= domain[0]) {\n return range[0];\n }\n else if (val >= domain[1]) {\n return range[1];\n }\n }\n else {\n if (val >= domain[0]) {\n return range[0];\n }\n else if (val <= domain[1]) {\n return range[1];\n }\n }\n }\n else {\n if (val === domain[0]) {\n return range[0];\n }\n if (val === domain[1]) {\n return range[1];\n }\n }\n\n return (val - domain[0]) / subDomain * subRange + range[0];\n}\n\n/**\n * Convert a percent string to absolute number.\n * Returns NaN if percent is not a valid string or number\n * @memberOf module:echarts/util/number\n * @param {string|number} percent\n * @param {number} all\n * @return {number}\n */\nexport function parsePercent(percent, all) {\n switch (percent) {\n case 'center':\n case 'middle':\n percent = '50%';\n break;\n case 'left':\n case 'top':\n percent = '0%';\n break;\n case 'right':\n case 'bottom':\n percent = '100%';\n break;\n }\n if (typeof percent === 'string') {\n if (_trim(percent).match(/%$/)) {\n return parseFloat(percent) / 100 * all;\n }\n\n return parseFloat(percent);\n }\n\n return percent == null ? NaN : +percent;\n}\n\n/**\n * (1) Fix rounding error of float numbers.\n * (2) Support return string to avoid scientific notation like '3.5e-7'.\n *\n * @param {number} x\n * @param {number} [precision]\n * @param {boolean} [returnStr]\n * @return {number|string}\n */\nexport function round(x, precision, returnStr) {\n if (precision == null) {\n precision = 10;\n }\n // Avoid range error\n precision = Math.min(Math.max(0, precision), 20);\n x = (+x).toFixed(precision);\n return returnStr ? x : +x;\n}\n\nexport function asc(arr) {\n arr.sort(function (a, b) {\n return a - b;\n });\n return arr;\n}\n\n/**\n * Get precision\n * @param {number} val\n */\nexport function getPrecision(val) {\n val = +val;\n if (isNaN(val)) {\n return 0;\n }\n // It is much faster than methods converting number to string as follows\n // var tmp = val.toString();\n // return tmp.length - 1 - tmp.indexOf('.');\n // especially when precision is low\n var e = 1;\n var count = 0;\n while (Math.round(val * e) / e !== val) {\n e *= 10;\n count++;\n }\n return count;\n}\n\n/**\n * @param {string|number} val\n * @return {number}\n */\nexport function getPrecisionSafe(val) {\n var str = val.toString();\n\n // Consider scientific notation: '3.4e-12' '3.4e+12'\n var eIndex = str.indexOf('e');\n if (eIndex > 0) {\n var precision = +str.slice(eIndex + 1);\n return precision < 0 ? -precision : 0;\n }\n else {\n var dotIndex = str.indexOf('.');\n return dotIndex < 0 ? 0 : str.length - 1 - dotIndex;\n }\n}\n\n/**\n * Minimal dicernible data precisioin according to a single pixel.\n *\n * @param {Array.<number>} dataExtent\n * @param {Array.<number>} pixelExtent\n * @return {number} precision\n */\nexport function getPixelPrecision(dataExtent, pixelExtent) {\n var log = Math.log;\n var LN10 = Math.LN10;\n var dataQuantity = Math.floor(log(dataExtent[1] - dataExtent[0]) / LN10);\n var sizeQuantity = Math.round(log(Math.abs(pixelExtent[1] - pixelExtent[0])) / LN10);\n // toFixed() digits argument must be between 0 and 20.\n var precision = Math.min(Math.max(-dataQuantity + sizeQuantity, 0), 20);\n return !isFinite(precision) ? 20 : precision;\n}\n\n/**\n * Get a data of given precision, assuring the sum of percentages\n * in valueList is 1.\n * The largest remainer method is used.\n * https://en.wikipedia.org/wiki/Largest_remainder_method\n *\n * @param {Array.<number>} valueList a list of all data\n * @param {number} idx index of the data to be processed in valueList\n * @param {number} precision integer number showing digits of precision\n * @return {number} percent ranging from 0 to 100\n */\nexport function getPercentWithPrecision(valueList, idx, precision) {\n if (!valueList[idx]) {\n return 0;\n }\n\n var sum = zrUtil.reduce(valueList, function (acc, val) {\n return acc + (isNaN(val) ? 0 : val);\n }, 0);\n if (sum === 0) {\n return 0;\n }\n\n var digits = Math.pow(10, precision);\n var votesPerQuota = zrUtil.map(valueList, function (val) {\n return (isNaN(val) ? 0 : val) / sum * digits * 100;\n });\n var targetSeats = digits * 100;\n\n var seats = zrUtil.map(votesPerQuota, function (votes) {\n // Assign automatic seats.\n return Math.floor(votes);\n });\n var currentSum = zrUtil.reduce(seats, function (acc, val) {\n return acc + val;\n }, 0);\n\n var remainder = zrUtil.map(votesPerQuota, function (votes, idx) {\n return votes - seats[idx];\n });\n\n // Has remainding votes.\n while (currentSum < targetSeats) {\n // Find next largest remainder.\n var max = Number.NEGATIVE_INFINITY;\n var maxId = null;\n for (var i = 0, len = remainder.length; i < len; ++i) {\n if (remainder[i] > max) {\n max = remainder[i];\n maxId = i;\n }\n }\n\n // Add a vote to max remainder.\n ++seats[maxId];\n remainder[maxId] = 0;\n ++currentSum;\n }\n\n return seats[idx] / digits;\n}\n\n// Number.MAX_SAFE_INTEGER, ie do not support.\nexport var MAX_SAFE_INTEGER = 9007199254740991;\n\n/**\n * To 0 - 2 * PI, considering negative radian.\n * @param {number} radian\n * @return {number}\n */\nexport function remRadian(radian) {\n var pi2 = Math.PI * 2;\n return (radian % pi2 + pi2) % pi2;\n}\n\n/**\n * @param {type} radian\n * @return {boolean}\n */\nexport function isRadianAroundZero(val) {\n return val > -RADIAN_EPSILON && val < RADIAN_EPSILON;\n}\n\nvar TIME_REG = /^(?:(\\d{4})(?:[-\\/](\\d{1,2})(?:[-\\/](\\d{1,2})(?:[T ](\\d{1,2})(?::(\\d\\d)(?::(\\d\\d)(?:[.,](\\d+))?)?)?(Z|[\\+\\-]\\d\\d:?\\d\\d)?)?)?)?)?$/; // jshint ignore:line\n\n/**\n * @param {string|Date|number} value These values can be accepted:\n * + An instance of Date, represent a time in its own time zone.\n * + Or string in a subset of ISO 8601, only including:\n * + only year, month, date: '2012-03', '2012-03-01', '2012-03-01 05', '2012-03-01 05:06',\n * + separated with T or space: '2012-03-01T12:22:33.123', '2012-03-01 12:22:33.123',\n * + time zone: '2012-03-01T12:22:33Z', '2012-03-01T12:22:33+8000', '2012-03-01T12:22:33-05:00',\n * all of which will be treated as local time if time zone is not specified\n * (see <https://momentjs.com/>).\n * + Or other string format, including (all of which will be treated as loacal time):\n * '2012', '2012-3-1', '2012/3/1', '2012/03/01',\n * '2009/6/12 2:00', '2009/6/12 2:05:08', '2009/6/12 2:05:08.123'\n * + a timestamp, which represent a time in UTC.\n * @return {Date} date\n */\nexport function parseDate(value) {\n if (value instanceof Date) {\n return value;\n }\n else if (typeof value === 'string') {\n // Different browsers parse date in different way, so we parse it manually.\n // Some other issues:\n // new Date('1970-01-01') is UTC,\n // new Date('1970/01/01') and new Date('1970-1-01') is local.\n // See issue #3623\n var match = TIME_REG.exec(value);\n\n if (!match) {\n // return Invalid Date.\n return new Date(NaN);\n }\n\n // Use local time when no timezone offset specifed.\n if (!match[8]) {\n // match[n] can only be string or undefined.\n // But take care of '12' + 1 => '121'.\n return new Date(\n +match[1],\n +(match[2] || 1) - 1,\n +match[3] || 1,\n +match[4] || 0,\n +(match[5] || 0),\n +match[6] || 0,\n +match[7] || 0\n );\n }\n // Timezoneoffset of Javascript Date has considered DST (Daylight Saving Time,\n // https://tc39.github.io/ecma262/#sec-daylight-saving-time-adjustment).\n // For example, system timezone is set as \"Time Zone: America/Toronto\",\n // then these code will get different result:\n // `new Date(1478411999999).getTimezoneOffset(); // get 240`\n // `new Date(1478412000000).getTimezoneOffset(); // get 300`\n // So we should not use `new Date`, but use `Date.UTC`.\n else {\n var hour = +match[4] || 0;\n if (match[8].toUpperCase() !== 'Z') {\n hour -= match[8].slice(0, 3);\n }\n return new Date(Date.UTC(\n +match[1],\n +(match[2] || 1) - 1,\n +match[3] || 1,\n hour,\n +(match[5] || 0),\n +match[6] || 0,\n +match[7] || 0\n ));\n }\n }\n else if (value == null) {\n return new Date(NaN);\n }\n\n return new Date(Math.round(value));\n}\n\n/**\n * Quantity of a number. e.g. 0.1, 1, 10, 100\n *\n * @param {number} val\n * @return {number}\n */\nexport function quantity(val) {\n return Math.pow(10, quantityExponent(val));\n}\n\nfunction quantityExponent(val) {\n return Math.floor(Math.log(val) / Math.LN10);\n}\n\n/**\n * find a “nice” number approximately equal to x. Round the number if round = true,\n * take ceiling if round = false. The primary observation is that the “nicest”\n * numbers in decimal are 1, 2, and 5, and all power-of-ten multiples of these numbers.\n *\n * See \"Nice Numbers for Graph Labels\" of Graphic Gems.\n *\n * @param {number} val Non-negative value.\n * @param {boolean} round\n * @return {number}\n */\nexport function nice(val, round) {\n var exponent = quantityExponent(val);\n var exp10 = Math.pow(10, exponent);\n var f = val / exp10; // 1 <= f < 10\n var nf;\n if (round) {\n if (f < 1.5) { nf = 1; }\n else if (f < 2.5) { nf = 2; }\n else if (f < 4) { nf = 3; }\n else if (f < 7) { nf = 5; }\n else { nf = 10; }\n }\n else {\n if (f < 1) { nf = 1; }\n else if (f < 2) { nf = 2; }\n else if (f < 3) { nf = 3; }\n else if (f < 5) { nf = 5; }\n else { nf = 10; }\n }\n val = nf * exp10;\n\n // Fix 3 * 0.1 === 0.30000000000000004 issue (see IEEE 754).\n // 20 is the uppper bound of toFixed.\n return exponent >= -20 ? +val.toFixed(exponent < 0 ? -exponent : 0) : val;\n}\n\n/**\n * Order intervals asc, and split them when overlap.\n * expect(numberUtil.reformIntervals([\n * {interval: [18, 62], close: [1, 1]},\n * {interval: [-Infinity, -70], close: [0, 0]},\n * {interval: [-70, -26], close: [1, 1]},\n * {interval: [-26, 18], close: [1, 1]},\n * {interval: [62, 150], close: [1, 1]},\n * {interval: [106, 150], close: [1, 1]},\n * {interval: [150, Infinity], close: [0, 0]}\n * ])).toEqual([\n * {interval: [-Infinity, -70], close: [0, 0]},\n * {interval: [-70, -26], close: [1, 1]},\n * {interval: [-26, 18], close: [0, 1]},\n * {interval: [18, 62], close: [0, 1]},\n * {interval: [62, 150], close: [0, 1]},\n * {interval: [150, Infinity], close: [0, 0]}\n * ]);\n * @param {Array.<Object>} list, where `close` mean open or close\n * of the interval, and Infinity can be used.\n * @return {Array.<Object>} The origin list, which has been reformed.\n */\nexport function reformIntervals(list) {\n list.sort(function (a, b) {\n return littleThan(a, b, 0) ? -1 : 1;\n });\n\n var curr = -Infinity;\n var currClose = 1;\n for (var i = 0; i < list.length;) {\n var interval = list[i].interval;\n var close = list[i].close;\n\n for (var lg = 0; lg < 2; lg++) {\n if (interval[lg] <= curr) {\n interval[lg] = curr;\n close[lg] = !lg ? 1 - currClose : 1;\n }\n curr = interval[lg];\n currClose = close[lg];\n }\n\n if (interval[0] === interval[1] && close[0] * close[1] !== 1) {\n list.splice(i, 1);\n }\n else {\n i++;\n }\n }\n\n return list;\n\n function littleThan(a, b, lg) {\n return a.interval[lg] < b.interval[lg]\n || (\n a.interval[lg] === b.interval[lg]\n && (\n (a.close[lg] - b.close[lg] === (!lg ? 1 : -1))\n || (!lg && littleThan(a, b, 1))\n )\n );\n }\n}\n\n/**\n * parseFloat NaNs numeric-cast false positives (null|true|false|\"\")\n * ...but misinterprets leading-number strings, particularly hex literals (\"0x...\")\n * subtraction forces infinities to NaN\n *\n * @param {*} v\n * @return {boolean}\n */\nexport function isNumeric(v) {\n return v - parseFloat(v) >= 0;\n}\n","import * as zrUtil from 'zrender/src/core/util';\nimport * as textContain from 'zrender/src/contain/text';\nimport * as numberUtil from './number';\n\n/**\n * 每三位默认加,格式化\n * @param {string|number} x\n * @return {string}\n */\nexport function addCommas(x) {\n if (isNaN(x)) {\n return '-';\n }\n x = (x + '').split('.');\n return x[0].replace(/(\\d{1,3})(?=(?:\\d{3})+(?!\\d))/g,'$1,')\n + (x.length > 1 ? ('.' + x[1]) : '');\n}\n\n/**\n * @param {string} str\n * @param {boolean} [upperCaseFirst=false]\n * @return {string} str\n */\nexport function toCamelCase(str, upperCaseFirst) {\n str = (str || '').toLowerCase().replace(/-(.)/g, function(match, group1) {\n return group1.toUpperCase();\n });\n\n if (upperCaseFirst && str) {\n str = str.charAt(0).toUpperCase() + str.slice(1);\n }\n\n return str;\n}\n\nexport var normalizeCssArray = zrUtil.normalizeCssArray;\n\nexport function encodeHTML(source) {\n return String(source)\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\nvar TPL_VAR_ALIAS = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];\n\nvar wrapVar = function (varName, seriesIdx) {\n return '{' + varName + (seriesIdx == null ? '' : seriesIdx) + '}';\n};\n\n/**\n * Template formatter\n * @param {string} tpl\n * @param {Array.<Object>|Object} paramsList\n * @param {boolean} [encode=false]\n * @return {string}\n */\nexport function formatTpl(tpl, paramsList, encode) {\n if (!zrUtil.isArray(paramsList)) {\n paramsList = [paramsList];\n }\n var seriesLen = paramsList.length;\n if (!seriesLen) {\n return '';\n }\n\n var $vars = paramsList[0].$vars || [];\n for (var i = 0; i < $vars.length; i++) {\n var alias = TPL_VAR_ALIAS[i];\n var val = wrapVar(alias, 0);\n tpl = tpl.replace(wrapVar(alias), encode ? encodeHTML(val) : val);\n }\n for (var seriesIdx = 0; seriesIdx < seriesLen; seriesIdx++) {\n for (var k = 0; k < $vars.length; k++) {\n var val = paramsList[seriesIdx][$vars[k]];\n tpl = tpl.replace(\n wrapVar(TPL_VAR_ALIAS[k], seriesIdx),\n encode ? encodeHTML(val) : val\n );\n }\n }\n\n return tpl;\n}\n\n/**\n * simple Template formatter\n *\n * @param {string} tpl\n * @param {Object} param\n * @param {boolean} [encode=false]\n * @return {string}\n */\nexport function formatTplSimple(tpl, param, encode) {\n zrUtil.each(param, function (value, key) {\n tpl = tpl.replace(\n '{' + key + '}',\n encode ? encodeHTML(value) : value\n );\n });\n return tpl;\n}\n\n/**\n * @param {string} color\n * @param {string} [extraCssText]\n * @return {string}\n */\nexport function getTooltipMarker(color, extraCssText) {\n return color\n ? '<span style=\"display:inline-block;margin-right:5px;'\n + 'border-radius:10px;width:9px;height:9px;background-color:'\n + encodeHTML(color) + ';' + (extraCssText || '') + '\"></span>'\n : '';\n}\n\n/**\n * @param {string} str\n * @return {string}\n * @inner\n */\nvar s2d = function (str) {\n return str < 10 ? ('0' + str) : str;\n};\n\n/**\n * ISO Date format\n * @param {string} tpl\n * @param {number} value\n * @param {boolean} [isUTC=false] Default in local time.\n * see `module:echarts/scale/Time`\n * and `module:echarts/util/number#parseDate`.\n * @inner\n */\nexport function formatTime(tpl, value, isUTC) {\n if (tpl === 'week'\n || tpl === 'month'\n || tpl === 'quarter'\n || tpl === 'half-year'\n || tpl === 'year'\n ) {\n tpl = 'MM-dd\\nyyyy';\n }\n\n var date = numberUtil.parseDate(value);\n var utc = isUTC ? 'UTC' : '';\n var y = date['get' + utc + 'FullYear']();\n var M = date['get' + utc + 'Month']() + 1;\n var d = date['get' + utc + 'Date']();\n var h = date['get' + utc + 'Hours']();\n var m = date['get' + utc + 'Minutes']();\n var s = date['get' + utc + 'Seconds']();\n\n tpl = tpl.replace('MM', s2d(M))\n .replace('M', M)\n .replace('yyyy', y)\n .replace('yy', y % 100)\n .replace('dd', s2d(d))\n .replace('d', d)\n .replace('hh', s2d(h))\n .replace('h', h)\n .replace('mm', s2d(m))\n .replace('m', m)\n .replace('ss', s2d(s))\n .replace('s', s);\n\n return tpl;\n}\n\n/**\n * Capital first\n * @param {string} str\n * @return {string}\n */\nexport function capitalFirst(str) {\n return str ? str.charAt(0).toUpperCase() + str.substr(1) : str;\n}\n\nexport var truncateText = textContain.truncateText;\n\nexport var getTextRect = textContain.getBoundingRect;\n","import {__DEV__} from '../config';\nimport * as zrUtil from 'zrender/src/core/util';\n\nvar TYPE_DELIMITER = '.';\nvar IS_CONTAINER = '___EC__COMPONENT__CONTAINER___';\nvar MEMBER_PRIFIX = '\\0ec_\\0';\n\n/**\n * Hide private class member.\n * The same behavior as `host[name] = value;` (can be right-value)\n * @public\n */\nexport function set(host, name, value) {\n return (host[MEMBER_PRIFIX + name] = value);\n}\n\n/**\n * Hide private class member.\n * The same behavior as `host[name];`\n * @public\n */\nexport function get(host, name) {\n return host[MEMBER_PRIFIX + name];\n}\n\n/**\n * For hidden private class member.\n * The same behavior as `host.hasOwnProperty(name);`\n * @public\n */\nexport function hasOwn(host, name) {\n return host.hasOwnProperty(MEMBER_PRIFIX + name);\n}\n\n/**\n * Notice, parseClassType('') should returns {main: '', sub: ''}\n * @public\n */\nexport function parseClassType(componentType) {\n var ret = {main: '', sub: ''};\n if (componentType) {\n componentType = componentType.split(TYPE_DELIMITER);\n ret.main = componentType[0] || '';\n ret.sub = componentType[1] || '';\n }\n return ret;\n}\n\n/**\n * @public\n */\nfunction checkClassType(componentType) {\n zrUtil.assert(\n /^[a-zA-Z0-9_]+([.][a-zA-Z0-9_]+)?$/.test(componentType),\n 'componentType \"' + componentType + '\" illegal'\n );\n}\n\n/**\n * @public\n */\nexport function enableClassExtend(RootClass, mandatoryMethods) {\n\n RootClass.$constructor = RootClass;\n RootClass.extend = function (proto) {\n\n if (__DEV__) {\n zrUtil.each(mandatoryMethods, function (method) {\n if (!proto[method]) {\n console.warn(\n 'Method `' + method + '` should be implemented'\n + (proto.type ? ' in ' + proto.type : '') + '.'\n );\n }\n });\n }\n\n var superClass = this;\n var ExtendedClass = function () {\n if (!proto.$constructor) {\n superClass.apply(this, arguments);\n }\n else {\n proto.$constructor.apply(this, arguments);\n }\n };\n\n zrUtil.extend(ExtendedClass.prototype, proto);\n\n ExtendedClass.extend = this.extend;\n ExtendedClass.superCall = superCall;\n ExtendedClass.superApply = superApply;\n zrUtil.inherits(ExtendedClass, this);\n ExtendedClass.superClass = superClass;\n\n return ExtendedClass;\n };\n}\n\n// superCall should have class info, which can not be fetch from 'this'.\n// Consider this case:\n// class A has method f,\n// class B inherits class A, overrides method f, f call superApply('f'),\n// class C inherits class B, do not overrides method f,\n// then when method of class C is called, dead loop occured.\nfunction superCall(context, methodName) {\n var args = zrUtil.slice(arguments, 2);\n return this.superClass.prototype[methodName].apply(context, args);\n}\n\nfunction superApply(context, methodName, args) {\n return this.superClass.prototype[methodName].apply(context, args);\n}\n\n/**\n * @param {Object} entity\n * @param {Object} options\n * @param {boolean} [options.registerWhenExtend]\n * @public\n */\nexport function enableClassManagement(entity, options) {\n options = options || {};\n\n /**\n * Component model classes\n * key: componentType,\n * value:\n * componentClass, when componentType is 'xxx'\n * or Object.<subKey, componentClass>, when componentType is 'xxx.yy'\n * @type {Object}\n */\n var storage = {};\n\n entity.registerClass = function (Clazz, componentType) {\n if (componentType) {\n checkClassType(componentType);\n componentType = parseClassType(componentType);\n\n if (!componentType.sub) {\n if (__DEV__) {\n if (storage[componentType.main]) {\n console.warn(componentType.main + ' exists.');\n }\n }\n storage[componentType.main] = Clazz;\n }\n else if (componentType.sub !== IS_CONTAINER) {\n var container = makeContainer(componentType);\n container[componentType.sub] = Clazz;\n }\n }\n return Clazz;\n };\n\n entity.getClass = function (componentMainType, subType, throwWhenNotFound) {\n var Clazz = storage[componentMainType];\n\n if (Clazz && Clazz[IS_CONTAINER]) {\n Clazz = subType ? Clazz[subType] : null;\n }\n\n if (throwWhenNotFound && !Clazz) {\n throw new Error(\n !subType\n ? componentMainType + '.' + 'type should be specified.'\n : 'Component ' + componentMainType + '.' + (subType || '') + ' not exists. Load it first.'\n );\n }\n\n return Clazz;\n };\n\n entity.getClassesByMainType = function (componentType) {\n componentType = parseClassType(componentType);\n\n var result = [];\n var obj = storage[componentType.main];\n\n if (obj && obj[IS_CONTAINER]) {\n zrUtil.each(obj, function (o, type) {\n type !== IS_CONTAINER && result.push(o);\n });\n }\n else {\n result.push(obj);\n }\n\n return result;\n };\n\n entity.hasClass = function (componentType) {\n // Just consider componentType.main.\n componentType = parseClassType(componentType);\n return !!storage[componentType.main];\n };\n\n /**\n * @return {Array.<string>} Like ['aa', 'bb'], but can not be ['aa.xx']\n */\n entity.getAllClassMainTypes = function () {\n var types = [];\n zrUtil.each(storage, function (obj, type) {\n types.push(type);\n });\n return types;\n };\n\n /**\n * If a main type is container and has sub types\n * @param {string} mainType\n * @return {boolean}\n */\n entity.hasSubTypes = function (componentType) {\n componentType = parseClassType(componentType);\n var obj = storage[componentType.main];\n return obj && obj[IS_CONTAINER];\n };\n\n entity.parseClassType = parseClassType;\n\n function makeContainer(componentType) {\n var container = storage[componentType.main];\n if (!container || !container[IS_CONTAINER]) {\n container = storage[componentType.main] = {};\n container[IS_CONTAINER] = true;\n }\n return container;\n }\n\n if (options.registerWhenExtend) {\n var originalExtend = entity.extend;\n if (originalExtend) {\n entity.extend = function (proto) {\n var ExtendedClass = originalExtend.call(this, proto);\n return entity.registerClass(ExtendedClass, proto.type);\n };\n }\n }\n\n return entity;\n}\n\n/**\n * @param {string|Array.<string>} properties\n */\nexport function setReadOnly(obj, properties) {\n // FIXME It seems broken in IE8 simulation of IE11\n // if (!zrUtil.isArray(properties)) {\n // properties = properties != null ? [properties] : [];\n // }\n // zrUtil.each(properties, function (prop) {\n // var value = obj[prop];\n\n // Object.defineProperty\n // && Object.defineProperty(obj, prop, {\n // value: value, writable: false\n // });\n // zrUtil.isArray(obj[prop])\n // && Object.freeze\n // && Object.freeze(obj[prop]);\n // });\n}\n","// TODO Parse shadow style\n// TODO Only shallow path support\nimport * as zrUtil from 'zrender/src/core/util';\n\nexport default function (properties) {\n // Normalize\n for (var i = 0; i < properties.length; i++) {\n if (!properties[i][1]) {\n properties[i][1] = properties[i][0];\n }\n }\n return function (model, excludes, includes) {\n var style = {};\n for (var i = 0; i < properties.length; i++) {\n var propName = properties[i][1];\n if ((excludes && zrUtil.indexOf(excludes, propName) >= 0)\n || (includes && zrUtil.indexOf(includes, propName) < 0)\n ) {\n continue;\n }\n var val = model.getShallow(propName);\n if (val != null) {\n style[properties[i][0]] = val;\n }\n }\n return style;\n };\n}","import makeStyleMapper from './makeStyleMapper';\n\nvar getLineStyle = makeStyleMapper(\n [\n ['lineWidth', 'width'],\n ['stroke', 'color'],\n ['opacity'],\n ['shadowBlur'],\n ['shadowOffsetX'],\n ['shadowOffsetY'],\n ['shadowColor']\n ]\n);\n\nexport default {\n getLineStyle: function (excludes) {\n var style = getLineStyle(this, excludes);\n var lineDash = this.getLineDash(style.lineWidth);\n lineDash && (style.lineDash = lineDash);\n return style;\n },\n\n getLineDash: function (lineWidth) {\n if (lineWidth == null) {\n lineWidth = 1;\n }\n var lineType = this.get('type');\n var dotSize = Math.max(lineWidth, 2);\n var dashSize = lineWidth * 4;\n return (lineType === 'solid' || lineType == null) ? null\n : (lineType === 'dashed' ? [dashSize, dashSize] : [dotSize, dotSize]);\n }\n};","import makeStyleMapper from './makeStyleMapper';\n\nvar getAreaStyle = makeStyleMapper(\n [\n ['fill', 'color'],\n ['shadowBlur'],\n ['shadowOffsetX'],\n ['shadowOffsetY'],\n ['opacity'],\n ['shadowColor']\n ]\n);\n\nexport default {\n getAreaStyle: function (excludes, includes) {\n return getAreaStyle(this, excludes, includes);\n }\n};","/**\n * 曲线辅助模块\n * @module zrender/core/curve\n * @author pissang(https://www.github.com/pissang)\n */\n\nimport {\n create as v2Create,\n distSquare as v2DistSquare\n} from './vector';\n\nvar mathPow = Math.pow;\nvar mathSqrt = Math.sqrt;\n\nvar EPSILON = 1e-8;\nvar EPSILON_NUMERIC = 1e-4;\n\nvar THREE_SQRT = mathSqrt(3);\nvar ONE_THIRD = 1 / 3;\n\n// 临时变量\nvar _v0 = v2Create();\nvar _v1 = v2Create();\nvar _v2 = v2Create();\n\nfunction isAroundZero(val) {\n return val > -EPSILON && val < EPSILON;\n}\nfunction isNotAroundZero(val) {\n return val > EPSILON || val < -EPSILON;\n}\n/**\n * 计算三次贝塞尔值\n * @memberOf module:zrender/core/curve\n * @param {number} p0\n * @param {number} p1\n * @param {number} p2\n * @param {number} p3\n * @param {number} t\n * @return {number}\n */\nexport function cubicAt(p0, p1, p2, p3, t) {\n var onet = 1 - t;\n return onet * onet * (onet * p0 + 3 * t * p1)\n + t * t * (t * p3 + 3 * onet * p2);\n}\n\n/**\n * 计算三次贝塞尔导数值\n * @memberOf module:zrender/core/curve\n * @param {number} p0\n * @param {number} p1\n * @param {number} p2\n * @param {number} p3\n * @param {number} t\n * @return {number}\n */\nexport function cubicDerivativeAt(p0, p1, p2, p3, t) {\n var onet = 1 - t;\n return 3 * (\n ((p1 - p0) * onet + 2 * (p2 - p1) * t) * onet\n + (p3 - p2) * t * t\n );\n}\n\n/**\n * 计算三次贝塞尔方程根,使用盛金公式\n * @memberOf module:zrender/core/curve\n * @param {number} p0\n * @param {number} p1\n * @param {number} p2\n * @param {number} p3\n * @param {number} val\n * @param {Array.<number>} roots\n * @return {number} 有效根数目\n */\nexport function cubicRootAt(p0, p1, p2, p3, val, roots) {\n // Evaluate roots of cubic functions\n var a = p3 + 3 * (p1 - p2) - p0;\n var b = 3 * (p2 - p1 * 2 + p0);\n var c = 3 * (p1 - p0);\n var d = p0 - val;\n\n var A = b * b - 3 * a * c;\n var B = b * c - 9 * a * d;\n var C = c * c - 3 * b * d;\n\n var n = 0;\n\n if (isAroundZero(A) && isAroundZero(B)) {\n if (isAroundZero(b)) {\n roots[0] = 0;\n }\n else {\n var t1 = -c / b; //t1, t2, t3, b is not zero\n if (t1 >= 0 && t1 <= 1) {\n roots[n++] = t1;\n }\n }\n }\n else {\n var disc = B * B - 4 * A * C;\n\n if (isAroundZero(disc)) {\n var K = B / A;\n var t1 = -b / a + K; // t1, a is not zero\n var t2 = -K / 2; // t2, t3\n if (t1 >= 0 && t1 <= 1) {\n roots[n++] = t1;\n }\n if (t2 >= 0 && t2 <= 1) {\n roots[n++] = t2;\n }\n }\n else if (disc > 0) {\n var discSqrt = mathSqrt(disc);\n var Y1 = A * b + 1.5 * a * (-B + discSqrt);\n var Y2 = A * b + 1.5 * a * (-B - discSqrt);\n if (Y1 < 0) {\n Y1 = -mathPow(-Y1, ONE_THIRD);\n }\n else {\n Y1 = mathPow(Y1, ONE_THIRD);\n }\n if (Y2 < 0) {\n Y2 = -mathPow(-Y2, ONE_THIRD);\n }\n else {\n Y2 = mathPow(Y2, ONE_THIRD);\n }\n var t1 = (-b - (Y1 + Y2)) / (3 * a);\n if (t1 >= 0 && t1 <= 1) {\n roots[n++] = t1;\n }\n }\n else {\n var T = (2 * A * b - 3 * a * B) / (2 * mathSqrt(A * A * A));\n var theta = Math.acos(T) / 3;\n var ASqrt = mathSqrt(A);\n var tmp = Math.cos(theta);\n\n var t1 = (-b - 2 * ASqrt * tmp) / (3 * a);\n var t2 = (-b + ASqrt * (tmp + THREE_SQRT * Math.sin(theta))) / (3 * a);\n var t3 = (-b + ASqrt * (tmp - THREE_SQRT * Math.sin(theta))) / (3 * a);\n if (t1 >= 0 && t1 <= 1) {\n roots[n++] = t1;\n }\n if (t2 >= 0 && t2 <= 1) {\n roots[n++] = t2;\n }\n if (t3 >= 0 && t3 <= 1) {\n roots[n++] = t3;\n }\n }\n }\n return n;\n}\n\n/**\n * 计算三次贝塞尔方程极限值的位置\n * @memberOf module:zrender/core/curve\n * @param {number} p0\n * @param {number} p1\n * @param {number} p2\n * @param {number} p3\n * @param {Array.<number>} extrema\n * @return {number} 有效数目\n */\nexport function cubicExtrema(p0, p1, p2, p3, extrema) {\n var b = 6 * p2 - 12 * p1 + 6 * p0;\n var a = 9 * p1 + 3 * p3 - 3 * p0 - 9 * p2;\n var c = 3 * p1 - 3 * p0;\n\n var n = 0;\n if (isAroundZero(a)) {\n if (isNotAroundZero(b)) {\n var t1 = -c / b;\n if (t1 >= 0 && t1 <=1) {\n extrema[n++] = t1;\n }\n }\n }\n else {\n var disc = b * b - 4 * a * c;\n if (isAroundZero(disc)) {\n extrema[0] = -b / (2 * a);\n }\n else if (disc > 0) {\n var discSqrt = mathSqrt(disc);\n var t1 = (-b + discSqrt) / (2 * a);\n var t2 = (-b - discSqrt) / (2 * a);\n if (t1 >= 0 && t1 <= 1) {\n extrema[n++] = t1;\n }\n if (t2 >= 0 && t2 <= 1) {\n extrema[n++] = t2;\n }\n }\n }\n return n;\n}\n\n/**\n * 细分三次贝塞尔曲线\n * @memberOf module:zrender/core/curve\n * @param {number} p0\n * @param {number} p1\n * @param {number} p2\n * @param {number} p3\n * @param {number} t\n * @param {Array.<number>} out\n */\nexport function cubicSubdivide(p0, p1, p2, p3, t, out) {\n var p01 = (p1 - p0) * t + p0;\n var p12 = (p2 - p1) * t + p1;\n var p23 = (p3 - p2) * t + p2;\n\n var p012 = (p12 - p01) * t + p01;\n var p123 = (p23 - p12) * t + p12;\n\n var p0123 = (p123 - p012) * t + p012;\n // Seg0\n out[0] = p0;\n out[1] = p01;\n out[2] = p012;\n out[3] = p0123;\n // Seg1\n out[4] = p0123;\n out[5] = p123;\n out[6] = p23;\n out[7] = p3;\n}\n\n/**\n * 投射点到三次贝塞尔曲线上,返回投射距离。\n * 投射点有可能会有一个或者多个,这里只返回其中距离最短的一个。\n * @param {number} x0\n * @param {number} y0\n * @param {number} x1\n * @param {number} y1\n * @param {number} x2\n * @param {number} y2\n * @param {number} x3\n * @param {number} y3\n * @param {number} x\n * @param {number} y\n * @param {Array.<number>} [out] 投射点\n * @return {number}\n */\nexport function cubicProjectPoint(\n x0, y0, x1, y1, x2, y2, x3, y3,\n x, y, out\n) {\n // http://pomax.github.io/bezierinfo/#projections\n var t;\n var interval = 0.005;\n var d = Infinity;\n var prev;\n var next;\n var d1;\n var d2;\n\n _v0[0] = x;\n _v0[1] = y;\n\n // 先粗略估计一下可能的最小距离的 t 值\n // PENDING\n for (var _t = 0; _t < 1; _t += 0.05) {\n _v1[0] = cubicAt(x0, x1, x2, x3, _t);\n _v1[1] = cubicAt(y0, y1, y2, y3, _t);\n d1 = v2DistSquare(_v0, _v1);\n if (d1 < d) {\n t = _t;\n d = d1;\n }\n }\n d = Infinity;\n\n // At most 32 iteration\n for (var i = 0; i < 32; i++) {\n if (interval < EPSILON_NUMERIC) {\n break;\n }\n prev = t - interval;\n next = t + interval;\n // t - interval\n _v1[0] = cubicAt(x0, x1, x2, x3, prev);\n _v1[1] = cubicAt(y0, y1, y2, y3, prev);\n\n d1 = v2DistSquare(_v1, _v0);\n\n if (prev >= 0 && d1 < d) {\n t = prev;\n d = d1;\n }\n else {\n // t + interval\n _v2[0] = cubicAt(x0, x1, x2, x3, next);\n _v2[1] = cubicAt(y0, y1, y2, y3, next);\n d2 = v2DistSquare(_v2, _v0);\n\n if (next <= 1 && d2 < d) {\n t = next;\n d = d2;\n }\n else {\n interval *= 0.5;\n }\n }\n }\n // t\n if (out) {\n out[0] = cubicAt(x0, x1, x2, x3, t);\n out[1] = cubicAt(y0, y1, y2, y3, t);\n }\n // console.log(interval, i);\n return mathSqrt(d);\n}\n\n/**\n * 计算二次方贝塞尔值\n * @param {number} p0\n * @param {number} p1\n * @param {number} p2\n * @param {number} t\n * @return {number}\n */\nexport function quadraticAt(p0, p1, p2, t) {\n var onet = 1 - t;\n return onet * (onet * p0 + 2 * t * p1) + t * t * p2;\n}\n\n/**\n * 计算二次方贝塞尔导数值\n * @param {number} p0\n * @param {number} p1\n * @param {number} p2\n * @param {number} t\n * @return {number}\n */\nexport function quadraticDerivativeAt(p0, p1, p2, t) {\n return 2 * ((1 - t) * (p1 - p0) + t * (p2 - p1));\n}\n\n/**\n * 计算二次方贝塞尔方程根\n * @param {number} p0\n * @param {number} p1\n * @param {number} p2\n * @param {number} t\n * @param {Array.<number>} roots\n * @return {number} 有效根数目\n */\nexport function quadraticRootAt(p0, p1, p2, val, roots) {\n var a = p0 - 2 * p1 + p2;\n var b = 2 * (p1 - p0);\n var c = p0 - val;\n\n var n = 0;\n if (isAroundZero(a)) {\n if (isNotAroundZero(b)) {\n var t1 = -c / b;\n if (t1 >= 0 && t1 <= 1) {\n roots[n++] = t1;\n }\n }\n }\n else {\n var disc = b * b - 4 * a * c;\n if (isAroundZero(disc)) {\n var t1 = -b / (2 * a);\n if (t1 >= 0 && t1 <= 1) {\n roots[n++] = t1;\n }\n }\n else if (disc > 0) {\n var discSqrt = mathSqrt(disc);\n var t1 = (-b + discSqrt) / (2 * a);\n var t2 = (-b - discSqrt) / (2 * a);\n if (t1 >= 0 && t1 <= 1) {\n roots[n++] = t1;\n }\n if (t2 >= 0 && t2 <= 1) {\n roots[n++] = t2;\n }\n }\n }\n return n;\n}\n\n/**\n * 计算二次贝塞尔方程极限值\n * @memberOf module:zrender/core/curve\n * @param {number} p0\n * @param {number} p1\n * @param {number} p2\n * @return {number}\n */\nexport function quadraticExtremum(p0, p1, p2) {\n var divider = p0 + p2 - 2 * p1;\n if (divider === 0) {\n // p1 is center of p0 and p2\n return 0.5;\n }\n else {\n return (p0 - p1) / divider;\n }\n}\n\n/**\n * 细分二次贝塞尔曲线\n * @memberOf module:zrender/core/curve\n * @param {number} p0\n * @param {number} p1\n * @param {number} p2\n * @param {number} t\n * @param {Array.<number>} out\n */\nexport function quadraticSubdivide(p0, p1, p2, t, out) {\n var p01 = (p1 - p0) * t + p0;\n var p12 = (p2 - p1) * t + p1;\n var p012 = (p12 - p01) * t + p01;\n\n // Seg0\n out[0] = p0;\n out[1] = p01;\n out[2] = p012;\n\n // Seg1\n out[3] = p012;\n out[4] = p12;\n out[5] = p2;\n}\n\n/**\n * 投射点到二次贝塞尔曲线上,返回投射距离。\n * 投射点有可能会有一个或者多个,这里只返回其中距离最短的一个。\n * @param {number} x0\n * @param {number} y0\n * @param {number} x1\n * @param {number} y1\n * @param {number} x2\n * @param {number} y2\n * @param {number} x\n * @param {number} y\n * @param {Array.<number>} out 投射点\n * @return {number}\n */\nexport function quadraticProjectPoint(\n x0, y0, x1, y1, x2, y2,\n x, y, out\n) {\n // http://pomax.github.io/bezierinfo/#projections\n var t;\n var interval = 0.005;\n var d = Infinity;\n\n _v0[0] = x;\n _v0[1] = y;\n\n // 先粗略估计一下可能的最小距离的 t 值\n // PENDING\n for (var _t = 0; _t < 1; _t += 0.05) {\n _v1[0] = quadraticAt(x0, x1, x2, _t);\n _v1[1] = quadraticAt(y0, y1, y2, _t);\n var d1 = v2DistSquare(_v0, _v1);\n if (d1 < d) {\n t = _t;\n d = d1;\n }\n }\n d = Infinity;\n\n // At most 32 iteration\n for (var i = 0; i < 32; i++) {\n if (interval < EPSILON_NUMERIC) {\n break;\n }\n var prev = t - interval;\n var next = t + interval;\n // t - interval\n _v1[0] = quadraticAt(x0, x1, x2, prev);\n _v1[1] = quadraticAt(y0, y1, y2, prev);\n\n var d1 = v2DistSquare(_v1, _v0);\n\n if (prev >= 0 && d1 < d) {\n t = prev;\n d = d1;\n }\n else {\n // t + interval\n _v2[0] = quadraticAt(x0, x1, x2, next);\n _v2[1] = quadraticAt(y0, y1, y2, next);\n var d2 = v2DistSquare(_v2, _v0);\n if (next <= 1 && d2 < d) {\n t = next;\n d = d2;\n }\n else {\n interval *= 0.5;\n }\n }\n }\n // t\n if (out) {\n out[0] = quadraticAt(x0, x1, x2, t);\n out[1] = quadraticAt(y0, y1, y2, t);\n }\n // console.log(interval, i);\n return mathSqrt(d);\n}\n","/**\n * @author Yi Shen(https://github.com/pissang)\n */\n\nimport * as vec2 from './vector';\nimport * as curve from './curve';\n\nvar mathMin = Math.min;\nvar mathMax = Math.max;\nvar mathSin = Math.sin;\nvar mathCos = Math.cos;\nvar PI2 = Math.PI * 2;\n\nvar start = vec2.create();\nvar end = vec2.create();\nvar extremity = vec2.create();\n\n/**\n * 从顶点数组中计算出最小包围盒,写入`min`和`max`中\n * @module zrender/core/bbox\n * @param {Array<Object>} points 顶点数组\n * @param {number} min\n * @param {number} max\n */\nexport function fromPoints(points, min, max) {\n if (points.length === 0) {\n return;\n }\n var p = points[0];\n var left = p[0];\n var right = p[0];\n var top = p[1];\n var bottom = p[1];\n var i;\n\n for (i = 1; i < points.length; i++) {\n p = points[i];\n left = mathMin(left, p[0]);\n right = mathMax(right, p[0]);\n top = mathMin(top, p[1]);\n bottom = mathMax(bottom, p[1]);\n }\n\n min[0] = left;\n min[1] = top;\n max[0] = right;\n max[1] = bottom;\n}\n\n/**\n * @memberOf module:zrender/core/bbox\n * @param {number} x0\n * @param {number} y0\n * @param {number} x1\n * @param {number} y1\n * @param {Array.<number>} min\n * @param {Array.<number>} max\n */\nexport function fromLine(x0, y0, x1, y1, min, max) {\n min[0] = mathMin(x0, x1);\n min[1] = mathMin(y0, y1);\n max[0] = mathMax(x0, x1);\n max[1] = mathMax(y0, y1);\n}\n\nvar xDim = [];\nvar yDim = [];\n/**\n * 从三阶贝塞尔曲线(p0, p1, p2, p3)中计算出最小包围盒,写入`min`和`max`中\n * @memberOf module:zrender/core/bbox\n * @param {number} x0\n * @param {number} y0\n * @param {number} x1\n * @param {number} y1\n * @param {number} x2\n * @param {number} y2\n * @param {number} x3\n * @param {number} y3\n * @param {Array.<number>} min\n * @param {Array.<number>} max\n */\nexport function fromCubic(\n x0, y0, x1, y1, x2, y2, x3, y3, min, max\n) {\n var cubicExtrema = curve.cubicExtrema;\n var cubicAt = curve.cubicAt;\n var i;\n var n = cubicExtrema(x0, x1, x2, x3, xDim);\n min[0] = Infinity;\n min[1] = Infinity;\n max[0] = -Infinity;\n max[1] = -Infinity;\n\n for (i = 0; i < n; i++) {\n var x = cubicAt(x0, x1, x2, x3, xDim[i]);\n min[0] = mathMin(x, min[0]);\n max[0] = mathMax(x, max[0]);\n }\n n = cubicExtrema(y0, y1, y2, y3, yDim);\n for (i = 0; i < n; i++) {\n var y = cubicAt(y0, y1, y2, y3, yDim[i]);\n min[1] = mathMin(y, min[1]);\n max[1] = mathMax(y, max[1]);\n }\n\n min[0] = mathMin(x0, min[0]);\n max[0] = mathMax(x0, max[0]);\n min[0] = mathMin(x3, min[0]);\n max[0] = mathMax(x3, max[0]);\n\n min[1] = mathMin(y0, min[1]);\n max[1] = mathMax(y0, max[1]);\n min[1] = mathMin(y3, min[1]);\n max[1] = mathMax(y3, max[1]);\n}\n\n/**\n * 从二阶贝塞尔曲线(p0, p1, p2)中计算出最小包围盒,写入`min`和`max`中\n * @memberOf module:zrender/core/bbox\n * @param {number} x0\n * @param {number} y0\n * @param {number} x1\n * @param {number} y1\n * @param {number} x2\n * @param {number} y2\n * @param {Array.<number>} min\n * @param {Array.<number>} max\n */\nexport function fromQuadratic(x0, y0, x1, y1, x2, y2, min, max) {\n var quadraticExtremum = curve.quadraticExtremum;\n var quadraticAt = curve.quadraticAt;\n // Find extremities, where derivative in x dim or y dim is zero\n var tx =\n mathMax(\n mathMin(quadraticExtremum(x0, x1, x2), 1), 0\n );\n var ty =\n mathMax(\n mathMin(quadraticExtremum(y0, y1, y2), 1), 0\n );\n\n var x = quadraticAt(x0, x1, x2, tx);\n var y = quadraticAt(y0, y1, y2, ty);\n\n min[0] = mathMin(x0, x2, x);\n min[1] = mathMin(y0, y2, y);\n max[0] = mathMax(x0, x2, x);\n max[1] = mathMax(y0, y2, y);\n}\n\n/**\n * 从圆弧中计算出最小包围盒,写入`min`和`max`中\n * @method\n * @memberOf module:zrender/core/bbox\n * @param {number} x\n * @param {number} y\n * @param {number} rx\n * @param {number} ry\n * @param {number} startAngle\n * @param {number} endAngle\n * @param {number} anticlockwise\n * @param {Array.<number>} min\n * @param {Array.<number>} max\n */\nexport function fromArc(\n x, y, rx, ry, startAngle, endAngle, anticlockwise, min, max\n) {\n var vec2Min = vec2.min;\n var vec2Max = vec2.max;\n\n var diff = Math.abs(startAngle - endAngle);\n\n\n if (diff % PI2 < 1e-4 && diff > 1e-4) {\n // Is a circle\n min[0] = x - rx;\n min[1] = y - ry;\n max[0] = x + rx;\n max[1] = y + ry;\n return;\n }\n\n start[0] = mathCos(startAngle) * rx + x;\n start[1] = mathSin(startAngle) * ry + y;\n\n end[0] = mathCos(endAngle) * rx + x;\n end[1] = mathSin(endAngle) * ry + y;\n\n vec2Min(min, start, end);\n vec2Max(max, start, end);\n\n // Thresh to [0, Math.PI * 2]\n startAngle = startAngle % (PI2);\n if (startAngle < 0) {\n startAngle = startAngle + PI2;\n }\n endAngle = endAngle % (PI2);\n if (endAngle < 0) {\n endAngle = endAngle + PI2;\n }\n\n if (startAngle > endAngle && !anticlockwise) {\n endAngle += PI2;\n }\n else if (startAngle < endAngle && anticlockwise) {\n startAngle += PI2;\n }\n if (anticlockwise) {\n var tmp = endAngle;\n endAngle = startAngle;\n startAngle = tmp;\n }\n\n // var number = 0;\n // var step = (anticlockwise ? -Math.PI : Math.PI) / 2;\n for (var angle = 0; angle < endAngle; angle += Math.PI / 2) {\n if (angle > startAngle) {\n extremity[0] = mathCos(angle) * rx + x;\n extremity[1] = mathSin(angle) * ry + y;\n\n vec2Min(min, extremity, min);\n vec2Max(max, extremity, max);\n }\n }\n}\n","/**\n * Path 代理,可以在`buildPath`中用于替代`ctx`, 会保存每个path操作的命令到pathCommands属性中\n * 可以用于 isInsidePath 判断以及获取boundingRect\n *\n * @module zrender/core/PathProxy\n * @author Yi Shen (http://www.github.com/pissang)\n */\n\n// TODO getTotalLength, getPointAtLength\n\nimport * as curve from './curve';\nimport * as vec2 from './vector';\nimport * as bbox from './bbox';\nimport BoundingRect from './BoundingRect';\nimport {devicePixelRatio as dpr} from '../config';\n\nvar CMD = {\n M: 1,\n L: 2,\n C: 3,\n Q: 4,\n A: 5,\n Z: 6,\n // Rect\n R: 7\n};\n\n// var CMD_MEM_SIZE = {\n// M: 3,\n// L: 3,\n// C: 7,\n// Q: 5,\n// A: 9,\n// R: 5,\n// Z: 1\n// };\n\nvar min = [];\nvar max = [];\nvar min2 = [];\nvar max2 = [];\nvar mathMin = Math.min;\nvar mathMax = Math.max;\nvar mathCos = Math.cos;\nvar mathSin = Math.sin;\nvar mathSqrt = Math.sqrt;\nvar mathAbs = Math.abs;\n\nvar hasTypedArray = typeof Float32Array != 'undefined';\n\n/**\n * @alias module:zrender/core/PathProxy\n * @constructor\n */\nvar PathProxy = function (notSaveData) {\n\n this._saveData = !(notSaveData || false);\n\n if (this._saveData) {\n /**\n * Path data. Stored as flat array\n * @type {Array.<Object>}\n */\n this.data = [];\n }\n\n this._ctx = null;\n};\n\n/**\n * 快速计算Path包围盒(并不是最小包围盒)\n * @return {Object}\n */\nPathProxy.prototype = {\n\n constructor: PathProxy,\n\n _xi: 0,\n _yi: 0,\n\n _x0: 0,\n _y0: 0,\n // Unit x, Unit y. Provide for avoiding drawing that too short line segment\n _ux: 0,\n _uy: 0,\n\n _len: 0,\n\n _lineDash: null,\n\n _dashOffset: 0,\n\n _dashIdx: 0,\n\n _dashSum: 0,\n\n /**\n * @readOnly\n */\n setScale: function (sx, sy) {\n this._ux = mathAbs(1 / dpr / sx) || 0;\n this._uy = mathAbs(1 / dpr / sy) || 0;\n },\n\n getContext: function () {\n return this._ctx;\n },\n\n /**\n * @param {CanvasRenderingContext2D} ctx\n * @return {module:zrender/core/PathProxy}\n */\n beginPath: function (ctx) {\n\n this._ctx = ctx;\n\n ctx && ctx.beginPath();\n\n ctx && (this.dpr = ctx.dpr);\n\n // Reset\n if (this._saveData) {\n this._len = 0;\n }\n\n if (this._lineDash) {\n this._lineDash = null;\n\n this._dashOffset = 0;\n }\n\n return this;\n },\n\n /**\n * @param {number} x\n * @param {number} y\n * @return {module:zrender/core/PathProxy}\n */\n moveTo: function (x, y) {\n this.addData(CMD.M, x, y);\n this._ctx && this._ctx.moveTo(x, y);\n\n // x0, y0, xi, yi 是记录在 _dashedXXXXTo 方法中使用\n // xi, yi 记录当前点, x0, y0 在 closePath 的时候回到起始点。\n // 有可能在 beginPath 之后直接调用 lineTo,这时候 x0, y0 需要\n // 在 lineTo 方法中记录,这里先不考虑这种情况,dashed line 也只在 IE10- 中不支持\n this._x0 = x;\n this._y0 = y;\n\n this._xi = x;\n this._yi = y;\n\n return this;\n },\n\n /**\n * @param {number} x\n * @param {number} y\n * @return {module:zrender/core/PathProxy}\n */\n lineTo: function (x, y) {\n var exceedUnit = mathAbs(x - this._xi) > this._ux\n || mathAbs(y - this._yi) > this._uy\n // Force draw the first segment\n || this._len < 5;\n\n this.addData(CMD.L, x, y);\n\n if (this._ctx && exceedUnit) {\n this._needsDash() ? this._dashedLineTo(x, y)\n : this._ctx.lineTo(x, y);\n }\n if (exceedUnit) {\n this._xi = x;\n this._yi = y;\n }\n\n return this;\n },\n\n /**\n * @param {number} x1\n * @param {number} y1\n * @param {number} x2\n * @param {number} y2\n * @param {number} x3\n * @param {number} y3\n * @return {module:zrender/core/PathProxy}\n */\n bezierCurveTo: function (x1, y1, x2, y2, x3, y3) {\n this.addData(CMD.C, x1, y1, x2, y2, x3, y3);\n if (this._ctx) {\n this._needsDash() ? this._dashedBezierTo(x1, y1, x2, y2, x3, y3)\n : this._ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3);\n }\n this._xi = x3;\n this._yi = y3;\n return this;\n },\n\n /**\n * @param {number} x1\n * @param {number} y1\n * @param {number} x2\n * @param {number} y2\n * @return {module:zrender/core/PathProxy}\n */\n quadraticCurveTo: function (x1, y1, x2, y2) {\n this.addData(CMD.Q, x1, y1, x2, y2);\n if (this._ctx) {\n this._needsDash() ? this._dashedQuadraticTo(x1, y1, x2, y2)\n : this._ctx.quadraticCurveTo(x1, y1, x2, y2);\n }\n this._xi = x2;\n this._yi = y2;\n return this;\n },\n\n /**\n * @param {number} cx\n * @param {number} cy\n * @param {number} r\n * @param {number} startAngle\n * @param {number} endAngle\n * @param {boolean} anticlockwise\n * @return {module:zrender/core/PathProxy}\n */\n arc: function (cx, cy, r, startAngle, endAngle, anticlockwise) {\n this.addData(\n CMD.A, cx, cy, r, r, startAngle, endAngle - startAngle, 0, anticlockwise ? 0 : 1\n );\n this._ctx && this._ctx.arc(cx, cy, r, startAngle, endAngle, anticlockwise);\n\n this._xi = mathCos(endAngle) * r + cx;\n this._yi = mathSin(endAngle) * r + cx;\n return this;\n },\n\n // TODO\n arcTo: function (x1, y1, x2, y2, radius) {\n if (this._ctx) {\n this._ctx.arcTo(x1, y1, x2, y2, radius);\n }\n return this;\n },\n\n // TODO\n rect: function (x, y, w, h) {\n this._ctx && this._ctx.rect(x, y, w, h);\n this.addData(CMD.R, x, y, w, h);\n return this;\n },\n\n /**\n * @return {module:zrender/core/PathProxy}\n */\n closePath: function () {\n this.addData(CMD.Z);\n\n var ctx = this._ctx;\n var x0 = this._x0;\n var y0 = this._y0;\n if (ctx) {\n this._needsDash() && this._dashedLineTo(x0, y0);\n ctx.closePath();\n }\n\n this._xi = x0;\n this._yi = y0;\n return this;\n },\n\n /**\n * Context 从外部传入,因为有可能是 rebuildPath 完之后再 fill。\n * stroke 同样\n * @param {CanvasRenderingContext2D} ctx\n * @return {module:zrender/core/PathProxy}\n */\n fill: function (ctx) {\n ctx && ctx.fill();\n this.toStatic();\n },\n\n /**\n * @param {CanvasRenderingContext2D} ctx\n * @return {module:zrender/core/PathProxy}\n */\n stroke: function (ctx) {\n ctx && ctx.stroke();\n this.toStatic();\n },\n\n /**\n * 必须在其它绘制命令前调用\n * Must be invoked before all other path drawing methods\n * @return {module:zrender/core/PathProxy}\n */\n setLineDash: function (lineDash) {\n if (lineDash instanceof Array) {\n this._lineDash = lineDash;\n\n this._dashIdx = 0;\n\n var lineDashSum = 0;\n for (var i = 0; i < lineDash.length; i++) {\n lineDashSum += lineDash[i];\n }\n this._dashSum = lineDashSum;\n }\n return this;\n },\n\n /**\n * 必须在其它绘制命令前调用\n * Must be invoked before all other path drawing methods\n * @return {module:zrender/core/PathProxy}\n */\n setLineDashOffset: function (offset) {\n this._dashOffset = offset;\n return this;\n },\n\n /**\n *\n * @return {boolean}\n */\n len: function () {\n return this._len;\n },\n\n /**\n * 直接设置 Path 数据\n */\n setData: function (data) {\n\n var len = data.length;\n\n if (! (this.data && this.data.length == len) && hasTypedArray) {\n this.data = new Float32Array(len);\n }\n\n for (var i = 0; i < len; i++) {\n this.data[i] = data[i];\n }\n\n this._len = len;\n },\n\n /**\n * 添加子路径\n * @param {module:zrender/core/PathProxy|Array.<module:zrender/core/PathProxy>} path\n */\n appendPath: function (path) {\n if (!(path instanceof Array)) {\n path = [path];\n }\n var len = path.length;\n var appendSize = 0;\n var offset = this._len;\n for (var i = 0; i < len; i++) {\n appendSize += path[i].len();\n }\n if (hasTypedArray && (this.data instanceof Float32Array)) {\n this.data = new Float32Array(offset + appendSize);\n }\n for (var i = 0; i < len; i++) {\n var appendPathData = path[i].data;\n for (var k = 0; k < appendPathData.length; k++) {\n this.data[offset++] = appendPathData[k];\n }\n }\n this._len = offset;\n },\n\n /**\n * 填充 Path 数据。\n * 尽量复用而不申明新的数组。大部分图形重绘的指令数据长度都是不变的。\n */\n addData: function (cmd) {\n if (!this._saveData) {\n return;\n }\n\n var data = this.data;\n if (this._len + arguments.length > data.length) {\n // 因为之前的数组已经转换成静态的 Float32Array\n // 所以不够用时需要扩展一个新的动态数组\n this._expandData();\n data = this.data;\n }\n for (var i = 0; i < arguments.length; i++) {\n data[this._len++] = arguments[i];\n }\n\n this._prevCmd = cmd;\n },\n\n _expandData: function () {\n // Only if data is Float32Array\n if (!(this.data instanceof Array)) {\n var newData = [];\n for (var i = 0; i < this._len; i++) {\n newData[i] = this.data[i];\n }\n this.data = newData;\n }\n },\n\n /**\n * If needs js implemented dashed line\n * @return {boolean}\n * @private\n */\n _needsDash: function () {\n return this._lineDash;\n },\n\n _dashedLineTo: function (x1, y1) {\n var dashSum = this._dashSum;\n var offset = this._dashOffset;\n var lineDash = this._lineDash;\n var ctx = this._ctx;\n\n var x0 = this._xi;\n var y0 = this._yi;\n var dx = x1 - x0;\n var dy = y1 - y0;\n var dist = mathSqrt(dx * dx + dy * dy);\n var x = x0;\n var y = y0;\n var dash;\n var nDash = lineDash.length;\n var idx;\n dx /= dist;\n dy /= dist;\n\n if (offset < 0) {\n // Convert to positive offset\n offset = dashSum + offset;\n }\n offset %= dashSum;\n x -= offset * dx;\n y -= offset * dy;\n\n while ((dx > 0 && x <= x1) || (dx < 0 && x >= x1)\n || (dx == 0 && ((dy > 0 && y <= y1) || (dy < 0 && y >= y1)))) {\n idx = this._dashIdx;\n dash = lineDash[idx];\n x += dx * dash;\n y += dy * dash;\n this._dashIdx = (idx + 1) % nDash;\n // Skip positive offset\n if ((dx > 0 && x < x0) || (dx < 0 && x > x0) || (dy > 0 && y < y0) || (dy < 0 && y > y0)) {\n continue;\n }\n ctx[idx % 2 ? 'moveTo' : 'lineTo'](\n dx >= 0 ? mathMin(x, x1) : mathMax(x, x1),\n dy >= 0 ? mathMin(y, y1) : mathMax(y, y1)\n );\n }\n // Offset for next lineTo\n dx = x - x1;\n dy = y - y1;\n this._dashOffset = -mathSqrt(dx * dx + dy * dy);\n },\n\n // Not accurate dashed line to\n _dashedBezierTo: function (x1, y1, x2, y2, x3, y3) {\n var dashSum = this._dashSum;\n var offset = this._dashOffset;\n var lineDash = this._lineDash;\n var ctx = this._ctx;\n\n var x0 = this._xi;\n var y0 = this._yi;\n var t;\n var dx;\n var dy;\n var cubicAt = curve.cubicAt;\n var bezierLen = 0;\n var idx = this._dashIdx;\n var nDash = lineDash.length;\n\n var x;\n var y;\n\n var tmpLen = 0;\n\n if (offset < 0) {\n // Convert to positive offset\n offset = dashSum + offset;\n }\n offset %= dashSum;\n // Bezier approx length\n for (t = 0; t < 1; t += 0.1) {\n dx = cubicAt(x0, x1, x2, x3, t + 0.1)\n - cubicAt(x0, x1, x2, x3, t);\n dy = cubicAt(y0, y1, y2, y3, t + 0.1)\n - cubicAt(y0, y1, y2, y3, t);\n bezierLen += mathSqrt(dx * dx + dy * dy);\n }\n\n // Find idx after add offset\n for (; idx < nDash; idx++) {\n tmpLen += lineDash[idx];\n if (tmpLen > offset) {\n break;\n }\n }\n t = (tmpLen - offset) / bezierLen;\n\n while (t <= 1) {\n\n x = cubicAt(x0, x1, x2, x3, t);\n y = cubicAt(y0, y1, y2, y3, t);\n\n // Use line to approximate dashed bezier\n // Bad result if dash is long\n idx % 2 ? ctx.moveTo(x, y)\n : ctx.lineTo(x, y);\n\n t += lineDash[idx] / bezierLen;\n\n idx = (idx + 1) % nDash;\n }\n\n // Finish the last segment and calculate the new offset\n (idx % 2 !== 0) && ctx.lineTo(x3, y3);\n dx = x3 - x;\n dy = y3 - y;\n this._dashOffset = -mathSqrt(dx * dx + dy * dy);\n },\n\n _dashedQuadraticTo: function (x1, y1, x2, y2) {\n // Convert quadratic to cubic using degree elevation\n var x3 = x2;\n var y3 = y2;\n x2 = (x2 + 2 * x1) / 3;\n y2 = (y2 + 2 * y1) / 3;\n x1 = (this._xi + 2 * x1) / 3;\n y1 = (this._yi + 2 * y1) / 3;\n\n this._dashedBezierTo(x1, y1, x2, y2, x3, y3);\n },\n\n /**\n * 转成静态的 Float32Array 减少堆内存占用\n * Convert dynamic array to static Float32Array\n */\n toStatic: function () {\n var data = this.data;\n if (data instanceof Array) {\n data.length = this._len;\n if (hasTypedArray) {\n this.data = new Float32Array(data);\n }\n }\n },\n\n /**\n * @return {module:zrender/core/BoundingRect}\n */\n getBoundingRect: function () {\n min[0] = min[1] = min2[0] = min2[1] = Number.MAX_VALUE;\n max[0] = max[1] = max2[0] = max2[1] = -Number.MAX_VALUE;\n\n var data = this.data;\n var xi = 0;\n var yi = 0;\n var x0 = 0;\n var y0 = 0;\n\n for (var i = 0; i < data.length;) {\n var cmd = data[i++];\n\n if (i == 1) {\n // 如果第一个命令是 L, C, Q\n // 则 previous point 同绘制命令的第一个 point\n //\n // 第一个命令为 Arc 的情况下会在后面特殊处理\n xi = data[i];\n yi = data[i + 1];\n\n x0 = xi;\n y0 = yi;\n }\n\n switch (cmd) {\n case CMD.M:\n // moveTo 命令重新创建一个新的 subpath, 并且更新新的起点\n // 在 closePath 的时候使用\n x0 = data[i++];\n y0 = data[i++];\n xi = x0;\n yi = y0;\n min2[0] = x0;\n min2[1] = y0;\n max2[0] = x0;\n max2[1] = y0;\n break;\n case CMD.L:\n bbox.fromLine(xi, yi, data[i], data[i + 1], min2, max2);\n xi = data[i++];\n yi = data[i++];\n break;\n case CMD.C:\n bbox.fromCubic(\n xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1],\n min2, max2\n );\n xi = data[i++];\n yi = data[i++];\n break;\n case CMD.Q:\n bbox.fromQuadratic(\n xi, yi, data[i++], data[i++], data[i], data[i + 1],\n min2, max2\n );\n xi = data[i++];\n yi = data[i++];\n break;\n case CMD.A:\n // TODO Arc 判断的开销比较大\n var cx = data[i++];\n var cy = data[i++];\n var rx = data[i++];\n var ry = data[i++];\n var startAngle = data[i++];\n var endAngle = data[i++] + startAngle;\n // TODO Arc 旋转\n var psi = data[i++];\n var anticlockwise = 1 - data[i++];\n\n if (i == 1) {\n // 直接使用 arc 命令\n // 第一个命令起点还未定义\n x0 = mathCos(startAngle) * rx + cx;\n y0 = mathSin(startAngle) * ry + cy;\n }\n\n bbox.fromArc(\n cx, cy, rx, ry, startAngle, endAngle,\n anticlockwise, min2, max2\n );\n\n xi = mathCos(endAngle) * rx + cx;\n yi = mathSin(endAngle) * ry + cy;\n break;\n case CMD.R:\n x0 = xi = data[i++];\n y0 = yi = data[i++];\n var width = data[i++];\n var height = data[i++];\n // Use fromLine\n bbox.fromLine(x0, y0, x0 + width, y0 + height, min2, max2);\n break;\n case CMD.Z:\n xi = x0;\n yi = y0;\n break;\n }\n\n // Union\n vec2.min(min, min, min2);\n vec2.max(max, max, max2);\n }\n\n // No data\n if (i === 0) {\n min[0] = min[1] = max[0] = max[1] = 0;\n }\n\n return new BoundingRect(\n min[0], min[1], max[0] - min[0], max[1] - min[1]\n );\n },\n\n /**\n * Rebuild path from current data\n * Rebuild path will not consider javascript implemented line dash.\n * @param {CanvasRenderingContext2D} ctx\n */\n rebuildPath: function (ctx) {\n var d = this.data;\n var x0, y0;\n var xi, yi;\n var x, y;\n var ux = this._ux;\n var uy = this._uy;\n var len = this._len;\n for (var i = 0; i < len;) {\n var cmd = d[i++];\n\n if (i == 1) {\n // 如果第一个命令是 L, C, Q\n // 则 previous point 同绘制命令的第一个 point\n //\n // 第一个命令为 Arc 的情况下会在后面特殊处理\n xi = d[i];\n yi = d[i + 1];\n\n x0 = xi;\n y0 = yi;\n }\n switch (cmd) {\n case CMD.M:\n x0 = xi = d[i++];\n y0 = yi = d[i++];\n ctx.moveTo(xi, yi);\n break;\n case CMD.L:\n x = d[i++];\n y = d[i++];\n // Not draw too small seg between\n if (mathAbs(x - xi) > ux || mathAbs(y - yi) > uy || i === len - 1) {\n ctx.lineTo(x, y);\n xi = x;\n yi = y;\n }\n break;\n case CMD.C:\n ctx.bezierCurveTo(\n d[i++], d[i++], d[i++], d[i++], d[i++], d[i++]\n );\n xi = d[i - 2];\n yi = d[i - 1];\n break;\n case CMD.Q:\n ctx.quadraticCurveTo(d[i++], d[i++], d[i++], d[i++]);\n xi = d[i - 2];\n yi = d[i - 1];\n break;\n case CMD.A:\n var cx = d[i++];\n var cy = d[i++];\n var rx = d[i++];\n var ry = d[i++];\n var theta = d[i++];\n var dTheta = d[i++];\n var psi = d[i++];\n var fs = d[i++];\n var r = (rx > ry) ? rx : ry;\n var scaleX = (rx > ry) ? 1 : rx / ry;\n var scaleY = (rx > ry) ? ry / rx : 1;\n var isEllipse = Math.abs(rx - ry) > 1e-3;\n var endAngle = theta + dTheta;\n if (isEllipse) {\n ctx.translate(cx, cy);\n ctx.rotate(psi);\n ctx.scale(scaleX, scaleY);\n ctx.arc(0, 0, r, theta, endAngle, 1 - fs);\n ctx.scale(1 / scaleX, 1 / scaleY);\n ctx.rotate(-psi);\n ctx.translate(-cx, -cy);\n }\n else {\n ctx.arc(cx, cy, r, theta, endAngle, 1 - fs);\n }\n\n if (i == 1) {\n // 直接使用 arc 命令\n // 第一个命令起点还未定义\n x0 = mathCos(theta) * rx + cx;\n y0 = mathSin(theta) * ry + cy;\n }\n xi = mathCos(endAngle) * rx + cx;\n yi = mathSin(endAngle) * ry + cy;\n break;\n case CMD.R:\n x0 = xi = d[i];\n y0 = yi = d[i + 1];\n ctx.rect(d[i++], d[i++], d[i++], d[i++]);\n break;\n case CMD.Z:\n ctx.closePath();\n xi = x0;\n yi = y0;\n }\n }\n }\n};\n\nPathProxy.CMD = CMD;\n\nexport default PathProxy;","\n/**\n * 线段包含判断\n * @param {number} x0\n * @param {number} y0\n * @param {number} x1\n * @param {number} y1\n * @param {number} lineWidth\n * @param {number} x\n * @param {number} y\n * @return {boolean}\n */\nexport function containStroke(x0, y0, x1, y1, lineWidth, x, y) {\n if (lineWidth === 0) {\n return false;\n }\n var _l = lineWidth;\n var _a = 0;\n var _b = x0;\n // Quick reject\n if (\n (y > y0 + _l && y > y1 + _l)\n || (y < y0 - _l && y < y1 - _l)\n || (x > x0 + _l && x > x1 + _l)\n || (x < x0 - _l && x < x1 - _l)\n ) {\n return false;\n }\n\n if (x0 !== x1) {\n _a = (y0 - y1) / (x0 - x1);\n _b = (x0 * y1 - x1 * y0) / (x0 - x1) ;\n }\n else {\n return Math.abs(x - x0) <= _l / 2;\n }\n var tmp = _a * x - y + _b;\n var _s = tmp * tmp / (_a * _a + 1);\n return _s <= _l / 2 * _l / 2;\n}","\nimport * as curve from '../core/curve';\n\n/**\n * 三次贝塞尔曲线描边包含判断\n * @param {number} x0\n * @param {number} y0\n * @param {number} x1\n * @param {number} y1\n * @param {number} x2\n * @param {number} y2\n * @param {number} x3\n * @param {number} y3\n * @param {number} lineWidth\n * @param {number} x\n * @param {number} y\n * @return {boolean}\n */\nexport function containStroke(x0, y0, x1, y1, x2, y2, x3, y3, lineWidth, x, y) {\n if (lineWidth === 0) {\n return false;\n }\n var _l = lineWidth;\n // Quick reject\n if (\n (y > y0 + _l && y > y1 + _l && y > y2 + _l && y > y3 + _l)\n || (y < y0 - _l && y < y1 - _l && y < y2 - _l && y < y3 - _l)\n || (x > x0 + _l && x > x1 + _l && x > x2 + _l && x > x3 + _l)\n || (x < x0 - _l && x < x1 - _l && x < x2 - _l && x < x3 - _l)\n ) {\n return false;\n }\n var d = curve.cubicProjectPoint(\n x0, y0, x1, y1, x2, y2, x3, y3,\n x, y, null\n );\n return d <= _l / 2;\n}","import {quadraticProjectPoint} from '../core/curve';\n\n/**\n * 二次贝塞尔曲线描边包含判断\n * @param {number} x0\n * @param {number} y0\n * @param {number} x1\n * @param {number} y1\n * @param {number} x2\n * @param {number} y2\n * @param {number} lineWidth\n * @param {number} x\n * @param {number} y\n * @return {boolean}\n */\nexport function containStroke(x0, y0, x1, y1, x2, y2, lineWidth, x, y) {\n if (lineWidth === 0) {\n return false;\n }\n var _l = lineWidth;\n // Quick reject\n if (\n (y > y0 + _l && y > y1 + _l && y > y2 + _l)\n || (y < y0 - _l && y < y1 - _l && y < y2 - _l)\n || (x > x0 + _l && x > x1 + _l && x > x2 + _l)\n || (x < x0 - _l && x < x1 - _l && x < x2 - _l)\n ) {\n return false;\n }\n var d = quadraticProjectPoint(\n x0, y0, x1, y1, x2, y2,\n x, y, null\n );\n return d <= _l / 2;\n}\n","\nvar PI2 = Math.PI * 2;\n\nexport function normalizeRadian(angle) {\n angle %= PI2;\n if (angle < 0) {\n angle += PI2;\n }\n return angle;\n}","\nimport {normalizeRadian} from './util';\n\nvar PI2 = Math.PI * 2;\n\n/**\n * 圆弧描边包含判断\n * @param {number} cx\n * @param {number} cy\n * @param {number} r\n * @param {number} startAngle\n * @param {number} endAngle\n * @param {boolean} anticlockwise\n * @param {number} lineWidth\n * @param {number} x\n * @param {number} y\n * @return {Boolean}\n */\nexport function containStroke(\n cx, cy, r, startAngle, endAngle, anticlockwise,\n lineWidth, x, y\n) {\n\n if (lineWidth === 0) {\n return false;\n }\n var _l = lineWidth;\n\n x -= cx;\n y -= cy;\n var d = Math.sqrt(x * x + y * y);\n\n if ((d - _l > r) || (d + _l < r)) {\n return false;\n }\n if (Math.abs(startAngle - endAngle) % PI2 < 1e-4) {\n // Is a circle\n return true;\n }\n if (anticlockwise) {\n var tmp = startAngle;\n startAngle = normalizeRadian(endAngle);\n endAngle = normalizeRadian(tmp);\n } else {\n startAngle = normalizeRadian(startAngle);\n endAngle = normalizeRadian(endAngle);\n }\n if (startAngle > endAngle) {\n endAngle += PI2;\n }\n\n var angle = Math.atan2(y, x);\n if (angle < 0) {\n angle += PI2;\n }\n return (angle >= startAngle && angle <= endAngle)\n || (angle + PI2 >= startAngle && angle + PI2 <= endAngle);\n}","\nexport default function windingLine(x0, y0, x1, y1, x, y) {\n if ((y > y0 && y > y1) || (y < y0 && y < y1)) {\n return 0;\n }\n // Ignore horizontal line\n if (y1 === y0) {\n return 0;\n }\n var dir = y1 < y0 ? 1 : -1;\n var t = (y - y0) / (y1 - y0);\n\n // Avoid winding error when intersection point is the connect point of two line of polygon\n if (t === 1 || t === 0) {\n dir = y1 < y0 ? 0.5 : -0.5;\n }\n\n var x_ = t * (x1 - x0) + x0;\n\n return x_ > x ? dir : 0;\n}","import PathProxy from '../core/PathProxy';\nimport * as line from './line';\nimport * as cubic from './cubic';\nimport * as quadratic from './quadratic';\nimport * as arc from './arc';\nimport {normalizeRadian} from './util';\nimport * as curve from '../core/curve';\nimport windingLine from './windingLine';\n\nvar CMD = PathProxy.CMD;\nvar PI2 = Math.PI * 2;\n\nvar EPSILON = 1e-4;\n\nfunction isAroundEqual(a, b) {\n return Math.abs(a - b) < EPSILON;\n}\n\n// 临时数组\nvar roots = [-1, -1, -1];\nvar extrema = [-1, -1];\n\nfunction swapExtrema() {\n var tmp = extrema[0];\n extrema[0] = extrema[1];\n extrema[1] = tmp;\n}\n\nfunction windingCubic(x0, y0, x1, y1, x2, y2, x3, y3, x, y) {\n // Quick reject\n if (\n (y > y0 && y > y1 && y > y2 && y > y3)\n || (y < y0 && y < y1 && y < y2 && y < y3)\n ) {\n return 0;\n }\n var nRoots = curve.cubicRootAt(y0, y1, y2, y3, y, roots);\n if (nRoots === 0) {\n return 0;\n }\n else {\n var w = 0;\n var nExtrema = -1;\n var y0_, y1_;\n for (var i = 0; i < nRoots; i++) {\n var t = roots[i];\n\n // Avoid winding error when intersection point is the connect point of two line of polygon\n var unit = (t === 0 || t === 1) ? 0.5 : 1;\n\n var x_ = curve.cubicAt(x0, x1, x2, x3, t);\n if (x_ < x) { // Quick reject\n continue;\n }\n if (nExtrema < 0) {\n nExtrema = curve.cubicExtrema(y0, y1, y2, y3, extrema);\n if (extrema[1] < extrema[0] && nExtrema > 1) {\n swapExtrema();\n }\n y0_ = curve.cubicAt(y0, y1, y2, y3, extrema[0]);\n if (nExtrema > 1) {\n y1_ = curve.cubicAt(y0, y1, y2, y3, extrema[1]);\n }\n }\n if (nExtrema == 2) {\n // 分成三段单调函数\n if (t < extrema[0]) {\n w += y0_ < y0 ? unit : -unit;\n }\n else if (t < extrema[1]) {\n w += y1_ < y0_ ? unit : -unit;\n }\n else {\n w += y3 < y1_ ? unit : -unit;\n }\n }\n else {\n // 分成两段单调函数\n if (t < extrema[0]) {\n w += y0_ < y0 ? unit : -unit;\n }\n else {\n w += y3 < y0_ ? unit : -unit;\n }\n }\n }\n return w;\n }\n}\n\nfunction windingQuadratic(x0, y0, x1, y1, x2, y2, x, y) {\n // Quick reject\n if (\n (y > y0 && y > y1 && y > y2)\n || (y < y0 && y < y1 && y < y2)\n ) {\n return 0;\n }\n var nRoots = curve.quadraticRootAt(y0, y1, y2, y, roots);\n if (nRoots === 0) {\n return 0;\n }\n else {\n var t = curve.quadraticExtremum(y0, y1, y2);\n if (t >= 0 && t <= 1) {\n var w = 0;\n var y_ = curve.quadraticAt(y0, y1, y2, t);\n for (var i = 0; i < nRoots; i++) {\n // Remove one endpoint.\n var unit = (roots[i] === 0 || roots[i] === 1) ? 0.5 : 1;\n\n var x_ = curve.quadraticAt(x0, x1, x2, roots[i]);\n if (x_ < x) { // Quick reject\n continue;\n }\n if (roots[i] < t) {\n w += y_ < y0 ? unit : -unit;\n }\n else {\n w += y2 < y_ ? unit : -unit;\n }\n }\n return w;\n }\n else {\n // Remove one endpoint.\n var unit = (roots[0] === 0 || roots[0] === 1) ? 0.5 : 1;\n\n var x_ = curve.quadraticAt(x0, x1, x2, roots[0]);\n if (x_ < x) { // Quick reject\n return 0;\n }\n return y2 < y0 ? unit : -unit;\n }\n }\n}\n\n// TODO\n// Arc 旋转\nfunction windingArc(\n cx, cy, r, startAngle, endAngle, anticlockwise, x, y\n) {\n y -= cy;\n if (y > r || y < -r) {\n return 0;\n }\n var tmp = Math.sqrt(r * r - y * y);\n roots[0] = -tmp;\n roots[1] = tmp;\n\n var diff = Math.abs(startAngle - endAngle);\n if (diff < 1e-4) {\n return 0;\n }\n if (diff % PI2 < 1e-4) {\n // Is a circle\n startAngle = 0;\n endAngle = PI2;\n var dir = anticlockwise ? 1 : -1;\n if (x >= roots[0] + cx && x <= roots[1] + cx) {\n return dir;\n } else {\n return 0;\n }\n }\n\n if (anticlockwise) {\n var tmp = startAngle;\n startAngle = normalizeRadian(endAngle);\n endAngle = normalizeRadian(tmp);\n }\n else {\n startAngle = normalizeRadian(startAngle);\n endAngle = normalizeRadian(endAngle);\n }\n if (startAngle > endAngle) {\n endAngle += PI2;\n }\n\n var w = 0;\n for (var i = 0; i < 2; i++) {\n var x_ = roots[i];\n if (x_ + cx > x) {\n var angle = Math.atan2(y, x_);\n var dir = anticlockwise ? 1 : -1;\n if (angle < 0) {\n angle = PI2 + angle;\n }\n if (\n (angle >= startAngle && angle <= endAngle)\n || (angle + PI2 >= startAngle && angle + PI2 <= endAngle)\n ) {\n if (angle > Math.PI / 2 && angle < Math.PI * 1.5) {\n dir = -dir;\n }\n w += dir;\n }\n }\n }\n return w;\n}\n\nfunction containPath(data, lineWidth, isStroke, x, y) {\n var w = 0;\n var xi = 0;\n var yi = 0;\n var x0 = 0;\n var y0 = 0;\n\n for (var i = 0; i < data.length;) {\n var cmd = data[i++];\n // Begin a new subpath\n if (cmd === CMD.M && i > 1) {\n // Close previous subpath\n if (!isStroke) {\n w += windingLine(xi, yi, x0, y0, x, y);\n }\n // 如果被任何一个 subpath 包含\n // if (w !== 0) {\n // return true;\n // }\n }\n\n if (i == 1) {\n // 如果第一个命令是 L, C, Q\n // 则 previous point 同绘制命令的第一个 point\n //\n // 第一个命令为 Arc 的情况下会在后面特殊处理\n xi = data[i];\n yi = data[i + 1];\n\n x0 = xi;\n y0 = yi;\n }\n\n switch (cmd) {\n case CMD.M:\n // moveTo 命令重新创建一个新的 subpath, 并且更新新的起点\n // 在 closePath 的时候使用\n x0 = data[i++];\n y0 = data[i++];\n xi = x0;\n yi = y0;\n break;\n case CMD.L:\n if (isStroke) {\n if (line.containStroke(xi, yi, data[i], data[i + 1], lineWidth, x, y)) {\n return true;\n }\n }\n else {\n // NOTE 在第一个命令为 L, C, Q 的时候会计算出 NaN\n w += windingLine(xi, yi, data[i], data[i + 1], x, y) || 0;\n }\n xi = data[i++];\n yi = data[i++];\n break;\n case CMD.C:\n if (isStroke) {\n if (cubic.containStroke(xi, yi,\n data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1],\n lineWidth, x, y\n )) {\n return true;\n }\n }\n else {\n w += windingCubic(\n xi, yi,\n data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1],\n x, y\n ) || 0;\n }\n xi = data[i++];\n yi = data[i++];\n break;\n case CMD.Q:\n if (isStroke) {\n if (quadratic.containStroke(xi, yi,\n data[i++], data[i++], data[i], data[i + 1],\n lineWidth, x, y\n )) {\n return true;\n }\n }\n else {\n w += windingQuadratic(\n xi, yi,\n data[i++], data[i++], data[i], data[i + 1],\n x, y\n ) || 0;\n }\n xi = data[i++];\n yi = data[i++];\n break;\n case CMD.A:\n // TODO Arc 判断的开销比较大\n var cx = data[i++];\n var cy = data[i++];\n var rx = data[i++];\n var ry = data[i++];\n var theta = data[i++];\n var dTheta = data[i++];\n // TODO Arc 旋转\n var psi = data[i++];\n var anticlockwise = 1 - data[i++];\n var x1 = Math.cos(theta) * rx + cx;\n var y1 = Math.sin(theta) * ry + cy;\n // 不是直接使用 arc 命令\n if (i > 1) {\n w += windingLine(xi, yi, x1, y1, x, y);\n }\n else {\n // 第一个命令起点还未定义\n x0 = x1;\n y0 = y1;\n }\n // zr 使用scale来模拟椭圆, 这里也对x做一定的缩放\n var _x = (x - cx) * ry / rx + cx;\n if (isStroke) {\n if (arc.containStroke(\n cx, cy, ry, theta, theta + dTheta, anticlockwise,\n lineWidth, _x, y\n )) {\n return true;\n }\n }\n else {\n w += windingArc(\n cx, cy, ry, theta, theta + dTheta, anticlockwise,\n _x, y\n );\n }\n xi = Math.cos(theta + dTheta) * rx + cx;\n yi = Math.sin(theta + dTheta) * ry + cy;\n break;\n case CMD.R:\n x0 = xi = data[i++];\n y0 = yi = data[i++];\n var width = data[i++];\n var height = data[i++];\n var x1 = x0 + width;\n var y1 = y0 + height;\n if (isStroke) {\n if (line.containStroke(x0, y0, x1, y0, lineWidth, x, y)\n || line.containStroke(x1, y0, x1, y1, lineWidth, x, y)\n || line.containStroke(x1, y1, x0, y1, lineWidth, x, y)\n || line.containStroke(x0, y1, x0, y0, lineWidth, x, y)\n ) {\n return true;\n }\n }\n else {\n // FIXME Clockwise ?\n w += windingLine(x1, y0, x1, y1, x, y);\n w += windingLine(x0, y1, x0, y0, x, y);\n }\n break;\n case CMD.Z:\n if (isStroke) {\n if (line.containStroke(\n xi, yi, x0, y0, lineWidth, x, y\n )) {\n return true;\n }\n }\n else {\n // Close a subpath\n w += windingLine(xi, yi, x0, y0, x, y);\n // 如果被任何一个 subpath 包含\n // FIXME subpaths may overlap\n // if (w !== 0) {\n // return true;\n // }\n }\n xi = x0;\n yi = y0;\n break;\n }\n }\n if (!isStroke && !isAroundEqual(yi, y0)) {\n w += windingLine(xi, yi, x0, y0, x, y) || 0;\n }\n return w !== 0;\n}\n\nexport function contain(pathData, x, y) {\n return containPath(pathData, 0, false, x, y);\n}\n\nexport function containStroke(pathData, lineWidth, x, y) {\n return containPath(pathData, lineWidth, true, x, y);\n}","import Displayable from './Displayable';\nimport * as zrUtil from '../core/util';\nimport PathProxy from '../core/PathProxy';\nimport * as pathContain from '../contain/path';\nimport Pattern from './Pattern';\n\nvar getCanvasPattern = Pattern.prototype.getCanvasPattern;\n\nvar abs = Math.abs;\n\nvar pathProxyForDraw = new PathProxy(true);\n/**\n * @alias module:zrender/graphic/Path\n * @extends module:zrender/graphic/Displayable\n * @constructor\n * @param {Object} opts\n */\nfunction Path(opts) {\n Displayable.call(this, opts);\n\n /**\n * @type {module:zrender/core/PathProxy}\n * @readOnly\n */\n this.path = null;\n}\n\nPath.prototype = {\n\n constructor: Path,\n\n type: 'path',\n\n __dirtyPath: true,\n\n strokeContainThreshold: 5,\n\n brush: function (ctx, prevEl) {\n var style = this.style;\n var path = this.path || pathProxyForDraw;\n var hasStroke = style.hasStroke();\n var hasFill = style.hasFill();\n var fill = style.fill;\n var stroke = style.stroke;\n var hasFillGradient = hasFill && !!(fill.colorStops);\n var hasStrokeGradient = hasStroke && !!(stroke.colorStops);\n var hasFillPattern = hasFill && !!(fill.image);\n var hasStrokePattern = hasStroke && !!(stroke.image);\n\n style.bind(ctx, this, prevEl);\n this.setTransform(ctx);\n\n if (this.__dirty) {\n var rect;\n // Update gradient because bounding rect may changed\n if (hasFillGradient) {\n rect = rect || this.getBoundingRect();\n this._fillGradient = style.getGradient(ctx, fill, rect);\n }\n if (hasStrokeGradient) {\n rect = rect || this.getBoundingRect();\n this._strokeGradient = style.getGradient(ctx, stroke, rect);\n }\n }\n // Use the gradient or pattern\n if (hasFillGradient) {\n // PENDING If may have affect the state\n ctx.fillStyle = this._fillGradient;\n }\n else if (hasFillPattern) {\n ctx.fillStyle = getCanvasPattern.call(fill, ctx);\n }\n if (hasStrokeGradient) {\n ctx.strokeStyle = this._strokeGradient;\n }\n else if (hasStrokePattern) {\n ctx.strokeStyle = getCanvasPattern.call(stroke, ctx);\n }\n\n var lineDash = style.lineDash;\n var lineDashOffset = style.lineDashOffset;\n\n var ctxLineDash = !!ctx.setLineDash;\n\n // Update path sx, sy\n var scale = this.getGlobalScale();\n path.setScale(scale[0], scale[1]);\n\n // Proxy context\n // Rebuild path in following 2 cases\n // 1. Path is dirty\n // 2. Path needs javascript implemented lineDash stroking.\n // In this case, lineDash information will not be saved in PathProxy\n if (this.__dirtyPath\n || (lineDash && !ctxLineDash && hasStroke)\n ) {\n path.beginPath(ctx);\n\n // Setting line dash before build path\n if (lineDash && !ctxLineDash) {\n path.setLineDash(lineDash);\n path.setLineDashOffset(lineDashOffset);\n }\n\n this.buildPath(path, this.shape, false);\n\n // Clear path dirty flag\n if (this.path) {\n this.__dirtyPath = false;\n }\n }\n else {\n // Replay path building\n ctx.beginPath();\n this.path.rebuildPath(ctx);\n }\n\n hasFill && path.fill(ctx);\n\n if (lineDash && ctxLineDash) {\n ctx.setLineDash(lineDash);\n ctx.lineDashOffset = lineDashOffset;\n }\n\n hasStroke && path.stroke(ctx);\n\n if (lineDash && ctxLineDash) {\n // PENDING\n // Remove lineDash\n ctx.setLineDash([]);\n }\n\n this.restoreTransform(ctx);\n\n // Draw rect text\n if (style.text != null) {\n this.drawRectText(ctx, this.getBoundingRect());\n }\n },\n\n // When bundling path, some shape may decide if use moveTo to begin a new subpath or closePath\n // Like in circle\n buildPath: function (ctx, shapeCfg, inBundle) {},\n\n createPathProxy: function () {\n this.path = new PathProxy();\n },\n\n getBoundingRect: function () {\n var rect = this._rect;\n var style = this.style;\n var needsUpdateRect = !rect;\n if (needsUpdateRect) {\n var path = this.path;\n if (!path) {\n // Create path on demand.\n path = this.path = new PathProxy();\n }\n if (this.__dirtyPath) {\n path.beginPath();\n this.buildPath(path, this.shape, false);\n }\n rect = path.getBoundingRect();\n }\n this._rect = rect;\n\n if (style.hasStroke()) {\n // Needs update rect with stroke lineWidth when\n // 1. Element changes scale or lineWidth\n // 2. Shape is changed\n var rectWithStroke = this._rectWithStroke || (this._rectWithStroke = rect.clone());\n if (this.__dirty || needsUpdateRect) {\n rectWithStroke.copy(rect);\n // FIXME Must after updateTransform\n var w = style.lineWidth;\n // PENDING, Min line width is needed when line is horizontal or vertical\n var lineScale = style.strokeNoScale ? this.getLineScale() : 1;\n\n // Only add extra hover lineWidth when there are no fill\n if (!style.hasFill()) {\n w = Math.max(w, this.strokeContainThreshold || 4);\n }\n // Consider line width\n // Line scale can't be 0;\n if (lineScale > 1e-10) {\n rectWithStroke.width += w / lineScale;\n rectWithStroke.height += w / lineScale;\n rectWithStroke.x -= w / lineScale / 2;\n rectWithStroke.y -= w / lineScale / 2;\n }\n }\n\n // Return rect with stroke\n return rectWithStroke;\n }\n\n return rect;\n },\n\n contain: function (x, y) {\n var localPos = this.transformCoordToLocal(x, y);\n var rect = this.getBoundingRect();\n var style = this.style;\n x = localPos[0];\n y = localPos[1];\n\n if (rect.contain(x, y)) {\n var pathData = this.path.data;\n if (style.hasStroke()) {\n var lineWidth = style.lineWidth;\n var lineScale = style.strokeNoScale ? this.getLineScale() : 1;\n // Line scale can't be 0;\n if (lineScale > 1e-10) {\n // Only add extra hover lineWidth when there are no fill\n if (!style.hasFill()) {\n lineWidth = Math.max(lineWidth, this.strokeContainThreshold);\n }\n if (pathContain.containStroke(\n pathData, lineWidth / lineScale, x, y\n )) {\n return true;\n }\n }\n }\n if (style.hasFill()) {\n return pathContain.contain(pathData, x, y);\n }\n }\n return false;\n },\n\n /**\n * @param {boolean} dirtyPath\n */\n dirty: function (dirtyPath) {\n if (dirtyPath == null) {\n dirtyPath = true;\n }\n // Only mark dirty, not mark clean\n if (dirtyPath) {\n this.__dirtyPath = dirtyPath;\n this._rect = null;\n }\n\n this.__dirty = true;\n\n this.__zr && this.__zr.refresh();\n\n // Used as a clipping path\n if (this.__clipTarget) {\n this.__clipTarget.dirty();\n }\n },\n\n /**\n * Alias for animate('shape')\n * @param {boolean} loop\n */\n animateShape: function (loop) {\n return this.animate('shape', loop);\n },\n\n // Overwrite attrKV\n attrKV: function (key, value) {\n // FIXME\n if (key === 'shape') {\n this.setShape(value);\n this.__dirtyPath = true;\n this._rect = null;\n }\n else {\n Displayable.prototype.attrKV.call(this, key, value);\n }\n },\n\n /**\n * @param {Object|string} key\n * @param {*} value\n */\n setShape: function (key, value) {\n var shape = this.shape;\n // Path from string may not have shape\n if (shape) {\n if (zrUtil.isObject(key)) {\n for (var name in key) {\n if (key.hasOwnProperty(name)) {\n shape[name] = key[name];\n }\n }\n }\n else {\n shape[key] = value;\n }\n this.dirty(true);\n }\n return this;\n },\n\n getLineScale: function () {\n var m = this.transform;\n // Get the line scale.\n // Determinant of `m` means how much the area is enlarged by the\n // transformation. So its square root can be used as a scale factor\n // for width.\n return m && abs(m[0] - 1) > 1e-10 && abs(m[3] - 1) > 1e-10\n ? Math.sqrt(abs(m[0] * m[3] - m[2] * m[1]))\n : 1;\n }\n};\n\n/**\n * 扩展一个 Path element, 比如星形,圆等。\n * Extend a path element\n * @param {Object} props\n * @param {string} props.type Path type\n * @param {Function} props.init Initialize\n * @param {Function} props.buildPath Overwrite buildPath method\n * @param {Object} [props.style] Extended default style config\n * @param {Object} [props.shape] Extended default shape config\n */\nPath.extend = function (defaults) {\n var Sub = function (opts) {\n Path.call(this, opts);\n\n if (defaults.style) {\n // Extend default style\n this.style.extendFrom(defaults.style, false);\n }\n\n // Extend default shape\n var defaultShape = defaults.shape;\n if (defaultShape) {\n this.shape = this.shape || {};\n var thisShape = this.shape;\n for (var name in defaultShape) {\n if (\n ! thisShape.hasOwnProperty(name)\n && defaultShape.hasOwnProperty(name)\n ) {\n thisShape[name] = defaultShape[name];\n }\n }\n }\n\n defaults.init && defaults.init.call(this, opts);\n };\n\n zrUtil.inherits(Sub, Path);\n\n // FIXME 不能 extend position, rotation 等引用对象\n for (var name in defaults) {\n // Extending prototype values and methods\n if (name !== 'style' && name !== 'shape') {\n Sub.prototype[name] = defaults[name];\n }\n }\n\n return Sub;\n};\n\nzrUtil.inherits(Path, Displayable);\n\nexport default Path;","import PathProxy from '../core/PathProxy';\nimport {applyTransform as v2ApplyTransform} from '../core/vector';\n\nvar CMD = PathProxy.CMD;\n\nvar points = [[], [], []];\nvar mathSqrt = Math.sqrt;\nvar mathAtan2 = Math.atan2;\n\nexport default function (path, m) {\n var data = path.data;\n var cmd;\n var nPoint;\n var i;\n var j;\n var k;\n var p;\n\n var M = CMD.M;\n var C = CMD.C;\n var L = CMD.L;\n var R = CMD.R;\n var A = CMD.A;\n var Q = CMD.Q;\n\n for (i = 0, j = 0; i < data.length;) {\n cmd = data[i++];\n j = i;\n nPoint = 0;\n\n switch (cmd) {\n case M:\n nPoint = 1;\n break;\n case L:\n nPoint = 1;\n break;\n case C:\n nPoint = 3;\n break;\n case Q:\n nPoint = 2;\n break;\n case A:\n var x = m[4];\n var y = m[5];\n var sx = mathSqrt(m[0] * m[0] + m[1] * m[1]);\n var sy = mathSqrt(m[2] * m[2] + m[3] * m[3]);\n var angle = mathAtan2(-m[1] / sy, m[0] / sx);\n // cx\n data[i] *= sx;\n data[i++] += x;\n // cy\n data[i] *= sy;\n data[i++] += y;\n // Scale rx and ry\n // FIXME Assume psi is 0 here\n data[i++] *= sx;\n data[i++] *= sy;\n\n // Start angle\n data[i++] += angle;\n // end angle\n data[i++] += angle;\n // FIXME psi\n i += 2;\n j = i;\n break;\n case R:\n // x0, y0\n p[0] = data[i++];\n p[1] = data[i++];\n v2ApplyTransform(p, p, m);\n data[j++] = p[0];\n data[j++] = p[1];\n // x1, y1\n p[0] += data[i++];\n p[1] += data[i++];\n v2ApplyTransform(p, p, m);\n data[j++] = p[0];\n data[j++] = p[1];\n }\n\n for (k = 0; k < nPoint; k++) {\n var p = points[k];\n p[0] = data[i++];\n p[1] = data[i++];\n\n v2ApplyTransform(p, p, m);\n // Write back\n data[j++] = p[0];\n data[j++] = p[1];\n }\n }\n}\n","import Path from '../graphic/Path';\nimport PathProxy from '../core/PathProxy';\nimport transformPath from './transformPath';\n\n// command chars\nvar cc = [\n 'm', 'M', 'l', 'L', 'v', 'V', 'h', 'H', 'z', 'Z',\n 'c', 'C', 'q', 'Q', 't', 'T', 's', 'S', 'a', 'A'\n];\n\nvar mathSqrt = Math.sqrt;\nvar mathSin = Math.sin;\nvar mathCos = Math.cos;\nvar PI = Math.PI;\n\nvar vMag = function(v) {\n return Math.sqrt(v[0] * v[0] + v[1] * v[1]);\n};\nvar vRatio = function(u, v) {\n return (u[0] * v[0] + u[1] * v[1]) / (vMag(u) * vMag(v));\n};\nvar vAngle = function(u, v) {\n return (u[0] * v[1] < u[1] * v[0] ? -1 : 1)\n * Math.acos(vRatio(u, v));\n};\n\nfunction processArc(x1, y1, x2, y2, fa, fs, rx, ry, psiDeg, cmd, path) {\n var psi = psiDeg * (PI / 180.0);\n var xp = mathCos(psi) * (x1 - x2) / 2.0\n + mathSin(psi) * (y1 - y2) / 2.0;\n var yp = -1 * mathSin(psi) * (x1 - x2) / 2.0\n + mathCos(psi) * (y1 - y2) / 2.0;\n\n var lambda = (xp * xp) / (rx * rx) + (yp * yp) / (ry * ry);\n\n if (lambda > 1) {\n rx *= mathSqrt(lambda);\n ry *= mathSqrt(lambda);\n }\n\n var f = (fa === fs ? -1 : 1)\n * mathSqrt((((rx * rx) * (ry * ry))\n - ((rx * rx) * (yp * yp))\n - ((ry * ry) * (xp * xp))) / ((rx * rx) * (yp * yp)\n + (ry * ry) * (xp * xp))\n ) || 0;\n\n var cxp = f * rx * yp / ry;\n var cyp = f * -ry * xp / rx;\n\n var cx = (x1 + x2) / 2.0\n + mathCos(psi) * cxp\n - mathSin(psi) * cyp;\n var cy = (y1 + y2) / 2.0\n + mathSin(psi) * cxp\n + mathCos(psi) * cyp;\n\n var theta = vAngle([ 1, 0 ], [ (xp - cxp) / rx, (yp - cyp) / ry ]);\n var u = [ (xp - cxp) / rx, (yp - cyp) / ry ];\n var v = [ (-1 * xp - cxp) / rx, (-1 * yp - cyp) / ry ];\n var dTheta = vAngle(u, v);\n\n if (vRatio(u, v) <= -1) {\n dTheta = PI;\n }\n if (vRatio(u, v) >= 1) {\n dTheta = 0;\n }\n if (fs === 0 && dTheta > 0) {\n dTheta = dTheta - 2 * PI;\n }\n if (fs === 1 && dTheta < 0) {\n dTheta = dTheta + 2 * PI;\n }\n\n path.addData(cmd, cx, cy, rx, ry, theta, dTheta, psi, fs);\n}\n\nfunction createPathProxyFromString(data) {\n if (!data) {\n return [];\n }\n\n // command string\n var cs = data.replace(/-/g, ' -')\n .replace(/ /g, ' ')\n .replace(/ /g, ',')\n .replace(/,,/g, ',');\n\n var n;\n // create pipes so that we can split the data\n for (n = 0; n < cc.length; n++) {\n cs = cs.replace(new RegExp(cc[n], 'g'), '|' + cc[n]);\n }\n\n // create array\n var arr = cs.split('|');\n // init context point\n var cpx = 0;\n var cpy = 0;\n\n var path = new PathProxy();\n var CMD = PathProxy.CMD;\n\n var prevCmd;\n for (n = 1; n < arr.length; n++) {\n var str = arr[n];\n var c = str.charAt(0);\n var off = 0;\n var p = str.slice(1).replace(/e,-/g, 'e-').split(',');\n var cmd;\n\n if (p.length > 0 && p[0] === '') {\n p.shift();\n }\n\n for (var i = 0; i < p.length; i++) {\n p[i] = parseFloat(p[i]);\n }\n while (off < p.length && !isNaN(p[off])) {\n if (isNaN(p[0])) {\n break;\n }\n var ctlPtx;\n var ctlPty;\n\n var rx;\n var ry;\n var psi;\n var fa;\n var fs;\n\n var x1 = cpx;\n var y1 = cpy;\n\n // convert l, H, h, V, and v to L\n switch (c) {\n case 'l':\n cpx += p[off++];\n cpy += p[off++];\n cmd = CMD.L;\n path.addData(cmd, cpx, cpy);\n break;\n case 'L':\n cpx = p[off++];\n cpy = p[off++];\n cmd = CMD.L;\n path.addData(cmd, cpx, cpy);\n break;\n case 'm':\n cpx += p[off++];\n cpy += p[off++];\n cmd = CMD.M;\n path.addData(cmd, cpx, cpy);\n c = 'l';\n break;\n case 'M':\n cpx = p[off++];\n cpy = p[off++];\n cmd = CMD.M;\n path.addData(cmd, cpx, cpy);\n c = 'L';\n break;\n case 'h':\n cpx += p[off++];\n cmd = CMD.L;\n path.addData(cmd, cpx, cpy);\n break;\n case 'H':\n cpx = p[off++];\n cmd = CMD.L;\n path.addData(cmd, cpx, cpy);\n break;\n case 'v':\n cpy += p[off++];\n cmd = CMD.L;\n path.addData(cmd, cpx, cpy);\n break;\n case 'V':\n cpy = p[off++];\n cmd = CMD.L;\n path.addData(cmd, cpx, cpy);\n break;\n case 'C':\n cmd = CMD.C;\n path.addData(\n cmd, p[off++], p[off++], p[off++], p[off++], p[off++], p[off++]\n );\n cpx = p[off - 2];\n cpy = p[off - 1];\n break;\n case 'c':\n cmd = CMD.C;\n path.addData(\n cmd,\n p[off++] + cpx, p[off++] + cpy,\n p[off++] + cpx, p[off++] + cpy,\n p[off++] + cpx, p[off++] + cpy\n );\n cpx += p[off - 2];\n cpy += p[off - 1];\n break;\n case 'S':\n ctlPtx = cpx;\n ctlPty = cpy;\n var len = path.len();\n var pathData = path.data;\n if (prevCmd === CMD.C) {\n ctlPtx += cpx - pathData[len - 4];\n ctlPty += cpy - pathData[len - 3];\n }\n cmd = CMD.C;\n x1 = p[off++];\n y1 = p[off++];\n cpx = p[off++];\n cpy = p[off++];\n path.addData(cmd, ctlPtx, ctlPty, x1, y1, cpx, cpy);\n break;\n case 's':\n ctlPtx = cpx;\n ctlPty = cpy;\n var len = path.len();\n var pathData = path.data;\n if (prevCmd === CMD.C) {\n ctlPtx += cpx - pathData[len - 4];\n ctlPty += cpy - pathData[len - 3];\n }\n cmd = CMD.C;\n x1 = cpx + p[off++];\n y1 = cpy + p[off++];\n cpx += p[off++];\n cpy += p[off++];\n path.addData(cmd, ctlPtx, ctlPty, x1, y1, cpx, cpy);\n break;\n case 'Q':\n x1 = p[off++];\n y1 = p[off++];\n cpx = p[off++];\n cpy = p[off++];\n cmd = CMD.Q;\n path.addData(cmd, x1, y1, cpx, cpy);\n break;\n case 'q':\n x1 = p[off++] + cpx;\n y1 = p[off++] + cpy;\n cpx += p[off++];\n cpy += p[off++];\n cmd = CMD.Q;\n path.addData(cmd, x1, y1, cpx, cpy);\n break;\n case 'T':\n ctlPtx = cpx;\n ctlPty = cpy;\n var len = path.len();\n var pathData = path.data;\n if (prevCmd === CMD.Q) {\n ctlPtx += cpx - pathData[len - 4];\n ctlPty += cpy - pathData[len - 3];\n }\n cpx = p[off++];\n cpy = p[off++];\n cmd = CMD.Q;\n path.addData(cmd, ctlPtx, ctlPty, cpx, cpy);\n break;\n case 't':\n ctlPtx = cpx;\n ctlPty = cpy;\n var len = path.len();\n var pathData = path.data;\n if (prevCmd === CMD.Q) {\n ctlPtx += cpx - pathData[len - 4];\n ctlPty += cpy - pathData[len - 3];\n }\n cpx += p[off++];\n cpy += p[off++];\n cmd = CMD.Q;\n path.addData(cmd, ctlPtx, ctlPty, cpx, cpy);\n break;\n case 'A':\n rx = p[off++];\n ry = p[off++];\n psi = p[off++];\n fa = p[off++];\n fs = p[off++];\n\n x1 = cpx, y1 = cpy;\n cpx = p[off++];\n cpy = p[off++];\n cmd = CMD.A;\n processArc(\n x1, y1, cpx, cpy, fa, fs, rx, ry, psi, cmd, path\n );\n break;\n case 'a':\n rx = p[off++];\n ry = p[off++];\n psi = p[off++];\n fa = p[off++];\n fs = p[off++];\n\n x1 = cpx, y1 = cpy;\n cpx += p[off++];\n cpy += p[off++];\n cmd = CMD.A;\n processArc(\n x1, y1, cpx, cpy, fa, fs, rx, ry, psi, cmd, path\n );\n break;\n }\n }\n\n if (c === 'z' || c === 'Z') {\n cmd = CMD.Z;\n path.addData(cmd);\n }\n\n prevCmd = cmd;\n }\n\n path.toStatic();\n\n return path;\n}\n\n// TODO Optimize double memory cost problem\nfunction createPathOptions(str, opts) {\n var pathProxy = createPathProxyFromString(str);\n opts = opts || {};\n opts.buildPath = function (path) {\n if (path.setData) {\n path.setData(pathProxy.data);\n // Svg and vml renderer don't have context\n var ctx = path.getContext();\n if (ctx) {\n path.rebuildPath(ctx);\n }\n }\n else {\n var ctx = path;\n pathProxy.rebuildPath(ctx);\n }\n };\n\n opts.applyTransform = function (m) {\n transformPath(pathProxy, m);\n\n this.dirty(true);\n };\n\n return opts;\n}\n\n/**\n * Create a Path object from path string data\n * http://www.w3.org/TR/SVG/paths.html#PathData\n * @param {Object} opts Other options\n */\nexport function createFromString(str, opts) {\n return new Path(createPathOptions(str, opts));\n}\n\n/**\n * Create a Path class from path string data\n * @param {string} str\n * @param {Object} opts Other options\n */\nexport function extendFromString(str, opts) {\n return Path.extend(createPathOptions(str, opts));\n}\n\n/**\n * Merge multiple paths\n */\n// TODO Apply transform\n// TODO stroke dash\n// TODO Optimize double memory cost problem\nexport function mergePath(pathEls, opts) {\n var pathList = [];\n var len = pathEls.length;\n for (var i = 0; i < len; i++) {\n var pathEl = pathEls[i];\n if (!pathEl.path) {\n pathEl.createPathProxy();\n }\n if (pathEl.__dirtyPath) {\n pathEl.buildPath(pathEl.path, pathEl.shape, true);\n }\n pathList.push(pathEl.path);\n }\n\n var pathBundle = new Path(opts);\n // Need path proxy.\n pathBundle.createPathProxy();\n pathBundle.buildPath = function (path) {\n path.appendPath(pathList);\n // Svg and vml renderer don't have context\n var ctx = path.getContext();\n if (ctx) {\n path.rebuildPath(ctx);\n }\n };\n\n return pathBundle;\n}","import Displayable from './Displayable';\nimport * as zrUtil from '../core/util';\nimport * as textContain from '../contain/text';\nimport * as textHelper from './helper/text';\n\n/**\n * @alias zrender/graphic/Text\n * @extends module:zrender/graphic/Displayable\n * @constructor\n * @param {Object} opts\n */\nvar Text = function (opts) { // jshint ignore:line\n Displayable.call(this, opts);\n};\n\nText.prototype = {\n\n constructor: Text,\n\n type: 'text',\n\n brush: function (ctx, prevEl) {\n var style = this.style;\n\n // Optimize, avoid normalize every time.\n this.__dirty && textHelper.normalizeTextStyle(style, true);\n\n // Use props with prefix 'text'.\n style.fill = style.stroke = style.shadowBlur = style.shadowColor =\n style.shadowOffsetX = style.shadowOffsetY = null;\n\n var text = style.text;\n // Convert to string\n text != null && (text += '');\n\n // Always bind style\n style.bind(ctx, this, prevEl);\n\n if (!textHelper.needDrawText(text, style)) {\n return;\n }\n\n this.setTransform(ctx);\n\n textHelper.renderText(this, ctx, text, style);\n\n this.restoreTransform(ctx);\n },\n\n getBoundingRect: function () {\n var style = this.style;\n\n // Optimize, avoid normalize every time.\n this.__dirty && textHelper.normalizeTextStyle(style, true);\n\n if (!this._rect) {\n var text = style.text;\n text != null ? (text += '') : (text = '');\n\n var rect = textContain.getBoundingRect(\n style.text + '',\n style.font,\n style.textAlign,\n style.textVerticalAlign,\n style.textPadding,\n style.rich\n );\n\n rect.x += style.x || 0;\n rect.y += style.y || 0;\n\n if (textHelper.getStroke(style.textStroke, style.textStrokeWidth)) {\n var w = style.textStrokeWidth;\n rect.x -= w / 2;\n rect.y -= w / 2;\n rect.width += w;\n rect.height += w;\n }\n\n this._rect = rect;\n }\n\n return this._rect;\n }\n};\n\nzrUtil.inherits(Text, Displayable);\n\nexport default Text;","/**\n * 圆形\n * @module zrender/shape/Circle\n */\n\nimport Path from '../Path';\n\nexport default Path.extend({\n\n type: 'circle',\n\n shape: {\n cx: 0,\n cy: 0,\n r: 0\n },\n\n\n buildPath : function (ctx, shape, inBundle) {\n // Better stroking in ShapeBundle\n // Always do it may have performence issue ( fill may be 2x more cost)\n if (inBundle) {\n ctx.moveTo(shape.cx + shape.r, shape.cy);\n }\n // else {\n // if (ctx.allocate && !ctx.data.length) {\n // ctx.allocate(ctx.CMD_MEM_SIZE.A);\n // }\n // }\n // Better stroking in ShapeBundle\n // ctx.moveTo(shape.cx + shape.r, shape.cy);\n ctx.arc(shape.cx, shape.cy, shape.r, 0, Math.PI * 2, true);\n }\n});","import env from '../../core/env';\n\n// Fix weird bug in some version of IE11 (like 11.0.9600.178**),\n// where exception \"unexpected call to method or property access\"\n// might be thrown when calling ctx.fill or ctx.stroke after a path\n// whose area size is zero is drawn and ctx.clip() is called and\n// shadowBlur is set. See #4572, #3112, #5777.\n// (e.g.,\n// ctx.moveTo(10, 10);\n// ctx.lineTo(20, 10);\n// ctx.closePath();\n// ctx.clip();\n// ctx.shadowBlur = 10;\n// ...\n// ctx.fill();\n// )\n\nvar shadowTemp = [\n ['shadowBlur', 0],\n ['shadowColor', '#000'],\n ['shadowOffsetX', 0],\n ['shadowOffsetY', 0]\n];\n\nexport default function (orignalBrush) {\n\n // version string can be: '11.0'\n return (env.browser.ie && env.browser.version >= 11)\n\n ? function () {\n var clipPaths = this.__clipPaths;\n var style = this.style;\n var modified;\n\n if (clipPaths) {\n for (var i = 0; i < clipPaths.length; i++) {\n var clipPath = clipPaths[i];\n var shape = clipPath && clipPath.shape;\n var type = clipPath && clipPath.type;\n\n if (shape && (\n (type === 'sector' && shape.startAngle === shape.endAngle)\n || (type === 'rect' && (!shape.width || !shape.height))\n )) {\n for (var j = 0; j < shadowTemp.length; j++) {\n // It is save to put shadowTemp static, because shadowTemp\n // will be all modified each item brush called.\n shadowTemp[j][2] = style[shadowTemp[j][0]];\n style[shadowTemp[j][0]] = shadowTemp[j][1];\n }\n modified = true;\n break;\n }\n }\n }\n\n orignalBrush.apply(this, arguments);\n\n if (modified) {\n for (var j = 0; j < shadowTemp.length; j++) {\n style[shadowTemp[j][0]] = shadowTemp[j][2];\n }\n }\n }\n\n : orignalBrush;\n}\n","/**\n * 扇形\n * @module zrender/graphic/shape/Sector\n */\n\nimport Path from '../Path';\nimport fixClipWithShadow from '../helper/fixClipWithShadow';\n\nexport default Path.extend({\n\n type: 'sector',\n\n shape: {\n\n cx: 0,\n\n cy: 0,\n\n r0: 0,\n\n r: 0,\n\n startAngle: 0,\n\n endAngle: Math.PI * 2,\n\n clockwise: true\n },\n\n brush: fixClipWithShadow(Path.prototype.brush),\n\n buildPath: function (ctx, shape) {\n\n var x = shape.cx;\n var y = shape.cy;\n var r0 = Math.max(shape.r0 || 0, 0);\n var r = Math.max(shape.r, 0);\n var startAngle = shape.startAngle;\n var endAngle = shape.endAngle;\n var clockwise = shape.clockwise;\n\n var unitX = Math.cos(startAngle);\n var unitY = Math.sin(startAngle);\n\n ctx.moveTo(unitX * r0 + x, unitY * r0 + y);\n\n ctx.lineTo(unitX * r + x, unitY * r + y);\n\n ctx.arc(x, y, r, startAngle, endAngle, !clockwise);\n\n ctx.lineTo(\n Math.cos(endAngle) * r0 + x,\n Math.sin(endAngle) * r0 + y\n );\n\n if (r0 !== 0) {\n ctx.arc(x, y, r0, endAngle, startAngle, clockwise);\n }\n\n ctx.closePath();\n }\n});","/**\n * 圆环\n * @module zrender/graphic/shape/Ring\n */\n\nimport Path from '../Path';\n\nexport default Path.extend({\n\n type: 'ring',\n\n shape: {\n cx: 0,\n cy: 0,\n r: 0,\n r0: 0\n },\n\n buildPath: function (ctx, shape) {\n var x = shape.cx;\n var y = shape.cy;\n var PI2 = Math.PI * 2;\n ctx.moveTo(x + shape.r, y);\n ctx.arc(x, y, shape.r, 0, PI2, false);\n ctx.moveTo(x + shape.r0, y);\n ctx.arc(x, y, shape.r0, 0, PI2, true);\n }\n});","/**\n * Catmull-Rom spline 插值折线\n * @module zrender/shape/util/smoothSpline\n * @author pissang (https://www.github.com/pissang)\n * Kener (@Kener-林峰, kener.linfeng@gmail.com)\n * errorrik (errorrik@gmail.com)\n */\n\nimport {distance as v2Distance} from '../../core/vector';\n\n/**\n * @inner\n */\nfunction interpolate(p0, p1, p2, p3, t, t2, t3) {\n var v0 = (p2 - p0) * 0.5;\n var v1 = (p3 - p1) * 0.5;\n return (2 * (p1 - p2) + v0 + v1) * t3\n + (-3 * (p1 - p2) - 2 * v0 - v1) * t2\n + v0 * t + p1;\n}\n\n/**\n * @alias module:zrender/shape/util/smoothSpline\n * @param {Array} points 线段顶点数组\n * @param {boolean} isLoop\n * @return {Array}\n */\nexport default function (points, isLoop) {\n var len = points.length;\n var ret = [];\n\n var distance = 0;\n for (var i = 1; i < len; i++) {\n distance += v2Distance(points[i - 1], points[i]);\n }\n\n var segs = distance / 2;\n segs = segs < len ? len : segs;\n for (var i = 0; i < segs; i++) {\n var pos = i / (segs - 1) * (isLoop ? len : len - 1);\n var idx = Math.floor(pos);\n\n var w = pos - idx;\n\n var p0;\n var p1 = points[idx % len];\n var p2;\n var p3;\n if (!isLoop) {\n p0 = points[idx === 0 ? idx : idx - 1];\n p2 = points[idx > len - 2 ? len - 1 : idx + 1];\n p3 = points[idx > len - 3 ? len - 1 : idx + 2];\n }\n else {\n p0 = points[(idx - 1 + len) % len];\n p2 = points[(idx + 1) % len];\n p3 = points[(idx + 2) % len];\n }\n\n var w2 = w * w;\n var w3 = w * w2;\n\n ret.push([\n interpolate(p0[0], p1[0], p2[0], p3[0], w, w2, w3),\n interpolate(p0[1], p1[1], p2[1], p3[1], w, w2, w3)\n ]);\n }\n return ret;\n}","/**\n * 贝塞尔平滑曲线\n * @module zrender/shape/util/smoothBezier\n * @author pissang (https://www.github.com/pissang)\n * Kener (@Kener-林峰, kener.linfeng@gmail.com)\n * errorrik (errorrik@gmail.com)\n */\n\nimport {\n min as v2Min,\n max as v2Max,\n scale as v2Scale,\n distance as v2Distance,\n add as v2Add,\n clone as v2Clone,\n sub as v2Sub\n} from '../../core/vector';\n\n/**\n * 贝塞尔平滑曲线\n * @alias module:zrender/shape/util/smoothBezier\n * @param {Array} points 线段顶点数组\n * @param {number} smooth 平滑等级, 0-1\n * @param {boolean} isLoop\n * @param {Array} constraint 将计算出来的控制点约束在一个包围盒内\n * 比如 [[0, 0], [100, 100]], 这个包围盒会与\n * 整个折线的包围盒做一个并集用来约束控制点。\n * @param {Array} 计算出来的控制点数组\n */\nexport default function (points, smooth, isLoop, constraint) {\n var cps = [];\n\n var v = [];\n var v1 = [];\n var v2 = [];\n var prevPoint;\n var nextPoint;\n\n var min, max;\n if (constraint) {\n min = [Infinity, Infinity];\n max = [-Infinity, -Infinity];\n for (var i = 0, len = points.length; i < len; i++) {\n v2Min(min, min, points[i]);\n v2Max(max, max, points[i]);\n }\n // 与指定的包围盒做并集\n v2Min(min, min, constraint[0]);\n v2Max(max, max, constraint[1]);\n }\n\n for (var i = 0, len = points.length; i < len; i++) {\n var point = points[i];\n\n if (isLoop) {\n prevPoint = points[i ? i - 1 : len - 1];\n nextPoint = points[(i + 1) % len];\n }\n else {\n if (i === 0 || i === len - 1) {\n cps.push(v2Clone(points[i]));\n continue;\n }\n else {\n prevPoint = points[i - 1];\n nextPoint = points[i + 1];\n }\n }\n\n v2Sub(v, nextPoint, prevPoint);\n\n // use degree to scale the handle length\n v2Scale(v, v, smooth);\n\n var d0 = v2Distance(point, prevPoint);\n var d1 = v2Distance(point, nextPoint);\n var sum = d0 + d1;\n if (sum !== 0) {\n d0 /= sum;\n d1 /= sum;\n }\n\n v2Scale(v1, v, -d0);\n v2Scale(v2, v, d1);\n var cp0 = v2Add([], point, v1);\n var cp1 = v2Add([], point, v2);\n if (constraint) {\n v2Max(cp0, cp0, min);\n v2Min(cp0, cp0, max);\n v2Max(cp1, cp1, min);\n v2Min(cp1, cp1, max);\n }\n cps.push(cp0);\n cps.push(cp1);\n }\n\n if (isLoop) {\n cps.push(cps.shift());\n }\n\n return cps;\n}","\nimport smoothSpline from './smoothSpline';\nimport smoothBezier from './smoothBezier';\n\nexport function buildPath(ctx, shape, closePath) {\n var points = shape.points;\n var smooth = shape.smooth;\n if (points && points.length >= 2) {\n if (smooth && smooth !== 'spline') {\n var controlPoints = smoothBezier(\n points, smooth, closePath, shape.smoothConstraint\n );\n\n ctx.moveTo(points[0][0], points[0][1]);\n var len = points.length;\n for (var i = 0; i < (closePath ? len : len - 1); i++) {\n var cp1 = controlPoints[i * 2];\n var cp2 = controlPoints[i * 2 + 1];\n var p = points[(i + 1) % len];\n ctx.bezierCurveTo(\n cp1[0], cp1[1], cp2[0], cp2[1], p[0], p[1]\n );\n }\n }\n else {\n if (smooth === 'spline') {\n points = smoothSpline(points, closePath);\n }\n\n ctx.moveTo(points[0][0], points[0][1]);\n for (var i = 1, l = points.length; i < l; i++) {\n ctx.lineTo(points[i][0], points[i][1]);\n }\n }\n\n closePath && ctx.closePath();\n }\n}\n","/**\n * 多边形\n * @module zrender/shape/Polygon\n */\n\nimport Path from '../Path';\nimport * as polyHelper from '../helper/poly';\n\nexport default Path.extend({\n\n type: 'polygon',\n\n shape: {\n points: null,\n\n smooth: false,\n\n smoothConstraint: null\n },\n\n buildPath: function (ctx, shape) {\n polyHelper.buildPath(ctx, shape, true);\n }\n});","/**\n * @module zrender/graphic/shape/Polyline\n */\n\nimport Path from '../Path';\nimport * as polyHelper from '../helper/poly';\n\nexport default Path.extend({\n\n type: 'polyline',\n\n shape: {\n points: null,\n\n smooth: false,\n\n smoothConstraint: null\n },\n\n style: {\n stroke: '#000',\n\n fill: null\n },\n\n buildPath: function (ctx, shape) {\n polyHelper.buildPath(ctx, shape, false);\n }\n});","/**\n * 矩形\n * @module zrender/graphic/shape/Rect\n */\n\nimport Path from '../Path';\nimport * as roundRectHelper from '../helper/roundRect';\n\nexport default Path.extend({\n\n type: 'rect',\n\n shape: {\n // 左上、右上、右下、左下角的半径依次为r1、r2、r3、r4\n // r缩写为1 相当于 [1, 1, 1, 1]\n // r缩写为[1] 相当于 [1, 1, 1, 1]\n // r缩写为[1, 2] 相当于 [1, 2, 1, 2]\n // r缩写为[1, 2, 3] 相当于 [1, 2, 3, 2]\n r: 0,\n\n x: 0,\n y: 0,\n width: 0,\n height: 0\n },\n\n buildPath: function (ctx, shape) {\n var x = shape.x;\n var y = shape.y;\n var width = shape.width;\n var height = shape.height;\n if (!shape.r) {\n ctx.rect(x, y, width, height);\n }\n else {\n roundRectHelper.buildPath(ctx, shape);\n }\n ctx.closePath();\n return;\n }\n});","/**\n * 直线\n * @module zrender/graphic/shape/Line\n */\n\nimport Path from '../Path';\n\nexport default Path.extend({\n\n type: 'line',\n\n shape: {\n // Start point\n x1: 0,\n y1: 0,\n // End point\n x2: 0,\n y2: 0,\n\n percent: 1\n },\n\n style: {\n stroke: '#000',\n fill: null\n },\n\n buildPath: function (ctx, shape) {\n var x1 = shape.x1;\n var y1 = shape.y1;\n var x2 = shape.x2;\n var y2 = shape.y2;\n var percent = shape.percent;\n\n if (percent === 0) {\n return;\n }\n\n ctx.moveTo(x1, y1);\n\n if (percent < 1) {\n x2 = x1 * (1 - percent) + x2 * percent;\n y2 = y1 * (1 - percent) + y2 * percent;\n }\n ctx.lineTo(x2, y2);\n },\n\n /**\n * Get point at percent\n * @param {number} percent\n * @return {Array.<number>}\n */\n pointAt: function (p) {\n var shape = this.shape;\n return [\n shape.x1 * (1 - p) + shape.x2 * p,\n shape.y1 * (1 - p) + shape.y2 * p\n ];\n }\n});","/**\n * 贝塞尔曲线\n * @module zrender/shape/BezierCurve\n */\n\nimport Path from '../Path';\nimport * as vec2 from '../../core/vector';\nimport {\n quadraticSubdivide,\n cubicSubdivide,\n quadraticAt,\n cubicAt,\n quadraticDerivativeAt,\n cubicDerivativeAt\n} from '../../core/curve';\n\nvar out = [];\n\nfunction someVectorAt(shape, t, isTangent) {\n var cpx2 = shape.cpx2;\n var cpy2 = shape.cpy2;\n if (cpx2 === null || cpy2 === null) {\n return [\n (isTangent ? cubicDerivativeAt : cubicAt)(shape.x1, shape.cpx1, shape.cpx2, shape.x2, t),\n (isTangent ? cubicDerivativeAt : cubicAt)(shape.y1, shape.cpy1, shape.cpy2, shape.y2, t)\n ];\n }\n else {\n return [\n (isTangent ? quadraticDerivativeAt : quadraticAt)(shape.x1, shape.cpx1, shape.x2, t),\n (isTangent ? quadraticDerivativeAt : quadraticAt)(shape.y1, shape.cpy1, shape.y2, t)\n ];\n }\n}\n\nexport default Path.extend({\n\n type: 'bezier-curve',\n\n shape: {\n x1: 0,\n y1: 0,\n x2: 0,\n y2: 0,\n cpx1: 0,\n cpy1: 0,\n // cpx2: 0,\n // cpy2: 0\n\n // Curve show percent, for animating\n percent: 1\n },\n\n style: {\n stroke: '#000',\n fill: null\n },\n\n buildPath: function (ctx, shape) {\n var x1 = shape.x1;\n var y1 = shape.y1;\n var x2 = shape.x2;\n var y2 = shape.y2;\n var cpx1 = shape.cpx1;\n var cpy1 = shape.cpy1;\n var cpx2 = shape.cpx2;\n var cpy2 = shape.cpy2;\n var percent = shape.percent;\n if (percent === 0) {\n return;\n }\n\n ctx.moveTo(x1, y1);\n\n if (cpx2 == null || cpy2 == null) {\n if (percent < 1) {\n quadraticSubdivide(\n x1, cpx1, x2, percent, out\n );\n cpx1 = out[1];\n x2 = out[2];\n quadraticSubdivide(\n y1, cpy1, y2, percent, out\n );\n cpy1 = out[1];\n y2 = out[2];\n }\n\n ctx.quadraticCurveTo(\n cpx1, cpy1,\n x2, y2\n );\n }\n else {\n if (percent < 1) {\n cubicSubdivide(\n x1, cpx1, cpx2, x2, percent, out\n );\n cpx1 = out[1];\n cpx2 = out[2];\n x2 = out[3];\n cubicSubdivide(\n y1, cpy1, cpy2, y2, percent, out\n );\n cpy1 = out[1];\n cpy2 = out[2];\n y2 = out[3];\n }\n ctx.bezierCurveTo(\n cpx1, cpy1,\n cpx2, cpy2,\n x2, y2\n );\n }\n },\n\n /**\n * Get point at percent\n * @param {number} t\n * @return {Array.<number>}\n */\n pointAt: function (t) {\n return someVectorAt(this.shape, t, false);\n },\n\n /**\n * Get tangent at percent\n * @param {number} t\n * @return {Array.<number>}\n */\n tangentAt: function (t) {\n var p = someVectorAt(this.shape, t, true);\n return vec2.normalize(p, p);\n }\n});","/**\n * 圆弧\n * @module zrender/graphic/shape/Arc\n */\n\nimport Path from '../Path';\n\nexport default Path.extend({\n\n type: 'arc',\n\n shape: {\n\n cx: 0,\n\n cy: 0,\n\n r: 0,\n\n startAngle: 0,\n\n endAngle: Math.PI * 2,\n\n clockwise: true\n },\n\n style: {\n\n stroke: '#000',\n\n fill: null\n },\n\n buildPath: function (ctx, shape) {\n\n var x = shape.cx;\n var y = shape.cy;\n var r = Math.max(shape.r, 0);\n var startAngle = shape.startAngle;\n var endAngle = shape.endAngle;\n var clockwise = shape.clockwise;\n\n var unitX = Math.cos(startAngle);\n var unitY = Math.sin(startAngle);\n\n ctx.moveTo(unitX * r + x, unitY * r + y);\n ctx.arc(x, y, r, startAngle, endAngle, !clockwise);\n }\n});","// CompoundPath to improve performance\n\nimport Path from './Path';\n\nexport default Path.extend({\n\n type: 'compound',\n\n shape: {\n\n paths: null\n },\n\n _updatePathDirty: function () {\n var dirtyPath = this.__dirtyPath;\n var paths = this.shape.paths;\n for (var i = 0; i < paths.length; i++) {\n // Mark as dirty if any subpath is dirty\n dirtyPath = dirtyPath || paths[i].__dirtyPath;\n }\n this.__dirtyPath = dirtyPath;\n this.__dirty = this.__dirty || dirtyPath;\n },\n\n beforeBrush: function () {\n this._updatePathDirty();\n var paths = this.shape.paths || [];\n var scale = this.getGlobalScale();\n // Update path scale\n for (var i = 0; i < paths.length; i++) {\n if (!paths[i].path) {\n paths[i].createPathProxy();\n }\n paths[i].path.setScale(scale[0], scale[1]);\n }\n },\n\n buildPath: function (ctx, shape) {\n var paths = shape.paths || [];\n for (var i = 0; i < paths.length; i++) {\n paths[i].buildPath(ctx, paths[i].shape, true);\n }\n },\n\n afterBrush: function () {\n var paths = this.shape.paths || [];\n for (var i = 0; i < paths.length; i++) {\n paths[i].__dirtyPath = false;\n }\n },\n\n getBoundingRect: function () {\n this._updatePathDirty();\n return Path.prototype.getBoundingRect.call(this);\n }\n});","\n/**\n * @param {Array.<Object>} colorStops\n */\nvar Gradient = function (colorStops) {\n\n this.colorStops = colorStops || [];\n\n};\n\nGradient.prototype = {\n\n constructor: Gradient,\n\n addColorStop: function (offset, color) {\n this.colorStops.push({\n\n offset: offset,\n\n color: color\n });\n }\n\n};\n\nexport default Gradient;","import * as zrUtil from '../core/util';\nimport Gradient from './Gradient';\n\n/**\n * x, y, x2, y2 are all percent from 0 to 1\n * @param {number} [x=0]\n * @param {number} [y=0]\n * @param {number} [x2=1]\n * @param {number} [y2=0]\n * @param {Array.<Object>} colorStops\n * @param {boolean} [globalCoord=false]\n */\nvar LinearGradient = function (x, y, x2, y2, colorStops, globalCoord) {\n // Should do nothing more in this constructor. Because gradient can be\n // declard by `color: {type: 'linear', colorStops: ...}`, where\n // this constructor will not be called.\n\n this.x = x == null ? 0 : x;\n\n this.y = y == null ? 0 : y;\n\n this.x2 = x2 == null ? 1 : x2;\n\n this.y2 = y2 == null ? 0 : y2;\n\n // Can be cloned\n this.type = 'linear';\n\n // If use global coord\n this.global = globalCoord || false;\n\n Gradient.call(this, colorStops);\n};\n\nLinearGradient.prototype = {\n\n constructor: LinearGradient\n};\n\nzrUtil.inherits(LinearGradient, Gradient);\n\nexport default LinearGradient;","import * as zrUtil from '../core/util';\nimport Gradient from './Gradient';\n\n/**\n * x, y, r are all percent from 0 to 1\n * @param {number} [x=0.5]\n * @param {number} [y=0.5]\n * @param {number} [r=0.5]\n * @param {Array.<Object>} [colorStops]\n * @param {boolean} [globalCoord=false]\n */\nvar RadialGradient = function (x, y, r, colorStops, globalCoord) {\n // Should do nothing more in this constructor. Because gradient can be\n // declard by `color: {type: 'radial', colorStops: ...}`, where\n // this constructor will not be called.\n\n this.x = x == null ? 0.5 : x;\n\n this.y = y == null ? 0.5 : y;\n\n this.r = r == null ? 0.5 : r;\n\n // Can be cloned\n this.type = 'radial';\n\n // If use global coord\n this.global = globalCoord || false;\n\n Gradient.call(this, colorStops);\n};\n\nRadialGradient.prototype = {\n\n constructor: RadialGradient\n};\n\nzrUtil.inherits(RadialGradient, Gradient);\n\nexport default RadialGradient;","import * as zrUtil from 'zrender/src/core/util';\nimport * as pathTool from 'zrender/src/tool/path';\nimport * as colorTool from 'zrender/src/tool/color';\nimport * as matrix from 'zrender/src/core/matrix';\nimport * as vector from 'zrender/src/core/vector';\nimport Path from 'zrender/src/graphic/Path';\nimport Transformable from 'zrender/src/mixin/Transformable';\nimport Image from 'zrender/src/graphic/Image';\nimport Group from 'zrender/src/container/Group';\nimport Text from 'zrender/src/graphic/Text';\nimport Circle from 'zrender/src/graphic/shape/Circle';\nimport Sector from 'zrender/src/graphic/shape/Sector';\nimport Ring from 'zrender/src/graphic/shape/Ring';\nimport Polygon from 'zrender/src/graphic/shape/Polygon';\nimport Polyline from 'zrender/src/graphic/shape/Polyline';\nimport Rect from 'zrender/src/graphic/shape/Rect';\nimport Line from 'zrender/src/graphic/shape/Line';\nimport BezierCurve from 'zrender/src/graphic/shape/BezierCurve';\nimport Arc from 'zrender/src/graphic/shape/Arc';\nimport CompoundPath from 'zrender/src/graphic/CompoundPath';\nimport LinearGradient from 'zrender/src/graphic/LinearGradient';\nimport RadialGradient from 'zrender/src/graphic/RadialGradient';\nimport BoundingRect from 'zrender/src/core/BoundingRect';\n\n\nvar round = Math.round;\nvar mathMax = Math.max;\nvar mathMin = Math.min;\n\nvar EMPTY_OBJ = {};\n\n/**\n * Extend shape with parameters\n */\nexport function extendShape(opts) {\n return Path.extend(opts);\n}\n\n/**\n * Extend path\n */\nexport function extendPath(pathData, opts) {\n return pathTool.extendFromString(pathData, opts);\n}\n\n/**\n * Create a path element from path data string\n * @param {string} pathData\n * @param {Object} opts\n * @param {module:zrender/core/BoundingRect} rect\n * @param {string} [layout=cover] 'center' or 'cover'\n */\nexport function makePath(pathData, opts, rect, layout) {\n var path = pathTool.createFromString(pathData, opts);\n var boundingRect = path.getBoundingRect();\n if (rect) {\n if (layout === 'center') {\n rect = centerGraphic(rect, boundingRect);\n }\n\n resizePath(path, rect);\n }\n return path;\n}\n\n/**\n * Create a image element from image url\n * @param {string} imageUrl image url\n * @param {Object} opts options\n * @param {module:zrender/core/BoundingRect} rect constrain rect\n * @param {string} [layout=cover] 'center' or 'cover'\n */\nexport function makeImage(imageUrl, rect, layout) {\n var path = new Image({\n style: {\n image: imageUrl,\n x: rect.x,\n y: rect.y,\n width: rect.width,\n height: rect.height\n },\n onload: function (img) {\n if (layout === 'center') {\n var boundingRect = {\n width: img.width,\n height: img.height\n };\n path.setStyle(centerGraphic(rect, boundingRect));\n }\n }\n });\n return path;\n}\n\n/**\n * Get position of centered element in bounding box.\n *\n * @param {Object} rect element local bounding box\n * @param {Object} boundingRect constraint bounding box\n * @return {Object} element position containing x, y, width, and height\n */\nfunction centerGraphic(rect, boundingRect) {\n // Set rect to center, keep width / height ratio.\n var aspect = boundingRect.width / boundingRect.height;\n var width = rect.height * aspect;\n var height;\n if (width <= rect.width) {\n height = rect.height;\n }\n else {\n width = rect.width;\n height = width / aspect;\n }\n var cx = rect.x + rect.width / 2;\n var cy = rect.y + rect.height / 2;\n\n return {\n x: cx - width / 2,\n y: cy - height / 2,\n width: width,\n height: height\n };\n}\n\nexport var mergePath = pathTool.mergePath;\n\n/**\n * Resize a path to fit the rect\n * @param {module:zrender/graphic/Path} path\n * @param {Object} rect\n */\nexport function resizePath(path, rect) {\n if (!path.applyTransform) {\n return;\n }\n\n var pathRect = path.getBoundingRect();\n\n var m = pathRect.calculateTransform(rect);\n\n path.applyTransform(m);\n}\n\n/**\n * Sub pixel optimize line for canvas\n *\n * @param {Object} param\n * @param {Object} [param.shape]\n * @param {number} [param.shape.x1]\n * @param {number} [param.shape.y1]\n * @param {number} [param.shape.x2]\n * @param {number} [param.shape.y2]\n * @param {Object} [param.style]\n * @param {number} [param.style.lineWidth]\n * @return {Object} Modified param\n */\nexport function subPixelOptimizeLine(param) {\n var shape = param.shape;\n var lineWidth = param.style.lineWidth;\n\n if (round(shape.x1 * 2) === round(shape.x2 * 2)) {\n shape.x1 = shape.x2 = subPixelOptimize(shape.x1, lineWidth, true);\n }\n if (round(shape.y1 * 2) === round(shape.y2 * 2)) {\n shape.y1 = shape.y2 = subPixelOptimize(shape.y1, lineWidth, true);\n }\n return param;\n}\n\n/**\n * Sub pixel optimize rect for canvas\n *\n * @param {Object} param\n * @param {Object} [param.shape]\n * @param {number} [param.shape.x]\n * @param {number} [param.shape.y]\n * @param {number} [param.shape.width]\n * @param {number} [param.shape.height]\n * @param {Object} [param.style]\n * @param {number} [param.style.lineWidth]\n * @return {Object} Modified param\n */\nexport function subPixelOptimizeRect(param) {\n var shape = param.shape;\n var lineWidth = param.style.lineWidth;\n var originX = shape.x;\n var originY = shape.y;\n var originWidth = shape.width;\n var originHeight = shape.height;\n shape.x = subPixelOptimize(shape.x, lineWidth, true);\n shape.y = subPixelOptimize(shape.y, lineWidth, true);\n shape.width = Math.max(\n subPixelOptimize(originX + originWidth, lineWidth, false) - shape.x,\n originWidth === 0 ? 0 : 1\n );\n shape.height = Math.max(\n subPixelOptimize(originY + originHeight, lineWidth, false) - shape.y,\n originHeight === 0 ? 0 : 1\n );\n return param;\n}\n\n/**\n * Sub pixel optimize for canvas\n *\n * @param {number} position Coordinate, such as x, y\n * @param {number} lineWidth Should be nonnegative integer.\n * @param {boolean=} positiveOrNegative Default false (negative).\n * @return {number} Optimized position.\n */\nexport function subPixelOptimize(position, lineWidth, positiveOrNegative) {\n // Assure that (position + lineWidth / 2) is near integer edge,\n // otherwise line will be fuzzy in canvas.\n var doubledPosition = round(position * 2);\n return (doubledPosition + round(lineWidth)) % 2 === 0\n ? doubledPosition / 2\n : (doubledPosition + (positiveOrNegative ? 1 : -1)) / 2;\n}\n\nfunction hasFillOrStroke(fillOrStroke) {\n return fillOrStroke != null && fillOrStroke != 'none';\n}\n\nfunction liftColor(color) {\n return typeof color === 'string' ? colorTool.lift(color, -0.1) : color;\n}\n\n/**\n * @private\n */\nfunction cacheElementStl(el) {\n if (el.__hoverStlDirty) {\n var stroke = el.style.stroke;\n var fill = el.style.fill;\n\n // Create hoverStyle on mouseover\n var hoverStyle = el.__hoverStl;\n hoverStyle.fill = hoverStyle.fill\n || (hasFillOrStroke(fill) ? liftColor(fill) : null);\n hoverStyle.stroke = hoverStyle.stroke\n || (hasFillOrStroke(stroke) ? liftColor(stroke) : null);\n\n var normalStyle = {};\n for (var name in hoverStyle) {\n // See comment in `doSingleEnterHover`.\n if (hoverStyle[name] != null) {\n normalStyle[name] = el.style[name];\n }\n }\n\n el.__normalStl = normalStyle;\n\n el.__hoverStlDirty = false;\n }\n}\n\n/**\n * @private\n */\nfunction doSingleEnterHover(el) {\n if (el.__isHover) {\n return;\n }\n\n cacheElementStl(el);\n\n if (el.useHoverLayer) {\n el.__zr && el.__zr.addHover(el, el.__hoverStl);\n }\n else {\n var style = el.style;\n var insideRollbackOpt = style.insideRollbackOpt;\n\n // Consider case: only `position: 'top'` is set on emphasis, then text\n // color should be returned to `autoColor`, rather than remain '#fff'.\n // So we should rollback then apply again after style merging.\n insideRollbackOpt && rollbackInsideStyle(style);\n\n // styles can be:\n // {\n // label: {\n // normal: {\n // show: false,\n // position: 'outside',\n // fontSize: 18\n // },\n // emphasis: {\n // show: true\n // }\n // }\n // },\n // where properties of `emphasis` may not appear in `normal`. We previously use\n // module:echarts/util/model#defaultEmphasis to merge `normal` to `emphasis`.\n // But consider rich text and setOption in merge mode, it is impossible to cover\n // all properties in merge. So we use merge mode when setting style here, where\n // only properties that is not `null/undefined` can be set. The disadventage:\n // null/undefined can not be used to remove style any more in `emphasis`.\n style.extendFrom(el.__hoverStl);\n\n // Do not save `insideRollback`.\n if (insideRollbackOpt) {\n applyInsideStyle(style, style.insideOriginalTextPosition, insideRollbackOpt);\n\n // textFill may be rollbacked to null.\n if (style.textFill == null) {\n style.textFill = insideRollbackOpt.autoColor;\n }\n }\n\n el.dirty(false);\n el.z2 += 1;\n }\n\n el.__isHover = true;\n}\n\n/**\n * @inner\n */\nfunction doSingleLeaveHover(el) {\n if (!el.__isHover) {\n return;\n }\n\n var normalStl = el.__normalStl;\n if (el.useHoverLayer) {\n el.__zr && el.__zr.removeHover(el);\n }\n else {\n // Consider null/undefined value, should use\n // `setStyle` but not `extendFrom(stl, true)`.\n normalStl && el.setStyle(normalStl);\n el.z2 -= 1;\n }\n\n el.__isHover = false;\n}\n\n/**\n * @inner\n */\nfunction doEnterHover(el) {\n el.type === 'group'\n ? el.traverse(function (child) {\n if (child.type !== 'group') {\n doSingleEnterHover(child);\n }\n })\n : doSingleEnterHover(el);\n}\n\nfunction doLeaveHover(el) {\n el.type === 'group'\n ? el.traverse(function (child) {\n if (child.type !== 'group') {\n doSingleLeaveHover(child);\n }\n })\n : doSingleLeaveHover(el);\n}\n\n/**\n * @inner\n */\nfunction setElementHoverStl(el, hoverStl) {\n // If element has sepcified hoverStyle, then use it instead of given hoverStyle\n // Often used when item group has a label element and it's hoverStyle is different\n el.__hoverStl = el.hoverStyle || hoverStl || {};\n el.__hoverStlDirty = true;\n\n if (el.__isHover) {\n cacheElementStl(el);\n }\n}\n\n/**\n * @inner\n */\nfunction onElementMouseOver(e) {\n if (this.__hoverSilentOnTouch && e.zrByTouch) {\n return;\n }\n\n // Only if element is not in emphasis status\n !this.__isEmphasis && doEnterHover(this);\n}\n\n/**\n * @inner\n */\nfunction onElementMouseOut(e) {\n if (this.__hoverSilentOnTouch && e.zrByTouch) {\n return;\n }\n\n // Only if element is not in emphasis status\n !this.__isEmphasis && doLeaveHover(this);\n}\n\n/**\n * @inner\n */\nfunction enterEmphasis() {\n this.__isEmphasis = true;\n doEnterHover(this);\n}\n\n/**\n * @inner\n */\nfunction leaveEmphasis() {\n this.__isEmphasis = false;\n doLeaveHover(this);\n}\n\n/**\n * Set hover style of element.\n * This method can be called repeatly without side-effects.\n * @param {module:zrender/Element} el\n * @param {Object} [hoverStyle]\n * @param {Object} [opt]\n * @param {boolean} [opt.hoverSilentOnTouch=false]\n * In touch device, mouseover event will be trigger on touchstart event\n * (see module:zrender/dom/HandlerProxy). By this mechanism, we can\n * conviniently use hoverStyle when tap on touch screen without additional\n * code for compatibility.\n * But if the chart/component has select feature, which usually also use\n * hoverStyle, there might be conflict between 'select-highlight' and\n * 'hover-highlight' especially when roam is enabled (see geo for example).\n * In this case, hoverSilentOnTouch should be used to disable hover-highlight\n * on touch device.\n */\nexport function setHoverStyle(el, hoverStyle, opt) {\n el.__hoverSilentOnTouch = opt && opt.hoverSilentOnTouch;\n\n el.type === 'group'\n ? el.traverse(function (child) {\n if (child.type !== 'group') {\n setElementHoverStl(child, hoverStyle);\n }\n })\n : setElementHoverStl(el, hoverStyle);\n\n // Duplicated function will be auto-ignored, see Eventful.js.\n el.on('mouseover', onElementMouseOver)\n .on('mouseout', onElementMouseOut);\n\n // Emphasis, normal can be triggered manually\n el.on('emphasis', enterEmphasis)\n .on('normal', leaveEmphasis);\n}\n\n/**\n * @param {Object|module:zrender/graphic/Style} normalStyle\n * @param {Object} emphasisStyle\n * @param {module:echarts/model/Model} normalModel\n * @param {module:echarts/model/Model} emphasisModel\n * @param {Object} opt Check `opt` of `setTextStyleCommon` to find other props.\n * @param {Object} [opt.defaultText]\n * @param {module:echarts/model/Model} [opt.labelFetcher] Fetch text by\n * `opt.labelFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex)`\n * @param {module:echarts/model/Model} [opt.labelDataIndex] Fetch text by\n * `opt.textFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex)`\n * @param {module:echarts/model/Model} [opt.labelDimIndex] Fetch text by\n * `opt.textFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex)`\n * @param {Object} [normalSpecified]\n * @param {Object} [emphasisSpecified]\n */\nexport function setLabelStyle(\n normalStyle, emphasisStyle,\n normalModel, emphasisModel,\n opt,\n normalSpecified, emphasisSpecified\n) {\n opt = opt || EMPTY_OBJ;\n var labelFetcher = opt.labelFetcher;\n var labelDataIndex = opt.labelDataIndex;\n var labelDimIndex = opt.labelDimIndex;\n\n // This scenario, `label.normal.show = true; label.emphasis.show = false`,\n // is not supported util someone requests.\n\n var showNormal = normalModel.getShallow('show');\n var showEmphasis = emphasisModel.getShallow('show');\n\n // Consider performance, only fetch label when necessary.\n // If `normal.show` is `false` and `emphasis.show` is `true` and `emphasis.formatter` is not set,\n // label should be displayed, where text is fetched by `normal.formatter` or `opt.defaultText`.\n var baseText = (showNormal || showEmphasis)\n ? zrUtil.retrieve2(\n labelFetcher\n ? labelFetcher.getFormattedLabel(labelDataIndex, 'normal', null, labelDimIndex)\n : null,\n opt.defaultText\n )\n : null;\n var normalStyleText = showNormal ? baseText : null;\n var emphasisStyleText = showEmphasis\n ? zrUtil.retrieve2(\n labelFetcher\n ? labelFetcher.getFormattedLabel(labelDataIndex, 'emphasis', null, labelDimIndex)\n : null,\n baseText\n )\n : null;\n\n // Optimize: If style.text is null, text will not be drawn.\n if (normalStyleText != null || emphasisStyleText != null) {\n // Always set `textStyle` even if `normalStyle.text` is null, because default\n // values have to be set on `normalStyle`.\n // If we set default values on `emphasisStyle`, consider case:\n // Firstly, `setOption(... label: {normal: {text: null}, emphasis: {show: true}} ...);`\n // Secondly, `setOption(... label: {noraml: {show: true, text: 'abc', color: 'red'} ...);`\n // Then the 'red' will not work on emphasis.\n setTextStyle(normalStyle, normalModel, normalSpecified, opt);\n setTextStyle(emphasisStyle, emphasisModel, emphasisSpecified, opt, true);\n }\n\n normalStyle.text = normalStyleText;\n emphasisStyle.text = emphasisStyleText;\n}\n\n/**\n * Set basic textStyle properties.\n * @param {Object|module:zrender/graphic/Style} textStyle\n * @param {module:echarts/model/Model} model\n * @param {Object} [specifiedTextStyle] Can be overrided by settings in model.\n * @param {Object} [opt] See `opt` of `setTextStyleCommon`.\n * @param {boolean} [isEmphasis]\n */\nexport function setTextStyle(\n textStyle, textStyleModel, specifiedTextStyle, opt, isEmphasis\n) {\n setTextStyleCommon(textStyle, textStyleModel, opt, isEmphasis);\n specifiedTextStyle && zrUtil.extend(textStyle, specifiedTextStyle);\n textStyle.host && textStyle.host.dirty && textStyle.host.dirty(false);\n\n return textStyle;\n}\n\n/**\n * Set text option in the style.\n * @deprecated\n * @param {Object} textStyle\n * @param {module:echarts/model/Model} labelModel\n * @param {string|boolean} defaultColor Default text color.\n * If set as false, it will be processed as a emphasis style.\n */\nexport function setText(textStyle, labelModel, defaultColor) {\n var opt = {isRectText: true};\n var isEmphasis;\n\n if (defaultColor === false) {\n isEmphasis = true;\n }\n else {\n // Support setting color as 'auto' to get visual color.\n opt.autoColor = defaultColor;\n }\n setTextStyleCommon(textStyle, labelModel, opt, isEmphasis);\n textStyle.host && textStyle.host.dirty && textStyle.host.dirty(false);\n}\n\n/**\n * {\n * disableBox: boolean, Whether diable drawing box of block (outer most).\n * isRectText: boolean,\n * autoColor: string, specify a color when color is 'auto',\n * for textFill, textStroke, textBackgroundColor, and textBorderColor.\n * If autoColor specified, it is used as default textFill.\n * useInsideStyle:\n * `true`: Use inside style (textFill, textStroke, textStrokeWidth)\n * if `textFill` is not specified.\n * `false`: Do not use inside style.\n * `null/undefined`: use inside style if `isRectText` is true and\n * `textFill` is not specified and textPosition contains `'inside'`.\n * forceRich: boolean\n * }\n */\nfunction setTextStyleCommon(textStyle, textStyleModel, opt, isEmphasis) {\n // Consider there will be abnormal when merge hover style to normal style if given default value.\n opt = opt || EMPTY_OBJ;\n\n if (opt.isRectText) {\n var textPosition = textStyleModel.getShallow('position')\n || (isEmphasis ? null : 'inside');\n // 'outside' is not a valid zr textPostion value, but used\n // in bar series, and magric type should be considered.\n textPosition === 'outside' && (textPosition = 'top');\n textStyle.textPosition = textPosition;\n textStyle.textOffset = textStyleModel.getShallow('offset');\n var labelRotate = textStyleModel.getShallow('rotate');\n labelRotate != null && (labelRotate *= Math.PI / 180);\n textStyle.textRotation = labelRotate;\n textStyle.textDistance = zrUtil.retrieve2(\n textStyleModel.getShallow('distance'), isEmphasis ? null : 5\n );\n }\n\n var ecModel = textStyleModel.ecModel;\n var globalTextStyle = ecModel && ecModel.option.textStyle;\n\n // Consider case:\n // {\n // data: [{\n // value: 12,\n // label: {\n // normal: {\n // rich: {\n // // no 'a' here but using parent 'a'.\n // }\n // }\n // }\n // }],\n // rich: {\n // a: { ... }\n // }\n // }\n var richItemNames = getRichItemNames(textStyleModel);\n var richResult;\n if (richItemNames) {\n richResult = {};\n for (var name in richItemNames) {\n if (richItemNames.hasOwnProperty(name)) {\n // Cascade is supported in rich.\n var richTextStyle = textStyleModel.getModel(['rich', name]);\n // In rich, never `disableBox`.\n setTokenTextStyle(richResult[name] = {}, richTextStyle, globalTextStyle, opt, isEmphasis);\n }\n }\n }\n textStyle.rich = richResult;\n\n setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isEmphasis, true);\n\n if (opt.forceRich && !opt.textStyle) {\n opt.textStyle = {};\n }\n\n return textStyle;\n}\n\n// Consider case:\n// {\n// data: [{\n// value: 12,\n// label: {\n// normal: {\n// rich: {\n// // no 'a' here but using parent 'a'.\n// }\n// }\n// }\n// }],\n// rich: {\n// a: { ... }\n// }\n// }\nfunction getRichItemNames(textStyleModel) {\n // Use object to remove duplicated names.\n var richItemNameMap;\n while (textStyleModel && textStyleModel !== textStyleModel.ecModel) {\n var rich = (textStyleModel.option || EMPTY_OBJ).rich;\n if (rich) {\n richItemNameMap = richItemNameMap || {};\n for (var name in rich) {\n if (rich.hasOwnProperty(name)) {\n richItemNameMap[name] = 1;\n }\n }\n }\n textStyleModel = textStyleModel.parentModel;\n }\n return richItemNameMap;\n}\n\nfunction setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isEmphasis, isBlock) {\n // In merge mode, default value should not be given.\n globalTextStyle = !isEmphasis && globalTextStyle || EMPTY_OBJ;\n\n textStyle.textFill = getAutoColor(textStyleModel.getShallow('color'), opt)\n || globalTextStyle.color;\n textStyle.textStroke = getAutoColor(textStyleModel.getShallow('textBorderColor'), opt)\n || globalTextStyle.textBorderColor;\n textStyle.textStrokeWidth = zrUtil.retrieve2(\n textStyleModel.getShallow('textBorderWidth'),\n globalTextStyle.textBorderWidth\n );\n\n if (!isEmphasis) {\n if (isBlock) {\n // Always set `insideRollback`, for clearing previous.\n var originalTextPosition = textStyle.textPosition;\n textStyle.insideRollback = applyInsideStyle(textStyle, originalTextPosition, opt);\n // Save original textPosition, because style.textPosition will be repalced by\n // real location (like [10, 30]) in zrender.\n textStyle.insideOriginalTextPosition = originalTextPosition;\n textStyle.insideRollbackOpt = opt;\n }\n\n // Set default finally.\n if (textStyle.textFill == null) {\n textStyle.textFill = opt.autoColor;\n }\n }\n\n // Do not use `getFont` here, because merge should be supported, where\n // part of these properties may be changed in emphasis style, and the\n // others should remain their original value got from normal style.\n textStyle.fontStyle = textStyleModel.getShallow('fontStyle') || globalTextStyle.fontStyle;\n textStyle.fontWeight = textStyleModel.getShallow('fontWeight') || globalTextStyle.fontWeight;\n textStyle.fontSize = textStyleModel.getShallow('fontSize') || globalTextStyle.fontSize;\n textStyle.fontFamily = textStyleModel.getShallow('fontFamily') || globalTextStyle.fontFamily;\n\n textStyle.textAlign = textStyleModel.getShallow('align');\n textStyle.textVerticalAlign = textStyleModel.getShallow('verticalAlign')\n || textStyleModel.getShallow('baseline');\n\n textStyle.textLineHeight = textStyleModel.getShallow('lineHeight');\n textStyle.textWidth = textStyleModel.getShallow('width');\n textStyle.textHeight = textStyleModel.getShallow('height');\n textStyle.textTag = textStyleModel.getShallow('tag');\n\n if (!isBlock || !opt.disableBox) {\n textStyle.textBackgroundColor = getAutoColor(textStyleModel.getShallow('backgroundColor'), opt);\n textStyle.textPadding = textStyleModel.getShallow('padding');\n textStyle.textBorderColor = getAutoColor(textStyleModel.getShallow('borderColor'), opt);\n textStyle.textBorderWidth = textStyleModel.getShallow('borderWidth');\n textStyle.textBorderRadius = textStyleModel.getShallow('borderRadius');\n\n textStyle.textBoxShadowColor = textStyleModel.getShallow('shadowColor');\n textStyle.textBoxShadowBlur = textStyleModel.getShallow('shadowBlur');\n textStyle.textBoxShadowOffsetX = textStyleModel.getShallow('shadowOffsetX');\n textStyle.textBoxShadowOffsetY = textStyleModel.getShallow('shadowOffsetY');\n }\n\n textStyle.textShadowColor = textStyleModel.getShallow('textShadowColor')\n || globalTextStyle.textShadowColor;\n textStyle.textShadowBlur = textStyleModel.getShallow('textShadowBlur')\n || globalTextStyle.textShadowBlur;\n textStyle.textShadowOffsetX = textStyleModel.getShallow('textShadowOffsetX')\n || globalTextStyle.textShadowOffsetX;\n textStyle.textShadowOffsetY = textStyleModel.getShallow('textShadowOffsetY')\n || globalTextStyle.textShadowOffsetY;\n}\n\nfunction getAutoColor(color, opt) {\n return color !== 'auto' ? color : (opt && opt.autoColor) ? opt.autoColor : null;\n}\n\nfunction applyInsideStyle(textStyle, textPosition, opt) {\n var useInsideStyle = opt.useInsideStyle;\n var insideRollback;\n\n if (textStyle.textFill == null\n && useInsideStyle !== false\n && (useInsideStyle === true\n || (opt.isRectText\n && textPosition\n // textPosition can be [10, 30]\n && typeof textPosition === 'string'\n && textPosition.indexOf('inside') >= 0\n )\n )\n ) {\n insideRollback = {\n textFill: null,\n textStroke: textStyle.textStroke,\n textStrokeWidth: textStyle.textStrokeWidth\n };\n textStyle.textFill = '#fff';\n // Consider text with #fff overflow its container.\n if (textStyle.textStroke == null) {\n textStyle.textStroke = opt.autoColor;\n textStyle.textStrokeWidth == null && (textStyle.textStrokeWidth = 2);\n }\n }\n\n return insideRollback;\n}\n\nfunction rollbackInsideStyle(style) {\n var insideRollback = style.insideRollback;\n if (insideRollback) {\n style.textFill = insideRollback.textFill;\n style.textStroke = insideRollback.textStroke;\n style.textStrokeWidth = insideRollback.textStrokeWidth;\n }\n}\n\nexport function getFont(opt, ecModel) {\n // ecModel or default text style model.\n var gTextStyleModel = ecModel || ecModel.getModel('textStyle');\n return [\n // FIXME in node-canvas fontWeight is before fontStyle\n opt.fontStyle || gTextStyleModel && gTextStyleModel.getShallow('fontStyle') || '',\n opt.fontWeight || gTextStyleModel && gTextStyleModel.getShallow('fontWeight') || '',\n (opt.fontSize || gTextStyleModel && gTextStyleModel.getShallow('fontSize') || 12) + 'px',\n opt.fontFamily || gTextStyleModel && gTextStyleModel.getShallow('fontFamily') || 'sans-serif'\n ].join(' ');\n}\n\nfunction animateOrSetProps(isUpdate, el, props, animatableModel, dataIndex, cb) {\n if (typeof dataIndex === 'function') {\n cb = dataIndex;\n dataIndex = null;\n }\n // Do not check 'animation' property directly here. Consider this case:\n // animation model is an `itemModel`, whose does not have `isAnimationEnabled`\n // but its parent model (`seriesModel`) does.\n var animationEnabled = animatableModel && animatableModel.isAnimationEnabled();\n\n if (animationEnabled) {\n var postfix = isUpdate ? 'Update' : '';\n var duration = animatableModel.getShallow('animationDuration' + postfix);\n var animationEasing = animatableModel.getShallow('animationEasing' + postfix);\n var animationDelay = animatableModel.getShallow('animationDelay' + postfix);\n if (typeof animationDelay === 'function') {\n animationDelay = animationDelay(\n dataIndex,\n animatableModel.getAnimationDelayParams\n ? animatableModel.getAnimationDelayParams(el, dataIndex)\n : null\n );\n }\n if (typeof duration === 'function') {\n duration = duration(dataIndex);\n }\n\n duration > 0\n ? el.animateTo(props, duration, animationDelay || 0, animationEasing, cb, !!cb)\n : (el.stopAnimation(), el.attr(props), cb && cb());\n }\n else {\n el.stopAnimation();\n el.attr(props);\n cb && cb();\n }\n}\n\n/**\n * Update graphic element properties with or without animation according to the\n * configuration in series.\n *\n * Caution: this method will stop previous animation.\n * So if do not use this method to one element twice before\n * animation starts, unless you know what you are doing.\n *\n * @param {module:zrender/Element} el\n * @param {Object} props\n * @param {module:echarts/model/Model} [animatableModel]\n * @param {number} [dataIndex]\n * @param {Function} [cb]\n * @example\n * graphic.updateProps(el, {\n * position: [100, 100]\n * }, seriesModel, dataIndex, function () { console.log('Animation done!'); });\n * // Or\n * graphic.updateProps(el, {\n * position: [100, 100]\n * }, seriesModel, function () { console.log('Animation done!'); });\n */\nexport function updateProps(el, props, animatableModel, dataIndex, cb) {\n animateOrSetProps(true, el, props, animatableModel, dataIndex, cb);\n}\n\n/**\n * Init graphic element properties with or without animation according to the\n * configuration in series.\n *\n * Caution: this method will stop previous animation.\n * So if do not use this method to one element twice before\n * animation starts, unless you know what you are doing.\n *\n * @param {module:zrender/Element} el\n * @param {Object} props\n * @param {module:echarts/model/Model} [animatableModel]\n * @param {number} [dataIndex]\n * @param {Function} cb\n */\nexport function initProps(el, props, animatableModel, dataIndex, cb) {\n animateOrSetProps(false, el, props, animatableModel, dataIndex, cb);\n}\n\n/**\n * Get transform matrix of target (param target),\n * in coordinate of its ancestor (param ancestor)\n *\n * @param {module:zrender/mixin/Transformable} target\n * @param {module:zrender/mixin/Transformable} [ancestor]\n */\nexport function getTransform(target, ancestor) {\n var mat = matrix.identity([]);\n\n while (target && target !== ancestor) {\n matrix.mul(mat, target.getLocalTransform(), mat);\n target = target.parent;\n }\n\n return mat;\n}\n\n/**\n * Apply transform to an vertex.\n * @param {Array.<number>} target [x, y]\n * @param {Array.<number>|TypedArray.<number>|Object} transform Can be:\n * + Transform matrix: like [1, 0, 0, 1, 0, 0]\n * + {position, rotation, scale}, the same as `zrender/Transformable`.\n * @param {boolean=} invert Whether use invert matrix.\n * @return {Array.<number>} [x, y]\n */\nexport function applyTransform(target, transform, invert) {\n if (transform && !zrUtil.isArrayLike(transform)) {\n transform = Transformable.getLocalTransform(transform);\n }\n\n if (invert) {\n transform = matrix.invert([], transform);\n }\n return vector.applyTransform([], target, transform);\n}\n\n/**\n * @param {string} direction 'left' 'right' 'top' 'bottom'\n * @param {Array.<number>} transform Transform matrix: like [1, 0, 0, 1, 0, 0]\n * @param {boolean=} invert Whether use invert matrix.\n * @return {string} Transformed direction. 'left' 'right' 'top' 'bottom'\n */\nexport function transformDirection(direction, transform, invert) {\n\n // Pick a base, ensure that transform result will not be (0, 0).\n var hBase = (transform[4] === 0 || transform[5] === 0 || transform[0] === 0)\n ? 1 : Math.abs(2 * transform[4] / transform[0]);\n var vBase = (transform[4] === 0 || transform[5] === 0 || transform[2] === 0)\n ? 1 : Math.abs(2 * transform[4] / transform[2]);\n\n var vertex = [\n direction === 'left' ? -hBase : direction === 'right' ? hBase : 0,\n direction === 'top' ? -vBase : direction === 'bottom' ? vBase : 0\n ];\n\n vertex = applyTransform(vertex, transform, invert);\n\n return Math.abs(vertex[0]) > Math.abs(vertex[1])\n ? (vertex[0] > 0 ? 'right' : 'left')\n : (vertex[1] > 0 ? 'bottom' : 'top');\n}\n\n/**\n * Apply group transition animation from g1 to g2.\n * If no animatableModel, no animation.\n */\nexport function groupTransition(g1, g2, animatableModel, cb) {\n if (!g1 || !g2) {\n return;\n }\n\n function getElMap(g) {\n var elMap = {};\n g.traverse(function (el) {\n if (!el.isGroup && el.anid) {\n elMap[el.anid] = el;\n }\n });\n return elMap;\n }\n function getAnimatableProps(el) {\n var obj = {\n position: vector.clone(el.position),\n rotation: el.rotation\n };\n if (el.shape) {\n obj.shape = zrUtil.extend({}, el.shape);\n }\n return obj;\n }\n var elMap1 = getElMap(g1);\n\n g2.traverse(function (el) {\n if (!el.isGroup && el.anid) {\n var oldEl = elMap1[el.anid];\n if (oldEl) {\n var newProp = getAnimatableProps(el);\n el.attr(getAnimatableProps(oldEl));\n updateProps(el, newProp, animatableModel, el.dataIndex);\n }\n // else {\n // if (el.previousProps) {\n // graphic.updateProps\n // }\n // }\n }\n });\n}\n\n/**\n * @param {Array.<Array.<number>>} points Like: [[23, 44], [53, 66], ...]\n * @param {Object} rect {x, y, width, height}\n * @return {Array.<Array.<number>>} A new clipped points.\n */\nexport function clipPointsByRect(points, rect) {\n return zrUtil.map(points, function (point) {\n var x = point[0];\n x = mathMax(x, rect.x);\n x = mathMin(x, rect.x + rect.width);\n var y = point[1];\n y = mathMax(y, rect.y);\n y = mathMin(y, rect.y + rect.height);\n return [x, y];\n });\n}\n\n/**\n * @param {Object} targetRect {x, y, width, height}\n * @param {Object} rect {x, y, width, height}\n * @return {Object} A new clipped rect. If rect size are negative, return undefined.\n */\nexport function clipRectByRect(targetRect, rect) {\n var x = mathMax(targetRect.x, rect.x);\n var x2 = mathMin(targetRect.x + targetRect.width, rect.x + rect.width);\n var y = mathMax(targetRect.y, rect.y);\n var y2 = mathMin(targetRect.y + targetRect.height, rect.y + rect.height);\n\n if (x2 >= x && y2 >= y) {\n return {\n x: x,\n y: y,\n width: x2 - x,\n height: y2 - y\n };\n }\n}\n\n/**\n * @param {string} iconStr Support 'image://' or 'path://' or direct svg path.\n * @param {Object} [opt] Properties of `module:zrender/Element`, except `style`.\n * @param {Object} [rect] {x, y, width, height}\n * @return {module:zrender/Element} Icon path or image element.\n */\nexport function createIcon(iconStr, opt, rect) {\n opt = zrUtil.extend({rectHover: true}, opt);\n var style = opt.style = {strokeNoScale: true};\n rect = rect || {x: -1, y: -1, width: 2, height: 2};\n\n if (iconStr) {\n return iconStr.indexOf('image://') === 0\n ? (\n style.image = iconStr.slice(8),\n zrUtil.defaults(style, rect),\n new Image(opt)\n )\n : (\n makePath(\n iconStr.replace('path://', ''),\n opt,\n rect,\n 'center'\n )\n );\n }\n}\n\nexport {\n Group,\n Image,\n Text,\n Circle,\n Sector,\n Ring,\n Polygon,\n Polyline,\n Rect,\n Line,\n BezierCurve,\n Arc,\n CompoundPath,\n LinearGradient,\n RadialGradient,\n BoundingRect\n};\n","import * as textContain from 'zrender/src/contain/text';\nimport * as graphicUtil from '../../util/graphic';\n\nvar PATH_COLOR = ['textStyle', 'color'];\n\nexport default {\n /**\n * Get color property or get color from option.textStyle.color\n * @param {boolean} [isEmphasis]\n * @return {string}\n */\n getTextColor: function (isEmphasis) {\n var ecModel = this.ecModel;\n return this.getShallow('color')\n || (\n (!isEmphasis && ecModel) ? ecModel.get(PATH_COLOR) : null\n );\n },\n\n /**\n * Create font string from fontStyle, fontWeight, fontSize, fontFamily\n * @return {string}\n */\n getFont: function () {\n return graphicUtil.getFont({\n fontStyle: this.getShallow('fontStyle'),\n fontWeight: this.getShallow('fontWeight'),\n fontSize: this.getShallow('fontSize'),\n fontFamily: this.getShallow('fontFamily')\n }, this.ecModel);\n },\n\n getTextRect: function (text) {\n return textContain.getBoundingRect(\n text,\n this.getFont(),\n this.getShallow('align'),\n this.getShallow('verticalAlign') || this.getShallow('baseline'),\n this.getShallow('padding'),\n this.getShallow('rich'),\n this.getShallow('truncateText')\n );\n }\n};","import makeStyleMapper from './makeStyleMapper';\n\nvar getItemStyle = makeStyleMapper(\n [\n ['fill', 'color'],\n ['stroke', 'borderColor'],\n ['lineWidth', 'borderWidth'],\n ['opacity'],\n ['shadowBlur'],\n ['shadowOffsetX'],\n ['shadowOffsetY'],\n ['shadowColor'],\n ['textPosition'],\n ['textAlign']\n ]\n);\n\nexport default {\n getItemStyle: function (excludes, includes) {\n var style = getItemStyle(this, excludes, includes);\n var lineDash = this.getBorderLineDash();\n lineDash && (style.lineDash = lineDash);\n return style;\n },\n\n getBorderLineDash: function () {\n var lineType = this.get('borderType');\n return (lineType === 'solid' || lineType == null) ? null\n : (lineType === 'dashed' ? [5, 5] : [1, 1]);\n }\n};","/**\n * @module echarts/model/Model\n */\n\nimport * as zrUtil from 'zrender/src/core/util';\nimport env from 'zrender/src/core/env';\nimport * as clazzUtil from '../util/clazz';\n\nimport lineStyleMixin from './mixin/lineStyle';\nimport areaStyleMixin from './mixin/areaStyle';\nimport textStyleMixin from './mixin/textStyle';\nimport itemStyleMixin from './mixin/itemStyle';\n\nvar mixin = zrUtil.mixin;\n\n/**\n * @alias module:echarts/model/Model\n * @constructor\n * @param {Object} option\n * @param {module:echarts/model/Model} [parentModel]\n * @param {module:echarts/model/Global} [ecModel]\n */\nfunction Model(option, parentModel, ecModel) {\n /**\n * @type {module:echarts/model/Model}\n * @readOnly\n */\n this.parentModel = parentModel;\n\n /**\n * @type {module:echarts/model/Global}\n * @readOnly\n */\n this.ecModel = ecModel;\n\n /**\n * @type {Object}\n * @protected\n */\n this.option = option;\n\n // Simple optimization\n // if (this.init) {\n // if (arguments.length <= 4) {\n // this.init(option, parentModel, ecModel, extraOpt);\n // }\n // else {\n // this.init.apply(this, arguments);\n // }\n // }\n}\n\nModel.prototype = {\n\n constructor: Model,\n\n /**\n * Model 的初始化函数\n * @param {Object} option\n */\n init: null,\n\n /**\n * 从新的 Option merge\n */\n mergeOption: function (option) {\n zrUtil.merge(this.option, option, true);\n },\n\n /**\n * @param {string|Array.<string>} path\n * @param {boolean} [ignoreParent=false]\n * @return {*}\n */\n get: function (path, ignoreParent) {\n if (path == null) {\n return this.option;\n }\n\n return doGet(\n this.option,\n this.parsePath(path),\n !ignoreParent && getParent(this, path)\n );\n },\n\n /**\n * @param {string} key\n * @param {boolean} [ignoreParent=false]\n * @return {*}\n */\n getShallow: function (key, ignoreParent) {\n var option = this.option;\n\n var val = option == null ? option : option[key];\n var parentModel = !ignoreParent && getParent(this, key);\n if (val == null && parentModel) {\n val = parentModel.getShallow(key);\n }\n return val;\n },\n\n /**\n * @param {string|Array.<string>} [path]\n * @param {module:echarts/model/Model} [parentModel]\n * @return {module:echarts/model/Model}\n */\n getModel: function (path, parentModel) {\n var obj = path == null\n ? this.option\n : doGet(this.option, path = this.parsePath(path));\n\n var thisParentModel;\n parentModel = parentModel || (\n (thisParentModel = getParent(this, path))\n && thisParentModel.getModel(path)\n );\n\n return new Model(obj, parentModel, this.ecModel);\n },\n\n /**\n * If model has option\n */\n isEmpty: function () {\n return this.option == null;\n },\n\n restoreData: function () {},\n\n // Pending\n clone: function () {\n var Ctor = this.constructor;\n return new Ctor(zrUtil.clone(this.option));\n },\n\n setReadOnly: function (properties) {\n clazzUtil.setReadOnly(this, properties);\n },\n\n // If path is null/undefined, return null/undefined.\n parsePath: function(path) {\n if (typeof path === 'string') {\n path = path.split('.');\n }\n return path;\n },\n\n /**\n * @param {Function} getParentMethod\n * param {Array.<string>|string} path\n * return {module:echarts/model/Model}\n */\n customizeGetParent: function (getParentMethod) {\n clazzUtil.set(this, 'getParent', getParentMethod);\n },\n\n isAnimationEnabled: function () {\n if (!env.node) {\n if (this.option.animation != null) {\n return !!this.option.animation;\n }\n else if (this.parentModel) {\n return this.parentModel.isAnimationEnabled();\n }\n }\n }\n};\n\nfunction doGet(obj, pathArr, parentModel) {\n for (var i = 0; i < pathArr.length; i++) {\n // Ignore empty\n if (!pathArr[i]) {\n continue;\n }\n // obj could be number/string/... (like 0)\n obj = (obj && typeof obj === 'object') ? obj[pathArr[i]] : null;\n if (obj == null) {\n break;\n }\n }\n if (obj == null && parentModel) {\n obj = parentModel.get(pathArr);\n }\n return obj;\n}\n\n// `path` can be null/undefined\nfunction getParent(model, path) {\n var getParentMethod = clazzUtil.get(model, 'getParent');\n return getParentMethod ? getParentMethod.call(model, path) : model.parentModel;\n}\n\n// Enable Model.extend.\nclazzUtil.enableClassExtend(Model);\n\nmixin(Model, lineStyleMixin);\nmixin(Model, areaStyleMixin);\nmixin(Model, textStyleMixin);\nmixin(Model, itemStyleMixin);\n\nexport default Model;","import * as zrUtil from 'zrender/src/core/util';\nimport * as formatUtil from './format';\nimport * as nubmerUtil from './number';\nimport Model from '../model/Model';\n\nvar each = zrUtil.each;\nvar isObject = zrUtil.isObject;\n\n/**\n * If value is not array, then translate it to array.\n * @param {*} value\n * @return {Array} [value] or value\n */\nexport function normalizeToArray(value) {\n return value instanceof Array\n ? value\n : value == null\n ? []\n : [value];\n}\n\n/**\n * Sync default option between normal and emphasis like `position` and `show`\n * In case some one will write code like\n * label: {\n * normal: {\n * show: false,\n * position: 'outside',\n * fontSize: 18\n * },\n * emphasis: {\n * show: true\n * }\n * }\n * @param {Object} opt\n * @param {Array.<string>} subOpts\n */\nexport function defaultEmphasis(opt, subOpts) {\n if (opt) {\n var emphasisOpt = opt.emphasis = opt.emphasis || {};\n var normalOpt = opt.normal = opt.normal || {};\n\n // Default emphasis option from normal\n for (var i = 0, len = subOpts.length; i < len; i++) {\n var subOptName = subOpts[i];\n if (!emphasisOpt.hasOwnProperty(subOptName)\n && normalOpt.hasOwnProperty(subOptName)\n ) {\n emphasisOpt[subOptName] = normalOpt[subOptName];\n }\n }\n }\n}\n\nexport var TEXT_STYLE_OPTIONS = [\n 'fontStyle', 'fontWeight', 'fontSize', 'fontFamily',\n 'rich', 'tag', 'color', 'textBorderColor', 'textBorderWidth',\n 'width', 'height', 'lineHeight', 'align', 'verticalAlign', 'baseline',\n 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY',\n 'textShadowColor', 'textShadowBlur', 'textShadowOffsetX', 'textShadowOffsetY',\n 'backgroundColor', 'borderColor', 'borderWidth', 'borderRadius', 'padding'\n];\n\n// modelUtil.LABEL_OPTIONS = modelUtil.TEXT_STYLE_OPTIONS.concat([\n// 'position', 'offset', 'rotate', 'origin', 'show', 'distance', 'formatter',\n// 'fontStyle', 'fontWeight', 'fontSize', 'fontFamily',\n// // FIXME: deprecated, check and remove it.\n// 'textStyle'\n// ]);\n\n/**\n * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}]\n * This helper method retieves value from data.\n * @param {string|number|Date|Array|Object} dataItem\n * @return {number|string|Date|Array.<number|string|Date>}\n */\nexport function getDataItemValue(dataItem) {\n // Performance sensitive.\n return dataItem && (dataItem.value == null ? dataItem : dataItem.value);\n}\n\n/**\n * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}]\n * This helper method determine if dataItem has extra option besides value\n * @param {string|number|Date|Array|Object} dataItem\n */\nexport function isDataItemOption(dataItem) {\n return isObject(dataItem)\n && !(dataItem instanceof Array);\n // // markLine data can be array\n // && !(dataItem[0] && isObject(dataItem[0]) && !(dataItem[0] instanceof Array));\n}\n\n/**\n * This helper method convert value in data.\n * @param {string|number|Date} value\n * @param {Object|string} [dimInfo] If string (like 'x'), dimType defaults 'number'.\n */\nexport function converDataValue(value, dimInfo) {\n // Performance sensitive.\n var dimType = dimInfo && dimInfo.type;\n if (dimType === 'ordinal') {\n return value;\n }\n\n if (dimType === 'time'\n // spead up when using timestamp\n && typeof value !== 'number'\n && value != null\n && value !== '-'\n ) {\n value = +nubmerUtil.parseDate(value);\n }\n\n // dimType defaults 'number'.\n // If dimType is not ordinal and value is null or undefined or NaN or '-',\n // parse to NaN.\n return (value == null || value === '')\n ? NaN : +value; // If string (like '-'), using '+' parse to NaN\n}\n\n/**\n * Create a model proxy to be used in tooltip for edge data, markLine data, markPoint data.\n * @param {module:echarts/data/List} data\n * @param {Object} opt\n * @param {string} [opt.seriesIndex]\n * @param {Object} [opt.name]\n * @param {Object} [opt.mainType]\n * @param {Object} [opt.subType]\n */\nexport function createDataFormatModel(data, opt) {\n var model = new Model();\n zrUtil.mixin(model, dataFormatMixin);\n model.seriesIndex = opt.seriesIndex;\n model.name = opt.name || '';\n model.mainType = opt.mainType;\n model.subType = opt.subType;\n\n model.getData = function () {\n return data;\n };\n return model;\n}\n\n// PENDING A little ugly\nexport var dataFormatMixin = {\n /**\n * Get params for formatter\n * @param {number} dataIndex\n * @param {string} [dataType]\n * @return {Object}\n */\n getDataParams: function (dataIndex, dataType) {\n var data = this.getData(dataType);\n var rawValue = this.getRawValue(dataIndex, dataType);\n var rawDataIndex = data.getRawIndex(dataIndex);\n var name = data.getName(dataIndex, true);\n var itemOpt = data.getRawDataItem(dataIndex);\n var color = data.getItemVisual(dataIndex, 'color');\n\n return {\n componentType: this.mainType,\n componentSubType: this.subType,\n seriesType: this.mainType === 'series' ? this.subType : null,\n seriesIndex: this.seriesIndex,\n seriesId: this.id,\n seriesName: this.name,\n name: name,\n dataIndex: rawDataIndex,\n data: itemOpt,\n dataType: dataType,\n value: rawValue,\n color: color,\n marker: formatUtil.getTooltipMarker(color),\n\n // Param name list for mapping `a`, `b`, `c`, `d`, `e`\n $vars: ['seriesName', 'name', 'value']\n };\n },\n\n /**\n * Format label\n * @param {number} dataIndex\n * @param {string} [status='normal'] 'normal' or 'emphasis'\n * @param {string} [dataType]\n * @param {number} [dimIndex]\n * @param {string} [labelProp='label']\n * @return {string}\n */\n getFormattedLabel: function (dataIndex, status, dataType, dimIndex, labelProp) {\n status = status || 'normal';\n var data = this.getData(dataType);\n var itemModel = data.getItemModel(dataIndex);\n\n var params = this.getDataParams(dataIndex, dataType);\n if (dimIndex != null && (params.value instanceof Array)) {\n params.value = params.value[dimIndex];\n }\n\n var formatter = itemModel.get([labelProp || 'label', status, 'formatter']);\n\n if (typeof formatter === 'function') {\n params.status = status;\n return formatter(params);\n }\n else if (typeof formatter === 'string') {\n return formatUtil.formatTpl(formatter, params);\n }\n },\n\n /**\n * Get raw value in option\n * @param {number} idx\n * @param {string} [dataType]\n * @return {Object}\n */\n getRawValue: function (idx, dataType) {\n var data = this.getData(dataType);\n var dataItem = data.getRawDataItem(idx);\n if (dataItem != null) {\n return (isObject(dataItem) && !(dataItem instanceof Array))\n ? dataItem.value : dataItem;\n }\n },\n\n /**\n * Should be implemented.\n * @param {number} dataIndex\n * @param {boolean} [multipleSeries=false]\n * @param {number} [dataType]\n * @return {string} tooltip string\n */\n formatTooltip: zrUtil.noop\n};\n\n/**\n * Mapping to exists for merge.\n *\n * @public\n * @param {Array.<Object>|Array.<module:echarts/model/Component>} exists\n * @param {Object|Array.<Object>} newCptOptions\n * @return {Array.<Object>} Result, like [{exist: ..., option: ...}, {}],\n * index of which is the same as exists.\n */\nexport function mappingToExists(exists, newCptOptions) {\n // Mapping by the order by original option (but not order of\n // new option) in merge mode. Because we should ensure\n // some specified index (like xAxisIndex) is consistent with\n // original option, which is easy to understand, espatially in\n // media query. And in most case, merge option is used to\n // update partial option but not be expected to change order.\n newCptOptions = (newCptOptions || []).slice();\n\n var result = zrUtil.map(exists || [], function (obj, index) {\n return {exist: obj};\n });\n\n // Mapping by id or name if specified.\n each(newCptOptions, function (cptOption, index) {\n if (!isObject(cptOption)) {\n return;\n }\n\n // id has highest priority.\n for (var i = 0; i < result.length; i++) {\n if (!result[i].option // Consider name: two map to one.\n && cptOption.id != null\n && result[i].exist.id === cptOption.id + ''\n ) {\n result[i].option = cptOption;\n newCptOptions[index] = null;\n return;\n }\n }\n\n for (var i = 0; i < result.length; i++) {\n var exist = result[i].exist;\n if (!result[i].option // Consider name: two map to one.\n // Can not match when both ids exist but different.\n && (exist.id == null || cptOption.id == null)\n && cptOption.name != null\n && !isIdInner(cptOption)\n && !isIdInner(exist)\n && exist.name === cptOption.name + ''\n ) {\n result[i].option = cptOption;\n newCptOptions[index] = null;\n return;\n }\n }\n });\n\n // Otherwise mapping by index.\n each(newCptOptions, function (cptOption, index) {\n if (!isObject(cptOption)) {\n return;\n }\n\n var i = 0;\n for (; i < result.length; i++) {\n var exist = result[i].exist;\n if (!result[i].option\n // Existing model that already has id should be able to\n // mapped to (because after mapping performed model may\n // be assigned with a id, whish should not affect next\n // mapping), except those has inner id.\n && !isIdInner(exist)\n // Caution:\n // Do not overwrite id. But name can be overwritten,\n // because axis use name as 'show label text'.\n // 'exist' always has id and name and we dont\n // need to check it.\n && cptOption.id == null\n ) {\n result[i].option = cptOption;\n break;\n }\n }\n\n if (i >= result.length) {\n result.push({option: cptOption});\n }\n });\n\n return result;\n}\n\n/**\n * Make id and name for mapping result (result of mappingToExists)\n * into `keyInfo` field.\n *\n * @public\n * @param {Array.<Object>} Result, like [{exist: ..., option: ...}, {}],\n * which order is the same as exists.\n * @return {Array.<Object>} The input.\n */\nexport function makeIdAndName(mapResult) {\n // We use this id to hash component models and view instances\n // in echarts. id can be specified by user, or auto generated.\n\n // The id generation rule ensures new view instance are able\n // to mapped to old instance when setOption are called in\n // no-merge mode. So we generate model id by name and plus\n // type in view id.\n\n // name can be duplicated among components, which is convenient\n // to specify multi components (like series) by one name.\n\n // Ensure that each id is distinct.\n var idMap = zrUtil.createHashMap();\n\n each(mapResult, function (item, index) {\n var existCpt = item.exist;\n existCpt && idMap.set(existCpt.id, item);\n });\n\n each(mapResult, function (item, index) {\n var opt = item.option;\n\n zrUtil.assert(\n !opt || opt.id == null || !idMap.get(opt.id) || idMap.get(opt.id) === item,\n 'id duplicates: ' + (opt && opt.id)\n );\n\n opt && opt.id != null && idMap.set(opt.id, item);\n !item.keyInfo && (item.keyInfo = {});\n });\n\n // Make name and id.\n each(mapResult, function (item, index) {\n var existCpt = item.exist;\n var opt = item.option;\n var keyInfo = item.keyInfo;\n\n if (!isObject(opt)) {\n return;\n }\n\n // name can be overwitten. Consider case: axis.name = '20km'.\n // But id generated by name will not be changed, which affect\n // only in that case: setOption with 'not merge mode' and view\n // instance will be recreated, which can be accepted.\n keyInfo.name = opt.name != null\n ? opt.name + ''\n : existCpt\n ? existCpt.name\n : '\\0-'; // name may be displayed on screen, so use '-'.\n\n if (existCpt) {\n keyInfo.id = existCpt.id;\n }\n else if (opt.id != null) {\n keyInfo.id = opt.id + '';\n }\n else {\n // Consider this situatoin:\n // optionA: [{name: 'a'}, {name: 'a'}, {..}]\n // optionB [{..}, {name: 'a'}, {name: 'a'}]\n // Series with the same name between optionA and optionB\n // should be mapped.\n var idNum = 0;\n do {\n keyInfo.id = '\\0' + keyInfo.name + '\\0' + idNum++;\n }\n while (idMap.get(keyInfo.id));\n }\n\n idMap.set(keyInfo.id, item);\n });\n}\n\n/**\n * @public\n * @param {Object} cptOption\n * @return {boolean}\n */\nexport function isIdInner(cptOption) {\n return isObject(cptOption)\n && cptOption.id\n && (cptOption.id + '').indexOf('\\0_ec_\\0') === 0;\n}\n\n/**\n * A helper for removing duplicate items between batchA and batchB,\n * and in themselves, and categorize by series.\n *\n * @param {Array.<Object>} batchA Like: [{seriesId: 2, dataIndex: [32, 4, 5]}, ...]\n * @param {Array.<Object>} batchB Like: [{seriesId: 2, dataIndex: [32, 4, 5]}, ...]\n * @return {Array.<Array.<Object>, Array.<Object>>} result: [resultBatchA, resultBatchB]\n */\nexport function compressBatches(batchA, batchB) {\n var mapA = {};\n var mapB = {};\n\n makeMap(batchA || [], mapA);\n makeMap(batchB || [], mapB, mapA);\n\n return [mapToArray(mapA), mapToArray(mapB)];\n\n function makeMap(sourceBatch, map, otherMap) {\n for (var i = 0, len = sourceBatch.length; i < len; i++) {\n var seriesId = sourceBatch[i].seriesId;\n var dataIndices = normalizeToArray(sourceBatch[i].dataIndex);\n var otherDataIndices = otherMap && otherMap[seriesId];\n\n for (var j = 0, lenj = dataIndices.length; j < lenj; j++) {\n var dataIndex = dataIndices[j];\n\n if (otherDataIndices && otherDataIndices[dataIndex]) {\n otherDataIndices[dataIndex] = null;\n }\n else {\n (map[seriesId] || (map[seriesId] = {}))[dataIndex] = 1;\n }\n }\n }\n }\n\n function mapToArray(map, isData) {\n var result = [];\n for (var i in map) {\n if (map.hasOwnProperty(i) && map[i] != null) {\n if (isData) {\n result.push(+i);\n }\n else {\n var dataIndices = mapToArray(map[i], true);\n dataIndices.length && result.push({seriesId: i, dataIndex: dataIndices});\n }\n }\n }\n return result;\n }\n}\n\n/**\n * @param {module:echarts/data/List} data\n * @param {Object} payload Contains dataIndex (means rawIndex) / dataIndexInside / name\n * each of which can be Array or primary type.\n * @return {number|Array.<number>} dataIndex If not found, return undefined/null.\n */\nexport function queryDataIndex(data, payload) {\n if (payload.dataIndexInside != null) {\n return payload.dataIndexInside;\n }\n else if (payload.dataIndex != null) {\n return zrUtil.isArray(payload.dataIndex)\n ? zrUtil.map(payload.dataIndex, function (value) {\n return data.indexOfRawIndex(value);\n })\n : data.indexOfRawIndex(payload.dataIndex);\n }\n else if (payload.name != null) {\n return zrUtil.isArray(payload.name)\n ? zrUtil.map(payload.name, function (value) {\n return data.indexOfName(value);\n })\n : data.indexOfName(payload.name);\n }\n}\n\n/**\n * Enable property storage to any host object.\n * Notice: Serialization is not supported.\n *\n * For example:\n * var get = modelUitl.makeGetter();\n *\n * function some(hostObj) {\n * get(hostObj)._someProperty = 1212;\n * ...\n * }\n *\n * @return {Function}\n */\nexport var makeGetter = (function () {\n var index = 0;\n return function () {\n var key = '\\0__ec_prop_getter_' + index++;\n return function (hostObj) {\n return hostObj[key] || (hostObj[key] = {});\n };\n };\n})();\n\n/**\n * @param {module:echarts/model/Global} ecModel\n * @param {string|Object} finder\n * If string, e.g., 'geo', means {geoIndex: 0}.\n * If Object, could contain some of these properties below:\n * {\n * seriesIndex, seriesId, seriesName,\n * geoIndex, geoId, geoName,\n * bmapIndex, bmapId, bmapName,\n * xAxisIndex, xAxisId, xAxisName,\n * yAxisIndex, yAxisId, yAxisName,\n * gridIndex, gridId, gridName,\n * ... (can be extended)\n * }\n * Each properties can be number|string|Array.<number>|Array.<string>\n * For example, a finder could be\n * {\n * seriesIndex: 3,\n * geoId: ['aa', 'cc'],\n * gridName: ['xx', 'rr']\n * }\n * xxxIndex can be set as 'all' (means all xxx) or 'none' (means not specify)\n * If nothing or null/undefined specified, return nothing.\n * @param {Object} [opt]\n * @param {string} [opt.defaultMainType]\n * @param {Array.<string>} [opt.includeMainTypes]\n * @return {Object} result like:\n * {\n * seriesModels: [seriesModel1, seriesModel2],\n * seriesModel: seriesModel1, // The first model\n * geoModels: [geoModel1, geoModel2],\n * geoModel: geoModel1, // The first model\n * ...\n * }\n */\nexport function parseFinder(ecModel, finder, opt) {\n if (zrUtil.isString(finder)) {\n var obj = {};\n obj[finder + 'Index'] = 0;\n finder = obj;\n }\n\n var defaultMainType = opt && opt.defaultMainType;\n if (defaultMainType\n && !has(finder, defaultMainType + 'Index')\n && !has(finder, defaultMainType + 'Id')\n && !has(finder, defaultMainType + 'Name')\n ) {\n finder[defaultMainType + 'Index'] = 0;\n }\n\n var result = {};\n\n each(finder, function (value, key) {\n var value = finder[key];\n\n // Exclude 'dataIndex' and other illgal keys.\n if (key === 'dataIndex' || key === 'dataIndexInside') {\n result[key] = value;\n return;\n }\n\n var parsedKey = key.match(/^(\\w+)(Index|Id|Name)$/) || [];\n var mainType = parsedKey[1];\n var queryType = (parsedKey[2] || '').toLowerCase();\n\n if (!mainType\n || !queryType\n || value == null\n || (queryType === 'index' && value === 'none')\n || (opt && opt.includeMainTypes && zrUtil.indexOf(opt.includeMainTypes, mainType) < 0)\n ) {\n return;\n }\n\n var queryParam = {mainType: mainType};\n if (queryType !== 'index' || value !== 'all') {\n queryParam[queryType] = value;\n }\n\n var models = ecModel.queryComponents(queryParam);\n result[mainType + 'Models'] = models;\n result[mainType + 'Model'] = models[0];\n });\n\n return result;\n}\n\n/**\n * @see {module:echarts/data/helper/completeDimensions}\n * @param {module:echarts/data/List} data\n * @param {string|number} dataDim\n * @return {string}\n */\nexport function dataDimToCoordDim(data, dataDim) {\n var dimensions = data.dimensions;\n dataDim = data.getDimension(dataDim);\n for (var i = 0; i < dimensions.length; i++) {\n var dimItem = data.getDimensionInfo(dimensions[i]);\n if (dimItem.name === dataDim) {\n return dimItem.coordDim;\n }\n }\n}\n\n/**\n * @see {module:echarts/data/helper/completeDimensions}\n * @param {module:echarts/data/List} data\n * @param {string} coordDim\n * @return {Array.<string>} data dimensions on the coordDim.\n */\nexport function coordDimToDataDim(data, coordDim) {\n var dataDim = [];\n each(data.dimensions, function (dimName) {\n var dimItem = data.getDimensionInfo(dimName);\n if (dimItem.coordDim === coordDim) {\n dataDim[dimItem.coordDimIndex] = dimItem.name;\n }\n });\n return dataDim;\n}\n\n/**\n * @see {module:echarts/data/helper/completeDimensions}\n * @param {module:echarts/data/List} data\n * @param {string} otherDim Can be `otherDims`\n * like 'label' or 'tooltip'.\n * @return {Array.<string>} data dimensions on the otherDim.\n */\nexport function otherDimToDataDim(data, otherDim) {\n var dataDim = [];\n each(data.dimensions, function (dimName) {\n var dimItem = data.getDimensionInfo(dimName);\n var otherDims = dimItem.otherDims;\n var dimIndex = otherDims[otherDim];\n if (dimIndex != null && dimIndex !== false) {\n dataDim[dimIndex] = dimItem.name;\n }\n });\n return dataDim;\n}\n\nfunction has(obj, prop) {\n return obj && obj.hasOwnProperty(prop);\n}\n","import * as zrUtil from 'zrender/src/core/util';\nimport {parseClassType} from './clazz';\n\nvar base = 0;\n\nvar DELIMITER = '_';\n\n/**\n * @public\n * @param {string} type\n * @return {string}\n */\nexport function getUID(type) {\n // Considering the case of crossing js context,\n // use Math.random to make id as unique as possible.\n return [(type || ''), base++, Math.random()].join(DELIMITER);\n}\n\n/**\n * @inner\n */\nexport function enableSubTypeDefaulter(entity) {\n\n var subTypeDefaulters = {};\n\n entity.registerSubTypeDefaulter = function (componentType, defaulter) {\n componentType = parseClassType(componentType);\n subTypeDefaulters[componentType.main] = defaulter;\n };\n\n entity.determineSubType = function (componentType, option) {\n var type = option.type;\n if (!type) {\n var componentTypeMain = parseClassType(componentType).main;\n if (entity.hasSubTypes(componentType) && subTypeDefaulters[componentTypeMain]) {\n type = subTypeDefaulters[componentTypeMain](option);\n }\n }\n return type;\n };\n\n return entity;\n}\n\n/**\n * Topological travel on Activity Network (Activity On Vertices).\n * Dependencies is defined in Model.prototype.dependencies, like ['xAxis', 'yAxis'].\n *\n * If 'xAxis' or 'yAxis' is absent in componentTypeList, just ignore it in topology.\n *\n * If there is circle dependencey, Error will be thrown.\n *\n */\nexport function enableTopologicalTravel(entity, dependencyGetter) {\n\n /**\n * @public\n * @param {Array.<string>} targetNameList Target Component type list.\n * Can be ['aa', 'bb', 'aa.xx']\n * @param {Array.<string>} fullNameList By which we can build dependency graph.\n * @param {Function} callback Params: componentType, dependencies.\n * @param {Object} context Scope of callback.\n */\n entity.topologicalTravel = function (targetNameList, fullNameList, callback, context) {\n if (!targetNameList.length) {\n return;\n }\n\n var result = makeDepndencyGraph(fullNameList);\n var graph = result.graph;\n var stack = result.noEntryList;\n\n var targetNameSet = {};\n zrUtil.each(targetNameList, function (name) {\n targetNameSet[name] = true;\n });\n\n while (stack.length) {\n var currComponentType = stack.pop();\n var currVertex = graph[currComponentType];\n var isInTargetNameSet = !!targetNameSet[currComponentType];\n if (isInTargetNameSet) {\n callback.call(context, currComponentType, currVertex.originalDeps.slice());\n delete targetNameSet[currComponentType];\n }\n zrUtil.each(\n currVertex.successor,\n isInTargetNameSet ? removeEdgeAndAdd : removeEdge\n );\n }\n\n zrUtil.each(targetNameSet, function () {\n throw new Error('Circle dependency may exists');\n });\n\n function removeEdge(succComponentType) {\n graph[succComponentType].entryCount--;\n if (graph[succComponentType].entryCount === 0) {\n stack.push(succComponentType);\n }\n }\n\n // Consider this case: legend depends on series, and we call\n // chart.setOption({series: [...]}), where only series is in option.\n // If we do not have 'removeEdgeAndAdd', legendModel.mergeOption will\n // not be called, but only sereis.mergeOption is called. Thus legend\n // have no chance to update its local record about series (like which\n // name of series is available in legend).\n function removeEdgeAndAdd(succComponentType) {\n targetNameSet[succComponentType] = true;\n removeEdge(succComponentType);\n }\n };\n\n /**\n * DepndencyGraph: {Object}\n * key: conponentType,\n * value: {\n * successor: [conponentTypes...],\n * originalDeps: [conponentTypes...],\n * entryCount: {number}\n * }\n */\n function makeDepndencyGraph(fullNameList) {\n var graph = {};\n var noEntryList = [];\n\n zrUtil.each(fullNameList, function (name) {\n\n var thisItem = createDependencyGraphItem(graph, name);\n var originalDeps = thisItem.originalDeps = dependencyGetter(name);\n\n var availableDeps = getAvailableDependencies(originalDeps, fullNameList);\n thisItem.entryCount = availableDeps.length;\n if (thisItem.entryCount === 0) {\n noEntryList.push(name);\n }\n\n zrUtil.each(availableDeps, function (dependentName) {\n if (zrUtil.indexOf(thisItem.predecessor, dependentName) < 0) {\n thisItem.predecessor.push(dependentName);\n }\n var thatItem = createDependencyGraphItem(graph, dependentName);\n if (zrUtil.indexOf(thatItem.successor, dependentName) < 0) {\n thatItem.successor.push(name);\n }\n });\n });\n\n return {graph: graph, noEntryList: noEntryList};\n }\n\n function createDependencyGraphItem(graph, name) {\n if (!graph[name]) {\n graph[name] = {predecessor: [], successor: []};\n }\n return graph[name];\n }\n\n function getAvailableDependencies(originalDeps, fullNameList) {\n var availableDeps = [];\n zrUtil.each(originalDeps, function (dep) {\n zrUtil.indexOf(fullNameList, dep) >= 0 && availableDeps.push(dep);\n });\n return availableDeps;\n }\n}\n","// Layout helpers for each component positioning\n\nimport * as zrUtil from 'zrender/src/core/util';\nimport BoundingRect from 'zrender/src/core/BoundingRect';\nimport {parsePercent} from './number';\nimport * as formatUtil from './format';\n\nvar each = zrUtil.each;\n\n/**\n * @public\n */\nexport var LOCATION_PARAMS = [\n 'left', 'right', 'top', 'bottom', 'width', 'height'\n];\n\n/**\n * @public\n */\nexport var HV_NAMES = [\n ['width', 'left', 'right'],\n ['height', 'top', 'bottom']\n];\n\nfunction boxLayout(orient, group, gap, maxWidth, maxHeight) {\n var x = 0;\n var y = 0;\n\n if (maxWidth == null) {\n maxWidth = Infinity;\n }\n if (maxHeight == null) {\n maxHeight = Infinity;\n }\n var currentLineMaxSize = 0;\n\n group.eachChild(function (child, idx) {\n var position = child.position;\n var rect = child.getBoundingRect();\n var nextChild = group.childAt(idx + 1);\n var nextChildRect = nextChild && nextChild.getBoundingRect();\n var nextX;\n var nextY;\n\n if (orient === 'horizontal') {\n var moveX = rect.width + (nextChildRect ? (-nextChildRect.x + rect.x) : 0);\n nextX = x + moveX;\n // Wrap when width exceeds maxWidth or meet a `newline` group\n // FIXME compare before adding gap?\n if (nextX > maxWidth || child.newline) {\n x = 0;\n nextX = moveX;\n y += currentLineMaxSize + gap;\n currentLineMaxSize = rect.height;\n }\n else {\n // FIXME: consider rect.y is not `0`?\n currentLineMaxSize = Math.max(currentLineMaxSize, rect.height);\n }\n }\n else {\n var moveY = rect.height + (nextChildRect ? (-nextChildRect.y + rect.y) : 0);\n nextY = y + moveY;\n // Wrap when width exceeds maxHeight or meet a `newline` group\n if (nextY > maxHeight || child.newline) {\n x += currentLineMaxSize + gap;\n y = 0;\n nextY = moveY;\n currentLineMaxSize = rect.width;\n }\n else {\n currentLineMaxSize = Math.max(currentLineMaxSize, rect.width);\n }\n }\n\n if (child.newline) {\n return;\n }\n\n position[0] = x;\n position[1] = y;\n\n orient === 'horizontal'\n ? (x = nextX + gap)\n : (y = nextY + gap);\n });\n}\n\n/**\n * VBox or HBox layouting\n * @param {string} orient\n * @param {module:zrender/container/Group} group\n * @param {number} gap\n * @param {number} [width=Infinity]\n * @param {number} [height=Infinity]\n */\nexport var box = boxLayout;\n\n/**\n * VBox layouting\n * @param {module:zrender/container/Group} group\n * @param {number} gap\n * @param {number} [width=Infinity]\n * @param {number} [height=Infinity]\n */\nexport var vbox = zrUtil.curry(boxLayout, 'vertical');\n\n/**\n * HBox layouting\n * @param {module:zrender/container/Group} group\n * @param {number} gap\n * @param {number} [width=Infinity]\n * @param {number} [height=Infinity]\n */\nexport var hbox = zrUtil.curry(boxLayout, 'horizontal');\n\n/**\n * If x or x2 is not specified or 'center' 'left' 'right',\n * the width would be as long as possible.\n * If y or y2 is not specified or 'middle' 'top' 'bottom',\n * the height would be as long as possible.\n *\n * @param {Object} positionInfo\n * @param {number|string} [positionInfo.x]\n * @param {number|string} [positionInfo.y]\n * @param {number|string} [positionInfo.x2]\n * @param {number|string} [positionInfo.y2]\n * @param {Object} containerRect {width, height}\n * @param {string|number} margin\n * @return {Object} {width, height}\n */\nexport function getAvailableSize(positionInfo, containerRect, margin) {\n var containerWidth = containerRect.width;\n var containerHeight = containerRect.height;\n\n var x = parsePercent(positionInfo.x, containerWidth);\n var y = parsePercent(positionInfo.y, containerHeight);\n var x2 = parsePercent(positionInfo.x2, containerWidth);\n var y2 = parsePercent(positionInfo.y2, containerHeight);\n\n (isNaN(x) || isNaN(parseFloat(positionInfo.x))) && (x = 0);\n (isNaN(x2) || isNaN(parseFloat(positionInfo.x2))) && (x2 = containerWidth);\n (isNaN(y) || isNaN(parseFloat(positionInfo.y))) && (y = 0);\n (isNaN(y2) || isNaN(parseFloat(positionInfo.y2))) && (y2 = containerHeight);\n\n margin = formatUtil.normalizeCssArray(margin || 0);\n\n return {\n width: Math.max(x2 - x - margin[1] - margin[3], 0),\n height: Math.max(y2 - y - margin[0] - margin[2], 0)\n };\n}\n\n/**\n * Parse position info.\n *\n * @param {Object} positionInfo\n * @param {number|string} [positionInfo.left]\n * @param {number|string} [positionInfo.top]\n * @param {number|string} [positionInfo.right]\n * @param {number|string} [positionInfo.bottom]\n * @param {number|string} [positionInfo.width]\n * @param {number|string} [positionInfo.height]\n * @param {number|string} [positionInfo.aspect] Aspect is width / height\n * @param {Object} containerRect\n * @param {string|number} [margin]\n *\n * @return {module:zrender/core/BoundingRect}\n */\nexport function getLayoutRect(\n positionInfo, containerRect, margin\n) {\n margin = formatUtil.normalizeCssArray(margin || 0);\n\n var containerWidth = containerRect.width;\n var containerHeight = containerRect.height;\n\n var left = parsePercent(positionInfo.left, containerWidth);\n var top = parsePercent(positionInfo.top, containerHeight);\n var right = parsePercent(positionInfo.right, containerWidth);\n var bottom = parsePercent(positionInfo.bottom, containerHeight);\n var width = parsePercent(positionInfo.width, containerWidth);\n var height = parsePercent(positionInfo.height, containerHeight);\n\n var verticalMargin = margin[2] + margin[0];\n var horizontalMargin = margin[1] + margin[3];\n var aspect = positionInfo.aspect;\n\n // If width is not specified, calculate width from left and right\n if (isNaN(width)) {\n width = containerWidth - right - horizontalMargin - left;\n }\n if (isNaN(height)) {\n height = containerHeight - bottom - verticalMargin - top;\n }\n\n if (aspect != null) {\n // If width and height are not given\n // 1. Graph should not exceeds the container\n // 2. Aspect must be keeped\n // 3. Graph should take the space as more as possible\n // FIXME\n // Margin is not considered, because there is no case that both\n // using margin and aspect so far.\n if (isNaN(width) && isNaN(height)) {\n if (aspect > containerWidth / containerHeight) {\n width = containerWidth * 0.8;\n }\n else {\n height = containerHeight * 0.8;\n }\n }\n\n // Calculate width or height with given aspect\n if (isNaN(width)) {\n width = aspect * height;\n }\n if (isNaN(height)) {\n height = width / aspect;\n }\n }\n\n // If left is not specified, calculate left from right and width\n if (isNaN(left)) {\n left = containerWidth - right - width - horizontalMargin;\n }\n if (isNaN(top)) {\n top = containerHeight - bottom - height - verticalMargin;\n }\n\n // Align left and top\n switch (positionInfo.left || positionInfo.right) {\n case 'center':\n left = containerWidth / 2 - width / 2 - margin[3];\n break;\n case 'right':\n left = containerWidth - width - horizontalMargin;\n break;\n }\n switch (positionInfo.top || positionInfo.bottom) {\n case 'middle':\n case 'center':\n top = containerHeight / 2 - height / 2 - margin[0];\n break;\n case 'bottom':\n top = containerHeight - height - verticalMargin;\n break;\n }\n // If something is wrong and left, top, width, height are calculated as NaN\n left = left || 0;\n top = top || 0;\n if (isNaN(width)) {\n // Width may be NaN if only one value is given except width\n width = containerWidth - horizontalMargin - left - (right || 0);\n }\n if (isNaN(height)) {\n // Height may be NaN if only one value is given except height\n height = containerHeight - verticalMargin - top - (bottom || 0);\n }\n\n var rect = new BoundingRect(left + margin[3], top + margin[0], width, height);\n rect.margin = margin;\n return rect;\n}\n\n\n/**\n * Position a zr element in viewport\n * Group position is specified by either\n * {left, top}, {right, bottom}\n * If all properties exists, right and bottom will be igonred.\n *\n * Logic:\n * 1. Scale (against origin point in parent coord)\n * 2. Rotate (against origin point in parent coord)\n * 3. Traslate (with el.position by this method)\n * So this method only fixes the last step 'Traslate', which does not affect\n * scaling and rotating.\n *\n * If be called repeatly with the same input el, the same result will be gotten.\n *\n * @param {module:zrender/Element} el Should have `getBoundingRect` method.\n * @param {Object} positionInfo\n * @param {number|string} [positionInfo.left]\n * @param {number|string} [positionInfo.top]\n * @param {number|string} [positionInfo.right]\n * @param {number|string} [positionInfo.bottom]\n * @param {number|string} [positionInfo.width] Only for opt.boundingModel: 'raw'\n * @param {number|string} [positionInfo.height] Only for opt.boundingModel: 'raw'\n * @param {Object} containerRect\n * @param {string|number} margin\n * @param {Object} [opt]\n * @param {Array.<number>} [opt.hv=[1,1]] Only horizontal or only vertical.\n * @param {Array.<number>} [opt.boundingMode='all']\n * Specify how to calculate boundingRect when locating.\n * 'all': Position the boundingRect that is transformed and uioned\n * both itself and its descendants.\n * This mode simplies confine the elements in the bounding\n * of their container (e.g., using 'right: 0').\n * 'raw': Position the boundingRect that is not transformed and only itself.\n * This mode is useful when you want a element can overflow its\n * container. (Consider a rotated circle needs to be located in a corner.)\n * In this mode positionInfo.width/height can only be number.\n */\nexport function positionElement(el, positionInfo, containerRect, margin, opt) {\n var h = !opt || !opt.hv || opt.hv[0];\n var v = !opt || !opt.hv || opt.hv[1];\n var boundingMode = opt && opt.boundingMode || 'all';\n\n if (!h && !v) {\n return;\n }\n\n var rect;\n if (boundingMode === 'raw') {\n rect = el.type === 'group'\n ? new BoundingRect(0, 0, +positionInfo.width || 0, +positionInfo.height || 0)\n : el.getBoundingRect();\n }\n else {\n rect = el.getBoundingRect();\n if (el.needLocalTransform()) {\n var transform = el.getLocalTransform();\n // Notice: raw rect may be inner object of el,\n // which should not be modified.\n rect = rect.clone();\n rect.applyTransform(transform);\n }\n }\n\n // The real width and height can not be specified but calculated by the given el.\n positionInfo = getLayoutRect(\n zrUtil.defaults(\n {width: rect.width, height: rect.height},\n positionInfo\n ),\n containerRect,\n margin\n );\n\n // Because 'tranlate' is the last step in transform\n // (see zrender/core/Transformable#getLocalTransfrom),\n // we can just only modify el.position to get final result.\n var elPos = el.position;\n var dx = h ? positionInfo.x - rect.x : 0;\n var dy = v ? positionInfo.y - rect.y : 0;\n\n el.attr('position', boundingMode === 'raw' ? [dx, dy] : [elPos[0] + dx, elPos[1] + dy]);\n}\n\n/**\n * @param {Object} option Contains some of the properties in HV_NAMES.\n * @param {number} hvIdx 0: horizontal; 1: vertical.\n */\nexport function sizeCalculable(option, hvIdx) {\n return option[HV_NAMES[hvIdx][0]] != null\n || (option[HV_NAMES[hvIdx][1]] != null && option[HV_NAMES[hvIdx][2]] != null);\n}\n\n/**\n * Consider Case:\n * When defulat option has {left: 0, width: 100}, and we set {right: 0}\n * through setOption or media query, using normal zrUtil.merge will cause\n * {right: 0} does not take effect.\n *\n * @example\n * ComponentModel.extend({\n * init: function () {\n * ...\n * var inputPositionParams = layout.getLayoutParams(option);\n * this.mergeOption(inputPositionParams);\n * },\n * mergeOption: function (newOption) {\n * newOption && zrUtil.merge(thisOption, newOption, true);\n * layout.mergeLayoutParam(thisOption, newOption);\n * }\n * });\n *\n * @param {Object} targetOption\n * @param {Object} newOption\n * @param {Object|string} [opt]\n * @param {boolean|Array.<boolean>} [opt.ignoreSize=false] Used for the components\n * that width (or height) should not be calculated by left and right (or top and bottom).\n */\nexport function mergeLayoutParam(targetOption, newOption, opt) {\n !zrUtil.isObject(opt) && (opt = {});\n\n var ignoreSize = opt.ignoreSize;\n !zrUtil.isArray(ignoreSize) && (ignoreSize = [ignoreSize, ignoreSize]);\n\n var hResult = merge(HV_NAMES[0], 0);\n var vResult = merge(HV_NAMES[1], 1);\n\n copy(HV_NAMES[0], targetOption, hResult);\n copy(HV_NAMES[1], targetOption, vResult);\n\n function merge(names, hvIdx) {\n var newParams = {};\n var newValueCount = 0;\n var merged = {};\n var mergedValueCount = 0;\n var enoughParamNumber = 2;\n\n each(names, function (name) {\n merged[name] = targetOption[name];\n });\n each(names, function (name) {\n // Consider case: newOption.width is null, which is\n // set by user for removing width setting.\n hasProp(newOption, name) && (newParams[name] = merged[name] = newOption[name]);\n hasValue(newParams, name) && newValueCount++;\n hasValue(merged, name) && mergedValueCount++;\n });\n\n if (ignoreSize[hvIdx]) {\n // Only one of left/right is premitted to exist.\n if (hasValue(newOption, names[1])) {\n merged[names[2]] = null;\n }\n else if (hasValue(newOption, names[2])) {\n merged[names[1]] = null;\n }\n return merged;\n }\n\n // Case: newOption: {width: ..., right: ...},\n // or targetOption: {right: ...} and newOption: {width: ...},\n // There is no conflict when merged only has params count\n // little than enoughParamNumber.\n if (mergedValueCount === enoughParamNumber || !newValueCount) {\n return merged;\n }\n // Case: newOption: {width: ..., right: ...},\n // Than we can make sure user only want those two, and ignore\n // all origin params in targetOption.\n else if (newValueCount >= enoughParamNumber) {\n return newParams;\n }\n else {\n // Chose another param from targetOption by priority.\n for (var i = 0; i < names.length; i++) {\n var name = names[i];\n if (!hasProp(newParams, name) && hasProp(targetOption, name)) {\n newParams[name] = targetOption[name];\n break;\n }\n }\n return newParams;\n }\n }\n\n function hasProp(obj, name) {\n return obj.hasOwnProperty(name);\n }\n\n function hasValue(obj, name) {\n return obj[name] != null && obj[name] !== 'auto';\n }\n\n function copy(names, target, source) {\n each(names, function (name) {\n target[name] = source[name];\n });\n }\n}\n\n/**\n * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object.\n * @param {Object} source\n * @return {Object} Result contains those props.\n */\nexport function getLayoutParams(source) {\n return copyLayoutParams({}, source);\n}\n\n/**\n * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object.\n * @param {Object} source\n * @return {Object} Result contains those props.\n */\nexport function copyLayoutParams(target, source) {\n source && target && each(LOCATION_PARAMS, function (name) {\n source.hasOwnProperty(name) && (target[name] = source[name]);\n });\n return target;\n}\n","\nexport default {\n getBoxLayoutParams: function () {\n return {\n left: this.get('left'),\n top: this.get('top'),\n right: this.get('right'),\n bottom: this.get('bottom'),\n width: this.get('width'),\n height: this.get('height')\n };\n }\n};","/**\n * Component model\n *\n * @module echarts/model/Component\n */\n\nimport * as zrUtil from 'zrender/src/core/util';\nimport Model from './Model';\nimport * as componentUtil from '../util/component';\nimport * as clazzUtil from '../util/clazz';\nimport * as layout from '../util/layout';\nimport boxLayoutMixin from './mixin/boxLayout';\n\nvar arrayPush = Array.prototype.push;\n\n/**\n * @alias module:echarts/model/Component\n * @constructor\n * @param {Object} option\n * @param {module:echarts/model/Model} parentModel\n * @param {module:echarts/model/Model} ecModel\n */\nvar ComponentModel = Model.extend({\n\n type: 'component',\n\n /**\n * @readOnly\n * @type {string}\n */\n id: '',\n\n /**\n * @readOnly\n */\n name: '',\n\n /**\n * @readOnly\n * @type {string}\n */\n mainType: '',\n\n /**\n * @readOnly\n * @type {string}\n */\n subType: '',\n\n /**\n * @readOnly\n * @type {number}\n */\n componentIndex: 0,\n\n /**\n * @type {Object}\n * @protected\n */\n defaultOption: null,\n\n /**\n * @type {module:echarts/model/Global}\n * @readOnly\n */\n ecModel: null,\n\n /**\n * key: componentType\n * value: Component model list, can not be null.\n * @type {Object.<string, Array.<module:echarts/model/Model>>}\n * @readOnly\n */\n dependentModels: [],\n\n /**\n * @type {string}\n * @readOnly\n */\n uid: null,\n\n /**\n * Support merge layout params.\n * Only support 'box' now (left/right/top/bottom/width/height).\n * @type {string|Object} Object can be {ignoreSize: true}\n * @readOnly\n */\n layoutMode: null,\n\n $constructor: function (option, parentModel, ecModel, extraOpt) {\n Model.call(this, option, parentModel, ecModel, extraOpt);\n\n this.uid = componentUtil.getUID('componentModel');\n },\n\n\n init: function (option, parentModel, ecModel, extraOpt) {\n this.mergeDefaultAndTheme(option, ecModel);\n },\n\n mergeDefaultAndTheme: function (option, ecModel) {\n var layoutMode = this.layoutMode;\n var inputPositionParams = layoutMode\n ? layout.getLayoutParams(option) : {};\n\n var themeModel = ecModel.getTheme();\n zrUtil.merge(option, themeModel.get(this.mainType));\n zrUtil.merge(option, this.getDefaultOption());\n\n if (layoutMode) {\n layout.mergeLayoutParam(option, inputPositionParams, layoutMode);\n }\n },\n\n mergeOption: function (option, extraOpt) {\n zrUtil.merge(this.option, option, true);\n\n var layoutMode = this.layoutMode;\n if (layoutMode) {\n layout.mergeLayoutParam(this.option, option, layoutMode);\n }\n },\n\n // Hooker after init or mergeOption\n optionUpdated: function (newCptOption, isInit) {},\n\n getDefaultOption: function () {\n if (!clazzUtil.hasOwn(this, '__defaultOption')) {\n var optList = [];\n var Class = this.constructor;\n while (Class) {\n var opt = Class.prototype.defaultOption;\n opt && optList.push(opt);\n Class = Class.superClass;\n }\n\n var defaultOption = {};\n for (var i = optList.length - 1; i >= 0; i--) {\n defaultOption = zrUtil.merge(defaultOption, optList[i], true);\n }\n clazzUtil.set(this, '__defaultOption', defaultOption);\n }\n return clazzUtil.get(this, '__defaultOption');\n },\n\n getReferringComponents: function (mainType) {\n return this.ecModel.queryComponents({\n mainType: mainType,\n index: this.get(mainType + 'Index', true),\n id: this.get(mainType + 'Id', true)\n });\n }\n\n});\n\n// Reset ComponentModel.extend, add preConstruct.\n// clazzUtil.enableClassExtend(\n// ComponentModel,\n// function (option, parentModel, ecModel, extraOpt) {\n// // Set dependentModels, componentIndex, name, id, mainType, subType.\n// zrUtil.extend(this, extraOpt);\n\n// this.uid = componentUtil.getUID('componentModel');\n\n// // this.setReadOnly([\n// // 'type', 'id', 'uid', 'name', 'mainType', 'subType',\n// // 'dependentModels', 'componentIndex'\n// // ]);\n// }\n// );\n\n// Add capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on.\nclazzUtil.enableClassManagement(\n ComponentModel, {registerWhenExtend: true}\n);\ncomponentUtil.enableSubTypeDefaulter(ComponentModel);\n\n// Add capability of ComponentModel.topologicalTravel.\ncomponentUtil.enableTopologicalTravel(ComponentModel, getDependencies);\n\nfunction getDependencies(componentType) {\n var deps = [];\n zrUtil.each(ComponentModel.getClassesByMainType(componentType), function (Clazz) {\n arrayPush.apply(deps, Clazz.prototype.dependencies || []);\n });\n // Ensure main type\n return zrUtil.map(deps, function (type) {\n return clazzUtil.parseClassType(type).main;\n });\n}\n\nzrUtil.mixin(ComponentModel, boxLayoutMixin);\n\nexport default ComponentModel;","\nvar platform = '';\n// Navigator not exists in node\nif (typeof navigator !== 'undefined') {\n platform = navigator.platform || '';\n}\n\nexport default {\n // 全图默认背景\n // backgroundColor: 'rgba(0,0,0,0)',\n\n // https://dribbble.com/shots/1065960-Infographic-Pie-chart-visualization\n // color: ['#5793f3', '#d14a61', '#fd9c35', '#675bba', '#fec42c', '#dd4444', '#d4df5a', '#cd4870'],\n // 浅色\n // color: ['#bcd3bb', '#e88f70', '#edc1a5', '#9dc5c8', '#e1e8c8', '#7b7c68', '#e5b5b5', '#f0b489', '#928ea8', '#bda29a'],\n // color: ['#cc5664', '#9bd6ec', '#ea946e', '#8acaaa', '#f1ec64', '#ee8686', '#a48dc1', '#5da6bc', '#b9dcae'],\n // 深色\n color: ['#c23531','#2f4554', '#61a0a8', '#d48265', '#91c7ae','#749f83', '#ca8622', '#bda29a','#6e7074', '#546570', '#c4ccd3'],\n\n // 默认需要 Grid 配置项\n // grid: {},\n // 主题,主题\n textStyle: {\n // color: '#000',\n // decoration: 'none',\n // PENDING\n fontFamily: platform.match(/^Win/) ? 'Microsoft YaHei' : 'sans-serif',\n // fontFamily: 'Arial, Verdana, sans-serif',\n fontSize: 12,\n fontStyle: 'normal',\n fontWeight: 'normal'\n },\n\n // http://blogs.adobe.com/webplatform/2014/02/24/using-blend-modes-in-html-canvas/\n // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation\n // Default is source-over\n blendMode: null,\n\n animation: 'auto',\n animationDuration: 1000,\n animationDurationUpdate: 300,\n animationEasing: 'exponentialOut',\n animationEasingUpdate: 'cubicOut',\n\n animationThreshold: 2000,\n // Configuration for progressive/incremental rendering\n progressiveThreshold: 3000,\n progressive: 400,\n\n // Threshold of if use single hover layer to optimize.\n // It is recommended that `hoverLayerThreshold` is equivalent to or less than\n // `progressiveThreshold`, otherwise hover will cause restart of progressive,\n // which is unexpected.\n // see example <echarts/test/heatmap-large.html>.\n hoverLayerThreshold: 3000,\n\n // See: module:echarts/scale/Time\n useUTC: false\n};","import {set, get} from '../../util/clazz';\n\nexport default {\n clearColorPalette: function () {\n set(this, 'colorIdx', 0);\n set(this, 'colorNameMap', {});\n },\n\n getColorFromPalette: function (name, scope) {\n scope = scope || this;\n var colorIdx = get(scope, 'colorIdx') || 0;\n var colorNameMap = get(scope, 'colorNameMap') || set(scope, 'colorNameMap', {});\n // Use `hasOwnProperty` to avoid conflict with Object.prototype.\n if (colorNameMap.hasOwnProperty(name)) {\n return colorNameMap[name];\n }\n var colorPalette = this.get('color', true) || [];\n if (!colorPalette.length) {\n return;\n }\n\n var color = colorPalette[colorIdx];\n if (name) {\n colorNameMap[name] = color;\n }\n set(scope, 'colorIdx', (colorIdx + 1) % colorPalette.length);\n\n return color;\n }\n};","/**\n * ECharts global model\n *\n * @module {echarts/model/Global}\n */\n\n\n/**\n * Caution: If the mechanism should be changed some day, these cases\n * should be considered:\n *\n * (1) In `merge option` mode, if using the same option to call `setOption`\n * many times, the result should be the same (try our best to ensure that).\n * (2) In `merge option` mode, if a component has no id/name specified, it\n * will be merged by index, and the result sequence of the components is\n * consistent to the original sequence.\n * (3) `reset` feature (in toolbox). Find detailed info in comments about\n * `mergeOption` in module:echarts/model/OptionManager.\n */\n\nimport {__DEV__} from '../config';\nimport * as zrUtil from 'zrender/src/core/util';\nimport * as modelUtil from '../util/model';\nimport Model from './Model';\nimport ComponentModel from './Component';\nimport globalDefault from './globalDefault';\nimport colorPaletteMinin from './mixin/colorPalette';\n\nvar each = zrUtil.each;\nvar filter = zrUtil.filter;\nvar map = zrUtil.map;\nvar isArray = zrUtil.isArray;\nvar indexOf = zrUtil.indexOf;\nvar isObject = zrUtil.isObject;\n\nvar OPTION_INNER_KEY = '\\0_ec_inner';\n\n/**\n * @alias module:echarts/model/Global\n *\n * @param {Object} option\n * @param {module:echarts/model/Model} parentModel\n * @param {Object} theme\n */\nvar GlobalModel = Model.extend({\n\n constructor: GlobalModel,\n\n init: function (option, parentModel, theme, optionManager) {\n theme = theme || {};\n\n this.option = null; // Mark as not initialized.\n\n /**\n * @type {module:echarts/model/Model}\n * @private\n */\n this._theme = new Model(theme);\n\n /**\n * @type {module:echarts/model/OptionManager}\n */\n this._optionManager = optionManager;\n },\n\n setOption: function (option, optionPreprocessorFuncs) {\n zrUtil.assert(\n !(OPTION_INNER_KEY in option),\n 'please use chart.getOption()'\n );\n\n this._optionManager.setOption(option, optionPreprocessorFuncs);\n\n this.resetOption(null);\n },\n\n /**\n * @param {string} type null/undefined: reset all.\n * 'recreate': force recreate all.\n * 'timeline': only reset timeline option\n * 'media': only reset media query option\n * @return {boolean} Whether option changed.\n */\n resetOption: function (type) {\n var optionChanged = false;\n var optionManager = this._optionManager;\n\n if (!type || type === 'recreate') {\n var baseOption = optionManager.mountOption(type === 'recreate');\n\n if (!this.option || type === 'recreate') {\n initBase.call(this, baseOption);\n }\n else {\n this.restoreData();\n this.mergeOption(baseOption);\n }\n optionChanged = true;\n }\n\n if (type === 'timeline' || type === 'media') {\n this.restoreData();\n }\n\n if (!type || type === 'recreate' || type === 'timeline') {\n var timelineOption = optionManager.getTimelineOption(this);\n timelineOption && (this.mergeOption(timelineOption), optionChanged = true);\n }\n\n if (!type || type === 'recreate' || type === 'media') {\n var mediaOptions = optionManager.getMediaOption(this, this._api);\n if (mediaOptions.length) {\n each(mediaOptions, function (mediaOption) {\n this.mergeOption(mediaOption, optionChanged = true);\n }, this);\n }\n }\n\n return optionChanged;\n },\n\n /**\n * @protected\n */\n mergeOption: function (newOption) {\n var option = this.option;\n var componentsMap = this._componentsMap;\n var newCptTypes = [];\n\n // 如果不存在对应的 component model 则直接 merge\n each(newOption, function (componentOption, mainType) {\n if (componentOption == null) {\n return;\n }\n\n if (!ComponentModel.hasClass(mainType)) {\n option[mainType] = option[mainType] == null\n ? zrUtil.clone(componentOption)\n : zrUtil.merge(option[mainType], componentOption, true);\n }\n else {\n newCptTypes.push(mainType);\n }\n });\n\n // FIXME OPTION 同步是否要改回原来的\n ComponentModel.topologicalTravel(\n newCptTypes, ComponentModel.getAllClassMainTypes(), visitComponent, this\n );\n\n this._seriesIndices = this._seriesIndices || [];\n\n function visitComponent(mainType, dependencies) {\n var newCptOptionList = modelUtil.normalizeToArray(newOption[mainType]);\n\n var mapResult = modelUtil.mappingToExists(\n componentsMap.get(mainType), newCptOptionList\n );\n\n modelUtil.makeIdAndName(mapResult);\n\n // Set mainType and complete subType.\n each(mapResult, function (item, index) {\n var opt = item.option;\n if (isObject(opt)) {\n item.keyInfo.mainType = mainType;\n item.keyInfo.subType = determineSubType(mainType, opt, item.exist);\n }\n });\n\n var dependentModels = getComponentsByTypes(\n componentsMap, dependencies\n );\n\n option[mainType] = [];\n componentsMap.set(mainType, []);\n\n each(mapResult, function (resultItem, index) {\n var componentModel = resultItem.exist;\n var newCptOption = resultItem.option;\n\n zrUtil.assert(\n isObject(newCptOption) || componentModel,\n 'Empty component definition'\n );\n\n // Consider where is no new option and should be merged using {},\n // see removeEdgeAndAdd in topologicalTravel and\n // ComponentModel.getAllClassMainTypes.\n if (!newCptOption) {\n componentModel.mergeOption({}, this);\n componentModel.optionUpdated({}, false);\n }\n else {\n var ComponentModelClass = ComponentModel.getClass(\n mainType, resultItem.keyInfo.subType, true\n );\n\n if (componentModel && componentModel instanceof ComponentModelClass) {\n componentModel.name = resultItem.keyInfo.name;\n componentModel.mergeOption(newCptOption, this);\n componentModel.optionUpdated(newCptOption, false);\n }\n else {\n // PENDING Global as parent ?\n var extraOpt = zrUtil.extend(\n {\n dependentModels: dependentModels,\n componentIndex: index\n },\n resultItem.keyInfo\n );\n componentModel = new ComponentModelClass(\n newCptOption, this, this, extraOpt\n );\n zrUtil.extend(componentModel, extraOpt);\n componentModel.init(newCptOption, this, this, extraOpt);\n // Call optionUpdated after init.\n // newCptOption has been used as componentModel.option\n // and may be merged with theme and default, so pass null\n // to avoid confusion.\n componentModel.optionUpdated(null, true);\n }\n }\n\n componentsMap.get(mainType)[index] = componentModel;\n option[mainType][index] = componentModel.option;\n }, this);\n\n // Backup series for filtering.\n if (mainType === 'series') {\n this._seriesIndices = createSeriesIndices(componentsMap.get('series'));\n }\n }\n },\n\n /**\n * Get option for output (cloned option and inner info removed)\n * @public\n * @return {Object}\n */\n getOption: function () {\n var option = zrUtil.clone(this.option);\n\n each(option, function (opts, mainType) {\n if (ComponentModel.hasClass(mainType)) {\n var opts = modelUtil.normalizeToArray(opts);\n for (var i = opts.length - 1; i >= 0; i--) {\n // Remove options with inner id.\n if (modelUtil.isIdInner(opts[i])) {\n opts.splice(i, 1);\n }\n }\n option[mainType] = opts;\n }\n });\n\n delete option[OPTION_INNER_KEY];\n\n return option;\n },\n\n /**\n * @return {module:echarts/model/Model}\n */\n getTheme: function () {\n return this._theme;\n },\n\n /**\n * @param {string} mainType\n * @param {number} [idx=0]\n * @return {module:echarts/model/Component}\n */\n getComponent: function (mainType, idx) {\n var list = this._componentsMap.get(mainType);\n if (list) {\n return list[idx || 0];\n }\n },\n\n /**\n * If none of index and id and name used, return all components with mainType.\n * @param {Object} condition\n * @param {string} condition.mainType\n * @param {string} [condition.subType] If ignore, only query by mainType\n * @param {number|Array.<number>} [condition.index] Either input index or id or name.\n * @param {string|Array.<string>} [condition.id] Either input index or id or name.\n * @param {string|Array.<string>} [condition.name] Either input index or id or name.\n * @return {Array.<module:echarts/model/Component>}\n */\n queryComponents: function (condition) {\n var mainType = condition.mainType;\n if (!mainType) {\n return [];\n }\n\n var index = condition.index;\n var id = condition.id;\n var name = condition.name;\n\n var cpts = this._componentsMap.get(mainType);\n\n if (!cpts || !cpts.length) {\n return [];\n }\n\n var result;\n\n if (index != null) {\n if (!isArray(index)) {\n index = [index];\n }\n result = filter(map(index, function (idx) {\n return cpts[idx];\n }), function (val) {\n return !!val;\n });\n }\n else if (id != null) {\n var isIdArray = isArray(id);\n result = filter(cpts, function (cpt) {\n return (isIdArray && indexOf(id, cpt.id) >= 0)\n || (!isIdArray && cpt.id === id);\n });\n }\n else if (name != null) {\n var isNameArray = isArray(name);\n result = filter(cpts, function (cpt) {\n return (isNameArray && indexOf(name, cpt.name) >= 0)\n || (!isNameArray && cpt.name === name);\n });\n }\n else {\n // Return all components with mainType\n result = cpts.slice();\n }\n\n return filterBySubType(result, condition);\n },\n\n /**\n * The interface is different from queryComponents,\n * which is convenient for inner usage.\n *\n * @usage\n * var result = findComponents(\n * {mainType: 'dataZoom', query: {dataZoomId: 'abc'}}\n * );\n * var result = findComponents(\n * {mainType: 'series', subType: 'pie', query: {seriesName: 'uio'}}\n * );\n * var result = findComponents(\n * {mainType: 'series'},\n * function (model, index) {...}\n * );\n * // result like [component0, componnet1, ...]\n *\n * @param {Object} condition\n * @param {string} condition.mainType Mandatory.\n * @param {string} [condition.subType] Optional.\n * @param {Object} [condition.query] like {xxxIndex, xxxId, xxxName},\n * where xxx is mainType.\n * If query attribute is null/undefined or has no index/id/name,\n * do not filtering by query conditions, which is convenient for\n * no-payload situations or when target of action is global.\n * @param {Function} [condition.filter] parameter: component, return boolean.\n * @return {Array.<module:echarts/model/Component>}\n */\n findComponents: function (condition) {\n var query = condition.query;\n var mainType = condition.mainType;\n\n var queryCond = getQueryCond(query);\n var result = queryCond\n ? this.queryComponents(queryCond)\n : this._componentsMap.get(mainType);\n\n return doFilter(filterBySubType(result, condition));\n\n function getQueryCond(q) {\n var indexAttr = mainType + 'Index';\n var idAttr = mainType + 'Id';\n var nameAttr = mainType + 'Name';\n return q && (\n q[indexAttr] != null\n || q[idAttr] != null\n || q[nameAttr] != null\n )\n ? {\n mainType: mainType,\n // subType will be filtered finally.\n index: q[indexAttr],\n id: q[idAttr],\n name: q[nameAttr]\n }\n : null;\n }\n\n function doFilter(res) {\n return condition.filter\n ? filter(res, condition.filter)\n : res;\n }\n },\n\n /**\n * @usage\n * eachComponent('legend', function (legendModel, index) {\n * ...\n * });\n * eachComponent(function (componentType, model, index) {\n * // componentType does not include subType\n * // (componentType is 'xxx' but not 'xxx.aa')\n * });\n * eachComponent(\n * {mainType: 'dataZoom', query: {dataZoomId: 'abc'}},\n * function (model, index) {...}\n * );\n * eachComponent(\n * {mainType: 'series', subType: 'pie', query: {seriesName: 'uio'}},\n * function (model, index) {...}\n * );\n *\n * @param {string|Object=} mainType When mainType is object, the definition\n * is the same as the method 'findComponents'.\n * @param {Function} cb\n * @param {*} context\n */\n eachComponent: function (mainType, cb, context) {\n var componentsMap = this._componentsMap;\n\n if (typeof mainType === 'function') {\n context = cb;\n cb = mainType;\n componentsMap.each(function (components, componentType) {\n each(components, function (component, index) {\n cb.call(context, componentType, component, index);\n });\n });\n }\n else if (zrUtil.isString(mainType)) {\n each(componentsMap.get(mainType), cb, context);\n }\n else if (isObject(mainType)) {\n var queryResult = this.findComponents(mainType);\n each(queryResult, cb, context);\n }\n },\n\n /**\n * @param {string} name\n * @return {Array.<module:echarts/model/Series>}\n */\n getSeriesByName: function (name) {\n var series = this._componentsMap.get('series');\n return filter(series, function (oneSeries) {\n return oneSeries.name === name;\n });\n },\n\n /**\n * @param {number} seriesIndex\n * @return {module:echarts/model/Series}\n */\n getSeriesByIndex: function (seriesIndex) {\n return this._componentsMap.get('series')[seriesIndex];\n },\n\n /**\n * @param {string} subType\n * @return {Array.<module:echarts/model/Series>}\n */\n getSeriesByType: function (subType) {\n var series = this._componentsMap.get('series');\n return filter(series, function (oneSeries) {\n return oneSeries.subType === subType;\n });\n },\n\n /**\n * @return {Array.<module:echarts/model/Series>}\n */\n getSeries: function () {\n return this._componentsMap.get('series').slice();\n },\n\n /**\n * After filtering, series may be different\n * frome raw series.\n *\n * @param {Function} cb\n * @param {*} context\n */\n eachSeries: function (cb, context) {\n assertSeriesInitialized(this);\n each(this._seriesIndices, function (rawSeriesIndex) {\n var series = this._componentsMap.get('series')[rawSeriesIndex];\n cb.call(context, series, rawSeriesIndex);\n }, this);\n },\n\n /**\n * Iterate raw series before filtered.\n *\n * @param {Function} cb\n * @param {*} context\n */\n eachRawSeries: function (cb, context) {\n each(this._componentsMap.get('series'), cb, context);\n },\n\n /**\n * After filtering, series may be different.\n * frome raw series.\n *\n * @parma {string} subType\n * @param {Function} cb\n * @param {*} context\n */\n eachSeriesByType: function (subType, cb, context) {\n assertSeriesInitialized(this);\n each(this._seriesIndices, function (rawSeriesIndex) {\n var series = this._componentsMap.get('series')[rawSeriesIndex];\n if (series.subType === subType) {\n cb.call(context, series, rawSeriesIndex);\n }\n }, this);\n },\n\n /**\n * Iterate raw series before filtered of given type.\n *\n * @parma {string} subType\n * @param {Function} cb\n * @param {*} context\n */\n eachRawSeriesByType: function (subType, cb, context) {\n return each(this.getSeriesByType(subType), cb, context);\n },\n\n /**\n * @param {module:echarts/model/Series} seriesModel\n */\n isSeriesFiltered: function (seriesModel) {\n assertSeriesInitialized(this);\n return zrUtil.indexOf(this._seriesIndices, seriesModel.componentIndex) < 0;\n },\n\n /**\n * @return {Array.<number>}\n */\n getCurrentSeriesIndices: function () {\n return (this._seriesIndices || []).slice();\n },\n\n /**\n * @param {Function} cb\n * @param {*} context\n */\n filterSeries: function (cb, context) {\n assertSeriesInitialized(this);\n var filteredSeries = filter(\n this._componentsMap.get('series'), cb, context\n );\n this._seriesIndices = createSeriesIndices(filteredSeries);\n },\n\n restoreData: function () {\n var componentsMap = this._componentsMap;\n\n this._seriesIndices = createSeriesIndices(componentsMap.get('series'));\n\n var componentTypes = [];\n componentsMap.each(function (components, componentType) {\n componentTypes.push(componentType);\n });\n\n ComponentModel.topologicalTravel(\n componentTypes,\n ComponentModel.getAllClassMainTypes(),\n function (componentType, dependencies) {\n each(componentsMap.get(componentType), function (component) {\n component.restoreData();\n });\n }\n );\n }\n\n});\n\n/**\n * @inner\n */\nfunction mergeTheme(option, theme) {\n zrUtil.each(theme, function (themeItem, name) {\n // 如果有 component model 则把具体的 merge 逻辑交给该 model 处理\n if (!ComponentModel.hasClass(name)) {\n if (typeof themeItem === 'object') {\n option[name] = !option[name]\n ? zrUtil.clone(themeItem)\n : zrUtil.merge(option[name], themeItem, false);\n }\n else {\n if (option[name] == null) {\n option[name] = themeItem;\n }\n }\n }\n });\n}\n\nfunction initBase(baseOption) {\n baseOption = baseOption;\n\n // Using OPTION_INNER_KEY to mark that this option can not be used outside,\n // i.e. `chart.setOption(chart.getModel().option);` is forbiden.\n this.option = {};\n this.option[OPTION_INNER_KEY] = 1;\n\n /**\n * Init with series: [], in case of calling findSeries method\n * before series initialized.\n * @type {Object.<string, Array.<module:echarts/model/Model>>}\n * @private\n */\n this._componentsMap = zrUtil.createHashMap({series: []});\n\n /**\n * Mapping between filtered series list and raw series list.\n * key: filtered series indices, value: raw series indices.\n * @type {Array.<nubmer>}\n * @private\n */\n this._seriesIndices = null;\n\n mergeTheme(baseOption, this._theme.option);\n\n // TODO Needs clone when merging to the unexisted property\n zrUtil.merge(baseOption, globalDefault, false);\n\n this.mergeOption(baseOption);\n}\n\n/**\n * @inner\n * @param {Array.<string>|string} types model types\n * @return {Object} key: {string} type, value: {Array.<Object>} models\n */\nfunction getComponentsByTypes(componentsMap, types) {\n if (!zrUtil.isArray(types)) {\n types = types ? [types] : [];\n }\n\n var ret = {};\n each(types, function (type) {\n ret[type] = (componentsMap.get(type) || []).slice();\n });\n\n return ret;\n}\n\n/**\n * @inner\n */\nfunction determineSubType(mainType, newCptOption, existComponent) {\n var subType = newCptOption.type\n ? newCptOption.type\n : existComponent\n ? existComponent.subType\n // Use determineSubType only when there is no existComponent.\n : ComponentModel.determineSubType(mainType, newCptOption);\n\n // tooltip, markline, markpoint may always has no subType\n return subType;\n}\n\n/**\n * @inner\n */\nfunction createSeriesIndices(seriesModels) {\n return map(seriesModels, function (series) {\n return series.componentIndex;\n }) || [];\n}\n\n/**\n * @inner\n */\nfunction filterBySubType(components, condition) {\n // Using hasOwnProperty for restrict. Consider\n // subType is undefined in user payload.\n return condition.hasOwnProperty('subType')\n ? filter(components, function (cpt) {\n return cpt.subType === condition.subType;\n })\n : components;\n}\n\n/**\n * @inner\n */\nfunction assertSeriesInitialized(ecModel) {\n // Components that use _seriesIndices should depends on series component,\n // which make sure that their initialization is after series.\n if (__DEV__) {\n if (!ecModel._seriesIndices) {\n throw new Error('Option should contains series.');\n }\n }\n}\n\nzrUtil.mixin(GlobalModel, colorPaletteMinin);\n\nexport default GlobalModel;\n","import * as zrUtil from 'zrender/src/core/util';\n\nvar echartsAPIList = [\n 'getDom', 'getZr', 'getWidth', 'getHeight', 'getDevicePixelRatio', 'dispatchAction', 'isDisposed',\n 'on', 'off', 'getDataURL', 'getConnectedDataURL', 'getModel', 'getOption',\n 'getViewOfComponentModel', 'getViewOfSeriesModel'\n];\n// And `getCoordinateSystems` and `getComponentByElement` will be injected in echarts.js\n\nfunction ExtensionAPI(chartInstance) {\n zrUtil.each(echartsAPIList, function (name) {\n this[name] = zrUtil.bind(chartInstance[name], chartInstance);\n }, this);\n}\n\nexport default ExtensionAPI;","import * as zrUtil from 'zrender/src/core/util';\n\nvar coordinateSystemCreators = {};\n\nfunction CoordinateSystemManager() {\n\n this._coordinateSystems = [];\n}\n\nCoordinateSystemManager.prototype = {\n\n constructor: CoordinateSystemManager,\n\n create: function (ecModel, api) {\n var coordinateSystems = [];\n zrUtil.each(coordinateSystemCreators, function (creater, type) {\n var list = creater.create(ecModel, api);\n coordinateSystems = coordinateSystems.concat(list || []);\n });\n\n this._coordinateSystems = coordinateSystems;\n },\n\n update: function (ecModel, api) {\n zrUtil.each(this._coordinateSystems, function (coordSys) {\n // FIXME MUST have\n coordSys.update && coordSys.update(ecModel, api);\n });\n },\n\n getCoordinateSystems: function () {\n return this._coordinateSystems.slice();\n }\n};\n\nCoordinateSystemManager.register = function (type, coordinateSystemCreator) {\n coordinateSystemCreators[type] = coordinateSystemCreator;\n};\n\nCoordinateSystemManager.get = function (type) {\n return coordinateSystemCreators[type];\n};\n\nexport default CoordinateSystemManager;","/**\n * ECharts option manager\n *\n * @module {echarts/model/OptionManager}\n */\n\n\nimport * as zrUtil from 'zrender/src/core/util';\nimport * as modelUtil from '../util/model';\nimport ComponentModel from './Component';\n\nvar each = zrUtil.each;\nvar clone = zrUtil.clone;\nvar map = zrUtil.map;\nvar merge = zrUtil.merge;\n\nvar QUERY_REG = /^(min|max)?(.+)$/;\n\n/**\n * TERM EXPLANATIONS:\n *\n * [option]:\n *\n * An object that contains definitions of components. For example:\n * var option = {\n * title: {...},\n * legend: {...},\n * visualMap: {...},\n * series: [\n * {data: [...]},\n * {data: [...]},\n * ...\n * ]\n * };\n *\n * [rawOption]:\n *\n * An object input to echarts.setOption. 'rawOption' may be an\n * 'option', or may be an object contains multi-options. For example:\n * var option = {\n * baseOption: {\n * title: {...},\n * legend: {...},\n * series: [\n * {data: [...]},\n * {data: [...]},\n * ...\n * ]\n * },\n * timeline: {...},\n * options: [\n * {title: {...}, series: {data: [...]}},\n * {title: {...}, series: {data: [...]}},\n * ...\n * ],\n * media: [\n * {\n * query: {maxWidth: 320},\n * option: {series: {x: 20}, visualMap: {show: false}}\n * },\n * {\n * query: {minWidth: 320, maxWidth: 720},\n * option: {series: {x: 500}, visualMap: {show: true}}\n * },\n * {\n * option: {series: {x: 1200}, visualMap: {show: true}}\n * }\n * ]\n * };\n *\n * @alias module:echarts/model/OptionManager\n * @param {module:echarts/ExtensionAPI} api\n */\nfunction OptionManager(api) {\n\n /**\n * @private\n * @type {module:echarts/ExtensionAPI}\n */\n this._api = api;\n\n /**\n * @private\n * @type {Array.<number>}\n */\n this._timelineOptions = [];\n\n /**\n * @private\n * @type {Array.<Object>}\n */\n this._mediaList = [];\n\n /**\n * @private\n * @type {Object}\n */\n this._mediaDefault;\n\n /**\n * -1, means default.\n * empty means no media.\n * @private\n * @type {Array.<number>}\n */\n this._currentMediaIndices = [];\n\n /**\n * @private\n * @type {Object}\n */\n this._optionBackup;\n\n /**\n * @private\n * @type {Object}\n */\n this._newBaseOption;\n}\n\n// timeline.notMerge is not supported in ec3. Firstly there is rearly\n// case that notMerge is needed. Secondly supporting 'notMerge' requires\n// rawOption cloned and backuped when timeline changed, which does no\n// good to performance. What's more, that both timeline and setOption\n// method supply 'notMerge' brings complex and some problems.\n// Consider this case:\n// (step1) chart.setOption({timeline: {notMerge: false}, ...}, false);\n// (step2) chart.setOption({timeline: {notMerge: true}, ...}, false);\n\nOptionManager.prototype = {\n\n constructor: OptionManager,\n\n /**\n * @public\n * @param {Object} rawOption Raw option.\n * @param {module:echarts/model/Global} ecModel\n * @param {Array.<Function>} optionPreprocessorFuncs\n * @return {Object} Init option\n */\n setOption: function (rawOption, optionPreprocessorFuncs) {\n rawOption = clone(rawOption, true);\n\n // FIXME\n // 如果 timeline options 或者 media 中设置了某个属性,而baseOption中没有设置,则进行警告。\n\n var oldOptionBackup = this._optionBackup;\n var newParsedOption = parseRawOption.call(\n this, rawOption, optionPreprocessorFuncs, !oldOptionBackup\n );\n this._newBaseOption = newParsedOption.baseOption;\n\n // For setOption at second time (using merge mode);\n if (oldOptionBackup) {\n // Only baseOption can be merged.\n mergeOption(oldOptionBackup.baseOption, newParsedOption.baseOption);\n\n // For simplicity, timeline options and media options do not support merge,\n // that is, if you `setOption` twice and both has timeline options, the latter\n // timeline opitons will not be merged to the formers, but just substitude them.\n if (newParsedOption.timelineOptions.length) {\n oldOptionBackup.timelineOptions = newParsedOption.timelineOptions;\n }\n if (newParsedOption.mediaList.length) {\n oldOptionBackup.mediaList = newParsedOption.mediaList;\n }\n if (newParsedOption.mediaDefault) {\n oldOptionBackup.mediaDefault = newParsedOption.mediaDefault;\n }\n }\n else {\n this._optionBackup = newParsedOption;\n }\n },\n\n /**\n * @param {boolean} isRecreate\n * @return {Object}\n */\n mountOption: function (isRecreate) {\n var optionBackup = this._optionBackup;\n\n // TODO\n // 如果没有reset功能则不clone。\n\n this._timelineOptions = map(optionBackup.timelineOptions, clone);\n this._mediaList = map(optionBackup.mediaList, clone);\n this._mediaDefault = clone(optionBackup.mediaDefault);\n this._currentMediaIndices = [];\n\n return clone(isRecreate\n // this._optionBackup.baseOption, which is created at the first `setOption`\n // called, and is merged into every new option by inner method `mergeOption`\n // each time `setOption` called, can be only used in `isRecreate`, because\n // its reliability is under suspicion. In other cases option merge is\n // performed by `model.mergeOption`.\n ? optionBackup.baseOption : this._newBaseOption\n );\n },\n\n /**\n * @param {module:echarts/model/Global} ecModel\n * @return {Object}\n */\n getTimelineOption: function (ecModel) {\n var option;\n var timelineOptions = this._timelineOptions;\n\n if (timelineOptions.length) {\n // getTimelineOption can only be called after ecModel inited,\n // so we can get currentIndex from timelineModel.\n var timelineModel = ecModel.getComponent('timeline');\n if (timelineModel) {\n option = clone(\n timelineOptions[timelineModel.getCurrentIndex()],\n true\n );\n }\n }\n\n return option;\n },\n\n /**\n * @param {module:echarts/model/Global} ecModel\n * @return {Array.<Object>}\n */\n getMediaOption: function (ecModel) {\n var ecWidth = this._api.getWidth();\n var ecHeight = this._api.getHeight();\n var mediaList = this._mediaList;\n var mediaDefault = this._mediaDefault;\n var indices = [];\n var result = [];\n\n // No media defined.\n if (!mediaList.length && !mediaDefault) {\n return result;\n }\n\n // Multi media may be applied, the latter defined media has higher priority.\n for (var i = 0, len = mediaList.length; i < len; i++) {\n if (applyMediaQuery(mediaList[i].query, ecWidth, ecHeight)) {\n indices.push(i);\n }\n }\n\n // FIXME\n // 是否mediaDefault应该强制用户设置,否则可能修改不能回归。\n if (!indices.length && mediaDefault) {\n indices = [-1];\n }\n\n if (indices.length && !indicesEquals(indices, this._currentMediaIndices)) {\n result = map(indices, function (index) {\n return clone(\n index === -1 ? mediaDefault.option : mediaList[index].option\n );\n });\n }\n // Otherwise return nothing.\n\n this._currentMediaIndices = indices;\n\n return result;\n }\n};\n\nfunction parseRawOption(rawOption, optionPreprocessorFuncs, isNew) {\n var timelineOptions = [];\n var mediaList = [];\n var mediaDefault;\n var baseOption;\n\n // Compatible with ec2.\n var timelineOpt = rawOption.timeline;\n\n if (rawOption.baseOption) {\n baseOption = rawOption.baseOption;\n }\n\n // For timeline\n if (timelineOpt || rawOption.options) {\n baseOption = baseOption || {};\n timelineOptions = (rawOption.options || []).slice();\n }\n\n // For media query\n if (rawOption.media) {\n baseOption = baseOption || {};\n var media = rawOption.media;\n each(media, function (singleMedia) {\n if (singleMedia && singleMedia.option) {\n if (singleMedia.query) {\n mediaList.push(singleMedia);\n }\n else if (!mediaDefault) {\n // Use the first media default.\n mediaDefault = singleMedia;\n }\n }\n });\n }\n\n // For normal option\n if (!baseOption) {\n baseOption = rawOption;\n }\n\n // Set timelineOpt to baseOption in ec3,\n // which is convenient for merge option.\n if (!baseOption.timeline) {\n baseOption.timeline = timelineOpt;\n }\n\n // Preprocess.\n each([baseOption].concat(timelineOptions)\n .concat(zrUtil.map(mediaList, function (media) {\n return media.option;\n })),\n function (option) {\n each(optionPreprocessorFuncs, function (preProcess) {\n preProcess(option, isNew);\n });\n }\n );\n\n return {\n baseOption: baseOption,\n timelineOptions: timelineOptions,\n mediaDefault: mediaDefault,\n mediaList: mediaList\n };\n}\n\n/**\n * @see <http://www.w3.org/TR/css3-mediaqueries/#media1>\n * Support: width, height, aspectRatio\n * Can use max or min as prefix.\n */\nfunction applyMediaQuery(query, ecWidth, ecHeight) {\n var realMap = {\n width: ecWidth,\n height: ecHeight,\n aspectratio: ecWidth / ecHeight // lowser case for convenientce.\n };\n\n var applicatable = true;\n\n zrUtil.each(query, function (value, attr) {\n var matched = attr.match(QUERY_REG);\n\n if (!matched || !matched[1] || !matched[2]) {\n return;\n }\n\n var operator = matched[1];\n var realAttr = matched[2].toLowerCase();\n\n if (!compare(realMap[realAttr], value, operator)) {\n applicatable = false;\n }\n });\n\n return applicatable;\n}\n\nfunction compare(real, expect, operator) {\n if (operator === 'min') {\n return real >= expect;\n }\n else if (operator === 'max') {\n return real <= expect;\n }\n else { // Equals\n return real === expect;\n }\n}\n\nfunction indicesEquals(indices1, indices2) {\n // indices is always order by asc and has only finite number.\n return indices1.join(',') === indices2.join(',');\n}\n\n/**\n * Consider case:\n * `chart.setOption(opt1);`\n * Then user do some interaction like dataZoom, dataView changing.\n * `chart.setOption(opt2);`\n * Then user press 'reset button' in toolbox.\n *\n * After doing that all of the interaction effects should be reset, the\n * chart should be the same as the result of invoke\n * `chart.setOption(opt1); chart.setOption(opt2);`.\n *\n * Although it is not able ensure that\n * `chart.setOption(opt1); chart.setOption(opt2);` is equivalents to\n * `chart.setOption(merge(opt1, opt2));` exactly,\n * this might be the only simple way to implement that feature.\n *\n * MEMO: We've considered some other approaches:\n * 1. Each model handle its self restoration but not uniform treatment.\n * (Too complex in logic and error-prone)\n * 2. Use a shadow ecModel. (Performace expensive)\n */\nfunction mergeOption(oldOption, newOption) {\n newOption = newOption || {};\n\n each(newOption, function (newCptOpt, mainType) {\n if (newCptOpt == null) {\n return;\n }\n\n var oldCptOpt = oldOption[mainType];\n\n if (!ComponentModel.hasClass(mainType)) {\n oldOption[mainType] = merge(oldCptOpt, newCptOpt, true);\n }\n else {\n newCptOpt = modelUtil.normalizeToArray(newCptOpt);\n oldCptOpt = modelUtil.normalizeToArray(oldCptOpt);\n\n var mapResult = modelUtil.mappingToExists(oldCptOpt, newCptOpt);\n\n oldOption[mainType] = map(mapResult, function (item) {\n return (item.option && item.exist)\n ? merge(item.exist, item.option, true)\n : (item.exist || item.option);\n });\n }\n });\n}\n\nexport default OptionManager;","import * as zrUtil from 'zrender/src/core/util';\nimport * as modelUtil from '../../util/model';\n\nvar each = zrUtil.each;\nvar isObject = zrUtil.isObject;\n\nvar POSSIBLE_STYLES = [\n 'areaStyle', 'lineStyle', 'nodeStyle', 'linkStyle',\n 'chordStyle', 'label', 'labelLine'\n];\n\nfunction compatItemStyle(opt) {\n var itemStyleOpt = opt && opt.itemStyle;\n if (!itemStyleOpt) {\n return;\n }\n for (var i = 0, len = POSSIBLE_STYLES.length; i < len; i++) {\n var styleName = POSSIBLE_STYLES[i];\n var normalItemStyleOpt = itemStyleOpt.normal;\n var emphasisItemStyleOpt = itemStyleOpt.emphasis;\n if (normalItemStyleOpt && normalItemStyleOpt[styleName]) {\n opt[styleName] = opt[styleName] || {};\n if (!opt[styleName].normal) {\n opt[styleName].normal = normalItemStyleOpt[styleName];\n }\n else {\n zrUtil.merge(opt[styleName].normal, normalItemStyleOpt[styleName]);\n }\n normalItemStyleOpt[styleName] = null;\n }\n if (emphasisItemStyleOpt && emphasisItemStyleOpt[styleName]) {\n opt[styleName] = opt[styleName] || {};\n if (!opt[styleName].emphasis) {\n opt[styleName].emphasis = emphasisItemStyleOpt[styleName];\n }\n else {\n zrUtil.merge(opt[styleName].emphasis, emphasisItemStyleOpt[styleName]);\n }\n emphasisItemStyleOpt[styleName] = null;\n }\n }\n}\n\nfunction compatTextStyle(opt, propName) {\n var labelOptSingle = isObject(opt) && opt[propName];\n var textStyle = isObject(labelOptSingle) && labelOptSingle.textStyle;\n if (textStyle) {\n for (var i = 0, len = modelUtil.TEXT_STYLE_OPTIONS.length; i < len; i++) {\n var propName = modelUtil.TEXT_STYLE_OPTIONS[i];\n if (textStyle.hasOwnProperty(propName)) {\n labelOptSingle[propName] = textStyle[propName];\n }\n }\n }\n}\n\nfunction compatLabelTextStyle(labelOpt) {\n if (isObject(labelOpt)) {\n compatTextStyle(labelOpt, 'normal');\n compatTextStyle(labelOpt, 'emphasis');\n }\n}\n\nfunction processSeries(seriesOpt) {\n if (!isObject(seriesOpt)) {\n return;\n }\n\n compatItemStyle(seriesOpt);\n compatLabelTextStyle(seriesOpt.label);\n // treemap\n compatLabelTextStyle(seriesOpt.upperLabel);\n // graph\n compatLabelTextStyle(seriesOpt.edgeLabel);\n\n var markPoint = seriesOpt.markPoint;\n compatItemStyle(markPoint);\n compatLabelTextStyle(markPoint && markPoint.label);\n\n var markLine = seriesOpt.markLine;\n compatItemStyle(seriesOpt.markLine);\n compatLabelTextStyle(markLine && markLine.label);\n\n var markArea = seriesOpt.markArea;\n compatLabelTextStyle(markArea && markArea.label);\n\n // For gauge\n compatTextStyle(seriesOpt, 'axisLabel');\n compatTextStyle(seriesOpt, 'title');\n compatTextStyle(seriesOpt, 'detail');\n\n var data = seriesOpt.data;\n if (data) {\n for (var i = 0; i < data.length; i++) {\n compatItemStyle(data[i]);\n compatLabelTextStyle(data[i] && data[i].label);\n }\n }\n\n // mark point data\n var markPoint = seriesOpt.markPoint;\n if (markPoint && markPoint.data) {\n var mpData = markPoint.data;\n for (var i = 0; i < mpData.length; i++) {\n compatItemStyle(mpData[i]);\n compatLabelTextStyle(mpData[i] && mpData[i].label);\n }\n }\n // mark line data\n var markLine = seriesOpt.markLine;\n if (markLine && markLine.data) {\n var mlData = markLine.data;\n for (var i = 0; i < mlData.length; i++) {\n if (zrUtil.isArray(mlData[i])) {\n compatItemStyle(mlData[i][0]);\n compatLabelTextStyle(mlData[i][0] && mlData[i][0].label);\n compatItemStyle(mlData[i][1]);\n compatLabelTextStyle(mlData[i][1] && mlData[i][1].label);\n }\n else {\n compatItemStyle(mlData[i]);\n compatLabelTextStyle(mlData[i] && mlData[i].label);\n }\n }\n }\n}\n\nfunction toArr(o) {\n return zrUtil.isArray(o) ? o : o ? [o] : [];\n}\n\nfunction toObj(o) {\n return (zrUtil.isArray(o) ? o[0] : o) || {};\n}\n\nexport default function (option, isTheme) {\n each(toArr(option.series), function (seriesOpt) {\n isObject(seriesOpt) && processSeries(seriesOpt);\n });\n\n var axes = ['xAxis', 'yAxis', 'radiusAxis', 'angleAxis', 'singleAxis', 'parallelAxis', 'radar'];\n isTheme && axes.push('valueAxis', 'categoryAxis', 'logAxis', 'timeAxis');\n\n each(\n axes,\n function (axisName) {\n each(toArr(option[axisName]), function (axisOpt) {\n if (axisOpt) {\n compatTextStyle(axisOpt, 'axisLabel');\n compatTextStyle(axisOpt.axisPointer, 'label');\n }\n });\n }\n );\n\n each(toArr(option.parallel), function (parallelOpt) {\n var parallelAxisDefault = parallelOpt && parallelOpt.parallelAxisDefault;\n compatTextStyle(parallelAxisDefault, 'axisLabel');\n compatTextStyle(parallelAxisDefault && parallelAxisDefault.axisPointer, 'label');\n });\n\n each(toArr(option.calendar), function (calendarOpt) {\n compatTextStyle(calendarOpt, 'dayLabel');\n compatTextStyle(calendarOpt, 'monthLabel');\n compatTextStyle(calendarOpt, 'yearLabel');\n });\n\n // radar.name.textStyle\n each(toArr(option.radar), function (radarOpt) {\n compatTextStyle(radarOpt, 'name');\n });\n\n each(toArr(option.geo), function (geoOpt) {\n if (isObject(geoOpt)) {\n compatLabelTextStyle(geoOpt.label);\n each(toArr(geoOpt.regions), function (regionObj) {\n compatLabelTextStyle(regionObj.label);\n });\n }\n });\n\n compatLabelTextStyle(toObj(option.timeline).label);\n compatTextStyle(toObj(option.axisPointer), 'label');\n compatTextStyle(toObj(option.tooltip).axisPointer, 'label');\n}","// Compatitable with 2.0\n\nimport {each, isArray, isObject} from 'zrender/src/core/util';\nimport compatStyle from './helper/compatStyle';\nimport {normalizeToArray} from '../util/model';\n\nfunction get(opt, path) {\n path = path.split(',');\n var obj = opt;\n for (var i = 0; i < path.length; i++) {\n obj = obj && obj[path[i]];\n if (obj == null) {\n break;\n }\n }\n return obj;\n}\n\nfunction set(opt, path, val, overwrite) {\n path = path.split(',');\n var obj = opt;\n var key;\n for (var i = 0; i < path.length - 1; i++) {\n key = path[i];\n if (obj[key] == null) {\n obj[key] = {};\n }\n obj = obj[key];\n }\n if (overwrite || obj[path[i]] == null) {\n obj[path[i]] = val;\n }\n}\n\nfunction compatLayoutProperties(option) {\n each(LAYOUT_PROPERTIES, function (prop) {\n if (prop[0] in option && !(prop[1] in option)) {\n option[prop[1]] = option[prop[0]];\n }\n });\n}\n\nvar LAYOUT_PROPERTIES = [\n ['x', 'left'], ['y', 'top'], ['x2', 'right'], ['y2', 'bottom']\n];\n\nvar COMPATITABLE_COMPONENTS = [\n 'grid', 'geo', 'parallel', 'legend', 'toolbox', 'title', 'visualMap', 'dataZoom', 'timeline'\n];\n\nvar COMPATITABLE_SERIES = [\n 'bar', 'boxplot', 'candlestick', 'chord', 'effectScatter',\n 'funnel', 'gauge', 'lines', 'graph', 'heatmap', 'line', 'map', 'parallel',\n 'pie', 'radar', 'sankey', 'scatter', 'treemap'\n];\n\nexport default function (option, isTheme) {\n compatStyle(option, isTheme);\n\n // Make sure series array for model initialization.\n option.series = normalizeToArray(option.series);\n\n each(option.series, function (seriesOpt) {\n if (!isObject(seriesOpt)) {\n return;\n }\n\n var seriesType = seriesOpt.type;\n\n if (seriesType === 'pie' || seriesType === 'gauge') {\n if (seriesOpt.clockWise != null) {\n seriesOpt.clockwise = seriesOpt.clockWise;\n }\n }\n if (seriesType === 'gauge') {\n var pointerColor = get(seriesOpt, 'pointer.color');\n pointerColor != null\n && set(seriesOpt, 'itemStyle.normal.color', pointerColor);\n }\n\n for (var i = 0; i < COMPATITABLE_SERIES.length; i++) {\n if (COMPATITABLE_SERIES[i] === seriesOpt.type) {\n compatLayoutProperties(seriesOpt);\n break;\n }\n }\n });\n\n // dataRange has changed to visualMap\n if (option.dataRange) {\n option.visualMap = option.dataRange;\n }\n\n each(COMPATITABLE_COMPONENTS, function (componentName) {\n var options = option[componentName];\n if (options) {\n if (!isArray(options)) {\n options = [options];\n }\n each(options, function (option) {\n compatLayoutProperties(option);\n });\n }\n });\n}","import {__DEV__} from '../config';\nimport * as zrUtil from 'zrender/src/core/util';\nimport env from 'zrender/src/core/env';\nimport {\n formatTime,\n encodeHTML,\n addCommas,\n getTooltipMarker\n} from '../util/format';\nimport {set, get} from '../util/clazz';\nimport * as modelUtil from '../util/model';\nimport ComponentModel from './Component';\nimport colorPaletteMixin from './mixin/colorPalette';\nimport {\n getLayoutParams,\n mergeLayoutParam\n} from '../util/layout';\n\nvar SeriesModel = ComponentModel.extend({\n\n type: 'series.__base__',\n\n /**\n * @readOnly\n */\n seriesIndex: 0,\n\n // coodinateSystem will be injected in the echarts/CoordinateSystem\n coordinateSystem: null,\n\n /**\n * @type {Object}\n * @protected\n */\n defaultOption: null,\n\n /**\n * Data provided for legend\n * @type {Function}\n */\n // PENDING\n legendDataProvider: null,\n\n /**\n * Access path of color for visual\n */\n visualColorAccessPath: 'itemStyle.normal.color',\n\n /**\n * Support merge layout params.\n * Only support 'box' now (left/right/top/bottom/width/height).\n * @type {string|Object} Object can be {ignoreSize: true}\n * @readOnly\n */\n layoutMode: null,\n\n init: function (option, parentModel, ecModel, extraOpt) {\n\n /**\n * @type {number}\n * @readOnly\n */\n this.seriesIndex = this.componentIndex;\n\n this.mergeDefaultAndTheme(option, ecModel);\n\n var data = this.getInitialData(option, ecModel);\n if (__DEV__) {\n zrUtil.assert(data, 'getInitialData returned invalid data.');\n }\n /**\n * @type {module:echarts/data/List|module:echarts/data/Tree|module:echarts/data/Graph}\n * @private\n */\n set(this, 'dataBeforeProcessed', data);\n\n // If we reverse the order (make data firstly, and then make\n // dataBeforeProcessed by cloneShallow), cloneShallow will\n // cause data.graph.data !== data when using\n // module:echarts/data/Graph or module:echarts/data/Tree.\n // See module:echarts/data/helper/linkList\n this.restoreData();\n },\n\n /**\n * Util for merge default and theme to option\n * @param {Object} option\n * @param {module:echarts/model/Global} ecModel\n */\n mergeDefaultAndTheme: function (option, ecModel) {\n var layoutMode = this.layoutMode;\n var inputPositionParams = layoutMode\n ? getLayoutParams(option) : {};\n\n // Backward compat: using subType on theme.\n // But if name duplicate between series subType\n // (for example: parallel) add component mainType,\n // add suffix 'Series'.\n var themeSubType = this.subType;\n if (ComponentModel.hasClass(themeSubType)) {\n themeSubType += 'Series';\n }\n zrUtil.merge(\n option,\n ecModel.getTheme().get(this.subType)\n );\n zrUtil.merge(option, this.getDefaultOption());\n\n // Default label emphasis `show`\n modelUtil.defaultEmphasis(option.label, ['show']);\n\n this.fillDataTextStyle(option.data);\n\n if (layoutMode) {\n mergeLayoutParam(option, inputPositionParams, layoutMode);\n }\n },\n\n mergeOption: function (newSeriesOption, ecModel) {\n newSeriesOption = zrUtil.merge(this.option, newSeriesOption, true);\n this.fillDataTextStyle(newSeriesOption.data);\n\n var layoutMode = this.layoutMode;\n if (layoutMode) {\n mergeLayoutParam(this.option, newSeriesOption, layoutMode);\n }\n\n var data = this.getInitialData(newSeriesOption, ecModel);\n // TODO Merge data?\n if (data) {\n set(this, 'data', data);\n set(this, 'dataBeforeProcessed', data.cloneShallow());\n }\n },\n\n fillDataTextStyle: function (data) {\n // Default data label emphasis `show`\n // FIXME Tree structure data ?\n // FIXME Performance ?\n if (data) {\n var props = ['show'];\n for (var i = 0; i < data.length; i++) {\n if (data[i] && data[i].label) {\n modelUtil.defaultEmphasis(data[i].label, props);\n }\n }\n }\n },\n\n /**\n * Init a data structure from data related option in series\n * Must be overwritten\n */\n getInitialData: function () {},\n\n /**\n * @param {string} [dataType]\n * @return {module:echarts/data/List}\n */\n getData: function (dataType) {\n var data = get(this, 'data');\n return dataType == null ? data : data.getLinkedData(dataType);\n },\n\n /**\n * @param {module:echarts/data/List} data\n */\n setData: function (data) {\n set(this, 'data', data);\n },\n\n /**\n * Get data before processed\n * @return {module:echarts/data/List}\n */\n getRawData: function () {\n return get(this, 'dataBeforeProcessed');\n },\n\n /**\n * Coord dimension to data dimension.\n *\n * By default the result is the same as dimensions of series data.\n * But in some series data dimensions are different from coord dimensions (i.e.\n * candlestick and boxplot). Override this method to handle those cases.\n *\n * Coord dimension to data dimension can be one-to-many\n *\n * @param {string} coordDim\n * @return {Array.<string>} dimensions on the axis.\n */\n coordDimToDataDim: function (coordDim) {\n return modelUtil.coordDimToDataDim(this.getData(), coordDim);\n },\n\n /**\n * Convert data dimension to coord dimension.\n *\n * @param {string|number} dataDim\n * @return {string}\n */\n dataDimToCoordDim: function (dataDim) {\n return modelUtil.dataDimToCoordDim(this.getData(), dataDim);\n },\n\n /**\n * Get base axis if has coordinate system and has axis.\n * By default use coordSys.getBaseAxis();\n * Can be overrided for some chart.\n * @return {type} description\n */\n getBaseAxis: function () {\n var coordSys = this.coordinateSystem;\n return coordSys && coordSys.getBaseAxis && coordSys.getBaseAxis();\n },\n\n // FIXME\n /**\n * Default tooltip formatter\n *\n * @param {number} dataIndex\n * @param {boolean} [multipleSeries=false]\n * @param {number} [dataType]\n */\n formatTooltip: function (dataIndex, multipleSeries, dataType) {\n function formatArrayValue(value) {\n var vertially = zrUtil.reduce(value, function (vertially, val, idx) {\n var dimItem = data.getDimensionInfo(idx);\n return vertially |= dimItem && dimItem.tooltip !== false && dimItem.tooltipName != null;\n }, 0);\n\n var result = [];\n var tooltipDims = modelUtil.otherDimToDataDim(data, 'tooltip');\n\n tooltipDims.length\n ? zrUtil.each(tooltipDims, function (dimIdx) {\n setEachItem(data.get(dimIdx, dataIndex), dimIdx);\n })\n // By default, all dims is used on tooltip.\n : zrUtil.each(value, setEachItem);\n\n function setEachItem(val, dimIdx) {\n var dimInfo = data.getDimensionInfo(dimIdx);\n // If `dimInfo.tooltip` is not set, show tooltip.\n if (!dimInfo || dimInfo.otherDims.tooltip === false) {\n return;\n }\n var dimType = dimInfo.type;\n var valStr = (vertially ? '- ' + (dimInfo.tooltipName || dimInfo.name) + ': ' : '')\n + (dimType === 'ordinal'\n ? val + ''\n : dimType === 'time'\n ? (multipleSeries ? '' : formatTime('yyyy/MM/dd hh:mm:ss', val))\n : addCommas(val)\n );\n valStr && result.push(encodeHTML(valStr));\n }\n\n return (vertially ? '<br/>' : '') + result.join(vertially ? '<br/>' : ', ');\n }\n\n var data = get(this, 'data');\n\n var value = this.getRawValue(dataIndex);\n var formattedValue = zrUtil.isArray(value)\n ? formatArrayValue(value) : encodeHTML(addCommas(value));\n var name = data.getName(dataIndex);\n\n var color = data.getItemVisual(dataIndex, 'color');\n if (zrUtil.isObject(color) && color.colorStops) {\n color = (color.colorStops[0] || {}).color;\n }\n color = color || 'transparent';\n\n var colorEl = getTooltipMarker(color);\n\n var seriesName = this.name;\n // FIXME\n if (seriesName === '\\0-') {\n // Not show '-'\n seriesName = '';\n }\n seriesName = seriesName\n ? encodeHTML(seriesName) + (!multipleSeries ? '<br/>' : ': ')\n : '';\n return !multipleSeries\n ? seriesName + colorEl\n + (name\n ? encodeHTML(name) + ': ' + formattedValue\n : formattedValue\n )\n : colorEl + seriesName + formattedValue;\n },\n\n /**\n * @return {boolean}\n */\n isAnimationEnabled: function () {\n if (env.node) {\n return false;\n }\n\n var animationEnabled = this.getShallow('animation');\n if (animationEnabled) {\n if (this.getData().count() > this.getShallow('animationThreshold')) {\n animationEnabled = false;\n }\n }\n return animationEnabled;\n },\n\n restoreData: function () {\n set(this, 'data', get(this, 'dataBeforeProcessed').cloneShallow());\n },\n\n getColorFromPalette: function (name, scope) {\n var ecModel = this.ecModel;\n // PENDING\n var color = colorPaletteMixin.getColorFromPalette.call(this, name, scope);\n if (!color) {\n color = ecModel.getColorFromPalette(name, scope);\n }\n return color;\n },\n\n /**\n * Get data indices for show tooltip content. See tooltip.\n * @abstract\n * @param {Array.<string>|string} dim\n * @param {Array.<number>} value\n * @param {module:echarts/coord/single/SingleAxis} baseAxis\n * @return {Object} {dataIndices, nestestValue}.\n */\n getAxisTooltipData: null,\n\n /**\n * See tooltip.\n * @abstract\n * @param {number} dataIndex\n * @return {Array.<number>} Point of tooltip. null/undefined can be returned.\n */\n getTooltipPosition: null\n});\n\nzrUtil.mixin(SeriesModel, modelUtil.dataFormatMixin);\nzrUtil.mixin(SeriesModel, colorPaletteMixin);\n\nexport default SeriesModel;","import Group from 'zrender/src/container/Group';\nimport * as componentUtil from '../util/component';\nimport * as clazzUtil from '../util/clazz';\n\nvar Component = function () {\n /**\n * @type {module:zrender/container/Group}\n * @readOnly\n */\n this.group = new Group();\n\n /**\n * @type {string}\n * @readOnly\n */\n this.uid = componentUtil.getUID('viewComponent');\n};\n\nComponent.prototype = {\n\n constructor: Component,\n\n init: function (ecModel, api) {},\n\n render: function (componentModel, ecModel, api, payload) {},\n\n dispose: function () {}\n\n};\n\nvar componentProto = Component.prototype;\ncomponentProto.updateView\n = componentProto.updateLayout\n = componentProto.updateVisual\n = function (seriesModel, ecModel, api, payload) {\n // Do nothing;\n };\n// Enable Component.extend.\nclazzUtil.enableClassExtend(Component);\n\n// Enable capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on.\nclazzUtil.enableClassManagement(Component, {registerWhenExtend: true});\n\nexport default Component;","import * as zrUtil from 'zrender/src/core/util';\nimport Group from 'zrender/src/container/Group';\nimport * as componentUtil from '../util/component';\nimport * as clazzUtil from '../util/clazz';\nimport * as modelUtil from '../util/model';\n\nfunction Chart() {\n\n /**\n * @type {module:zrender/container/Group}\n * @readOnly\n */\n this.group = new Group();\n\n /**\n * @type {string}\n * @readOnly\n */\n this.uid = componentUtil.getUID('viewChart');\n}\n\nChart.prototype = {\n\n type: 'chart',\n\n /**\n * Init the chart\n * @param {module:echarts/model/Global} ecModel\n * @param {module:echarts/ExtensionAPI} api\n */\n init: function (ecModel, api) {},\n\n /**\n * Render the chart\n * @param {module:echarts/model/Series} seriesModel\n * @param {module:echarts/model/Global} ecModel\n * @param {module:echarts/ExtensionAPI} api\n * @param {Object} payload\n */\n render: function (seriesModel, ecModel, api, payload) {},\n\n /**\n * Highlight series or specified data item\n * @param {module:echarts/model/Series} seriesModel\n * @param {module:echarts/model/Global} ecModel\n * @param {module:echarts/ExtensionAPI} api\n * @param {Object} payload\n */\n highlight: function (seriesModel, ecModel, api, payload) {\n toggleHighlight(seriesModel.getData(), payload, 'emphasis');\n },\n\n /**\n * Downplay series or specified data item\n * @param {module:echarts/model/Series} seriesModel\n * @param {module:echarts/model/Global} ecModel\n * @param {module:echarts/ExtensionAPI} api\n * @param {Object} payload\n */\n downplay: function (seriesModel, ecModel, api, payload) {\n toggleHighlight(seriesModel.getData(), payload, 'normal');\n },\n\n /**\n * Remove self\n * @param {module:echarts/model/Global} ecModel\n * @param {module:echarts/ExtensionAPI} api\n */\n remove: function (ecModel, api) {\n this.group.removeAll();\n },\n\n /**\n * Dispose self\n * @param {module:echarts/model/Global} ecModel\n * @param {module:echarts/ExtensionAPI} api\n */\n dispose: function () {}\n\n /**\n * The view contains the given point.\n * @interface\n * @param {Array.<number>} point\n * @return {boolean}\n */\n // containPoint: function () {}\n\n};\n\nvar chartProto = Chart.prototype;\nchartProto.updateView\n = chartProto.updateLayout\n = chartProto.updateVisual\n = function (seriesModel, ecModel, api, payload) {\n this.render(seriesModel, ecModel, api, payload);\n };\n\n/**\n * Set state of single element\n * @param {module:zrender/Element} el\n * @param {string} state\n */\nfunction elSetState(el, state) {\n if (el) {\n el.trigger(state);\n if (el.type === 'group') {\n for (var i = 0; i < el.childCount(); i++) {\n elSetState(el.childAt(i), state);\n }\n }\n }\n}\n/**\n * @param {module:echarts/data/List} data\n * @param {Object} payload\n * @param {string} state 'normal'|'emphasis'\n * @inner\n */\nfunction toggleHighlight(data, payload, state) {\n var dataIndex = modelUtil.queryDataIndex(data, payload);\n\n if (dataIndex != null) {\n zrUtil.each(modelUtil.normalizeToArray(dataIndex), function (dataIdx) {\n elSetState(data.getItemGraphicEl(dataIdx), state);\n });\n }\n else {\n data.eachItemGraphicEl(function (el) {\n elSetState(el, state);\n });\n }\n}\n\n// Enable Chart.extend.\nclazzUtil.enableClassExtend(Chart, ['dispose']);\n\n// Add capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on.\nclazzUtil.enableClassManagement(Chart, {registerWhenExtend: true});\n\nexport default Chart;","\nvar ORIGIN_METHOD = '\\0__throttleOriginMethod';\nvar RATE = '\\0__throttleRate';\nvar THROTTLE_TYPE = '\\0__throttleType';\n\n/**\n * @public\n * @param {(Function)} fn\n * @param {number} [delay=0] Unit: ms.\n * @param {boolean} [debounce=false]\n * true: If call interval less than `delay`, only the last call works.\n * false: If call interval less than `delay, call works on fixed rate.\n * @return {(Function)} throttled fn.\n */\nexport function throttle(fn, delay, debounce) {\n\n var currCall;\n var lastCall = 0;\n var lastExec = 0;\n var timer = null;\n var diff;\n var scope;\n var args;\n var debounceNextCall;\n\n delay = delay || 0;\n\n function exec() {\n lastExec = (new Date()).getTime();\n timer = null;\n fn.apply(scope, args || []);\n }\n\n var cb = function () {\n currCall = (new Date()).getTime();\n scope = this;\n args = arguments;\n var thisDelay = debounceNextCall || delay;\n var thisDebounce = debounceNextCall || debounce;\n debounceNextCall = null;\n diff = currCall - (thisDebounce ? lastCall : lastExec) - thisDelay;\n\n clearTimeout(timer);\n\n if (thisDebounce) {\n timer = setTimeout(exec, thisDelay);\n }\n else {\n if (diff >= 0) {\n exec();\n }\n else {\n timer = setTimeout(exec, -diff);\n }\n }\n\n lastCall = currCall;\n };\n\n /**\n * Clear throttle.\n * @public\n */\n cb.clear = function () {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n };\n\n /**\n * Enable debounce once.\n */\n cb.debounceNextCall = function (debounceDelay) {\n debounceNextCall = debounceDelay;\n };\n\n return cb;\n}\n\n/**\n * Create throttle method or update throttle rate.\n *\n * @example\n * ComponentView.prototype.render = function () {\n * ...\n * throttle.createOrUpdate(\n * this,\n * '_dispatchAction',\n * this.model.get('throttle'),\n * 'fixRate'\n * );\n * };\n * ComponentView.prototype.remove = function () {\n * throttle.clear(this, '_dispatchAction');\n * };\n * ComponentView.prototype.dispose = function () {\n * throttle.clear(this, '_dispatchAction');\n * };\n *\n * @public\n * @param {Object} obj\n * @param {string} fnAttr\n * @param {number} [rate]\n * @param {string} [throttleType='fixRate'] 'fixRate' or 'debounce'\n * @return {Function} throttled function.\n */\nexport function createOrUpdate(obj, fnAttr, rate, throttleType) {\n var fn = obj[fnAttr];\n\n if (!fn) {\n return;\n }\n\n var originFn = fn[ORIGIN_METHOD] || fn;\n var lastThrottleType = fn[THROTTLE_TYPE];\n var lastRate = fn[RATE];\n\n if (lastRate !== rate || lastThrottleType !== throttleType) {\n if (rate == null || !throttleType) {\n return (obj[fnAttr] = originFn);\n }\n\n fn = obj[fnAttr] = throttle(\n originFn, rate, throttleType === 'debounce'\n );\n fn[ORIGIN_METHOD] = originFn;\n fn[THROTTLE_TYPE] = throttleType;\n fn[RATE] = rate;\n }\n\n return fn;\n}\n\n/**\n * Clear throttle. Example see throttle.createOrUpdate.\n *\n * @public\n * @param {Object} obj\n * @param {string} fnAttr\n */\nexport function clear(obj, fnAttr) {\n var fn = obj[fnAttr];\n if (fn && fn[ORIGIN_METHOD]) {\n obj[fnAttr] = fn[ORIGIN_METHOD];\n }\n}\n","import Gradient from 'zrender/src/graphic/Gradient';\n\nexport default function (ecModel) {\n function encodeColor(seriesModel) {\n var colorAccessPath = (seriesModel.visualColorAccessPath || 'itemStyle.normal.color').split('.');\n var data = seriesModel.getData();\n var color = seriesModel.get(colorAccessPath) // Set in itemStyle\n || seriesModel.getColorFromPalette(seriesModel.get('name')); // Default color\n\n // FIXME Set color function or use the platte color\n data.setVisual('color', color);\n\n // Only visible series has each data be visual encoded\n if (!ecModel.isSeriesFiltered(seriesModel)) {\n if (typeof color === 'function' && !(color instanceof Gradient)) {\n data.each(function (idx) {\n data.setItemVisual(\n idx, 'color', color(seriesModel.getDataParams(idx))\n );\n });\n }\n\n // itemStyle in each data item\n data.each(function (idx) {\n var itemModel = data.getItemModel(idx);\n var color = itemModel.get(colorAccessPath, true);\n if (color != null) {\n data.setItemVisual(idx, 'color', color);\n }\n });\n }\n }\n ecModel.eachRawSeries(encodeColor);\n}","import * as zrUtil from 'zrender/src/core/util';\nimport * as graphic from '../util/graphic';\n\nvar PI = Math.PI;\n\n/**\n * @param {module:echarts/ExtensionAPI} api\n * @param {Object} [opts]\n * @param {string} [opts.text]\n * @param {string} [opts.color]\n * @param {string} [opts.textColor]\n * @return {module:zrender/Element}\n */\nexport default function (api, opts) {\n opts = opts || {};\n zrUtil.defaults(opts, {\n text: 'loading',\n color: '#c23531',\n textColor: '#000',\n maskColor: 'rgba(255, 255, 255, 0.8)',\n zlevel: 0\n });\n var mask = new graphic.Rect({\n style: {\n fill: opts.maskColor\n },\n zlevel: opts.zlevel,\n z: 10000\n });\n var arc = new graphic.Arc({\n shape: {\n startAngle: -PI / 2,\n endAngle: -PI / 2 + 0.1,\n r: 10\n },\n style: {\n stroke: opts.color,\n lineCap: 'round',\n lineWidth: 5\n },\n zlevel: opts.zlevel,\n z: 10001\n });\n var labelRect = new graphic.Rect({\n style: {\n fill: 'none',\n text: opts.text,\n textPosition: 'right',\n textDistance: 10,\n textFill: opts.textColor\n },\n zlevel: opts.zlevel,\n z: 10001\n });\n\n arc.animateShape(true)\n .when(1000, {\n endAngle: PI * 3 / 2\n })\n .start('circularInOut');\n arc.animateShape(true)\n .when(1000, {\n startAngle: PI * 3 / 2\n })\n .delay(300)\n .start('circularInOut');\n\n var group = new graphic.Group();\n group.add(arc);\n group.add(labelRect);\n group.add(mask);\n // Inject resize\n group.resize = function () {\n var cx = api.getWidth() / 2;\n var cy = api.getHeight() / 2;\n arc.setShape({\n cx: cx,\n cy: cy\n });\n var r = arc.shape.r;\n labelRect.setShape({\n x: cx - r,\n y: cy - r,\n width: r * 2,\n height: r * 2\n });\n\n mask.setShape({\n x: 0,\n y: 0,\n width: api.getWidth(),\n height: api.getHeight()\n });\n };\n group.resize();\n return group;\n}","\n/*!\n * ECharts, a javascript interactive chart library.\n *\n * Copyright (c) 2015, Baidu Inc.\n * All rights reserved.\n *\n * LICENSE\n * https://github.com/ecomfe/echarts/blob/master/LICENSE.txt\n */\n\nimport {__DEV__} from './config';\nimport * as zrender from 'zrender/src/zrender';\nimport * as zrUtil from 'zrender/src/core/util';\nimport * as colorTool from 'zrender/src/tool/color';\nimport env from 'zrender/src/core/env';\nimport timsort from 'zrender/src/core/timsort';\nimport Eventful from 'zrender/src/mixin/Eventful';\nimport GlobalModel from './model/Global';\nimport ExtensionAPI from './ExtensionAPI';\nimport CoordinateSystemManager from './CoordinateSystem';\nimport OptionManager from './model/OptionManager';\nimport backwardCompat from './preprocessor/backwardCompat';\nimport ComponentModel from './model/Component';\nimport SeriesModel from './model/Series';\nimport ComponentView from './view/Component';\nimport ChartView from './view/Chart';\nimport * as graphic from './util/graphic';\nimport * as modelUtil from './util/model';\nimport {throttle} from './util/throttle';\nimport seriesColor from './visual/seriesColor';\nimport loadingDefault from './loading/default';\n\nvar each = zrUtil.each;\nvar parseClassType = ComponentModel.parseClassType;\n\nexport var version = '3.8.4';\n\nexport var dependencies = {\n zrender: '3.7.3'\n};\n\nvar PRIORITY_PROCESSOR_FILTER = 1000;\nvar PRIORITY_PROCESSOR_STATISTIC = 5000;\n\nvar PRIORITY_VISUAL_LAYOUT = 1000;\nvar PRIORITY_VISUAL_GLOBAL = 2000;\nvar PRIORITY_VISUAL_CHART = 3000;\nvar PRIORITY_VISUAL_COMPONENT = 4000;\n// FIXME\n// necessary?\nvar PRIORITY_VISUAL_BRUSH = 5000;\n\nexport var PRIORITY = {\n PROCESSOR: {\n FILTER: PRIORITY_PROCESSOR_FILTER,\n STATISTIC: PRIORITY_PROCESSOR_STATISTIC\n },\n VISUAL: {\n LAYOUT: PRIORITY_VISUAL_LAYOUT,\n GLOBAL: PRIORITY_VISUAL_GLOBAL,\n CHART: PRIORITY_VISUAL_CHART,\n COMPONENT: PRIORITY_VISUAL_COMPONENT,\n BRUSH: PRIORITY_VISUAL_BRUSH\n }\n};\n\n// Main process have three entries: `setOption`, `dispatchAction` and `resize`,\n// where they must not be invoked nestedly, except the only case: invoke\n// dispatchAction with updateMethod \"none\" in main process.\n// This flag is used to carry out this rule.\n// All events will be triggered out side main process (i.e. when !this[IN_MAIN_PROCESS]).\nvar IN_MAIN_PROCESS = '__flagInMainProcess';\nvar HAS_GRADIENT_OR_PATTERN_BG = '__hasGradientOrPatternBg';\nvar OPTION_UPDATED = '__optionUpdated';\nvar ACTION_REG = /^[a-zA-Z0-9_]+$/;\n\n\nfunction createRegisterEventWithLowercaseName(method) {\n return function (eventName, handler, context) {\n // Event name is all lowercase\n eventName = eventName && eventName.toLowerCase();\n Eventful.prototype[method].call(this, eventName, handler, context);\n };\n}\n\n/**\n * @module echarts~MessageCenter\n */\nfunction MessageCenter() {\n Eventful.call(this);\n}\nMessageCenter.prototype.on = createRegisterEventWithLowercaseName('on');\nMessageCenter.prototype.off = createRegisterEventWithLowercaseName('off');\nMessageCenter.prototype.one = createRegisterEventWithLowercaseName('one');\nzrUtil.mixin(MessageCenter, Eventful);\n\n/**\n * @module echarts~ECharts\n */\nfunction ECharts(dom, theme, opts) {\n opts = opts || {};\n\n // Get theme by name\n if (typeof theme === 'string') {\n theme = themeStorage[theme];\n }\n\n /**\n * @type {string}\n */\n this.id;\n\n /**\n * Group id\n * @type {string}\n */\n this.group;\n\n /**\n * @type {HTMLElement}\n * @private\n */\n this._dom = dom;\n\n var defaultRenderer = 'canvas';\n if (__DEV__) {\n defaultRenderer = (\n typeof window === 'undefined' ? global : window\n ).__ECHARTS__DEFAULT__RENDERER__ || defaultRenderer;\n }\n\n /**\n * @type {module:zrender/ZRender}\n * @private\n */\n var zr = this._zr = zrender.init(dom, {\n renderer: opts.renderer || defaultRenderer,\n devicePixelRatio: opts.devicePixelRatio,\n width: opts.width,\n height: opts.height\n });\n\n /**\n * Expect 60 pfs.\n * @type {Function}\n * @private\n */\n this._throttledZrFlush = throttle(zrUtil.bind(zr.flush, zr), 17);\n\n var theme = zrUtil.clone(theme);\n theme && backwardCompat(theme, true);\n /**\n * @type {Object}\n * @private\n */\n this._theme = theme;\n\n /**\n * @type {Array.<module:echarts/view/Chart>}\n * @private\n */\n this._chartsViews = [];\n\n /**\n * @type {Object.<string, module:echarts/view/Chart>}\n * @private\n */\n this._chartsMap = {};\n\n /**\n * @type {Array.<module:echarts/view/Component>}\n * @private\n */\n this._componentsViews = [];\n\n /**\n * @type {Object.<string, module:echarts/view/Component>}\n * @private\n */\n this._componentsMap = {};\n\n /**\n * @type {module:echarts/CoordinateSystem}\n * @private\n */\n this._coordSysMgr = new CoordinateSystemManager();\n\n /**\n * @type {module:echarts/ExtensionAPI}\n * @private\n */\n this._api = createExtensionAPI(this);\n\n Eventful.call(this);\n\n /**\n * @type {module:echarts~MessageCenter}\n * @private\n */\n this._messageCenter = new MessageCenter();\n\n // Init mouse events\n this._initEvents();\n\n // In case some people write `window.onresize = chart.resize`\n this.resize = zrUtil.bind(this.resize, this);\n\n // Can't dispatch action during rendering procedure\n this._pendingActions = [];\n // Sort on demand\n function prioritySortFunc(a, b) {\n return a.prio - b.prio;\n }\n timsort(visualFuncs, prioritySortFunc);\n timsort(dataProcessorFuncs, prioritySortFunc);\n\n zr.animation.on('frame', this._onframe, this);\n\n // ECharts instance can be used as value.\n zrUtil.setAsPrimitive(this);\n}\n\nvar echartsProto = ECharts.prototype;\n\nechartsProto._onframe = function () {\n // Lazy update\n if (this[OPTION_UPDATED]) {\n var silent = this[OPTION_UPDATED].silent;\n\n this[IN_MAIN_PROCESS] = true;\n\n updateMethods.prepareAndUpdate.call(this);\n\n this[IN_MAIN_PROCESS] = false;\n\n this[OPTION_UPDATED] = false;\n\n flushPendingActions.call(this, silent);\n\n triggerUpdatedEvent.call(this, silent);\n }\n};\n/**\n * @return {HTMLElement}\n */\nechartsProto.getDom = function () {\n return this._dom;\n};\n\n/**\n * @return {module:zrender~ZRender}\n */\nechartsProto.getZr = function () {\n return this._zr;\n};\n\n/**\n * Usage:\n * chart.setOption(option, notMerge, lazyUpdate);\n * chart.setOption(option, {\n * notMerge: ...,\n * lazyUpdate: ...,\n * silent: ...\n * });\n *\n * @param {Object} option\n * @param {Object|boolean} [opts] opts or notMerge.\n * @param {boolean} [opts.notMerge=false]\n * @param {boolean} [opts.lazyUpdate=false] Useful when setOption frequently.\n */\nechartsProto.setOption = function (option, notMerge, lazyUpdate) {\n if (__DEV__) {\n zrUtil.assert(!this[IN_MAIN_PROCESS], '`setOption` should not be called during main process.');\n }\n\n var silent;\n if (zrUtil.isObject(notMerge)) {\n lazyUpdate = notMerge.lazyUpdate;\n silent = notMerge.silent;\n notMerge = notMerge.notMerge;\n }\n\n this[IN_MAIN_PROCESS] = true;\n\n if (!this._model || notMerge) {\n var optionManager = new OptionManager(this._api);\n var theme = this._theme;\n var ecModel = this._model = new GlobalModel(null, null, theme, optionManager);\n ecModel.init(null, null, theme, optionManager);\n }\n\n this._model.setOption(option, optionPreprocessorFuncs);\n\n if (lazyUpdate) {\n this[OPTION_UPDATED] = {silent: silent};\n this[IN_MAIN_PROCESS] = false;\n }\n else {\n updateMethods.prepareAndUpdate.call(this);\n // Ensure zr refresh sychronously, and then pixel in canvas can be\n // fetched after `setOption`.\n this._zr.flush();\n\n this[OPTION_UPDATED] = false;\n this[IN_MAIN_PROCESS] = false;\n\n flushPendingActions.call(this, silent);\n triggerUpdatedEvent.call(this, silent);\n }\n};\n\n/**\n * @DEPRECATED\n */\nechartsProto.setTheme = function () {\n console.log('ECharts#setTheme() is DEPRECATED in ECharts 3.0');\n};\n\n/**\n * @return {module:echarts/model/Global}\n */\nechartsProto.getModel = function () {\n return this._model;\n};\n\n/**\n * @return {Object}\n */\nechartsProto.getOption = function () {\n return this._model && this._model.getOption();\n};\n\n/**\n * @return {number}\n */\nechartsProto.getWidth = function () {\n return this._zr.getWidth();\n};\n\n/**\n * @return {number}\n */\nechartsProto.getHeight = function () {\n return this._zr.getHeight();\n};\n\n/**\n * @return {number}\n */\nechartsProto.getDevicePixelRatio = function () {\n return this._zr.painter.dpr || window.devicePixelRatio || 1;\n};\n\n/**\n * Get canvas which has all thing rendered\n * @param {Object} opts\n * @param {string} [opts.backgroundColor]\n * @return {string}\n */\nechartsProto.getRenderedCanvas = function (opts) {\n if (!env.canvasSupported) {\n return;\n }\n opts = opts || {};\n opts.pixelRatio = opts.pixelRatio || 1;\n opts.backgroundColor = opts.backgroundColor\n || this._model.get('backgroundColor');\n var zr = this._zr;\n var list = zr.storage.getDisplayList();\n // Stop animations\n zrUtil.each(list, function (el) {\n el.stopAnimation(true);\n });\n return zr.painter.getRenderedCanvas(opts);\n};\n\n/**\n * Get svg data url\n * @return {string}\n */\nechartsProto.getSvgDataUrl = function () {\n if (!env.svgSupported) {\n return;\n }\n\n var zr = this._zr;\n var list = zr.storage.getDisplayList();\n // Stop animations\n zrUtil.each(list, function (el) {\n el.stopAnimation(true);\n });\n\n return zr.painter.pathToSvg();\n};\n\n/**\n * @return {string}\n * @param {Object} opts\n * @param {string} [opts.type='png']\n * @param {string} [opts.pixelRatio=1]\n * @param {string} [opts.backgroundColor]\n * @param {string} [opts.excludeComponents]\n */\nechartsProto.getDataURL = function (opts) {\n opts = opts || {};\n var excludeComponents = opts.excludeComponents;\n var ecModel = this._model;\n var excludesComponentViews = [];\n var self = this;\n\n each(excludeComponents, function (componentType) {\n ecModel.eachComponent({\n mainType: componentType\n }, function (component) {\n var view = self._componentsMap[component.__viewId];\n if (!view.group.ignore) {\n excludesComponentViews.push(view);\n view.group.ignore = true;\n }\n });\n });\n\n var url = this._zr.painter.getType() === 'svg'\n ? this.getSvgDataUrl()\n : this.getRenderedCanvas(opts).toDataURL(\n 'image/' + (opts && opts.type || 'png')\n );\n\n each(excludesComponentViews, function (view) {\n view.group.ignore = false;\n });\n\n return url;\n};\n\n\n/**\n * @return {string}\n * @param {Object} opts\n * @param {string} [opts.type='png']\n * @param {string} [opts.pixelRatio=1]\n * @param {string} [opts.backgroundColor]\n */\nechartsProto.getConnectedDataURL = function (opts) {\n if (!env.canvasSupported) {\n return;\n }\n var groupId = this.group;\n var mathMin = Math.min;\n var mathMax = Math.max;\n var MAX_NUMBER = Infinity;\n if (connectedGroups[groupId]) {\n var left = MAX_NUMBER;\n var top = MAX_NUMBER;\n var right = -MAX_NUMBER;\n var bottom = -MAX_NUMBER;\n var canvasList = [];\n var dpr = (opts && opts.pixelRatio) || 1;\n\n zrUtil.each(instances, function (chart, id) {\n if (chart.group === groupId) {\n var canvas = chart.getRenderedCanvas(\n zrUtil.clone(opts)\n );\n var boundingRect = chart.getDom().getBoundingClientRect();\n left = mathMin(boundingRect.left, left);\n top = mathMin(boundingRect.top, top);\n right = mathMax(boundingRect.right, right);\n bottom = mathMax(boundingRect.bottom, bottom);\n canvasList.push({\n dom: canvas,\n left: boundingRect.left,\n top: boundingRect.top\n });\n }\n });\n\n left *= dpr;\n top *= dpr;\n right *= dpr;\n bottom *= dpr;\n var width = right - left;\n var height = bottom - top;\n var targetCanvas = zrUtil.createCanvas();\n targetCanvas.width = width;\n targetCanvas.height = height;\n var zr = zrender.init(targetCanvas);\n\n each(canvasList, function (item) {\n var img = new graphic.Image({\n style: {\n x: item.left * dpr - left,\n y: item.top * dpr - top,\n image: item.dom\n }\n });\n zr.add(img);\n });\n zr.refreshImmediately();\n\n return targetCanvas.toDataURL('image/' + (opts && opts.type || 'png'));\n }\n else {\n return this.getDataURL(opts);\n }\n};\n\n/**\n * Convert from logical coordinate system to pixel coordinate system.\n * See CoordinateSystem#convertToPixel.\n * @param {string|Object} finder\n * If string, e.g., 'geo', means {geoIndex: 0}.\n * If Object, could contain some of these properties below:\n * {\n * seriesIndex / seriesId / seriesName,\n * geoIndex / geoId, geoName,\n * bmapIndex / bmapId / bmapName,\n * xAxisIndex / xAxisId / xAxisName,\n * yAxisIndex / yAxisId / yAxisName,\n * gridIndex / gridId / gridName,\n * ... (can be extended)\n * }\n * @param {Array|number} value\n * @return {Array|number} result\n */\nechartsProto.convertToPixel = zrUtil.curry(doConvertPixel, 'convertToPixel');\n\n/**\n * Convert from pixel coordinate system to logical coordinate system.\n * See CoordinateSystem#convertFromPixel.\n * @param {string|Object} finder\n * If string, e.g., 'geo', means {geoIndex: 0}.\n * If Object, could contain some of these properties below:\n * {\n * seriesIndex / seriesId / seriesName,\n * geoIndex / geoId / geoName,\n * bmapIndex / bmapId / bmapName,\n * xAxisIndex / xAxisId / xAxisName,\n * yAxisIndex / yAxisId / yAxisName\n * gridIndex / gridId / gridName,\n * ... (can be extended)\n * }\n * @param {Array|number} value\n * @return {Array|number} result\n */\nechartsProto.convertFromPixel = zrUtil.curry(doConvertPixel, 'convertFromPixel');\n\nfunction doConvertPixel(methodName, finder, value) {\n var ecModel = this._model;\n var coordSysList = this._coordSysMgr.getCoordinateSystems();\n var result;\n\n finder = modelUtil.parseFinder(ecModel, finder);\n\n for (var i = 0; i < coordSysList.length; i++) {\n var coordSys = coordSysList[i];\n if (coordSys[methodName]\n && (result = coordSys[methodName](ecModel, finder, value)) != null\n ) {\n return result;\n }\n }\n\n if (__DEV__) {\n console.warn(\n 'No coordinate system that supports ' + methodName + ' found by the given finder.'\n );\n }\n}\n\n/**\n * Is the specified coordinate systems or components contain the given pixel point.\n * @param {string|Object} finder\n * If string, e.g., 'geo', means {geoIndex: 0}.\n * If Object, could contain some of these properties below:\n * {\n * seriesIndex / seriesId / seriesName,\n * geoIndex / geoId / geoName,\n * bmapIndex / bmapId / bmapName,\n * xAxisIndex / xAxisId / xAxisName,\n * yAxisIndex / yAxisId / yAxisName,\n * gridIndex / gridId / gridName,\n * ... (can be extended)\n * }\n * @param {Array|number} value\n * @return {boolean} result\n */\nechartsProto.containPixel = function (finder, value) {\n var ecModel = this._model;\n var result;\n\n finder = modelUtil.parseFinder(ecModel, finder);\n\n zrUtil.each(finder, function (models, key) {\n key.indexOf('Models') >= 0 && zrUtil.each(models, function (model) {\n var coordSys = model.coordinateSystem;\n if (coordSys && coordSys.containPoint) {\n result |= !!coordSys.containPoint(value);\n }\n else if (key === 'seriesModels') {\n var view = this._chartsMap[model.__viewId];\n if (view && view.containPoint) {\n result |= view.containPoint(value, model);\n }\n else {\n if (__DEV__) {\n console.warn(key + ': ' + (view\n ? 'The found component do not support containPoint.'\n : 'No view mapping to the found component.'\n ));\n }\n }\n }\n else {\n if (__DEV__) {\n console.warn(key + ': containPoint is not supported');\n }\n }\n }, this);\n }, this);\n\n return !!result;\n};\n\n/**\n * Get visual from series or data.\n * @param {string|Object} finder\n * If string, e.g., 'series', means {seriesIndex: 0}.\n * If Object, could contain some of these properties below:\n * {\n * seriesIndex / seriesId / seriesName,\n * dataIndex / dataIndexInside\n * }\n * If dataIndex is not specified, series visual will be fetched,\n * but not data item visual.\n * If all of seriesIndex, seriesId, seriesName are not specified,\n * visual will be fetched from first series.\n * @param {string} visualType 'color', 'symbol', 'symbolSize'\n */\nechartsProto.getVisual = function (finder, visualType) {\n var ecModel = this._model;\n\n finder = modelUtil.parseFinder(ecModel, finder, {defaultMainType: 'series'});\n\n var seriesModel = finder.seriesModel;\n\n if (__DEV__) {\n if (!seriesModel) {\n console.warn('There is no specified seires model');\n }\n }\n\n var data = seriesModel.getData();\n\n var dataIndexInside = finder.hasOwnProperty('dataIndexInside')\n ? finder.dataIndexInside\n : finder.hasOwnProperty('dataIndex')\n ? data.indexOfRawIndex(finder.dataIndex)\n : null;\n\n return dataIndexInside != null\n ? data.getItemVisual(dataIndexInside, visualType)\n : data.getVisual(visualType);\n};\n\n/**\n * Get view of corresponding component model\n * @param {module:echarts/model/Component} componentModel\n * @return {module:echarts/view/Component}\n */\nechartsProto.getViewOfComponentModel = function (componentModel) {\n return this._componentsMap[componentModel.__viewId];\n};\n\n/**\n * Get view of corresponding series model\n * @param {module:echarts/model/Series} seriesModel\n * @return {module:echarts/view/Chart}\n */\nechartsProto.getViewOfSeriesModel = function (seriesModel) {\n return this._chartsMap[seriesModel.__viewId];\n};\n\n\nvar updateMethods = {\n\n /**\n * @param {Object} payload\n * @private\n */\n update: function (payload) {\n // console.profile && console.profile('update');\n\n var ecModel = this._model;\n var api = this._api;\n var coordSysMgr = this._coordSysMgr;\n var zr = this._zr;\n // update before setOption\n if (!ecModel) {\n return;\n }\n\n // Fixme First time update ?\n ecModel.restoreData();\n\n // TODO\n // Save total ecModel here for undo/redo (after restoring data and before processing data).\n // Undo (restoration of total ecModel) can be carried out in 'action' or outside API call.\n\n // Create new coordinate system each update\n // In LineView may save the old coordinate system and use it to get the orignal point\n coordSysMgr.create(this._model, this._api);\n\n processData.call(this, ecModel, api);\n\n stackSeriesData.call(this, ecModel);\n\n coordSysMgr.update(ecModel, api);\n\n doVisualEncoding.call(this, ecModel, payload);\n\n doRender.call(this, ecModel, payload);\n\n // Set background\n var backgroundColor = ecModel.get('backgroundColor') || 'transparent';\n\n var painter = zr.painter;\n // TODO all use clearColor ?\n if (painter.isSingleCanvas && painter.isSingleCanvas()) {\n zr.configLayer(0, {\n clearColor: backgroundColor\n });\n }\n else {\n // In IE8\n if (!env.canvasSupported) {\n var colorArr = colorTool.parse(backgroundColor);\n backgroundColor = colorTool.stringify(colorArr, 'rgb');\n if (colorArr[3] === 0) {\n backgroundColor = 'transparent';\n }\n }\n if (backgroundColor.colorStops || backgroundColor.image) {\n // Gradient background\n // FIXME Fixed layer?\n zr.configLayer(0, {\n clearColor: backgroundColor\n });\n this[HAS_GRADIENT_OR_PATTERN_BG] = true;\n\n this._dom.style.background = 'transparent';\n }\n else {\n if (this[HAS_GRADIENT_OR_PATTERN_BG]) {\n zr.configLayer(0, {\n clearColor: null\n });\n }\n this[HAS_GRADIENT_OR_PATTERN_BG] = false;\n\n this._dom.style.background = backgroundColor;\n }\n }\n\n each(postUpdateFuncs, function (func) {\n func(ecModel, api);\n });\n\n // console.profile && console.profileEnd('update');\n },\n\n /**\n * @param {Object} payload\n * @private\n */\n updateView: function (payload) {\n var ecModel = this._model;\n\n // update before setOption\n if (!ecModel) {\n return;\n }\n\n ecModel.eachSeries(function (seriesModel) {\n seriesModel.getData().clearAllVisual();\n });\n\n doVisualEncoding.call(this, ecModel, payload);\n\n invokeUpdateMethod.call(this, 'updateView', ecModel, payload);\n },\n\n /**\n * @param {Object} payload\n * @private\n */\n updateVisual: function (payload) {\n var ecModel = this._model;\n\n // update before setOption\n if (!ecModel) {\n return;\n }\n\n ecModel.eachSeries(function (seriesModel) {\n seriesModel.getData().clearAllVisual();\n });\n\n doVisualEncoding.call(this, ecModel, payload, true);\n\n invokeUpdateMethod.call(this, 'updateVisual', ecModel, payload);\n },\n\n /**\n * @param {Object} payload\n * @private\n */\n updateLayout: function (payload) {\n var ecModel = this._model;\n\n // update before setOption\n if (!ecModel) {\n return;\n }\n\n doLayout.call(this, ecModel, payload);\n\n invokeUpdateMethod.call(this, 'updateLayout', ecModel, payload);\n },\n\n /**\n * @param {Object} payload\n * @private\n */\n prepareAndUpdate: function (payload) {\n var ecModel = this._model;\n\n prepareView.call(this, 'component', ecModel);\n\n prepareView.call(this, 'chart', ecModel);\n\n updateMethods.update.call(this, payload);\n }\n};\n\n/**\n * @private\n */\nfunction updateDirectly(ecIns, method, payload, mainType, subType) {\n var ecModel = ecIns._model;\n\n // broadcast\n if (!mainType) {\n each(ecIns._componentsViews.concat(ecIns._chartsViews), callView);\n return;\n }\n\n var query = {};\n query[mainType + 'Id'] = payload[mainType + 'Id'];\n query[mainType + 'Index'] = payload[mainType + 'Index'];\n query[mainType + 'Name'] = payload[mainType + 'Name'];\n\n var condition = {mainType: mainType, query: query};\n subType && (condition.subType = subType); // subType may be '' by parseClassType;\n\n // If dispatchAction before setOption, do nothing.\n ecModel && ecModel.eachComponent(condition, function (model, index) {\n callView(ecIns[\n mainType === 'series' ? '_chartsMap' : '_componentsMap'\n ][model.__viewId]);\n }, ecIns);\n\n function callView(view) {\n view && view.__alive && view[method] && view[method](\n view.__model, ecModel, ecIns._api, payload\n );\n }\n}\n\n/**\n * Resize the chart\n * @param {Object} opts\n * @param {number} [opts.width] Can be 'auto' (the same as null/undefined)\n * @param {number} [opts.height] Can be 'auto' (the same as null/undefined)\n * @param {boolean} [opts.silent=false]\n */\nechartsProto.resize = function (opts) {\n if (__DEV__) {\n zrUtil.assert(!this[IN_MAIN_PROCESS], '`resize` should not be called during main process.');\n }\n\n this[IN_MAIN_PROCESS] = true;\n\n this._zr.resize(opts);\n\n var optionChanged = this._model && this._model.resetOption('media');\n var updateMethod = optionChanged ? 'prepareAndUpdate' : 'update';\n\n updateMethods[updateMethod].call(this);\n\n // Resize loading effect\n this._loadingFX && this._loadingFX.resize();\n\n this[IN_MAIN_PROCESS] = false;\n\n var silent = opts && opts.silent;\n\n flushPendingActions.call(this, silent);\n\n triggerUpdatedEvent.call(this, silent);\n};\n\n/**\n * Show loading effect\n * @param {string} [name='default']\n * @param {Object} [cfg]\n */\nechartsProto.showLoading = function (name, cfg) {\n if (zrUtil.isObject(name)) {\n cfg = name;\n name = '';\n }\n name = name || 'default';\n\n this.hideLoading();\n if (!loadingEffects[name]) {\n if (__DEV__) {\n console.warn('Loading effects ' + name + ' not exists.');\n }\n return;\n }\n var el = loadingEffects[name](this._api, cfg);\n var zr = this._zr;\n this._loadingFX = el;\n\n zr.add(el);\n};\n\n/**\n * Hide loading effect\n */\nechartsProto.hideLoading = function () {\n this._loadingFX && this._zr.remove(this._loadingFX);\n this._loadingFX = null;\n};\n\n/**\n * @param {Object} eventObj\n * @return {Object}\n */\nechartsProto.makeActionFromEvent = function (eventObj) {\n var payload = zrUtil.extend({}, eventObj);\n payload.type = eventActionMap[eventObj.type];\n return payload;\n};\n\n/**\n * @pubilc\n * @param {Object} payload\n * @param {string} [payload.type] Action type\n * @param {Object|boolean} [opt] If pass boolean, means opt.silent\n * @param {boolean} [opt.silent=false] Whether trigger events.\n * @param {boolean} [opt.flush=undefined]\n * true: Flush immediately, and then pixel in canvas can be fetched\n * immediately. Caution: it might affect performance.\n * false: Not not flush.\n * undefined: Auto decide whether perform flush.\n */\nechartsProto.dispatchAction = function (payload, opt) {\n if (!zrUtil.isObject(opt)) {\n opt = {silent: !!opt};\n }\n\n if (!actions[payload.type]) {\n return;\n }\n\n // Avoid dispatch action before setOption. Especially in `connect`.\n if (!this._model) {\n return;\n }\n\n // May dispatchAction in rendering procedure\n if (this[IN_MAIN_PROCESS]) {\n this._pendingActions.push(payload);\n return;\n }\n\n doDispatchAction.call(this, payload, opt.silent);\n\n if (opt.flush) {\n this._zr.flush(true);\n }\n else if (opt.flush !== false && env.browser.weChat) {\n // In WeChat embeded browser, `requestAnimationFrame` and `setInterval`\n // hang when sliding page (on touch event), which cause that zr does not\n // refresh util user interaction finished, which is not expected.\n // But `dispatchAction` may be called too frequently when pan on touch\n // screen, which impacts performance if do not throttle them.\n this._throttledZrFlush();\n }\n\n flushPendingActions.call(this, opt.silent);\n\n triggerUpdatedEvent.call(this, opt.silent);\n};\n\nfunction doDispatchAction(payload, silent) {\n var payloadType = payload.type;\n var escapeConnect = payload.escapeConnect;\n var actionWrap = actions[payloadType];\n var actionInfo = actionWrap.actionInfo;\n\n var cptType = (actionInfo.update || 'update').split(':');\n var updateMethod = cptType.pop();\n cptType = cptType[0] != null && parseClassType(cptType[0]);\n\n this[IN_MAIN_PROCESS] = true;\n\n var payloads = [payload];\n var batched = false;\n // Batch action\n if (payload.batch) {\n batched = true;\n payloads = zrUtil.map(payload.batch, function (item) {\n item = zrUtil.defaults(zrUtil.extend({}, item), payload);\n item.batch = null;\n return item;\n });\n }\n\n var eventObjBatch = [];\n var eventObj;\n var isHighDown = payloadType === 'highlight' || payloadType === 'downplay';\n\n each(payloads, function (batchItem) {\n // Action can specify the event by return it.\n eventObj = actionWrap.action(batchItem, this._model, this._api);\n // Emit event outside\n eventObj = eventObj || zrUtil.extend({}, batchItem);\n // Convert type to eventType\n eventObj.type = actionInfo.event || eventObj.type;\n eventObjBatch.push(eventObj);\n\n // light update does not perform data process, layout and visual.\n if (isHighDown) {\n // method, payload, mainType, subType\n updateDirectly(this, updateMethod, batchItem, 'series');\n }\n else if (cptType) {\n updateDirectly(this, updateMethod, batchItem, cptType.main, cptType.sub);\n }\n }, this);\n\n if (updateMethod !== 'none' && !isHighDown && !cptType) {\n // Still dirty\n if (this[OPTION_UPDATED]) {\n // FIXME Pass payload ?\n updateMethods.prepareAndUpdate.call(this, payload);\n this[OPTION_UPDATED] = false;\n }\n else {\n updateMethods[updateMethod].call(this, payload);\n }\n }\n\n // Follow the rule of action batch\n if (batched) {\n eventObj = {\n type: actionInfo.event || payloadType,\n escapeConnect: escapeConnect,\n batch: eventObjBatch\n };\n }\n else {\n eventObj = eventObjBatch[0];\n }\n\n this[IN_MAIN_PROCESS] = false;\n\n !silent && this._messageCenter.trigger(eventObj.type, eventObj);\n}\n\nfunction flushPendingActions(silent) {\n var pendingActions = this._pendingActions;\n while (pendingActions.length) {\n var payload = pendingActions.shift();\n doDispatchAction.call(this, payload, silent);\n }\n}\n\nfunction triggerUpdatedEvent(silent) {\n !silent && this.trigger('updated');\n}\n\n/**\n * Register event\n * @method\n */\nechartsProto.on = createRegisterEventWithLowercaseName('on');\nechartsProto.off = createRegisterEventWithLowercaseName('off');\nechartsProto.one = createRegisterEventWithLowercaseName('one');\n\n/**\n * @param {string} methodName\n * @private\n */\nfunction invokeUpdateMethod(methodName, ecModel, payload) {\n var api = this._api;\n\n // Update all components\n each(this._componentsViews, function (component) {\n var componentModel = component.__model;\n component[methodName](componentModel, ecModel, api, payload);\n\n updateZ(componentModel, component);\n }, this);\n\n // Upate all charts\n ecModel.eachSeries(function (seriesModel, idx) {\n var chart = this._chartsMap[seriesModel.__viewId];\n chart[methodName](seriesModel, ecModel, api, payload);\n\n updateZ(seriesModel, chart);\n\n updateProgressiveAndBlend(seriesModel, chart);\n }, this);\n\n // If use hover layer\n updateHoverLayerStatus(this._zr, ecModel);\n\n // Post render\n each(postUpdateFuncs, function (func) {\n func(ecModel, api);\n });\n}\n\n/**\n * Prepare view instances of charts and components\n * @param {module:echarts/model/Global} ecModel\n * @private\n */\nfunction prepareView(type, ecModel) {\n var isComponent = type === 'component';\n var viewList = isComponent ? this._componentsViews : this._chartsViews;\n var viewMap = isComponent ? this._componentsMap : this._chartsMap;\n var zr = this._zr;\n\n for (var i = 0; i < viewList.length; i++) {\n viewList[i].__alive = false;\n }\n\n ecModel[isComponent ? 'eachComponent' : 'eachSeries'](function (componentType, model) {\n if (isComponent) {\n if (componentType === 'series') {\n return;\n }\n }\n else {\n model = componentType;\n }\n\n // Consider: id same and type changed.\n var viewId = '_ec_' + model.id + '_' + model.type;\n var view = viewMap[viewId];\n if (!view) {\n var classType = parseClassType(model.type);\n var Clazz = isComponent\n ? ComponentView.getClass(classType.main, classType.sub)\n : ChartView.getClass(classType.sub);\n if (Clazz) {\n view = new Clazz();\n view.init(ecModel, this._api);\n viewMap[viewId] = view;\n viewList.push(view);\n zr.add(view.group);\n }\n else {\n // Error\n return;\n }\n }\n\n model.__viewId = view.__id = viewId;\n view.__alive = true;\n view.__model = model;\n view.group.__ecComponentInfo = {\n mainType: model.mainType,\n index: model.componentIndex\n };\n }, this);\n\n for (var i = 0; i < viewList.length;) {\n var view = viewList[i];\n if (!view.__alive) {\n zr.remove(view.group);\n view.dispose(ecModel, this._api);\n viewList.splice(i, 1);\n delete viewMap[view.__id];\n view.__id = view.group.__ecComponentInfo = null;\n }\n else {\n i++;\n }\n }\n}\n\n/**\n * Processor data in each series\n *\n * @param {module:echarts/model/Global} ecModel\n * @private\n */\nfunction processData(ecModel, api) {\n each(dataProcessorFuncs, function (process) {\n process.func(ecModel, api);\n });\n}\n\n/**\n * @private\n */\nfunction stackSeriesData(ecModel) {\n var stackedDataMap = {};\n ecModel.eachSeries(function (series) {\n var stack = series.get('stack');\n var data = series.getData();\n if (stack && data.type === 'list') {\n var previousStack = stackedDataMap[stack];\n // Avoid conflict with Object.prototype\n if (stackedDataMap.hasOwnProperty(stack) && previousStack) {\n data.stackedOn = previousStack;\n }\n stackedDataMap[stack] = data;\n }\n });\n}\n\n/**\n * Layout before each chart render there series, special visual encoding stage\n *\n * @param {module:echarts/model/Global} ecModel\n * @private\n */\nfunction doLayout(ecModel, payload) {\n var api = this._api;\n each(visualFuncs, function (visual) {\n if (visual.isLayout) {\n visual.func(ecModel, api, payload);\n }\n });\n}\n\n/**\n * Encode visual infomation from data after data processing\n *\n * @param {module:echarts/model/Global} ecModel\n * @param {object} layout\n * @param {boolean} [excludesLayout]\n * @private\n */\nfunction doVisualEncoding(ecModel, payload, excludesLayout) {\n var api = this._api;\n ecModel.clearColorPalette();\n ecModel.eachSeries(function (seriesModel) {\n seriesModel.clearColorPalette();\n });\n each(visualFuncs, function (visual) {\n (!excludesLayout || !visual.isLayout)\n && visual.func(ecModel, api, payload);\n });\n}\n\n/**\n * Render each chart and component\n * @private\n */\nfunction doRender(ecModel, payload) {\n var api = this._api;\n // Render all components\n each(this._componentsViews, function (componentView) {\n var componentModel = componentView.__model;\n componentView.render(componentModel, ecModel, api, payload);\n\n updateZ(componentModel, componentView);\n }, this);\n\n each(this._chartsViews, function (chart) {\n chart.__alive = false;\n }, this);\n\n // Render all charts\n ecModel.eachSeries(function (seriesModel, idx) {\n var chartView = this._chartsMap[seriesModel.__viewId];\n chartView.__alive = true;\n chartView.render(seriesModel, ecModel, api, payload);\n\n chartView.group.silent = !!seriesModel.get('silent');\n\n updateZ(seriesModel, chartView);\n\n updateProgressiveAndBlend(seriesModel, chartView);\n\n }, this);\n\n // If use hover layer\n updateHoverLayerStatus(this._zr, ecModel);\n\n // Remove groups of unrendered charts\n each(this._chartsViews, function (chart) {\n if (!chart.__alive) {\n chart.remove(ecModel, api);\n }\n }, this);\n}\n\nvar MOUSE_EVENT_NAMES = [\n 'click', 'dblclick', 'mouseover', 'mouseout', 'mousemove',\n 'mousedown', 'mouseup', 'globalout', 'contextmenu'\n];\n/**\n * @private\n */\nechartsProto._initEvents = function () {\n each(MOUSE_EVENT_NAMES, function (eveName) {\n this._zr.on(eveName, function (e) {\n var ecModel = this.getModel();\n var el = e.target;\n var params;\n\n // no e.target when 'globalout'.\n if (eveName === 'globalout') {\n params = {};\n }\n else if (el && el.dataIndex != null) {\n var dataModel = el.dataModel || ecModel.getSeriesByIndex(el.seriesIndex);\n params = dataModel && dataModel.getDataParams(el.dataIndex, el.dataType) || {};\n }\n // If element has custom eventData of components\n else if (el && el.eventData) {\n params = zrUtil.extend({}, el.eventData);\n }\n\n if (params) {\n params.event = e;\n params.type = eveName;\n this.trigger(eveName, params);\n }\n\n }, this);\n }, this);\n\n each(eventActionMap, function (actionType, eventType) {\n this._messageCenter.on(eventType, function (event) {\n this.trigger(eventType, event);\n }, this);\n }, this);\n};\n\n/**\n * @return {boolean}\n */\nechartsProto.isDisposed = function () {\n return this._disposed;\n};\n\n/**\n * Clear\n */\nechartsProto.clear = function () {\n this.setOption({ series: [] }, true);\n};\n\n/**\n * Dispose instance\n */\nechartsProto.dispose = function () {\n if (this._disposed) {\n if (__DEV__) {\n console.warn('Instance ' + this.id + ' has been disposed');\n }\n return;\n }\n this._disposed = true;\n\n var api = this._api;\n var ecModel = this._model;\n\n each(this._componentsViews, function (component) {\n component.dispose(ecModel, api);\n });\n each(this._chartsViews, function (chart) {\n chart.dispose(ecModel, api);\n });\n\n // Dispose after all views disposed\n this._zr.dispose();\n\n delete instances[this.id];\n};\n\nzrUtil.mixin(ECharts, Eventful);\n\nfunction updateHoverLayerStatus(zr, ecModel) {\n var storage = zr.storage;\n var elCount = 0;\n storage.traverse(function (el) {\n if (!el.isGroup) {\n elCount++;\n }\n });\n if (elCount > ecModel.get('hoverLayerThreshold') && !env.node) {\n storage.traverse(function (el) {\n if (!el.isGroup) {\n el.useHoverLayer = true;\n }\n });\n }\n}\n\n/**\n * Update chart progressive and blend.\n * @param {module:echarts/model/Series|module:echarts/model/Component} model\n * @param {module:echarts/view/Component|module:echarts/view/Chart} view\n */\nfunction updateProgressiveAndBlend(seriesModel, chartView) {\n // Progressive configuration\n var elCount = 0;\n chartView.group.traverse(function (el) {\n if (el.type !== 'group' && !el.ignore) {\n elCount++;\n }\n });\n var frameDrawNum = +seriesModel.get('progressive');\n var needProgressive = elCount > seriesModel.get('progressiveThreshold') && frameDrawNum && !env.node;\n if (needProgressive) {\n chartView.group.traverse(function (el) {\n // FIXME marker and other components\n if (!el.isGroup) {\n el.progressive = needProgressive ?\n Math.floor(elCount++ / frameDrawNum) : -1;\n if (needProgressive) {\n el.stopAnimation(true);\n }\n }\n });\n }\n\n // Blend configration\n var blendMode = seriesModel.get('blendMode') || null;\n if (__DEV__) {\n if (!env.canvasSupported && blendMode && blendMode !== 'source-over') {\n console.warn('Only canvas support blendMode');\n }\n }\n chartView.group.traverse(function (el) {\n // FIXME marker and other components\n if (!el.isGroup) {\n el.setStyle('blend', blendMode);\n }\n });\n}\n\n/**\n * @param {module:echarts/model/Series|module:echarts/model/Component} model\n * @param {module:echarts/view/Component|module:echarts/view/Chart} view\n */\nfunction updateZ(model, view) {\n var z = model.get('z');\n var zlevel = model.get('zlevel');\n // Set z and zlevel\n view.group.traverse(function (el) {\n if (el.type !== 'group') {\n z != null && (el.z = z);\n zlevel != null && (el.zlevel = zlevel);\n }\n });\n}\n\nfunction createExtensionAPI(ecInstance) {\n var coordSysMgr = ecInstance._coordSysMgr;\n return zrUtil.extend(new ExtensionAPI(ecInstance), {\n // Inject methods\n getCoordinateSystems: zrUtil.bind(\n coordSysMgr.getCoordinateSystems, coordSysMgr\n ),\n getComponentByElement: function (el) {\n while (el) {\n var modelInfo = el.__ecComponentInfo;\n if (modelInfo != null) {\n return ecInstance._model.getComponent(modelInfo.mainType, modelInfo.index);\n }\n el = el.parent;\n }\n }\n });\n}\n\n/**\n * @type {Object} key: actionType.\n * @inner\n */\nvar actions = {};\n\n/**\n * Map eventType to actionType\n * @type {Object}\n */\nvar eventActionMap = {};\n\n/**\n * Data processor functions of each stage\n * @type {Array.<Object.<string, Function>>}\n * @inner\n */\nvar dataProcessorFuncs = [];\n\n/**\n * @type {Array.<Function>}\n * @inner\n */\nvar optionPreprocessorFuncs = [];\n\n/**\n * @type {Array.<Function>}\n * @inner\n */\nvar postUpdateFuncs = [];\n\n/**\n * Visual encoding functions of each stage\n * @type {Array.<Object.<string, Function>>}\n * @inner\n */\nvar visualFuncs = [];\n/**\n * Theme storage\n * @type {Object.<key, Object>}\n */\nvar themeStorage = {};\n/**\n * Loading effects\n */\nvar loadingEffects = {};\n\nvar instances = {};\nvar connectedGroups = {};\n\nvar idBase = new Date() - 0;\nvar groupIdBase = new Date() - 0;\nvar DOM_ATTRIBUTE_KEY = '_echarts_instance_';\n\nvar mapDataStores = {};\n\nfunction enableConnect(chart) {\n var STATUS_PENDING = 0;\n var STATUS_UPDATING = 1;\n var STATUS_UPDATED = 2;\n var STATUS_KEY = '__connectUpdateStatus';\n\n function updateConnectedChartsStatus(charts, status) {\n for (var i = 0; i < charts.length; i++) {\n var otherChart = charts[i];\n otherChart[STATUS_KEY] = status;\n }\n }\n\n zrUtil.each(eventActionMap, function (actionType, eventType) {\n chart._messageCenter.on(eventType, function (event) {\n if (connectedGroups[chart.group] && chart[STATUS_KEY] !== STATUS_PENDING) {\n if (event && event.escapeConnect) {\n return;\n }\n\n var action = chart.makeActionFromEvent(event);\n var otherCharts = [];\n\n zrUtil.each(instances, function (otherChart) {\n if (otherChart !== chart && otherChart.group === chart.group) {\n otherCharts.push(otherChart);\n }\n });\n\n updateConnectedChartsStatus(otherCharts, STATUS_PENDING);\n each(otherCharts, function (otherChart) {\n if (otherChart[STATUS_KEY] !== STATUS_UPDATING) {\n otherChart.dispatchAction(action);\n }\n });\n updateConnectedChartsStatus(otherCharts, STATUS_UPDATED);\n }\n });\n });\n}\n\n/**\n * @param {HTMLElement} dom\n * @param {Object} [theme]\n * @param {Object} opts\n * @param {number} [opts.devicePixelRatio] Use window.devicePixelRatio by default\n * @param {string} [opts.renderer] Currently only 'canvas' is supported.\n * @param {number} [opts.width] Use clientWidth of the input `dom` by default.\n * Can be 'auto' (the same as null/undefined)\n * @param {number} [opts.height] Use clientHeight of the input `dom` by default.\n * Can be 'auto' (the same as null/undefined)\n */\nexport function init(dom, theme, opts) {\n if (__DEV__) {\n // Check version\n if ((zrender.version.replace('.', '') - 0) < (dependencies.zrender.replace('.', '') - 0)) {\n throw new Error(\n 'zrender/src ' + zrender.version\n + ' is too old for ECharts ' + version\n + '. Current version need ZRender '\n + dependencies.zrender + '+'\n );\n }\n\n if (!dom) {\n throw new Error('Initialize failed: invalid dom.');\n }\n }\n\n var existInstance = getInstanceByDom(dom);\n if (existInstance) {\n if (__DEV__) {\n console.warn('There is a chart instance already initialized on the dom.');\n }\n return existInstance;\n }\n\n if (__DEV__) {\n if (zrUtil.isDom(dom)\n && dom.nodeName.toUpperCase() !== 'CANVAS'\n && (\n (!dom.clientWidth && (!opts || opts.width == null))\n || (!dom.clientHeight && (!opts || opts.height == null))\n )\n ) {\n console.warn('Can\\'t get dom width or height');\n }\n }\n\n var chart = new ECharts(dom, theme, opts);\n chart.id = 'ec_' + idBase++;\n instances[chart.id] = chart;\n\n if (dom.setAttribute) {\n dom.setAttribute(DOM_ATTRIBUTE_KEY, chart.id);\n }\n else {\n dom[DOM_ATTRIBUTE_KEY] = chart.id;\n }\n\n enableConnect(chart);\n\n return chart;\n}\n\n/**\n * @return {string|Array.<module:echarts~ECharts>} groupId\n */\nexport function connect(groupId) {\n // Is array of charts\n if (zrUtil.isArray(groupId)) {\n var charts = groupId;\n groupId = null;\n // If any chart has group\n zrUtil.each(charts, function (chart) {\n if (chart.group != null) {\n groupId = chart.group;\n }\n });\n groupId = groupId || ('g_' + groupIdBase++);\n zrUtil.each(charts, function (chart) {\n chart.group = groupId;\n });\n }\n connectedGroups[groupId] = true;\n return groupId;\n}\n\n/**\n * @DEPRECATED\n * @return {string} groupId\n */\nexport function disConnect(groupId) {\n connectedGroups[groupId] = false;\n}\n\n/**\n * @return {string} groupId\n */\nexport var disconnect = disConnect;\n\n/**\n * Dispose a chart instance\n * @param {module:echarts~ECharts|HTMLDomElement|string} chart\n */\nexport function dispose(chart) {\n if (typeof chart === 'string') {\n chart = instances[chart];\n }\n else if (!(chart instanceof ECharts)){\n // Try to treat as dom\n chart = getInstanceByDom(chart);\n }\n if ((chart instanceof ECharts) && !chart.isDisposed()) {\n chart.dispose();\n }\n}\n\n/**\n * @param {HTMLElement} dom\n * @return {echarts~ECharts}\n */\nexport function getInstanceByDom(dom) {\n var key;\n if (dom.getAttribute) {\n key = dom.getAttribute(DOM_ATTRIBUTE_KEY);\n }\n else {\n key = dom[DOM_ATTRIBUTE_KEY];\n }\n return instances[key];\n}\n\n/**\n * @param {string} key\n * @return {echarts~ECharts}\n */\nexport function getInstanceById(key) {\n return instances[key];\n}\n\n/**\n * Register theme\n */\nexport function registerTheme(name, theme) {\n themeStorage[name] = theme;\n}\n\n/**\n * Register option preprocessor\n * @param {Function} preprocessorFunc\n */\nexport function registerPreprocessor(preprocessorFunc) {\n optionPreprocessorFuncs.push(preprocessorFunc);\n}\n\n/**\n * @param {number} [priority=1000]\n * @param {Function} processorFunc\n */\nexport function registerProcessor(priority, processorFunc) {\n if (typeof priority === 'function') {\n processorFunc = priority;\n priority = PRIORITY_PROCESSOR_FILTER;\n }\n if (__DEV__) {\n if (isNaN(priority)) {\n throw new Error('Unkown processor priority');\n }\n }\n dataProcessorFuncs.push({\n prio: priority,\n func: processorFunc\n });\n}\n\n/**\n * Register postUpdater\n * @param {Function} postUpdateFunc\n */\nexport function registerPostUpdate(postUpdateFunc) {\n postUpdateFuncs.push(postUpdateFunc);\n}\n\n/**\n * Usage:\n * registerAction('someAction', 'someEvent', function () { ... });\n * registerAction('someAction', function () { ... });\n * registerAction(\n * {type: 'someAction', event: 'someEvent', update: 'updateView'},\n * function () { ... }\n * );\n *\n * @param {(string|Object)} actionInfo\n * @param {string} actionInfo.type\n * @param {string} [actionInfo.event]\n * @param {string} [actionInfo.update]\n * @param {string} [eventName]\n * @param {Function} action\n */\nexport function registerAction(actionInfo, eventName, action) {\n if (typeof eventName === 'function') {\n action = eventName;\n eventName = '';\n }\n var actionType = zrUtil.isObject(actionInfo)\n ? actionInfo.type\n : ([actionInfo, actionInfo = {\n event: eventName\n }][0]);\n\n // Event name is all lowercase\n actionInfo.event = (actionInfo.event || actionType).toLowerCase();\n eventName = actionInfo.event;\n\n // Validate action type and event name.\n zrUtil.assert(ACTION_REG.test(actionType) && ACTION_REG.test(eventName));\n\n if (!actions[actionType]) {\n actions[actionType] = {action: action, actionInfo: actionInfo};\n }\n eventActionMap[eventName] = actionType;\n}\n\n/**\n * @param {string} type\n * @param {*} CoordinateSystem\n */\nexport function registerCoordinateSystem(type, CoordinateSystem) {\n CoordinateSystemManager.register(type, CoordinateSystem);\n}\n\n/**\n * Get dimensions of specified coordinate system.\n * @param {string} type\n * @return {Array.<string|Object>}\n */\nexport function getCoordinateSystemDimensions(type) {\n var coordSysCreator = CoordinateSystemManager.get(type);\n if (coordSysCreator) {\n return coordSysCreator.getDimensionsInfo\n ? coordSysCreator.getDimensionsInfo()\n : coordSysCreator.dimensions.slice();\n }\n}\n\n/**\n * Layout is a special stage of visual encoding\n * Most visual encoding like color are common for different chart\n * But each chart has it's own layout algorithm\n *\n * @param {number} [priority=1000]\n * @param {Function} layoutFunc\n */\nexport function registerLayout(priority, layoutFunc) {\n if (typeof priority === 'function') {\n layoutFunc = priority;\n priority = PRIORITY_VISUAL_LAYOUT;\n }\n if (__DEV__) {\n if (isNaN(priority)) {\n throw new Error('Unkown layout priority');\n }\n }\n visualFuncs.push({\n prio: priority,\n func: layoutFunc,\n isLayout: true\n });\n}\n\n/**\n * @param {number} [priority=3000]\n * @param {Function} visualFunc\n */\nexport function registerVisual(priority, visualFunc) {\n if (typeof priority === 'function') {\n visualFunc = priority;\n priority = PRIORITY_VISUAL_CHART;\n }\n if (__DEV__) {\n if (isNaN(priority)) {\n throw new Error('Unkown visual priority');\n }\n }\n visualFuncs.push({\n prio: priority,\n func: visualFunc\n });\n}\n\n/**\n * @param {string} name\n */\nexport function registerLoading(name, loadingFx) {\n loadingEffects[name] = loadingFx;\n}\n\n/**\n * @param {Object} opts\n * @param {string} [superClass]\n */\nexport function extendComponentModel(opts/*, superClass*/) {\n // var Clazz = ComponentModel;\n // if (superClass) {\n // var classType = parseClassType(superClass);\n // Clazz = ComponentModel.getClass(classType.main, classType.sub, true);\n // }\n return ComponentModel.extend(opts);\n}\n\n/**\n * @param {Object} opts\n * @param {string} [superClass]\n */\nexport function extendComponentView(opts/*, superClass*/) {\n // var Clazz = ComponentView;\n // if (superClass) {\n // var classType = parseClassType(superClass);\n // Clazz = ComponentView.getClass(classType.main, classType.sub, true);\n // }\n return ComponentView.extend(opts);\n}\n\n/**\n * @param {Object} opts\n * @param {string} [superClass]\n */\nexport function extendSeriesModel(opts/*, superClass*/) {\n // var Clazz = SeriesModel;\n // if (superClass) {\n // superClass = 'series.' + superClass.replace('series.', '');\n // var classType = parseClassType(superClass);\n // Clazz = ComponentModel.getClass(classType.main, classType.sub, true);\n // }\n return SeriesModel.extend(opts);\n}\n\n/**\n * @param {Object} opts\n * @param {string} [superClass]\n */\nexport function extendChartView(opts/*, superClass*/) {\n // var Clazz = ChartView;\n // if (superClass) {\n // superClass = superClass.replace('series.', '');\n // var classType = parseClassType(superClass);\n // Clazz = ChartView.getClass(classType.main, true);\n // }\n return ChartView.extend(opts);\n}\n\n/**\n * ZRender need a canvas context to do measureText.\n * But in node environment canvas may be created by node-canvas.\n * So we need to specify how to create a canvas instead of using document.createElement('canvas')\n *\n * Be careful of using it in the browser.\n *\n * @param {Function} creator\n * @example\n * var Canvas = require('canvas');\n * var echarts = require('echarts');\n * echarts.setCanvasCreator(function () {\n * // Small size is enough.\n * return new Canvas(32, 32);\n * });\n */\nexport function setCanvasCreator(creator) {\n zrUtil.$override('createCanvas', creator);\n}\n\n/**\n * @param {string} mapName\n * @param {Object|string} geoJson\n * @param {Object} [specialAreas]\n *\n * @example\n * $.get('USA.json', function (geoJson) {\n * echarts.registerMap('USA', geoJson);\n * // Or\n * echarts.registerMap('USA', {\n * geoJson: geoJson,\n * specialAreas: {}\n * })\n * });\n */\nexport function registerMap(mapName, geoJson, specialAreas) {\n if (geoJson.geoJson && !geoJson.features) {\n specialAreas = geoJson.specialAreas;\n geoJson = geoJson.geoJson;\n }\n if (typeof geoJson === 'string') {\n geoJson = (typeof JSON !== 'undefined' && JSON.parse)\n ? JSON.parse(geoJson) : (new Function('return (' + geoJson + ');'))();\n }\n mapDataStores[mapName] = {\n geoJson: geoJson,\n specialAreas: specialAreas\n };\n}\n\n/**\n * @param {string} mapName\n * @return {Object}\n */\nexport function getMap(mapName) {\n return mapDataStores[mapName];\n}\n\nregisterVisual(PRIORITY_VISUAL_GLOBAL, seriesColor);\nregisterPreprocessor(backwardCompat);\nregisterLoading('default', loadingDefault);\n\n// Default actions\n\nregisterAction({\n type: 'highlight',\n event: 'highlight',\n update: 'highlight'\n}, zrUtil.noop);\n\nregisterAction({\n type: 'downplay',\n event: 'downplay',\n update: 'downplay'\n}, zrUtil.noop);\n\n\n// For backward compatibility, where the namespace `dataTool` will\n// be mounted on `echarts` is the extension `dataTool` is imported.\nexport var dataTool = {};\n","\nfunction defaultKeyGetter(item) {\n return item;\n}\n\n/**\n * @param {Array} oldArr\n * @param {Array} newArr\n * @param {Function} oldKeyGetter\n * @param {Function} newKeyGetter\n * @param {Object} [context] Can be visited by this.context in callback.\n */\nfunction DataDiffer(oldArr, newArr, oldKeyGetter, newKeyGetter, context) {\n this._old = oldArr;\n this._new = newArr;\n\n this._oldKeyGetter = oldKeyGetter || defaultKeyGetter;\n this._newKeyGetter = newKeyGetter || defaultKeyGetter;\n\n this.context = context;\n}\n\nDataDiffer.prototype = {\n\n constructor: DataDiffer,\n\n /**\n * Callback function when add a data\n */\n add: function (func) {\n this._add = func;\n return this;\n },\n\n /**\n * Callback function when update a data\n */\n update: function (func) {\n this._update = func;\n return this;\n },\n\n /**\n * Callback function when remove a data\n */\n remove: function (func) {\n this._remove = func;\n return this;\n },\n\n execute: function () {\n var oldArr = this._old;\n var newArr = this._new;\n\n var oldDataIndexMap = {};\n var newDataIndexMap = {};\n var oldDataKeyArr = [];\n var newDataKeyArr = [];\n var i;\n\n initIndexMap(oldArr, oldDataIndexMap, oldDataKeyArr, '_oldKeyGetter', this);\n initIndexMap(newArr, newDataIndexMap, newDataKeyArr, '_newKeyGetter', this);\n\n // Travel by inverted order to make sure order consistency\n // when duplicate keys exists (consider newDataIndex.pop() below).\n // For performance consideration, these code below do not look neat.\n for (i = 0; i < oldArr.length; i++) {\n var key = oldDataKeyArr[i];\n var idx = newDataIndexMap[key];\n\n // idx can never be empty array here. see 'set null' logic below.\n if (idx != null) {\n // Consider there is duplicate key (for example, use dataItem.name as key).\n // We should make sure every item in newArr and oldArr can be visited.\n var len = idx.length;\n if (len) {\n len === 1 && (newDataIndexMap[key] = null);\n idx = idx.unshift();\n }\n else {\n newDataIndexMap[key] = null;\n }\n this._update && this._update(idx, i);\n }\n else {\n this._remove && this._remove(i);\n }\n }\n\n for (var i = 0; i < newDataKeyArr.length; i++) {\n var key = newDataKeyArr[i];\n if (newDataIndexMap.hasOwnProperty(key)) {\n var idx = newDataIndexMap[key];\n if (idx == null) {\n continue;\n }\n // idx can never be empty array here. see 'set null' logic above.\n if (!idx.length) {\n this._add && this._add(idx);\n }\n else {\n for (var j = 0, len = idx.length; j < len; j++) {\n this._add && this._add(idx[j]);\n }\n }\n }\n }\n }\n};\n\nfunction initIndexMap(arr, map, keyArr, keyGetterName, dataDiffer) {\n for (var i = 0; i < arr.length; i++) {\n // Add prefix to avoid conflict with Object.prototype.\n var key = '_ec_' + dataDiffer[keyGetterName](arr[i], i);\n var existence = map[key];\n if (existence == null) {\n keyArr.push(key);\n map[key] = i;\n }\n else {\n if (!existence.length) {\n map[key] = existence = [existence];\n }\n existence.push(i);\n }\n }\n}\n\nexport default DataDiffer;","/**\n * List for data storage\n * @module echarts/data/List\n */\n\nimport {__DEV__} from '../config';\nimport * as zrUtil from 'zrender/src/core/util';\nimport Model from '../model/Model';\nimport DataDiffer from './DataDiffer';\nimport * as modelUtil from '../util/model';\n\nvar isObject = zrUtil.isObject;\n\nvar UNDEFINED = 'undefined';\nvar globalObj = typeof window === UNDEFINED ? global : window;\n\nvar dataCtors = {\n 'float': typeof globalObj.Float64Array === UNDEFINED\n ? Array : globalObj.Float64Array,\n 'int': typeof globalObj.Int32Array === UNDEFINED\n ? Array : globalObj.Int32Array,\n // Ordinal data type can be string or int\n 'ordinal': Array,\n 'number': Array,\n 'time': Array\n};\n\nvar TRANSFERABLE_PROPERTIES = [\n 'stackedOn', 'hasItemOption', '_nameList', '_idList', '_rawData'\n];\n\nfunction transferProperties(a, b) {\n zrUtil.each(TRANSFERABLE_PROPERTIES.concat(b.__wrappedMethods || []), function (propName) {\n if (b.hasOwnProperty(propName)) {\n a[propName] = b[propName];\n }\n });\n\n a.__wrappedMethods = b.__wrappedMethods;\n}\n\nfunction DefaultDataProvider(dataArray) {\n this._array = dataArray || [];\n}\n\nDefaultDataProvider.prototype.pure = false;\n\nDefaultDataProvider.prototype.count = function () {\n return this._array.length;\n};\nDefaultDataProvider.prototype.getItem = function (idx) {\n return this._array[idx];\n};\n\n/**\n * @constructor\n * @alias module:echarts/data/List\n *\n * @param {Array.<string|Object>} dimensions\n * For example, ['someDimName', {name: 'someDimName', type: 'someDimType'}, ...].\n * Dimensions should be concrete names like x, y, z, lng, lat, angle, radius\n * @param {module:echarts/model/Model} hostModel\n */\nvar List = function (dimensions, hostModel) {\n\n dimensions = dimensions || ['x', 'y'];\n\n var dimensionInfos = {};\n var dimensionNames = [];\n for (var i = 0; i < dimensions.length; i++) {\n var dimensionName;\n var dimensionInfo = {};\n if (typeof dimensions[i] === 'string') {\n dimensionName = dimensions[i];\n dimensionInfo = {\n name: dimensionName,\n coordDim: dimensionName,\n coordDimIndex: 0,\n stackable: false,\n // Type can be 'float', 'int', 'number'\n // Default is number, Precision of float may not enough\n type: 'number'\n };\n }\n else {\n dimensionInfo = dimensions[i];\n dimensionName = dimensionInfo.name;\n dimensionInfo.type = dimensionInfo.type || 'number';\n if (!dimensionInfo.coordDim) {\n dimensionInfo.coordDim = dimensionName;\n dimensionInfo.coordDimIndex = 0;\n }\n }\n dimensionInfo.otherDims = dimensionInfo.otherDims || {};\n dimensionNames.push(dimensionName);\n dimensionInfos[dimensionName] = dimensionInfo;\n }\n\n /**\n * @readOnly\n * @type {Array.<string>}\n */\n this.dimensions = dimensionNames;\n\n /**\n * Infomation of each data dimension, like data type.\n * @type {Object}\n */\n this._dimensionInfos = dimensionInfos;\n\n /**\n * @type {module:echarts/model/Model}\n */\n this.hostModel = hostModel;\n\n /**\n * @type {module:echarts/model/Model}\n */\n this.dataType;\n\n /**\n * Indices stores the indices of data subset after filtered.\n * This data subset will be used in chart.\n * @type {Array.<number>}\n * @readOnly\n */\n this.indices = [];\n\n /**\n * Data storage\n * @type {Object.<key, TypedArray|Array>}\n * @private\n */\n this._storage = {};\n\n /**\n * @type {Array.<string>}\n */\n this._nameList = [];\n /**\n * @type {Array.<string>}\n */\n this._idList = [];\n\n /**\n * Models of data option is stored sparse for optimizing memory cost\n * @type {Array.<module:echarts/model/Model>}\n * @private\n */\n this._optionModels = [];\n\n /**\n * @param {module:echarts/data/List}\n */\n this.stackedOn = null;\n\n /**\n * Global visual properties after visual coding\n * @type {Object}\n * @private\n */\n this._visual = {};\n\n /**\n * Globel layout properties.\n * @type {Object}\n * @private\n */\n this._layout = {};\n\n /**\n * Item visual properties after visual coding\n * @type {Array.<Object>}\n * @private\n */\n this._itemVisuals = [];\n\n /**\n * Item layout properties after layout\n * @type {Array.<Object>}\n * @private\n */\n this._itemLayouts = [];\n\n /**\n * Graphic elemnents\n * @type {Array.<module:zrender/Element>}\n * @private\n */\n this._graphicEls = [];\n\n /**\n * @type {Array.<Array|Object>}\n * @private\n */\n this._rawData;\n\n /**\n * @type {Object}\n * @private\n */\n this._extent;\n};\n\nvar listProto = List.prototype;\n\nlistProto.type = 'list';\n\n/**\n * If each data item has it's own option\n * @type {boolean}\n */\nlistProto.hasItemOption = true;\n\n/**\n * Get dimension name\n * @param {string|number} dim\n * Dimension can be concrete names like x, y, z, lng, lat, angle, radius\n * Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius'\n * @return {string} Concrete dim name.\n */\nlistProto.getDimension = function (dim) {\n if (!isNaN(dim)) {\n dim = this.dimensions[dim] || dim;\n }\n return dim;\n};\n\n/**\n * Get type and stackable info of particular dimension\n * @param {string|number} dim\n * Dimension can be concrete names like x, y, z, lng, lat, angle, radius\n * Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius'\n */\nlistProto.getDimensionInfo = function (dim) {\n return zrUtil.clone(this._dimensionInfos[this.getDimension(dim)]);\n};\n\n/**\n * Initialize from data\n * @param {Array.<Object|number|Array>} data\n * @param {Array.<string>} [nameList]\n * @param {Function} [dimValueGetter] (dataItem, dimName, dataIndex, dimIndex) => number\n */\nlistProto.initData = function (data, nameList, dimValueGetter) {\n data = data || [];\n\n var isDataArray = zrUtil.isArray(data);\n if (isDataArray) {\n data = new DefaultDataProvider(data);\n }\n if (__DEV__) {\n if (!isDataArray && (typeof data.getItem != 'function' || typeof data.count != 'function')) {\n throw new Error('Inavlid data provider.');\n }\n }\n\n this._rawData = data;\n\n // Clear\n var storage = this._storage = {};\n var indices = this.indices = [];\n\n var dimensions = this.dimensions;\n var dimensionInfoMap = this._dimensionInfos;\n\n var size = data.count();\n\n var idList = [];\n var nameRepeatCount = {};\n var nameDimIdx;\n\n nameList = nameList || [];\n\n // Init storage\n for (var i = 0; i < dimensions.length; i++) {\n var dimInfo = dimensionInfoMap[dimensions[i]];\n dimInfo.otherDims.itemName === 0 && (nameDimIdx = i);\n var DataCtor = dataCtors[dimInfo.type];\n storage[dimensions[i]] = new DataCtor(size);\n }\n\n var self = this;\n if (!dimValueGetter) {\n self.hasItemOption = false;\n }\n // Default dim value getter\n dimValueGetter = dimValueGetter || function (dataItem, dimName, dataIndex, dimIndex) {\n var value = modelUtil.getDataItemValue(dataItem);\n // If any dataItem is like { value: 10 }\n if (modelUtil.isDataItemOption(dataItem)) {\n self.hasItemOption = true;\n }\n return modelUtil.converDataValue(\n (value instanceof Array)\n ? value[dimIndex]\n // If value is a single number or something else not array.\n : value,\n dimensionInfoMap[dimName]\n );\n };\n\n for (var i = 0; i < size; i++) {\n // NOTICE: Try not to write things into dataItem\n var dataItem = data.getItem(i);\n // Each data item is value\n // [1, 2]\n // 2\n // Bar chart, line chart which uses category axis\n // only gives the 'y' value. 'x' value is the indices of cateogry\n // Use a tempValue to normalize the value to be a (x, y) value\n\n // Store the data by dimensions\n for (var k = 0; k < dimensions.length; k++) {\n var dim = dimensions[k];\n var dimStorage = storage[dim];\n // PENDING NULL is empty or zero\n dimStorage[i] = dimValueGetter(dataItem, dim, i, k);\n }\n\n indices.push(i);\n }\n\n // Use the name in option and create id\n for (var i = 0; i < size; i++) {\n var dataItem = data.getItem(i);\n if (!nameList[i] && dataItem) {\n if (dataItem.name != null) {\n nameList[i] = dataItem.name;\n }\n else if (nameDimIdx != null) {\n nameList[i] = storage[dimensions[nameDimIdx]][i];\n }\n }\n var name = nameList[i] || '';\n // Try using the id in option\n var id = dataItem && dataItem.id;\n\n if (!id && name) {\n // Use name as id and add counter to avoid same name\n nameRepeatCount[name] = nameRepeatCount[name] || 0;\n id = name;\n if (nameRepeatCount[name] > 0) {\n id += '__ec__' + nameRepeatCount[name];\n }\n nameRepeatCount[name]++;\n }\n id && (idList[i] = id);\n }\n\n this._nameList = nameList;\n this._idList = idList;\n};\n\n/**\n * @return {number}\n */\nlistProto.count = function () {\n return this.indices.length;\n};\n\n/**\n * Get value. Return NaN if idx is out of range.\n * @param {string} dim Dim must be concrete name.\n * @param {number} idx\n * @param {boolean} stack\n * @return {number}\n */\nlistProto.get = function (dim, idx, stack) {\n var storage = this._storage;\n var dataIndex = this.indices[idx];\n\n // If value not exists\n if (dataIndex == null || !storage[dim]) {\n return NaN;\n }\n\n var value = storage[dim][dataIndex];\n // FIXME ordinal data type is not stackable\n if (stack) {\n var dimensionInfo = this._dimensionInfos[dim];\n if (dimensionInfo && dimensionInfo.stackable) {\n var stackedOn = this.stackedOn;\n while (stackedOn) {\n // Get no stacked data of stacked on\n var stackedValue = stackedOn.get(dim, idx);\n // Considering positive stack, negative stack and empty data\n if ((value >= 0 && stackedValue > 0) // Positive stack\n || (value <= 0 && stackedValue < 0) // Negative stack\n ) {\n value += stackedValue;\n }\n stackedOn = stackedOn.stackedOn;\n }\n }\n }\n return value;\n};\n\n/**\n * Get value for multi dimensions.\n * @param {Array.<string>} [dimensions] If ignored, using all dimensions.\n * @param {number} idx\n * @param {boolean} stack\n * @return {number}\n */\nlistProto.getValues = function (dimensions, idx, stack) {\n var values = [];\n\n if (!zrUtil.isArray(dimensions)) {\n stack = idx;\n idx = dimensions;\n dimensions = this.dimensions;\n }\n\n for (var i = 0, len = dimensions.length; i < len; i++) {\n values.push(this.get(dimensions[i], idx, stack));\n }\n\n return values;\n};\n\n/**\n * If value is NaN. Inlcuding '-'\n * @param {string} dim\n * @param {number} idx\n * @return {number}\n */\nlistProto.hasValue = function (idx) {\n var dimensions = this.dimensions;\n var dimensionInfos = this._dimensionInfos;\n for (var i = 0, len = dimensions.length; i < len; i++) {\n if (\n // Ordinal type can be string or number\n dimensionInfos[dimensions[i]].type !== 'ordinal'\n && isNaN(this.get(dimensions[i], idx))\n ) {\n return false;\n }\n }\n return true;\n};\n\n/**\n * Get extent of data in one dimension\n * @param {string} dim\n * @param {boolean} stack\n * @param {Function} filter\n */\nlistProto.getDataExtent = function (dim, stack, filter) {\n dim = this.getDimension(dim);\n var dimData = this._storage[dim];\n var dimInfo = this.getDimensionInfo(dim);\n stack = (dimInfo && dimInfo.stackable) && stack;\n var dimExtent = (this._extent || (this._extent = {}))[dim + (!!stack)];\n var value;\n if (dimExtent) {\n return dimExtent;\n }\n // var dimInfo = this._dimensionInfos[dim];\n if (dimData) {\n var min = Infinity;\n var max = -Infinity;\n // var isOrdinal = dimInfo.type === 'ordinal';\n for (var i = 0, len = this.count(); i < len; i++) {\n value = this.get(dim, i, stack);\n // FIXME\n // if (isOrdinal && typeof value === 'string') {\n // value = zrUtil.indexOf(dimData, value);\n // }\n if (!filter || filter(value, dim, i)) {\n value < min && (min = value);\n value > max && (max = value);\n }\n }\n return (this._extent[dim + !!stack] = [min, max]);\n }\n else {\n return [Infinity, -Infinity];\n }\n};\n\n/**\n * Get sum of data in one dimension\n * @param {string} dim\n * @param {boolean} stack\n */\nlistProto.getSum = function (dim, stack) {\n var dimData = this._storage[dim];\n var sum = 0;\n if (dimData) {\n for (var i = 0, len = this.count(); i < len; i++) {\n var value = this.get(dim, i, stack);\n if (!isNaN(value)) {\n sum += value;\n }\n }\n }\n return sum;\n};\n\n/**\n * Retreive the index with given value\n * @param {number} idx\n * @param {number} value\n * @return {number}\n */\n// FIXME Precision of float value\nlistProto.indexOf = function (dim, value) {\n var storage = this._storage;\n var dimData = storage[dim];\n var indices = this.indices;\n\n if (dimData) {\n for (var i = 0, len = indices.length; i < len; i++) {\n var rawIndex = indices[i];\n if (dimData[rawIndex] === value) {\n return i;\n }\n }\n }\n return -1;\n};\n\n/**\n * Retreive the index with given name\n * @param {number} idx\n * @param {number} name\n * @return {number}\n */\nlistProto.indexOfName = function (name) {\n var indices = this.indices;\n var nameList = this._nameList;\n\n for (var i = 0, len = indices.length; i < len; i++) {\n var rawIndex = indices[i];\n if (nameList[rawIndex] === name) {\n return i;\n }\n }\n\n return -1;\n};\n\n/**\n * Retreive the index with given raw data index\n * @param {number} idx\n * @param {number} name\n * @return {number}\n */\nlistProto.indexOfRawIndex = function (rawIndex) {\n // Indices are ascending\n var indices = this.indices;\n\n // If rawIndex === dataIndex\n var rawDataIndex = indices[rawIndex];\n if (rawDataIndex != null && rawDataIndex === rawIndex) {\n return rawIndex;\n }\n\n var left = 0;\n var right = indices.length - 1;\n while (left <= right) {\n var mid = (left + right) / 2 | 0;\n if (indices[mid] < rawIndex) {\n left = mid + 1;\n }\n else if (indices[mid] > rawIndex) {\n right = mid - 1;\n }\n else {\n return mid;\n }\n }\n return -1;\n};\n\n/**\n * Retreive the index of nearest value\n * @param {string} dim\n * @param {number} value\n * @param {boolean} stack If given value is after stacked\n * @param {number} [maxDistance=Infinity]\n * @return {Array.<number>} Considere multiple points has the same value.\n */\nlistProto.indicesOfNearest = function (dim, value, stack, maxDistance) {\n var storage = this._storage;\n var dimData = storage[dim];\n var nearestIndices = [];\n\n if (!dimData) {\n return nearestIndices;\n }\n\n if (maxDistance == null) {\n maxDistance = Infinity;\n }\n\n var minDist = Number.MAX_VALUE;\n var minDiff = -1;\n for (var i = 0, len = this.count(); i < len; i++) {\n var diff = value - this.get(dim, i, stack);\n var dist = Math.abs(diff);\n if (diff <= maxDistance && dist <= minDist) {\n // For the case of two data are same on xAxis, which has sequence data.\n // Show the nearest index\n // https://github.com/ecomfe/echarts/issues/2869\n if (dist < minDist || (diff >= 0 && minDiff < 0)) {\n minDist = dist;\n minDiff = diff;\n nearestIndices.length = 0;\n }\n nearestIndices.push(i);\n }\n }\n return nearestIndices;\n};\n\n/**\n * Get raw data index\n * @param {number} idx\n * @return {number}\n */\nlistProto.getRawIndex = function (idx) {\n var rawIdx = this.indices[idx];\n return rawIdx == null ? -1 : rawIdx;\n};\n\n/**\n * Get raw data item\n * @param {number} idx\n * @return {number}\n */\nlistProto.getRawDataItem = function (idx) {\n return this._rawData.getItem(this.getRawIndex(idx));\n};\n\n/**\n * @param {number} idx\n * @param {boolean} [notDefaultIdx=false]\n * @return {string}\n */\nlistProto.getName = function (idx) {\n return this._nameList[this.indices[idx]] || '';\n};\n\n/**\n * @param {number} idx\n * @param {boolean} [notDefaultIdx=false]\n * @return {string}\n */\nlistProto.getId = function (idx) {\n return this._idList[this.indices[idx]] || (this.getRawIndex(idx) + '');\n};\n\n\nfunction normalizeDimensions(dimensions) {\n if (!zrUtil.isArray(dimensions)) {\n dimensions = [dimensions];\n }\n return dimensions;\n}\n\n/**\n * Data iteration\n * @param {string|Array.<string>}\n * @param {Function} cb\n * @param {boolean} [stack=false]\n * @param {*} [context=this]\n *\n * @example\n * list.each('x', function (x, idx) {});\n * list.each(['x', 'y'], function (x, y, idx) {});\n * list.each(function (idx) {})\n */\nlistProto.each = function (dims, cb, stack, context) {\n if (typeof dims === 'function') {\n context = stack;\n stack = cb;\n cb = dims;\n dims = [];\n }\n\n dims = zrUtil.map(normalizeDimensions(dims), this.getDimension, this);\n\n var value = [];\n var dimSize = dims.length;\n var indices = this.indices;\n\n context = context || this;\n\n for (var i = 0; i < indices.length; i++) {\n // Simple optimization\n switch (dimSize) {\n case 0:\n cb.call(context, i);\n break;\n case 1:\n cb.call(context, this.get(dims[0], i, stack), i);\n break;\n case 2:\n cb.call(context, this.get(dims[0], i, stack), this.get(dims[1], i, stack), i);\n break;\n default:\n for (var k = 0; k < dimSize; k++) {\n value[k] = this.get(dims[k], i, stack);\n }\n // Index\n value[k] = i;\n cb.apply(context, value);\n }\n }\n};\n\n/**\n * Data filter\n * @param {string|Array.<string>}\n * @param {Function} cb\n * @param {boolean} [stack=false]\n * @param {*} [context=this]\n */\nlistProto.filterSelf = function (dimensions, cb, stack, context) {\n if (typeof dimensions === 'function') {\n context = stack;\n stack = cb;\n cb = dimensions;\n dimensions = [];\n }\n\n dimensions = zrUtil.map(\n normalizeDimensions(dimensions), this.getDimension, this\n );\n\n var newIndices = [];\n var value = [];\n var dimSize = dimensions.length;\n var indices = this.indices;\n\n context = context || this;\n\n for (var i = 0; i < indices.length; i++) {\n var keep;\n // Simple optimization\n if (!dimSize) {\n keep = cb.call(context, i);\n }\n else if (dimSize === 1) {\n keep = cb.call(\n context, this.get(dimensions[0], i, stack), i\n );\n }\n else {\n for (var k = 0; k < dimSize; k++) {\n value[k] = this.get(dimensions[k], i, stack);\n }\n value[k] = i;\n keep = cb.apply(context, value);\n }\n if (keep) {\n newIndices.push(indices[i]);\n }\n }\n\n this.indices = newIndices;\n\n // Reset data extent\n this._extent = {};\n\n return this;\n};\n\n/**\n * Data mapping to a plain array\n * @param {string|Array.<string>} [dimensions]\n * @param {Function} cb\n * @param {boolean} [stack=false]\n * @param {*} [context=this]\n * @return {Array}\n */\nlistProto.mapArray = function (dimensions, cb, stack, context) {\n if (typeof dimensions === 'function') {\n context = stack;\n stack = cb;\n cb = dimensions;\n dimensions = [];\n }\n\n var result = [];\n this.each(dimensions, function () {\n result.push(cb && cb.apply(this, arguments));\n }, stack, context);\n return result;\n};\n\nfunction cloneListForMapAndSample(original, excludeDimensions) {\n var allDimensions = original.dimensions;\n var list = new List(\n zrUtil.map(allDimensions, original.getDimensionInfo, original),\n original.hostModel\n );\n // FIXME If needs stackedOn, value may already been stacked\n transferProperties(list, original);\n\n var storage = list._storage = {};\n var originalStorage = original._storage;\n // Init storage\n for (var i = 0; i < allDimensions.length; i++) {\n var dim = allDimensions[i];\n var dimStore = originalStorage[dim];\n if (zrUtil.indexOf(excludeDimensions, dim) >= 0) {\n storage[dim] = new dimStore.constructor(\n originalStorage[dim].length\n );\n }\n else {\n // Direct reference for other dimensions\n storage[dim] = originalStorage[dim];\n }\n }\n return list;\n}\n\n/**\n * Data mapping to a new List with given dimensions\n * @param {string|Array.<string>} dimensions\n * @param {Function} cb\n * @param {boolean} [stack=false]\n * @param {*} [context=this]\n * @return {Array}\n */\nlistProto.map = function (dimensions, cb, stack, context) {\n dimensions = zrUtil.map(\n normalizeDimensions(dimensions), this.getDimension, this\n );\n\n var list = cloneListForMapAndSample(this, dimensions);\n // Following properties are all immutable.\n // So we can reference to the same value\n var indices = list.indices = this.indices;\n\n var storage = list._storage;\n\n var tmpRetValue = [];\n this.each(dimensions, function () {\n var idx = arguments[arguments.length - 1];\n var retValue = cb && cb.apply(this, arguments);\n if (retValue != null) {\n // a number\n if (typeof retValue === 'number') {\n tmpRetValue[0] = retValue;\n retValue = tmpRetValue;\n }\n for (var i = 0; i < retValue.length; i++) {\n var dim = dimensions[i];\n var dimStore = storage[dim];\n var rawIdx = indices[idx];\n if (dimStore) {\n dimStore[rawIdx] = retValue[i];\n }\n }\n }\n }, stack, context);\n\n return list;\n};\n\n/**\n * Large data down sampling on given dimension\n * @param {string} dimension\n * @param {number} rate\n * @param {Function} sampleValue\n * @param {Function} sampleIndex Sample index for name and id\n */\nlistProto.downSample = function (dimension, rate, sampleValue, sampleIndex) {\n var list = cloneListForMapAndSample(this, [dimension]);\n var storage = this._storage;\n var targetStorage = list._storage;\n\n var originalIndices = this.indices;\n var indices = list.indices = [];\n\n var frameValues = [];\n var frameIndices = [];\n var frameSize = Math.floor(1 / rate);\n\n var dimStore = targetStorage[dimension];\n var len = this.count();\n // Copy data from original data\n for (var i = 0; i < storage[dimension].length; i++) {\n targetStorage[dimension][i] = storage[dimension][i];\n }\n for (var i = 0; i < len; i += frameSize) {\n // Last frame\n if (frameSize > len - i) {\n frameSize = len - i;\n frameValues.length = frameSize;\n }\n for (var k = 0; k < frameSize; k++) {\n var idx = originalIndices[i + k];\n frameValues[k] = dimStore[idx];\n frameIndices[k] = idx;\n }\n var value = sampleValue(frameValues);\n var idx = frameIndices[sampleIndex(frameValues, value) || 0];\n // Only write value on the filtered data\n dimStore[idx] = value;\n indices.push(idx);\n }\n\n return list;\n};\n\n/**\n * Get model of one data item.\n *\n * @param {number} idx\n */\n// FIXME Model proxy ?\nlistProto.getItemModel = function (idx) {\n var hostModel = this.hostModel;\n idx = this.indices[idx];\n return new Model(this._rawData.getItem(idx), hostModel, hostModel && hostModel.ecModel);\n};\n\n/**\n * Create a data differ\n * @param {module:echarts/data/List} otherList\n * @return {module:echarts/data/DataDiffer}\n */\nlistProto.diff = function (otherList) {\n var idList = this._idList;\n var otherIdList = otherList && otherList._idList;\n var val;\n // Use prefix to avoid index to be the same as otherIdList[idx],\n // which will cause weird udpate animation.\n var prefix = 'e\\0\\0';\n\n return new DataDiffer(\n otherList ? otherList.indices : [],\n this.indices,\n function (idx) {\n return (val = otherIdList[idx]) != null ? val : prefix + idx;\n },\n function (idx) {\n return (val = idList[idx]) != null ? val : prefix + idx;\n }\n );\n};\n/**\n * Get visual property.\n * @param {string} key\n */\nlistProto.getVisual = function (key) {\n var visual = this._visual;\n return visual && visual[key];\n};\n\n/**\n * Set visual property\n * @param {string|Object} key\n * @param {*} [value]\n *\n * @example\n * setVisual('color', color);\n * setVisual({\n * 'color': color\n * });\n */\nlistProto.setVisual = function (key, val) {\n if (isObject(key)) {\n for (var name in key) {\n if (key.hasOwnProperty(name)) {\n this.setVisual(name, key[name]);\n }\n }\n return;\n }\n this._visual = this._visual || {};\n this._visual[key] = val;\n};\n\n/**\n * Set layout property.\n * @param {string|Object} key\n * @param {*} [val]\n */\nlistProto.setLayout = function (key, val) {\n if (isObject(key)) {\n for (var name in key) {\n if (key.hasOwnProperty(name)) {\n this.setLayout(name, key[name]);\n }\n }\n return;\n }\n this._layout[key] = val;\n};\n\n/**\n * Get layout property.\n * @param {string} key.\n * @return {*}\n */\nlistProto.getLayout = function (key) {\n return this._layout[key];\n};\n\n/**\n * Get layout of single data item\n * @param {number} idx\n */\nlistProto.getItemLayout = function (idx) {\n return this._itemLayouts[idx];\n};\n\n/**\n * Set layout of single data item\n * @param {number} idx\n * @param {Object} layout\n * @param {boolean=} [merge=false]\n */\nlistProto.setItemLayout = function (idx, layout, merge) {\n this._itemLayouts[idx] = merge\n ? zrUtil.extend(this._itemLayouts[idx] || {}, layout)\n : layout;\n};\n\n/**\n * Clear all layout of single data item\n */\nlistProto.clearItemLayouts = function () {\n this._itemLayouts.length = 0;\n};\n\n/**\n * Get visual property of single data item\n * @param {number} idx\n * @param {string} key\n * @param {boolean} [ignoreParent=false]\n */\nlistProto.getItemVisual = function (idx, key, ignoreParent) {\n var itemVisual = this._itemVisuals[idx];\n var val = itemVisual && itemVisual[key];\n if (val == null && !ignoreParent) {\n // Use global visual property\n return this.getVisual(key);\n }\n return val;\n};\n\n/**\n * Set visual property of single data item\n *\n * @param {number} idx\n * @param {string|Object} key\n * @param {*} [value]\n *\n * @example\n * setItemVisual(0, 'color', color);\n * setItemVisual(0, {\n * 'color': color\n * });\n */\nlistProto.setItemVisual = function (idx, key, value) {\n var itemVisual = this._itemVisuals[idx] || {};\n this._itemVisuals[idx] = itemVisual;\n\n if (isObject(key)) {\n for (var name in key) {\n if (key.hasOwnProperty(name)) {\n itemVisual[name] = key[name];\n }\n }\n return;\n }\n itemVisual[key] = value;\n};\n\n/**\n * Clear itemVisuals and list visual.\n */\nlistProto.clearAllVisual = function () {\n this._visual = {};\n this._itemVisuals = [];\n};\n\nvar setItemDataAndSeriesIndex = function (child) {\n child.seriesIndex = this.seriesIndex;\n child.dataIndex = this.dataIndex;\n child.dataType = this.dataType;\n};\n/**\n * Set graphic element relative to data. It can be set as null\n * @param {number} idx\n * @param {module:zrender/Element} [el]\n */\nlistProto.setItemGraphicEl = function (idx, el) {\n var hostModel = this.hostModel;\n\n if (el) {\n // Add data index and series index for indexing the data by element\n // Useful in tooltip\n el.dataIndex = idx;\n el.dataType = this.dataType;\n el.seriesIndex = hostModel && hostModel.seriesIndex;\n if (el.type === 'group') {\n el.traverse(setItemDataAndSeriesIndex, el);\n }\n }\n\n this._graphicEls[idx] = el;\n};\n\n/**\n * @param {number} idx\n * @return {module:zrender/Element}\n */\nlistProto.getItemGraphicEl = function (idx) {\n return this._graphicEls[idx];\n};\n\n/**\n * @param {Function} cb\n * @param {*} context\n */\nlistProto.eachItemGraphicEl = function (cb, context) {\n zrUtil.each(this._graphicEls, function (el, idx) {\n if (el) {\n cb && cb.call(context, el, idx);\n }\n });\n};\n\n/**\n * Shallow clone a new list except visual and layout properties, and graph elements.\n * New list only change the indices.\n */\nlistProto.cloneShallow = function () {\n var dimensionInfoList = zrUtil.map(this.dimensions, this.getDimensionInfo, this);\n var list = new List(dimensionInfoList, this.hostModel);\n\n // FIXME\n list._storage = this._storage;\n\n transferProperties(list, this);\n\n\n // Clone will not change the data extent and indices\n list.indices = this.indices.slice();\n\n if (this._extent) {\n list._extent = zrUtil.extend({}, this._extent);\n }\n\n return list;\n};\n\n/**\n * Wrap some method to add more feature\n * @param {string} methodName\n * @param {Function} injectFunction\n */\nlistProto.wrapMethod = function (methodName, injectFunction) {\n var originalMethod = this[methodName];\n if (typeof originalMethod !== 'function') {\n return;\n }\n this.__wrappedMethods = this.__wrappedMethods || [];\n this.__wrappedMethods.push(methodName);\n this[methodName] = function () {\n var res = originalMethod.apply(this, arguments);\n return injectFunction.apply(this, [res].concat(zrUtil.slice(arguments)));\n };\n};\n\n// Methods that create a new list based on this list should be listed here.\n// Notice that those method should `RETURN` the new list.\nlistProto.TRANSFERABLE_METHODS = ['cloneShallow', 'downSample', 'map'];\n// Methods that change indices of this list should be listed here.\nlistProto.CHANGABLE_METHODS = ['filterSelf'];\n\nexport default List;","/**\n * Complete dimensions by data (guess dimension).\n */\n\nimport * as zrUtil from 'zrender/src/core/util';\nimport {normalizeToArray} from '../../util/model';\n\nvar each = zrUtil.each;\nvar isString = zrUtil.isString;\nvar defaults = zrUtil.defaults;\n\nvar OTHER_DIMS = {tooltip: 1, label: 1, itemName: 1};\n\n/**\n * Complete the dimensions array, by user defined `dimension` and `encode`,\n * and guessing from the data structure.\n * If no 'value' dimension specified, the first no-named dimension will be\n * named as 'value'.\n *\n * @param {Array.<string>} sysDims Necessary dimensions, like ['x', 'y'], which\n * provides not only dim template, but also default order.\n * `name` of each item provides default coord name.\n * [{dimsDef: []}, ...] can be specified to give names.\n * @param {Array} data Data list. [[1, 2, 3], [2, 3, 4]].\n * @param {Object} [opt]\n * @param {Array.<Object|string>} [opt.dimsDef] option.series.dimensions User defined dimensions\n * For example: ['asdf', {name, type}, ...].\n * @param {Object} [opt.encodeDef] option.series.encode {x: 2, y: [3, 1], tooltip: [1, 2], label: 3}\n * @param {string} [opt.extraPrefix] Prefix of name when filling the left dimensions.\n * @param {string} [opt.extraFromZero] If specified, extra dim names will be:\n * extraPrefix + 0, extraPrefix + extraBaseIndex + 1 ...\n * If not specified, extra dim names will be:\n * extraPrefix, extraPrefix + 0, extraPrefix + 1 ...\n * @param {number} [opt.dimCount] If not specified, guess by the first data item.\n * @return {Array.<Object>} [{\n * name: string mandatory,\n * coordDim: string mandatory,\n * coordDimIndex: number mandatory,\n * type: string optional,\n * tooltipName: string optional,\n * otherDims: {\n * tooltip: number optional,\n * label: number optional\n * },\n * isExtraCoord: boolean true or undefined.\n * other props ...\n * }]\n */\nfunction completeDimensions(sysDims, data, opt) {\n data = data || [];\n opt = opt || {};\n sysDims = (sysDims || []).slice();\n var dimsDef = (opt.dimsDef || []).slice();\n var encodeDef = zrUtil.createHashMap(opt.encodeDef);\n var dataDimNameMap = zrUtil.createHashMap();\n var coordDimNameMap = zrUtil.createHashMap();\n // var valueCandidate;\n var result = [];\n\n var dimCount = opt.dimCount;\n if (dimCount == null) {\n var value0 = retrieveValue(data[0]);\n dimCount = Math.max(\n zrUtil.isArray(value0) && value0.length || 1,\n sysDims.length,\n dimsDef.length\n );\n each(sysDims, function (sysDimItem) {\n var sysDimItemDimsDef = sysDimItem.dimsDef;\n sysDimItemDimsDef && (dimCount = Math.max(dimCount, sysDimItemDimsDef.length));\n });\n }\n\n // Apply user defined dims (`name` and `type`) and init result.\n for (var i = 0; i < dimCount; i++) {\n var dimDefItem = isString(dimsDef[i]) ? {name: dimsDef[i]} : (dimsDef[i] || {});\n var userDimName = dimDefItem.name;\n var resultItem = result[i] = {otherDims: {}};\n // Name will be applied later for avoiding duplication.\n if (userDimName != null && dataDimNameMap.get(userDimName) == null) {\n // Only if `series.dimensions` is defined in option, tooltipName\n // will be set, and dimension will be diplayed vertically in\n // tooltip by default.\n resultItem.name = resultItem.tooltipName = userDimName;\n dataDimNameMap.set(userDimName, i);\n }\n dimDefItem.type != null && (resultItem.type = dimDefItem.type);\n }\n\n // Set `coordDim` and `coordDimIndex` by `encodeDef` and normalize `encodeDef`.\n encodeDef.each(function (dataDims, coordDim) {\n dataDims = encodeDef.set(coordDim, normalizeToArray(dataDims).slice());\n each(dataDims, function (resultDimIdx, coordDimIndex) {\n // The input resultDimIdx can be dim name or index.\n isString(resultDimIdx) && (resultDimIdx = dataDimNameMap.get(resultDimIdx));\n if (resultDimIdx != null && resultDimIdx < dimCount) {\n dataDims[coordDimIndex] = resultDimIdx;\n applyDim(result[resultDimIdx], coordDim, coordDimIndex);\n }\n });\n });\n\n // Apply templetes and default order from `sysDims`.\n var availDimIdx = 0;\n each(sysDims, function (sysDimItem, sysDimIndex) {\n var coordDim;\n var sysDimItem;\n var sysDimItemDimsDef;\n var sysDimItemOtherDims;\n if (isString(sysDimItem)) {\n coordDim = sysDimItem;\n sysDimItem = {};\n }\n else {\n coordDim = sysDimItem.name;\n sysDimItem = zrUtil.clone(sysDimItem);\n // `coordDimIndex` should not be set directly.\n sysDimItemDimsDef = sysDimItem.dimsDef;\n sysDimItemOtherDims = sysDimItem.otherDims;\n sysDimItem.name = sysDimItem.coordDim = sysDimItem.coordDimIndex\n = sysDimItem.dimsDef = sysDimItem.otherDims = null;\n }\n\n var dataDims = normalizeToArray(encodeDef.get(coordDim));\n // dimensions provides default dim sequences.\n if (!dataDims.length) {\n for (var i = 0; i < (sysDimItemDimsDef && sysDimItemDimsDef.length || 1); i++) {\n while (availDimIdx < result.length && result[availDimIdx].coordDim != null) {\n availDimIdx++;\n }\n availDimIdx < result.length && dataDims.push(availDimIdx++);\n }\n }\n // Apply templates.\n each(dataDims, function (resultDimIdx, coordDimIndex) {\n var resultItem = result[resultDimIdx];\n applyDim(defaults(resultItem, sysDimItem), coordDim, coordDimIndex);\n if (resultItem.name == null && sysDimItemDimsDef) {\n resultItem.name = resultItem.tooltipName = sysDimItemDimsDef[coordDimIndex];\n }\n sysDimItemOtherDims && defaults(resultItem.otherDims, sysDimItemOtherDims);\n });\n });\n\n // Make sure the first extra dim is 'value'.\n var extra = opt.extraPrefix || 'value';\n\n // Set dim `name` and other `coordDim` and other props.\n for (var resultDimIdx = 0; resultDimIdx < dimCount; resultDimIdx++) {\n var resultItem = result[resultDimIdx] = result[resultDimIdx] || {};\n var coordDim = resultItem.coordDim;\n\n coordDim == null && (\n resultItem.coordDim = genName(extra, coordDimNameMap, opt.extraFromZero),\n resultItem.coordDimIndex = 0,\n resultItem.isExtraCoord = true\n );\n\n resultItem.name == null && (resultItem.name = genName(\n resultItem.coordDim,\n dataDimNameMap\n ));\n\n resultItem.type == null && guessOrdinal(data, resultDimIdx)\n && (resultItem.type = 'ordinal');\n }\n\n return result;\n\n function applyDim(resultItem, coordDim, coordDimIndex) {\n if (OTHER_DIMS[coordDim]) {\n resultItem.otherDims[coordDim] = coordDimIndex;\n }\n else {\n resultItem.coordDim = coordDim;\n resultItem.coordDimIndex = coordDimIndex;\n coordDimNameMap.set(coordDim, true);\n }\n }\n\n function genName(name, map, fromZero) {\n if (fromZero || map.get(name) != null) {\n var i = 0;\n while (map.get(name + i) != null) {\n i++;\n }\n name += i;\n }\n map.set(name, true);\n return name;\n }\n}\n\n// The rule should not be complex, otherwise user might not\n// be able to known where the data is wrong.\nvar guessOrdinal = completeDimensions.guessOrdinal = function (data, dimIndex) {\n for (var i = 0, len = data.length; i < len; i++) {\n var value = retrieveValue(data[i]);\n\n if (!zrUtil.isArray(value)) {\n return false;\n }\n\n var value = value[dimIndex];\n // Consider usage convenience, '1', '2' will be treated as \"number\".\n // `isFinit('')` get `true`.\n if (value != null && isFinite(value) && value !== '') {\n return false;\n }\n else if (isString(value) && value !== '-') {\n return true;\n }\n }\n return false;\n};\n\nfunction retrieveValue(o) {\n return zrUtil.isArray(o) ? o : zrUtil.isObject(o) ? o.value: o;\n}\n\nexport default completeDimensions;\n","import {__DEV__} from '../../config';\nimport * as zrUtil from 'zrender/src/core/util';\nimport List from '../../data/List';\nimport completeDimensions from '../../data/helper/completeDimensions';\nimport {getDataItemValue, converDataValue, isDataItemOption} from '../../util/model';\nimport CoordinateSystem from '../../CoordinateSystem';\n\nfunction firstDataNotNull(data) {\n var i = 0;\n while (i < data.length && data[i] == null) {\n i++;\n }\n return data[i];\n}\nfunction ifNeedCompleteOrdinalData(data) {\n var sampleItem = firstDataNotNull(data);\n return sampleItem != null\n && !zrUtil.isArray(getDataItemValue(sampleItem));\n}\n\n/**\n * Helper function to create a list from option data\n */\nfunction createListFromArray(data, seriesModel, ecModel) {\n // If data is undefined\n data = data || [];\n\n if (__DEV__) {\n if (!zrUtil.isArray(data)) {\n throw new Error('Invalid data.');\n }\n }\n\n var coordSysName = seriesModel.get('coordinateSystem');\n var creator = creators[coordSysName];\n var registeredCoordSys = CoordinateSystem.get(coordSysName);\n var completeDimOpt = {\n encodeDef: seriesModel.get('encode'),\n dimsDef: seriesModel.get('dimensions')\n };\n\n // FIXME\n var axesInfo = creator && creator(data, seriesModel, ecModel, completeDimOpt);\n var dimensions = axesInfo && axesInfo.dimensions;\n if (!dimensions) {\n // Get dimensions from registered coordinate system\n dimensions = (registeredCoordSys && (\n registeredCoordSys.getDimensionsInfo\n ? registeredCoordSys.getDimensionsInfo()\n : registeredCoordSys.dimensions.slice()\n )) || ['x', 'y'];\n dimensions = completeDimensions(dimensions, data, completeDimOpt);\n }\n\n var categoryIndex = axesInfo ? axesInfo.categoryIndex : -1;\n\n var list = new List(dimensions, seriesModel);\n\n var nameList = createNameList(axesInfo, data);\n\n var categories = {};\n var dimValueGetter = (categoryIndex >= 0 && ifNeedCompleteOrdinalData(data))\n ? function (itemOpt, dimName, dataIndex, dimIndex) {\n // If any dataItem is like { value: 10 }\n if (isDataItemOption(itemOpt)) {\n list.hasItemOption = true;\n }\n\n // Use dataIndex as ordinal value in categoryAxis\n return dimIndex === categoryIndex\n ? dataIndex\n : converDataValue(getDataItemValue(itemOpt), dimensions[dimIndex]);\n }\n : function (itemOpt, dimName, dataIndex, dimIndex) {\n var value = getDataItemValue(itemOpt);\n var val = converDataValue(value && value[dimIndex], dimensions[dimIndex]);\n // If any dataItem is like { value: 10 }\n if (isDataItemOption(itemOpt)) {\n list.hasItemOption = true;\n }\n\n var categoryAxesModels = axesInfo && axesInfo.categoryAxesModels;\n if (categoryAxesModels && categoryAxesModels[dimName]) {\n // If given value is a category string\n if (typeof val === 'string') {\n // Lazy get categories\n categories[dimName] = categories[dimName]\n || categoryAxesModels[dimName].getCategories();\n val = zrUtil.indexOf(categories[dimName], val);\n if (val < 0 && !isNaN(val)) {\n // In case some one write '1', '2' istead of 1, 2\n val = +val;\n }\n }\n }\n return val;\n };\n\n list.hasItemOption = false;\n list.initData(data, nameList, dimValueGetter);\n\n return list;\n}\n\nfunction isStackable(axisType) {\n return axisType !== 'category' && axisType !== 'time';\n}\n\nfunction getDimTypeByAxis(axisType) {\n return axisType === 'category'\n ? 'ordinal'\n : axisType === 'time'\n ? 'time'\n : 'float';\n}\n\n/**\n * Creaters for each coord system.\n */\nvar creators = {\n\n cartesian2d: function (data, seriesModel, ecModel, completeDimOpt) {\n\n var axesModels = zrUtil.map(['xAxis', 'yAxis'], function (name) {\n return ecModel.queryComponents({\n mainType: name,\n index: seriesModel.get(name + 'Index'),\n id: seriesModel.get(name + 'Id')\n })[0];\n });\n var xAxisModel = axesModels[0];\n var yAxisModel = axesModels[1];\n\n if (__DEV__) {\n if (!xAxisModel) {\n throw new Error('xAxis \"' + zrUtil.retrieve(\n seriesModel.get('xAxisIndex'),\n seriesModel.get('xAxisId'),\n 0\n ) + '\" not found');\n }\n if (!yAxisModel) {\n throw new Error('yAxis \"' + zrUtil.retrieve(\n seriesModel.get('xAxisIndex'),\n seriesModel.get('yAxisId'),\n 0\n ) + '\" not found');\n }\n }\n\n var xAxisType = xAxisModel.get('type');\n var yAxisType = yAxisModel.get('type');\n\n var dimensions = [\n {\n name: 'x',\n type: getDimTypeByAxis(xAxisType),\n stackable: isStackable(xAxisType)\n },\n {\n name: 'y',\n // If two category axes\n type: getDimTypeByAxis(yAxisType),\n stackable: isStackable(yAxisType)\n }\n ];\n\n var isXAxisCateogry = xAxisType === 'category';\n var isYAxisCategory = yAxisType === 'category';\n\n dimensions = completeDimensions(dimensions, data, completeDimOpt);\n\n var categoryAxesModels = {};\n if (isXAxisCateogry) {\n categoryAxesModels.x = xAxisModel;\n }\n if (isYAxisCategory) {\n categoryAxesModels.y = yAxisModel;\n }\n return {\n dimensions: dimensions,\n categoryIndex: isXAxisCateogry ? 0 : (isYAxisCategory ? 1 : -1),\n categoryAxesModels: categoryAxesModels\n };\n },\n\n singleAxis: function (data, seriesModel, ecModel, completeDimOpt) {\n\n var singleAxisModel = ecModel.queryComponents({\n mainType: 'singleAxis',\n index: seriesModel.get('singleAxisIndex'),\n id: seriesModel.get('singleAxisId')\n })[0];\n\n if (__DEV__) {\n if (!singleAxisModel) {\n throw new Error('singleAxis should be specified.');\n }\n }\n\n var singleAxisType = singleAxisModel.get('type');\n var isCategory = singleAxisType === 'category';\n\n var dimensions = [{\n name: 'single',\n type: getDimTypeByAxis(singleAxisType),\n stackable: isStackable(singleAxisType)\n }];\n\n dimensions = completeDimensions(dimensions, data, completeDimOpt);\n\n var categoryAxesModels = {};\n if (isCategory) {\n categoryAxesModels.single = singleAxisModel;\n }\n\n return {\n dimensions: dimensions,\n categoryIndex: isCategory ? 0 : -1,\n categoryAxesModels: categoryAxesModels\n };\n },\n\n polar: function (data, seriesModel, ecModel, completeDimOpt) {\n var polarModel = ecModel.queryComponents({\n mainType: 'polar',\n index: seriesModel.get('polarIndex'),\n id: seriesModel.get('polarId')\n })[0];\n\n var angleAxisModel = polarModel.findAxisModel('angleAxis');\n var radiusAxisModel = polarModel.findAxisModel('radiusAxis');\n\n if (__DEV__) {\n if (!angleAxisModel) {\n throw new Error('angleAxis option not found');\n }\n if (!radiusAxisModel) {\n throw new Error('radiusAxis option not found');\n }\n }\n\n var radiusAxisType = radiusAxisModel.get('type');\n var angleAxisType = angleAxisModel.get('type');\n\n var dimensions = [\n {\n name: 'radius',\n type: getDimTypeByAxis(radiusAxisType),\n stackable: isStackable(radiusAxisType)\n },\n {\n name: 'angle',\n type: getDimTypeByAxis(angleAxisType),\n stackable: isStackable(angleAxisType)\n }\n ];\n var isAngleAxisCateogry = angleAxisType === 'category';\n var isRadiusAxisCateogry = radiusAxisType === 'category';\n\n dimensions = completeDimensions(dimensions, data, completeDimOpt);\n\n var categoryAxesModels = {};\n if (isRadiusAxisCateogry) {\n categoryAxesModels.radius = radiusAxisModel;\n }\n if (isAngleAxisCateogry) {\n categoryAxesModels.angle = angleAxisModel;\n }\n return {\n dimensions: dimensions,\n categoryIndex: isAngleAxisCateogry ? 1 : (isRadiusAxisCateogry ? 0 : -1),\n categoryAxesModels: categoryAxesModels\n };\n },\n\n geo: function (data, seriesModel, ecModel, completeDimOpt) {\n // TODO Region\n // 多个散点图系列在同一个地区的时候\n return {\n dimensions: completeDimensions([\n {name: 'lng'},\n {name: 'lat'}\n ], data, completeDimOpt)\n };\n }\n};\n\nfunction createNameList(result, data) {\n var nameList = [];\n\n var categoryDim = result && result.dimensions[result.categoryIndex];\n var categoryAxisModel;\n if (categoryDim) {\n categoryAxisModel = result.categoryAxesModels[categoryDim.name];\n }\n\n if (categoryAxisModel) {\n // FIXME Two category axis\n var categories = categoryAxisModel.getCategories();\n if (categories) {\n var dataLen = data.length;\n // Ordered data is given explicitly like\n // [[3, 0.2], [1, 0.3], [2, 0.15]]\n // or given scatter data,\n // pick the category\n if (zrUtil.isArray(data[0]) && data[0].length > 1) {\n nameList = [];\n for (var i = 0; i < dataLen; i++) {\n nameList[i] = categories[data[i][result.categoryIndex || 0]];\n }\n }\n else {\n nameList = categories.slice(0);\n }\n }\n }\n\n return nameList;\n}\n\nexport default createListFromArray;\n","/**\n * // Scale class management\n * @module echarts/scale/Scale\n */\n\nimport * as clazzUtil from '../util/clazz';\n\n/**\n * @param {Object} [setting]\n */\nfunction Scale(setting) {\n this._setting = setting || {};\n\n /**\n * Extent\n * @type {Array.<number>}\n * @protected\n */\n this._extent = [Infinity, -Infinity];\n\n /**\n * Step is calculated in adjustExtent\n * @type {Array.<number>}\n * @protected\n */\n this._interval = 0;\n\n this.init && this.init.apply(this, arguments);\n}\n\n/**\n * Parse input val to valid inner number.\n * @param {*} val\n * @return {number}\n */\nScale.prototype.parse = function (val) {\n // Notice: This would be a trap here, If the implementation\n // of this method depends on extent, and this method is used\n // before extent set (like in dataZoom), it would be wrong.\n // Nevertheless, parse does not depend on extent generally.\n return val;\n};\n\nScale.prototype.getSetting = function (name) {\n return this._setting[name];\n};\n\nScale.prototype.contain = function (val) {\n var extent = this._extent;\n return val >= extent[0] && val <= extent[1];\n};\n\n/**\n * Normalize value to linear [0, 1], return 0.5 if extent span is 0\n * @param {number} val\n * @return {number}\n */\nScale.prototype.normalize = function (val) {\n var extent = this._extent;\n if (extent[1] === extent[0]) {\n return 0.5;\n }\n return (val - extent[0]) / (extent[1] - extent[0]);\n};\n\n/**\n * Scale normalized value\n * @param {number} val\n * @return {number}\n */\nScale.prototype.scale = function (val) {\n var extent = this._extent;\n return val * (extent[1] - extent[0]) + extent[0];\n};\n\n/**\n * Set extent from data\n * @param {Array.<number>} other\n */\nScale.prototype.unionExtent = function (other) {\n var extent = this._extent;\n other[0] < extent[0] && (extent[0] = other[0]);\n other[1] > extent[1] && (extent[1] = other[1]);\n // not setExtent because in log axis it may transformed to power\n // this.setExtent(extent[0], extent[1]);\n};\n\n/**\n * Set extent from data\n * @param {module:echarts/data/List} data\n * @param {string} dim\n */\nScale.prototype.unionExtentFromData = function (data, dim) {\n this.unionExtent(data.getDataExtent(dim, true));\n};\n\n/**\n * Get extent\n * @return {Array.<number>}\n */\nScale.prototype.getExtent = function () {\n return this._extent.slice();\n};\n\n/**\n * Set extent\n * @param {number} start\n * @param {number} end\n */\nScale.prototype.setExtent = function (start, end) {\n var thisExtent = this._extent;\n if (!isNaN(start)) {\n thisExtent[0] = start;\n }\n if (!isNaN(end)) {\n thisExtent[1] = end;\n }\n};\n\n/**\n * @return {Array.<string>}\n */\nScale.prototype.getTicksLabels = function () {\n var labels = [];\n var ticks = this.getTicks();\n for (var i = 0; i < ticks.length; i++) {\n labels.push(this.getLabel(ticks[i]));\n }\n return labels;\n};\n\n/**\n * When axis extent depends on data and no data exists,\n * axis ticks should not be drawn, which is named 'blank'.\n */\nScale.prototype.isBlank = function () {\n return this._isBlank;\n},\n\n/**\n * When axis extent depends on data and no data exists,\n * axis ticks should not be drawn, which is named 'blank'.\n */\nScale.prototype.setBlank = function (isBlank) {\n this._isBlank = isBlank;\n};\n\n\nclazzUtil.enableClassExtend(Scale);\nclazzUtil.enableClassManagement(Scale, {\n registerWhenExtend: true\n});\n\nexport default Scale;","/**\n * Linear continuous scale\n * @module echarts/coord/scale/Ordinal\n *\n * http://en.wikipedia.org/wiki/Level_of_measurement\n */\n\n// FIXME only one data\n\nimport * as zrUtil from 'zrender/src/core/util';\nimport Scale from './Scale';\n\nvar scaleProto = Scale.prototype;\n\nvar OrdinalScale = Scale.extend({\n\n type: 'ordinal',\n\n init: function (data, extent) {\n this._data = data;\n this._extent = extent || [0, data.length - 1];\n },\n\n parse: function (val) {\n return typeof val === 'string'\n ? zrUtil.indexOf(this._data, val)\n // val might be float.\n : Math.round(val);\n },\n\n contain: function (rank) {\n rank = this.parse(rank);\n return scaleProto.contain.call(this, rank)\n && this._data[rank] != null;\n },\n\n /**\n * Normalize given rank or name to linear [0, 1]\n * @param {number|string} [val]\n * @return {number}\n */\n normalize: function (val) {\n return scaleProto.normalize.call(this, this.parse(val));\n },\n\n scale: function (val) {\n return Math.round(scaleProto.scale.call(this, val));\n },\n\n /**\n * @return {Array}\n */\n getTicks: function () {\n var ticks = [];\n var extent = this._extent;\n var rank = extent[0];\n\n while (rank <= extent[1]) {\n ticks.push(rank);\n rank++;\n }\n\n return ticks;\n },\n\n /**\n * Get item on rank n\n * @param {number} n\n * @return {string}\n */\n getLabel: function (n) {\n return this._data[n];\n },\n\n /**\n * @return {number}\n */\n count: function () {\n return this._extent[1] - this._extent[0] + 1;\n },\n\n /**\n * @override\n */\n unionExtentFromData: function (data, dim) {\n this.unionExtent(data.getDataExtent(dim, false));\n },\n\n niceTicks: zrUtil.noop,\n niceExtent: zrUtil.noop\n});\n\n/**\n * @return {module:echarts/scale/Time}\n */\nOrdinalScale.create = function () {\n return new OrdinalScale();\n};\n\nexport default OrdinalScale;","/**\n * For testable.\n */\n\nimport * as numberUtil from '../util/number';\n\nvar roundNumber = numberUtil.round;\n\n/**\n * @param {Array.<number>} extent Both extent[0] and extent[1] should be valid number.\n * Should be extent[0] < extent[1].\n * @param {number} splitNumber splitNumber should be >= 1.\n * @param {number} [minInterval]\n * @param {number} [maxInterval]\n * @return {Object} {interval, intervalPrecision, niceTickExtent}\n */\nexport function intervalScaleNiceTicks(extent, splitNumber, minInterval, maxInterval) {\n var result = {};\n var span = extent[1] - extent[0];\n\n var interval = result.interval = numberUtil.nice(span / splitNumber, true);\n if (minInterval != null && interval < minInterval) {\n interval = result.interval = minInterval;\n }\n if (maxInterval != null && interval > maxInterval) {\n interval = result.interval = maxInterval;\n }\n // Tow more digital for tick.\n var precision = result.intervalPrecision = getIntervalPrecision(interval);\n // Niced extent inside original extent\n var niceTickExtent = result.niceTickExtent = [\n roundNumber(Math.ceil(extent[0] / interval) * interval, precision),\n roundNumber(Math.floor(extent[1] / interval) * interval, precision)\n ];\n\n fixExtent(niceTickExtent, extent);\n\n return result;\n}\n\n/**\n * @param {number} interval\n * @return {number} interval precision\n */\nexport function getIntervalPrecision(interval) {\n // Tow more digital for tick.\n return numberUtil.getPrecisionSafe(interval) + 2;\n}\n\nfunction clamp(niceTickExtent, idx, extent) {\n niceTickExtent[idx] = Math.max(Math.min(niceTickExtent[idx], extent[1]), extent[0]);\n}\n\n// In some cases (e.g., splitNumber is 1), niceTickExtent may be out of extent.\nexport function fixExtent(niceTickExtent, extent) {\n !isFinite(niceTickExtent[0]) && (niceTickExtent[0] = extent[0]);\n !isFinite(niceTickExtent[1]) && (niceTickExtent[1] = extent[1]);\n clamp(niceTickExtent, 0, extent);\n clamp(niceTickExtent, 1, extent);\n if (niceTickExtent[0] > niceTickExtent[1]) {\n niceTickExtent[0] = niceTickExtent[1];\n }\n}\n\nexport function intervalScaleGetTicks(interval, extent, niceTickExtent, intervalPrecision) {\n var ticks = [];\n\n // If interval is 0, return [];\n if (!interval) {\n return ticks;\n }\n\n // Consider this case: using dataZoom toolbox, zoom and zoom.\n var safeLimit = 10000;\n\n if (extent[0] < niceTickExtent[0]) {\n ticks.push(extent[0]);\n }\n var tick = niceTickExtent[0];\n\n while (tick <= niceTickExtent[1]) {\n ticks.push(tick);\n // Avoid rounding error\n tick = roundNumber(tick + interval, intervalPrecision);\n if (tick === ticks[ticks.length - 1]) {\n // Consider out of safe float point, e.g.,\n // -3711126.9907707 + 2e-10 === -3711126.9907707\n break;\n }\n if (ticks.length > safeLimit) {\n return [];\n }\n }\n // Consider this case: the last item of ticks is smaller\n // than niceTickExtent[1] and niceTickExtent[1] === extent[1].\n if (extent[1] > (ticks.length ? ticks[ticks.length - 1] : niceTickExtent[1])) {\n ticks.push(extent[1]);\n }\n\n return ticks;\n}\n","/**\n * Interval scale\n * @module echarts/scale/Interval\n */\n\n\nimport * as numberUtil from '../util/number';\nimport * as formatUtil from '../util/format';\nimport Scale from './Scale';\nimport * as helper from './helper';\n\nvar roundNumber = numberUtil.round;\n\n/**\n * @alias module:echarts/coord/scale/Interval\n * @constructor\n */\nvar IntervalScale = Scale.extend({\n\n type: 'interval',\n\n _interval: 0,\n\n _intervalPrecision: 2,\n\n setExtent: function (start, end) {\n var thisExtent = this._extent;\n //start,end may be a Number like '25',so...\n if (!isNaN(start)) {\n thisExtent[0] = parseFloat(start);\n }\n if (!isNaN(end)) {\n thisExtent[1] = parseFloat(end);\n }\n },\n\n unionExtent: function (other) {\n var extent = this._extent;\n other[0] < extent[0] && (extent[0] = other[0]);\n other[1] > extent[1] && (extent[1] = other[1]);\n\n // unionExtent may called by it's sub classes\n IntervalScale.prototype.setExtent.call(this, extent[0], extent[1]);\n },\n /**\n * Get interval\n */\n getInterval: function () {\n return this._interval;\n },\n\n /**\n * Set interval\n */\n setInterval: function (interval) {\n this._interval = interval;\n // Dropped auto calculated niceExtent and use user setted extent\n // We assume user wan't to set both interval, min, max to get a better result\n this._niceExtent = this._extent.slice();\n\n this._intervalPrecision = helper.getIntervalPrecision(interval);\n },\n\n /**\n * @return {Array.<number>}\n */\n getTicks: function () {\n return helper.intervalScaleGetTicks(\n this._interval, this._extent, this._niceExtent, this._intervalPrecision\n );\n },\n\n /**\n * @return {Array.<string>}\n */\n getTicksLabels: function () {\n var labels = [];\n var ticks = this.getTicks();\n for (var i = 0; i < ticks.length; i++) {\n labels.push(this.getLabel(ticks[i]));\n }\n return labels;\n },\n\n /**\n * @param {number} data\n * @param {Object} [opt]\n * @param {number|string} [opt.precision] If 'auto', use nice presision.\n * @param {boolean} [opt.pad] returns 1.50 but not 1.5 if precision is 2.\n * @return {string}\n */\n getLabel: function (data, opt) {\n if (data == null) {\n return '';\n }\n\n var precision = opt && opt.precision;\n\n if (precision == null) {\n precision = numberUtil.getPrecisionSafe(data) || 0;\n }\n else if (precision === 'auto') {\n // Should be more precise then tick.\n precision = this._intervalPrecision;\n }\n\n // (1) If `precision` is set, 12.005 should be display as '12.00500'.\n // (2) Use roundNumber (toFixed) to avoid scientific notation like '3.5e-7'.\n data = roundNumber(data, precision, true);\n\n return formatUtil.addCommas(data);\n },\n\n /**\n * Update interval and extent of intervals for nice ticks\n *\n * @param {number} [splitNumber = 5] Desired number of ticks\n * @param {number} [minInterval]\n * @param {number} [maxInterval]\n */\n niceTicks: function (splitNumber, minInterval, maxInterval) {\n splitNumber = splitNumber || 5;\n var extent = this._extent;\n var span = extent[1] - extent[0];\n if (!isFinite(span)) {\n return;\n }\n // User may set axis min 0 and data are all negative\n // FIXME If it needs to reverse ?\n if (span < 0) {\n span = -span;\n extent.reverse();\n }\n\n var result = helper.intervalScaleNiceTicks(\n extent, splitNumber, minInterval, maxInterval\n );\n\n this._intervalPrecision = result.intervalPrecision;\n this._interval = result.interval;\n this._niceExtent = result.niceTickExtent;\n },\n\n /**\n * Nice extent.\n * @param {Object} opt\n * @param {number} [opt.splitNumber = 5] Given approx tick number\n * @param {boolean} [opt.fixMin=false]\n * @param {boolean} [opt.fixMax=false]\n * @param {boolean} [opt.minInterval]\n * @param {boolean} [opt.maxInterval]\n */\n niceExtent: function (opt) {\n var extent = this._extent;\n // If extent start and end are same, expand them\n if (extent[0] === extent[1]) {\n if (extent[0] !== 0) {\n // Expand extent\n var expandSize = extent[0];\n // In the fowllowing case\n // Axis has been fixed max 100\n // Plus data are all 100 and axis extent are [100, 100].\n // Extend to the both side will cause expanded max is larger than fixed max.\n // So only expand to the smaller side.\n if (!opt.fixMax) {\n extent[1] += expandSize / 2;\n extent[0] -= expandSize / 2;\n }\n else {\n extent[0] -= expandSize / 2;\n }\n }\n else {\n extent[1] = 1;\n }\n }\n var span = extent[1] - extent[0];\n // If there are no data and extent are [Infinity, -Infinity]\n if (!isFinite(span)) {\n extent[0] = 0;\n extent[1] = 1;\n }\n\n this.niceTicks(opt.splitNumber, opt.minInterval, opt.maxInterval);\n\n // var extent = this._extent;\n var interval = this._interval;\n\n if (!opt.fixMin) {\n extent[0] = roundNumber(Math.floor(extent[0] / interval) * interval);\n }\n if (!opt.fixMax) {\n extent[1] = roundNumber(Math.ceil(extent[1] / interval) * interval);\n }\n }\n});\n\n/**\n * @return {module:echarts/scale/Time}\n */\nIntervalScale.create = function () {\n return new IntervalScale();\n};\n\nexport default IntervalScale;","// [About UTC and local time zone]:\n// In most cases, `number.parseDate` will treat input data string as local time\n// (except time zone is specified in time string). And `format.formateTime` returns\n// local time by default. option.useUTC is false by default. This design have\n// concidered these common case:\n// (1) Time that is persistent in server is in UTC, but it is needed to be diplayed\n// in local time by default.\n// (2) By default, the input data string (e.g., '2011-01-02') should be displayed\n// as its original time, without any time difference.\n\nimport * as zrUtil from 'zrender/src/core/util';\nimport * as numberUtil from '../util/number';\nimport * as formatUtil from '../util/format';\nimport * as scaleHelper from './helper';\nimport IntervalScale from './Interval';\n\nvar intervalScaleProto = IntervalScale.prototype;\n\nvar mathCeil = Math.ceil;\nvar mathFloor = Math.floor;\nvar ONE_SECOND = 1000;\nvar ONE_MINUTE = ONE_SECOND * 60;\nvar ONE_HOUR = ONE_MINUTE * 60;\nvar ONE_DAY = ONE_HOUR * 24;\n\n// FIXME 公用?\nvar bisect = function (a, x, lo, hi) {\n while (lo < hi) {\n var mid = lo + hi >>> 1;\n if (a[mid][1] < x) {\n lo = mid + 1;\n }\n else {\n hi = mid;\n }\n }\n return lo;\n};\n\n/**\n * @alias module:echarts/coord/scale/Time\n * @constructor\n */\nvar TimeScale = IntervalScale.extend({\n type: 'time',\n\n /**\n * @override\n */\n getLabel: function (val) {\n var stepLvl = this._stepLvl;\n\n var date = new Date(val);\n\n return formatUtil.formatTime(stepLvl[0], date, this.getSetting('useUTC'));\n },\n\n /**\n * @override\n */\n niceExtent: function (opt) {\n var extent = this._extent;\n // If extent start and end are same, expand them\n if (extent[0] === extent[1]) {\n // Expand extent\n extent[0] -= ONE_DAY;\n extent[1] += ONE_DAY;\n }\n // If there are no data and extent are [Infinity, -Infinity]\n if (extent[1] === -Infinity && extent[0] === Infinity) {\n var d = new Date();\n extent[1] = +new Date(d.getFullYear(), d.getMonth(), d.getDate());\n extent[0] = extent[1] - ONE_DAY;\n }\n\n this.niceTicks(opt.splitNumber, opt.minInterval, opt.maxInterval);\n\n // var extent = this._extent;\n var interval = this._interval;\n\n if (!opt.fixMin) {\n extent[0] = numberUtil.round(mathFloor(extent[0] / interval) * interval);\n }\n if (!opt.fixMax) {\n extent[1] = numberUtil.round(mathCeil(extent[1] / interval) * interval);\n }\n },\n\n /**\n * @override\n */\n niceTicks: function (approxTickNum, minInterval, maxInterval) {\n approxTickNum = approxTickNum || 10;\n\n var extent = this._extent;\n var span = extent[1] - extent[0];\n var approxInterval = span / approxTickNum;\n\n if (minInterval != null && approxInterval < minInterval) {\n approxInterval = minInterval;\n }\n if (maxInterval != null && approxInterval > maxInterval) {\n approxInterval = maxInterval;\n }\n\n var scaleLevelsLen = scaleLevels.length;\n var idx = bisect(scaleLevels, approxInterval, 0, scaleLevelsLen);\n\n var level = scaleLevels[Math.min(idx, scaleLevelsLen - 1)];\n var interval = level[1];\n // Same with interval scale if span is much larger than 1 year\n if (level[0] === 'year') {\n var yearSpan = span / interval;\n\n // From \"Nice Numbers for Graph Labels\" of Graphic Gems\n // var niceYearSpan = numberUtil.nice(yearSpan, false);\n var yearStep = numberUtil.nice(yearSpan / approxTickNum, true);\n\n interval *= yearStep;\n }\n\n var timezoneOffset = this.getSetting('useUTC')\n ? 0 : (new Date(+extent[0] || +extent[1])).getTimezoneOffset() * 60 * 1000;\n var niceExtent = [\n Math.round(mathCeil((extent[0] - timezoneOffset) / interval) * interval + timezoneOffset),\n Math.round(mathFloor((extent[1] - timezoneOffset) / interval) * interval + timezoneOffset)\n ];\n\n scaleHelper.fixExtent(niceExtent, extent);\n\n this._stepLvl = level;\n // Interval will be used in getTicks\n this._interval = interval;\n this._niceExtent = niceExtent;\n },\n\n parse: function (val) {\n // val might be float.\n return +numberUtil.parseDate(val);\n }\n});\n\nzrUtil.each(['contain', 'normalize'], function (methodName) {\n TimeScale.prototype[methodName] = function (val) {\n return intervalScaleProto[methodName].call(this, this.parse(val));\n };\n});\n\n// Steps from d3\nvar scaleLevels = [\n // Format interval\n ['hh:mm:ss', ONE_SECOND], // 1s\n ['hh:mm:ss', ONE_SECOND * 5], // 5s\n ['hh:mm:ss', ONE_SECOND * 10], // 10s\n ['hh:mm:ss', ONE_SECOND * 15], // 15s\n ['hh:mm:ss', ONE_SECOND * 30], // 30s\n ['hh:mm\\nMM-dd', ONE_MINUTE], // 1m\n ['hh:mm\\nMM-dd', ONE_MINUTE * 5], // 5m\n ['hh:mm\\nMM-dd', ONE_MINUTE * 10], // 10m\n ['hh:mm\\nMM-dd', ONE_MINUTE * 15], // 15m\n ['hh:mm\\nMM-dd', ONE_MINUTE * 30], // 30m\n ['hh:mm\\nMM-dd', ONE_HOUR], // 1h\n ['hh:mm\\nMM-dd', ONE_HOUR * 2], // 2h\n ['hh:mm\\nMM-dd', ONE_HOUR * 6], // 6h\n ['hh:mm\\nMM-dd', ONE_HOUR * 12], // 12h\n ['MM-dd\\nyyyy', ONE_DAY], // 1d\n ['MM-dd\\nyyyy', ONE_DAY * 2], // 2d\n ['MM-dd\\nyyyy', ONE_DAY * 3], // 3d\n ['MM-dd\\nyyyy', ONE_DAY * 4], // 4d\n ['MM-dd\\nyyyy', ONE_DAY * 5], // 5d\n ['MM-dd\\nyyyy', ONE_DAY * 6], // 6d\n ['week', ONE_DAY * 7], // 7d\n ['MM-dd\\nyyyy', ONE_DAY * 10], // 10d\n ['week', ONE_DAY * 14], // 2w\n ['week', ONE_DAY * 21], // 3w\n ['month', ONE_DAY * 31], // 1M\n ['week', ONE_DAY * 42], // 6w\n ['month', ONE_DAY * 62], // 2M\n ['week', ONE_DAY * 42], // 10w\n ['quarter', ONE_DAY * 380 / 4], // 3M\n ['month', ONE_DAY * 31 * 4], // 4M\n ['month', ONE_DAY * 31 * 5], // 5M\n ['half-year', ONE_DAY * 380 / 2], // 6M\n ['month', ONE_DAY * 31 * 8], // 8M\n ['month', ONE_DAY * 31 * 10], // 10M\n ['year', ONE_DAY * 380] // 1Y\n];\n\n/**\n * @param {module:echarts/model/Model}\n * @return {module:echarts/scale/Time}\n */\nTimeScale.create = function (model) {\n return new TimeScale({useUTC: model.ecModel.get('useUTC')});\n};\n\nexport default TimeScale;","/**\n * Log scale\n * @module echarts/scale/Log\n */\n\nimport * as zrUtil from 'zrender/src/core/util';\nimport Scale from './Scale';\nimport * as numberUtil from '../util/number';\n\n// Use some method of IntervalScale\nimport IntervalScale from './Interval';\n\nvar scaleProto = Scale.prototype;\nvar intervalScaleProto = IntervalScale.prototype;\n\nvar getPrecisionSafe = numberUtil.getPrecisionSafe;\nvar roundingErrorFix = numberUtil.round;\n\nvar mathFloor = Math.floor;\nvar mathCeil = Math.ceil;\nvar mathPow = Math.pow;\n\nvar mathLog = Math.log;\n\nvar LogScale = Scale.extend({\n\n type: 'log',\n\n base: 10,\n\n $constructor: function () {\n Scale.apply(this, arguments);\n this._originalScale = new IntervalScale();\n },\n\n /**\n * @return {Array.<number>}\n */\n getTicks: function () {\n var originalScale = this._originalScale;\n var extent = this._extent;\n var originalExtent = originalScale.getExtent();\n\n return zrUtil.map(intervalScaleProto.getTicks.call(this), function (val) {\n var powVal = numberUtil.round(mathPow(this.base, val));\n\n // Fix #4158\n powVal = (val === extent[0] && originalScale.__fixMin)\n ? fixRoundingError(powVal, originalExtent[0])\n : powVal;\n powVal = (val === extent[1] && originalScale.__fixMax)\n ? fixRoundingError(powVal, originalExtent[1])\n : powVal;\n\n return powVal;\n }, this);\n },\n\n /**\n * @param {number} val\n * @return {string}\n */\n getLabel: intervalScaleProto.getLabel,\n\n /**\n * @param {number} val\n * @return {number}\n */\n scale: function (val) {\n val = scaleProto.scale.call(this, val);\n return mathPow(this.base, val);\n },\n\n /**\n * @param {number} start\n * @param {number} end\n */\n setExtent: function (start, end) {\n var base = this.base;\n start = mathLog(start) / mathLog(base);\n end = mathLog(end) / mathLog(base);\n intervalScaleProto.setExtent.call(this, start, end);\n },\n\n /**\n * @return {number} end\n */\n getExtent: function () {\n var base = this.base;\n var extent = scaleProto.getExtent.call(this);\n extent[0] = mathPow(base, extent[0]);\n extent[1] = mathPow(base, extent[1]);\n\n // Fix #4158\n var originalScale = this._originalScale;\n var originalExtent = originalScale.getExtent();\n originalScale.__fixMin && (extent[0] = fixRoundingError(extent[0], originalExtent[0]));\n originalScale.__fixMax && (extent[1] = fixRoundingError(extent[1], originalExtent[1]));\n\n return extent;\n },\n\n /**\n * @param {Array.<number>} extent\n */\n unionExtent: function (extent) {\n this._originalScale.unionExtent(extent);\n\n var base = this.base;\n extent[0] = mathLog(extent[0]) / mathLog(base);\n extent[1] = mathLog(extent[1]) / mathLog(base);\n scaleProto.unionExtent.call(this, extent);\n },\n\n /**\n * @override\n */\n unionExtentFromData: function (data, dim) {\n this.unionExtent(data.getDataExtent(dim, true, function (val) {\n return val > 0;\n }));\n },\n\n /**\n * Update interval and extent of intervals for nice ticks\n * @param {number} [approxTickNum = 10] Given approx tick number\n */\n niceTicks: function (approxTickNum) {\n approxTickNum = approxTickNum || 10;\n var extent = this._extent;\n var span = extent[1] - extent[0];\n if (span === Infinity || span <= 0) {\n return;\n }\n\n var interval = numberUtil.quantity(span);\n var err = approxTickNum / span * interval;\n\n // Filter ticks to get closer to the desired count.\n if (err <= 0.5) {\n interval *= 10;\n }\n\n // Interval should be integer\n while (!isNaN(interval) && Math.abs(interval) < 1 && Math.abs(interval) > 0) {\n interval *= 10;\n }\n\n var niceExtent = [\n numberUtil.round(mathCeil(extent[0] / interval) * interval),\n numberUtil.round(mathFloor(extent[1] / interval) * interval)\n ];\n\n this._interval = interval;\n this._niceExtent = niceExtent;\n },\n\n /**\n * Nice extent.\n * @override\n */\n niceExtent: function (opt) {\n intervalScaleProto.niceExtent.call(this, opt);\n\n var originalScale = this._originalScale;\n originalScale.__fixMin = opt.fixMin;\n originalScale.__fixMax = opt.fixMax;\n }\n\n});\n\nzrUtil.each(['contain', 'normalize'], function (methodName) {\n LogScale.prototype[methodName] = function (val) {\n val = mathLog(val) / mathLog(this.base);\n return scaleProto[methodName].call(this, val);\n };\n});\n\nLogScale.create = function () {\n return new LogScale();\n};\n\nfunction fixRoundingError(val, originalVal) {\n return roundingErrorFix(val, getPrecisionSafe(originalVal));\n}\n\nexport default LogScale;","import {__DEV__} from '../config';\nimport * as zrUtil from 'zrender/src/core/util';\nimport * as textContain from 'zrender/src/contain/text';\nimport OrdinalScale from '../scale/Ordinal';\nimport IntervalScale from '../scale/Interval';\nimport Scale from '../scale/Scale';\nimport * as numberUtil from '../util/number';\n\nimport '../scale/Time';\nimport '../scale/Log';\n\n/**\n * Get axis scale extent before niced.\n * Item of returned array can only be number (including Infinity and NaN).\n */\nexport function getScaleExtent(scale, model) {\n var scaleType = scale.type;\n\n var min = model.getMin();\n var max = model.getMax();\n var fixMin = min != null;\n var fixMax = max != null;\n var originalExtent = scale.getExtent();\n\n var axisDataLen;\n var boundaryGap;\n var span;\n if (scaleType === 'ordinal') {\n axisDataLen = (model.get('data') || []).length;\n }\n else {\n boundaryGap = model.get('boundaryGap');\n if (!zrUtil.isArray(boundaryGap)) {\n boundaryGap = [boundaryGap || 0, boundaryGap || 0];\n }\n if (typeof boundaryGap[0] === 'boolean') {\n if (__DEV__) {\n console.warn('Boolean type for boundaryGap is only '\n + 'allowed for ordinal axis. Please use string in '\n + 'percentage instead, e.g., \"20%\". Currently, '\n + 'boundaryGap is set to be 0.');\n }\n boundaryGap = [0, 0];\n }\n boundaryGap[0] = numberUtil.parsePercent(boundaryGap[0], 1);\n boundaryGap[1] = numberUtil.parsePercent(boundaryGap[1], 1);\n span = (originalExtent[1] - originalExtent[0])\n || Math.abs(originalExtent[0]);\n }\n\n // Notice: When min/max is not set (that is, when there are null/undefined,\n // which is the most common case), these cases should be ensured:\n // (1) For 'ordinal', show all axis.data.\n // (2) For others:\n // + `boundaryGap` is applied (if min/max set, boundaryGap is\n // disabled).\n // + If `needCrossZero`, min/max should be zero, otherwise, min/max should\n // be the result that originalExtent enlarged by boundaryGap.\n // (3) If no data, it should be ensured that `scale.setBlank` is set.\n\n // FIXME\n // (1) When min/max is 'dataMin' or 'dataMax', should boundaryGap be able to used?\n // (2) When `needCrossZero` and all data is positive/negative, should it be ensured\n // that the results processed by boundaryGap are positive/negative?\n\n if (min == null) {\n min = scaleType === 'ordinal'\n ? (axisDataLen ? 0 : NaN)\n : originalExtent[0] - boundaryGap[0] * span;\n }\n if (max == null) {\n max = scaleType === 'ordinal'\n ? (axisDataLen ? axisDataLen - 1 : NaN)\n : originalExtent[1] + boundaryGap[1] * span;\n }\n\n if (min === 'dataMin') {\n min = originalExtent[0];\n }\n else if (typeof min === 'function') {\n min = min({\n min: originalExtent[0],\n max: originalExtent[1]\n });\n }\n\n if (max === 'dataMax') {\n max = originalExtent[1];\n }\n else if (typeof max === 'function') {\n max = max({\n min: originalExtent[0],\n max: originalExtent[1]\n });\n }\n\n (min == null || !isFinite(min)) && (min = NaN);\n (max == null || !isFinite(max)) && (max = NaN);\n\n scale.setBlank(zrUtil.eqNaN(min) || zrUtil.eqNaN(max));\n\n // Evaluate if axis needs cross zero\n if (model.getNeedCrossZero()) {\n // Axis is over zero and min is not set\n if (min > 0 && max > 0 && !fixMin) {\n min = 0;\n }\n // Axis is under zero and max is not set\n if (min < 0 && max < 0 && !fixMax) {\n max = 0;\n }\n }\n\n return [min, max];\n}\n\nexport function niceScaleExtent(scale, model) {\n var extent = getScaleExtent(scale, model);\n var fixMin = model.getMin() != null;\n var fixMax = model.getMax() != null;\n var splitNumber = model.get('splitNumber');\n\n if (scale.type === 'log') {\n scale.base = model.get('logBase');\n }\n\n var scaleType = scale.type;\n scale.setExtent(extent[0], extent[1]);\n scale.niceExtent({\n splitNumber: splitNumber,\n fixMin: fixMin,\n fixMax: fixMax,\n minInterval: (scaleType === 'interval' || scaleType === 'time')\n ? model.get('minInterval') : null,\n maxInterval: (scaleType === 'interval' || scaleType === 'time')\n ? model.get('maxInterval') : null\n });\n\n // If some one specified the min, max. And the default calculated interval\n // is not good enough. He can specify the interval. It is often appeared\n // in angle axis with angle 0 - 360. Interval calculated in interval scale is hard\n // to be 60.\n // FIXME\n var interval = model.get('interval');\n if (interval != null) {\n scale.setInterval && scale.setInterval(interval);\n }\n}\n\n/**\n * @param {module:echarts/model/Model} model\n * @param {string} [axisType] Default retrieve from model.type\n * @return {module:echarts/scale/*}\n */\nexport function createScaleByModel(model, axisType) {\n axisType = axisType || model.get('type');\n if (axisType) {\n switch (axisType) {\n // Buildin scale\n case 'category':\n return new OrdinalScale(\n model.getCategories(), [Infinity, -Infinity]\n );\n case 'value':\n return new IntervalScale();\n // Extended scale, like time and log\n default:\n return (Scale.getClass(axisType) || IntervalScale).create(model);\n }\n }\n}\n\n/**\n * Check if the axis corss 0\n */\nexport function ifAxisCrossZero(axis) {\n var dataExtent = axis.scale.getExtent();\n var min = dataExtent[0];\n var max = dataExtent[1];\n return !((min > 0 && max > 0) || (min < 0 && max < 0));\n}\n\n/**\n * @param {Array.<number>} tickCoords In axis self coordinate.\n * @param {Array.<string>} labels\n * @param {string} font\n * @param {number} axisRotate 0: towards right horizontally, clock-wise is negative.\n * @param {number} [labelRotate=0] 0: towards right horizontally, clock-wise is negative.\n * @return {number}\n */\nexport function getAxisLabelInterval(tickCoords, labels, font, axisRotate, labelRotate) {\n var textSpaceTakenRect;\n var autoLabelInterval = 0;\n var accumulatedLabelInterval = 0;\n var rotation = (axisRotate - labelRotate) / 180 * Math.PI;\n\n var step = 1;\n if (labels.length > 40) {\n // Simple optimization for large amount of labels\n step = Math.floor(labels.length / 40);\n }\n\n for (var i = 0; i < tickCoords.length; i += step) {\n var tickCoord = tickCoords[i];\n\n // Not precise, do not consider align and vertical align\n // and each distance from axis line yet.\n var rect = textContain.getBoundingRect(\n labels[i], font, 'center', 'top'\n );\n rect.x += tickCoord * Math.cos(rotation);\n rect.y += tickCoord * Math.sin(rotation);\n\n // Magic number\n rect.width *= 1.3;\n rect.height *= 1.3;\n\n if (!textSpaceTakenRect) {\n textSpaceTakenRect = rect.clone();\n }\n // There is no space for current label;\n else if (textSpaceTakenRect.intersect(rect)) {\n accumulatedLabelInterval++;\n autoLabelInterval = Math.max(autoLabelInterval, accumulatedLabelInterval);\n }\n else {\n textSpaceTakenRect.union(rect);\n // Reset\n accumulatedLabelInterval = 0;\n }\n }\n if (autoLabelInterval === 0 && step > 1) {\n return step;\n }\n return (autoLabelInterval + 1) * step - 1;\n}\n\n/**\n * @param {Object} axis\n * @param {Function} labelFormatter\n * @return {Array.<string>}\n */\nexport function getFormattedLabels(axis, labelFormatter) {\n var scale = axis.scale;\n var labels = scale.getTicksLabels();\n var ticks = scale.getTicks();\n if (typeof labelFormatter === 'string') {\n labelFormatter = (function (tpl) {\n return function (val) {\n return tpl.replace('{value}', val != null ? val : '');\n };\n })(labelFormatter);\n // Consider empty array\n return zrUtil.map(labels, labelFormatter);\n }\n else if (typeof labelFormatter === 'function') {\n return zrUtil.map(ticks, function (tick, idx) {\n return labelFormatter(\n getAxisRawValue(axis, tick),\n idx\n );\n }, this);\n }\n else {\n return labels;\n }\n}\n\nexport function getAxisRawValue(axis, value) {\n // In category axis with data zoom, tick is not the original\n // index of axis.data. So tick should not be exposed to user\n // in category axis.\n return axis.type === 'category' ? axis.scale.getLabel(value) : value;\n}\n","import * as zrUtil from 'zrender/src/core/util';\nimport * as axisHelper from './axisHelper';\n\nfunction getName(obj) {\n if (zrUtil.isObject(obj) && obj.value != null) {\n return obj.value;\n }\n else {\n return obj + '';\n }\n}\n\nexport default {\n\n /**\n * Format labels\n * @return {Array.<string>}\n */\n getFormattedLabels: function () {\n return axisHelper.getFormattedLabels(\n this.axis,\n this.get('axisLabel.formatter')\n );\n },\n\n /**\n * Get categories\n */\n getCategories: function () {\n return this.get('type') === 'category'\n && zrUtil.map(this.get('data'), getName);\n },\n\n /**\n * @param {boolean} origin\n * @return {number|string} min value or 'dataMin' or null/undefined (means auto) or NaN\n */\n getMin: function (origin) {\n var option = this.option;\n var min = (!origin && option.rangeStart != null)\n ? option.rangeStart : option.min;\n\n if (this.axis\n && min != null\n && min !== 'dataMin'\n && typeof min !== 'function'\n && !zrUtil.eqNaN(min)\n ) {\n min = this.axis.scale.parse(min);\n }\n return min;\n },\n\n /**\n * @param {boolean} origin\n * @return {number|string} max value or 'dataMax' or null/undefined (means auto) or NaN\n */\n getMax: function (origin) {\n var option = this.option;\n var max = (!origin && option.rangeEnd != null)\n ? option.rangeEnd : option.max;\n\n if (this.axis\n && max != null\n && max !== 'dataMax'\n && typeof max !== 'function'\n && !zrUtil.eqNaN(max)\n ) {\n max = this.axis.scale.parse(max);\n }\n return max;\n },\n\n /**\n * @return {boolean}\n */\n getNeedCrossZero: function () {\n var option = this.option;\n return (option.rangeStart != null || option.rangeEnd != null)\n ? false : !option.scale;\n },\n\n /**\n * Should be implemented by each axis model if necessary.\n * @return {module:echarts/model/Component} coordinate system model\n */\n getCoordSysModel: zrUtil.noop,\n\n /**\n * @param {number} rangeStart Can only be finite number or null/undefined or NaN.\n * @param {number} rangeEnd Can only be finite number or null/undefined or NaN.\n */\n setRange: function (rangeStart, rangeEnd) {\n this.option.rangeStart = rangeStart;\n this.option.rangeEnd = rangeEnd;\n },\n\n /**\n * Reset range\n */\n resetRange: function () {\n // rangeStart and rangeEnd is readonly.\n this.option.rangeStart = this.option.rangeEnd = null;\n }\n};","// Symbol factory\n\nimport * as zrUtil from 'zrender/src/core/util';\nimport * as graphic from './graphic';\nimport BoundingRect from 'zrender/src/core/BoundingRect';\n\n/**\n * Triangle shape\n * @inner\n */\nvar Triangle = graphic.extendShape({\n type: 'triangle',\n shape: {\n cx: 0,\n cy: 0,\n width: 0,\n height: 0\n },\n buildPath: function (path, shape) {\n var cx = shape.cx;\n var cy = shape.cy;\n var width = shape.width / 2;\n var height = shape.height / 2;\n path.moveTo(cx, cy - height);\n path.lineTo(cx + width, cy + height);\n path.lineTo(cx - width, cy + height);\n path.closePath();\n }\n});\n\n/**\n * Diamond shape\n * @inner\n */\nvar Diamond = graphic.extendShape({\n type: 'diamond',\n shape: {\n cx: 0,\n cy: 0,\n width: 0,\n height: 0\n },\n buildPath: function (path, shape) {\n var cx = shape.cx;\n var cy = shape.cy;\n var width = shape.width / 2;\n var height = shape.height / 2;\n path.moveTo(cx, cy - height);\n path.lineTo(cx + width, cy);\n path.lineTo(cx, cy + height);\n path.lineTo(cx - width, cy);\n path.closePath();\n }\n});\n\n/**\n * Pin shape\n * @inner\n */\nvar Pin = graphic.extendShape({\n type: 'pin',\n shape: {\n // x, y on the cusp\n x: 0,\n y: 0,\n width: 0,\n height: 0\n },\n\n buildPath: function (path, shape) {\n var x = shape.x;\n var y = shape.y;\n var w = shape.width / 5 * 3;\n // Height must be larger than width\n var h = Math.max(w, shape.height);\n var r = w / 2;\n\n // Dist on y with tangent point and circle center\n var dy = r * r / (h - r);\n var cy = y - h + r + dy;\n var angle = Math.asin(dy / r);\n // Dist on x with tangent point and circle center\n var dx = Math.cos(angle) * r;\n\n var tanX = Math.sin(angle);\n var tanY = Math.cos(angle);\n\n var cpLen = r * 0.6;\n var cpLen2 = r * 0.7;\n\n path.moveTo(x - dx, cy + dy);\n\n path.arc(\n x, cy, r,\n Math.PI - angle,\n Math.PI * 2 + angle\n );\n path.bezierCurveTo(\n x + dx - tanX * cpLen, cy + dy + tanY * cpLen,\n x, y - cpLen2,\n x, y\n );\n path.bezierCurveTo(\n x, y - cpLen2,\n x - dx + tanX * cpLen, cy + dy + tanY * cpLen,\n x - dx, cy + dy\n );\n path.closePath();\n }\n});\n\n/**\n * Arrow shape\n * @inner\n */\nvar Arrow = graphic.extendShape({\n\n type: 'arrow',\n\n shape: {\n x: 0,\n y: 0,\n width: 0,\n height: 0\n },\n\n buildPath: function (ctx, shape) {\n var height = shape.height;\n var width = shape.width;\n var x = shape.x;\n var y = shape.y;\n var dx = width / 3 * 2;\n ctx.moveTo(x, y);\n ctx.lineTo(x + dx, y + height);\n ctx.lineTo(x, y + height / 4 * 3);\n ctx.lineTo(x - dx, y + height);\n ctx.lineTo(x, y);\n ctx.closePath();\n }\n});\n\n/**\n * Map of path contructors\n * @type {Object.<string, module:zrender/graphic/Path>}\n */\nvar symbolCtors = {\n\n line: graphic.Line,\n\n rect: graphic.Rect,\n\n roundRect: graphic.Rect,\n\n square: graphic.Rect,\n\n circle: graphic.Circle,\n\n diamond: Diamond,\n\n pin: Pin,\n\n arrow: Arrow,\n\n triangle: Triangle\n};\n\nvar symbolShapeMakers = {\n\n line: function (x, y, w, h, shape) {\n // FIXME\n shape.x1 = x;\n shape.y1 = y + h / 2;\n shape.x2 = x + w;\n shape.y2 = y + h / 2;\n },\n\n rect: function (x, y, w, h, shape) {\n shape.x = x;\n shape.y = y;\n shape.width = w;\n shape.height = h;\n },\n\n roundRect: function (x, y, w, h, shape) {\n shape.x = x;\n shape.y = y;\n shape.width = w;\n shape.height = h;\n shape.r = Math.min(w, h) / 4;\n },\n\n square: function (x, y, w, h, shape) {\n var size = Math.min(w, h);\n shape.x = x;\n shape.y = y;\n shape.width = size;\n shape.height = size;\n },\n\n circle: function (x, y, w, h, shape) {\n // Put circle in the center of square\n shape.cx = x + w / 2;\n shape.cy = y + h / 2;\n shape.r = Math.min(w, h) / 2;\n },\n\n diamond: function (x, y, w, h, shape) {\n shape.cx = x + w / 2;\n shape.cy = y + h / 2;\n shape.width = w;\n shape.height = h;\n },\n\n pin: function (x, y, w, h, shape) {\n shape.x = x + w / 2;\n shape.y = y + h / 2;\n shape.width = w;\n shape.height = h;\n },\n\n arrow: function (x, y, w, h, shape) {\n shape.x = x + w / 2;\n shape.y = y + h / 2;\n shape.width = w;\n shape.height = h;\n },\n\n triangle: function (x, y, w, h, shape) {\n shape.cx = x + w / 2;\n shape.cy = y + h / 2;\n shape.width = w;\n shape.height = h;\n }\n};\n\nvar symbolBuildProxies = {};\nzrUtil.each(symbolCtors, function (Ctor, name) {\n symbolBuildProxies[name] = new Ctor();\n});\n\nvar SymbolClz = graphic.extendShape({\n\n type: 'symbol',\n\n shape: {\n symbolType: '',\n x: 0,\n y: 0,\n width: 0,\n height: 0\n },\n\n beforeBrush: function () {\n var style = this.style;\n var shape = this.shape;\n // FIXME\n if (shape.symbolType === 'pin' && style.textPosition === 'inside') {\n style.textPosition = ['50%', '40%'];\n style.textAlign = 'center';\n style.textVerticalAlign = 'middle';\n }\n },\n\n buildPath: function (ctx, shape, inBundle) {\n var symbolType = shape.symbolType;\n var proxySymbol = symbolBuildProxies[symbolType];\n if (shape.symbolType !== 'none') {\n if (!proxySymbol) {\n // Default rect\n symbolType = 'rect';\n proxySymbol = symbolBuildProxies[symbolType];\n }\n symbolShapeMakers[symbolType](\n shape.x, shape.y, shape.width, shape.height, proxySymbol.shape\n );\n proxySymbol.buildPath(ctx, proxySymbol.shape, inBundle);\n }\n }\n});\n\n// Provide setColor helper method to avoid determine if set the fill or stroke outside\nfunction symbolPathSetColor(color, innerColor) {\n if (this.type !== 'image') {\n var symbolStyle = this.style;\n var symbolShape = this.shape;\n if (symbolShape && symbolShape.symbolType === 'line') {\n symbolStyle.stroke = color;\n }\n else if (this.__isEmptyBrush) {\n symbolStyle.stroke = color;\n symbolStyle.fill = innerColor || '#fff';\n }\n else {\n // FIXME 判断图形默认是填充还是描边,使用 onlyStroke ?\n symbolStyle.fill && (symbolStyle.fill = color);\n symbolStyle.stroke && (symbolStyle.stroke = color);\n }\n this.dirty(false);\n }\n}\n\n/**\n * Create a symbol element with given symbol configuration: shape, x, y, width, height, color\n * @param {string} symbolType\n * @param {number} x\n * @param {number} y\n * @param {number} w\n * @param {number} h\n * @param {string} color\n * @param {boolean} [keepAspect=false] whether to keep the ratio of w/h,\n * for path and image only.\n */\nexport function createSymbol(symbolType, x, y, w, h, color, keepAspect) {\n // TODO Support image object, DynamicImage.\n\n var isEmpty = symbolType.indexOf('empty') === 0;\n if (isEmpty) {\n symbolType = symbolType.substr(5, 1).toLowerCase() + symbolType.substr(6);\n }\n var symbolPath;\n\n if (symbolType.indexOf('image://') === 0) {\n symbolPath = graphic.makeImage(\n symbolType.slice(8),\n new BoundingRect(x, y, w, h),\n keepAspect ? 'center' : 'cover'\n );\n }\n else if (symbolType.indexOf('path://') === 0) {\n symbolPath = graphic.makePath(\n symbolType.slice(7),\n {},\n new BoundingRect(x, y, w, h),\n keepAspect ? 'center' : 'cover'\n );\n }\n else {\n symbolPath = new SymbolClz({\n shape: {\n symbolType: symbolType,\n x: x,\n y: y,\n width: w,\n height: h\n }\n });\n }\n\n symbolPath.__isEmptyBrush = isEmpty;\n\n symbolPath.setColor = symbolPathSetColor;\n\n symbolPath.setColor(color);\n\n return symbolPath;\n}\n","import * as zrUtil from 'zrender/src/core/util';\nimport createListFromArray from './chart/helper/createListFromArray';\nimport * as axisHelper from './coord/axisHelper';\nimport axisModelCommonMixin from './coord/axisModelCommonMixin';\nimport Model from './model/Model';\n\n/**\n * Create a muti dimension List structure from seriesModel.\n * @param {module:echarts/model/Model} seriesModel\n * @return {module:echarts/data/List} list\n */\nexport function createList(seriesModel) {\n var data = seriesModel.get('data');\n return createListFromArray(data, seriesModel, seriesModel.ecModel);\n}\n\n/**\n * @see {module:echarts/data/helper/completeDimensions}\n */\nexport {default as completeDimensions} from './data/helper/completeDimensions';\n\n/**\n * Create a symbol element with given symbol configuration: shape, x, y, width, height, color\n * @see http://echarts.baidu.com/option.html#series-scatter.symbol\n * @param {string} symbolDesc\n * @param {number} x\n * @param {number} y\n * @param {number} w\n * @param {number} h\n * @param {string} color\n */\nexport {createSymbol} from './util/symbol';\n\n/**\n * Create scale\n * @param {Array.<number>} dataExtent\n * @param {Object|module:echarts/Model} option\n */\nexport function createScale(dataExtent, option) {\n var axisModel = option;\n if (!(option instanceof Model)) {\n axisModel = new Model(option);\n zrUtil.mixin(axisModel, axisModelCommonMixin);\n }\n\n var scale = axisHelper.createScaleByModel(axisModel);\n scale.setExtent(dataExtent[0], dataExtent[1]);\n\n axisHelper.niceScaleExtent(scale, axisModel);\n return scale;\n}\n\n/**\n * Mixin common methods to axis model,\n *\n * Inlcude methods\n * `getFormattedLabels() => Array.<string>`\n * `getCategories() => Array.<string>`\n * `getMin(origin: boolean) => number`\n * `getMax(origin: boolean) => number`\n * `getNeedCrossZero() => boolean`\n * `setRange(start: number, end: number)`\n * `resetRange()`\n */\nexport function mixinAxisModelCommonMethods(Model) {\n zrUtil.mixin(Model, axisModelCommonMixin);\n}","\nimport * as zrUtil from 'zrender/src/core/util';\nimport * as numberUtil from '../util/number';\nimport * as axisHelper from './axisHelper';\n\nvar linearMap = numberUtil.linearMap;\n\nfunction fixExtentWithBands(extent, nTick) {\n var size = extent[1] - extent[0];\n var len = nTick;\n var margin = size / len / 2;\n extent[0] += margin;\n extent[1] -= margin;\n}\n\nvar normalizedExtent = [0, 1];\n/**\n * @name module:echarts/coord/CartesianAxis\n * @constructor\n */\nvar Axis = function (dim, scale, extent) {\n\n /**\n * Axis dimension. Such as 'x', 'y', 'z', 'angle', 'radius'\n * @type {string}\n */\n this.dim = dim;\n\n /**\n * Axis scale\n * @type {module:echarts/coord/scale/*}\n */\n this.scale = scale;\n\n /**\n * @type {Array.<number>}\n * @private\n */\n this._extent = extent || [0, 0];\n\n /**\n * @type {boolean}\n */\n this.inverse = false;\n\n /**\n * Usually true when axis has a ordinal scale\n * @type {boolean}\n */\n this.onBand = false;\n\n /**\n * @private\n * @type {number}\n */\n this._labelInterval;\n};\n\nAxis.prototype = {\n\n constructor: Axis,\n\n /**\n * If axis extent contain given coord\n * @param {number} coord\n * @return {boolean}\n */\n contain: function (coord) {\n var extent = this._extent;\n var min = Math.min(extent[0], extent[1]);\n var max = Math.max(extent[0], extent[1]);\n return coord >= min && coord <= max;\n },\n\n /**\n * If axis extent contain given data\n * @param {number} data\n * @return {boolean}\n */\n containData: function (data) {\n return this.contain(this.dataToCoord(data));\n },\n\n /**\n * Get coord extent.\n * @return {Array.<number>}\n */\n getExtent: function () {\n return this._extent.slice();\n },\n\n /**\n * Get precision used for formatting\n * @param {Array.<number>} [dataExtent]\n * @return {number}\n */\n getPixelPrecision: function (dataExtent) {\n return numberUtil.getPixelPrecision(\n dataExtent || this.scale.getExtent(),\n this._extent\n );\n },\n\n /**\n * Set coord extent\n * @param {number} start\n * @param {number} end\n */\n setExtent: function (start, end) {\n var extent = this._extent;\n extent[0] = start;\n extent[1] = end;\n },\n\n /**\n * Convert data to coord. Data is the rank if it has a ordinal scale\n * @param {number} data\n * @param {boolean} clamp\n * @return {number}\n */\n dataToCoord: function (data, clamp) {\n var extent = this._extent;\n var scale = this.scale;\n data = scale.normalize(data);\n\n if (this.onBand && scale.type === 'ordinal') {\n extent = extent.slice();\n fixExtentWithBands(extent, scale.count());\n }\n\n return linearMap(data, normalizedExtent, extent, clamp);\n },\n\n /**\n * Convert coord to data. Data is the rank if it has a ordinal scale\n * @param {number} coord\n * @param {boolean} clamp\n * @return {number}\n */\n coordToData: function (coord, clamp) {\n var extent = this._extent;\n var scale = this.scale;\n\n if (this.onBand && scale.type === 'ordinal') {\n extent = extent.slice();\n fixExtentWithBands(extent, scale.count());\n }\n\n var t = linearMap(coord, extent, normalizedExtent, clamp);\n\n return this.scale.scale(t);\n },\n\n /**\n * Convert pixel point to data in axis\n * @param {Array.<number>} point\n * @param {boolean} clamp\n * @return {number} data\n */\n pointToData: function (point, clamp) {\n // Should be implemented in derived class if necessary.\n },\n\n /**\n * @return {Array.<number>}\n */\n getTicksCoords: function (alignWithLabel) {\n if (this.onBand && !alignWithLabel) {\n var bands = this.getBands();\n var coords = [];\n for (var i = 0; i < bands.length; i++) {\n coords.push(bands[i][0]);\n }\n if (bands[i - 1]) {\n coords.push(bands[i - 1][1]);\n }\n return coords;\n }\n else {\n return zrUtil.map(this.scale.getTicks(), this.dataToCoord, this);\n }\n },\n\n /**\n * Coords of labels are on the ticks or on the middle of bands\n * @return {Array.<number>}\n */\n getLabelsCoords: function () {\n return zrUtil.map(this.scale.getTicks(), this.dataToCoord, this);\n },\n\n /**\n * Get bands.\n *\n * If axis has labels [1, 2, 3, 4]. Bands on the axis are\n * |---1---|---2---|---3---|---4---|.\n *\n * @return {Array}\n */\n // FIXME Situation when labels is on ticks\n getBands: function () {\n var extent = this.getExtent();\n var bands = [];\n var len = this.scale.count();\n var start = extent[0];\n var end = extent[1];\n var span = end - start;\n\n for (var i = 0; i < len; i++) {\n bands.push([\n span * i / len + start,\n span * (i + 1) / len + start\n ]);\n }\n return bands;\n },\n\n /**\n * Get width of band\n * @return {number}\n */\n getBandWidth: function () {\n var axisExtent = this._extent;\n var dataExtent = this.scale.getExtent();\n\n var len = dataExtent[1] - dataExtent[0] + (this.onBand ? 1 : 0);\n // Fix #2728, avoid NaN when only one data.\n len === 0 && (len = 1);\n\n var size = Math.abs(axisExtent[1] - axisExtent[0]);\n\n return Math.abs(size) / len;\n },\n\n /**\n * @abstract\n * @return {boolean} Is horizontal\n */\n isHorizontal: null,\n\n /**\n * @abstract\n * @return {number} Get axis rotate, by degree.\n */\n getRotate: null,\n\n /**\n * Get interval of the axis label.\n * To get precise result, at least one of `getRotate` and `isHorizontal`\n * should be implemented.\n * @return {number}\n */\n getLabelInterval: function () {\n var labelInterval = this._labelInterval;\n if (!labelInterval) {\n var axisModel = this.model;\n var labelModel = axisModel.getModel('axisLabel');\n labelInterval = labelModel.get('interval');\n\n if (this.type === 'category'\n && (labelInterval == null || labelInterval === 'auto')\n ) {\n labelInterval = axisHelper.getAxisLabelInterval(\n zrUtil.map(this.scale.getTicks(), this.dataToCoord, this),\n axisModel.getFormattedLabels(),\n labelModel.getFont(),\n this.getRotate\n ? this.getRotate()\n : (this.isHorizontal && !this.isHorizontal())\n ? 90\n : 0,\n labelModel.get('rotate')\n );\n }\n\n this._labelInterval = labelInterval;\n }\n return labelInterval;\n }\n\n};\n\nexport default Axis;","import windingLine from './windingLine';\n\nvar EPSILON = 1e-8;\n\nfunction isAroundEqual(a, b) {\n return Math.abs(a - b) < EPSILON;\n}\n\nexport function contain(points, x, y) {\n var w = 0;\n var p = points[0];\n\n if (!p) {\n return false;\n }\n\n for (var i = 1; i < points.length; i++) {\n var p2 = points[i];\n w += windingLine(p[0], p[1], p2[0], p2[1], x, y);\n p = p2;\n }\n\n // Close polygon\n var p0 = points[0];\n if (!isAroundEqual(p[0], p0[0]) || !isAroundEqual(p[1], p0[1])) {\n w += windingLine(p[0], p[1], p0[0], p0[1], x, y);\n }\n\n return w !== 0;\n}\n","/**\n * @module echarts/coord/geo/Region\n */\n\nimport BoundingRect from 'zrender/src/core/BoundingRect';\nimport * as bbox from 'zrender/src/core/bbox';\nimport * as vec2 from 'zrender/src/core/vector';\nimport * as polygonContain from 'zrender/src/contain/polygon';\n\n/**\n * @param {string} name\n * @param {Array} geometries\n * @param {Array.<number>} cp\n */\nfunction Region(name, geometries, cp) {\n\n /**\n * @type {string}\n * @readOnly\n */\n this.name = name;\n\n /**\n * @type {Array.<Array>}\n * @readOnly\n */\n this.geometries = geometries;\n\n if (!cp) {\n var rect = this.getBoundingRect();\n cp = [\n rect.x + rect.width / 2,\n rect.y + rect.height / 2\n ];\n }\n else {\n cp = [cp[0], cp[1]];\n }\n /**\n * @type {Array.<number>}\n */\n this.center = cp;\n}\n\nRegion.prototype = {\n\n constructor: Region,\n\n properties: null,\n\n /**\n * @return {module:zrender/core/BoundingRect}\n */\n getBoundingRect: function () {\n var rect = this._rect;\n if (rect) {\n return rect;\n }\n\n var MAX_NUMBER = Number.MAX_VALUE;\n var min = [MAX_NUMBER, MAX_NUMBER];\n var max = [-MAX_NUMBER, -MAX_NUMBER];\n var min2 = [];\n var max2 = [];\n var geometries = this.geometries;\n for (var i = 0; i < geometries.length; i++) {\n // Only support polygon\n if (geometries[i].type !== 'polygon') {\n continue;\n }\n // Doesn't consider hole\n var exterior = geometries[i].exterior;\n bbox.fromPoints(exterior, min2, max2);\n vec2.min(min, min, min2);\n vec2.max(max, max, max2);\n }\n // No data\n if (i === 0) {\n min[0] = min[1] = max[0] = max[1] = 0;\n }\n\n return (this._rect = new BoundingRect(\n min[0], min[1], max[0] - min[0], max[1] - min[1]\n ));\n },\n\n /**\n * @param {<Array.<number>} coord\n * @return {boolean}\n */\n contain: function (coord) {\n var rect = this.getBoundingRect();\n var geometries = this.geometries;\n if (!rect.contain(coord[0], coord[1])) {\n return false;\n }\n loopGeo: for (var i = 0, len = geometries.length; i < len; i++) {\n // Only support polygon.\n if (geometries[i].type !== 'polygon') {\n continue;\n }\n var exterior = geometries[i].exterior;\n var interiors = geometries[i].interiors;\n if (polygonContain.contain(exterior, coord[0], coord[1])) {\n // Not in the region if point is in the hole.\n for (var k = 0; k < (interiors ? interiors.length : 0); k++) {\n if (polygonContain.contain(interiors[k])) {\n continue loopGeo;\n }\n }\n return true;\n }\n }\n return false;\n },\n\n transformTo: function (x, y, width, height) {\n var rect = this.getBoundingRect();\n var aspect = rect.width / rect.height;\n if (!width) {\n width = aspect * height;\n }\n else if (!height) {\n height = width / aspect ;\n }\n var target = new BoundingRect(x, y, width, height);\n var transform = rect.calculateTransform(target);\n var geometries = this.geometries;\n for (var i = 0; i < geometries.length; i++) {\n // Only support polygon.\n if (geometries[i].type !== 'polygon') {\n continue;\n }\n var exterior = geometries[i].exterior;\n var interiors = geometries[i].interiors;\n for (var p = 0; p < exterior.length; p++) {\n vec2.applyTransform(exterior[p], exterior[p], transform);\n }\n for (var h = 0; h < (interiors ? interiors.length : 0); h++) {\n for (var p = 0; p < interiors[h].length; p++) {\n vec2.applyTransform(interiors[h][p], interiors[h][p], transform);\n }\n }\n }\n rect = this._rect;\n rect.copy(target);\n // Update center\n this.center = [\n rect.x + rect.width / 2,\n rect.y + rect.height / 2\n ];\n }\n};\n\nexport default Region;","/**\n * Parse and decode geo json\n * @module echarts/coord/geo/parseGeoJson\n */\n\nimport * as zrUtil from 'zrender/src/core/util';\nimport Region from './Region';\n\nfunction decode(json) {\n if (!json.UTF8Encoding) {\n return json;\n }\n var encodeScale = json.UTF8Scale;\n if (encodeScale == null) {\n encodeScale = 1024;\n }\n\n var features = json.features;\n\n for (var f = 0; f < features.length; f++) {\n var feature = features[f];\n var geometry = feature.geometry;\n var coordinates = geometry.coordinates;\n var encodeOffsets = geometry.encodeOffsets;\n\n for (var c = 0; c < coordinates.length; c++) {\n var coordinate = coordinates[c];\n\n if (geometry.type === 'Polygon') {\n coordinates[c] = decodePolygon(\n coordinate,\n encodeOffsets[c],\n encodeScale\n );\n }\n else if (geometry.type === 'MultiPolygon') {\n for (var c2 = 0; c2 < coordinate.length; c2++) {\n var polygon = coordinate[c2];\n coordinate[c2] = decodePolygon(\n polygon,\n encodeOffsets[c][c2],\n encodeScale\n );\n }\n }\n }\n }\n // Has been decoded\n json.UTF8Encoding = false;\n return json;\n}\n\nfunction decodePolygon(coordinate, encodeOffsets, encodeScale) {\n var result = [];\n var prevX = encodeOffsets[0];\n var prevY = encodeOffsets[1];\n\n for (var i = 0; i < coordinate.length; i += 2) {\n var x = coordinate.charCodeAt(i) - 64;\n var y = coordinate.charCodeAt(i + 1) - 64;\n // ZigZag decoding\n x = (x >> 1) ^ (-(x & 1));\n y = (y >> 1) ^ (-(y & 1));\n // Delta deocding\n x += prevX;\n y += prevY;\n\n prevX = x;\n prevY = y;\n // Dequantize\n result.push([x / encodeScale, y / encodeScale]);\n }\n\n return result;\n}\n\n/**\n * @alias module:echarts/coord/geo/parseGeoJson\n * @param {Object} geoJson\n * @return {module:zrender/container/Group}\n */\nexport default function (geoJson) {\n\n decode(geoJson);\n\n return zrUtil.map(zrUtil.filter(geoJson.features, function (featureObj) {\n // Output of mapshaper may have geometry null\n return featureObj.geometry\n && featureObj.properties\n && featureObj.geometry.coordinates.length > 0;\n }), function (featureObj) {\n var properties = featureObj.properties;\n var geo = featureObj.geometry;\n\n var coordinates = geo.coordinates;\n\n var geometries = [];\n if (geo.type === 'Polygon') {\n geometries.push({\n type: 'polygon',\n // According to the GeoJSON specification.\n // First must be exterior, and the rest are all interior(holes).\n exterior: coordinates[0],\n interiors: coordinates.slice(1)\n });\n }\n if (geo.type === 'MultiPolygon') {\n zrUtil.each(coordinates, function (item) {\n if (item[0]) {\n geometries.push({\n type: 'polygon',\n exterior: item[0],\n interiors: item.slice(1)\n });\n }\n });\n }\n\n var region = new Region(\n properties.name,\n geometries,\n properties.cp\n );\n region.properties = properties;\n return region;\n });\n}","/**\n * Do not mount those modules on 'src/echarts' for better tree shaking.\n */\n\nimport * as zrender from 'zrender/src/zrender';\nimport * as matrix from 'zrender/src/core/matrix';\nimport * as vector from 'zrender/src/core/vector';\nimport * as zrUtil from 'zrender/src/core/util';\nimport * as colorTool from 'zrender/src/tool/color';\nimport * as graphic from './util/graphic';\nimport * as numberUtil from './util/number';\nimport * as formatUtil from './util/format';\nimport {throttle} from './util/throttle';\nimport * as ecHelper from './helper';\n\n\nexport {zrender};\nexport {default as List} from './data/List';\nexport {default as Model} from './model/Model';\nexport {default as Axis} from './coord/Axis';\nexport {graphic};\nexport {numberUtil as number};\nexport {formatUtil as format};\nexport {throttle};\nexport {ecHelper as helper};\nexport {matrix};\nexport {vector};\nexport {colorTool as color};\nexport {default as env} from 'zrender/src/core/env';\nexport {default as parseGeoJson} from './coord/geo/parseGeoJson';\n\nvar ecUtil = {};\nzrUtil.each([\n 'map', 'each', 'filter', 'indexOf', 'inherits', 'reduce', 'filter',\n 'bind', 'curry', 'isArray', 'isString', 'isObject', 'isFunction',\n 'extend', 'defaults', 'clone', 'merge'\n ],\n function (name) {\n ecUtil[name] = zrUtil[name];\n }\n);\nexport {ecUtil as util};\n","import {__DEV__} from '../../config';\nimport createListFromArray from '../helper/createListFromArray';\nimport SeriesModel from '../../model/Series';\n\nexport default SeriesModel.extend({\n\n type: 'series.line',\n\n dependencies: ['grid', 'polar'],\n\n getInitialData: function (option, ecModel) {\n if (__DEV__) {\n var coordSys = option.coordinateSystem;\n if (coordSys !== 'polar' && coordSys !== 'cartesian2d') {\n throw new Error('Line not support coordinateSystem besides cartesian and polar');\n }\n }\n return createListFromArray(option.data, this, ecModel);\n },\n\n defaultOption: {\n zlevel: 0, // 一级层叠\n z: 2, // 二级层叠\n coordinateSystem: 'cartesian2d',\n legendHoverLink: true,\n\n hoverAnimation: true,\n // stack: null\n // xAxisIndex: 0,\n // yAxisIndex: 0,\n\n // polarIndex: 0,\n\n // If clip the overflow value\n clipOverflow: true,\n // cursor: null,\n\n label: {\n normal: {\n position: 'top'\n }\n },\n // itemStyle: {\n // normal: {},\n // emphasis: {}\n // },\n lineStyle: {\n normal: {\n width: 2,\n type: 'solid'\n }\n },\n // areaStyle: {},\n // false, 'start', 'end', 'middle'\n step: false,\n\n // Disabled if step is true\n smooth: false,\n smoothMonotone: null,\n // 拐点图形类型\n symbol: 'emptyCircle',\n // 拐点图形大小\n symbolSize: 4,\n // 拐点图形旋转控制\n symbolRotate: null,\n\n // 是否显示 symbol, 只有在 tooltip hover 的时候显示\n showSymbol: true,\n // 标志图形默认只有主轴显示(随主轴标签间隔隐藏策略)\n showAllSymbol: false,\n\n // 是否连接断点\n connectNulls: false,\n\n // 数据过滤,'average', 'max', 'min', 'sum'\n sampling: 'none',\n\n animationEasing: 'linear',\n\n // Disable progressive\n progressive: 0,\n hoverLayerThreshold: Infinity\n }\n});","/**\n * @module echarts/chart/helper/Symbol\n */\n\nimport {otherDimToDataDim} from '../../util/model';\n\nexport function findLabelValueDim(data) {\n var valueDim;\n var labelDims = otherDimToDataDim(data, 'label');\n\n if (labelDims.length) {\n valueDim = labelDims[0];\n }\n else {\n // Get last value dim\n var dimensions = data.dimensions.slice();\n var dataType;\n while (dimensions.length && (\n valueDim = dimensions.pop(),\n dataType = data.getDimensionInfo(valueDim).type,\n dataType === 'ordinal' || dataType === 'time'\n )) {} // jshint ignore:line\n }\n\n return valueDim;\n}\n","/**\n * @module echarts/chart/helper/Symbol\n */\n\nimport * as zrUtil from 'zrender/src/core/util';\nimport {createSymbol} from '../../util/symbol';\nimport * as graphic from '../../util/graphic';\nimport {parsePercent} from '../../util/number';\nimport {findLabelValueDim} from './labelHelper';\n\n\nfunction getSymbolSize(data, idx) {\n var symbolSize = data.getItemVisual(idx, 'symbolSize');\n return symbolSize instanceof Array\n ? symbolSize.slice()\n : [+symbolSize, +symbolSize];\n}\n\nfunction getScale(symbolSize) {\n return [symbolSize[0] / 2, symbolSize[1] / 2];\n}\n\n/**\n * @constructor\n * @alias {module:echarts/chart/helper/Symbol}\n * @param {module:echarts/data/List} data\n * @param {number} idx\n * @extends {module:zrender/graphic/Group}\n */\nfunction SymbolClz(data, idx, seriesScope) {\n graphic.Group.call(this);\n\n this.updateData(data, idx, seriesScope);\n}\n\nvar symbolProto = SymbolClz.prototype;\n\nfunction driftSymbol(dx, dy) {\n this.parent.drift(dx, dy);\n}\n\nsymbolProto._createSymbol = function (symbolType, data, idx, symbolSize) {\n // Remove paths created before\n this.removeAll();\n\n var color = data.getItemVisual(idx, 'color');\n\n // var symbolPath = createSymbol(\n // symbolType, -0.5, -0.5, 1, 1, color\n // );\n // If width/height are set too small (e.g., set to 1) on ios10\n // and macOS Sierra, a circle stroke become a rect, no matter what\n // the scale is set. So we set width/height as 2. See #4150.\n var symbolPath = createSymbol(\n symbolType, -1, -1, 2, 2, color\n );\n\n symbolPath.attr({\n z2: 100,\n culling: true,\n scale: getScale(symbolSize)\n });\n // Rewrite drift method\n symbolPath.drift = driftSymbol;\n\n this._symbolType = symbolType;\n\n this.add(symbolPath);\n};\n\n/**\n * Stop animation\n * @param {boolean} toLastFrame\n */\nsymbolProto.stopSymbolAnimation = function (toLastFrame) {\n this.childAt(0).stopAnimation(toLastFrame);\n};\n\n/**\n * FIXME:\n * Caution: This method breaks the encapsulation of this module,\n * but it indeed brings convenience. So do not use the method\n * unless you detailedly know all the implements of `Symbol`,\n * especially animation.\n *\n * Get symbol path element.\n */\nsymbolProto.getSymbolPath = function () {\n return this.childAt(0);\n};\n\n/**\n * Get scale(aka, current symbol size).\n * Including the change caused by animation\n */\nsymbolProto.getScale = function () {\n return this.childAt(0).scale;\n};\n\n/**\n * Highlight symbol\n */\nsymbolProto.highlight = function () {\n this.childAt(0).trigger('emphasis');\n};\n\n/**\n * Downplay symbol\n */\nsymbolProto.downplay = function () {\n this.childAt(0).trigger('normal');\n};\n\n/**\n * @param {number} zlevel\n * @param {number} z\n */\nsymbolProto.setZ = function (zlevel, z) {\n var symbolPath = this.childAt(0);\n symbolPath.zlevel = zlevel;\n symbolPath.z = z;\n};\n\nsymbolProto.setDraggable = function (draggable) {\n var symbolPath = this.childAt(0);\n symbolPath.draggable = draggable;\n symbolPath.cursor = draggable ? 'move' : 'pointer';\n};\n\n/**\n * Update symbol properties\n * @param {module:echarts/data/List} data\n * @param {number} idx\n * @param {Object} [seriesScope]\n * @param {Object} [seriesScope.itemStyle]\n * @param {Object} [seriesScope.hoverItemStyle]\n * @param {Object} [seriesScope.symbolRotate]\n * @param {Object} [seriesScope.symbolOffset]\n * @param {module:echarts/model/Model} [seriesScope.labelModel]\n * @param {module:echarts/model/Model} [seriesScope.hoverLabelModel]\n * @param {boolean} [seriesScope.hoverAnimation]\n * @param {Object} [seriesScope.cursorStyle]\n * @param {module:echarts/model/Model} [seriesScope.itemModel]\n * @param {string} [seriesScope.symbolInnerColor]\n * @param {Object} [seriesScope.fadeIn=false]\n */\nsymbolProto.updateData = function (data, idx, seriesScope) {\n this.silent = false;\n\n var symbolType = data.getItemVisual(idx, 'symbol') || 'circle';\n var seriesModel = data.hostModel;\n var symbolSize = getSymbolSize(data, idx);\n var isInit = symbolType !== this._symbolType;\n\n if (isInit) {\n this._createSymbol(symbolType, data, idx, symbolSize);\n }\n else {\n var symbolPath = this.childAt(0);\n symbolPath.silent = false;\n graphic.updateProps(symbolPath, {\n scale: getScale(symbolSize)\n }, seriesModel, idx);\n }\n\n this._updateCommon(data, idx, symbolSize, seriesScope);\n\n if (isInit) {\n var symbolPath = this.childAt(0);\n var fadeIn = seriesScope && seriesScope.fadeIn;\n\n var target = {scale: symbolPath.scale.slice()};\n fadeIn && (target.style = {opacity: symbolPath.style.opacity});\n\n symbolPath.scale = [0, 0];\n fadeIn && (symbolPath.style.opacity = 0);\n\n graphic.initProps(symbolPath, target, seriesModel, idx);\n }\n\n this._seriesModel = seriesModel;\n};\n\n// Update common properties\nvar normalStyleAccessPath = ['itemStyle', 'normal'];\nvar emphasisStyleAccessPath = ['itemStyle', 'emphasis'];\nvar normalLabelAccessPath = ['label', 'normal'];\nvar emphasisLabelAccessPath = ['label', 'emphasis'];\n\n/**\n * @param {module:echarts/data/List} data\n * @param {number} idx\n * @param {Array.<number>} symbolSize\n * @param {Object} [seriesScope]\n */\nsymbolProto._updateCommon = function (data, idx, symbolSize, seriesScope) {\n var symbolPath = this.childAt(0);\n var seriesModel = data.hostModel;\n var color = data.getItemVisual(idx, 'color');\n\n // Reset style\n if (symbolPath.type !== 'image') {\n symbolPath.useStyle({\n strokeNoScale: true\n });\n }\n\n var itemStyle = seriesScope && seriesScope.itemStyle;\n var hoverItemStyle = seriesScope && seriesScope.hoverItemStyle;\n var symbolRotate = seriesScope && seriesScope.symbolRotate;\n var symbolOffset = seriesScope && seriesScope.symbolOffset;\n var labelModel = seriesScope && seriesScope.labelModel;\n var hoverLabelModel = seriesScope && seriesScope.hoverLabelModel;\n var hoverAnimation = seriesScope && seriesScope.hoverAnimation;\n var cursorStyle = seriesScope && seriesScope.cursorStyle;\n\n if (!seriesScope || data.hasItemOption) {\n var itemModel = (seriesScope && seriesScope.itemModel)\n ? seriesScope.itemModel : data.getItemModel(idx);\n\n // Color must be excluded.\n // Because symbol provide setColor individually to set fill and stroke\n itemStyle = itemModel.getModel(normalStyleAccessPath).getItemStyle(['color']);\n hoverItemStyle = itemModel.getModel(emphasisStyleAccessPath).getItemStyle();\n\n symbolRotate = itemModel.getShallow('symbolRotate');\n symbolOffset = itemModel.getShallow('symbolOffset');\n\n labelModel = itemModel.getModel(normalLabelAccessPath);\n hoverLabelModel = itemModel.getModel(emphasisLabelAccessPath);\n hoverAnimation = itemModel.getShallow('hoverAnimation');\n cursorStyle = itemModel.getShallow('cursor');\n }\n else {\n hoverItemStyle = zrUtil.extend({}, hoverItemStyle);\n }\n\n var elStyle = symbolPath.style;\n\n symbolPath.attr('rotation', (symbolRotate || 0) * Math.PI / 180 || 0);\n\n if (symbolOffset) {\n symbolPath.attr('position', [\n parsePercent(symbolOffset[0], symbolSize[0]),\n parsePercent(symbolOffset[1], symbolSize[1])\n ]);\n }\n\n cursorStyle && symbolPath.attr('cursor', cursorStyle);\n\n // PENDING setColor before setStyle!!!\n symbolPath.setColor(color, seriesScope && seriesScope.symbolInnerColor);\n\n symbolPath.setStyle(itemStyle);\n\n var opacity = data.getItemVisual(idx, 'opacity');\n if (opacity != null) {\n elStyle.opacity = opacity;\n }\n\n var useNameLabel = seriesScope && seriesScope.useNameLabel;\n var valueDim = !useNameLabel && findLabelValueDim(data);\n\n if (useNameLabel || valueDim != null) {\n graphic.setLabelStyle(\n elStyle, hoverItemStyle, labelModel, hoverLabelModel,\n {\n labelFetcher: seriesModel,\n labelDataIndex: idx,\n defaultText: useNameLabel ? data.getName(idx) : data.get(valueDim, idx),\n isRectText: true,\n autoColor: color\n }\n );\n }\n\n symbolPath.off('mouseover')\n .off('mouseout')\n .off('emphasis')\n .off('normal');\n\n symbolPath.hoverStyle = hoverItemStyle;\n\n // FIXME\n // Do not use symbol.trigger('emphasis'), but use symbol.highlight() instead.\n graphic.setHoverStyle(symbolPath);\n\n var scale = getScale(symbolSize);\n\n if (hoverAnimation && seriesModel.isAnimationEnabled()) {\n var onEmphasis = function() {\n var ratio = scale[1] / scale[0];\n this.animateTo({\n scale: [\n Math.max(scale[0] * 1.1, scale[0] + 3),\n Math.max(scale[1] * 1.1, scale[1] + 3 * ratio)\n ]\n }, 400, 'elasticOut');\n };\n var onNormal = function() {\n this.animateTo({\n scale: scale\n }, 400, 'elasticOut');\n };\n symbolPath.on('mouseover', onEmphasis)\n .on('mouseout', onNormal)\n .on('emphasis', onEmphasis)\n .on('normal', onNormal);\n }\n};\n\n/**\n * @param {Function} cb\n * @param {Object} [opt]\n * @param {Object} [opt.keepLabel=true]\n */\nsymbolProto.fadeOut = function (cb, opt) {\n var symbolPath = this.childAt(0);\n // Avoid mistaken hover when fading out\n this.silent = symbolPath.silent = true;\n // Not show text when animating\n !(opt && opt.keepLabel) && (symbolPath.style.text = null);\n\n graphic.updateProps(\n symbolPath,\n {\n style: {opacity: 0},\n scale: [0, 0]\n },\n this._seriesModel,\n this.dataIndex,\n cb\n );\n};\n\nzrUtil.inherits(SymbolClz, graphic.Group);\n\nexport default SymbolClz;","/**\n * @module echarts/chart/helper/SymbolDraw\n */\n\nimport * as graphic from '../../util/graphic';\nimport SymbolClz from './Symbol';\n\n/**\n * @constructor\n * @alias module:echarts/chart/helper/SymbolDraw\n * @param {module:zrender/graphic/Group} [symbolCtor]\n */\nfunction SymbolDraw(symbolCtor) {\n this.group = new graphic.Group();\n\n this._symbolCtor = symbolCtor || SymbolClz;\n}\n\nvar symbolDrawProto = SymbolDraw.prototype;\n\nfunction symbolNeedsDraw(data, idx, isIgnore) {\n var point = data.getItemLayout(idx);\n // Is an object\n // if (point && point.hasOwnProperty('point')) {\n // point = point.point;\n // }\n return point && !isNaN(point[0]) && !isNaN(point[1]) && !(isIgnore && isIgnore(idx))\n && data.getItemVisual(idx, 'symbol') !== 'none';\n}\n/**\n * Update symbols draw by new data\n * @param {module:echarts/data/List} data\n * @param {Array.<boolean>} [isIgnore]\n */\nsymbolDrawProto.updateData = function (data, isIgnore) {\n var group = this.group;\n var seriesModel = data.hostModel;\n var oldData = this._data;\n\n var SymbolCtor = this._symbolCtor;\n\n var seriesScope = {\n itemStyle: seriesModel.getModel('itemStyle.normal').getItemStyle(['color']),\n hoverItemStyle: seriesModel.getModel('itemStyle.emphasis').getItemStyle(),\n symbolRotate: seriesModel.get('symbolRotate'),\n symbolOffset: seriesModel.get('symbolOffset'),\n hoverAnimation: seriesModel.get('hoverAnimation'),\n\n labelModel: seriesModel.getModel('label.normal'),\n hoverLabelModel: seriesModel.getModel('label.emphasis'),\n cursorStyle: seriesModel.get('cursor')\n };\n\n data.diff(oldData)\n .add(function (newIdx) {\n var point = data.getItemLayout(newIdx);\n if (symbolNeedsDraw(data, newIdx, isIgnore)) {\n var symbolEl = new SymbolCtor(data, newIdx, seriesScope);\n symbolEl.attr('position', point);\n data.setItemGraphicEl(newIdx, symbolEl);\n group.add(symbolEl);\n }\n })\n .update(function (newIdx, oldIdx) {\n var symbolEl = oldData.getItemGraphicEl(oldIdx);\n var point = data.getItemLayout(newIdx);\n if (!symbolNeedsDraw(data, newIdx, isIgnore)) {\n group.remove(symbolEl);\n return;\n }\n if (!symbolEl) {\n symbolEl = new SymbolCtor(data, newIdx);\n symbolEl.attr('position', point);\n }\n else {\n symbolEl.updateData(data, newIdx, seriesScope);\n graphic.updateProps(symbolEl, {\n position: point\n }, seriesModel);\n }\n\n // Add back\n group.add(symbolEl);\n\n data.setItemGraphicEl(newIdx, symbolEl);\n })\n .remove(function (oldIdx) {\n var el = oldData.getItemGraphicEl(oldIdx);\n el && el.fadeOut(function () {\n group.remove(el);\n });\n })\n .execute();\n\n this._data = data;\n};\n\nsymbolDrawProto.updateLayout = function () {\n var data = this._data;\n if (data) {\n // Not use animation\n data.eachItemGraphicEl(function (el, idx) {\n var point = data.getItemLayout(idx);\n el.attr('position', point);\n });\n }\n};\n\nsymbolDrawProto.remove = function (enableAnimation) {\n var group = this.group;\n var data = this._data;\n if (data) {\n if (enableAnimation) {\n data.eachItemGraphicEl(function (el) {\n el.fadeOut(function () {\n group.remove(el);\n });\n });\n }\n else {\n group.removeAll();\n }\n }\n};\n\nexport default SymbolDraw;","\n// var arrayDiff = require('zrender/src/core/arrayDiff');\n// 'zrender/src/core/arrayDiff' has been used before, but it did\n// not do well in performance when roam with fixed dataZoom window.\n\nfunction sign(val) {\n return val >= 0 ? 1 : -1;\n}\n\nfunction getStackedOnPoint(coordSys, data, idx) {\n var baseAxis = coordSys.getBaseAxis();\n var valueAxis = coordSys.getOtherAxis(baseAxis);\n var valueStart = baseAxis.onZero\n ? 0 : valueAxis.scale.getExtent()[0];\n\n var valueDim = valueAxis.dim;\n var baseDataOffset = valueDim === 'x' || valueDim === 'radius' ? 1 : 0;\n\n var stackedOnSameSign;\n var stackedOn = data.stackedOn;\n var val = data.get(valueDim, idx);\n // Find first stacked value with same sign\n while (stackedOn &&\n sign(stackedOn.get(valueDim, idx)) === sign(val)\n ) {\n stackedOnSameSign = stackedOn;\n break;\n }\n var stackedData = [];\n stackedData[baseDataOffset] = data.get(baseAxis.dim, idx);\n stackedData[1 - baseDataOffset] = stackedOnSameSign\n ? stackedOnSameSign.get(valueDim, idx, true) : valueStart;\n\n return coordSys.dataToPoint(stackedData);\n}\n\n// function convertToIntId(newIdList, oldIdList) {\n// // Generate int id instead of string id.\n// // Compare string maybe slow in score function of arrDiff\n\n// // Assume id in idList are all unique\n// var idIndicesMap = {};\n// var idx = 0;\n// for (var i = 0; i < newIdList.length; i++) {\n// idIndicesMap[newIdList[i]] = idx;\n// newIdList[i] = idx++;\n// }\n// for (var i = 0; i < oldIdList.length; i++) {\n// var oldId = oldIdList[i];\n// // Same with newIdList\n// if (idIndicesMap[oldId]) {\n// oldIdList[i] = idIndicesMap[oldId];\n// }\n// else {\n// oldIdList[i] = idx++;\n// }\n// }\n// }\n\nfunction diffData(oldData, newData) {\n var diffResult = [];\n\n newData.diff(oldData)\n .add(function (idx) {\n diffResult.push({cmd: '+', idx: idx});\n })\n .update(function (newIdx, oldIdx) {\n diffResult.push({cmd: '=', idx: oldIdx, idx1: newIdx});\n })\n .remove(function (idx) {\n diffResult.push({cmd: '-', idx: idx});\n })\n .execute();\n\n return diffResult;\n}\n\nexport default function (\n oldData, newData,\n oldStackedOnPoints, newStackedOnPoints,\n oldCoordSys, newCoordSys\n) {\n var diff = diffData(oldData, newData);\n\n // var newIdList = newData.mapArray(newData.getId);\n // var oldIdList = oldData.mapArray(oldData.getId);\n\n // convertToIntId(newIdList, oldIdList);\n\n // // FIXME One data ?\n // diff = arrayDiff(oldIdList, newIdList);\n\n var currPoints = [];\n var nextPoints = [];\n // Points for stacking base line\n var currStackedPoints = [];\n var nextStackedPoints = [];\n\n var status = [];\n var sortedIndices = [];\n var rawIndices = [];\n var dims = newCoordSys.dimensions;\n for (var i = 0; i < diff.length; i++) {\n var diffItem = diff[i];\n var pointAdded = true;\n\n // FIXME, animation is not so perfect when dataZoom window moves fast\n // Which is in case remvoing or add more than one data in the tail or head\n switch (diffItem.cmd) {\n case '=':\n var currentPt = oldData.getItemLayout(diffItem.idx);\n var nextPt = newData.getItemLayout(diffItem.idx1);\n // If previous data is NaN, use next point directly\n if (isNaN(currentPt[0]) || isNaN(currentPt[1])) {\n currentPt = nextPt.slice();\n }\n currPoints.push(currentPt);\n nextPoints.push(nextPt);\n\n currStackedPoints.push(oldStackedOnPoints[diffItem.idx]);\n nextStackedPoints.push(newStackedOnPoints[diffItem.idx1]);\n\n rawIndices.push(newData.getRawIndex(diffItem.idx1));\n break;\n case '+':\n var idx = diffItem.idx;\n currPoints.push(\n oldCoordSys.dataToPoint([\n newData.get(dims[0], idx, true), newData.get(dims[1], idx, true)\n ])\n );\n\n nextPoints.push(newData.getItemLayout(idx).slice());\n\n currStackedPoints.push(\n getStackedOnPoint(oldCoordSys, newData, idx)\n );\n nextStackedPoints.push(newStackedOnPoints[idx]);\n\n rawIndices.push(newData.getRawIndex(idx));\n break;\n case '-':\n var idx = diffItem.idx;\n var rawIndex = oldData.getRawIndex(idx);\n // Data is replaced. In the case of dynamic data queue\n // FIXME FIXME FIXME\n if (rawIndex !== idx) {\n currPoints.push(oldData.getItemLayout(idx));\n nextPoints.push(newCoordSys.dataToPoint([\n oldData.get(dims[0], idx, true), oldData.get(dims[1], idx, true)\n ]));\n\n currStackedPoints.push(oldStackedOnPoints[idx]);\n nextStackedPoints.push(\n getStackedOnPoint(\n newCoordSys, oldData, idx\n )\n );\n\n rawIndices.push(rawIndex);\n }\n else {\n pointAdded = false;\n }\n }\n\n // Original indices\n if (pointAdded) {\n status.push(diffItem);\n sortedIndices.push(sortedIndices.length);\n }\n }\n\n // Diff result may be crossed if all items are changed\n // Sort by data index\n sortedIndices.sort(function (a, b) {\n return rawIndices[a] - rawIndices[b];\n });\n\n var sortedCurrPoints = [];\n var sortedNextPoints = [];\n\n var sortedCurrStackedPoints = [];\n var sortedNextStackedPoints = [];\n\n var sortedStatus = [];\n for (var i = 0; i < sortedIndices.length; i++) {\n var idx = sortedIndices[i];\n sortedCurrPoints[i] = currPoints[idx];\n sortedNextPoints[i] = nextPoints[idx];\n\n sortedCurrStackedPoints[i] = currStackedPoints[idx];\n sortedNextStackedPoints[i] = nextStackedPoints[idx];\n\n sortedStatus[i] = status[idx];\n }\n\n return {\n current: sortedCurrPoints,\n next: sortedNextPoints,\n\n stackedOnCurrent: sortedCurrStackedPoints,\n stackedOnNext: sortedNextStackedPoints,\n\n status: sortedStatus\n };\n}","// Poly path support NaN point\n\nimport Path from 'zrender/src/graphic/Path';\nimport * as vec2 from 'zrender/src/core/vector';\nimport fixClipWithShadow from 'zrender/src/graphic/helper/fixClipWithShadow';\n\nvar vec2Min = vec2.min;\nvar vec2Max = vec2.max;\n\nvar scaleAndAdd = vec2.scaleAndAdd;\nvar v2Copy = vec2.copy;\n\n// Temporary variable\nvar v = [];\nvar cp0 = [];\nvar cp1 = [];\n\nfunction isPointNull(p) {\n return isNaN(p[0]) || isNaN(p[1]);\n}\n\nfunction drawSegment(\n ctx, points, start, segLen, allLen,\n dir, smoothMin, smoothMax, smooth, smoothMonotone, connectNulls\n) {\n var prevIdx = 0;\n var idx = start;\n for (var k = 0; k < segLen; k++) {\n var p = points[idx];\n if (idx >= allLen || idx < 0) {\n break;\n }\n if (isPointNull(p)) {\n if (connectNulls) {\n idx += dir;\n continue;\n }\n break;\n }\n\n if (idx === start) {\n ctx[dir > 0 ? 'moveTo' : 'lineTo'](p[0], p[1]);\n v2Copy(cp0, p);\n }\n else {\n if (smooth > 0) {\n var nextIdx = idx + dir;\n var nextP = points[nextIdx];\n if (connectNulls) {\n // Find next point not null\n while (nextP && isPointNull(points[nextIdx])) {\n nextIdx += dir;\n nextP = points[nextIdx];\n }\n }\n\n var ratioNextSeg = 0.5;\n var prevP = points[prevIdx];\n var nextP = points[nextIdx];\n // Last point\n if (!nextP || isPointNull(nextP)) {\n v2Copy(cp1, p);\n }\n else {\n // If next data is null in not connect case\n if (isPointNull(nextP) && !connectNulls) {\n nextP = p;\n }\n\n vec2.sub(v, nextP, prevP);\n\n var lenPrevSeg;\n var lenNextSeg;\n if (smoothMonotone === 'x' || smoothMonotone === 'y') {\n var dim = smoothMonotone === 'x' ? 0 : 1;\n lenPrevSeg = Math.abs(p[dim] - prevP[dim]);\n lenNextSeg = Math.abs(p[dim] - nextP[dim]);\n }\n else {\n lenPrevSeg = vec2.dist(p, prevP);\n lenNextSeg = vec2.dist(p, nextP);\n }\n\n // Use ratio of seg length\n ratioNextSeg = lenNextSeg / (lenNextSeg + lenPrevSeg);\n\n scaleAndAdd(cp1, p, v, -smooth * (1 - ratioNextSeg));\n }\n // Smooth constraint\n vec2Min(cp0, cp0, smoothMax);\n vec2Max(cp0, cp0, smoothMin);\n vec2Min(cp1, cp1, smoothMax);\n vec2Max(cp1, cp1, smoothMin);\n\n ctx.bezierCurveTo(\n cp0[0], cp0[1],\n cp1[0], cp1[1],\n p[0], p[1]\n );\n // cp0 of next segment\n scaleAndAdd(cp0, p, v, smooth * ratioNextSeg);\n }\n else {\n ctx.lineTo(p[0], p[1]);\n }\n }\n\n prevIdx = idx;\n idx += dir;\n }\n\n return k;\n}\n\nfunction getBoundingBox(points, smoothConstraint) {\n var ptMin = [Infinity, Infinity];\n var ptMax = [-Infinity, -Infinity];\n if (smoothConstraint) {\n for (var i = 0; i < points.length; i++) {\n var pt = points[i];\n if (pt[0] < ptMin[0]) { ptMin[0] = pt[0]; }\n if (pt[1] < ptMin[1]) { ptMin[1] = pt[1]; }\n if (pt[0] > ptMax[0]) { ptMax[0] = pt[0]; }\n if (pt[1] > ptMax[1]) { ptMax[1] = pt[1]; }\n }\n }\n return {\n min: smoothConstraint ? ptMin : ptMax,\n max: smoothConstraint ? ptMax : ptMin\n };\n}\n\nexport var Polyline = Path.extend({\n\n type: 'ec-polyline',\n\n shape: {\n points: [],\n\n smooth: 0,\n\n smoothConstraint: true,\n\n smoothMonotone: null,\n\n connectNulls: false\n },\n\n style: {\n fill: null,\n\n stroke: '#000'\n },\n\n brush: fixClipWithShadow(Path.prototype.brush),\n\n buildPath: function (ctx, shape) {\n var points = shape.points;\n\n var i = 0;\n var len = points.length;\n\n var result = getBoundingBox(points, shape.smoothConstraint);\n\n if (shape.connectNulls) {\n // Must remove first and last null values avoid draw error in polygon\n for (; len > 0; len--) {\n if (!isPointNull(points[len - 1])) {\n break;\n }\n }\n for (; i < len; i++) {\n if (!isPointNull(points[i])) {\n break;\n }\n }\n }\n while (i < len) {\n i += drawSegment(\n ctx, points, i, len, len,\n 1, result.min, result.max, shape.smooth,\n shape.smoothMonotone, shape.connectNulls\n ) + 1;\n }\n }\n});\n\nexport var Polygon = Path.extend({\n\n type: 'ec-polygon',\n\n shape: {\n points: [],\n\n // Offset between stacked base points and points\n stackedOnPoints: [],\n\n smooth: 0,\n\n stackedOnSmooth: 0,\n\n smoothConstraint: true,\n\n smoothMonotone: null,\n\n connectNulls: false\n },\n\n brush: fixClipWithShadow(Path.prototype.brush),\n\n buildPath: function (ctx, shape) {\n var points = shape.points;\n var stackedOnPoints = shape.stackedOnPoints;\n\n var i = 0;\n var len = points.length;\n var smoothMonotone = shape.smoothMonotone;\n var bbox = getBoundingBox(points, shape.smoothConstraint);\n var stackedOnBBox = getBoundingBox(stackedOnPoints, shape.smoothConstraint);\n\n if (shape.connectNulls) {\n // Must remove first and last null values avoid draw error in polygon\n for (; len > 0; len--) {\n if (!isPointNull(points[len - 1])) {\n break;\n }\n }\n for (; i < len; i++) {\n if (!isPointNull(points[i])) {\n break;\n }\n }\n }\n while (i < len) {\n var k = drawSegment(\n ctx, points, i, len, len,\n 1, bbox.min, bbox.max, shape.smooth,\n smoothMonotone, shape.connectNulls\n );\n drawSegment(\n ctx, stackedOnPoints, i + k - 1, k, len,\n -1, stackedOnBBox.min, stackedOnBBox.max, shape.stackedOnSmooth,\n smoothMonotone, shape.connectNulls\n );\n i += k + 1;\n\n ctx.closePath();\n }\n }\n});\n","// FIXME step not support polar\n\nimport {__DEV__} from '../../config';\nimport * as zrUtil from 'zrender/src/core/util';\nimport SymbolDraw from '../helper/SymbolDraw';\nimport SymbolClz from '../helper/Symbol';\nimport lineAnimationDiff from './lineAnimationDiff';\nimport * as graphic from '../../util/graphic';\nimport * as modelUtil from '../../util/model';\nimport {Polyline, Polygon} from './poly';\nimport ChartView from '../../view/Chart';\n\nfunction isPointsSame(points1, points2) {\n if (points1.length !== points2.length) {\n return;\n }\n for (var i = 0; i < points1.length; i++) {\n var p1 = points1[i];\n var p2 = points2[i];\n if (p1[0] !== p2[0] || p1[1] !== p2[1]) {\n return;\n }\n }\n return true;\n}\n\nfunction getSmooth(smooth) {\n return typeof (smooth) === 'number' ? smooth : (smooth ? 0.3 : 0);\n}\n\nfunction getAxisExtentWithGap(axis) {\n var extent = axis.getGlobalExtent();\n if (axis.onBand) {\n // Remove extra 1px to avoid line miter in clipped edge\n var halfBandWidth = axis.getBandWidth() / 2 - 1;\n var dir = extent[1] > extent[0] ? 1 : -1;\n extent[0] += dir * halfBandWidth;\n extent[1] -= dir * halfBandWidth;\n }\n return extent;\n}\n\nfunction sign(val) {\n return val >= 0 ? 1 : -1;\n}\n\n/**\n * @param {module:echarts/coord/cartesian/Cartesian2D|module:echarts/coord/polar/Polar} coordSys\n * @param {module:echarts/data/List} data\n * @param {Array.<Array.<number>>} points\n * @private\n */\nfunction getStackedOnPoints(coordSys, data) {\n var baseAxis = coordSys.getBaseAxis();\n var valueAxis = coordSys.getOtherAxis(baseAxis);\n\n var valueStart = 0;\n if (!baseAxis.onZero) {\n var extent = valueAxis.scale.getExtent();\n if (extent[0] > 0) {\n // Both positive\n valueStart = extent[0];\n }\n else if (extent[1] < 0) {\n // Both negative\n valueStart = extent[1];\n }\n // If is one positive, and one negative, onZero shall be true\n }\n\n var valueDim = valueAxis.dim;\n\n var baseDataOffset = valueDim === 'x' || valueDim === 'radius' ? 1 : 0;\n\n return data.mapArray([valueDim], function (val, idx) {\n var stackedOnSameSign;\n var stackedOn = data.stackedOn;\n // Find first stacked value with same sign\n while (stackedOn &&\n sign(stackedOn.get(valueDim, idx)) === sign(val)\n ) {\n stackedOnSameSign = stackedOn;\n break;\n }\n var stackedData = [];\n stackedData[baseDataOffset] = data.get(baseAxis.dim, idx);\n stackedData[1 - baseDataOffset] = stackedOnSameSign\n ? stackedOnSameSign.get(valueDim, idx, true) : valueStart;\n\n return coordSys.dataToPoint(stackedData);\n }, true);\n}\n\nfunction createGridClipShape(cartesian, hasAnimation, seriesModel) {\n var xExtent = getAxisExtentWithGap(cartesian.getAxis('x'));\n var yExtent = getAxisExtentWithGap(cartesian.getAxis('y'));\n var isHorizontal = cartesian.getBaseAxis().isHorizontal();\n\n var x = Math.min(xExtent[0], xExtent[1]);\n var y = Math.min(yExtent[0], yExtent[1]);\n var width = Math.max(xExtent[0], xExtent[1]) - x;\n var height = Math.max(yExtent[0], yExtent[1]) - y;\n var lineWidth = seriesModel.get('lineStyle.normal.width') || 2;\n // Expand clip shape to avoid clipping when line value exceeds axis\n var expandSize = seriesModel.get('clipOverflow') ? lineWidth / 2 : Math.max(width, height);\n if (isHorizontal) {\n y -= expandSize;\n height += expandSize * 2;\n }\n else {\n x -= expandSize;\n width += expandSize * 2;\n }\n\n var clipPath = new graphic.Rect({\n shape: {\n x: x,\n y: y,\n width: width,\n height: height\n }\n });\n\n if (hasAnimation) {\n clipPath.shape[isHorizontal ? 'width' : 'height'] = 0;\n graphic.initProps(clipPath, {\n shape: {\n width: width,\n height: height\n }\n }, seriesModel);\n }\n\n return clipPath;\n}\n\nfunction createPolarClipShape(polar, hasAnimation, seriesModel) {\n var angleAxis = polar.getAngleAxis();\n var radiusAxis = polar.getRadiusAxis();\n\n var radiusExtent = radiusAxis.getExtent();\n var angleExtent = angleAxis.getExtent();\n\n var RADIAN = Math.PI / 180;\n\n var clipPath = new graphic.Sector({\n shape: {\n cx: polar.cx,\n cy: polar.cy,\n r0: radiusExtent[0],\n r: radiusExtent[1],\n startAngle: -angleExtent[0] * RADIAN,\n endAngle: -angleExtent[1] * RADIAN,\n clockwise: angleAxis.inverse\n }\n });\n\n if (hasAnimation) {\n clipPath.shape.endAngle = -angleExtent[0] * RADIAN;\n graphic.initProps(clipPath, {\n shape: {\n endAngle: -angleExtent[1] * RADIAN\n }\n }, seriesModel);\n }\n\n return clipPath;\n}\n\nfunction createClipShape(coordSys, hasAnimation, seriesModel) {\n return coordSys.type === 'polar'\n ? createPolarClipShape(coordSys, hasAnimation, seriesModel)\n : createGridClipShape(coordSys, hasAnimation, seriesModel);\n}\n\nfunction turnPointsIntoStep(points, coordSys, stepTurnAt) {\n var baseAxis = coordSys.getBaseAxis();\n var baseIndex = baseAxis.dim === 'x' || baseAxis.dim === 'radius' ? 0 : 1;\n\n var stepPoints = [];\n for (var i = 0; i < points.length - 1; i++) {\n var nextPt = points[i + 1];\n var pt = points[i];\n stepPoints.push(pt);\n\n var stepPt = [];\n switch (stepTurnAt) {\n case 'end':\n stepPt[baseIndex] = nextPt[baseIndex];\n stepPt[1 - baseIndex] = pt[1 - baseIndex];\n // default is start\n stepPoints.push(stepPt);\n break;\n case 'middle':\n // default is start\n var middle = (pt[baseIndex] + nextPt[baseIndex]) / 2;\n var stepPt2 = [];\n stepPt[baseIndex] = stepPt2[baseIndex] = middle;\n stepPt[1 - baseIndex] = pt[1 - baseIndex];\n stepPt2[1 - baseIndex] = nextPt[1 - baseIndex];\n stepPoints.push(stepPt);\n stepPoints.push(stepPt2);\n break;\n default:\n stepPt[baseIndex] = pt[baseIndex];\n stepPt[1 - baseIndex] = nextPt[1 - baseIndex];\n // default is start\n stepPoints.push(stepPt);\n }\n }\n // Last points\n points[i] && stepPoints.push(points[i]);\n return stepPoints;\n}\n\nfunction getVisualGradient(data, coordSys) {\n var visualMetaList = data.getVisual('visualMeta');\n if (!visualMetaList || !visualMetaList.length || !data.count()) {\n // When data.count() is 0, gradient range can not be calculated.\n return;\n }\n\n var visualMeta;\n for (var i = visualMetaList.length - 1; i >= 0; i--) {\n // Can only be x or y\n if (visualMetaList[i].dimension < 2) {\n visualMeta = visualMetaList[i];\n break;\n }\n }\n if (!visualMeta || coordSys.type !== 'cartesian2d') {\n if (__DEV__) {\n console.warn('Visual map on line style only support x or y dimension.');\n }\n return;\n }\n\n // If the area to be rendered is bigger than area defined by LinearGradient,\n // the canvas spec prescribes that the color of the first stop and the last\n // stop should be used. But if two stops are added at offset 0, in effect\n // browsers use the color of the second stop to render area outside\n // LinearGradient. So we can only infinitesimally extend area defined in\n // LinearGradient to render `outerColors`.\n\n var dimension = visualMeta.dimension;\n var dimName = data.dimensions[dimension];\n var axis = coordSys.getAxis(dimName);\n\n // dataToCoor mapping may not be linear, but must be monotonic.\n var colorStops = zrUtil.map(visualMeta.stops, function (stop) {\n return {\n coord: axis.toGlobalCoord(axis.dataToCoord(stop.value)),\n color: stop.color\n };\n });\n var stopLen = colorStops.length;\n var outerColors = visualMeta.outerColors.slice();\n\n if (stopLen && colorStops[0].coord > colorStops[stopLen - 1].coord) {\n colorStops.reverse();\n outerColors.reverse();\n }\n\n var tinyExtent = 10; // Arbitrary value: 10px\n var minCoord = colorStops[0].coord - tinyExtent;\n var maxCoord = colorStops[stopLen - 1].coord + tinyExtent;\n var coordSpan = maxCoord - minCoord;\n\n if (coordSpan < 1e-3) {\n return 'transparent';\n }\n\n zrUtil.each(colorStops, function (stop) {\n stop.offset = (stop.coord - minCoord) / coordSpan;\n });\n colorStops.push({\n offset: stopLen ? colorStops[stopLen - 1].offset : 0.5,\n color: outerColors[1] || 'transparent'\n });\n colorStops.unshift({ // notice colorStops.length have been changed.\n offset: stopLen ? colorStops[0].offset : 0.5,\n color: outerColors[0] || 'transparent'\n });\n\n // zrUtil.each(colorStops, function (colorStop) {\n // // Make sure each offset has rounded px to avoid not sharp edge\n // colorStop.offset = (Math.round(colorStop.offset * (end - start) + start) - start) / (end - start);\n // });\n\n var gradient = new graphic.LinearGradient(0, 0, 0, 0, colorStops, true);\n gradient[dimName] = minCoord;\n gradient[dimName + '2'] = maxCoord;\n\n return gradient;\n}\n\nexport default ChartView.extend({\n\n type: 'line',\n\n init: function () {\n var lineGroup = new graphic.Group();\n\n var symbolDraw = new SymbolDraw();\n this.group.add(symbolDraw.group);\n\n this._symbolDraw = symbolDraw;\n this._lineGroup = lineGroup;\n },\n\n render: function (seriesModel, ecModel, api) {\n var coordSys = seriesModel.coordinateSystem;\n var group = this.group;\n var data = seriesModel.getData();\n var lineStyleModel = seriesModel.getModel('lineStyle.normal');\n var areaStyleModel = seriesModel.getModel('areaStyle.normal');\n\n var points = data.mapArray(data.getItemLayout, true);\n\n var isCoordSysPolar = coordSys.type === 'polar';\n var prevCoordSys = this._coordSys;\n\n var symbolDraw = this._symbolDraw;\n var polyline = this._polyline;\n var polygon = this._polygon;\n\n var lineGroup = this._lineGroup;\n\n var hasAnimation = seriesModel.get('animation');\n\n var isAreaChart = !areaStyleModel.isEmpty();\n var stackedOnPoints = getStackedOnPoints(coordSys, data);\n\n var showSymbol = seriesModel.get('showSymbol');\n\n var isSymbolIgnore = showSymbol && !isCoordSysPolar && !seriesModel.get('showAllSymbol')\n && this._getSymbolIgnoreFunc(data, coordSys);\n\n // Remove temporary symbols\n var oldData = this._data;\n oldData && oldData.eachItemGraphicEl(function (el, idx) {\n if (el.__temp) {\n group.remove(el);\n oldData.setItemGraphicEl(idx, null);\n }\n });\n\n // Remove previous created symbols if showSymbol changed to false\n if (!showSymbol) {\n symbolDraw.remove();\n }\n\n group.add(lineGroup);\n\n // FIXME step not support polar\n var step = !isCoordSysPolar && seriesModel.get('step');\n // Initialization animation or coordinate system changed\n if (\n !(polyline && prevCoordSys.type === coordSys.type && step === this._step)\n ) {\n showSymbol && symbolDraw.updateData(data, isSymbolIgnore);\n\n if (step) {\n // TODO If stacked series is not step\n points = turnPointsIntoStep(points, coordSys, step);\n stackedOnPoints = turnPointsIntoStep(stackedOnPoints, coordSys, step);\n }\n\n polyline = this._newPolyline(points, coordSys, hasAnimation);\n if (isAreaChart) {\n polygon = this._newPolygon(\n points, stackedOnPoints,\n coordSys, hasAnimation\n );\n }\n lineGroup.setClipPath(createClipShape(coordSys, true, seriesModel));\n }\n else {\n if (isAreaChart && !polygon) {\n // If areaStyle is added\n polygon = this._newPolygon(\n points, stackedOnPoints,\n coordSys, hasAnimation\n );\n }\n else if (polygon && !isAreaChart) {\n // If areaStyle is removed\n lineGroup.remove(polygon);\n polygon = this._polygon = null;\n }\n\n // Update clipPath\n lineGroup.setClipPath(createClipShape(coordSys, false, seriesModel));\n\n // Always update, or it is wrong in the case turning on legend\n // because points are not changed\n showSymbol && symbolDraw.updateData(data, isSymbolIgnore);\n\n // Stop symbol animation and sync with line points\n // FIXME performance?\n data.eachItemGraphicEl(function (el) {\n el.stopAnimation(true);\n });\n\n // In the case data zoom triggerred refreshing frequently\n // Data may not change if line has a category axis. So it should animate nothing\n if (!isPointsSame(this._stackedOnPoints, stackedOnPoints)\n || !isPointsSame(this._points, points)\n ) {\n if (hasAnimation) {\n this._updateAnimation(\n data, stackedOnPoints, coordSys, api, step\n );\n }\n else {\n // Not do it in update with animation\n if (step) {\n // TODO If stacked series is not step\n points = turnPointsIntoStep(points, coordSys, step);\n stackedOnPoints = turnPointsIntoStep(stackedOnPoints, coordSys, step);\n }\n\n polyline.setShape({\n points: points\n });\n polygon && polygon.setShape({\n points: points,\n stackedOnPoints: stackedOnPoints\n });\n }\n }\n }\n\n var visualColor = getVisualGradient(data, coordSys) || data.getVisual('color');\n\n polyline.useStyle(zrUtil.defaults(\n // Use color in lineStyle first\n lineStyleModel.getLineStyle(),\n {\n fill: 'none',\n stroke: visualColor,\n lineJoin: 'bevel'\n }\n ));\n\n var smooth = seriesModel.get('smooth');\n smooth = getSmooth(seriesModel.get('smooth'));\n polyline.setShape({\n smooth: smooth,\n smoothMonotone: seriesModel.get('smoothMonotone'),\n connectNulls: seriesModel.get('connectNulls')\n });\n\n if (polygon) {\n var stackedOn = data.stackedOn;\n var stackedOnSmooth = 0;\n\n polygon.useStyle(zrUtil.defaults(\n areaStyleModel.getAreaStyle(),\n {\n fill: visualColor,\n opacity: 0.7,\n lineJoin: 'bevel'\n }\n ));\n\n if (stackedOn) {\n var stackedOnSeries = stackedOn.hostModel;\n stackedOnSmooth = getSmooth(stackedOnSeries.get('smooth'));\n }\n\n polygon.setShape({\n smooth: smooth,\n stackedOnSmooth: stackedOnSmooth,\n smoothMonotone: seriesModel.get('smoothMonotone'),\n connectNulls: seriesModel.get('connectNulls')\n });\n }\n\n this._data = data;\n // Save the coordinate system for transition animation when data changed\n this._coordSys = coordSys;\n this._stackedOnPoints = stackedOnPoints;\n this._points = points;\n this._step = step;\n },\n\n dispose: function () {},\n\n highlight: function (seriesModel, ecModel, api, payload) {\n var data = seriesModel.getData();\n var dataIndex = modelUtil.queryDataIndex(data, payload);\n\n if (!(dataIndex instanceof Array) && dataIndex != null && dataIndex >= 0) {\n var symbol = data.getItemGraphicEl(dataIndex);\n if (!symbol) {\n // Create a temporary symbol if it is not exists\n var pt = data.getItemLayout(dataIndex);\n if (!pt) {\n // Null data\n return;\n }\n symbol = new SymbolClz(data, dataIndex);\n symbol.position = pt;\n symbol.setZ(\n seriesModel.get('zlevel'),\n seriesModel.get('z')\n );\n symbol.ignore = isNaN(pt[0]) || isNaN(pt[1]);\n symbol.__temp = true;\n data.setItemGraphicEl(dataIndex, symbol);\n\n // Stop scale animation\n symbol.stopSymbolAnimation(true);\n\n this.group.add(symbol);\n }\n symbol.highlight();\n }\n else {\n // Highlight whole series\n ChartView.prototype.highlight.call(\n this, seriesModel, ecModel, api, payload\n );\n }\n },\n\n downplay: function (seriesModel, ecModel, api, payload) {\n var data = seriesModel.getData();\n var dataIndex = modelUtil.queryDataIndex(data, payload);\n if (dataIndex != null && dataIndex >= 0) {\n var symbol = data.getItemGraphicEl(dataIndex);\n if (symbol) {\n if (symbol.__temp) {\n data.setItemGraphicEl(dataIndex, null);\n this.group.remove(symbol);\n }\n else {\n symbol.downplay();\n }\n }\n }\n else {\n // FIXME\n // can not downplay completely.\n // Downplay whole series\n ChartView.prototype.downplay.call(\n this, seriesModel, ecModel, api, payload\n );\n }\n },\n\n /**\n * @param {module:zrender/container/Group} group\n * @param {Array.<Array.<number>>} points\n * @private\n */\n _newPolyline: function (points) {\n var polyline = this._polyline;\n // Remove previous created polyline\n if (polyline) {\n this._lineGroup.remove(polyline);\n }\n\n polyline = new Polyline({\n shape: {\n points: points\n },\n silent: true,\n z2: 10\n });\n\n this._lineGroup.add(polyline);\n\n this._polyline = polyline;\n\n return polyline;\n },\n\n /**\n * @param {module:zrender/container/Group} group\n * @param {Array.<Array.<number>>} stackedOnPoints\n * @param {Array.<Array.<number>>} points\n * @private\n */\n _newPolygon: function (points, stackedOnPoints) {\n var polygon = this._polygon;\n // Remove previous created polygon\n if (polygon) {\n this._lineGroup.remove(polygon);\n }\n\n polygon = new Polygon({\n shape: {\n points: points,\n stackedOnPoints: stackedOnPoints\n },\n silent: true\n });\n\n this._lineGroup.add(polygon);\n\n this._polygon = polygon;\n return polygon;\n },\n /**\n * @private\n */\n _getSymbolIgnoreFunc: function (data, coordSys) {\n var categoryAxis = coordSys.getAxesByScale('ordinal')[0];\n // `getLabelInterval` is provided by echarts/component/axis\n if (categoryAxis && categoryAxis.isLabelIgnored) {\n return zrUtil.bind(categoryAxis.isLabelIgnored, categoryAxis);\n }\n },\n\n /**\n * @private\n */\n // FIXME Two value axis\n _updateAnimation: function (data, stackedOnPoints, coordSys, api, step) {\n var polyline = this._polyline;\n var polygon = this._polygon;\n var seriesModel = data.hostModel;\n\n var diff = lineAnimationDiff(\n this._data, data,\n this._stackedOnPoints, stackedOnPoints,\n this._coordSys, coordSys\n );\n\n var current = diff.current;\n var stackedOnCurrent = diff.stackedOnCurrent;\n var next = diff.next;\n var stackedOnNext = diff.stackedOnNext;\n if (step) {\n // TODO If stacked series is not step\n current = turnPointsIntoStep(diff.current, coordSys, step);\n stackedOnCurrent = turnPointsIntoStep(diff.stackedOnCurrent, coordSys, step);\n next = turnPointsIntoStep(diff.next, coordSys, step);\n stackedOnNext = turnPointsIntoStep(diff.stackedOnNext, coordSys, step);\n }\n // `diff.current` is subset of `current` (which should be ensured by\n // turnPointsIntoStep), so points in `__points` can be updated when\n // points in `current` are update during animation.\n polyline.shape.__points = diff.current;\n polyline.shape.points = current;\n\n graphic.updateProps(polyline, {\n shape: {\n points: next\n }\n }, seriesModel);\n\n if (polygon) {\n polygon.setShape({\n points: current,\n stackedOnPoints: stackedOnCurrent\n });\n graphic.updateProps(polygon, {\n shape: {\n points: next,\n stackedOnPoints: stackedOnNext\n }\n }, seriesModel);\n }\n\n var updatedDataInfo = [];\n var diffStatus = diff.status;\n\n for (var i = 0; i < diffStatus.length; i++) {\n var cmd = diffStatus[i].cmd;\n if (cmd === '=') {\n var el = data.getItemGraphicEl(diffStatus[i].idx1);\n if (el) {\n updatedDataInfo.push({\n el: el,\n ptIdx: i // Index of points\n });\n }\n }\n }\n\n if (polyline.animators && polyline.animators.length) {\n polyline.animators[0].during(function () {\n for (var i = 0; i < updatedDataInfo.length; i++) {\n var el = updatedDataInfo[i].el;\n el.attr('position', polyline.shape.__points[updatedDataInfo[i].ptIdx]);\n }\n });\n }\n },\n\n remove: function (ecModel) {\n var group = this.group;\n var oldData = this._data;\n this._lineGroup.removeAll();\n this._symbolDraw.remove(true);\n // Remove temporary created elements when highlighting\n oldData && oldData.eachItemGraphicEl(function (el, idx) {\n if (el.__temp) {\n group.remove(el);\n oldData.setItemGraphicEl(idx, null);\n }\n });\n\n this._polyline =\n this._polygon =\n this._coordSys =\n this._points =\n this._stackedOnPoints =\n this._data = null;\n }\n});","\nexport default function (seriesType, defaultSymbolType, legendSymbol, ecModel, api) {\n\n // Encoding visual for all series include which is filtered for legend drawing\n ecModel.eachRawSeriesByType(seriesType, function (seriesModel) {\n var data = seriesModel.getData();\n\n var symbolType = seriesModel.get('symbol') || defaultSymbolType;\n var symbolSize = seriesModel.get('symbolSize');\n\n data.setVisual({\n legendSymbol: legendSymbol || symbolType,\n symbol: symbolType,\n symbolSize: symbolSize\n });\n\n // Only visible series has each data be visual encoded\n if (!ecModel.isSeriesFiltered(seriesModel)) {\n if (typeof symbolSize === 'function') {\n data.each(function (idx) {\n var rawValue = seriesModel.getRawValue(idx);\n // FIXME\n var params = seriesModel.getDataParams(idx);\n data.setItemVisual(idx, 'symbolSize', symbolSize(rawValue, params));\n });\n }\n data.each(function (idx) {\n var itemModel = data.getItemModel(idx);\n var itemSymbolType = itemModel.getShallow('symbol', true);\n var itemSymbolSize = itemModel.getShallow('symbolSize', true);\n // If has item symbol\n if (itemSymbolType != null) {\n data.setItemVisual(idx, 'symbol', itemSymbolType);\n }\n if (itemSymbolSize != null) {\n // PENDING Transform symbolSize ?\n data.setItemVisual(idx, 'symbolSize', itemSymbolSize);\n }\n });\n }\n });\n}","\nexport default function (seriesType, ecModel) {\n ecModel.eachSeriesByType(seriesType, function (seriesModel) {\n var data = seriesModel.getData();\n var coordSys = seriesModel.coordinateSystem;\n\n if (!coordSys) {\n return;\n }\n\n var dims = [];\n var coordDims = coordSys.dimensions;\n for (var i = 0; i < coordDims.length; i++) {\n dims.push(seriesModel.coordDimToDataDim(coordSys.dimensions[i])[0]);\n }\n\n if (dims.length === 1) {\n data.each(dims[0], function (x, idx) {\n // Also {Array.<number>}, not undefined to avoid if...else... statement\n data.setItemLayout(idx, isNaN(x) ? [NaN, NaN] : coordSys.dataToPoint(x));\n });\n }\n else if (dims.length === 2) {\n data.each(dims, function (x, y, idx) {\n // Also {Array.<number>}, not undefined to avoid if...else... statement\n data.setItemLayout(\n idx, (isNaN(x) || isNaN(y)) ? [NaN, NaN] : coordSys.dataToPoint([x, y])\n );\n }, true);\n }\n });\n}","\nvar samplers = {\n average: function (frame) {\n var sum = 0;\n var count = 0;\n for (var i = 0; i < frame.length; i++) {\n if (!isNaN(frame[i])) {\n sum += frame[i];\n count++;\n }\n }\n // Return NaN if count is 0\n return count === 0 ? NaN : sum / count;\n },\n sum: function (frame) {\n var sum = 0;\n for (var i = 0; i < frame.length; i++) {\n // Ignore NaN\n sum += frame[i] || 0;\n }\n return sum;\n },\n max: function (frame) {\n var max = -Infinity;\n for (var i = 0; i < frame.length; i++) {\n frame[i] > max && (max = frame[i]);\n }\n return max;\n },\n min: function (frame) {\n var min = Infinity;\n for (var i = 0; i < frame.length; i++) {\n frame[i] < min && (min = frame[i]);\n }\n return min;\n },\n // TODO\n // Median\n nearest: function (frame) {\n return frame[0];\n }\n};\n\nvar indexSampler = function (frame, value) {\n return Math.round(frame.length / 2);\n};\n\nexport default function (seriesType, ecModel, api) {\n ecModel.eachSeriesByType(seriesType, function (seriesModel) {\n var data = seriesModel.getData();\n var sampling = seriesModel.get('sampling');\n var coordSys = seriesModel.coordinateSystem;\n // Only cartesian2d support down sampling\n if (coordSys.type === 'cartesian2d' && sampling) {\n var baseAxis = coordSys.getBaseAxis();\n var valueAxis = coordSys.getOtherAxis(baseAxis);\n var extent = baseAxis.getExtent();\n // Coordinste system has been resized\n var size = extent[1] - extent[0];\n var rate = Math.round(data.count() / size);\n if (rate > 1) {\n var sampler;\n if (typeof sampling === 'string') {\n sampler = samplers[sampling];\n }\n else if (typeof sampling === 'function') {\n sampler = sampling;\n }\n if (sampler) {\n data = data.downSample(\n valueAxis.dim, 1 / rate, sampler, indexSampler\n );\n seriesModel.setData(data);\n }\n }\n }\n }, this);\n}","/**\n * Cartesian coordinate system\n * @module echarts/coord/Cartesian\n *\n */\n\nimport * as zrUtil from 'zrender/src/core/util';\n\nfunction dimAxisMapper(dim) {\n return this._axes[dim];\n}\n\n/**\n * @alias module:echarts/coord/Cartesian\n * @constructor\n */\nvar Cartesian = function (name) {\n this._axes = {};\n\n this._dimList = [];\n\n /**\n * @type {string}\n */\n this.name = name || '';\n};\n\nCartesian.prototype = {\n\n constructor: Cartesian,\n\n type: 'cartesian',\n\n /**\n * Get axis\n * @param {number|string} dim\n * @return {module:echarts/coord/Cartesian~Axis}\n */\n getAxis: function (dim) {\n return this._axes[dim];\n },\n\n /**\n * Get axes list\n * @return {Array.<module:echarts/coord/Cartesian~Axis>}\n */\n getAxes: function () {\n return zrUtil.map(this._dimList, dimAxisMapper, this);\n },\n\n /**\n * Get axes list by given scale type\n */\n getAxesByScale: function (scaleType) {\n scaleType = scaleType.toLowerCase();\n return zrUtil.filter(\n this.getAxes(),\n function (axis) {\n return axis.scale.type === scaleType;\n }\n );\n },\n\n /**\n * Add axis\n * @param {module:echarts/coord/Cartesian.Axis}\n */\n addAxis: function (axis) {\n var dim = axis.dim;\n\n this._axes[dim] = axis;\n\n this._dimList.push(dim);\n },\n\n /**\n * Convert data to coord in nd space\n * @param {Array.<number>|Object.<string, number>} val\n * @return {Array.<number>|Object.<string, number>}\n */\n dataToCoord: function (val) {\n return this._dataCoordConvert(val, 'dataToCoord');\n },\n\n /**\n * Convert coord in nd space to data\n * @param {Array.<number>|Object.<string, number>} val\n * @return {Array.<number>|Object.<string, number>}\n */\n coordToData: function (val) {\n return this._dataCoordConvert(val, 'coordToData');\n },\n\n _dataCoordConvert: function (input, method) {\n var dimList = this._dimList;\n\n var output = input instanceof Array ? [] : {};\n\n for (var i = 0; i < dimList.length; i++) {\n var dim = dimList[i];\n var axis = this._axes[dim];\n\n output[dim] = axis[method](input[dim]);\n }\n\n return output;\n }\n};\n\nexport default Cartesian;","\nimport * as zrUtil from 'zrender/src/core/util';\nimport Cartesian from './Cartesian';\n\nfunction Cartesian2D(name) {\n\n Cartesian.call(this, name);\n}\n\nCartesian2D.prototype = {\n\n constructor: Cartesian2D,\n\n type: 'cartesian2d',\n\n /**\n * @type {Array.<string>}\n * @readOnly\n */\n dimensions: ['x', 'y'],\n\n /**\n * Base axis will be used on stacking.\n *\n * @return {module:echarts/coord/cartesian/Axis2D}\n */\n getBaseAxis: function () {\n return this.getAxesByScale('ordinal')[0]\n || this.getAxesByScale('time')[0]\n || this.getAxis('x');\n },\n\n /**\n * If contain point\n * @param {Array.<number>} point\n * @return {boolean}\n */\n containPoint: function (point) {\n var axisX = this.getAxis('x');\n var axisY = this.getAxis('y');\n return axisX.contain(axisX.toLocalCoord(point[0]))\n && axisY.contain(axisY.toLocalCoord(point[1]));\n },\n\n /**\n * If contain data\n * @param {Array.<number>} data\n * @return {boolean}\n */\n containData: function (data) {\n return this.getAxis('x').containData(data[0])\n && this.getAxis('y').containData(data[1]);\n },\n\n /**\n * @param {Array.<number>} data\n * @param {boolean} [clamp=false]\n * @return {Array.<number>}\n */\n dataToPoint: function (data, clamp) {\n var xAxis = this.getAxis('x');\n var yAxis = this.getAxis('y');\n return [\n xAxis.toGlobalCoord(xAxis.dataToCoord(data[0], clamp)),\n yAxis.toGlobalCoord(yAxis.dataToCoord(data[1], clamp))\n ];\n },\n\n /**\n * @param {Array.<number>} point\n * @param {boolean} [clamp=false]\n * @return {Array.<number>}\n */\n pointToData: function (point, clamp) {\n var xAxis = this.getAxis('x');\n var yAxis = this.getAxis('y');\n return [\n xAxis.coordToData(xAxis.toLocalCoord(point[0]), clamp),\n yAxis.coordToData(yAxis.toLocalCoord(point[1]), clamp)\n ];\n },\n\n /**\n * Get other axis\n * @param {module:echarts/coord/cartesian/Axis2D} axis\n */\n getOtherAxis: function (axis) {\n return this.getAxis(axis.dim === 'x' ? 'y' : 'x');\n }\n\n};\n\nzrUtil.inherits(Cartesian2D, Cartesian);\n\nexport default Cartesian2D;","import * as zrUtil from 'zrender/src/core/util';\nimport Axis from '../Axis';\n\n/**\n * Extend axis 2d\n * @constructor module:echarts/coord/cartesian/Axis2D\n * @extends {module:echarts/coord/cartesian/Axis}\n * @param {string} dim\n * @param {*} scale\n * @param {Array.<number>} coordExtent\n * @param {string} axisType\n * @param {string} position\n */\nvar Axis2D = function (dim, scale, coordExtent, axisType, position) {\n Axis.call(this, dim, scale, coordExtent);\n /**\n * Axis type\n * - 'category'\n * - 'value'\n * - 'time'\n * - 'log'\n * @type {string}\n */\n this.type = axisType || 'value';\n\n /**\n * Axis position\n * - 'top'\n * - 'bottom'\n * - 'left'\n * - 'right'\n */\n this.position = position || 'bottom';\n};\n\nAxis2D.prototype = {\n\n constructor: Axis2D,\n\n /**\n * Index of axis, can be used as key\n */\n index: 0,\n /**\n * If axis is on the zero position of the other axis\n * @type {boolean}\n */\n onZero: false,\n\n /**\n * Axis model\n * @param {module:echarts/coord/cartesian/AxisModel}\n */\n model: null,\n\n isHorizontal: function () {\n var position = this.position;\n return position === 'top' || position === 'bottom';\n },\n\n /**\n * Each item cooresponds to this.getExtent(), which\n * means globalExtent[0] may greater than globalExtent[1],\n * unless `asc` is input.\n *\n * @param {boolean} [asc]\n * @return {Array.<number>}\n */\n getGlobalExtent: function (asc) {\n var ret = this.getExtent();\n ret[0] = this.toGlobalCoord(ret[0]);\n ret[1] = this.toGlobalCoord(ret[1]);\n asc && ret[0] > ret[1] && ret.reverse();\n return ret;\n },\n\n getOtherAxis: function () {\n this.grid.getOtherAxis();\n },\n\n /**\n * If label is ignored.\n * Automatically used when axis is category and label can not be all shown\n * @param {number} idx\n * @return {boolean}\n */\n isLabelIgnored: function (idx) {\n if (this.type === 'category') {\n var labelInterval = this.getLabelInterval();\n return ((typeof labelInterval === 'function')\n && !labelInterval(idx, this.scale.getLabel(idx)))\n || idx % (labelInterval + 1);\n }\n },\n\n /**\n * @override\n */\n pointToData: function (point, clamp) {\n return this.coordToData(this.toLocalCoord(point[this.dim === 'x' ? 0 : 1]), clamp);\n },\n\n /**\n * Transform global coord to local coord,\n * i.e. var localCoord = axis.toLocalCoord(80);\n * designate by module:echarts/coord/cartesian/Grid.\n * @type {Function}\n */\n toLocalCoord: null,\n\n /**\n * Transform global coord to local coord,\n * i.e. var globalCoord = axis.toLocalCoord(40);\n * designate by module:echarts/coord/cartesian/Grid.\n * @type {Function}\n */\n toGlobalCoord: null\n\n};\n\nzrUtil.inherits(Axis2D, Axis);\n\nexport default Axis2D;","import * as zrUtil from 'zrender/src/core/util';\n\nvar defaultOption = {\n show: true,\n zlevel: 0, // 一级层叠\n z: 0, // 二级层叠\n // 反向坐标轴\n inverse: false,\n\n // 坐标轴名字,默认为空\n name: '',\n // 坐标轴名字位置,支持'start' | 'middle' | 'end'\n nameLocation: 'end',\n // 坐标轴名字旋转,degree。\n nameRotate: null, // Adapt to axis rotate, when nameLocation is 'middle'.\n nameTruncate: {\n maxWidth: null,\n ellipsis: '...',\n placeholder: '.'\n },\n // 坐标轴文字样式,默认取全局样式\n nameTextStyle: {},\n // 文字与轴线距离\n nameGap: 15,\n\n silent: false, // Default false to support tooltip.\n triggerEvent: false, // Default false to avoid legacy user event listener fail.\n\n tooltip: {\n show: false\n },\n\n axisPointer: {},\n\n // 坐标轴线\n axisLine: {\n // 默认显示,属性show控制显示与否\n show: true,\n onZero: true,\n onZeroAxisIndex: null,\n // 属性lineStyle控制线条样式\n lineStyle: {\n color: '#333',\n width: 1,\n type: 'solid'\n },\n // 坐标轴两端的箭头\n symbol: ['none', 'none'],\n symbolSize: [10, 15]\n },\n // 坐标轴小标记\n axisTick: {\n // 属性show控制显示与否,默认显示\n show: true,\n // 控制小标记是否在grid里\n inside: false,\n // 属性length控制线长\n length: 5,\n // 属性lineStyle控制线条样式\n lineStyle: {\n width: 1\n }\n },\n // 坐标轴文本标签,详见axis.axisLabel\n axisLabel: {\n show: true,\n // 控制文本标签是否在grid里\n inside: false,\n rotate: 0,\n showMinLabel: null, // true | false | null (auto)\n showMaxLabel: null, // true | false | null (auto)\n margin: 8,\n // formatter: null,\n // 其余属性默认使用全局文本样式,详见TEXTSTYLE\n fontSize: 12\n },\n // 分隔线\n splitLine: {\n // 默认显示,属性show控制显示与否\n show: true,\n // 属性lineStyle(详见lineStyle)控制线条样式\n lineStyle: {\n color: ['#ccc'],\n width: 1,\n type: 'solid'\n }\n },\n // 分隔区域\n splitArea: {\n // 默认不显示,属性show控制显示与否\n show: false,\n // 属性areaStyle(详见areaStyle)控制区域样式\n areaStyle: {\n color: ['rgba(250,250,250,0.3)','rgba(200,200,200,0.3)']\n }\n }\n};\n\nvar axisDefault = {};\n\naxisDefault.categoryAxis = zrUtil.merge({\n // 类目起始和结束两端空白策略\n boundaryGap: true,\n // splitArea: {\n // show: false\n // },\n splitLine: {\n show: false\n },\n // 坐标轴小标记\n axisTick: {\n // If tick is align with label when boundaryGap is true\n alignWithLabel: false,\n interval: 'auto'\n },\n // 坐标轴文本标签,详见axis.axisLabel\n axisLabel: {\n interval: 'auto'\n }\n}, defaultOption);\n\naxisDefault.valueAxis = zrUtil.merge({\n // 数值起始和结束两端空白策略\n boundaryGap: [0, 0],\n // 最小值, 设置成 'dataMin' 则从数据中计算最小值\n // min: null,\n // 最大值,设置成 'dataMax' 则从数据中计算最大值\n // max: null,\n // Readonly prop, specifies start value of the range when using data zoom.\n // rangeStart: null\n // Readonly prop, specifies end value of the range when using data zoom.\n // rangeEnd: null\n // 脱离0值比例,放大聚焦到最终_min,_max区间\n // scale: false,\n // 分割段数,默认为5\n splitNumber: 5\n // Minimum interval\n // minInterval: null\n // maxInterval: null\n}, defaultOption);\n\n// FIXME\naxisDefault.timeAxis = zrUtil.defaults({\n scale: true,\n min: 'dataMin',\n max: 'dataMax'\n}, axisDefault.valueAxis);\n\naxisDefault.logAxis = zrUtil.defaults({\n scale: true,\n logBase: 10\n}, axisDefault.valueAxis);\n\nexport default axisDefault;\n","import * as zrUtil from 'zrender/src/core/util';\nimport axisDefault from './axisDefault';\nimport ComponentModel from '../model/Component';\nimport {\n getLayoutParams,\n mergeLayoutParam\n} from '../util/layout';\n\n// FIXME axisType is fixed ?\nvar AXIS_TYPES = ['value', 'category', 'time', 'log'];\n\n/**\n * Generate sub axis model class\n * @param {string} axisName 'x' 'y' 'radius' 'angle' 'parallel'\n * @param {module:echarts/model/Component} BaseAxisModelClass\n * @param {Function} axisTypeDefaulter\n * @param {Object} [extraDefaultOption]\n */\nexport default function (axisName, BaseAxisModelClass, axisTypeDefaulter, extraDefaultOption) {\n\n zrUtil.each(AXIS_TYPES, function (axisType) {\n\n BaseAxisModelClass.extend({\n\n type: axisName + 'Axis.' + axisType,\n\n mergeDefaultAndTheme: function (option, ecModel) {\n var layoutMode = this.layoutMode;\n var inputPositionParams = layoutMode\n ? getLayoutParams(option) : {};\n\n var themeModel = ecModel.getTheme();\n zrUtil.merge(option, themeModel.get(axisType + 'Axis'));\n zrUtil.merge(option, this.getDefaultOption());\n\n option.type = axisTypeDefaulter(axisName, option);\n\n if (layoutMode) {\n mergeLayoutParam(option, inputPositionParams, layoutMode);\n }\n },\n\n defaultOption: zrUtil.mergeAll(\n [\n {},\n axisDefault[axisType + 'Axis'],\n extraDefaultOption\n ],\n true\n )\n });\n });\n\n ComponentModel.registerSubTypeDefaulter(\n axisName + 'Axis',\n zrUtil.curry(axisTypeDefaulter, axisName)\n );\n}","import * as zrUtil from 'zrender/src/core/util';\nimport ComponentModel from '../../model/Component';\nimport axisModelCreator from '../axisModelCreator';\nimport axisModelCommonMixin from '../axisModelCommonMixin';\n\nvar AxisModel = ComponentModel.extend({\n\n type: 'cartesian2dAxis',\n\n /**\n * @type {module:echarts/coord/cartesian/Axis2D}\n */\n axis: null,\n\n /**\n * @override\n */\n init: function () {\n AxisModel.superApply(this, 'init', arguments);\n this.resetRange();\n },\n\n /**\n * @override\n */\n mergeOption: function () {\n AxisModel.superApply(this, 'mergeOption', arguments);\n this.resetRange();\n },\n\n /**\n * @override\n */\n restoreData: function () {\n AxisModel.superApply(this, 'restoreData', arguments);\n this.resetRange();\n },\n\n /**\n * @override\n * @return {module:echarts/model/Component}\n */\n getCoordSysModel: function () {\n return this.ecModel.queryComponents({\n mainType: 'grid',\n index: this.option.gridIndex,\n id: this.option.gridId\n })[0];\n }\n\n});\n\nfunction getAxisType(axisDim, option) {\n // Default axis with data is category axis\n return option.type || (option.data ? 'category' : 'value');\n}\n\nzrUtil.merge(AxisModel.prototype, axisModelCommonMixin);\n\nvar extraOption = {\n // gridIndex: 0,\n // gridId: '',\n\n // Offset is for multiple axis on the same position\n offset: 0\n};\n\naxisModelCreator('x', AxisModel, getAxisType, extraOption);\naxisModelCreator('y', AxisModel, getAxisType, extraOption);\n\nexport default AxisModel;","// Grid 是在有直角坐标系的时候必须要存在的\n// 所以这里也要被 Cartesian2D 依赖\n\nimport './AxisModel';\nimport ComponentModel from '../../model/Component';\n\nexport default ComponentModel.extend({\n\n type: 'grid',\n\n dependencies: ['xAxis', 'yAxis'],\n\n layoutMode: 'box',\n\n /**\n * @type {module:echarts/coord/cartesian/Grid}\n */\n coordinateSystem: null,\n\n defaultOption: {\n show: false,\n zlevel: 0,\n z: 0,\n left: '10%',\n top: 60,\n right: '10%',\n bottom: 60,\n // If grid size contain label\n containLabel: false,\n // width: {totalWidth} - left - right,\n // height: {totalHeight} - top - bottom,\n backgroundColor: 'rgba(0,0,0,0)',\n borderWidth: 1,\n borderColor: '#ccc'\n }\n});","/**\n * Grid is a region which contains at most 4 cartesian systems\n *\n * TODO Default cartesian\n */\n\nimport {__DEV__} from '../../config';\nimport * as zrUtil from 'zrender/src/core/util';\nimport BoundingRect from 'zrender/src/core/BoundingRect';\nimport {getLayoutRect} from '../../util/layout';\nimport * as axisHelper from '../../coord/axisHelper';\nimport Cartesian2D from './Cartesian2D';\nimport Axis2D from './Axis2D';\nimport CoordinateSystem from '../../CoordinateSystem';\n\n// Depends on GridModel, AxisModel, which performs preprocess.\nimport './GridModel';\n\nvar each = zrUtil.each;\nvar ifAxisCrossZero = axisHelper.ifAxisCrossZero;\nvar niceScaleExtent = axisHelper.niceScaleExtent;\n\n/**\n * Check if the axis is used in the specified grid\n * @inner\n */\nfunction isAxisUsedInTheGrid(axisModel, gridModel, ecModel) {\n return axisModel.getCoordSysModel() === gridModel;\n}\n\nfunction rotateTextRect(textRect, rotate) {\n var rotateRadians = rotate * Math.PI / 180;\n var boundingBox = textRect.plain();\n var beforeWidth = boundingBox.width;\n var beforeHeight = boundingBox.height;\n var afterWidth = beforeWidth * Math.cos(rotateRadians) + beforeHeight * Math.sin(rotateRadians);\n var afterHeight = beforeWidth * Math.sin(rotateRadians) + beforeHeight * Math.cos(rotateRadians);\n var rotatedRect = new BoundingRect(boundingBox.x, boundingBox.y, afterWidth, afterHeight);\n\n return rotatedRect;\n}\n\nfunction getLabelUnionRect(axis) {\n var axisModel = axis.model;\n var labels = axisModel.getFormattedLabels();\n var axisLabelModel = axisModel.getModel('axisLabel');\n var rect;\n var step = 1;\n var labelCount = labels.length;\n if (labelCount > 40) {\n // Simple optimization for large amount of labels\n step = Math.ceil(labelCount / 40);\n }\n for (var i = 0; i < labelCount; i += step) {\n if (!axis.isLabelIgnored(i)) {\n var unrotatedSingleRect = axisLabelModel.getTextRect(labels[i]);\n var singleRect = rotateTextRect(unrotatedSingleRect, axisLabelModel.get('rotate') || 0);\n\n rect ? rect.union(singleRect) : (rect = singleRect);\n }\n }\n return rect;\n}\n\nfunction Grid(gridModel, ecModel, api) {\n /**\n * @type {Object.<string, module:echarts/coord/cartesian/Cartesian2D>}\n * @private\n */\n this._coordsMap = {};\n\n /**\n * @type {Array.<module:echarts/coord/cartesian/Cartesian>}\n * @private\n */\n this._coordsList = [];\n\n /**\n * @type {Object.<string, module:echarts/coord/cartesian/Axis2D>}\n * @private\n */\n this._axesMap = {};\n\n /**\n * @type {Array.<module:echarts/coord/cartesian/Axis2D>}\n * @private\n */\n this._axesList = [];\n\n this._initCartesian(gridModel, ecModel, api);\n\n this.model = gridModel;\n}\n\nvar gridProto = Grid.prototype;\n\ngridProto.type = 'grid';\n\ngridProto.axisPointerEnabled = true;\n\ngridProto.getRect = function () {\n return this._rect;\n};\n\ngridProto.update = function (ecModel, api) {\n\n var axesMap = this._axesMap;\n\n this._updateScale(ecModel, this.model);\n\n each(axesMap.x, function (xAxis) {\n niceScaleExtent(xAxis.scale, xAxis.model);\n });\n each(axesMap.y, function (yAxis) {\n niceScaleExtent(yAxis.scale, yAxis.model);\n });\n each(axesMap.x, function (xAxis) {\n fixAxisOnZero(axesMap, 'y', xAxis);\n });\n each(axesMap.y, function (yAxis) {\n fixAxisOnZero(axesMap, 'x', yAxis);\n });\n\n // Resize again if containLabel is enabled\n // FIXME It may cause getting wrong grid size in data processing stage\n this.resize(this.model, api);\n};\n\nfunction fixAxisOnZero(axesMap, otherAxisDim, axis) {\n // onZero can not be enabled in these two situations:\n // 1. When any other axis is a category axis.\n // 2. When no axis is cross 0 point.\n var axes = axesMap[otherAxisDim];\n\n if (!axis.onZero) {\n return;\n }\n\n var onZeroAxisIndex = axis.onZeroAxisIndex;\n\n // If target axis is specified.\n if (onZeroAxisIndex != null) {\n var otherAxis = axes[onZeroAxisIndex];\n if (otherAxis && canNotOnZeroToAxis(otherAxis)) {\n axis.onZero = false;\n }\n return;\n }\n\n for (var idx in axes) {\n if (axes.hasOwnProperty(idx)) {\n var otherAxis = axes[idx];\n if (otherAxis && !canNotOnZeroToAxis(otherAxis)) {\n onZeroAxisIndex = +idx;\n break;\n }\n }\n }\n\n if (onZeroAxisIndex == null) {\n axis.onZero = false;\n }\n axis.onZeroAxisIndex = onZeroAxisIndex;\n}\n\nfunction canNotOnZeroToAxis(axis) {\n return axis.type === 'category' || axis.type === 'time' || !ifAxisCrossZero(axis);\n}\n\n/**\n * Resize the grid\n * @param {module:echarts/coord/cartesian/GridModel} gridModel\n * @param {module:echarts/ExtensionAPI} api\n */\ngridProto.resize = function (gridModel, api, ignoreContainLabel) {\n\n var gridRect = getLayoutRect(\n gridModel.getBoxLayoutParams(), {\n width: api.getWidth(),\n height: api.getHeight()\n });\n\n this._rect = gridRect;\n\n var axesList = this._axesList;\n\n adjustAxes();\n\n // Minus label size\n if (!ignoreContainLabel && gridModel.get('containLabel')) {\n each(axesList, function (axis) {\n if (!axis.model.get('axisLabel.inside')) {\n var labelUnionRect = getLabelUnionRect(axis);\n if (labelUnionRect) {\n var dim = axis.isHorizontal() ? 'height' : 'width';\n var margin = axis.model.get('axisLabel.margin');\n gridRect[dim] -= labelUnionRect[dim] + margin;\n if (axis.position === 'top') {\n gridRect.y += labelUnionRect.height + margin;\n }\n else if (axis.position === 'left') {\n gridRect.x += labelUnionRect.width + margin;\n }\n }\n }\n });\n\n adjustAxes();\n }\n\n function adjustAxes() {\n each(axesList, function (axis) {\n var isHorizontal = axis.isHorizontal();\n var extent = isHorizontal ? [0, gridRect.width] : [0, gridRect.height];\n var idx = axis.inverse ? 1 : 0;\n axis.setExtent(extent[idx], extent[1 - idx]);\n updateAxisTransfrom(axis, isHorizontal ? gridRect.x : gridRect.y);\n });\n }\n};\n\n/**\n * @param {string} axisType\n * @param {number} [axisIndex]\n */\ngridProto.getAxis = function (axisType, axisIndex) {\n var axesMapOnDim = this._axesMap[axisType];\n if (axesMapOnDim != null) {\n if (axisIndex == null) {\n // Find first axis\n for (var name in axesMapOnDim) {\n if (axesMapOnDim.hasOwnProperty(name)) {\n return axesMapOnDim[name];\n }\n }\n }\n return axesMapOnDim[axisIndex];\n }\n};\n\n/**\n * @return {Array.<module:echarts/coord/Axis>}\n */\ngridProto.getAxes = function () {\n return this._axesList.slice();\n};\n\n/**\n * Usage:\n * grid.getCartesian(xAxisIndex, yAxisIndex);\n * grid.getCartesian(xAxisIndex);\n * grid.getCartesian(null, yAxisIndex);\n * grid.getCartesian({xAxisIndex: ..., yAxisIndex: ...});\n *\n * @param {number|Object} [xAxisIndex]\n * @param {number} [yAxisIndex]\n */\ngridProto.getCartesian = function (xAxisIndex, yAxisIndex) {\n if (xAxisIndex != null && yAxisIndex != null) {\n var key = 'x' + xAxisIndex + 'y' + yAxisIndex;\n return this._coordsMap[key];\n }\n\n if (zrUtil.isObject(xAxisIndex)) {\n yAxisIndex = xAxisIndex.yAxisIndex;\n xAxisIndex = xAxisIndex.xAxisIndex;\n }\n // When only xAxisIndex or yAxisIndex given, find its first cartesian.\n for (var i = 0, coordList = this._coordsList; i < coordList.length; i++) {\n if (coordList[i].getAxis('x').index === xAxisIndex\n || coordList[i].getAxis('y').index === yAxisIndex\n ) {\n return coordList[i];\n }\n }\n};\n\ngridProto.getCartesians = function () {\n return this._coordsList.slice();\n};\n\n/**\n * @implements\n * see {module:echarts/CoodinateSystem}\n */\ngridProto.convertToPixel = function (ecModel, finder, value) {\n var target = this._findConvertTarget(ecModel, finder);\n\n return target.cartesian\n ? target.cartesian.dataToPoint(value)\n : target.axis\n ? target.axis.toGlobalCoord(target.axis.dataToCoord(value))\n : null;\n};\n\n/**\n * @implements\n * see {module:echarts/CoodinateSystem}\n */\ngridProto.convertFromPixel = function (ecModel, finder, value) {\n var target = this._findConvertTarget(ecModel, finder);\n\n return target.cartesian\n ? target.cartesian.pointToData(value)\n : target.axis\n ? target.axis.coordToData(target.axis.toLocalCoord(value))\n : null;\n};\n\n/**\n * @inner\n */\ngridProto._findConvertTarget = function (ecModel, finder) {\n var seriesModel = finder.seriesModel;\n var xAxisModel = finder.xAxisModel\n || (seriesModel && seriesModel.getReferringComponents('xAxis')[0]);\n var yAxisModel = finder.yAxisModel\n || (seriesModel && seriesModel.getReferringComponents('yAxis')[0]);\n var gridModel = finder.gridModel;\n var coordsList = this._coordsList;\n var cartesian;\n var axis;\n\n if (seriesModel) {\n cartesian = seriesModel.coordinateSystem;\n zrUtil.indexOf(coordsList, cartesian) < 0 && (cartesian = null);\n }\n else if (xAxisModel && yAxisModel) {\n cartesian = this.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex);\n }\n else if (xAxisModel) {\n axis = this.getAxis('x', xAxisModel.componentIndex);\n }\n else if (yAxisModel) {\n axis = this.getAxis('y', yAxisModel.componentIndex);\n }\n // Lowest priority.\n else if (gridModel) {\n var grid = gridModel.coordinateSystem;\n if (grid === this) {\n cartesian = this._coordsList[0];\n }\n }\n\n return {cartesian: cartesian, axis: axis};\n};\n\n/**\n * @implements\n * see {module:echarts/CoodinateSystem}\n */\ngridProto.containPoint = function (point) {\n var coord = this._coordsList[0];\n if (coord) {\n return coord.containPoint(point);\n }\n};\n\n/**\n * Initialize cartesian coordinate systems\n * @private\n */\ngridProto._initCartesian = function (gridModel, ecModel, api) {\n var axisPositionUsed = {\n left: false,\n right: false,\n top: false,\n bottom: false\n };\n\n var axesMap = {\n x: {},\n y: {}\n };\n var axesCount = {\n x: 0,\n y: 0\n };\n\n /// Create axis\n ecModel.eachComponent('xAxis', createAxisCreator('x'), this);\n ecModel.eachComponent('yAxis', createAxisCreator('y'), this);\n\n if (!axesCount.x || !axesCount.y) {\n // Roll back when there no either x or y axis\n this._axesMap = {};\n this._axesList = [];\n return;\n }\n\n this._axesMap = axesMap;\n\n /// Create cartesian2d\n each(axesMap.x, function (xAxis, xAxisIndex) {\n each(axesMap.y, function (yAxis, yAxisIndex) {\n var key = 'x' + xAxisIndex + 'y' + yAxisIndex;\n var cartesian = new Cartesian2D(key);\n\n cartesian.grid = this;\n cartesian.model = gridModel;\n\n this._coordsMap[key] = cartesian;\n this._coordsList.push(cartesian);\n\n cartesian.addAxis(xAxis);\n cartesian.addAxis(yAxis);\n }, this);\n }, this);\n\n function createAxisCreator(axisType) {\n return function (axisModel, idx) {\n if (!isAxisUsedInTheGrid(axisModel, gridModel, ecModel)) {\n return;\n }\n\n var axisPosition = axisModel.get('position');\n if (axisType === 'x') {\n // Fix position\n if (axisPosition !== 'top' && axisPosition !== 'bottom') {\n // Default bottom of X\n axisPosition = 'bottom';\n if (axisPositionUsed[axisPosition]) {\n axisPosition = axisPosition === 'top' ? 'bottom' : 'top';\n }\n }\n }\n else {\n // Fix position\n if (axisPosition !== 'left' && axisPosition !== 'right') {\n // Default left of Y\n axisPosition = 'left';\n if (axisPositionUsed[axisPosition]) {\n axisPosition = axisPosition === 'left' ? 'right' : 'left';\n }\n }\n }\n axisPositionUsed[axisPosition] = true;\n\n var axis = new Axis2D(\n axisType, axisHelper.createScaleByModel(axisModel),\n [0, 0],\n axisModel.get('type'),\n axisPosition\n );\n\n var isCategory = axis.type === 'category';\n axis.onBand = isCategory && axisModel.get('boundaryGap');\n axis.inverse = axisModel.get('inverse');\n\n axis.onZero = axisModel.get('axisLine.onZero');\n axis.onZeroAxisIndex = axisModel.get('axisLine.onZeroAxisIndex');\n\n // Inject axis into axisModel\n axisModel.axis = axis;\n\n // Inject axisModel into axis\n axis.model = axisModel;\n\n // Inject grid info axis\n axis.grid = this;\n\n // Index of axis, can be used as key\n axis.index = idx;\n\n this._axesList.push(axis);\n\n axesMap[axisType][idx] = axis;\n axesCount[axisType]++;\n };\n }\n};\n\n/**\n * Update cartesian properties from series\n * @param {module:echarts/model/Option} option\n * @private\n */\ngridProto._updateScale = function (ecModel, gridModel) {\n // Reset scale\n zrUtil.each(this._axesList, function (axis) {\n axis.scale.setExtent(Infinity, -Infinity);\n });\n ecModel.eachSeries(function (seriesModel) {\n if (isCartesian2D(seriesModel)) {\n var axesModels = findAxesModels(seriesModel, ecModel);\n var xAxisModel = axesModels[0];\n var yAxisModel = axesModels[1];\n\n if (!isAxisUsedInTheGrid(xAxisModel, gridModel, ecModel)\n || !isAxisUsedInTheGrid(yAxisModel, gridModel, ecModel)\n ) {\n return;\n }\n\n var cartesian = this.getCartesian(\n xAxisModel.componentIndex, yAxisModel.componentIndex\n );\n var data = seriesModel.getData();\n var xAxis = cartesian.getAxis('x');\n var yAxis = cartesian.getAxis('y');\n\n if (data.type === 'list') {\n unionExtent(data, xAxis, seriesModel);\n unionExtent(data, yAxis, seriesModel);\n }\n }\n }, this);\n\n function unionExtent(data, axis, seriesModel) {\n each(seriesModel.coordDimToDataDim(axis.dim), function (dim) {\n axis.scale.unionExtentFromData(data, dim);\n });\n }\n};\n\n/**\n * @param {string} [dim] 'x' or 'y' or 'auto' or null/undefined\n * @return {Object} {baseAxes: [], otherAxes: []}\n */\ngridProto.getTooltipAxes = function (dim) {\n var baseAxes = [];\n var otherAxes = [];\n\n each(this.getCartesians(), function (cartesian) {\n var baseAxis = (dim != null && dim !== 'auto')\n ? cartesian.getAxis(dim) : cartesian.getBaseAxis();\n var otherAxis = cartesian.getOtherAxis(baseAxis);\n zrUtil.indexOf(baseAxes, baseAxis) < 0 && baseAxes.push(baseAxis);\n zrUtil.indexOf(otherAxes, otherAxis) < 0 && otherAxes.push(otherAxis);\n });\n\n return {baseAxes: baseAxes, otherAxes: otherAxes};\n};\n\n/**\n * @inner\n */\nfunction updateAxisTransfrom(axis, coordBase) {\n var axisExtent = axis.getExtent();\n var axisExtentSum = axisExtent[0] + axisExtent[1];\n\n // Fast transform\n axis.toGlobalCoord = axis.dim === 'x'\n ? function (coord) {\n return coord + coordBase;\n }\n : function (coord) {\n return axisExtentSum - coord + coordBase;\n };\n axis.toLocalCoord = axis.dim === 'x'\n ? function (coord) {\n return coord - coordBase;\n }\n : function (coord) {\n return axisExtentSum - coord + coordBase;\n };\n}\n\nvar axesTypes = ['xAxis', 'yAxis'];\n/**\n * @inner\n */\nfunction findAxesModels(seriesModel, ecModel) {\n return zrUtil.map(axesTypes, function (axisType) {\n var axisModel = seriesModel.getReferringComponents(axisType)[0];\n\n if (__DEV__) {\n if (!axisModel) {\n throw new Error(axisType + ' \"' + zrUtil.retrieve(\n seriesModel.get(axisType + 'Index'),\n seriesModel.get(axisType + 'Id'),\n 0\n ) + '\" not found');\n }\n }\n return axisModel;\n });\n}\n\n/**\n * @inner\n */\nfunction isCartesian2D(seriesModel) {\n return seriesModel.get('coordinateSystem') === 'cartesian2d';\n}\n\nGrid.create = function (ecModel, api) {\n var grids = [];\n ecModel.eachComponent('grid', function (gridModel, idx) {\n var grid = new Grid(gridModel, ecModel, api);\n grid.name = 'grid_' + idx;\n // dataSampling requires axis extent, so resize\n // should be performed in create stage.\n grid.resize(gridModel, api, true);\n\n gridModel.coordinateSystem = grid;\n\n grids.push(grid);\n });\n\n // Inject the coordinateSystems into seriesModel\n ecModel.eachSeries(function (seriesModel) {\n if (!isCartesian2D(seriesModel)) {\n return;\n }\n\n var axesModels = findAxesModels(seriesModel, ecModel);\n var xAxisModel = axesModels[0];\n var yAxisModel = axesModels[1];\n\n var gridModel = xAxisModel.getCoordSysModel();\n\n if (__DEV__) {\n if (!gridModel) {\n throw new Error(\n 'Grid \"' + zrUtil.retrieve(\n xAxisModel.get('gridIndex'),\n xAxisModel.get('gridId'),\n 0\n ) + '\" not found'\n );\n }\n if (xAxisModel.getCoordSysModel() !== yAxisModel.getCoordSysModel()) {\n throw new Error('xAxis and yAxis must use the same grid');\n }\n }\n\n var grid = gridModel.coordinateSystem;\n\n seriesModel.coordinateSystem = grid.getCartesian(\n xAxisModel.componentIndex, yAxisModel.componentIndex\n );\n });\n\n return grids;\n};\n\n// For deciding which dimensions to use when creating list data\nGrid.dimensions = Grid.prototype.dimensions = Cartesian2D.prototype.dimensions;\n\nCoordinateSystem.register('cartesian2d', Grid);\n\nexport default Grid;\n","import {retrieve, defaults, extend, each} from 'zrender/src/core/util';\nimport * as formatUtil from '../../util/format';\nimport * as graphic from '../../util/graphic';\nimport Model from '../../model/Model';\nimport {isRadianAroundZero, remRadian} from '../../util/number';\nimport {createSymbol} from '../../util/symbol';\nimport * as matrixUtil from 'zrender/src/core/matrix';\nimport {applyTransform as v2ApplyTransform} from 'zrender/src/core/vector';\n\n\nvar PI = Math.PI;\n\nfunction makeAxisEventDataBase(axisModel) {\n var eventData = {\n componentType: axisModel.mainType\n };\n eventData[axisModel.mainType + 'Index'] = axisModel.componentIndex;\n return eventData;\n}\n\n/**\n * A final axis is translated and rotated from a \"standard axis\".\n * So opt.position and opt.rotation is required.\n *\n * A standard axis is and axis from [0, 0] to [0, axisExtent[1]],\n * for example: (0, 0) ------------> (0, 50)\n *\n * nameDirection or tickDirection or labelDirection is 1 means tick\n * or label is below the standard axis, whereas is -1 means above\n * the standard axis. labelOffset means offset between label and axis,\n * which is useful when 'onZero', where axisLabel is in the grid and\n * label in outside grid.\n *\n * Tips: like always,\n * positive rotation represents anticlockwise, and negative rotation\n * represents clockwise.\n * The direction of position coordinate is the same as the direction\n * of screen coordinate.\n *\n * Do not need to consider axis 'inverse', which is auto processed by\n * axis extent.\n *\n * @param {module:zrender/container/Group} group\n * @param {Object} axisModel\n * @param {Object} opt Standard axis parameters.\n * @param {Array.<number>} opt.position [x, y]\n * @param {number} opt.rotation by radian\n * @param {number} [opt.nameDirection=1] 1 or -1 Used when nameLocation is 'middle' or 'center'.\n * @param {number} [opt.tickDirection=1] 1 or -1\n * @param {number} [opt.labelDirection=1] 1 or -1\n * @param {number} [opt.labelOffset=0] Usefull when onZero.\n * @param {string} [opt.axisLabelShow] default get from axisModel.\n * @param {string} [opt.axisName] default get from axisModel.\n * @param {number} [opt.axisNameAvailableWidth]\n * @param {number} [opt.labelRotate] by degree, default get from axisModel.\n * @param {number} [opt.labelInterval] Default label interval when label\n * interval from model is null or 'auto'.\n * @param {number} [opt.strokeContainThreshold] Default label interval when label\n * @param {number} [opt.nameTruncateMaxWidth]\n */\nvar AxisBuilder = function (axisModel, opt) {\n\n /**\n * @readOnly\n */\n this.opt = opt;\n\n /**\n * @readOnly\n */\n this.axisModel = axisModel;\n\n // Default value\n defaults(\n opt,\n {\n labelOffset: 0,\n nameDirection: 1,\n tickDirection: 1,\n labelDirection: 1,\n silent: true\n }\n );\n\n /**\n * @readOnly\n */\n this.group = new graphic.Group();\n\n // FIXME Not use a seperate text group?\n var dumbGroup = new graphic.Group({\n position: opt.position.slice(),\n rotation: opt.rotation\n });\n\n // this.group.add(dumbGroup);\n // this._dumbGroup = dumbGroup;\n\n dumbGroup.updateTransform();\n this._transform = dumbGroup.transform;\n\n this._dumbGroup = dumbGroup;\n};\n\nAxisBuilder.prototype = {\n\n constructor: AxisBuilder,\n\n hasBuilder: function (name) {\n return !!builders[name];\n },\n\n add: function (name) {\n builders[name].call(this);\n },\n\n getGroup: function () {\n return this.group;\n }\n\n};\n\nvar builders = {\n\n /**\n * @private\n */\n axisLine: function () {\n var opt = this.opt;\n var axisModel = this.axisModel;\n\n if (!axisModel.get('axisLine.show')) {\n return;\n }\n\n var extent = this.axisModel.axis.getExtent();\n\n var matrix = this._transform;\n var pt1 = [extent[0], 0];\n var pt2 = [extent[1], 0];\n if (matrix) {\n v2ApplyTransform(pt1, pt1, matrix);\n v2ApplyTransform(pt2, pt2, matrix);\n }\n\n var lineStyle = extend(\n {\n lineCap: 'round'\n },\n axisModel.getModel('axisLine.lineStyle').getLineStyle()\n );\n\n this.group.add(new graphic.Line(graphic.subPixelOptimizeLine({\n // Id for animation\n anid: 'line',\n\n shape: {\n x1: pt1[0],\n y1: pt1[1],\n x2: pt2[0],\n y2: pt2[1]\n },\n style: lineStyle,\n strokeContainThreshold: opt.strokeContainThreshold || 5,\n silent: true,\n z2: 1\n })));\n\n var arrows = axisModel.get('axisLine.symbol');\n var arrowSize = axisModel.get('axisLine.symbolSize');\n\n if (arrows != null) {\n if (typeof arrows === 'string') {\n // Use the same arrow for start and end point\n arrows = [arrows, arrows];\n }\n if (typeof arrowSize === 'string'\n || typeof arrowSize === 'number'\n ) {\n // Use the same size for width and height\n arrowSize = [arrowSize, arrowSize];\n }\n\n var symbolWidth = arrowSize[0];\n var symbolHeight = arrowSize[1];\n\n each([\n [opt.rotation + Math.PI / 2, pt1],\n [opt.rotation - Math.PI / 2, pt2]\n ], function (item, index) {\n if (arrows[index] !== 'none' && arrows[index] != null) {\n var symbol = createSymbol(\n arrows[index],\n -symbolWidth / 2,\n -symbolHeight / 2,\n symbolWidth,\n symbolHeight,\n lineStyle.stroke,\n true\n );\n symbol.attr({\n rotation: item[0],\n position: item[1],\n silent: true\n });\n this.group.add(symbol);\n }\n }, this);\n }\n },\n\n /**\n * @private\n */\n axisTickLabel: function () {\n var axisModel = this.axisModel;\n var opt = this.opt;\n\n var tickEls = buildAxisTick(this, axisModel, opt);\n var labelEls = buildAxisLabel(this, axisModel, opt);\n\n fixMinMaxLabelShow(axisModel, labelEls, tickEls);\n },\n\n /**\n * @private\n */\n axisName: function () {\n var opt = this.opt;\n var axisModel = this.axisModel;\n var name = retrieve(opt.axisName, axisModel.get('name'));\n\n if (!name) {\n return;\n }\n\n var nameLocation = axisModel.get('nameLocation');\n var nameDirection = opt.nameDirection;\n var textStyleModel = axisModel.getModel('nameTextStyle');\n var gap = axisModel.get('nameGap') || 0;\n\n var extent = this.axisModel.axis.getExtent();\n var gapSignal = extent[0] > extent[1] ? -1 : 1;\n var pos = [\n nameLocation === 'start'\n ? extent[0] - gapSignal * gap\n : nameLocation === 'end'\n ? extent[1] + gapSignal * gap\n : (extent[0] + extent[1]) / 2, // 'middle'\n // Reuse labelOffset.\n isNameLocationCenter(nameLocation) ? opt.labelOffset + nameDirection * gap : 0\n ];\n\n var labelLayout;\n\n var nameRotation = axisModel.get('nameRotate');\n if (nameRotation != null) {\n nameRotation = nameRotation * PI / 180; // To radian.\n }\n\n var axisNameAvailableWidth;\n\n if (isNameLocationCenter(nameLocation)) {\n labelLayout = innerTextLayout(\n opt.rotation,\n nameRotation != null ? nameRotation : opt.rotation, // Adapt to axis.\n nameDirection\n );\n }\n else {\n labelLayout = endTextLayout(\n opt, nameLocation, nameRotation || 0, extent\n );\n\n axisNameAvailableWidth = opt.axisNameAvailableWidth;\n if (axisNameAvailableWidth != null) {\n axisNameAvailableWidth = Math.abs(\n axisNameAvailableWidth / Math.sin(labelLayout.rotation)\n );\n !isFinite(axisNameAvailableWidth) && (axisNameAvailableWidth = null);\n }\n }\n\n var textFont = textStyleModel.getFont();\n\n var truncateOpt = axisModel.get('nameTruncate', true) || {};\n var ellipsis = truncateOpt.ellipsis;\n var maxWidth = retrieve(\n opt.nameTruncateMaxWidth, truncateOpt.maxWidth, axisNameAvailableWidth\n );\n // FIXME\n // truncate rich text? (consider performance)\n var truncatedText = (ellipsis != null && maxWidth != null)\n ? formatUtil.truncateText(\n name, maxWidth, textFont, ellipsis,\n {minChar: 2, placeholder: truncateOpt.placeholder}\n )\n : name;\n\n var tooltipOpt = axisModel.get('tooltip', true);\n\n var mainType = axisModel.mainType;\n var formatterParams = {\n componentType: mainType,\n name: name,\n $vars: ['name']\n };\n formatterParams[mainType + 'Index'] = axisModel.componentIndex;\n\n var textEl = new graphic.Text({\n // Id for animation\n anid: 'name',\n\n __fullText: name,\n __truncatedText: truncatedText,\n\n position: pos,\n rotation: labelLayout.rotation,\n silent: isSilent(axisModel),\n z2: 1,\n tooltip: (tooltipOpt && tooltipOpt.show)\n ? extend({\n content: name,\n formatter: function () {\n return name;\n },\n formatterParams: formatterParams\n }, tooltipOpt)\n : null\n });\n\n graphic.setTextStyle(textEl.style, textStyleModel, {\n text: truncatedText,\n textFont: textFont,\n textFill: textStyleModel.getTextColor()\n || axisModel.get('axisLine.lineStyle.color'),\n textAlign: labelLayout.textAlign,\n textVerticalAlign: labelLayout.textVerticalAlign\n });\n\n if (axisModel.get('triggerEvent')) {\n textEl.eventData = makeAxisEventDataBase(axisModel);\n textEl.eventData.targetType = 'axisName';\n textEl.eventData.name = name;\n }\n\n // FIXME\n this._dumbGroup.add(textEl);\n textEl.updateTransform();\n\n this.group.add(textEl);\n\n textEl.decomposeTransform();\n }\n\n};\n\n/**\n * @public\n * @static\n * @param {Object} opt\n * @param {number} axisRotation in radian\n * @param {number} textRotation in radian\n * @param {number} direction\n * @return {Object} {\n * rotation, // according to axis\n * textAlign,\n * textVerticalAlign\n * }\n */\nvar innerTextLayout = AxisBuilder.innerTextLayout = function (axisRotation, textRotation, direction) {\n var rotationDiff = remRadian(textRotation - axisRotation);\n var textAlign;\n var textVerticalAlign;\n\n if (isRadianAroundZero(rotationDiff)) { // Label is parallel with axis line.\n textVerticalAlign = direction > 0 ? 'top' : 'bottom';\n textAlign = 'center';\n }\n else if (isRadianAroundZero(rotationDiff - PI)) { // Label is inverse parallel with axis line.\n textVerticalAlign = direction > 0 ? 'bottom' : 'top';\n textAlign = 'center';\n }\n else {\n textVerticalAlign = 'middle';\n\n if (rotationDiff > 0 && rotationDiff < PI) {\n textAlign = direction > 0 ? 'right' : 'left';\n }\n else {\n textAlign = direction > 0 ? 'left' : 'right';\n }\n }\n\n return {\n rotation: rotationDiff,\n textAlign: textAlign,\n textVerticalAlign: textVerticalAlign\n };\n};\n\nfunction endTextLayout(opt, textPosition, textRotate, extent) {\n var rotationDiff = remRadian(textRotate - opt.rotation);\n var textAlign;\n var textVerticalAlign;\n var inverse = extent[0] > extent[1];\n var onLeft = (textPosition === 'start' && !inverse)\n || (textPosition !== 'start' && inverse);\n\n if (isRadianAroundZero(rotationDiff - PI / 2)) {\n textVerticalAlign = onLeft ? 'bottom' : 'top';\n textAlign = 'center';\n }\n else if (isRadianAroundZero(rotationDiff - PI * 1.5)) {\n textVerticalAlign = onLeft ? 'top' : 'bottom';\n textAlign = 'center';\n }\n else {\n textVerticalAlign = 'middle';\n if (rotationDiff < PI * 1.5 && rotationDiff > PI / 2) {\n textAlign = onLeft ? 'left' : 'right';\n }\n else {\n textAlign = onLeft ? 'right' : 'left';\n }\n }\n\n return {\n rotation: rotationDiff,\n textAlign: textAlign,\n textVerticalAlign: textVerticalAlign\n };\n}\n\nfunction isSilent(axisModel) {\n var tooltipOpt = axisModel.get('tooltip');\n return axisModel.get('silent')\n // Consider mouse cursor, add these restrictions.\n || !(\n axisModel.get('triggerEvent') || (tooltipOpt && tooltipOpt.show)\n );\n}\n\nfunction fixMinMaxLabelShow(axisModel, labelEls, tickEls) {\n // If min or max are user set, we need to check\n // If the tick on min(max) are overlap on their neighbour tick\n // If they are overlapped, we need to hide the min(max) tick label\n var showMinLabel = axisModel.get('axisLabel.showMinLabel');\n var showMaxLabel = axisModel.get('axisLabel.showMaxLabel');\n\n // FIXME\n // Have not consider onBand yet, where tick els is more than label els.\n\n labelEls = labelEls || [];\n tickEls = tickEls || [];\n\n var firstLabel = labelEls[0];\n var nextLabel = labelEls[1];\n var lastLabel = labelEls[labelEls.length - 1];\n var prevLabel = labelEls[labelEls.length - 2];\n\n var firstTick = tickEls[0];\n var nextTick = tickEls[1];\n var lastTick = tickEls[tickEls.length - 1];\n var prevTick = tickEls[tickEls.length - 2];\n\n if (showMinLabel === false) {\n ignoreEl(firstLabel);\n ignoreEl(firstTick);\n }\n else if (isTwoLabelOverlapped(firstLabel, nextLabel)) {\n if (showMinLabel) {\n ignoreEl(nextLabel);\n ignoreEl(nextTick);\n }\n else {\n ignoreEl(firstLabel);\n ignoreEl(firstTick);\n }\n }\n\n if (showMaxLabel === false) {\n ignoreEl(lastLabel);\n ignoreEl(lastTick);\n }\n else if (isTwoLabelOverlapped(prevLabel, lastLabel)) {\n if (showMaxLabel) {\n ignoreEl(prevLabel);\n ignoreEl(prevTick);\n }\n else {\n ignoreEl(lastLabel);\n ignoreEl(lastTick);\n }\n }\n}\n\nfunction ignoreEl(el) {\n el && (el.ignore = true);\n}\n\nfunction isTwoLabelOverlapped(current, next, labelLayout) {\n // current and next has the same rotation.\n var firstRect = current && current.getBoundingRect().clone();\n var nextRect = next && next.getBoundingRect().clone();\n\n if (!firstRect || !nextRect) {\n return;\n }\n\n // When checking intersect of two rotated labels, we use mRotationBack\n // to avoid that boundingRect is enlarge when using `boundingRect.applyTransform`.\n var mRotationBack = matrixUtil.identity([]);\n matrixUtil.rotate(mRotationBack, mRotationBack, -current.rotation);\n\n firstRect.applyTransform(matrixUtil.mul([], mRotationBack, current.getLocalTransform()));\n nextRect.applyTransform(matrixUtil.mul([], mRotationBack, next.getLocalTransform()));\n\n return firstRect.intersect(nextRect);\n}\n\nfunction isNameLocationCenter(nameLocation) {\n return nameLocation === 'middle' || nameLocation === 'center';\n}\n\n/**\n * @static\n */\nvar ifIgnoreOnTick = AxisBuilder.ifIgnoreOnTick = function (\n axis,\n i,\n interval,\n ticksCnt,\n showMinLabel,\n showMaxLabel\n) {\n if (i === 0 && showMinLabel || i === ticksCnt - 1 && showMaxLabel) {\n return false;\n }\n\n // FIXME\n // Have not consider label overlap (if label is too long) yet.\n\n var rawTick;\n var scale = axis.scale;\n return scale.type === 'ordinal'\n && (\n typeof interval === 'function'\n ? (\n rawTick = scale.getTicks()[i],\n !interval(rawTick, scale.getLabel(rawTick))\n )\n : i % (interval + 1)\n );\n};\n\n/**\n * @static\n */\nvar getInterval = AxisBuilder.getInterval = function (model, labelInterval) {\n var interval = model.get('interval');\n if (interval == null || interval == 'auto') {\n interval = labelInterval;\n }\n return interval;\n};\n\nfunction buildAxisTick(axisBuilder, axisModel, opt) {\n var axis = axisModel.axis;\n\n if (!axisModel.get('axisTick.show') || axis.scale.isBlank()) {\n return;\n }\n\n var tickModel = axisModel.getModel('axisTick');\n\n var lineStyleModel = tickModel.getModel('lineStyle');\n var tickLen = tickModel.get('length');\n\n var tickInterval = getInterval(tickModel, opt.labelInterval);\n var ticksCoords = axis.getTicksCoords(tickModel.get('alignWithLabel'));\n // FIXME\n // Corresponds to ticksCoords ?\n var ticks = axis.scale.getTicks();\n\n var showMinLabel = axisModel.get('axisLabel.showMinLabel');\n var showMaxLabel = axisModel.get('axisLabel.showMaxLabel');\n\n var pt1 = [];\n var pt2 = [];\n var matrix = axisBuilder._transform;\n\n var tickEls = [];\n\n var ticksCnt = ticksCoords.length;\n for (var i = 0; i < ticksCnt; i++) {\n // Only ordinal scale support tick interval\n if (ifIgnoreOnTick(\n axis, i, tickInterval, ticksCnt,\n showMinLabel, showMaxLabel\n )) {\n continue;\n }\n\n var tickCoord = ticksCoords[i];\n\n pt1[0] = tickCoord;\n pt1[1] = 0;\n pt2[0] = tickCoord;\n pt2[1] = opt.tickDirection * tickLen;\n\n if (matrix) {\n v2ApplyTransform(pt1, pt1, matrix);\n v2ApplyTransform(pt2, pt2, matrix);\n }\n // Tick line, Not use group transform to have better line draw\n var tickEl = new graphic.Line(graphic.subPixelOptimizeLine({\n // Id for animation\n anid: 'tick_' + ticks[i],\n\n shape: {\n x1: pt1[0],\n y1: pt1[1],\n x2: pt2[0],\n y2: pt2[1]\n },\n style: defaults(\n lineStyleModel.getLineStyle(),\n {\n stroke: axisModel.get('axisLine.lineStyle.color')\n }\n ),\n z2: 2,\n silent: true\n }));\n axisBuilder.group.add(tickEl);\n tickEls.push(tickEl);\n }\n\n return tickEls;\n}\n\nfunction buildAxisLabel(axisBuilder, axisModel, opt) {\n var axis = axisModel.axis;\n var show = retrieve(opt.axisLabelShow, axisModel.get('axisLabel.show'));\n\n if (!show || axis.scale.isBlank()) {\n return;\n }\n\n var labelModel = axisModel.getModel('axisLabel');\n var labelMargin = labelModel.get('margin');\n var ticks = axis.scale.getTicks();\n var labels = axisModel.getFormattedLabels();\n\n // Special label rotate.\n var labelRotation = (\n retrieve(opt.labelRotate, labelModel.get('rotate')) || 0\n ) * PI / 180;\n\n var labelLayout = innerTextLayout(opt.rotation, labelRotation, opt.labelDirection);\n var categoryData = axisModel.get('data');\n\n var labelEls = [];\n var silent = isSilent(axisModel);\n var triggerEvent = axisModel.get('triggerEvent');\n\n var showMinLabel = axisModel.get('axisLabel.showMinLabel');\n var showMaxLabel = axisModel.get('axisLabel.showMaxLabel');\n\n each(ticks, function (tickVal, index) {\n if (ifIgnoreOnTick(\n axis, index, opt.labelInterval, ticks.length,\n showMinLabel, showMaxLabel\n )) {\n return;\n }\n\n var itemLabelModel = labelModel;\n if (categoryData && categoryData[tickVal] && categoryData[tickVal].textStyle) {\n itemLabelModel = new Model(\n categoryData[tickVal].textStyle, labelModel, axisModel.ecModel\n );\n }\n\n var textColor = itemLabelModel.getTextColor()\n || axisModel.get('axisLine.lineStyle.color');\n\n var tickCoord = axis.dataToCoord(tickVal);\n var pos = [\n tickCoord,\n opt.labelOffset + opt.labelDirection * labelMargin\n ];\n var labelStr = axis.scale.getLabel(tickVal);\n\n var textEl = new graphic.Text({\n // Id for animation\n anid: 'label_' + tickVal,\n position: pos,\n rotation: labelLayout.rotation,\n silent: silent,\n z2: 10\n });\n\n graphic.setTextStyle(textEl.style, itemLabelModel, {\n text: labels[index],\n textAlign: itemLabelModel.getShallow('align', true)\n || labelLayout.textAlign,\n textVerticalAlign: itemLabelModel.getShallow('verticalAlign', true)\n || itemLabelModel.getShallow('baseline', true)\n || labelLayout.textVerticalAlign,\n textFill: typeof textColor === 'function'\n ? textColor(\n // (1) In category axis with data zoom, tick is not the original\n // index of axis.data. So tick should not be exposed to user\n // in category axis.\n // (2) Compatible with previous version, which always returns labelStr.\n // But in interval scale labelStr is like '223,445', which maked\n // user repalce ','. So we modify it to return original val but remain\n // it as 'string' to avoid error in replacing.\n axis.type === 'category' ? labelStr : axis.type === 'value' ? tickVal + '' : tickVal,\n index\n )\n : textColor\n });\n\n // Pack data for mouse event\n if (triggerEvent) {\n textEl.eventData = makeAxisEventDataBase(axisModel);\n textEl.eventData.targetType = 'axisLabel';\n textEl.eventData.value = labelStr;\n }\n\n // FIXME\n axisBuilder._dumbGroup.add(textEl);\n textEl.updateTransform();\n\n labelEls.push(textEl);\n axisBuilder.group.add(textEl);\n\n textEl.decomposeTransform();\n\n });\n\n return labelEls;\n}\n\n\nexport default AxisBuilder;","import * as zrUtil from 'zrender/src/core/util';\nimport Model from '../../model/Model';\n\nvar each = zrUtil.each;\nvar curry = zrUtil.curry;\n\n// Build axisPointerModel, mergin tooltip.axisPointer model for each axis.\n// allAxesInfo should be updated when setOption performed.\nexport function collect(ecModel, api) {\n var result = {\n /**\n * key: makeKey(axis.model)\n * value: {\n * axis,\n * coordSys,\n * axisPointerModel,\n * triggerTooltip,\n * involveSeries,\n * snap,\n * seriesModels,\n * seriesDataCount\n * }\n */\n axesInfo: {},\n seriesInvolved: false,\n /**\n * key: makeKey(coordSys.model)\n * value: Object: key makeKey(axis.model), value: axisInfo\n */\n coordSysAxesInfo: {},\n coordSysMap: {}\n };\n\n collectAxesInfo(result, ecModel, api);\n\n // Check seriesInvolved for performance, in case too many series in some chart.\n result.seriesInvolved && collectSeriesInfo(result, ecModel);\n\n return result;\n}\n\nfunction collectAxesInfo(result, ecModel, api) {\n var globalTooltipModel = ecModel.getComponent('tooltip');\n var globalAxisPointerModel = ecModel.getComponent('axisPointer');\n // links can only be set on global.\n var linksOption = globalAxisPointerModel.get('link', true) || [];\n var linkGroups = [];\n\n // Collect axes info.\n each(api.getCoordinateSystems(), function (coordSys) {\n // Some coordinate system do not support axes, like geo.\n if (!coordSys.axisPointerEnabled) {\n return;\n }\n\n var coordSysKey = makeKey(coordSys.model);\n var axesInfoInCoordSys = result.coordSysAxesInfo[coordSysKey] = {};\n result.coordSysMap[coordSysKey] = coordSys;\n\n // Set tooltip (like 'cross') is a convienent way to show axisPointer\n // for user. So we enable seting tooltip on coordSys model.\n var coordSysModel = coordSys.model;\n var baseTooltipModel = coordSysModel.getModel('tooltip', globalTooltipModel);\n\n each(coordSys.getAxes(), curry(saveTooltipAxisInfo, false, null));\n\n // If axis tooltip used, choose tooltip axis for each coordSys.\n // Notice this case: coordSys is `grid` but not `cartesian2D` here.\n if (coordSys.getTooltipAxes\n && globalTooltipModel\n // If tooltip.showContent is set as false, tooltip will not\n // show but axisPointer will show as normal.\n && baseTooltipModel.get('show')\n ) {\n // Compatible with previous logic. But series.tooltip.trigger: 'axis'\n // or series.data[n].tooltip.trigger: 'axis' are not support any more.\n var triggerAxis = baseTooltipModel.get('trigger') === 'axis';\n var cross = baseTooltipModel.get('axisPointer.type') === 'cross';\n var tooltipAxes = coordSys.getTooltipAxes(baseTooltipModel.get('axisPointer.axis'));\n if (triggerAxis || cross) {\n each(tooltipAxes.baseAxes, curry(\n saveTooltipAxisInfo, cross ? 'cross' : true, triggerAxis\n ));\n }\n if (cross) {\n each(tooltipAxes.otherAxes, curry(saveTooltipAxisInfo, 'cross', false));\n }\n }\n\n // fromTooltip: true | false | 'cross'\n // triggerTooltip: true | false | null\n function saveTooltipAxisInfo(fromTooltip, triggerTooltip, axis) {\n var axisPointerModel = axis.model.getModel('axisPointer', globalAxisPointerModel);\n\n var axisPointerShow = axisPointerModel.get('show');\n if (!axisPointerShow || (\n axisPointerShow === 'auto'\n && !fromTooltip\n && !isHandleTrigger(axisPointerModel)\n )) {\n return;\n }\n\n if (triggerTooltip == null) {\n triggerTooltip = axisPointerModel.get('triggerTooltip');\n }\n\n axisPointerModel = fromTooltip\n ? makeAxisPointerModel(\n axis, baseTooltipModel, globalAxisPointerModel, ecModel,\n fromTooltip, triggerTooltip\n )\n : axisPointerModel;\n\n var snap = axisPointerModel.get('snap');\n var key = makeKey(axis.model);\n var involveSeries = triggerTooltip || snap || axis.type === 'category';\n\n // If result.axesInfo[key] exist, override it (tooltip has higher priority).\n var axisInfo = result.axesInfo[key] = {\n key: key,\n axis: axis,\n coordSys: coordSys,\n axisPointerModel: axisPointerModel,\n triggerTooltip: triggerTooltip,\n involveSeries: involveSeries,\n snap: snap,\n useHandle: isHandleTrigger(axisPointerModel),\n seriesModels: []\n };\n axesInfoInCoordSys[key] = axisInfo;\n result.seriesInvolved |= involveSeries;\n\n var groupIndex = getLinkGroupIndex(linksOption, axis);\n if (groupIndex != null) {\n var linkGroup = linkGroups[groupIndex] || (linkGroups[groupIndex] = {axesInfo: {}});\n linkGroup.axesInfo[key] = axisInfo;\n linkGroup.mapper = linksOption[groupIndex].mapper;\n axisInfo.linkGroup = linkGroup;\n }\n }\n });\n}\n\nfunction makeAxisPointerModel(\n axis, baseTooltipModel, globalAxisPointerModel, ecModel, fromTooltip, triggerTooltip\n) {\n var tooltipAxisPointerModel = baseTooltipModel.getModel('axisPointer');\n var volatileOption = {};\n\n each(\n [\n 'type', 'snap', 'lineStyle', 'shadowStyle', 'label',\n 'animation', 'animationDurationUpdate', 'animationEasingUpdate', 'z'\n ],\n function (field) {\n volatileOption[field] = zrUtil.clone(tooltipAxisPointerModel.get(field));\n }\n );\n\n // category axis do not auto snap, otherwise some tick that do not\n // has value can not be hovered. value/time/log axis default snap if\n // triggered from tooltip and trigger tooltip.\n volatileOption.snap = axis.type !== 'category' && !!triggerTooltip;\n\n // Compatibel with previous behavior, tooltip axis do not show label by default.\n // Only these properties can be overrided from tooltip to axisPointer.\n if (tooltipAxisPointerModel.get('type') === 'cross') {\n volatileOption.type = 'line';\n }\n var labelOption = volatileOption.label || (volatileOption.label = {});\n // Follow the convention, do not show label when triggered by tooltip by default.\n labelOption.show == null && (labelOption.show = false);\n\n if (fromTooltip === 'cross') {\n // When 'cross', both axes show labels.\n labelOption.show = true;\n // If triggerTooltip, this is a base axis, which should better not use cross style\n // (cross style is dashed by default)\n if (!triggerTooltip) {\n var crossStyle = volatileOption.lineStyle = tooltipAxisPointerModel.get('crossStyle');\n crossStyle && zrUtil.defaults(labelOption, crossStyle.textStyle);\n }\n }\n\n return axis.model.getModel(\n 'axisPointer',\n new Model(volatileOption, globalAxisPointerModel, ecModel)\n );\n}\n\nfunction collectSeriesInfo(result, ecModel) {\n // Prepare data for axis trigger\n ecModel.eachSeries(function (seriesModel) {\n\n // Notice this case: this coordSys is `cartesian2D` but not `grid`.\n var coordSys = seriesModel.coordinateSystem;\n var seriesTooltipTrigger = seriesModel.get('tooltip.trigger', true);\n var seriesTooltipShow = seriesModel.get('tooltip.show', true);\n if (!coordSys\n || seriesTooltipTrigger === 'none'\n || seriesTooltipTrigger === false\n || seriesTooltipTrigger === 'item'\n || seriesTooltipShow === false\n || seriesModel.get('axisPointer.show', true) === false\n ) {\n return;\n }\n\n each(result.coordSysAxesInfo[makeKey(coordSys.model)], function (axisInfo) {\n var axis = axisInfo.axis;\n if (coordSys.getAxis(axis.dim) === axis) {\n axisInfo.seriesModels.push(seriesModel);\n axisInfo.seriesDataCount == null && (axisInfo.seriesDataCount = 0);\n axisInfo.seriesDataCount += seriesModel.getData().count();\n }\n });\n\n }, this);\n}\n\n/**\n * For example:\n * {\n * axisPointer: {\n * links: [{\n * xAxisIndex: [2, 4],\n * yAxisIndex: 'all'\n * }, {\n * xAxisId: ['a5', 'a7'],\n * xAxisName: 'xxx'\n * }]\n * }\n * }\n */\nfunction getLinkGroupIndex(linksOption, axis) {\n var axisModel = axis.model;\n var dim = axis.dim;\n for (var i = 0; i < linksOption.length; i++) {\n var linkOption = linksOption[i] || {};\n if (checkPropInLink(linkOption[dim + 'AxisId'], axisModel.id)\n || checkPropInLink(linkOption[dim + 'AxisIndex'], axisModel.componentIndex)\n || checkPropInLink(linkOption[dim + 'AxisName'], axisModel.name)\n ) {\n return i;\n }\n }\n}\n\nfunction checkPropInLink(linkPropValue, axisPropValue) {\n return linkPropValue === 'all'\n || (zrUtil.isArray(linkPropValue) && zrUtil.indexOf(linkPropValue, axisPropValue) >= 0)\n || linkPropValue === axisPropValue;\n}\n\nexport function fixValue(axisModel) {\n var axisInfo = getAxisInfo(axisModel);\n if (!axisInfo) {\n return;\n }\n\n var axisPointerModel = axisInfo.axisPointerModel;\n var scale = axisInfo.axis.scale;\n var option = axisPointerModel.option;\n var status = axisPointerModel.get('status');\n var value = axisPointerModel.get('value');\n\n // Parse init value for category and time axis.\n if (value != null) {\n value = scale.parse(value);\n }\n\n var useHandle = isHandleTrigger(axisPointerModel);\n // If `handle` used, `axisPointer` will always be displayed, so value\n // and status should be initialized.\n if (status == null) {\n option.status = useHandle ? 'show' : 'hide';\n }\n\n var extent = scale.getExtent().slice();\n extent[0] > extent[1] && extent.reverse();\n\n if (// Pick a value on axis when initializing.\n value == null\n // If both `handle` and `dataZoom` are used, value may be out of axis extent,\n // where we should re-pick a value to keep `handle` displaying normally.\n || value > extent[1]\n ) {\n // Make handle displayed on the end of the axis when init, which looks better.\n value = extent[1];\n }\n if (value < extent[0]) {\n value = extent[0];\n }\n\n option.value = value;\n\n if (useHandle) {\n option.status = axisInfo.axis.scale.isBlank() ? 'hide' : 'show';\n }\n}\n\nexport function getAxisInfo(axisModel) {\n var coordSysAxesInfo = (axisModel.ecModel.getComponent('axisPointer') || {}).coordSysAxesInfo;\n return coordSysAxesInfo && coordSysAxesInfo.axesInfo[makeKey(axisModel)];\n}\n\nexport function getAxisPointerModel(axisModel) {\n var axisInfo = getAxisInfo(axisModel);\n return axisInfo && axisInfo.axisPointerModel;\n}\n\nfunction isHandleTrigger(axisPointerModel) {\n return !!axisPointerModel.get('handle.show');\n}\n\n/**\n * @param {module:echarts/model/Model} model\n * @return {string} unique key\n */\nexport function makeKey(model) {\n return model.type + '||' + model.id;\n}\n","import {__DEV__} from '../../config';\nimport * as echarts from '../../echarts';\nimport * as axisPointerModelHelper from '../axisPointer/modelHelper';\n\n/**\n * Base class of AxisView.\n */\nvar AxisView = echarts.extendComponentView({\n\n type: 'axis',\n\n /**\n * @private\n */\n _axisPointer: null,\n\n /**\n * @protected\n * @type {string}\n */\n axisPointerClass: null,\n\n /**\n * @override\n */\n render: function (axisModel, ecModel, api, payload) {\n // FIXME\n // This process should proformed after coordinate systems updated\n // (axis scale updated), and should be performed each time update.\n // So put it here temporarily, although it is not appropriate to\n // put a model-writing procedure in `view`.\n this.axisPointerClass && axisPointerModelHelper.fixValue(axisModel);\n\n AxisView.superApply(this, 'render', arguments);\n\n updateAxisPointer(this, axisModel, ecModel, api, payload, true);\n },\n\n /**\n * Action handler.\n * @public\n * @param {module:echarts/coord/cartesian/AxisModel} axisModel\n * @param {module:echarts/model/Global} ecModel\n * @param {module:echarts/ExtensionAPI} api\n * @param {Object} payload\n */\n updateAxisPointer: function (axisModel, ecModel, api, payload, force) {\n updateAxisPointer(this, axisModel, ecModel, api, payload, false);\n },\n\n /**\n * @override\n */\n remove: function (ecModel, api) {\n var axisPointer = this._axisPointer;\n axisPointer && axisPointer.remove(api);\n AxisView.superApply(this, 'remove', arguments);\n },\n\n /**\n * @override\n */\n dispose: function (ecModel, api) {\n disposeAxisPointer(this, api);\n AxisView.superApply(this, 'dispose', arguments);\n }\n\n});\n\nfunction updateAxisPointer(axisView, axisModel, ecModel, api, payload, forceRender) {\n var Clazz = AxisView.getAxisPointerClass(axisView.axisPointerClass);\n if (!Clazz) {\n return;\n }\n var axisPointerModel = axisPointerModelHelper.getAxisPointerModel(axisModel);\n axisPointerModel\n ? (axisView._axisPointer || (axisView._axisPointer = new Clazz()))\n .render(axisModel, axisPointerModel, api, forceRender)\n : disposeAxisPointer(axisView, api);\n}\n\nfunction disposeAxisPointer(axisView, ecModel, api) {\n var axisPointer = axisView._axisPointer;\n axisPointer && axisPointer.dispose(ecModel, api);\n axisView._axisPointer = null;\n}\n\nvar axisPointerClazz = [];\n\nAxisView.registerAxisPointerClass = function (type, clazz) {\n if (__DEV__) {\n if (axisPointerClazz[type]) {\n throw new Error('axisPointer ' + type + ' exists');\n }\n }\n axisPointerClazz[type] = clazz;\n};\n\nAxisView.getAxisPointerClass = function (type) {\n return type && axisPointerClazz[type];\n};\n\nexport default AxisView;","import * as zrUtil from 'zrender/src/core/util';\n\n/**\n * @param {Object} opt {labelInside}\n * @return {Object} {\n * position, rotation, labelDirection, labelOffset,\n * tickDirection, labelRotate, labelInterval, z2\n * }\n */\nexport function layout(gridModel, axisModel, opt) {\n opt = opt || {};\n var grid = gridModel.coordinateSystem;\n var axis = axisModel.axis;\n var layout = {};\n\n var rawAxisPosition = axis.position;\n var axisPosition = axis.onZero ? 'onZero' : rawAxisPosition;\n var axisDim = axis.dim;\n\n var rect = grid.getRect();\n var rectBound = [rect.x, rect.x + rect.width, rect.y, rect.y + rect.height];\n var idx = {left: 0, right: 1, top: 0, bottom: 1, onZero: 2};\n var axisOffset = axisModel.get('offset') || 0;\n\n var posBound = axisDim === 'x'\n ? [rectBound[2] - axisOffset, rectBound[3] + axisOffset]\n : [rectBound[0] - axisOffset, rectBound[1] + axisOffset];\n\n if (axis.onZero) {\n var otherAxis = grid.getAxis(axisDim === 'x' ? 'y' : 'x', axis.onZeroAxisIndex);\n var onZeroCoord = otherAxis.toGlobalCoord(otherAxis.dataToCoord(0));\n posBound[idx['onZero']] = Math.max(Math.min(onZeroCoord, posBound[1]), posBound[0]);\n }\n\n // Axis position\n layout.position = [\n axisDim === 'y' ? posBound[idx[axisPosition]] : rectBound[0],\n axisDim === 'x' ? posBound[idx[axisPosition]] : rectBound[3]\n ];\n\n // Axis rotation\n layout.rotation = Math.PI / 2 * (axisDim === 'x' ? 0 : 1);\n\n // Tick and label direction, x y is axisDim\n var dirMap = {top: -1, bottom: 1, left: -1, right: 1};\n\n layout.labelDirection = layout.tickDirection = layout.nameDirection = dirMap[rawAxisPosition];\n layout.labelOffset = axis.onZero ? posBound[idx[rawAxisPosition]] - posBound[idx['onZero']] : 0;\n\n if (axisModel.get('axisTick.inside')) {\n layout.tickDirection = -layout.tickDirection;\n }\n if (zrUtil.retrieve(opt.labelInside, axisModel.get('axisLabel.inside'))) {\n layout.labelDirection = -layout.labelDirection;\n }\n\n // Special label rotation\n var labelRotate = axisModel.get('axisLabel.rotate');\n layout.labelRotate = axisPosition === 'top' ? -labelRotate : labelRotate;\n\n // label interval when auto mode.\n layout.labelInterval = axis.getLabelInterval();\n\n // Over splitLine and splitArea\n layout.z2 = 1;\n\n return layout;\n}\n","import * as zrUtil from 'zrender/src/core/util';\nimport * as graphic from '../../util/graphic';\nimport AxisBuilder from './AxisBuilder';\nimport AxisView from './AxisView';\nimport * as cartesianAxisHelper from './cartesianAxisHelper';\n\nvar ifIgnoreOnTick = AxisBuilder.ifIgnoreOnTick;\nvar getInterval = AxisBuilder.getInterval;\n\nvar axisBuilderAttrs = [\n 'axisLine', 'axisTickLabel', 'axisName'\n];\nvar selfBuilderAttrs = [\n 'splitArea', 'splitLine'\n];\n\n// function getAlignWithLabel(model, axisModel) {\n// var alignWithLabel = model.get('alignWithLabel');\n// if (alignWithLabel === 'auto') {\n// alignWithLabel = axisModel.get('axisTick.alignWithLabel');\n// }\n// return alignWithLabel;\n// }\n\nvar CartesianAxisView = AxisView.extend({\n\n type: 'cartesianAxis',\n\n axisPointerClass: 'CartesianAxisPointer',\n\n /**\n * @override\n */\n render: function (axisModel, ecModel, api, payload) {\n\n this.group.removeAll();\n\n var oldAxisGroup = this._axisGroup;\n this._axisGroup = new graphic.Group();\n\n this.group.add(this._axisGroup);\n\n if (!axisModel.get('show')) {\n return;\n }\n\n var gridModel = axisModel.getCoordSysModel();\n\n var layout = cartesianAxisHelper.layout(gridModel, axisModel);\n\n var axisBuilder = new AxisBuilder(axisModel, layout);\n\n zrUtil.each(axisBuilderAttrs, axisBuilder.add, axisBuilder);\n\n this._axisGroup.add(axisBuilder.getGroup());\n\n zrUtil.each(selfBuilderAttrs, function (name) {\n if (axisModel.get(name + '.show')) {\n this['_' + name](axisModel, gridModel, layout.labelInterval);\n }\n }, this);\n\n graphic.groupTransition(oldAxisGroup, this._axisGroup, axisModel);\n\n CartesianAxisView.superCall(this, 'render', axisModel, ecModel, api, payload);\n },\n\n /**\n * @param {module:echarts/coord/cartesian/AxisModel} axisModel\n * @param {module:echarts/coord/cartesian/GridModel} gridModel\n * @param {number|Function} labelInterval\n * @private\n */\n _splitLine: function (axisModel, gridModel, labelInterval) {\n var axis = axisModel.axis;\n\n if (axis.scale.isBlank()) {\n return;\n }\n\n var splitLineModel = axisModel.getModel('splitLine');\n var lineStyleModel = splitLineModel.getModel('lineStyle');\n var lineColors = lineStyleModel.get('color');\n\n var lineInterval = getInterval(splitLineModel, labelInterval);\n\n lineColors = zrUtil.isArray(lineColors) ? lineColors : [lineColors];\n\n var gridRect = gridModel.coordinateSystem.getRect();\n var isHorizontal = axis.isHorizontal();\n\n var lineCount = 0;\n\n var ticksCoords = axis.getTicksCoords(\n // splitLineModel.get('alignWithLabel')\n );\n var ticks = axis.scale.getTicks();\n\n var showMinLabel = axisModel.get('axisLabel.showMinLabel');\n var showMaxLabel = axisModel.get('axisLabel.showMaxLabel');\n\n var p1 = [];\n var p2 = [];\n // Simple optimization\n // Batching the lines if color are the same\n var lineStyle = lineStyleModel.getLineStyle();\n for (var i = 0; i < ticksCoords.length; i++) {\n if (ifIgnoreOnTick(\n axis, i, lineInterval, ticksCoords.length,\n showMinLabel, showMaxLabel\n )) {\n continue;\n }\n\n var tickCoord = axis.toGlobalCoord(ticksCoords[i]);\n\n if (isHorizontal) {\n p1[0] = tickCoord;\n p1[1] = gridRect.y;\n p2[0] = tickCoord;\n p2[1] = gridRect.y + gridRect.height;\n }\n else {\n p1[0] = gridRect.x;\n p1[1] = tickCoord;\n p2[0] = gridRect.x + gridRect.width;\n p2[1] = tickCoord;\n }\n\n var colorIndex = (lineCount++) % lineColors.length;\n this._axisGroup.add(new graphic.Line(graphic.subPixelOptimizeLine({\n anid: 'line_' + ticks[i],\n\n shape: {\n x1: p1[0],\n y1: p1[1],\n x2: p2[0],\n y2: p2[1]\n },\n style: zrUtil.defaults({\n stroke: lineColors[colorIndex]\n }, lineStyle),\n silent: true\n })));\n }\n },\n\n /**\n * @param {module:echarts/coord/cartesian/AxisModel} axisModel\n * @param {module:echarts/coord/cartesian/GridModel} gridModel\n * @param {number|Function} labelInterval\n * @private\n */\n _splitArea: function (axisModel, gridModel, labelInterval) {\n var axis = axisModel.axis;\n\n if (axis.scale.isBlank()) {\n return;\n }\n\n var splitAreaModel = axisModel.getModel('splitArea');\n var areaStyleModel = splitAreaModel.getModel('areaStyle');\n var areaColors = areaStyleModel.get('color');\n\n var gridRect = gridModel.coordinateSystem.getRect();\n\n var ticksCoords = axis.getTicksCoords(\n // splitAreaModel.get('alignWithLabel')\n );\n var ticks = axis.scale.getTicks();\n\n var prevX = axis.toGlobalCoord(ticksCoords[0]);\n var prevY = axis.toGlobalCoord(ticksCoords[0]);\n\n var count = 0;\n\n var areaInterval = getInterval(splitAreaModel, labelInterval);\n\n var areaStyle = areaStyleModel.getAreaStyle();\n areaColors = zrUtil.isArray(areaColors) ? areaColors : [areaColors];\n\n var showMinLabel = axisModel.get('axisLabel.showMinLabel');\n var showMaxLabel = axisModel.get('axisLabel.showMaxLabel');\n\n for (var i = 1; i < ticksCoords.length; i++) {\n if (ifIgnoreOnTick(\n axis, i, areaInterval, ticksCoords.length,\n showMinLabel, showMaxLabel\n )) {\n continue;\n }\n\n var tickCoord = axis.toGlobalCoord(ticksCoords[i]);\n\n var x;\n var y;\n var width;\n var height;\n if (axis.isHorizontal()) {\n x = prevX;\n y = gridRect.y;\n width = tickCoord - x;\n height = gridRect.height;\n }\n else {\n x = gridRect.x;\n y = prevY;\n width = gridRect.width;\n height = tickCoord - y;\n }\n\n var colorIndex = (count++) % areaColors.length;\n this._axisGroup.add(new graphic.Rect({\n anid: 'area_' + ticks[i],\n\n shape: {\n x: x,\n y: y,\n width: width,\n height: height\n },\n style: zrUtil.defaults({\n fill: areaColors[colorIndex]\n }, areaStyle),\n silent: true\n }));\n\n prevX = x + width;\n prevY = y + height;\n }\n }\n});\n\nCartesianAxisView.extend({\n type: 'xAxis'\n});\nCartesianAxisView.extend({\n type: 'yAxis'\n});\n","import * as echarts from '../echarts';\nimport * as zrUtil from 'zrender/src/core/util';\nimport * as graphic from '../util/graphic';\n\nimport '../coord/cartesian/Grid';\nimport './axis';\n\n// Grid view\necharts.extendComponentView({\n\n type: 'grid',\n\n render: function (gridModel, ecModel) {\n this.group.removeAll();\n if (gridModel.get('show')) {\n this.group.add(new graphic.Rect({\n shape: gridModel.coordinateSystem.getRect(),\n style: zrUtil.defaults({\n fill: gridModel.get('backgroundColor')\n }, gridModel.getItemStyle()),\n silent: true,\n z2: -1\n }));\n }\n }\n\n});\n\necharts.registerPreprocessor(function (option) {\n // Only create grid when need\n if (option.xAxis && option.yAxis && !option.grid) {\n option.grid = {};\n }\n});","import * as echarts from '../echarts';\nimport * as zrUtil from 'zrender/src/core/util';\n\nimport './line/LineSeries';\nimport './line/LineView';\nimport visualSymbol from '../visual/symbol';\nimport layoutPoints from '../layout/points';\nimport dataSample from '../processor/dataSample';\n\n// In case developer forget to include grid component\nimport '../component/gridSimple';\n\necharts.registerVisual(zrUtil.curry(\n visualSymbol, 'line', 'circle', 'line'\n));\necharts.registerLayout(zrUtil.curry(\n layoutPoints, 'line'\n));\n\n// Down sample after filter\necharts.registerProcessor(echarts.PRIORITY.PROCESSOR.STATISTIC, zrUtil.curry(\n dataSample, 'line'\n));\n","import * as zrUtil from 'zrender/src/core/util';\nimport {parsePercent} from '../util/number';\n\nvar STACK_PREFIX = '__ec_stack_';\n\nfunction getSeriesStackId(seriesModel) {\n return seriesModel.get('stack') || STACK_PREFIX + seriesModel.seriesIndex;\n}\n\nfunction getAxisKey(axis) {\n return axis.dim + axis.index;\n}\n\n/**\n * @param {Object} opt\n * @param {module:echarts/coord/Axis} opt.axis Only support category axis currently.\n * @param {number} opt.count Positive interger.\n * @param {number} [opt.barWidth]\n * @param {number} [opt.barMaxWidth]\n * @param {number} [opt.barGap]\n * @param {number} [opt.barCategoryGap]\n * @return {Object} {width, offset, offsetCenter} If axis.type is not 'category', return undefined.\n */\nfunction getLayoutOnAxis(opt, api) {\n var params = [];\n var baseAxis = opt.axis;\n var axisKey = 'axis0';\n\n if (baseAxis.type !== 'category') {\n return;\n }\n var bandWidth = baseAxis.getBandWidth();\n\n for (var i = 0; i < opt.count || 0; i++) {\n params.push(zrUtil.defaults({\n bandWidth: bandWidth,\n axisKey: axisKey,\n stackId: STACK_PREFIX + i\n }, opt));\n }\n var widthAndOffsets = doCalBarWidthAndOffset(params, api);\n\n var result = [];\n for (var i = 0; i < opt.count; i++) {\n var item = widthAndOffsets[axisKey][STACK_PREFIX + i];\n item.offsetCenter = item.offset + item.width / 2;\n result.push(item);\n }\n\n return result;\n}\n\nfunction calBarWidthAndOffset(barSeries, api) {\n var seriesInfoList = zrUtil.map(barSeries, function (seriesModel) {\n var data = seriesModel.getData();\n var cartesian = seriesModel.coordinateSystem;\n var baseAxis = cartesian.getBaseAxis();\n var axisExtent = baseAxis.getExtent();\n var bandWidth = baseAxis.type === 'category'\n ? baseAxis.getBandWidth()\n : (Math.abs(axisExtent[1] - axisExtent[0]) / data.count());\n\n var barWidth = parsePercent(\n seriesModel.get('barWidth'), bandWidth\n );\n var barMaxWidth = parsePercent(\n seriesModel.get('barMaxWidth'), bandWidth\n );\n var barGap = seriesModel.get('barGap');\n var barCategoryGap = seriesModel.get('barCategoryGap');\n\n return {\n bandWidth: bandWidth,\n barWidth: barWidth,\n barMaxWidth: barMaxWidth,\n barGap: barGap,\n barCategoryGap: barCategoryGap,\n axisKey: getAxisKey(baseAxis),\n stackId: getSeriesStackId(seriesModel)\n };\n });\n\n return doCalBarWidthAndOffset(seriesInfoList, api);\n}\n\nfunction doCalBarWidthAndOffset(seriesInfoList, api) {\n // Columns info on each category axis. Key is cartesian name\n var columnsMap = {};\n\n zrUtil.each(seriesInfoList, function (seriesInfo, idx) {\n var axisKey = seriesInfo.axisKey;\n var bandWidth = seriesInfo.bandWidth;\n var columnsOnAxis = columnsMap[axisKey] || {\n bandWidth: bandWidth,\n remainedWidth: bandWidth,\n autoWidthCount: 0,\n categoryGap: '20%',\n gap: '30%',\n stacks: {}\n };\n var stacks = columnsOnAxis.stacks;\n columnsMap[axisKey] = columnsOnAxis;\n\n var stackId = seriesInfo.stackId;\n\n if (!stacks[stackId]) {\n columnsOnAxis.autoWidthCount++;\n }\n stacks[stackId] = stacks[stackId] || {\n width: 0,\n maxWidth: 0\n };\n\n // Caution: In a single coordinate system, these barGrid attributes\n // will be shared by series. Consider that they have default values,\n // only the attributes set on the last series will work.\n // Do not change this fact unless there will be a break change.\n\n // TODO\n var barWidth = seriesInfo.barWidth;\n if (barWidth && !stacks[stackId].width) {\n // See #6312, do not restrict width.\n stacks[stackId].width = barWidth;\n barWidth = Math.min(columnsOnAxis.remainedWidth, barWidth);\n columnsOnAxis.remainedWidth -= barWidth;\n }\n\n var barMaxWidth = seriesInfo.barMaxWidth;\n barMaxWidth && (stacks[stackId].maxWidth = barMaxWidth);\n var barGap = seriesInfo.barGap;\n (barGap != null) && (columnsOnAxis.gap = barGap);\n var barCategoryGap = seriesInfo.barCategoryGap;\n (barCategoryGap != null) && (columnsOnAxis.categoryGap = barCategoryGap);\n });\n\n var result = {};\n\n zrUtil.each(columnsMap, function (columnsOnAxis, coordSysName) {\n\n result[coordSysName] = {};\n\n var stacks = columnsOnAxis.stacks;\n var bandWidth = columnsOnAxis.bandWidth;\n var categoryGap = parsePercent(columnsOnAxis.categoryGap, bandWidth);\n var barGapPercent = parsePercent(columnsOnAxis.gap, 1);\n\n var remainedWidth = columnsOnAxis.remainedWidth;\n var autoWidthCount = columnsOnAxis.autoWidthCount;\n var autoWidth = (remainedWidth - categoryGap)\n / (autoWidthCount + (autoWidthCount - 1) * barGapPercent);\n autoWidth = Math.max(autoWidth, 0);\n\n // Find if any auto calculated bar exceeded maxBarWidth\n zrUtil.each(stacks, function (column, stack) {\n var maxWidth = column.maxWidth;\n if (maxWidth && maxWidth < autoWidth) {\n maxWidth = Math.min(maxWidth, remainedWidth);\n if (column.width) {\n maxWidth = Math.min(maxWidth, column.width);\n }\n remainedWidth -= maxWidth;\n column.width = maxWidth;\n autoWidthCount--;\n }\n });\n\n // Recalculate width again\n autoWidth = (remainedWidth - categoryGap)\n / (autoWidthCount + (autoWidthCount - 1) * barGapPercent);\n autoWidth = Math.max(autoWidth, 0);\n\n var widthSum = 0;\n var lastColumn;\n zrUtil.each(stacks, function (column, idx) {\n if (!column.width) {\n column.width = autoWidth;\n }\n lastColumn = column;\n widthSum += column.width * (1 + barGapPercent);\n });\n if (lastColumn) {\n widthSum -= lastColumn.width * barGapPercent;\n }\n\n var offset = -widthSum / 2;\n zrUtil.each(stacks, function (column, stackId) {\n result[coordSysName][stackId] = result[coordSysName][stackId] || {\n offset: offset,\n width: column.width\n };\n\n offset += column.width * (1 + barGapPercent);\n });\n });\n\n return result;\n}\n\n/**\n * @param {string} seriesType\n * @param {module:echarts/model/Global} ecModel\n * @param {module:echarts/ExtensionAPI} api\n */\nfunction barLayoutGrid(seriesType, ecModel, api) {\n\n var barWidthAndOffset = calBarWidthAndOffset(\n zrUtil.filter(\n ecModel.getSeriesByType(seriesType),\n function (seriesModel) {\n return !ecModel.isSeriesFiltered(seriesModel)\n && seriesModel.coordinateSystem\n && seriesModel.coordinateSystem.type === 'cartesian2d';\n }\n )\n );\n\n var lastStackCoords = {};\n var lastStackCoordsOrigin = {};\n\n ecModel.eachSeriesByType(seriesType, function (seriesModel) {\n\n // Check series coordinate, do layout for cartesian2d only\n if (seriesModel.coordinateSystem.type !== 'cartesian2d') {\n return;\n }\n\n var data = seriesModel.getData();\n var cartesian = seriesModel.coordinateSystem;\n var baseAxis = cartesian.getBaseAxis();\n\n var stackId = getSeriesStackId(seriesModel);\n var columnLayoutInfo = barWidthAndOffset[getAxisKey(baseAxis)][stackId];\n var columnOffset = columnLayoutInfo.offset;\n var columnWidth = columnLayoutInfo.width;\n var valueAxis = cartesian.getOtherAxis(baseAxis);\n\n var barMinHeight = seriesModel.get('barMinHeight') || 0;\n\n var valueAxisStart = baseAxis.onZero\n ? valueAxis.toGlobalCoord(valueAxis.dataToCoord(0))\n : valueAxis.getGlobalExtent()[0];\n\n var coordDims = [\n seriesModel.coordDimToDataDim('x')[0],\n seriesModel.coordDimToDataDim('y')[0]\n ];\n var coords = data.mapArray(coordDims, function (x, y) {\n return cartesian.dataToPoint([x, y]);\n }, true);\n\n lastStackCoords[stackId] = lastStackCoords[stackId] || [];\n lastStackCoordsOrigin[stackId] = lastStackCoordsOrigin[stackId] || []; // Fix #4243\n\n data.setLayout({\n offset: columnOffset,\n size: columnWidth\n });\n\n data.each(seriesModel.coordDimToDataDim(valueAxis.dim)[0], function (value, idx) {\n if (isNaN(value)) {\n return;\n }\n\n if (!lastStackCoords[stackId][idx]) {\n lastStackCoords[stackId][idx] = {\n p: valueAxisStart, // Positive stack\n n: valueAxisStart // Negative stack\n };\n lastStackCoordsOrigin[stackId][idx] = {\n p: valueAxisStart, // Positive stack\n n: valueAxisStart // Negative stack\n };\n }\n var sign = value >= 0 ? 'p' : 'n';\n var coord = coords[idx];\n var lastCoord = lastStackCoords[stackId][idx][sign];\n var lastCoordOrigin = lastStackCoordsOrigin[stackId][idx][sign];\n var x;\n var y;\n var width;\n var height;\n\n if (valueAxis.isHorizontal()) {\n x = lastCoord;\n y = coord[1] + columnOffset;\n width = coord[0] - lastCoordOrigin;\n height = columnWidth;\n\n lastStackCoordsOrigin[stackId][idx][sign] += width;\n if (Math.abs(width) < barMinHeight) {\n width = (width < 0 ? -1 : 1) * barMinHeight;\n }\n lastStackCoords[stackId][idx][sign] += width;\n }\n else {\n x = coord[0] + columnOffset;\n y = lastCoord;\n width = columnWidth;\n height = coord[1] - lastCoordOrigin;\n\n lastStackCoordsOrigin[stackId][idx][sign] += height;\n if (Math.abs(height) < barMinHeight) {\n // Include zero to has a positive bar\n height = (height <= 0 ? -1 : 1) * barMinHeight;\n }\n lastStackCoords[stackId][idx][sign] += height;\n }\n\n data.setItemLayout(idx, {\n x: x,\n y: y,\n width: width,\n height: height\n });\n }, true);\n\n }, this);\n}\n\nbarLayoutGrid.getLayoutOnAxis = getLayoutOnAxis;\n\nexport default barLayoutGrid;","import SeriesModel from '../../model/Series';\nimport createListFromArray from '../helper/createListFromArray';\n\nexport default SeriesModel.extend({\n\n type: 'series.__base_bar__',\n\n getInitialData: function (option, ecModel) {\n return createListFromArray(option.data, this, ecModel);\n },\n\n getMarkerPosition: function (value) {\n var coordSys = this.coordinateSystem;\n if (coordSys) {\n // PENDING if clamp ?\n var pt = coordSys.dataToPoint(value, true);\n var data = this.getData();\n var offset = data.getLayout('offset');\n var size = data.getLayout('size');\n var offsetIndex = coordSys.getBaseAxis().isHorizontal() ? 0 : 1;\n pt[offsetIndex] += offset + size / 2;\n return pt;\n }\n return [NaN, NaN];\n },\n\n defaultOption: {\n zlevel: 0, // 一级层叠\n z: 2, // 二级层叠\n coordinateSystem: 'cartesian2d',\n legendHoverLink: true,\n // stack: null\n\n // Cartesian coordinate system\n // xAxisIndex: 0,\n // yAxisIndex: 0,\n\n // 最小高度改为0\n barMinHeight: 0,\n // 最小角度为0,仅对极坐标系下的柱状图有效\n barMinAngle: 0,\n // cursor: null,\n\n // barMaxWidth: null,\n // 默认自适应\n // barWidth: null,\n // 柱间距离,默认为柱形宽度的30%,可设固定值\n // barGap: '30%',\n // 类目间柱形距离,默认为类目间距的20%,可设固定值\n // barCategoryGap: '20%',\n // label: {\n // normal: {\n // show: false\n // }\n // },\n itemStyle: {\n // normal: {\n // color: '各异'\n // },\n // emphasis: {}\n }\n }\n});","import BaseBarSeries from './BaseBarSeries';\n\nexport default BaseBarSeries.extend({\n\n type: 'series.bar',\n\n dependencies: ['grid', 'polar'],\n\n brushSelector: 'rect'\n});\n","import * as graphic from '../../util/graphic';\n\nexport function setLabel(\n normalStyle, hoverStyle, itemModel, color, seriesModel, dataIndex, labelPositionOutside\n) {\n var labelModel = itemModel.getModel('label.normal');\n var hoverLabelModel = itemModel.getModel('label.emphasis');\n\n graphic.setLabelStyle(\n normalStyle, hoverStyle, labelModel, hoverLabelModel,\n {\n labelFetcher: seriesModel,\n labelDataIndex: dataIndex,\n defaultText: seriesModel.getRawValue(dataIndex),\n isRectText: true,\n autoColor: color\n }\n );\n\n fixPosition(normalStyle);\n fixPosition(hoverStyle);\n}\n\nfunction fixPosition(style, labelPositionOutside) {\n if (style.textPosition === 'outside') {\n style.textPosition = labelPositionOutside;\n }\n}","import makeStyleMapper from '../../model/mixin/makeStyleMapper';\n\nvar getBarItemStyle = makeStyleMapper(\n [\n ['fill', 'color'],\n ['stroke', 'borderColor'],\n ['lineWidth', 'borderWidth'],\n // Compatitable with 2\n ['stroke', 'barBorderColor'],\n ['lineWidth', 'barBorderWidth'],\n ['opacity'],\n ['shadowBlur'],\n ['shadowOffsetX'],\n ['shadowOffsetY'],\n ['shadowColor']\n ]\n);\n\nexport default {\n getBarItemStyle: function (excludes) {\n var style = getBarItemStyle(this, excludes);\n if (this.getBorderLineDash) {\n var lineDash = this.getBorderLineDash();\n lineDash && (style.lineDash = lineDash);\n }\n return style;\n }\n};\n\n","import {__DEV__} from '../../config';\nimport * as echarts from '../../echarts';\nimport * as zrUtil from 'zrender/src/core/util';\nimport * as graphic from '../../util/graphic';\nimport {setLabel} from './helper';\nimport Model from '../../model/Model';\nimport barItemStyle from './barItemStyle';\n\n\nvar BAR_BORDER_WIDTH_QUERY = ['itemStyle', 'normal', 'barBorderWidth'];\n\n// FIXME\n// Just for compatible with ec2.\nzrUtil.extend(Model.prototype, barItemStyle);\n\nexport default echarts.extendChartView({\n\n type: 'bar',\n\n render: function (seriesModel, ecModel, api) {\n var coordinateSystemType = seriesModel.get('coordinateSystem');\n\n if (coordinateSystemType === 'cartesian2d'\n || coordinateSystemType === 'polar'\n ) {\n this._render(seriesModel, ecModel, api);\n }\n else if (__DEV__) {\n console.warn('Only cartesian2d and polar supported for bar.');\n }\n\n return this.group;\n },\n\n dispose: zrUtil.noop,\n\n _render: function (seriesModel, ecModel, api) {\n var group = this.group;\n var data = seriesModel.getData();\n var oldData = this._data;\n\n var coord = seriesModel.coordinateSystem;\n var baseAxis = coord.getBaseAxis();\n var isHorizontalOrRadial;\n\n if (coord.type === 'cartesian2d') {\n isHorizontalOrRadial = baseAxis.isHorizontal();\n }\n else if (coord.type === 'polar') {\n isHorizontalOrRadial = baseAxis.dim === 'angle';\n }\n\n var animationModel = seriesModel.isAnimationEnabled() ? seriesModel : null;\n\n data.diff(oldData)\n .add(function (dataIndex) {\n if (!data.hasValue(dataIndex)) {\n return;\n }\n\n var itemModel = data.getItemModel(dataIndex);\n var layout = getLayout[coord.type](data, dataIndex, itemModel);\n var el = elementCreator[coord.type](\n data, dataIndex, itemModel, layout, isHorizontalOrRadial, animationModel\n );\n data.setItemGraphicEl(dataIndex, el);\n group.add(el);\n\n updateStyle(\n el, data, dataIndex, itemModel, layout,\n seriesModel, isHorizontalOrRadial, coord.type === 'polar'\n );\n })\n .update(function (newIndex, oldIndex) {\n var el = oldData.getItemGraphicEl(oldIndex);\n\n if (!data.hasValue(newIndex)) {\n group.remove(el);\n return;\n }\n\n var itemModel = data.getItemModel(newIndex);\n var layout = getLayout[coord.type](data, newIndex, itemModel);\n\n if (el) {\n graphic.updateProps(el, {shape: layout}, animationModel, newIndex);\n }\n else {\n el = elementCreator[coord.type](\n data, newIndex, itemModel, layout, isHorizontalOrRadial, animationModel, true\n );\n }\n\n data.setItemGraphicEl(newIndex, el);\n // Add back\n group.add(el);\n\n updateStyle(\n el, data, newIndex, itemModel, layout,\n seriesModel, isHorizontalOrRadial, coord.type === 'polar'\n );\n })\n .remove(function (dataIndex) {\n var el = oldData.getItemGraphicEl(dataIndex);\n if (coord.type === 'cartesian2d') {\n el && removeRect(dataIndex, animationModel, el);\n }\n else {\n el && removeSector(dataIndex, animationModel, el);\n }\n })\n .execute();\n\n this._data = data;\n },\n\n remove: function (ecModel, api) {\n var group = this.group;\n var data = this._data;\n if (ecModel.get('animation')) {\n if (data) {\n data.eachItemGraphicEl(function (el) {\n if (el.type === 'sector') {\n removeSector(el.dataIndex, ecModel, el);\n }\n else {\n removeRect(el.dataIndex, ecModel, el);\n }\n });\n }\n }\n else {\n group.removeAll();\n }\n }\n});\n\nvar elementCreator = {\n\n cartesian2d: function (\n data, dataIndex, itemModel, layout, isHorizontal,\n animationModel, isUpdate\n ) {\n var rect = new graphic.Rect({shape: zrUtil.extend({}, layout)});\n\n // Animation\n if (animationModel) {\n var rectShape = rect.shape;\n var animateProperty = isHorizontal ? 'height' : 'width';\n var animateTarget = {};\n rectShape[animateProperty] = 0;\n animateTarget[animateProperty] = layout[animateProperty];\n graphic[isUpdate ? 'updateProps' : 'initProps'](rect, {\n shape: animateTarget\n }, animationModel, dataIndex);\n }\n\n return rect;\n },\n\n polar: function (\n data, dataIndex, itemModel, layout, isRadial,\n animationModel, isUpdate\n ) {\n var sector = new graphic.Sector({shape: zrUtil.extend({}, layout)});\n\n // Animation\n if (animationModel) {\n var sectorShape = sector.shape;\n var animateProperty = isRadial ? 'r' : 'endAngle';\n var animateTarget = {};\n sectorShape[animateProperty] = isRadial ? 0 : layout.startAngle;\n animateTarget[animateProperty] = layout[animateProperty];\n graphic[isUpdate ? 'updateProps' : 'initProps'](sector, {\n shape: animateTarget\n }, animationModel, dataIndex);\n }\n\n return sector;\n }\n};\n\nfunction removeRect(dataIndex, animationModel, el) {\n // Not show text when animating\n el.style.text = null;\n graphic.updateProps(el, {\n shape: {\n width: 0\n }\n }, animationModel, dataIndex, function () {\n el.parent && el.parent.remove(el);\n });\n}\n\nfunction removeSector(dataIndex, animationModel, el) {\n // Not show text when animating\n el.style.text = null;\n graphic.updateProps(el, {\n shape: {\n r: el.shape.r0\n }\n }, animationModel, dataIndex, function () {\n el.parent && el.parent.remove(el);\n });\n}\n\nvar getLayout = {\n cartesian2d: function (data, dataIndex, itemModel) {\n var layout = data.getItemLayout(dataIndex);\n var fixedLineWidth = getLineWidth(itemModel, layout);\n\n // fix layout with lineWidth\n var signX = layout.width > 0 ? 1 : -1;\n var signY = layout.height > 0 ? 1 : -1;\n return {\n x: layout.x + signX * fixedLineWidth / 2,\n y: layout.y + signY * fixedLineWidth / 2,\n width: layout.width - signX * fixedLineWidth,\n height: layout.height - signY * fixedLineWidth\n };\n },\n\n polar: function (data, dataIndex, itemModel) {\n var layout = data.getItemLayout(dataIndex);\n return {\n cx: layout.cx,\n cy: layout.cy,\n r0: layout.r0,\n r: layout.r,\n startAngle: layout.startAngle,\n endAngle: layout.endAngle\n };\n }\n};\n\nfunction updateStyle(\n el, data, dataIndex, itemModel, layout, seriesModel, isHorizontal, isPolar\n) {\n var color = data.getItemVisual(dataIndex, 'color');\n var opacity = data.getItemVisual(dataIndex, 'opacity');\n var itemStyleModel = itemModel.getModel('itemStyle.normal');\n var hoverStyle = itemModel.getModel('itemStyle.emphasis').getBarItemStyle();\n\n if (!isPolar) {\n el.setShape('r', itemStyleModel.get('barBorderRadius') || 0);\n }\n\n el.useStyle(zrUtil.defaults(\n {\n fill: color,\n opacity: opacity\n },\n itemStyleModel.getBarItemStyle()\n ));\n\n var cursorStyle = itemModel.getShallow('cursor');\n cursorStyle && el.attr('cursor', cursorStyle);\n\n var labelPositionOutside = isHorizontal\n ? (layout.height > 0 ? 'bottom' : 'top')\n : (layout.width > 0 ? 'left' : 'right');\n\n if (!isPolar) {\n setLabel(\n el.style, hoverStyle, itemModel, color,\n seriesModel, dataIndex, labelPositionOutside\n );\n }\n\n graphic.setHoverStyle(el, hoverStyle);\n}\n\n// In case width or height are too small.\nfunction getLineWidth(itemModel, rawLayout) {\n var lineWidth = itemModel.get(BAR_BORDER_WIDTH_QUERY) || 0;\n return Math.min(lineWidth, Math.abs(rawLayout.width), Math.abs(rawLayout.height));\n}\n","import * as echarts from '../echarts';\nimport * as zrUtil from 'zrender/src/core/util';\nimport barLayoutGrid from '../layout/barGrid';\n\nimport '../coord/cartesian/Grid';\nimport './bar/BarSeries';\nimport './bar/BarView';\n// In case developer forget to include grid component\nimport '../component/gridSimple';\n\n\necharts.registerLayout(zrUtil.curry(barLayoutGrid, 'bar'));\n\n// Visual coding for legend\necharts.registerVisual(function (ecModel) {\n ecModel.eachSeriesByType('bar', function (seriesModel) {\n var data = seriesModel.getData();\n data.setVisual('legendSymbol', 'roundRect');\n });\n});\n","/**\n * Data selectable mixin for chart series.\n * To eanble data select, option of series must have `selectedMode`.\n * And each data item will use `selected` to toggle itself selected status\n */\n\nimport * as zrUtil from 'zrender/src/core/util';\n\nexport default {\n\n updateSelectedMap: function (targetList) {\n this._targetList = targetList.slice();\n this._selectTargetMap = zrUtil.reduce(targetList || [], function (targetMap, target) {\n targetMap.set(target.name, target);\n return targetMap;\n }, zrUtil.createHashMap());\n },\n\n /**\n * Either name or id should be passed as input here.\n * If both of them are defined, id is used.\n *\n * @param {string|undefined} name name of data\n * @param {number|undefined} id dataIndex of data\n */\n // PENGING If selectedMode is null ?\n select: function (name, id) {\n var target = id != null\n ? this._targetList[id]\n : this._selectTargetMap.get(name);\n var selectedMode = this.get('selectedMode');\n if (selectedMode === 'single') {\n this._selectTargetMap.each(function (target) {\n target.selected = false;\n });\n }\n target && (target.selected = true);\n },\n\n /**\n * Either name or id should be passed as input here.\n * If both of them are defined, id is used.\n *\n * @param {string|undefined} name name of data\n * @param {number|undefined} id dataIndex of data\n */\n unSelect: function (name, id) {\n var target = id != null\n ? this._targetList[id]\n : this._selectTargetMap.get(name);\n // var selectedMode = this.get('selectedMode');\n // selectedMode !== 'single' && target && (target.selected = false);\n target && (target.selected = false);\n },\n\n /**\n * Either name or id should be passed as input here.\n * If both of them are defined, id is used.\n *\n * @param {string|undefined} name name of data\n * @param {number|undefined} id dataIndex of data\n */\n toggleSelected: function (name, id) {\n var target = id != null\n ? this._targetList[id]\n : this._selectTargetMap.get(name);\n if (target != null) {\n this[target.selected ? 'unSelect' : 'select'](name, id);\n return target.selected;\n }\n },\n\n /**\n * Either name or id should be passed as input here.\n * If both of them are defined, id is used.\n *\n * @param {string|undefined} name name of data\n * @param {number|undefined} id dataIndex of data\n */\n isSelected: function (name, id) {\n var target = id != null\n ? this._targetList[id]\n : this._selectTargetMap.get(name);\n return target && target.selected;\n }\n};","import * as echarts from '../../echarts';\nimport List from '../../data/List';\nimport * as zrUtil from 'zrender/src/core/util';\nimport * as modelUtil from '../../util/model';\nimport {getPercentWithPrecision} from '../../util/number';\nimport completeDimensions from '../../data/helper/completeDimensions';\nimport dataSelectableMixin from '../../component/helper/selectableMixin';\n\nvar PieSeries = echarts.extendSeriesModel({\n\n type: 'series.pie',\n\n // Overwrite\n init: function (option) {\n PieSeries.superApply(this, 'init', arguments);\n\n // Enable legend selection for each data item\n // Use a function instead of direct access because data reference may changed\n this.legendDataProvider = function () {\n return this.getRawData();\n };\n\n this.updateSelectedMap(option.data);\n\n this._defaultLabelLine(option);\n },\n\n // Overwrite\n mergeOption: function (newOption) {\n PieSeries.superCall(this, 'mergeOption', newOption);\n this.updateSelectedMap(this.option.data);\n },\n\n getInitialData: function (option, ecModel) {\n var dimensions = completeDimensions(['value'], option.data);\n var list = new List(dimensions, this);\n list.initData(option.data);\n return list;\n },\n\n // Overwrite\n getDataParams: function (dataIndex) {\n var data = this.getData();\n var params = PieSeries.superCall(this, 'getDataParams', dataIndex);\n // FIXME toFixed?\n\n var valueList = [];\n data.each('value', function (value) {\n valueList.push(value);\n });\n\n params.percent = getPercentWithPrecision(\n valueList,\n dataIndex,\n data.hostModel.get('percentPrecision')\n );\n\n params.$vars.push('percent');\n return params;\n },\n\n _defaultLabelLine: function (option) {\n // Extend labelLine emphasis\n modelUtil.defaultEmphasis(option.labelLine, ['show']);\n\n var labelLineNormalOpt = option.labelLine.normal;\n var labelLineEmphasisOpt = option.labelLine.emphasis;\n // Not show label line if `label.normal.show = false`\n labelLineNormalOpt.show = labelLineNormalOpt.show\n && option.label.normal.show;\n labelLineEmphasisOpt.show = labelLineEmphasisOpt.show\n && option.label.emphasis.show;\n },\n\n defaultOption: {\n zlevel: 0,\n z: 2,\n legendHoverLink: true,\n\n hoverAnimation: true,\n // 默认全局居中\n center: ['50%', '50%'],\n radius: [0, '75%'],\n // 默认顺时针\n clockwise: true,\n startAngle: 90,\n // 最小角度改为0\n minAngle: 0,\n // 选中时扇区偏移量\n selectedOffset: 10,\n // 高亮扇区偏移量\n hoverOffset: 10,\n\n // If use strategy to avoid label overlapping\n avoidLabelOverlap: true,\n // 选择模式,默认关闭,可选single,multiple\n // selectedMode: false,\n // 南丁格尔玫瑰图模式,'radius'(半径) | 'area'(面积)\n // roseType: null,\n\n percentPrecision: 2,\n\n // If still show when all data zero.\n stillShowZeroSum: true,\n\n // cursor: null,\n\n label: {\n normal: {\n // If rotate around circle\n rotate: false,\n show: true,\n // 'outer', 'inside', 'center'\n position: 'outer'\n // formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调\n // 默认使用全局文本样式,详见TEXTSTYLE\n // distance: 当position为inner时有效,为label位置到圆心的距离与圆半径(环状图为内外半径和)的比例系数\n },\n emphasis: {}\n },\n // Enabled when label.normal.position is 'outer'\n labelLine: {\n normal: {\n show: true,\n // 引导线两段中的第一段长度\n length: 15,\n // 引导线两段中的第二段长度\n length2: 15,\n smooth: false,\n lineStyle: {\n // color: 各异,\n width: 1,\n type: 'solid'\n }\n }\n },\n itemStyle: {\n normal: {\n borderWidth: 1\n },\n emphasis: {}\n },\n\n // Animation type canbe expansion, scale\n animationType: 'expansion',\n\n animationEasing: 'cubicOut',\n\n data: []\n }\n});\n\nzrUtil.mixin(PieSeries, dataSelectableMixin);\n\nexport default PieSeries;","import * as zrUtil from 'zrender/src/core/util';\nimport * as graphic from '../../util/graphic';\nimport ChartView from '../../view/Chart';\n\n/**\n * @param {module:echarts/model/Series} seriesModel\n * @param {boolean} hasAnimation\n * @inner\n */\nfunction updateDataSelected(uid, seriesModel, hasAnimation, api) {\n var data = seriesModel.getData();\n var dataIndex = this.dataIndex;\n var name = data.getName(dataIndex);\n var selectedOffset = seriesModel.get('selectedOffset');\n\n api.dispatchAction({\n type: 'pieToggleSelect',\n from: uid,\n name: name,\n seriesId: seriesModel.id\n });\n\n data.each(function (idx) {\n toggleItemSelected(\n data.getItemGraphicEl(idx),\n data.getItemLayout(idx),\n seriesModel.isSelected(data.getName(idx)),\n selectedOffset,\n hasAnimation\n );\n });\n}\n\n/**\n * @param {module:zrender/graphic/Sector} el\n * @param {Object} layout\n * @param {boolean} isSelected\n * @param {number} selectedOffset\n * @param {boolean} hasAnimation\n * @inner\n */\nfunction toggleItemSelected(el, layout, isSelected, selectedOffset, hasAnimation) {\n var midAngle = (layout.startAngle + layout.endAngle) / 2;\n\n var dx = Math.cos(midAngle);\n var dy = Math.sin(midAngle);\n\n var offset = isSelected ? selectedOffset : 0;\n var position = [dx * offset, dy * offset];\n\n hasAnimation\n // animateTo will stop revious animation like update transition\n ? el.animate()\n .when(200, {\n position: position\n })\n .start('bounceOut')\n : el.attr('position', position);\n}\n\n/**\n * Piece of pie including Sector, Label, LabelLine\n * @constructor\n * @extends {module:zrender/graphic/Group}\n */\nfunction PiePiece(data, idx) {\n\n graphic.Group.call(this);\n\n var sector = new graphic.Sector({\n z2: 2\n });\n var polyline = new graphic.Polyline();\n var text = new graphic.Text();\n this.add(sector);\n this.add(polyline);\n this.add(text);\n\n this.updateData(data, idx, true);\n\n // Hover to change label and labelLine\n function onEmphasis() {\n polyline.ignore = polyline.hoverIgnore;\n text.ignore = text.hoverIgnore;\n }\n function onNormal() {\n polyline.ignore = polyline.normalIgnore;\n text.ignore = text.normalIgnore;\n }\n this.on('emphasis', onEmphasis)\n .on('normal', onNormal)\n .on('mouseover', onEmphasis)\n .on('mouseout', onNormal);\n}\n\nvar piePieceProto = PiePiece.prototype;\n\npiePieceProto.updateData = function (data, idx, firstCreate) {\n\n var sector = this.childAt(0);\n\n var seriesModel = data.hostModel;\n var itemModel = data.getItemModel(idx);\n var layout = data.getItemLayout(idx);\n var sectorShape = zrUtil.extend({}, layout);\n sectorShape.label = null;\n\n if (firstCreate) {\n sector.setShape(sectorShape);\n\n var animationType = seriesModel.getShallow('animationType');\n if (animationType === 'scale') {\n sector.shape.r = layout.r0;\n graphic.initProps(sector, {\n shape: {\n r: layout.r\n }\n }, seriesModel, idx);\n }\n // Expansion\n else {\n sector.shape.endAngle = layout.startAngle;\n graphic.updateProps(sector, {\n shape: {\n endAngle: layout.endAngle\n }\n }, seriesModel, idx);\n }\n\n }\n else {\n graphic.updateProps(sector, {\n shape: sectorShape\n }, seriesModel, idx);\n }\n\n // Update common style\n var itemStyleModel = itemModel.getModel('itemStyle');\n var visualColor = data.getItemVisual(idx, 'color');\n\n sector.useStyle(\n zrUtil.defaults(\n {\n lineJoin: 'bevel',\n fill: visualColor\n },\n itemStyleModel.getModel('normal').getItemStyle()\n )\n );\n sector.hoverStyle = itemStyleModel.getModel('emphasis').getItemStyle();\n\n var cursorStyle = itemModel.getShallow('cursor');\n cursorStyle && sector.attr('cursor', cursorStyle);\n\n // Toggle selected\n toggleItemSelected(\n this,\n data.getItemLayout(idx),\n itemModel.get('selected'),\n seriesModel.get('selectedOffset'),\n seriesModel.get('animation')\n );\n\n function onEmphasis() {\n // Sector may has animation of updating data. Force to move to the last frame\n // Or it may stopped on the wrong shape\n sector.stopAnimation(true);\n sector.animateTo({\n shape: {\n r: layout.r + seriesModel.get('hoverOffset')\n }\n }, 300, 'elasticOut');\n }\n function onNormal() {\n sector.stopAnimation(true);\n sector.animateTo({\n shape: {\n r: layout.r\n }\n }, 300, 'elasticOut');\n }\n sector.off('mouseover').off('mouseout').off('emphasis').off('normal');\n if (itemModel.get('hoverAnimation') && seriesModel.isAnimationEnabled()) {\n sector\n .on('mouseover', onEmphasis)\n .on('mouseout', onNormal)\n .on('emphasis', onEmphasis)\n .on('normal', onNormal);\n }\n\n this._updateLabel(data, idx);\n\n graphic.setHoverStyle(this);\n};\n\npiePieceProto._updateLabel = function (data, idx) {\n\n var labelLine = this.childAt(1);\n var labelText = this.childAt(2);\n\n var seriesModel = data.hostModel;\n var itemModel = data.getItemModel(idx);\n var layout = data.getItemLayout(idx);\n var labelLayout = layout.label;\n var visualColor = data.getItemVisual(idx, 'color');\n\n graphic.updateProps(labelLine, {\n shape: {\n points: labelLayout.linePoints || [\n [labelLayout.x, labelLayout.y], [labelLayout.x, labelLayout.y], [labelLayout.x, labelLayout.y]\n ]\n }\n }, seriesModel, idx);\n\n graphic.updateProps(labelText, {\n style: {\n x: labelLayout.x,\n y: labelLayout.y\n }\n }, seriesModel, idx);\n labelText.attr({\n rotation: labelLayout.rotation,\n origin: [labelLayout.x, labelLayout.y],\n z2: 10\n });\n\n var labelModel = itemModel.getModel('label.normal');\n var labelHoverModel = itemModel.getModel('label.emphasis');\n var labelLineModel = itemModel.getModel('labelLine.normal');\n var labelLineHoverModel = itemModel.getModel('labelLine.emphasis');\n var visualColor = data.getItemVisual(idx, 'color');\n\n graphic.setLabelStyle(\n labelText.style, labelText.hoverStyle = {}, labelModel, labelHoverModel,\n {\n labelFetcher: data.hostModel,\n labelDataIndex: idx,\n defaultText: data.getName(idx),\n autoColor: visualColor,\n useInsideStyle: !!labelLayout.inside\n },\n {\n textAlign: labelLayout.textAlign,\n textVerticalAlign: labelLayout.verticalAlign,\n opacity: data.getItemVisual(idx, 'opacity')\n }\n );\n\n labelText.ignore = labelText.normalIgnore = !labelModel.get('show');\n labelText.hoverIgnore = !labelHoverModel.get('show');\n\n labelLine.ignore = labelLine.normalIgnore = !labelLineModel.get('show');\n labelLine.hoverIgnore = !labelLineHoverModel.get('show');\n\n // Default use item visual color\n labelLine.setStyle({\n stroke: visualColor,\n opacity: data.getItemVisual(idx, 'opacity')\n });\n labelLine.setStyle(labelLineModel.getModel('lineStyle').getLineStyle());\n\n labelLine.hoverStyle = labelLineHoverModel.getModel('lineStyle').getLineStyle();\n\n var smooth = labelLineModel.get('smooth');\n if (smooth && smooth === true) {\n smooth = 0.4;\n }\n labelLine.setShape({\n smooth: smooth\n });\n};\n\nzrUtil.inherits(PiePiece, graphic.Group);\n\n\n// Pie view\nvar PieView = ChartView.extend({\n\n type: 'pie',\n\n init: function () {\n var sectorGroup = new graphic.Group();\n this._sectorGroup = sectorGroup;\n },\n\n render: function (seriesModel, ecModel, api, payload) {\n if (payload && (payload.from === this.uid)) {\n return;\n }\n\n var data = seriesModel.getData();\n var oldData = this._data;\n var group = this.group;\n\n var hasAnimation = ecModel.get('animation');\n var isFirstRender = !oldData;\n var animationType = seriesModel.get('animationType');\n\n var onSectorClick = zrUtil.curry(\n updateDataSelected, this.uid, seriesModel, hasAnimation, api\n );\n\n var selectedMode = seriesModel.get('selectedMode');\n\n data.diff(oldData)\n .add(function (idx) {\n var piePiece = new PiePiece(data, idx);\n // Default expansion animation\n if (isFirstRender && animationType !== 'scale') {\n piePiece.eachChild(function (child) {\n child.stopAnimation(true);\n });\n }\n\n selectedMode && piePiece.on('click', onSectorClick);\n\n data.setItemGraphicEl(idx, piePiece);\n\n group.add(piePiece);\n })\n .update(function (newIdx, oldIdx) {\n var piePiece = oldData.getItemGraphicEl(oldIdx);\n\n piePiece.updateData(data, newIdx);\n\n piePiece.off('click');\n selectedMode && piePiece.on('click', onSectorClick);\n group.add(piePiece);\n data.setItemGraphicEl(newIdx, piePiece);\n })\n .remove(function (idx) {\n var piePiece = oldData.getItemGraphicEl(idx);\n group.remove(piePiece);\n })\n .execute();\n\n if (\n hasAnimation && isFirstRender && data.count() > 0\n // Default expansion animation\n && animationType !== 'scale'\n ) {\n var shape = data.getItemLayout(0);\n var r = Math.max(api.getWidth(), api.getHeight()) / 2;\n\n var removeClipPath = zrUtil.bind(group.removeClipPath, group);\n group.setClipPath(this._createClipPath(\n shape.cx, shape.cy, r, shape.startAngle, shape.clockwise, removeClipPath, seriesModel\n ));\n }\n\n this._data = data;\n },\n\n dispose: function () {},\n\n _createClipPath: function (\n cx, cy, r, startAngle, clockwise, cb, seriesModel\n ) {\n var clipPath = new graphic.Sector({\n shape: {\n cx: cx,\n cy: cy,\n r0: 0,\n r: r,\n startAngle: startAngle,\n endAngle: startAngle,\n clockwise: clockwise\n }\n });\n\n graphic.initProps(clipPath, {\n shape: {\n endAngle: startAngle + (clockwise ? 1 : -1) * Math.PI * 2\n }\n }, seriesModel, cb);\n\n return clipPath;\n },\n\n /**\n * @implement\n */\n containPoint: function (point, seriesModel) {\n var data = seriesModel.getData();\n var itemLayout = data.getItemLayout(0);\n if (itemLayout) {\n var dx = point[0] - itemLayout.cx;\n var dy = point[1] - itemLayout.cy;\n var radius = Math.sqrt(dx * dx + dy * dy);\n return radius <= itemLayout.r && radius >= itemLayout.r0;\n }\n }\n\n});\n\nexport default PieView;","import * as echarts from '../echarts';\nimport * as zrUtil from 'zrender/src/core/util';\n\nexport default function (seriesType, actionInfos) {\n zrUtil.each(actionInfos, function (actionInfo) {\n actionInfo.update = 'updateView';\n /**\n * @payload\n * @property {string} seriesName\n * @property {string} name\n */\n echarts.registerAction(actionInfo, function (payload, ecModel) {\n var selected = {};\n ecModel.eachComponent(\n {mainType: 'series', subType: seriesType, query: payload},\n function (seriesModel) {\n if (seriesModel[actionInfo.method]) {\n seriesModel[actionInfo.method](\n payload.name,\n payload.dataIndex\n );\n }\n var data = seriesModel.getData();\n // Create selected map\n data.each(function (idx) {\n var name = data.getName(idx);\n selected[name] = seriesModel.isSelected(name)\n || false;\n });\n }\n );\n return {\n name: payload.name,\n selected: selected\n };\n });\n });\n}","// Pick color from palette for each data item.\n// Applicable for charts that require applying color palette\n// in data level (like pie, funnel, chord).\n\nexport default function (seriesType, ecModel) {\n // Pie and funnel may use diferrent scope\n var paletteScope = {};\n ecModel.eachRawSeriesByType(seriesType, function (seriesModel) {\n var dataAll = seriesModel.getRawData();\n var idxMap = {};\n if (!ecModel.isSeriesFiltered(seriesModel)) {\n var data = seriesModel.getData();\n data.each(function (idx) {\n var rawIdx = data.getRawIndex(idx);\n idxMap[rawIdx] = idx;\n });\n dataAll.each(function (rawIdx) {\n var filteredIdx = idxMap[rawIdx];\n\n // If series.itemStyle.normal.color is a function. itemVisual may be encoded\n var singleDataColor = |