PageSourceSearch

https://zgsxswtzb.gov.cn/static/js/echarts.js

js zgsxswtzb.gov.cn collected 2026-10-08 07:39:41 UTC 3,149,785 bytes, 90,302 lines download raw bytes

1(function (global, factory) {
2    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
3        typeof define === 'function' && define.amd ? define(['exports'], factory) :
4            (factory((global.echarts = {})));
5}(this, (function (exports) { 'use strict';
6
7    /*
8* Licensed to the Apache Software Foundation (ASF) under one
9* or more contributor license agreements.  See the NOTICE file
10* distributed with this work for additional information
11* regarding copyright ownership.  The ASF licenses this file
12* to you under the Apache License, Version 2.0 (the
13* "License"); you may not use this file except in compliance
14* with the License.  You may obtain a copy of the License at
15*
16*   http://www.apache.org/licenses/LICENSE-2.0
17*
18* Unless required by applicable law or agreed to in writing,
19* software distributed under the License is distributed on an
20* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21* KIND, either express or implied.  See the License for the
22* specific language governing permissions and limitations
23* under the License.
24*/
25
26// (1) The code `if (__DEV__) ...` can be removed by build tool.
27// (2) If intend to use `__DEV__`, this module should be imported. Use a global
28// variable `__DEV__` may cause that miss the declaration (see #6535), or the
29// declaration is behind of the using position (for example in `Model.extent`,
30// And tools like rollup can not analysis the dependency if not import).
31
32    var dev;
33
34// In browser
35    if (typeof window !== 'undefined') {
36        dev = window.__DEV__;
37    }
38// In node
39    else if (typeof global !== 'undefined') {
40        dev = global.__DEV__;
41    }
42
43    if (typeof dev === 'undefined') {
44        dev = true;
45    }
46
47    var __DEV__ = dev;
48
49    /**
50     * zrender: 鐢熸垚鍞竴id
51     *
52     * @author errorrik ([email protected])
53     */
54
55    var idStart = 0x0907;
56
57    var guid = function () {
58        return idStart++;
59    };
60
61    /**
62     * echarts璁惧鐜璇嗗埆
63     *
64     * @desc echarts鍩轰簬Canvas锛岀函Javascript鍥捐〃搴擄紝鎻愪緵鐩磋锛岀敓鍔紝鍙氦浜掞紝鍙釜鎬у寲瀹氬埗鐨勬暟鎹粺璁″浘琛ㄣ€�
65     * @author firede[[email protected]]
66     * @desc thanks zepto.
67     */
68
69    var env = {};
70
71    if (typeof wx === 'object' && typeof wx.getSystemInfoSync === 'function') {
72        // In Weixin Application
73        env = {
74            browser: {},
75            os: {},
76            node: false,
77            wxa: true, // Weixin Application
78            canvasSupported: true,
79            svgSupported: false,
80            touchEventsSupported: true
81        };
82    }
83    else if (typeof document === 'undefined' && typeof self !== 'undefined') {
84        // In worker
85        env = {
86            browser: {},
87            os: {},
88            node: false,
89            worker: true,
90            canvasSupported: true
91        };
92    }
93    else if (typeof navigator === 'undefined') {
94        // In node
95        env = {
96            browser: {},
97            os: {},
98            node: true,
99            worker: false,
100            // Assume canvas is supported
101            canvasSupported: true,
102            svgSupported: true
103        };
104    }
105    else {
106        env = detect(navigator.userAgent);
107    }
108
109    var env$1 = env;
110
111// Zepto.js
112// (c) 2010-2013 Thomas Fuchs
113// Zepto.js may be freely distributed under the MIT license.
114
115    function detect(ua) {
116        var os = {};
117        var browser = {};
118        // var webkit = ua.match(/Web[kK]it[\/]{0,1}([\d.]+)/);
119        // var android = ua.match(/(Android);?[\s\/]+([\d.]+)?/);
120        // var ipad = ua.match(/(iPad).*OS\s([\d_]+)/);
121        // var ipod = ua.match(/(iPod)(.*OS\s([\d_]+))?/);
122        // var iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/);
123        // var webos = ua.match(/(webOS|hpwOS)[\s\/]([\d.]+)/);
124        // var touchpad = webos && ua.match(/TouchPad/);
125        // var kindle = ua.match(/Kindle\/([\d.]+)/);
126        // var silk = ua.match(/Silk\/([\d._]+)/);
127        // var blackberry = ua.match(/(BlackBerry).*Version\/([\d.]+)/);
128        // var bb10 = ua.match(/(BB10).*Version\/([\d.]+)/);
129        // var rimtabletos = ua.match(/(RIM\sTablet\sOS)\s([\d.]+)/);
130        // var playbook = ua.match(/PlayBook/);
131        // var chrome = ua.match(/Chrome\/([\d.]+)/) || ua.match(/CriOS\/([\d.]+)/);
132        var firefox = ua.match(/Firefox\/([\d.]+)/);
133        // var safari = webkit && ua.match(/Mobile\//) && !chrome;
134        // var webview = ua.match(/(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/) && !chrome;
135        var ie = ua.match(/MSIE\s([\d.]+)/)
136            // IE 11 Trident/7.0; rv:11.0
137            || ua.match(/Trident\/.+?rv:(([\d.]+))/);
138        var edge = ua.match(/Edge\/([\d.]+)/); // IE 12 and 12+
139
140        var weChat = (/micromessenger/i).test(ua);
141
142        // Todo: clean this up with a better OS/browser seperation:
143        // - discern (more) between multiple browsers on android
144        // - decide if kindle fire in silk mode is android or not
145        // - Firefox on Android doesn't specify the Android version
146        // - possibly devide in os, device and browser hashes
147
148        // if (browser.webkit = !!webkit) browser.version = webkit[1];
149
150        // if (android) os.android = true, os.version = android[2];
151        // if (iphone && !ipod) os.ios = os.iphone = true, os.version = iphone[2].replace(/_/g, '.');
152        // if (ipad) os.ios = os.ipad = true, os.version = ipad[2].replace(/_/g, '.');
153        // if (ipod) os.ios = os.ipod = true, os.version = ipod[3] ? ipod[3].replace(/_/g, '.') : null;
154        // if (webos) os.webos = true, os.version = webos[2];
155        // if (touchpad) os.touchpad = true;
156        // if (blackberry) os.blackberry = true, os.version = blackberry[2];
157        // if (bb10) os.bb10 = true, os.version = bb10[2];
158        // if (rimtabletos) os.rimtabletos = true, os.version = rimtabletos[2];
159        // if (playbook) browser.playbook = true;
160        // if (kindle) os.kindle = true, os.version = kindle[1];
161        // if (silk) browser.silk = true, browser.version = silk[1];
162        // if (!silk && os.android && ua.match(/Kindle Fire/)) browser.silk = true;
163        // if (chrome) browser.chrome = true, browser.version = chrome[1];
164        if (firefox) {
165            browser.firefox = true;
166            browser.version = firefox[1];
167        }
168        // if (safari && (ua.match(/Safari/) || !!os.ios)) browser.safari = true;
169        // if (webview) browser.webview = true;
170
171        if (ie) {
172            browser.ie = true;
173            browser.version = ie[1];
174        }
175
176        if (edge) {
177            browser.edge = true;
178            browser.version = edge[1];
179        }
180
181        // It is difficult to detect WeChat in Win Phone precisely, because ua can
182        // not be set on win phone. So we do not consider Win Phone.
183        if (weChat) {
184            browser.weChat = true;
185        }
186
187        // os.tablet = !!(ipad || playbook || (android && !ua.match(/Mobile/)) ||
188        //     (firefox && ua.match(/Tablet/)) || (ie && !ua.match(/Phone/) && ua.match(/Touch/)));
189        // os.phone  = !!(!os.tablet && !os.ipod && (android || iphone || webos ||
190        //     (chrome && ua.match(/Android/)) || (chrome && ua.match(/CriOS\/([\d.]+)/)) ||
191        //     (firefox && ua.match(/Mobile/)) || (ie && ua.match(/Touch/))));
192
193        return {
194            browser: browser,
195            os: os,
196            node: false,
197            // 鍘熺敓canvas鏀寔锛屾敼鏋佺鐐逛簡
198            // canvasSupported : !(browser.ie && parseFloat(browser.version) < 9)
199            canvasSupported: !!document.createElement('canvas').getContext,
200            svgSupported: typeof SVGRect !== 'undefined',
201            // works on most browsers
202            // IE10/11 does not support touch event, and MS Edge supports them but not by
203            // default, so we dont check navigator.maxTouchPoints for them here.
204            touchEventsSupported: 'ontouchstart' in window && !browser.ie && !browser.edge,
205            // <http://caniuse.com/#search=pointer%20event>.
206            pointerEventsSupported: 'onpointerdown' in window
207            // Firefox supports pointer but not by default, only MS browsers are reliable on pointer
208            // events currently. So we dont use that on other browsers unless tested sufficiently.
209            // Although IE 10 supports pointer event, it use old style and is different from the
210            // standard. So we exclude that. (IE 10 is hardly used on touch device)
211            && (browser.edge || (browser.ie && browser.version >= 11))
212            // passiveSupported: detectPassiveSupport()
213        };
214    }
215
216// See https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
217// function detectPassiveSupport() {
218//     // Test via a getter in the options object to see if the passive property is accessed
219//     var supportsPassive = false;
220//     try {
221//         var opts = Object.defineProperty({}, 'passive', {
222//             get: function() {
223//                 supportsPassive = true;
224//             }
225//         });
226//         window.addEventListener('testPassive', function() {}, opts);
227//     } catch (e) {
228//     }
229//     return supportsPassive;
230// }
231
232    /**
233     * @module zrender/core/util
234     */
235
236// 鐢ㄤ簬澶勭悊merge鏃舵棤娉曢亶鍘咲ate绛夊璞$殑闂
237    var BUILTIN_OBJECT = {
238        '[object Function]': 1,
239        '[object RegExp]': 1,
240        '[object Date]': 1,
241        '[object Error]': 1,
242        '[object CanvasGradient]': 1,
243        '[object CanvasPattern]': 1,
244        // For node-canvas
245        '[object Image]': 1,
246        '[object Canvas]': 1
247    };
248
249    var TYPED_ARRAY = {
250        '[object Int8Array]': 1,
251        '[object Uint8Array]': 1,
252        '[object Uint8ClampedArray]': 1,
253        '[object Int16Array]': 1,
254        '[object Uint16Array]': 1,
255        '[object Int32Array]': 1,
256        '[object Uint32Array]': 1,
257        '[object Float32Array]': 1,
258        '[object Float64Array]': 1
259    };
260
261    var objToString = Object.prototype.toString;
262
263    var arrayProto = Array.prototype;
264    var nativeForEach = arrayProto.forEach;
265    var nativeFilter = arrayProto.filter;
266    var nativeSlice = arrayProto.slice;
267    var nativeMap = arrayProto.map;
268    var nativeReduce = arrayProto.reduce;
269
270// Avoid assign to an exported variable, for transforming to cjs.
271    var methods = {};
272
273    function $override(name, fn) {
274        // Clear ctx instance for different environment
275        if (name === 'createCanvas') {
276            _ctx = null;
277        }
278
279        methods[name] = fn;
280    }
281
282    /**
283     * Those data types can be cloned:
284     *     Plain object, Array, TypedArray, number, string, null, undefined.
285     * Those data types will be assgined using the orginal data:
286     *     BUILTIN_OBJECT
287     * Instance of user defined class will be cloned to a plain object, without
288     * properties in prototype.
289     * Other data types is not supported (not sure what will happen).
290     *
291     * Caution: do not support clone Date, for performance consideration.
292     * (There might be a large number of date in `series.data`).
293     * So date should not be modified in and out of echarts.
294     *
295     * @param {*} source
296     * @return {*} new
297     */
298    function clone(source) {
299        if (source == null || typeof source != 'object') {
300            return source;
301        }
302
303        var result = source;
304        var typeStr = objToString.call(source);
305
306        if (typeStr === '[object Array]') {
307            if (!isPrimitive(source)) {
308                result = [];
309                for (var i = 0, len = source.length; i < len; i++) {
310                    result[i] = clone(source[i]);
311                }
312            }
313        }
314        else if (TYPED_ARRAY[typeStr]) {
315            if (!isPrimitive(source)) {
316                var Ctor = source.constructor;
317                if (source.constructor.from) {
318                    result = Ctor.from(source);
319                }
320                else {
321                    result = new Ctor(source.length);
322                    for (var i = 0, len = source.length; i < len; i++) {
323                        result[i] = clone(source[i]);
324                    }
325                }
326            }
327        }
328        else if (!BUILTIN_OBJECT[typeStr] && !isPrimitive(source) && !isDom(source)) {
329            result = {};
330            for (var key in source) {
331                if (source.hasOwnProperty(key)) {
332                    result[key] = clone(source[key]);
333                }
334            }
335        }
336
337        return result;
338    }
339
340    /**
341     * @memberOf module:zrender/core/util
342     * @param {*} target
343     * @param {*} source
344     * @param {boolean} [overwrite=false]
345     */
346    function merge(target, source, overwrite) {
347        // We should escapse that source is string
348        // and enter for ... in ...
349        if (!isObject$1(source) || !isObject$1(target)) {
350            return overwrite ? clone(source) : target;
351        }
352
353        for (var key in source) {
354            if (source.hasOwnProperty(key)) {
355                var targetProp = target[key];
356                var sourceProp = source[key];
357
358                if (isObject$1(sourceProp)
359                    && isObject$1(targetProp)
360                    && !isArray(sourceProp)
361                    && !isArray(targetProp)
362                    && !isDom(sourceProp)
363                    && !isDom(targetProp)
364                    && !isBuiltInObject(sourceProp)
365                    && !isBuiltInObject(targetProp)
366                    && !isPrimitive(sourceProp)
367                    && !isPrimitive(targetProp)
368                ) {
369                    // 濡傛灉闇€瑕侀€掑綊瑕嗙洊锛屽氨閫掑綊璋冪敤merge
370                    merge(targetProp, sourceProp, overwrite);
371                }
372                else if (overwrite || !(key in target)) {
373                    // 鍚﹀垯鍙鐞唎verwrite涓簍rue锛屾垨鑰呭湪鐩爣瀵硅薄涓病鏈夋灞炴€х殑鎯呭喌
374                    // NOTE锛屽湪 target[key] 涓嶅瓨鍦ㄧ殑鏃跺€欎篃鏄洿鎺ヨ鐩�
375                    target[key] = clone(source[key], true);
376                }
377            }
378        }
379
380        return target;
381    }
382
383    /**
384     * @param {Array} targetAndSources The first item is target, and the rests are source.
385     * @param {boolean} [overwrite=false]
386     * @return {*} target
387     */
388    function mergeAll(targetAndSources, overwrite) {
389        var result = targetAndSources[0];
390        for (var i = 1, len = targetAndSources.length; i < len; i++) {
391            result = merge(result, targetAndSources[i], overwrite);
392        }
393        return result;
394    }
395
396    /**
397     * @param {*} target
398     * @param {*} source
399     * @memberOf module:zrender/core/util
400     */
401    function extend(target, source) {
402        for (var key in source) {
403            if (source.hasOwnProperty(key)) {
404                target[key] = source[key];
405            }
406        }
407        return target;
408    }
409
410    /**
411     * @param {*} target
412     * @param {*} source
413     * @param {boolean} [overlay=false]
414     * @memberOf module:zrender/core/util
415     */
416    function defaults(target, source, overlay) {
417        for (var key in source) {
418            if (source.hasOwnProperty(key)
419                && (overlay ? source[key] != null : target[key] == null)
420            ) {
421                target[key] = source[key];
422            }
423        }
424        return target;
425    }
426
427    var createCanvas = function () {
428        return methods.createCanvas();
429    };
430
431    methods.createCanvas = function () {
432        return document.createElement('canvas');
433    };
434
435// FIXME
436    var _ctx;
437
438    function getContext() {
439        if (!_ctx) {
440            // Use util.createCanvas instead of createCanvas
vendor: 11,173 bytes, lines 441-858
441            // because createCanvas may be overwritten in different environment
442            _ctx = createCanvas().getContext('2d');
443        }
444        return _ctx;
445    }
446
447    /**
448     * 鏌ヨ鏁扮粍涓厓绱犵殑index
449     * @memberOf module:zrender/core/util
450     */
451    function indexOf(array, value) {
452        if (array) {
453            if (array.indexOf) {
454                return array.indexOf(value);
455            }
456            for (var i = 0, len = array.length; i < len; i++) {
457                if (array[i] === value) {
458                    return i;
459                }
460            }
461        }
462        return -1;
463    }
464
465    /**
466     * 鏋勯€犵被缁ф壙鍏崇郴
467     *
468     * @memberOf module:zrender/core/util
469     * @param {Function} clazz 婧愮被
470     * @param {Function} baseClazz 鍩虹被
471     */
472    function inherits(clazz, baseClazz) {
473        var clazzPrototype = clazz.prototype;
474        function F() {}
475        F.prototype = baseClazz.prototype;
476        clazz.prototype = new F();
477
478        for (var prop in clazzPrototype) {
479            clazz.prototype[prop] = clazzPrototype[prop];
480        }
481        clazz.prototype.constructor = clazz;
482        clazz.superClass = baseClazz;
483    }
484
485    /**
486     * @memberOf module:zrender/core/util
487     * @param {Object|Function} target
488     * @param {Object|Function} sorce
489     * @param {boolean} overlay
490     */
491    function mixin(target, source, overlay) {
492        target = 'prototype' in target ? target.prototype : target;
493        source = 'prototype' in source ? source.prototype : source;
494
495        defaults(target, source, overlay);
496    }
497
498    /**
499     * Consider typed array.
500     * @param {Array|TypedArray} data
501     */
502    function isArrayLike(data) {
503        if (! data) {
504            return;
505        }
506        if (typeof data == 'string') {
507            return false;
508        }
509        return typeof data.length == 'number';
510    }
511
512    /**
513     * 鏁扮粍鎴栧璞¢亶鍘�
514     * @memberOf module:zrender/core/util
515     * @param {Object|Array} obj
516     * @param {Function} cb
517     * @param {*} [context]
518     */
519    function each$1(obj, cb, context) {
520        if (!(obj && cb)) {
521            return;
522        }
523        if (obj.forEach && obj.forEach === nativeForEach) {
524            obj.forEach(cb, context);
525        }
526        else if (obj.length === +obj.length) {
527            for (var i = 0, len = obj.length; i < len; i++) {
528                cb.call(context, obj[i], i, obj);
529            }
530        }
531        else {
532            for (var key in obj) {
533                if (obj.hasOwnProperty(key)) {
534                    cb.call(context, obj[key], key, obj);
535                }
536            }
537        }
538    }
539
540    /**
541     * 鏁扮粍鏄犲皠
542     * @memberOf module:zrender/core/util
543     * @param {Array} obj
544     * @param {Function} cb
545     * @param {*} [context]
546     * @return {Array}
547     */
548    function map(obj, cb, context) {
549        if (!(obj && cb)) {
550            return;
551        }
552        if (obj.map && obj.map === nativeMap) {
553            return obj.map(cb, context);
554        }
555        else {
556            var result = [];
557            for (var i = 0, len = obj.length; i < len; i++) {
558                result.push(cb.call(context, obj[i], i, obj));
559            }
560            return result;
561        }
562    }
563
564    /**
565     * @memberOf module:zrender/core/util
566     * @param {Array} obj
567     * @param {Function} cb
568     * @param {Object} [memo]
569     * @param {*} [context]
570     * @return {Array}
571     */
572    function reduce(obj, cb, memo, context) {
573        if (!(obj && cb)) {
574            return;
575        }
576        if (obj.reduce && obj.reduce === nativeReduce) {
577            return obj.reduce(cb, memo, context);
578        }
579        else {
580            for (var i = 0, len = obj.length; i < len; i++) {
581                memo = cb.call(context, memo, obj[i], i, obj);
582            }
583            return memo;
584        }
585    }
586
587    /**
588     * 鏁扮粍杩囨护
589     * @memberOf module:zrender/core/util
590     * @param {Array} obj
591     * @param {Function} cb
592     * @param {*} [context]
593     * @return {Array}
594     */
595    function filter(obj, cb, context) {
596        if (!(obj && cb)) {
597            return;
598        }
599        if (obj.filter && obj.filter === nativeFilter) {
600            return obj.filter(cb, context);
601        }
602        else {
603            var result = [];
604            for (var i = 0, len = obj.length; i < len; i++) {
605                if (cb.call(context, obj[i], i, obj)) {
606                    result.push(obj[i]);
607                }
608            }
609            return result;
610        }
611    }
612
613    /**
614     * 鏁扮粍椤规煡鎵�
615     * @memberOf module:zrender/core/util
616     * @param {Array} obj
617     * @param {Function} cb
618     * @param {*} [context]
619     * @return {*}
620     */
621    function find(obj, cb, context) {
622        if (!(obj && cb)) {
623            return;
624        }
625        for (var i = 0, len = obj.length; i < len; i++) {
626            if (cb.call(context, obj[i], i, obj)) {
627                return obj[i];
628            }
629        }
630    }
631
632    /**
633     * @memberOf module:zrender/core/util
634     * @param {Function} func
635     * @param {*} context
636     * @return {Function}
637     */
638    function bind(func, context) {
639        var args = nativeSlice.call(arguments, 2);
640        return function () {
641            return func.apply(context, args.concat(nativeSlice.call(arguments)));
642        };
643    }
644
645    /**
646     * @memberOf module:zrender/core/util
647     * @param {Function} func
648     * @return {Function}
649     */
650    function curry(func) {
651        var args = nativeSlice.call(arguments, 1);
652        return function () {
653            return func.apply(this, args.concat(nativeSlice.call(arguments)));
654        };
655    }
656
657    /**
658     * @memberOf module:zrender/core/util
659     * @param {*} value
660     * @return {boolean}
661     */
662    function isArray(value) {
663        return objToString.call(value) === '[object Array]';
664    }
665
666    /**
667     * @memberOf module:zrender/core/util
668     * @param {*} value
669     * @return {boolean}
670     */
671    function isFunction$1(value) {
672        return typeof value === 'function';
673    }
674
675    /**
676     * @memberOf module:zrender/core/util
677     * @param {*} value
678     * @return {boolean}
679     */
680    function isString(value) {
681        return objToString.call(value) === '[object String]';
682    }
683
684    /**
685     * @memberOf module:zrender/core/util
686     * @param {*} value
687     * @return {boolean}
688     */
689    function isObject$1(value) {
690        // Avoid a V8 JIT bug in Chrome 19-20.
691        // See https://code.google.com/p/v8/issues/detail?id=2291 for more details.
692        var type = typeof value;
693        return type === 'function' || (!!value && type == 'object');
694    }
695
696    /**
697     * @memberOf module:zrender/core/util
698     * @param {*} value
699     * @return {boolean}
700     */
701    function isBuiltInObject(value) {
702        return !!BUILTIN_OBJECT[objToString.call(value)];
703    }
704
705    /**
706     * @memberOf module:zrender/core/util
707     * @param {*} value
708     * @return {boolean}
709     */
710    function isTypedArray(value) {
711        return !!TYPED_ARRAY[objToString.call(value)];
712    }
713
714    /**
715     * @memberOf module:zrender/core/util
716     * @param {*} value
717     * @return {boolean}
718     */
719    function isDom(value) {
720        return typeof value === 'object'
721            && typeof value.nodeType === 'number'
722            && typeof value.ownerDocument === 'object';
723    }
724
725    /**
726     * Whether is exactly NaN. Notice isNaN('a') returns true.
727     * @param {*} value
728     * @return {boolean}
729     */
730    function eqNaN(value) {
731        return value !== value;
732    }
733
734    /**
735     * If value1 is not null, then return value1, otherwise judget rest of values.
736     * Low performance.
737     * @memberOf module:zrender/core/util
738     * @return {*} Final value
739     */
740    function retrieve(values) {
741        for (var i = 0, len = arguments.length; i < len; i++) {
742            if (arguments[i] != null) {
743                return arguments[i];
744            }
745        }
746    }
747
748    function retrieve2(value0, value1) {
749        return value0 != null
750            ? value0
751            : value1;
752    }
753
754    function retrieve3(value0, value1, value2) {
755        return value0 != null
756            ? value0
757            : value1 != null
758                ? value1
759                : value2;
760    }
761
762    /**
763     * @memberOf module:zrender/core/util
764     * @param {Array} arr
765     * @param {number} startIndex
766     * @param {number} endIndex
767     * @return {Array}
768     */
769    function slice() {
770        return Function.call.apply(nativeSlice, arguments);
771    }
772
773    /**
774     * Normalize css liked array configuration
775     * e.g.
776     *  3 => [3, 3, 3, 3]
777     *  [4, 2] => [4, 2, 4, 2]
778     *  [4, 3, 2] => [4, 3, 2, 3]
779     * @param {number|Array.<number>} val
780     * @return {Array.<number>}
781     */
782    function normalizeCssArray(val) {
783        if (typeof (val) === 'number') {
784            return [val, val, val, val];
785        }
786        var len = val.length;
787        if (len === 2) {
788            // vertical | horizontal
789            return [val[0], val[1], val[0], val[1]];
790        }
791        else if (len === 3) {
792            // top | horizontal | bottom
793            return [val[0], val[1], val[2], val[1]];
794        }
795        return val;
796    }
797
798    /**
799     * @memberOf module:zrender/core/util
800     * @param {boolean} condition
801     * @param {string} message
802     */
803    function assert$1(condition, message) {
804        if (!condition) {
805            throw new Error(message);
806        }
807    }
808
809    /**
810     * @memberOf module:zrender/core/util
811     * @param {string} str string to be trimed
812     * @return {string} trimed string
813     */
814    function trim(str) {
815        if (str == null) {
816            return null;
817        }
818        else if (typeof str.trim === 'function') {
819            return str.trim();
820        }
821        else {
822            return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
823        }
824    }
825
826    var primitiveKey = '__ec_primitive__';
827    /**
828     * Set an object as primitive to be ignored traversing children in clone or merge
829     */
830    function setAsPrimitive(obj) {
831        obj[primitiveKey] = true;
832    }
833
834    function isPrimitive(obj) {
835        return obj[primitiveKey];
836    }
837
838    /**
839     * @constructor
840     * @param {Object} obj Only apply `ownProperty`.
841     */
842    function HashMap(obj) {
843        var isArr = isArray(obj);
844        var thisMap = this;
845
846        (obj instanceof HashMap)
847            ? obj.each(visit)
848            : (obj && each$1(obj, visit));
849
850        function visit(value, key) {
851            isArr ? thisMap.set(value, key) : thisMap.set(key, value);
852        }
853    }
854
855// Add prefix to avoid conflict with Object.prototype.
856
857    HashMap.prototype = {
858        constructor: HashMap,
859        // Do not provide `has` method to avoid defining what is `has`.
860        // (We usually treat `null` and `undefined` as the same, different
861        // from ES6 Map).
862        get: function (key) {
863            return this.hasOwnProperty(key) ? this[key] : null;
864        },
865        set: function (key, value) {
866            // Comparing with invocation chaining, `return value` is more commonly
867            // used in this case: `var someVal = map.set('a', genVal());`
868            return (this[key] = value);
869        },
870        // Although util.each can be performed on this hashMap directly, user
871        // should not use the exposed keys, who are prefixed.
872        each: function (cb, context) {
873            context !== void 0 && (cb = bind(cb, context));
874            for (var key in this) {
875                this.hasOwnProperty(key) && cb(this[key], key);
876            }
877        },
878        // Do not use this method if performance sensitive.
879        removeKey: function (key) {
880            delete this[key];
881        }
882    };
883
884    function createHashMap(obj) {
885        return new HashMap(obj);
886    }
887
888    function concatArray(a, b) {
889        var newArray = new a.constructor(a.length + b.length);
890        for (var i = 0; i < a.length; i++) {
891            newArray[i] = a[i];
892        }
893        var offset = a.length;
894        for (i = 0; i < b.length; i++) {
895            newArray[i + offset] = b[i];
896        }
897        return newArray;
898    }
899
900
901    function noop() {}
902
903
904    var zrUtil = (Object.freeze || Object)({
905        $override: $override,
906        clone: clone,
907        merge: merge,
908        mergeAll: mergeAll,
909        extend: extend,
910        defaults: defaults,
911        createCanvas: createCanvas,
912        getContext: getContext,
913        indexOf: indexOf,
914        inherits: inherits,
915        mixin: mixin,
916        isArrayLike: isArrayLike,
917        each: each$1,
918        map: map,
919        reduce: reduce,
920        filter: filter,
921        find: find,
922        bind: bind,
923        curry: curry,
924        isArray: isArray,
925        isFunction: isFunction$1,
926        isString: isString,
927        isObject: isObject$1,
928        isBuiltInObject: isBuiltInObject,
929        isTypedArray: isTypedArray,
930        isDom: isDom,
931        eqNaN: eqNaN,
932        retrieve: retrieve,
933        retrieve2: retrieve2,
934        retrieve3: retrieve3,
935        slice: slice,
936        normalizeCssArray: normalizeCssArray,
937        assert: assert$1,
938        trim: trim,
939        setAsPrimitive: setAsPrimitive,
940        isPrimitive: isPrimitive,
941        createHashMap: createHashMap,
942        concatArray: concatArray,
943        noop: noop
944    });
945
946    var ArrayCtor = typeof Float32Array === 'undefined'
947        ? Array
948        : Float32Array;
949
950    /**
951     * 鍒涘缓涓€涓悜閲�
952     * @param {number} [x=0]
953     * @param {number} [y=0]
954     * @return {Vector2}
955     */
956    function create(x, y) {
957        var out = new ArrayCtor(2);
958        if (x == null) {
959            x = 0;
960        }
961        if (y == null) {
962            y = 0;
963        }
964        out[0] = x;
965        out[1] = y;
966        return out;
967    }
968
969    /**
970     * 澶嶅埗鍚戦噺鏁版嵁
971     * @param {Vector2} out
972     * @param {Vector2} v
973     * @return {Vector2}
974     */
975    function copy(out, v) {
976        out[0] = v[0];
977        out[1] = v[1];
978        return out;
979    }
980
981    /**
982     * 鍏嬮殕涓€涓悜閲�
983     * @param {Vector2} v
984     * @return {Vector2}
985     */
986    function clone$1(v) {
987        var out = new ArrayCtor(2);
988        out[0] = v[0];
989        out[1] = v[1];
990        return out;
991    }
992
993    /**
994     * 璁剧疆鍚戦噺鐨勪袱涓」
995     * @param {Vector2} out
996     * @param {number} a
997     * @param {number} b
998     * @return {Vector2} 缁撴灉
999     */
1000    function set(out, a, b) {
1001        out[0] = a;
1002        out[1] = b;
1003        return out;
1004    }
1005
1006    /**
1007     * 鍚戦噺鐩稿姞
1008     * @param {Vector2} out
1009     * @param {Vector2} v1
1010     * @param {Vector2} v2
1011     */
1012    function add(out, v1, v2) {
1013        out[0] = v1[0] + v2[0];
1014        out[1] = v1[1] + v2[1];
1015        return out;
1016    }
1017
1018    /**
1019     * 鍚戦噺缂╂斁鍚庣浉鍔�
1020     * @param {Vector2} out
1021     * @param {Vector2} v1
1022     * @param {Vector2} v2
1023     * @param {number} a
1024     */
1025    function scaleAndAdd(out, v1, v2, a) {
1026        out[0] = v1[0] + v2[0] * a;
1027        out[1] = v1[1] + v2[1] * a;
1028        return out;
1029    }
1030
1031    /**
1032     * 鍚戦噺鐩稿噺
1033     * @param {Vector2} out
1034     * @param {Vector2} v1
1035     * @param {Vector2} v2
1036     */
1037    function sub(out, v1, v2) {
1038        out[0] = v1[0] - v2[0];
1039        out[1] = v1[1] - v2[1];
1040        return out;
1041    }
1042
1043    /**
1044     * 鍚戦噺闀垮害
1045     * @param {Vector2} v
1046     * @return {number}
1047     */
1048    function len(v) {
1049        return Math.sqrt(lenSquare(v));
vendor: 4,461 bytes, lines 1050-1242
1050    }
1051    var length = len; // jshint ignore:line
1052
1053    /**
1054     * 鍚戦噺闀垮害骞虫柟
1055     * @param {Vector2} v
1056     * @return {number}
1057     */
1058    function lenSquare(v) {
1059        return v[0] * v[0] + v[1] * v[1];
1060    }
1061    var lengthSquare = lenSquare;
1062
1063    /**
1064     * 鍚戦噺涔樻硶
1065     * @param {Vector2} out
1066     * @param {Vector2} v1
1067     * @param {Vector2} v2
1068     */
1069    function mul(out, v1, v2) {
1070        out[0] = v1[0] * v2[0];
1071        out[1] = v1[1] * v2[1];
1072        return out;
1073    }
1074
1075    /**
1076     * 鍚戦噺闄ゆ硶
1077     * @param {Vector2} out
1078     * @param {Vector2} v1
1079     * @param {Vector2} v2
1080     */
1081    function div(out, v1, v2) {
1082        out[0] = v1[0] / v2[0];
1083        out[1] = v1[1] / v2[1];
1084        return out;
1085    }
1086
1087    /**
1088     * 鍚戦噺鐐逛箻
1089     * @param {Vector2} v1
1090     * @param {Vector2} v2
1091     * @return {number}
1092     */
1093    function dot(v1, v2) {
1094        return v1[0] * v2[0] + v1[1] * v2[1];
1095    }
1096
1097    /**
1098     * 鍚戦噺缂╂斁
1099     * @param {Vector2} out
1100     * @param {Vector2} v
1101     * @param {number} s
1102     */
1103    function scale(out, v, s) {
1104        out[0] = v[0] * s;
1105        out[1] = v[1] * s;
1106        return out;
1107    }
1108
1109    /**
1110     * 鍚戦噺褰掍竴鍖�
1111     * @param {Vector2} out
1112     * @param {Vector2} v
1113     */
1114    function normalize(out, v) {
1115        var d = len(v);
1116        if (d === 0) {
1117            out[0] = 0;
1118            out[1] = 0;
1119        }
1120        else {
1121            out[0] = v[0] / d;
1122            out[1] = v[1] / d;
1123        }
1124        return out;
1125    }
1126
1127    /**
1128     * 璁$畻鍚戦噺闂磋窛绂�
1129     * @param {Vector2} v1
1130     * @param {Vector2} v2
1131     * @return {number}
1132     */
1133    function distance(v1, v2) {
1134        return Math.sqrt(
1135            (v1[0] - v2[0]) * (v1[0] - v2[0])
1136            + (v1[1] - v2[1]) * (v1[1] - v2[1])
1137        );
1138    }
1139    var dist = distance;
1140
1141    /**
1142     * 鍚戦噺璺濈骞虫柟
1143     * @param {Vector2} v1
1144     * @param {Vector2} v2
1145     * @return {number}
1146     */
1147    function distanceSquare(v1, v2) {
1148        return (v1[0] - v2[0]) * (v1[0] - v2[0])
1149            + (v1[1] - v2[1]) * (v1[1] - v2[1]);
1150    }
1151    var distSquare = distanceSquare;
1152
1153    /**
1154     * 姹傝礋鍚戦噺
1155     * @param {Vector2} out
1156     * @param {Vector2} v
1157     */
1158    function negate(out, v) {
1159        out[0] = -v[0];
1160        out[1] = -v[1];
1161        return out;
1162    }
1163
1164    /**
1165     * 鎻掑€间袱涓偣
1166     * @param {Vector2} out
1167     * @param {Vector2} v1
1168     * @param {Vector2} v2
1169     * @param {number} t
1170     */
1171    function lerp(out, v1, v2, t) {
1172        out[0] = v1[0] + t * (v2[0] - v1[0]);
1173        out[1] = v1[1] + t * (v2[1] - v1[1]);
1174        return out;
1175    }
1176
1177    /**
1178     * 鐭╅樀宸︿箻鍚戦噺
1179     * @param {Vector2} out
1180     * @param {Vector2} v
1181     * @param {Vector2} m
1182     */
1183    function applyTransform(out, v, m) {
1184        var x = v[0];
1185        var y = v[1];
1186        out[0] = m[0] * x + m[2] * y + m[4];
1187        out[1] = m[1] * x + m[3] * y + m[5];
1188        return out;
1189    }
1190
1191    /**
1192     * 姹備袱涓悜閲忔渶灏忓€�
1193     * @param  {Vector2} out
1194     * @param  {Vector2} v1
1195     * @param  {Vector2} v2
1196     */
1197    function min(out, v1, v2) {
1198        out[0] = Math.min(v1[0], v2[0]);
1199        out[1] = Math.min(v1[1], v2[1]);
1200        return out;
1201    }
1202
1203    /**
1204     * 姹備袱涓悜閲忔渶澶у€�
1205     * @param  {Vector2} out
1206     * @param  {Vector2} v1
1207     * @param  {Vector2} v2
1208     */
1209    function max(out, v1, v2) {
1210        out[0] = Math.max(v1[0], v2[0]);
1211        out[1] = Math.max(v1[1], v2[1]);
1212        return out;
1213    }
1214
1215
1216    var vector = (Object.freeze || Object)({
1217        create: create,
1218        copy: copy,
1219        clone: clone$1,
1220        set: set,
1221        add: add,
1222        scaleAndAdd: scaleAndAdd,
1223        sub: sub,
1224        len: len,
1225        length: length,
1226        lenSquare: lenSquare,
1227        lengthSquare: lengthSquare,
1228        mul: mul,
1229        div: div,
1230        dot: dot,
1231        scale: scale,
1232        normalize: normalize,
1233        distance: distance,
1234        dist: dist,
1235        distanceSquare: distanceSquare,
1236        distSquare: distSquare,
1237        negate: negate,
1238        lerp: lerp,
1239        applyTransform: applyTransform,
1240        min: min,
1241        max: max
1242    });
1243
1244// TODO Draggable for group
1245// FIXME Draggable on element which has parent rotation or scale
1246    function Draggable() {
1247
1248        this.on('mousedown', this._dragStart, this);
1249        this.on('mousemove', this._drag, this);
1250        this.on('mouseup', this._dragEnd, this);
1251        this.on('globalout', this._dragEnd, this);
1252        // this._dropTarget = null;
1253        // this._draggingTarget = null;
1254
1255        // this._x = 0;
1256        // this._y = 0;
1257    }
1258
1259    Draggable.prototype = {
1260
1261        constructor: Draggable,
1262
1263        _dragStart: function (e) {
1264            var draggingTarget = e.target;
1265            if (draggingTarget && draggingTarget.draggable) {
1266                this._draggingTarget = draggingTarget;
1267                draggingTarget.dragging = true;
1268                this._x = e.offsetX;
1269                this._y = e.offsetY;
1270
1271                this.dispatchToElement(param(draggingTarget, e), 'dragstart', e.event);
1272            }
1273        },
1274
1275        _drag: function (e) {
1276            var draggingTarget = this._draggingTarget;
1277            if (draggingTarget) {
1278
1279                var x = e.offsetX;
1280                var y = e.offsetY;
1281
1282                var dx = x - this._x;
1283                var dy = y - this._y;
1284                this._x = x;
1285                this._y = y;
1286
1287                draggingTarget.drift(dx, dy, e);
1288                this.dispatchToElement(param(draggingTarget, e), 'drag', e.event);
1289
1290                var dropTarget = this.findHover(x, y, draggingTarget).target;
1291                var lastDropTarget = this._dropTarget;
1292                this._dropTarget = dropTarget;
1293
1294                if (draggingTarget !== dropTarget) {
1295                    if (lastDropTarget && dropTarget !== lastDropTarget) {
1296                        this.dispatchToElement(param(lastDropTarget, e), 'dragleave', e.event);
1297                    }
1298                    if (dropTarget && dropTarget !== lastDropTarget) {
1299                        this.dispatchToElement(param(dropTarget, e), 'dragenter', e.event);
1300                    }
1301                }
1302            }
1303        },
1304
1305        _dragEnd: function (e) {
1306            var draggingTarget = this._draggingTarget;
1307
1308            if (draggingTarget) {
1309                draggingTarget.dragging = false;
1310            }
1311
1312            this.dispatchToElement(param(draggingTarget, e), 'dragend', e.event);
1313
1314            if (this._dropTarget) {
1315                this.dispatchToElement(param(this._dropTarget, e), 'drop', e.event);
1316            }
1317
1318            this._draggingTarget = null;
1319            this._dropTarget = null;
1320        }
1321
1322    };
1323
1324    function param(target, e) {
1325        return {target: target, topTarget: e && e.topTarget};
1326    }
1327
1328    /**
1329     * 浜嬩欢鎵╁睍
1330     * @module zrender/mixin/Eventful
1331     * @author Kener (@Kener-鏋楀嘲, [email protected])
1332     *         pissang (https://www.github.com/pissang)
1333     */
1334
1335    var arrySlice = Array.prototype.slice;
1336
1337    /**
1338     * 浜嬩欢鍒嗗彂鍣�
1339     * @alias module:zrender/mixin/Eventful
1340     * @constructor
1341     */
1342    var Eventful = function () {
1343        this._$handlers = {};
1344    };
1345
1346    Eventful.prototype = {
1347
1348        constructor: Eventful,
1349
1350        /**
1351         * 鍗曟瑙﹀彂缁戝畾锛宼rigger鍚庨攢姣�
1352         *
1353         * @param {string} event 浜嬩欢鍚�
1354         * @param {Function} handler 鍝嶅簲鍑芥暟
1355         * @param {Object} context
1356         */
1357        one: function (event, handler, context) {
1358            var _h = this._$handlers;
1359
1360            if (!handler || !event) {
1361                return this;
1362            }
1363
1364            if (!_h[event]) {
1365                _h[event] = [];
1366            }
1367
1368            for (var i = 0; i < _h[event].length; i++) {
1369                if (_h[event][i].h === handler) {
1370                    return this;
1371                }
1372            }
1373
1374            _h[event].push({
1375                h: handler,
1376                one: true,
1377                ctx: context || this
1378            });
1379
1380            return this;
1381        },
1382
1383        /**
1384         * 缁戝畾浜嬩欢
1385         * @param {string} event 浜嬩欢鍚�
1386         * @param {Function} handler 浜嬩欢澶勭悊鍑芥暟
1387         * @param {Object} [context]
1388         */
1389        on: function (event, handler, context) {
1390            var _h = this._$handlers;
1391
1392            if (!handler || !event) {
1393                return this;
1394            }
1395
1396            if (!_h[event]) {
1397                _h[event] = [];
1398            }
1399
1400            for (var i = 0; i < _h[event].length; i++) {
1401                if (_h[event][i].h === handler) {
1402                    return this;
1403                }
1404            }
1405
1406            _h[event].push({
1407                h: handler,
1408                one: false,
1409                ctx: context || this
1410            });
1411
1412            return this;
1413        },
1414
1415        /**
1416         * 鏄惁缁戝畾浜嗕簨浠�
1417         * @param  {string}  event
1418         * @return {boolean}
1419         */
1420        isSilent: function (event) {
1421            var _h = this._$handlers;
1422            return _h[event] && _h[event].length;
1423        },
1424
1425        /**
1426         * 瑙g粦浜嬩欢
1427         * @param {string} event 浜嬩欢鍚�
1428         * @param {Function} [handler] 浜嬩欢澶勭悊鍑芥暟
1429         */
1430        off: function (event, handler) {
1431            var _h = this._$handlers;
1432
1433            if (!event) {
1434                this._$handlers = {};
1435                return this;
1436            }
1437
1438            if (handler) {
1439                if (_h[event]) {
1440                    var newList = [];
1441                    for (var i = 0, l = _h[event].length; i < l; i++) {
1442                        if (_h[event][i]['h'] != handler) {
1443                            newList.push(_h[event][i]);
1444                        }
1445                    }
1446                    _h[event] = newList;
1447                }
1448
1449                if (_h[event] && _h[event].length === 0) {
1450                    delete _h[event];
1451                }
1452            }
1453            else {
1454                delete _h[event];
1455            }
1456
1457            return this;
1458        },
1459
1460        /**
1461         * 浜嬩欢鍒嗗彂
1462         *
1463         * @param {string} type 浜嬩欢绫诲瀷
1464         */
1465        trigger: function (type) {
1466            if (this._$handlers[type]) {
1467                var args = arguments;
1468                var argLen = args.length;
1469
1470                if (argLen > 3) {
1471                    args = arrySlice.call(args, 1);
1472                }
1473
1474                var _h = this._$handlers[type];
1475                var len = _h.length;
1476                for (var i = 0; i < len;) {
1477                    // Optimize advise from backbone
1478                    switch (argLen) {
1479                        case 1:
1480                            _h[i]['h'].call(_h[i]['ctx']);
1481                            break;
1482                        case 2:
1483                            _h[i]['h'].call(_h[i]['ctx'], args[1]);
1484                            break;
1485                        case 3:
1486                            _h[i]['h'].call(_h[i]['ctx'], args[1], args[2]);
1487                            break;
1488                        default:
1489                            // have more than 2 given arguments
1490                            _h[i]['h'].apply(_h[i]['ctx'], args);
1491                            break;
1492                    }
1493
1494                    if (_h[i]['one']) {
1495                        _h.splice(i, 1);
1496                        len--;
1497                    }
1498                    else {
1499                        i++;
1500                    }
1501                }
1502            }
1503
1504            return this;
1505        },
1506
1507        /**
1508         * 甯︽湁context鐨勪簨浠跺垎鍙�, 鏈€鍚庝竴涓î
1508„弬鏁版槸浜嬩欢鍥炶皟鐨刢ontext
1509         * @param {string} type 浜嬩欢绫诲瀷
1510         */
1511        triggerWithContext: function (type) {
1512            if (this._$handlers[type]) {
1513                var args = arguments;
1514                var argLen = args.length;
1515
1516                if (argLen > 4) {
1517                    args = arrySlice.call(args, 1, args.length - 1);
1518                }
1519                var ctx = args[args.length - 1];
1520
1521                var _h = this._$handlers[type];
1522                var len = _h.length;
1523                for (var i = 0; i < len;) {
1524                    // Optimize advise from backbone
1525                    switch (argLen) {
1526                        case 1:
1527                            _h[i]['h'].call(ctx);
1528                            break;
1529                        case 2:
1530                            _h[i]['h'].call(ctx, args[1]);
1531                            break;
1532                        case 3:
1533                            _h[i]['h'].call(ctx, args[1], args[2]);
1534                            break;
1535                        default:
1536                            // have more than 2 given arguments
1537                            _h[i]['h'].apply(ctx, args);
1538                            break;
1539                    }
1540
1541                    if (_h[i]['one']) {
1542                        _h.splice(i, 1);
1543                        len--;
1544                    }
1545                    else {
1546                        i++;
1547                    }
1548                }
1549            }
1550
1551            return this;
1552        }
1553    };
1554
1555    var SILENT = 'silent';
1556
1557    function makeEventPacket(eveType, targetInfo, event) {
1558        return {
1559            type: eveType,
1560            event: event,
1561            // target can only be an element that is not silent.
1562            target: targetInfo.target,
1563            // topTarget can be a silent element.
1564            topTarget: targetInfo.topTarget,
1565            cancelBubble: false,
1566            offsetX: event.zrX,
1567            offsetY: event.zrY,
1568            gestureEvent: event.gestureEvent,
1569            pinchX: event.pinchX,
1570            pinchY: event.pinchY,
1571            pinchScale: event.pinchScale,
1572            wheelDelta: event.zrDelta,
1573            zrByTouch: event.zrByTouch,
1574            which: event.which
1575        };
1576    }
1577
1578    function EmptyProxy () {}
1579    EmptyProxy.prototype.dispose = function () {};
1580
1581    var handlerNames = [
1582        'click', 'dblclick', 'mousewheel', 'mouseout',
1583        'mouseup', 'mousedown', 'mousemove', 'contextmenu'
1584    ];
1585    /**
1586     * @alias module:zrender/Handler
1587     * @constructor
1588     * @extends module:zrender/mixin/Eventful
1589     * @param {module:zrender/Storage} storage Storage instance.
1590     * @param {module:zrender/Painter} painter Painter instance.
1591     * @param {module:zrender/dom/HandlerProxy} proxy HandlerProxy instance.
1592     * @param {HTMLElement} painterRoot painter.root (not painter.getViewportRoot()).
1593     */
1594    var Handler = function(storage, painter, proxy, painterRoot) {
1595        Eventful.call(this);
1596
1597        this.storage = storage;
1598
1599        this.painter = painter;
1600
1601        this.painterRoot = painterRoot;
1602
1603        proxy = proxy || new EmptyProxy();
1604
1605        /**
1606         * Proxy of event. can be Dom, WebGLSurface, etc.
1607         */
1608        this.proxy = null;
1609
1610        /**
1611         * {target, topTarget, x, y}
1612         * @private
1613         * @type {Object}
1614         */
1615        this._hovered = {};
1616
1617        /**
1618         * @private
1619         * @type {Date}
1620         */
1621        this._lastTouchMoment;
1622
1623        /**
1624         * @private
1625         * @type {number}
1626         */
1627        this._lastX;
1628
1629        /**
1630         * @private
1631         * @type {number}
1632         */
1633        this._lastY;
1634
1635
1636        Draggable.call(this);
1637
1638        this.setHandlerProxy(proxy);
1639    };
1640
1641    Handler.prototype = {
1642
1643        constructor: Handler,
1644
1645        setHandlerProxy: function (proxy) {
1646            if (this.proxy) {
1647                this.proxy.dispose();
1648            }
1649
1650            if (proxy) {
1651                each$1(handlerNames, function (name) {
1652                    proxy.on && proxy.on(name, this[name], this);
1653                }, this);
1654                // Attach handler
1655                proxy.handler = this;
1656            }
1657            this.proxy = proxy;
1658        },
1659
1660        mousemove: function (event) {
1661            var x = event.zrX;
1662            var y = event.zrY;
1663
1664            var lastHovered = this._hovered;
1665            var lastHoveredTarget = lastHovered.target;
1666
1667            // If lastHoveredTarget is removed from zr (detected by '__zr') by some API call
1668            // (like 'setOption' or 'dispatchAction') in event handlers, we should find
1669            // lastHovered again here. Otherwise 'mouseout' can not be triggered normally.
1670            // See #6198.
1671            if (lastHoveredTarget && !lastHoveredTarget.__zr) {
1672                lastHovered = this.findHover(lastHovered.x, lastHovered.y);
1673                lastHoveredTarget = lastHovered.target;
1674            }
1675
1676            var hovered = this._hovered = this.findHover(x, y);
1677            var hoveredTarget = hovered.target;
1678
1679            var proxy = this.proxy;
1680            proxy.setCursor && proxy.setCursor(hoveredTarget ? hoveredTarget.cursor : 'default');
1681
1682            // Mouse out on previous hovered element
1683            if (lastHoveredTarget && hoveredTarget !== lastHoveredTarget) {
1684                this.dispatchToElement(lastHovered, 'mouseout', event);
1685            }
1686
1687            // Mouse moving on one element
1688            this.dispatchToElement(hovered, 'mousemove', event);
1689
1690            // Mouse over on a new element
1691            if (hoveredTarget && hoveredTarget !== lastHoveredTarget) {
1692                this.dispatchToElement(hovered, 'mouseover', event);
1693            }
1694        },
1695
1696        mouseout: function (event) {
1697            this.dispatchToElement(this._hovered, 'mouseout', event);
1698
1699            // There might be some doms created by upper layer application
1700            // at the same level of painter.getViewportRoot() (e.g., tooltip
1701            // dom created by echarts), where 'globalout' event should not
1702            // be triggered when mouse enters these doms. (But 'mouseout'
1703            // should be triggered at the original hovered element as usual).
1704            var element = event.toElement || event.relatedTarget;
1705            var innerDom;
1706            do {
1707                element = element && element.parentNode;
1708            }
1709            while (element && element.nodeType != 9 && !(
1710                innerDom = element === this.painterRoot
1711            ));
1712
1713            !innerDom && this.trigger('globalout', {event: event});
1714        },
1715
1716        /**
1717         * Resize
1718         */
1719        resize: function (event) {
1720            this._hovered = {};
1721        },
1722
1723        /**
1724         * Dispatch event
1725         * @param {string} eventName
1726         * @param {event=} eventArgs
1727         */
1728        dispatch: function (eventName, eventArgs) {
1729            var handler = this[eventName];
1730            handler && handler.call(this, eventArgs);
1731        },
1732
1733        /**
1734         * Dispose
1735         */
1736        dispose: function () {
1737
1738            this.proxy.dispose();
1739
1740            this.storage =
1741                this.proxy =
1742                    this.painter = null;
1743        },
1744
1745        /**
1746         * 璁剧疆榛樿鐨刢ursor style
1747         * @param {string} [cursorStyle='default'] 渚嬪 crosshair
1748         */
1749        setCursorStyle: function (cursorStyle) {
1750            var proxy = this.proxy;
1751            proxy.setCursor && proxy.setCursor(cursorStyle);
1752        },
1753
1754        /**
1755         * 浜嬩欢鍒嗗彂浠g悊
1756         *
1757         * @private
1758         * @param {Object} targetInfo {target, topTarget} 鐩爣鍥惧舰鍏冪礌
1759         * @param {string} eventName 浜嬩欢鍚嶇О
1760         * @param {Object} event 浜嬩欢瀵硅薄
1761         */
1762        dispatchToElement: function (targetInfo, eventName, event) {
1763            targetInfo = targetInfo || {};
1764            var el = targetInfo.target;
1765            if (el && el.silent) {
1766                return;
1767            }
1768            var eventHandler = 'on' + eventName;
1769            var eventPacket = makeEventPacket(eventName, targetInfo, event);
1770
1771            while (el) {
1772                el[eventHandler]
1773                && (eventPacket.cancelBubble = el[eventHandler].call(el, eventPacket));
1774
1775                el.trigger(eventName, eventPacket);
1776
1777                el = el.parent;
1778
1779                if (eventPacket.cancelBubble) {
1780                    break;
1781                }
1782            }
1783
1784            if (!eventPacket.cancelBubble) {
1785                // 鍐掓场鍒伴《绾� zrender 瀵硅薄
1786                this.trigger(eventName, eventPacket);
1787                // 鍒嗗彂浜嬩欢鍒扮敤鎴疯嚜瀹氫箟灞�
1788                // 鐢ㄦ埛鏈夊彲鑳藉湪鍏ㄥ眬 click 浜嬩欢涓� dispose锛屾墍浠ラ渶瑕佸垽鏂笅 painter 鏄惁瀛樺湪
1789                this.painter && this.painter.eachOtherLayer(function (layer) {
1790                    if (typeof(layer[eventHandler]) == 'function') {
1791                        layer[eventHandler].call(layer, eventPacket);
1792                    }
1793                    if (layer.trigger) {
1794                        layer.trigger(eventName, eventPacket);
1795                    }
1796                });
1797            }
1798        },
1799
1800        /**
1801         * @private
1802         * @param {number} x
1803         * @param {number} y
1804         * @param {module:zrender/graphic/Displayable} exclude
1805         * @return {model:zrender/Element}
1806         * @method
1807         */
1808        findHover: function(x, y, exclude) {
1809            var list = this.storage.getDisplayList();
1810            var out = {x: x, y: y};
1811
1812            for (var i = list.length - 1; i >= 0 ; i--) {
1813                var hoverCheckResult;
1814                if (list[i] !== exclude
1815                    // getDisplayList may include ignored item in VML mode
1816                    && !list[i].ignore
1817                    && (hoverCheckResult = isHover(list[i], x, y))
1818                ) {
1819                    !out.topTarget && (out.topTarget = list[i]);
1820                    if (hoverCheckResult !== SILENT) {
1821                        out.target = list[i];
1822                        break;
1823                    }
1824                }
1825            }
1826
1827            return out;
1828        }
1829    };
1830
1831// Common handlers
1832    each$1(['click', 'mousedown', 'mouseup', 'mousewheel', 'dblclick', 'contextmenu'], function (name) {
1833        Handler.prototype[name] = function (event) {
1834            // Find hover again to avoid click event is dispatched manually. Or click is triggered without mouseover
1835            var hovered = this.findHover(event.zrX, event.zrY);
1836            var hoveredTarget = hovered.target;
1837
1838            if (name === 'mousedown') {
1839                this._downEl = hoveredTarget;
1840                this._downPoint = [event.zrX, event.zrY];
1841                // In case click triggered before mouseup
1842                this._upEl = hoveredTarget;
1843            }
1844            else if (name === 'mouseup') {
1845                this._upEl = hoveredTarget;
1846            }
1847            else if (name === 'click') {
1848                if (this._downEl !== this._upEl
1849                    // Original click event is triggered on the whole canvas element,
1850                    // including the case that `mousedown` - `mousemove` - `mou
1850seup`,
1851                    // which should be filtered, otherwise it will bring trouble to
1852                    // pan and zoom.
1853                    || !this._downPoint
1854                    // Arbitrary value
1855                    || dist(this._downPoint, [event.zrX, event.zrY]) > 4
1856                ) {
1857                    return;
1858                }
1859                this._downPoint = null;
1860            }
1861
1862            this.dispatchToElement(hovered, name, event);
1863        };
1864    });
1865
1866    function isHover(displayable, x, y) {
1867        if (displayable[displayable.rectHover ? 'rectContain' : 'contain'](x, y)) {
1868            var el = displayable;
1869            var isSilent;
1870            while (el) {
1871                // If clipped by ancestor.
1872                // FIXME: If clipPath has neither stroke nor fill,
1873                // el.clipPath.contain(x, y) will always return false.
1874                if (el.clipPath && !el.clipPath.contain(x, y))  {
1875                    return false;
1876                }
1877                if (el.silent) {
1878                    isSilent = true;
1879                }
1880                el = el.parent;
1881            }
1882            return isSilent ? SILENT : true;
1883        }
1884
1885        return false;
1886    }
1887
1888    mixin(Handler, Eventful);
1889    mixin(Handler, Draggable);
1890
1891    /**
1892     * 3x2鐭╅樀鎿嶄綔绫�
1893     * @exports zrender/tool/matrix
1894     */
1895
1896    var ArrayCtor$1 = typeof Float32Array === 'undefined'
1897        ? Array
1898        : Float32Array;
1899
1900    /**
1901     * Create a identity matrix.
1902     * @return {Float32Array|Array.<number>}
1903     */
1904    function create$1() {
1905        var out = new ArrayCtor$1(6);
1906        identity(out);
1907
1908        return out;
1909    }
1910
1911    /**
1912     * 璁剧疆鐭╅樀涓哄崟浣嶇煩闃�
1913     * @param {Float32Array|Array.<number>} out
1914     */
1915    function identity(out) {
1916        out[0] = 1;
1917        out[1] = 0;
1918        out[2] = 0;
1919        out[3] = 1;
1920        out[4] = 0;
1921        out[5] = 0;
1922        return out;
1923    }
1924
1925    /**
1926     * 澶嶅埗鐭╅樀
1927     * @param {Float32Array|Array.<number>} out
1928     * @param {Float32Array|Array.<number>} m
1929     */
1930    function copy$1(out, m) {
1931        out[0] = m[0];
1932        out[1] = m[1];
1933        out[2] = m[2];
1934        out[3] = m[3];
1935        out[4] = m[4];
1936        out[5] = m[5];
1937        return out;
1938    }
1939
1940    /**
1941     * 鐭╅樀鐩镐箻
1942     * @param {Float32Array|Array.<number>} out
1943     * @param {Float32Array|Array.<number>} m1
1944     * @param {Float32Array|Array.<number>} m2
1945     */
1946    function mul$1(out, m1, m2) {
1947        // Consider matrix.mul(m, m2, m);
1948        // where out is the same as m2.
1949        // So use temp variable to escape error.
1950        var out0 = m1[0] * m2[0] + m1[2] * m2[1];
1951        var out1 = m1[1] * m2[0] + m1[3] * m2[1];
1952        var out2 = m1[0] * m2[2] + m1[2] * m2[3];
1953        var out3 = m1[1] * m2[2] + m1[3] * m2[3];
1954        var out4 = m1[0] * m2[4] + m1[2] * m2[5] + m1[4];
1955        var out5 = m1[1] * m2[4] + m1[3] * m2[5] + m1[5];
1956        out[0] = out0;
1957        out[1] = out1;
1958        out[2] = out2;
1959        out[3] = out3;
1960        out[4] = out4;
1961        out[5] = out5;
1962        return out;
1963    }
1964
1965    /**
1966     * 骞崇Щ鍙樻崲
1967     * @param {Float32Array|Array.<number>} out
1968     * @param {Float32Array|Array.<number>} a
1969     * @param {Float32Array|Array.<number>} v
1970     */
1971    function translate(out, a, v) {
1972        out[0] = a[0];
1973        out[1] = a[1];
1974        out[2] = a[2];
1975        out[3] = a[3];
1976        out[4] = a[4] + v[0];
1977        out[5] = a[5] + v[1];
1978        return out;
1979    }
1980
1981    /**
1982     * 鏃嬭浆鍙樻崲
1983     * @param {Float32Array|Array.<number>} out
1984     * @param {Float32Array|Array.<number>} a
1985     * @param {number} rad
1986     */
1987    function rotate(out, a, rad) {
1988        var aa = a[0];
1989        var ac = a[2];
1990        var atx = a[4];
1991        var ab = a[1];
1992        var ad = a[3];
1993        var aty = a[5];
1994        var st = Math.sin(rad);
1995        var ct = Math.cos(rad);
1996
1997        out[0] = aa * ct + ab * st;
1998        out[1] = -aa * st + ab * ct;
1999        out[2] = ac * ct + ad * st;
2000        out[3] = -ac * st + ct * ad;
2001        out[4] = ct * atx + st * aty;
2002        out[5] = ct * aty - st * atx;
2003        return out;
2004    }
2005
2006    /**
2007     * 缂╂斁鍙樻崲
2008     * @param {Float32Array|Array.<number>} out
2009     * @param {Float32Array|Array.<number>} a
2010     * @param {Float32Array|Array.<number>} v
2011     */
2012    function scale$1(out, a, v) {
2013        var vx = v[0];
2014        var vy = v[1];
2015        out[0] = a[0] * vx;
2016        out[1] = a[1] * vy;
2017        out[2] = a[2] * vx;
2018        out[3] = a[3] * vy;
2019        out[4] = a[4] * vx;
2020        out[5] = a[5] * vy;
2021        return out;
2022    }
2023
2024    /**
2025     * 姹傞€嗙煩闃�
2026     * @param {Float32Array|Array.<number>} out
2027     * @param {Float32Array|Array.<number>} a
2028     */
2029    function invert(out, a) {
2030
2031        var aa = a[0];
2032        var ac = a[2];
2033        var atx = a[4];
2034        var ab = a[1];
2035        var ad = a[3];
2036        var aty = a[5];
2037
2038        var det = aa * ad - ab * ac;
2039        if (!det) {
2040            return null;
2041        }
2042        det = 1.0 / det;
2043
2044        out[0] = ad * det;
2045        out[1] = -ab * det;
2046        out[2] = -ac * det;
2047        out[3] = aa * det;
2048        out[4] = (ac * aty - ad * atx) * det;
2049        out[5] = (ab * atx - aa * aty) * det;
2050        return out;
2051    }
2052
2053    /**
2054     * Clone a new matrix.
2055     * @param {Float32Array|Array.<number>} a
2056     */
2057    function clone$2(a) {
2058        var b = create$1();
2059        copy$1(b, a);
2060        return b;
2061    }
2062
2063    var matrix = (Object.freeze || Object)({
2064        create: create$1,
2065        identity: identity,
2066        copy: copy$1,
2067        mul: mul$1,
2068        translate: translate,
2069        rotate: rotate,
2070        scale: scale$1,
2071        invert: invert,
2072        clone: clone$2
2073    });
2074
2075    /**
2076     * 鎻愪緵鍙樻崲鎵╁睍
2077     * @module zrender/mixin/Transformable
2078     * @author pissang (https://www.github.com/pissang)
2079     */
2080
2081    var mIdentity = identity;
2082
2083    var EPSILON = 5e-5;
2084
2085    function isNotAroundZero(val) {
2086        return val > EPSILON || val < -EPSILON;
2087    }
2088
2089    /**
2090     * @alias module:zrender/mixin/Transformable
2091     * @constructor
2092     */
2093    var Transformable = function (opts) {
2094        opts = opts || {};
2095        // If there are no given position, rotation, scale
2096        if (!opts.position) {
2097            /**
2098             * 骞崇Щ
2099             * @type {Array.<number>}
2100             * @default [0, 0]
2101             */
2102            this.position = [0, 0];
2103        }
2104        if (opts.rotation == null) {
2105            /**
2106             * 鏃嬭浆
2107             * @type {Array.<number>}
2108             * @default 0
2109             */
2110            this.rotation = 0;
2111        }
2112        if (!opts.scale) {
2113            /**
2114             * 缂╂斁
2115             * @type {Array.<number>}
2116             * @default [1, 1]
2117             */
2118            this.scale = [1, 1];
2119        }
2120        /**
2121         * 鏃嬭浆鍜岀缉鏀剧殑鍘熺偣
2122         * @type {Array.<number>}
2123         * @default null
2124         */
2125        this.origin = this.origin || null;
2126    };
2127
2128    var transformableProto = Transformable.prototype;
2129    transformableProto.transform = null;
2130
2131    /**
2132     * 鍒ゆ柇鏄惁闇€瑕佹湁鍧愭爣鍙樻崲
2133     * 濡傛灉鏈夊潗鏍囧彉鎹�, 鍒欎粠position, rotation, scale浠ュ強鐖惰妭鐐圭殑transform璁$畻鍑鸿嚜韬殑transform鐭╅樀
2134     */
2135    transformableProto.needLocalTransform = function () {
2136        return isNotAroundZero(this.rotation)
2137            || isNotAroundZero(this.position[0])
2138            || isNotAroundZero(this.position[1])
2139            || isNotAroundZero(this.scale[0] - 1)
2140            || isNotAroundZero(this.scale[1] - 1);
2141    };
2142
2143    transformableProto.updateTransform = function () {
2144        var parent = this.parent;
2145        var parentHasTransform = parent && parent.transform;
2146        var needLocalTransform = this.needLocalTransform();
2147
2148        var m = this.transform;
2149        if (!(needLocalTransform || parentHasTransform)) {
2150            m && mIdentity(m);
2151            return;
2152        }
2153
2154        m = m || create$1();
2155
2156        if (needLocalTransform) {
2157            this.getLocalTransform(m);
2158        }
2159        else {
2160            mIdentity(m);
2161        }
2162
2163        // 搴旂敤鐖惰妭鐐瑰彉鎹�
2164        if (parentHasTransform) {
2165            if (needLocalTransform) {
2166                mul$1(m, parent.transform, m);
2167            }
2168            else {
2169                copy$1(m, parent.transform);
2170            }
2171        }
2172        // 淇濆瓨杩欎釜鍙樻崲鐭╅樀
2173        this.transform = m;
2174
2175        this.invTransform = this.invTransform || create$1();
2176        invert(this.invTransform, m);
2177    };
2178
2179    transformableProto.getLocalTransform = function (m) {
2180        return Transformable.getLocalTransform(this, m);
2181    };
2182
2183    /**
2184     * 灏嗚嚜宸辩殑transform搴旂敤鍒癱ontext涓�
2185     * @param {CanvasRenderingContext2D} ctx
2186     */
2187    transformableProto.setTransform = function (ctx) {
2188        var m = this.transform;
2189        var dpr = ctx.dpr || 1;
2190        if (m) {
2191            ctx.setTransform(dpr * m[0], dpr * m[1], dpr * m[2], dpr * m[3], dpr * m[4], dpr * m[5]);
2192        }
2193        else {
2194            ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
2195        }
2196    };
2197
2198    transformableProto.restoreTransform = function (ctx) {
2199        var dpr = ctx.dpr || 1;
2200        ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
2201    };
2202
2203    var tmpTransform = [];
2204
2205    /**
2206     * 鍒嗚В`transform`鐭╅樀鍒癭position`, `rotation`, `scale`
2207     */
2208    transformableProto.decomposeTransform = function () {
2209        if (!this.transform) {
2210            return;
2211        }
2212        var parent = this.parent;
2213        var m = this.transform;
2214        if (parent && parent.transform) {
2215            // Get local transform and decompose them to position, scale, rotation
2216            mul$1(tmpTransform, parent.invTransform, m);
2217            m = tmpTransform;
2218        }
2219        var sx = m[0] * m[0] + m[1] * m[1];
2220        var sy = m[2] * m[2] + m[3] * m[3];
2221        var position = this.position;
2222        var scale$$1 = this.scale;
2223        if (isNotAroundZero(sx - 1)) {
2224            sx = Math.sqrt(sx);
2225        }
2226        if (isNotAroundZero(sy - 1)) {
2227            sy = Math.sqrt(sy);
2228        }
2229        if (m[0] < 0) {
2230            sx = -sx;
2231        }
2232        if (m[3] < 0) {
2233            sy = -sy;
2234        }
2235        position[0] = m[4];
2236        position[1] = m[5];
2237        scale$$1[0] = sx;
2238        scale$$1[1] = sy;
2239        this.rotation = Math.atan2(-m[1] / sy, m[0] / sx);
2240    };
2241
2242    /**
2243     * Get global scale
2244     * @return {Array.<number>}
2245     */
2246    transformableProto.getGlobalScale = function () {
2247        var m = this.transform;
2248        if (!m) {
2249            return [1, 1];
2250        }
2251        var sx = Math.sqrt(m[0] * m[0] + m[1] * m[1]);
2252        var sy = Math.sqrt(m[2] * m[2] + m[3] * m[3]);
2253        if (m[0] < 0) {
2254            sx = -sx;
2255        }
2256        if (m[3] < 0) {
2257            sy = -sy;
2258        }
2259        return [sx, sy];
2260    };
2261    /**
2262     * 鍙樻崲鍧愭爣浣嶇疆鍒� shape 鐨勫眬閮ㄥ潗鏍囩┖闂�
2263     * @method
2264     * @param {number} x
2265     * @param {number} y
2266     * @return {Array.<number>}
2267     */
2268    transformableProto.transformCoordToLocal = function (x, y) {
2269        var v2 = [x, y];
2270        var invTransform = this.invTransform;
2271        if (invTransform) {
2272            applyTransform(v2, v2, invTransform);
2273        }
2274        return v2;
2275    };
2276
2277    /**
2278     * 鍙樻崲灞€閮ㄥ潗鏍囦綅缃埌鍏ㄥ眬鍧愭爣绌洪棿
2279     * @method
2280     * @param {number} x
2281     * @param {number} y
2282     * @return {Array.<number>}
2283     */
2284    transformableProto.transformCoordToGlobal = function (x, y) {
2285        var v2 = [x, y];
2286        var transform = this.transform;
2287        if (transform) {
2288            applyTransform(v2, v2, transform);
2289        }
2290        return v2;
2291    };
2292
2293    /**
2294     * @static
2295     * @param {Object} target
2296     * @param {Array.<number>} target.origin
2297     * @param {number} target.rotation
2298     * @param {Array.<number>} target.position
2299     * @param {Array.<number>} [m]
2300     */
2301    Transformable.getLocalTransform = function (target, m) {
2302        m = m || [];
2303        mIdentity(m);
2304
2305        var origin = target.origin;
2306        var scale$$1 = target.scale || [1, 1];
2307        var rotation = target.rotation || 0;
2308        var position = target.position || [0, 0];
2309
2310        if (origin) {
2311            // Translate to origin
2312            m[4] -= origin[0];
2313            m[5] -= origin[1];
2314        }
2315        scale$1(m, m, scale$$1);
2316        if (rotation) {
2317            rotate(m, m, rotation);
2318        }
2319        if (origin) {
2320            // Translate back from origin
2321            m[4] += origin[0];
2322            m[5] += origin[1];
2323        }
2324
2325        m[4] += position[0];
2326        m[5] += position[1];
2327
2328        return m;
2329    };
2330
2331    /**
2332     * 缂撳姩浠g爜鏉ヨ嚜 https://github.com/sole/tween.js/blob/master/src/Tween.js
2333     * @see http://sole.github.io/tween.js/examples/03_graphs.html
2334     * @exports zrender/animation/easing
2335     */
2336    var easing = {
2337        /**
2338         * @param {number} k
2339         * @return {number}
2340         */
2341        linear: function (k) {
2342            return k;
2343        },
2344
2345        /**
2346         * @param {number} k
2347         * @return {number}
2348         */
2349        quadraticIn: function (k) {
2350            return k * k;
2351        },
2352        /**
2353         * @param {number} k
2354         * @return {number}
2355         */
2356        quadraticOut: function (k) {
2357            return k * (2 - k);
2358        },
2359        /**
2360         * @param {number} k
2361         * @return {number}
2362         */
2363        quadraticInOut: function (k) {
2364            if ((k *= 2) < 1) {
2365                return 0.5 * k * k;
2366            }
2367            return -0.5 * (--k * (k - 2) - 1);
2368        },
2369
vendor: 9,182 bytes, lines 2370-2693
2370        // 涓夋鏂圭殑缂撳姩锛坱^3锛�
2371        /**
2372         * @param {number} k
2373         * @return {number}
2374         */
2375        cubicIn: function (k) {
2376            return k * k * k;
2377        },
2378        /**
2379         * @param {number} k
2380         * @return {number}
2381         */
2382        cubicOut: function (k) {
2383            return --k * k * k + 1;
2384        },
2385        /**
2386         * @param {number} k
2387         * @return {number}
2388         */
2389        cubicInOut: function (k) {
2390            if ((k *= 2) < 1) {
2391                return 0.5 * k * k * k;
2392            }
2393            return 0.5 * ((k -= 2) * k * k + 2);
2394        },
2395
2396        // 鍥涙鏂圭殑缂撳姩锛坱^4锛�
2397        /**
2398         * @param {number} k
2399         * @return {number}
2400         */
2401        quarticIn: function (k) {
2402            return k * k * k * k;
2403        },
2404        /**
2405         * @param {number} k
2406         * @return {number}
2407         */
2408        quarticOut: function (k) {
2409            return 1 - (--k * k * k * k);
2410        },
2411        /**
2412         * @param {number} k
2413         * @return {number}
2414         */
2415        quarticInOut: function (k) {
2416            if ((k *= 2) < 1) {
2417                return 0.5 * k * k * k * k;
2418            }
2419            return -0.5 * ((k -= 2) * k * k * k - 2);
2420        },
2421
2422        // 浜旀鏂圭殑缂撳姩锛坱^5锛�
2423        /**
2424         * @param {number} k
2425         * @return {number}
2426         */
2427        quinticIn: function (k) {
2428            return k * k * k * k * k;
2429        },
2430        /**
2431         * @param {number} k
2432         * @return {number}
2433         */
2434        quinticOut: function (k) {
2435            return --k * k * k * k * k + 1;
2436        },
2437        /**
2438         * @param {number} k
2439         * @return {number}
2440         */
2441        quinticInOut: function (k) {
2442            if ((k *= 2) < 1) {
2443                return 0.5 * k * k * k * k * k;
2444            }
2445            return 0.5 * ((k -= 2) * k * k * k * k + 2);
2446        },
2447
2448        // 姝e鸡鏇茬嚎鐨勭紦鍔紙sin(t)锛�
2449        /**
2450         * @param {number} k
2451         * @return {number}
2452         */
2453        sinusoidalIn: function (k) {
2454            return 1 - Math.cos(k * Math.PI / 2);
2455        },
2456        /**
2457         * @param {number} k
2458         * @return {number}
2459         */
2460        sinusoidalOut: function (k) {
2461            return Math.sin(k * Math.PI / 2);
2462        },
2463        /**
2464         * @param {number} k
2465         * @return {number}
2466         */
2467        sinusoidalInOut: function (k) {
2468            return 0.5 * (1 - Math.cos(Math.PI * k));
2469        },
2470
2471        // 鎸囨暟鏇茬嚎鐨勭紦鍔紙2^t锛�
2472        /**
2473         * @param {number} k
2474         * @return {number}
2475         */
2476        exponentialIn: function (k) {
2477            return k === 0 ? 0 : Math.pow(1024, k - 1);
2478        },
2479        /**
2480         * @param {number} k
2481         * @return {number}
2482         */
2483        exponentialOut: function (k) {
2484            return k === 1 ? 1 : 1 - Math.pow(2, -10 * k);
2485        },
2486        /**
2487         * @param {number} k
2488         * @return {number}
2489         */
2490        exponentialInOut: function (k) {
2491            if (k === 0) {
2492                return 0;
2493            }
2494            if (k === 1) {
2495                return 1;
2496            }
2497            if ((k *= 2) < 1) {
2498                return 0.5 * Math.pow(1024, k - 1);
2499            }
2500            return 0.5 * (-Math.pow(2, -10 * (k - 1)) + 2);
2501        },
2502
2503        // 鍦嗗舰鏇茬嚎鐨勭紦鍔紙sqrt(1-t^2)锛�
2504        /**
2505         * @param {number} k
2506         * @return {number}
2507         */
2508        circularIn: function (k) {
2509            return 1 - Math.sqrt(1 - k * k);
2510        },
2511        /**
2512         * @param {number} k
2513         * @return {number}
2514         */
2515        circularOut: function (k) {
2516            return Math.sqrt(1 - (--k * k));
2517        },
2518        /**
2519         * @param {number} k
2520         * @return {number}
2521         */
2522        circularInOut: function (k) {
2523            if ((k *= 2) < 1) {
2524                return -0.5 * (Math.sqrt(1 - k * k) - 1);
2525            }
2526            return 0.5 * (Math.sqrt(1 - (k -= 2) * k) + 1);
2527        },
2528
2529        // 鍒涘缓绫讳技浜庡脊绨у湪鍋滄鍓嶆潵鍥炴尟鑽$殑鍔ㄧ敾
2530        /**
2531         * @param {number} k
2532         * @return {number}
2533         */
2534        elasticIn: function (k) {
2535            var s;
2536            var a = 0.1;
2537            var p = 0.4;
2538            if (k === 0) {
2539                return 0;
2540            }
2541            if (k === 1) {
2542                return 1;
2543            }
2544            if (!a || a < 1) {
2545                a = 1; s = p / 4;
2546            }
2547            else {
2548                s = p * Math.asin(1 / a) / (2 * Math.PI);
2549            }
2550            return -(a * Math.pow(2, 10 * (k -= 1)) *
2551                Math.sin((k - s) * (2 * Math.PI) / p));
2552        },
2553        /**
2554         * @param {number} k
2555         * @return {number}
2556         */
2557        elasticOut: function (k) {
2558            var s;
2559            var a = 0.1;
2560            var p = 0.4;
2561            if (k === 0) {
2562                return 0;
2563            }
2564            if (k === 1) {
2565                return 1;
2566            }
2567            if (!a || a < 1) {
2568                a = 1; s = p / 4;
2569            }
2570            else {
2571                s = p * Math.asin(1 / a) / (2 * Math.PI);
2572            }
2573            return (a * Math.pow(2, -10 * k) *
2574                Math.sin((k - s) * (2 * Math.PI) / p) + 1);
2575        },
2576        /**
2577         * @param {number} k
2578         * @return {number}
2579         */
2580        elasticInOut: function (k) {
2581            var s;
2582            var a = 0.1;
2583            var p = 0.4;
2584            if (k === 0) {
2585                return 0;
2586            }
2587            if (k === 1) {
2588                return 1;
2589            }
2590            if (!a || a < 1) {
2591                a = 1; s = p / 4;
2592            }
2593            else {
2594                s = p * Math.asin(1 / a) / (2 * Math.PI);
2595            }
2596            if ((k *= 2) < 1) {
2597                return -0.5 * (a * Math.pow(2, 10 * (k -= 1))
2598                    * Math.sin((k - s) * (2 * Math.PI) / p));
2599            }
2600            return a * Math.pow(2, -10 * (k -= 1))
2601                * Math.sin((k - s) * (2 * Math.PI) / p) * 0.5 + 1;
2602
2603        },
2604
2605        // 鍦ㄦ煇涓€鍔ㄧ敾寮€濮嬫部鎸囩ず鐨勮矾寰勮繘琛屽姩鐢诲鐞嗗墠绋嶇◢鏀跺洖璇ュ姩鐢荤殑绉诲姩
2606        /**
2607         * @param {number} k
2608         * @return {number}
2609         */
2610        backIn: function (k) {
2611            var s = 1.70158;
2612            return k * k * ((s + 1) * k - s);
2613        },
2614        /**
2615         * @param {number} k
2616         * @return {number}
2617         */
2618        backOut: function (k) {
2619            var s = 1.70158;
2620            return --k * k * ((s + 1) * k + s) + 1;
2621        },
2622        /**
2623         * @param {number} k
2624         * @return {number}
2625         */
2626        backInOut: function (k) {
2627            var s = 1.70158 * 1.525;
2628            if ((k *= 2) < 1) {
2629                return 0.5 * (k * k * ((s + 1) * k - s));
2630            }
2631            return 0.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2);
2632        },
2633
2634        // 鍒涘缓寮硅烦鏁堟灉
2635        /**
2636         * @param {number} k
2637         * @return {number}
2638         */
2639        bounceIn: function (k) {
2640            return 1 - easing.bounceOut(1 - k);
2641        },
2642        /**
2643         * @param {number} k
2644         * @return {number}
2645         */
2646        bounceOut: function (k) {
2647            if (k < (1 / 2.75)) {
2648                return 7.5625 * k * k;
2649            }
2650            else if (k < (2 / 2.75)) {
2651                return 7.5625 * (k -= (1.5 / 2.75)) * k + 0.75;
2652            }
2653            else if (k < (2.5 / 2.75)) {
2654                return 7.5625 * (k -= (2.25 / 2.75)) * k + 0.9375;
2655            }
2656            else {
2657                return 7.5625 * (k -= (2.625 / 2.75)) * k + 0.984375;
2658            }
2659        },
2660        /**
2661         * @param {number} k
2662         * @return {number}
2663         */
2664        bounceInOut: function (k) {
2665            if (k < 0.5) {
2666                return easing.bounceIn(k * 2) * 0.5;
2667            }
2668            return easing.bounceOut(k * 2 - 1) * 0.5 + 0.5;
2669        }
2670    };
2671
2672    /**
2673     * 鍔ㄧ敾涓绘帶鍒跺櫒
2674     * @config target 鍔ㄧ敾瀵硅薄锛屽彲浠ユ槸鏁扮粍锛屽鏋滄槸鏁扮粍鐨勮瘽浼氭壒閲忓垎鍙憃nframe绛変簨浠�
2675     * @config life(1000) 鍔ㄧ敾鏃堕暱
2676     * @config delay(0) 鍔ㄧ敾寤惰繜鏃堕棿
2677     * @config loop(true)
2678     * @config gap(0) 寰幆鐨勯棿闅旀椂闂�
2679     * @config onframe
2680     * @config easing(optional)
2681     * @config ondestroy(optional)
2682     * @config onrestart(optional)
2683     *
2684     * TODO pause
2685     */
2686
2687    function Clip(options) {
2688
2689        this._target = options.target;
2690
2691        // 鐢熷懡鍛ㄦ湡
2692        this._life = options.life || 1000;
2693        // 寤舵椂
2694        this._delay = options.delay || 0;
2695        // 寮€濮嬫椂闂�
2696        // this._startTime = new Date().getTime() + this._delay;// 鍗曚綅姣
2697        this._initialized = false;
2698
2699        // 鏄惁寰幆
2700        this.loop = options.loop == null ? false : options.loop;
2701
2702        this.gap = options.gap || 0;
2703
2704        this.easing = options.easing || 'Linear';
2705
2706        this.onframe = options.onframe;
2707        this.ondestroy = options.ondestroy;
2708        this.onrestart = options.onrestart;
2709
2710        this._pausedTime = 0;
2711        this._paused = false;
2712    }
2713
2714    Clip.prototype = {
2715
2716        constructor: Clip,
2717
2718        step: function (globalTime, deltaTime) {
2719            // Set startTime on first step, or _startTime may has milleseconds different between clips
2720            // PENDING
2721            if (!this._initialized) {
2722                this._startTime = globalTime + this._delay;
2723                this._initialized = true;
2724            }
2725
2726            if (this._paused) {
2727                this._pausedTime += deltaTime;
2728                return;
2729            }
2730
2731            var percent = (globalTime - this._startTime - this._pausedTime) / this._life;
2732
2733            // 杩樻病寮€濮�
2734            if (percent < 0) {
2735                return;
2736            }
2737
2738            percent = Math.min(percent, 1);
2739
2740            var easing$$1 = this.easing;
2741            var easingFunc = typeof easing$$1 == 'string' ? easing[easing$$1] : easing$$1;
2742            var schedule = typeof easingFunc === 'function'
2743                ? easingFunc(percent)
2744                : percent;
2745
2746            this.fire('frame', schedule);
2747
2748            // 缁撴潫
2749            if (percent == 1) {
2750                if (this.loop) {
2751                    this.restart (globalTime);
2752                    // 閲嶆柊寮€濮嬪懆鏈�
2753                    // 鎶涘嚭鑰屼笉鏄洿鎺ヨ皟鐢ㄤ簨浠剁洿鍒� stage.update 鍚庡啀缁熶竴璋冪敤杩欎簺浜嬩欢
2754                    return 'restart';
2755                }
2756
2757                // 鍔ㄧ敾瀹屾垚灏嗚繖涓帶鍒跺櫒鏍囪瘑涓哄緟鍒犻櫎
2758                // 鍦ˋnimation.update涓繘琛屾壒閲忓垹闄�
2759                this._needsRemove = true;
2760                return 'destroy';
2761            }
2762
2763            return null;
2764        },
2765
2766        restart: function (globalTime) {
2767            var remainder = (globalTime - this._startTime - this._pausedTime) % this._life;
2768            this._startTime = globalTime - remainder + this.gap;
2769            this._pausedTime = 0;
2770
2771            this._needsRemove = false;
2772        },
2773
2774        fire: function (eventType, arg) {
2775            eventType = 'on' + eventType;
2776            if (this[eventType]) {
2777                this[eventType](this._target, arg);
2778            }
2779        },
2780
2781        pause: function () {
2782            this._paused = true;
2783        },
2784
2785        resume: function () {
2786            this._paused = false;
2787        }
2788    };
2789
2790// Simple LRU cache use doubly linked list
2791// @module zrender/core/LRU
2792
2793    /**
2794     * Simple double linked list. Compared with array, it has O(1) remove operation.
2795     * @constructor
2796     */
2797    var LinkedList = function () {
2798
2799        /**
2800         * @type {module:zrender/core/LRU~Entry}
2801         */
2802        this.head = null;
2803
2804        /**
2805         * @type {module:zrender/core/LRU~Entry}
2806         */
2807        this.tail = null;
2808
2809        this._len = 0;
2810    };
2811
2812    var linkedListProto = LinkedList.prototype;
2813    /**
2814     * Insert a new value at the tail
2815     * @param  {} val
2816     * @return {module:zrender/core/LRU~Entry}
2817     */
2818    linkedListProto.insert = function (val) {
2819        var entry = new Entry(val);
2820        this.insertEntry(entry);
2821        return entry;
2822    };
2823
2824    /**
2825     * Insert an entry at the tail
2826     * @param  {module:zrender/core/LRU~Entry} entry
2827     */
2828    linkedListProto.insertEntry = function (entry) {
2829        if (!this.head) {
2830            this.head = this.tail = entry;
2831        }
2832        else {
2833            this.tail.next = entry;
2834            entry.prev = this.tail;
2835            entry.next = null;
2836            this.tail = entry;
2837        }
2838        this._len++;
2839    };
2840
2841    /**
2842     * Remove entry.
2843     * @param  {module:zrender/core/LRU~Entry} entry
2844     */
2845    linkedListProto.remove = function (entry) {
2846        var prev = entry.prev;
2847        var next = entry.next;
2848        if (prev) {
2849            prev.next = next;
2850        }
2851        else {
2852            // Is head
2853            this.head = next;
2854        }
2855        if (next) {
2856            next.prev = prev;
2857        }
2858        else {
2859            // Is tail
2860            this.tail = prev;
2861        }
2862        entry.next = entry.prev = null;
2863        this._len--;
2864    };
2865
2866    /**
2867     * @return {number}
2868     */
2869    linkedListProto.len = function () {
2870        return this._len;
2871    };
2872
2873    /**
2874     * Clear list
2875     */
2876    linkedListProto.clear = function () {
2877        this.head = this.tail = null;
2878        this._len = 0;
2879    };
2880
2881    /**
2882     * @constructor
2883     * @param {} val
2884     */
2885    var Entry = function (val) {
2886        /**
2887         * @type {}
2888         */
2889        this.value = val;
2890
2891        /**
2892         * @type {module:zrender/core/LRU~Entry}
2893         */
2894        this.next;
2895
2896        /**
2897         * @type {module:zrender/core/LRU~Entry}
2898         */
2899        this.prev;
2900    };
2901
2902    /**
2903     * LRU Cache
2904     * @constructor
2905     * @alias module:zrender/core/LRU
2906     */
2907    var LRU = function (maxSize) {
2908
2909        this._list = new LinkedList();
2910
2911        this._map = {};
2912
2913        this._maxSize = maxSize || 10;
2914
2915        this._lastRemovedEntry = null;
2916    };
2917
2918    var LRUProto = LRU.prototype;
2919
2920    /**
2921     * @param  {string} key
2922     * @param  {} value
2923     * @return {} Removed value
2924     */
2925    LRUProto.put = function (key, value) {
2926        var list = this._list;
2927        var map = this._map;
2928        var removed = null;
2929        if (map[key] == null) {
2930            var len = list.len();
2931            // Reuse last removed entry
2932            var entry = this._lastRemovedEntry;
2933
2934            if (len >= this._maxSize && len > 0) {
2935                // Remove the least recently used
2936                var leastUsedEntry = list.head;
2937                list.remove(leastUsedEntry);
2938                delete map[leastUsedEntry.key];
2939
2940                removed = leastUsedEntry.value;
2941                this._lastRemovedEntry = leastUsedEntry;
2942            }
2943
2944            if (entry) {
2945                entry.value = value;
2946            }
2947            else {
2948                entry = new Entry(value);
2949            }
2950            entry.key = key;
2951            list.insertEntry(entry);
2952            map[key] = entry;
2953        }
2954
2955        return removed;
2956    };
2957
2958    /**
2959     * @param  {string} key
2960     * @return {}
2961     */
2962    LRUProto.get = function (key) {
2963        var entry = this._map[key];
2964        var list = this._list;
2965        if (entry != null) {
2966            // Put the latest used entry in the tail
2967            if (entry !== list.tail) {
2968                list.remove(entry);
2969                list.insertEntry(entry);
2970            }
2971
2972            return entry.value;
2973        }
2974    };
2975
2976    /**
2977     * Clear the cache
2978     */
2979    LRUProto.clear = function () {
2980        this._list.clear();
2981        this._map = {};
2982    };
2983
2984    var kCSSColorTable = {
2985        'transparent': [0,0,0,0], 'aliceblue': [240,248,255,1],
2986        'antiquewhite': [250,235,215,1], 'aqua': [0,255,255,1],
2987        'aquamarine': [127,255,212,1], 'azure': [240,255,255,1],
2988        'beige': [245,245,220,1], 'bisque': [255,228,196,1],
2989        'black': [0,0,0,1], 'blanchedalmond': [255,235,205,1],
2990        'blue': [0,0,255,1], 'blueviolet': [138,43,226,1],
2991        'brown': [165,42,42,1], 'burlywood': [222,184,135,1],
2992        'cadetblue': [95,158,160,1], 'chartreuse': [127,255,0,1],
2993        'chocolate': [210,105,30,1], 'coral': [255,127,80,1],
2994        'cornflowerblue': [100,149,237,1], 'cornsilk': [255,248,220,1],
2995        'crimson': [220,20,60,1], 'cyan': [0,255,255,1],
2996        'darkblue': [0,0,139,1], 'darkcyan': [0,139,139,1],
2997        'darkgoldenrod': [184,134,11,1], 'darkgray': [169,169,169,1],
2998        'darkgreen': [0,100,0,1], 'darkgrey': [169,169,169,1],
2999        'darkkhaki': [189,183,107,1], 'darkmagenta': [139,0,139,1],
3000        'darkolivegreen': [85,107,47,1], 'darkorange': [255,140,0,1],
3001        'darkorchid': [153,50,204,1], 'darkred': [139,0,0,1],
3002        'darksalmon': [233,150,122,1], 'darkseagreen': [143,188,143,1],
3003        'darkslateblue': [72,61,139,1], 'darkslategray': [47,79,79,1],
3004        'darkslategrey': [47,79,79,1], 'darkturquoise': [0,206,209,1],
3005        'darkviolet': [148,0,211,1], 'deeppink': [255,20,147,1],
3006        'deepskyblue': [0,191,255,1], 'dimgray': [105,105,105,1],
3007        'dimgrey': [105,105,105,1], 'dodgerblue': [30,144,255,1],
3008        'firebrick': [178,34,34,1], 'floralwhite': [255,250,240,1],
3009        'forestgreen': [34,139,34,1], 'fuchsia': [255,0,255,1],
3010        'gainsboro': [220,220,220,1], 'ghostwhite': [248,248,255,1],
3011        'gold': [255,215,0,1], 'goldenrod': [218,165,32,1],
3012        'gray': [128,128,128,1], 'green': [0,128,0,1],
3013        'greenyellow': [173,255,47,1], 'grey': [128,128,128,1],
3014        'honeydew': [240,255,240,1], 'hotpink': [255,105,180,1],
3015        'indianred': [205,92,92,1], 'indigo': [75,0,130,1],
3016        'ivory': [255,255,240,1], 'khaki': [240,230,140,1],
3017        'lavender': [230,230,250,1], 'lavenderblush': [255,240,245,1],
3018        'lawngreen': [124,252,0,1], 'lemonchiffon': [255,250,205,1],
3019        'lightblue': [173,216,230,1], 'lightcoral': [240,128,128,1],
3020        'lightcyan': [224,255,255,1], 'lightgoldenrodyellow': [250,250,210,1],
3021        'lightgray': [211,211,211,1], 'lightgreen': [144,238,144,1],
3022        'lightgrey': [211,211,211,1], 'lightpink': [255,182,193,1],
3023        'lightsalmon': [255,160,122,1], 'lightseagreen': [32,178,170,1],
3024        'lightskyblue': [135,206,250,1], 'lightslategray': [119,136,153,1],
3025        'lightslategrey': [119,136,153,1], 'lightsteelblue': [176,196,222,1],
3026        'lightyellow': [255,255,224,1], 'lime': [0,255,0,1],
3027        'limegreen': [50,205,50,1], 'linen': [250,240,230,1],
3028        'magenta': [255,0,255,1], 'maroon': [128,0,0,1],
3029        'mediumaquamarine': [102,205,170,1], 'mediumblue': [0,0,205,1],
3030        'mediumorchid': [186,85,211,1], 'mediumpurple': [147,112,219,1],
3031        'mediumseagreen': [60,179,113,1], 'mediumslateblue': [123,104,238,1],
3032        'mediumspringgreen': [0,250,154,1], 'mediumturquoise': [72,209,204,1],
3033        'mediumvioletred': [199,21,133,1], 'midnightblue': [25,25,112,1],
3034        'mintcream': [245,255,250,1], 'mistyrose': [255,228,225,1],
3035        'moccasin': [255,228,181,1], 'navajowhite': [255,222,173,1],
3036        'navy': [0,0,128,1], 'oldlace': [253,245,230,1],
3037        'olive': [128,128,0,1], 'olivedrab': [107,142,35,1],
3038        'orange': [255,165,0,1], 'orangered': [255,69,0,1],
3039        'orchid': [218,112,214,1], 'palegoldenrod': [238,232,170,1],
3040        'palegreen': [152,251,152,1], 'paleturquoise': [175,238,238,1],
3041        'palevioletred': [219,112,147,1], 'papayawhip': [255,239,213,1],
3042        'peachpuff': [255,218,185,1], 'peru': [205,133,63,1],
3043        'pink': [255,192,203,1], 'plum': [221,160,221,1],
3044        'powderblue': [176,224,230,1], 'purple': [128,0,128,1],
3045        'red': [255,0,0,1], 'rosybrown': [188,143,143,1],
3046        'royalblue': [65,105,225,1], 'saddlebrown': [139,69,19,1],
3047        'salmon': [250,128,114,1], 'sandybrown': [244,164,96,1],
3048        'seagreen': [46,139,87,1], 'seashell': [255,245,238,1],
3049        'sienna': [160,82,45,1], 'silver': [192,192,192,1],
vendor: 6,228 bytes, lines 3050-3227
3050        'skyblue': [135,206,235,1], 'slateblue': [106,90,205,1],
3051        'slategray': [112,128,144,1], 'slategrey': [112,128,144,1],
3052        'snow': [255,250,250,1], 'springgreen': [0,255,127,1],
3053        'steelblue': [70,130,180,1], 'tan': [210,180,140,1],
3054        'teal': [0,128,128,1], 'thistle': [216,191,216,1],
3055        'tomato': [255,99,71,1], 'turquoise': [64,224,208,1],
3056        'violet': [238,130,238,1], 'wheat': [245,222,179,1],
3057        'white': [255,255,255,1], 'whitesmoke': [245,245,245,1],
3058        'yellow': [255,255,0,1], 'yellowgreen': [154,205,50,1]
3059    };
3060
3061    function clampCssByte(i) {  // Clamp to integer 0 .. 255.
3062        i = Math.round(i);  // Seems to be what Chrome does (vs truncation).
3063        return i < 0 ? 0 : i > 255 ? 255 : i;
3064    }
3065
3066    function clampCssAngle(i) {  // Clamp to integer 0 .. 360.
3067        i = Math.round(i);  // Seems to be what Chrome does (vs truncation).
3068        return i < 0 ? 0 : i > 360 ? 360 : i;
3069    }
3070
3071    function clampCssFloat(f) {  // Clamp to float 0.0 .. 1.0.
3072        return f < 0 ? 0 : f > 1 ? 1 : f;
3073    }
3074
3075    function parseCssInt(str) {  // int or percentage.
3076        if (str.length && str.charAt(str.length - 1) === '%') {
3077            return clampCssByte(parseFloat(str) / 100 * 255);
3078        }
3079        return clampCssByte(parseInt(str, 10));
3080    }
3081
3082    function parseCssFloat(str) {  // float or percentage.
3083        if (str.length && str.charAt(str.length - 1) === '%') {
3084            return clampCssFloat(parseFloat(str) / 100);
3085        }
3086        return clampCssFloat(parseFloat(str));
3087    }
3088
3089    function cssHueToRgb(m1, m2, h) {
3090        if (h < 0) {
3091            h += 1;
3092        }
3093        else if (h > 1) {
3094            h -= 1;
3095        }
3096
3097        if (h * 6 < 1) {
3098            return m1 + (m2 - m1) * h * 6;
3099        }
3100        if (h * 2 < 1) {
3101            return m2;
3102        }
3103        if (h * 3 < 2) {
3104            return m1 + (m2 - m1) * (2/3 - h) * 6;
3105        }
3106        return m1;
3107    }
3108
3109    function lerpNumber(a, b, p) {
3110        return a + (b - a) * p;
3111    }
3112
3113    function setRgba(out, r, g, b, a) {
3114        out[0] = r; out[1] = g; out[2] = b; out[3] = a;
3115        return out;
3116    }
3117    function copyRgba(out, a) {
3118        out[0] = a[0]; out[1] = a[1]; out[2] = a[2]; out[3] = a[3];
3119        return out;
3120    }
3121
3122    var colorCache = new LRU(20);
3123    var lastRemovedArr = null;
3124
3125    function putToCache(colorStr, rgbaArr) {
3126        // Reuse removed array
3127        if (lastRemovedArr) {
3128            copyRgba(lastRemovedArr, rgbaArr);
3129        }
3130        lastRemovedArr = colorCache.put(colorStr, lastRemovedArr || (rgbaArr.slice()));
3131    }
3132
3133    /**
3134     * @param {string} colorStr
3135     * @param {Array.<number>} out
3136     * @return {Array.<number>}
3137     * @memberOf module:zrender/util/color
3138     */
3139    function parse(colorStr, rgbaArr) {
3140        if (!colorStr) {
3141            return;
3142        }
3143        rgbaArr = rgbaArr || [];
3144
3145        var cached = colorCache.get(colorStr);
3146        if (cached) {
3147            return copyRgba(rgbaArr, cached);
3148        }
3149
3150        // colorStr may be not string
3151        colorStr = colorStr + '';
3152        // Remove all whitespace, not compliant, but should just be more accepting.
3153        var str = colorStr.replace(/ /g, '').toLowerCase();
3154
3155        // Color keywords (and transparent) lookup.
3156        if (str in kCSSColorTable) {
3157            copyRgba(rgbaArr, kCSSColorTable[str]);
3158            putToCache(colorStr, rgbaArr);
3159            return rgbaArr;
3160        }
3161
3162        // #abc and #abc123 syntax.
3163        if (str.charAt(0) === '#') {
3164            if (str.length === 4) {
3165                var iv = parseInt(str.substr(1), 16);  // TODO(deanm): Stricter parsing.
3166                if (!(iv >= 0 && iv <= 0xfff)) {
3167                    setRgba(rgbaArr, 0, 0, 0, 1);
3168                    return;  // Covers NaN.
3169                }
3170                setRgba(rgbaArr,
3171                    ((iv & 0xf00) >> 4) | ((iv & 0xf00) >> 8),
3172                    (iv & 0xf0) | ((iv & 0xf0) >> 4),
3173                    (iv & 0xf) | ((iv & 0xf) << 4),
3174                    1
3175                );
3176                putToCache(colorStr, rgbaArr);
3177                return rgbaArr;
3178            }
3179            else if (str.length === 7) {
3180                var iv = parseInt(str.substr(1), 16);  // TODO(deanm): Stricter parsing.
3181                if (!(iv >= 0 && iv <= 0xffffff)) {
3182                    setRgba(rgbaArr, 0, 0, 0, 1);
3183                    return;  // Covers NaN.
3184                }
3185                setRgba(rgbaArr,
3186                    (iv & 0xff0000) >> 16,
3187                    (iv & 0xff00) >> 8,
3188                    iv & 0xff,
3189                    1
3190                );
3191                putToCache(colorStr, rgbaArr);
3192                return rgbaArr;
3193            }
3194
3195            return;
3196        }
3197        var op = str.indexOf('('), ep = str.indexOf(')');
3198        if (op !== -1 && ep + 1 === str.length) {
3199            var fname = str.substr(0, op);
3200            var params = str.substr(op + 1, ep - (op + 1)).split(',');
3201            var alpha = 1;  // To allow case fallthrough.
3202            switch (fname) {
3203                case 'rgba':
3204                    if (params.length !== 4) {
3205                        setRgba(rgbaArr, 0, 0, 0, 1);
3206                        return;
3207                    }
3208                    alpha = parseCssFloat(params.pop()); // jshint ignore:line
3209                // Fall through.
3210                case 'rgb':
3211                    if (params.length !== 3) {
3212                        setRgba(rgbaArr, 0, 0, 0, 1);
3213                        return;
3214                    }
3215                    setRgba(rgbaArr,
3216                        parseCssInt(params[0]),
3217                        parseCssInt(params[1]),
3218                        parseCssInt(params[2]),
3219                        alpha
3220                    );
3221                    putToCache(colorStr, rgbaArr);
3222                    return rgbaArr;
3223                case 'hsla':
3224                    if (params.length !== 4) {
3225                        setRgba(rgbaArr, 0, 0, 0, 1);
3226                        return;
3227                    }
vendor: 7,852 bytes, lines 3228-3478
3228                    params[3] = parseCssFloat(params[3]);
3229                    hsla2rgba(params, rgbaArr);
3230                    putToCache(colorStr, rgbaArr);
3231                    return rgbaArr;
3232                case 'hsl':
3233                    if (params.length !== 3) {
3234                        setRgba(rgbaArr, 0, 0, 0, 1);
3235                        return;
3236                    }
3237                    hsla2rgba(params, rgbaArr);
3238                    putToCache(colorStr, rgbaArr);
3239                    return rgbaArr;
3240                default:
3241                    return;
3242            }
3243        }
3244
3245        setRgba(rgbaArr, 0, 0, 0, 1);
3246        return;
3247    }
3248
3249    /**
3250     * @param {Array.<number>} hsla
3251     * @param {Array.<number>} rgba
3252     * @return {Array.<number>} rgba
3253     */
3254    function hsla2rgba(hsla, rgba) {
3255        var h = (((parseFloat(hsla[0]) % 360) + 360) % 360) / 360;  // 0 .. 1
3256        // NOTE(deanm): According to the CSS spec s/l should only be
3257        // percentages, but we don't bother and let float or percentage.
3258        var s = parseCssFloat(hsla[1]);
3259        var l = parseCssFloat(hsla[2]);
3260        var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;
3261        var m1 = l * 2 - m2;
3262
3263        rgba = rgba || [];
3264        setRgba(rgba,
3265            clampCssByte(cssHueToRgb(m1, m2, h + 1 / 3) * 255),
3266            clampCssByte(cssHueToRgb(m1, m2, h) * 255),
3267            clampCssByte(cssHueToRgb(m1, m2, h - 1 / 3) * 255),
3268            1
3269        );
3270
3271        if (hsla.length === 4) {
3272            rgba[3] = hsla[3];
3273        }
3274
3275        return rgba;
3276    }
3277
3278    /**
3279     * @param {Array.<number>} rgba
3280     * @return {Array.<number>} hsla
3281     */
3282    function rgba2hsla(rgba) {
3283        if (!rgba) {
3284            return;
3285        }
3286
3287        // RGB from 0 to 255
3288        var R = rgba[0] / 255;
3289        var G = rgba[1] / 255;
3290        var B = rgba[2] / 255;
3291
3292        var vMin = Math.min(R, G, B); // Min. value of RGB
3293        var vMax = Math.max(R, G, B); // Max. value of RGB
3294        var delta = vMax - vMin; // Delta RGB value
3295
3296        var L = (vMax + vMin) / 2;
3297        var H;
3298        var S;
3299        // HSL results from 0 to 1
3300        if (delta === 0) {
3301            H = 0;
3302            S = 0;
3303        }
3304        else {
3305            if (L < 0.5) {
3306                S = delta / (vMax + vMin);
3307            }
3308            else {
3309                S = delta / (2 - vMax - vMin);
3310            }
3311
3312            var deltaR = (((vMax - R) / 6) + (delta / 2)) / delta;
3313            var deltaG = (((vMax - G) / 6) + (delta / 2)) / delta;
3314            var deltaB = (((vMax - B) / 6) + (delta / 2)) / delta;
3315
3316            if (R === vMax) {
3317                H = deltaB - deltaG;
3318            }
3319            else if (G === vMax) {
3320                H = (1 / 3) + deltaR - deltaB;
3321            }
3322            else if (B === vMax) {
3323                H = (2 / 3) + deltaG - deltaR;
3324            }
3325
3326            if (H < 0) {
3327                H += 1;
3328            }
3329
3330            if (H > 1) {
3331                H -= 1;
3332            }
3333        }
3334
3335        var hsla = [H * 360, S, L];
3336
3337        if (rgba[3] != null) {
3338            hsla.push(rgba[3]);
3339        }
3340
3341        return hsla;
3342    }
3343
3344    /**
3345     * @param {string} color
3346     * @param {number} level
3347     * @return {string}
3348     * @memberOf module:zrender/util/color
3349     */
3350    function lift(color, level) {
3351        var colorArr = parse(color);
3352        if (colorArr) {
3353            for (var i = 0; i < 3; i++) {
3354                if (level < 0) {
3355                    colorArr[i] = colorArr[i] * (1 - level) | 0;
3356                }
3357                else {
3358                    colorArr[i] = ((255 - colorArr[i]) * level + colorArr[i]) | 0;
3359                }
3360                if (colorArr[i] > 255) {
3361                    colorArr[i] = 255;
3362                }
3363                else if (color[i] < 0) {
3364                    colorArr[i] = 0;
3365                }
3366            }
3367            return stringify(colorArr, colorArr.length === 4 ? 'rgba' : 'rgb');
3368        }
3369    }
3370
3371    /**
3372     * @param {string} color
3373     * @return {string}
3374     * @memberOf module:zrender/util/color
3375     */
3376    function toHex(color) {
3377        var colorArr = parse(color);
3378        if (colorArr) {
3379            return ((1 << 24) + (colorArr[0] << 16) + (colorArr[1] << 8) + (+colorArr[2])).toString(16).slice(1);
3380        }
3381    }
3382
3383    /**
3384     * Map value to color. Faster than lerp methods because color is represented by rgba array.
3385     * @param {number} normalizedValue A float between 0 and 1.
3386     * @param {Array.<Array.<number>>} colors List of rgba color array
3387     * @param {Array.<number>} [out] Mapped gba color array
3388     * @return {Array.<number>} will be null/undefined if input illegal.
3389     */
3390    function fastLerp(normalizedValue, colors, out) {
3391        if (!(colors && colors.length)
3392            || !(normalizedValue >= 0 && normalizedValue <= 1)
3393        ) {
3394            return;
3395        }
3396
3397        out = out || [];
3398
3399        var value = normalizedValue * (colors.length - 1);
3400        var leftIndex = Math.floor(value);
3401        var rightIndex = Math.ceil(value);
3402        var leftColor = colors[leftIndex];
3403        var rightColor = colors[rightIndex];
3404        var dv = value - leftIndex;
3405        out[0] = clampCssByte(lerpNumber(leftColor[0], rightColor[0], dv));
3406        out[1] = clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv));
3407        out[2] = clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv));
3408        out[3] = clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv));
3409
3410        return out;
3411    }
3412
3413    /**
3414     * @deprecated
3415     */
3416    var fastMapToColor = fastLerp;
3417
3418    /**
3419     * @param {number} normalizedValue A float between 0 and 1.
3420     * @param {Array.<string>} colors Color list.
3421     * @param {boolean=} fullOutput Default false.
3422     * @return {(string|Object)} Result color. If fullOutput,
3423     *                           return {color: ..., leftIndex: ..., rightIndex: ..., value: ...},
3424     * @memberOf module:zrender/util/color
3425     */
3426    function lerp$1(normalizedValue, colors, fullOutput) {
3427        if (!(colors && colors.length)
3428            || !(normalizedValue >= 0 && normalizedValue <= 1)
3429        ) {
3430            return;
3431        }
3432
3433        var value = normalizedValue * (colors.length - 1);
3434        var leftIndex = Math.floor(value);
3435        var rightIndex = Math.ceil(value);
3436        var leftColor = parse(colors[leftIndex]);
3437        var rightColor = parse(colors[rightIndex]);
3438        var dv = value - leftIndex;
3439
3440        var color = stringify(
3441            [
3442                clampCssByte(lerpNumber(leftColor[0], rightColor[0], dv)),
3443                clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv)),
3444                clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv)),
3445                clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv))
3446            ],
3447            'rgba'
3448        );
3449
3450        return fullOutput
3451            ? {
3452                color: color,
3453                leftIndex: leftIndex,
3454                rightIndex: rightIndex,
3455                value: value
3456            }
3457            : color;
3458    }
3459
3460    /**
3461     * @deprecated
3462     */
3463    var mapToColor = lerp$1;
3464
3465    /**
3466     * @param {string} color
3467     * @param {number=} h 0 ~ 360, ignore when null.
3468     * @param {number=} s 0 ~ 1, ignore when null.
3469     * @param {number=} l 0 ~ 1, ignore when null.
3470     * @return {string} Color string in rgba format.
3471     * @memberOf module:zrender/util/color
3472     */
3473    function modifyHSL(color, h, s, l) {
3474        color = parse(color);
3475
3476        if (color) {
3477            color = rgba2hsla(color);
3478            h != null && (color[0] = clampCssAngle(h));
3479            s != null && (color[1] = parseCssFloat(s));
3480            l != null && (color[2] = parseCssFloat(l));
3481
3482            return stringify(hsla2rgba(color), 'rgba');
3483        }
3484    }
3485
3486    /**
3487     * @param {string} color
3488     * @param {number=} alpha 0 ~ 1
3489     * @return {string} Color string in rgba format.
3490     * @memberOf module:zrender/util/color
3491     */
3492    function modifyAlpha(color, alpha) {
3493        color = parse(color);
3494
3495        if (color && alpha != null) {
3496            color[3] = clampCssFloat(alpha);
3497            return stringify(color, 'rgba');
3498        }
3499    }
3500
3501    /**
3502     * @param {Array.<number>} arrColor like [12,33,44,0.4]
3503     * @param {string} type 'rgba', 'hsva', ...
3504     * @return {string} Result color. (If input illegal, return undefined).
3505     */
3506    function stringify(arrColor, type) {
3507        if (!arrColor || !arrColor.length) {
3508            return;
3509        }
3510        var colorStr = arrColor[0] + ',' + arrColor[1] + ',' + arrColor[2];
3511        if (type === 'rgba' || type === 'hsva' || type === 'hsla') {
3512            colorStr += ',' + arrColor[3];
3513        }
3514        return type + '(' + colorStr + ')';
3515    }
3516
3517
3518    var color = (Object.freeze || Object)({
3519        parse: parse,
3520        lift: lift,
3521        toHex: toHex,
3522        fastLerp: fastLerp,
3523        fastMapToColor: fastMapToColor,
3524        lerp: lerp$1,
3525        mapToColor: mapToColor,
3526        modifyHSL: modifyHSL,
3527        modifyAlpha: modifyAlpha,
3528        stringify: stringify
3529    });
3530
3531    /**
3532     * @module echarts/animation/Animator
3533     */
3534
3535    var arraySlice = Array.prototype.slice;
3536
3537    function defaultGetter(target, key) {
3538        return target[key];
3539    }
3540
3541    function defaultSetter(target, key, value) {
3542        target[key] = value;
3543    }
3544
3545    /**
3546     * @param  {number} p0
3547     * @param  {number} p1
3548     * @param  {number} percent
3549     * @return {number}
3550     */
3551    function interpolateNumber(p0, p1, percent) {
3552        return (p1 - p0) * percent + p0;
3553    }
3554
3555    /**
3556     * @param  {string} p0
3557     * @param  {string} p1
3558     * @param  {number} percent
3559     * @return {string}
3560     */
3561    function interpolateString(p0, p1, percent) {
3562        return percent > 0.5 ? p1 : p0;
3563    }
3564
3565    /**
3566     * @param  {Array} p0
3567     * @param  {Array} p1
3568     * @param  {number} percent
3569     * @param  {Array} out
3570     * @param  {number} arrDim
3571     */
3572    function interpolateArray(p0, p1, percent, out, arrDim) {
3573        var len = p0.length;
3574        if (arrDim == 1) {
3575            for (var i = 0; i < len; i++) {
3576                out[i] = interpolateNumber(p0[i], p1[i], percent);
3577            }
3578        }
3579        else {
3580            var len2 = len && p0[0].length;
3581            for (var i = 0; i < len; i++) {
3582                for (var j = 0; j < len2; j++) {
3583                    out[i][j] = interpolateNumber(
3584                        p0[i][j], p1[i][j], percent
3585                    );
3586                }
3587            }
3588        }
3589    }
3590
3591// arr0 is source array, arr1 is target array.
3592// Do some preprocess to avoid error happened when interpolating from arr0 to arr1
3593    function fillArr(arr0, arr1, arrDim) {
3594        var arr0Len = arr0.length;
3595        var arr1Len = arr1.length;
3596        if (arr0Len !== arr1Len) {
3597            // FIXME Not work for TypedArray
3598            var isPreviousLarger = arr0Len > arr1Len;
3599            if (isPreviousLarger) {
3600                // Cut the previous
3601                arr0.length = arr1Len;
3602            }
3603            else {
3604                // Fill the previous
3605                for (var i = arr0Len; i < arr1Len; i++) {
3606                    arr0.push(
3607                        arrDim === 1 ? arr1[i] : arraySlice.call(arr1[i])
3608                    );
3609                }
3610            }
3611        }
3612        // Handling NaN value
3613        var len2 = arr0[0] && arr0[0].length;
3614        for (var i = 0; i < arr0.length; i++) {
3615            if (arrDim === 1) {
3616                if (isNaN(arr0[i])) {
3617                    arr0[i] = arr1[i];
3618                }
3619            }
3620            else {
3621                for (var j = 0; j < len2; j++) {
3622                    if (isNaN(arr0[i][j])) {
3623                        arr0[i][j] = arr1[i][j];
3624                    }
3625                }
3626            }
3627        }
3628    }
3629
3630    /**
3631     * @param  {Array} arr0
3632     * @param  {Array} arr1
3633     * @param  {number} arrDim
3634     * @return {boolean}
3635     */
3636    function isArraySame(arr0, arr1, arrDim) {
3637        if (arr0 === arr1) {
3638            return true;
3639        }
3640        var len = arr0.length;
3641        if (len !== arr1.length) {
3642            return false;
3643        }
3644        if (arrDim === 1) {
3645            for (var i = 0; i < len; i++) {
3646                if (arr0[i] !== arr1[i]) {
3647                    return false;
3648                }
3649            }
3650        }
3651        else {
3652            var len2 = arr0[0].length;
3653            for (var i = 0; i < len; i++) {
3654                for (var j = 0; j < len2; j++) {
3655                    if (arr0[i][j] !== arr1[i][j]) {
3656                        return false;
3657                    }
3658                }
3659            }
3660        }
3661        return true;
3662    }
3663
3664    /**
3665     * Catmull Rom interpolate array
3666     * @param  {Array} p0
3667     * @param  {Array} p1
3668     * @param  {Array} p2
3669     * @param  {Array} p3
3670     * @param  {number} t
3671     * @param  {number} t2
3672     * @param  {number} t3
3673     * @param  {Array} out
3674     * @param  {number} arrDim
3675     */
3676    function catmullRomInterpolateArray(
3677        p0, p1, p2, p3, t, t2, t3, out, arrDim
3678    ) {
3679        var len = p0.length;
3680        if (arrDim == 1) {
3681            for (var i = 0; i < len; i++) {
3682                out[i] = catmullRomInterpolate(
3683                    p0[i], p1[i], p2[i], p3[i], t, t2, t3
3684                );
3685            }
3686        }
3687        else {
3688            var len2 = p0[0].length;
3689            for (var i = 0; i < len; i++) {
3690                for (var j = 0; j < len2; j++) {
3691                    out[i][j] = catmullRomInterpolate(
3692                        p0[i][j], p1[i][j], p2[i][j], p3[i][j],
3693                        t, t2, t3
3694                    );
3695                }
3696            }
3697        }
3698    }
3699
3700    /**
3701     * Catmull Rom interpolate number
3702     * @param  {number} p0
3703     * @param  {number} p1
3704     * @param  {number} p2
3705     * @param  {number} p3
3706     * @param  {number} t
3707     * @param  {number} t2
3708     * @param  {number} t3
3709     * @return {number}
3710     */
3711    function catmullRomInterpolate(p0, p1, p2, p3, t, t2, t3) {
3712        var v0 = (p2 - p0) * 0.5;
3713        var v1 = (p3 - p1) * 0.5;
3714        return (2 * (p1 - p2) + v0 + v1) * t3
3715            + (-3 * (p1 - p2) - 2 * v0 - v1) * t2
3716            + v0 * t + p1;
3717    }
3718
3719    function cloneValue(value) {
3720        if (isArrayLike(value)) {
3721            var len = value.length;
3722            if (isArrayLike(value[0])) {
3723                var ret = [];
3724                for (var i = 0; i < len; i++) {
3725                    ret.push(arraySlice.call(value[i]));
3726                }
3727                return ret;
3728            }
3729
3730            return arraySlice.call(value);
3731        }
3732
3733        return value;
3734    }
3735
3736    function rgba2String(rgba) {
3737        rgba[0] = Math.floor(rgba[0]);
3738        rgba[1] = Math.floor(rgba[1]);
3739        rgba[2] = Math.floor(rgba[2]);
3740
3741        return 'rgba(' + rgba.join(',') + ')';
3742    }
3743
3744    function getArrayDim(keyframes) {
3745        var lastValue = keyframes[keyframes.length - 1].value;
3746        return isArrayLike(lastValue && lastValue[0]) ? 2 : 1;
3747    }
3748
3749    function createTrackClip(animator, easing, oneTrackDone, keyframes, propName, forceAnimate) {
3750        var getter = animator._getter;
3751        var setter = animator._setter;
3752        var useSpline = easing === 'spline';
3753
3754        var trackLen = keyframes.length;
3755        if (!trackLen) {
3756            return;
3757        }
3758        // Guess data type
3759        var firstVal = keyframes[0].value;
3760        var isValueArray = isArrayLike(firstVal);
3761        var isValueColor = false;
3762        var isValueString = false;
3763
3764        // For vertices morphing
3765        var arrDim = isValueArray ? getArrayDim(keyframes) : 0;
3766
3767        var trackMaxTime;
3768        // Sort keyframe as ascending
3769        keyframes.sort(function(a, b) {
3770            return a.time - b.time;
3771        });
3772
3773        trackMaxTime = keyframes[trackLen - 1].time;
3774        // Percents of each keyframe
3775        var kfPercents = [];
3776        // Value of each keyframe
3777        var kfValues = [];
3778        var prevValue = keyframes[0].value;
3779        var isAllValueEqual = true;
3780        for (var i = 0; i < trackLen; i++) {
3781            kfPercents.push(keyframes[i].time / trackMaxTime);
3782            // Assume value is a color when it is a string
3783            var value = keyframes[i].value;
3784
3785            // Check if value is equal, deep check if value is array
3786            if (!((isValueArray && isArraySame(value, prevValue, arrDim))
3787                    || (!isValueArray && value === prevValue))) {
3788                isAllValueEqual = false;
3789            }
3790            prevValue = value;
3791
3792            // Try converting a string to a color array
3793            if (typeof value == 'string') {
3794                var colorArray = parse(value);
3795                if (colorArray) {
3796                    value = colorArray;
3797                    isValueColor = true;
3798                }
3799                else {
3800                    isValueString = true;
3801                }
3802            }
3803            kfValues.push(value);
3804        }
3805        if (!forceAnimate && isAllValueEqual) {
3806            return;
3807        }
3808
3809        var lastValue = kfValues[trackLen - 1];
3810        // Polyfill array and NaN value
3811        for (var i = 0; i < trackLen - 1; i++) {
3812            if (isValueArray) {
3813                fillArr(kfValues[i], lastValue, arrDim);
3814            }
3815            else {
3816                if (isNaN(kfValues[i]) && !isNaN(lastValue) && !isValueString && !isValueColor) {
3817                    kfValues[i] = lastValue;
3818                }
3819            }
3820        }
3821        isValueArray && fillArr(getter(animator._target, propName), lastValue, arrDim);
3822
3823        // Cache the key of last frame to speed up when
3824        // animation playback is sequency
3825        var lastFrame = 0;
3826        var lastFramePercent = 0;
3827        var start;
3828        var w;
3829        var p0;
3830        var p1;
3831        var p2;
3832        var p3;
3833
3834        if (isValueColor) {
3835            var rgba = [0, 0, 0, 0];
3836        }
3837
3838        var onframe = function (target, percent) {
3839            // Find the range keyframes
3840            // kf1-----kf2---------current--------kf3
3841            // find kf2 and kf3 and do interpolation
3842            var frame;
3843            // In the easing function like elasticOut, percent may less than 0
3844            if (percent < 0) {
3845                frame = 0;
3846            }
3847            else if (percent < lastFramePercent) {
3848                // Start from next key
3849                // PENDING start from lastFrame ?
3850                start = Math.min(lastFrame + 1, trackLen - 1);
3851                for (frame = start; frame >= 0; frame--) {
3852                    if (kfPercents[frame] <= percent) {
3853                        break;
3854                    }
3855                }
3856                // PENDING really need to do this ?
3857                frame = Math.min(frame, trackLen - 2);
3858            }
3859            else {
3860                for (frame = lastFrame; frame < trackLen; frame++) {
3861                    if (kfPercents[frame] > percent) {
3862                        break;
3863                    }
3864                }
3865                frame = Math.min(frame - 1, trackLen - 2);
3866            }
3867            lastFrame = frame;
3868            lastFramePercent = percent;
3869
3870            var range = (kfPercents[frame + 1] - kfPercents[frame]);
3871            if (range === 0) {
3872                return;
3873            }
3874            else {
3875                w = (percent - kfPercents[frame]) / range;
3876            }
3877            if (useSpline) {
3878                p1 = kfValues[frame];
3879                p0 = kfValues[frame === 0 ? frame : frame - 1];
3880                p2 = kfValues[frame > trackLen - 2 ? trackLen - 1 : frame + 1];
3881                p3 = kfValues[frame > trackLen - 3 ? trackLen - 1 : frame + 2];
3882                if (isValueArray) {
3883                    catmullRomInterpolateArray(
3884                        p0, p1, p2, p3, w, w * w, w * w * w,
3885                        getter(target, propName),
3886                        arrDim
3887                    );
3888                }
3889                else {
3890                    var value;
3891                    if (isValueColor) {
3892                        value = catmullRomInterpolateArray(
3893                            p0, p1, p2, p3, w, w * w, w * w * w,
3894                            rgba, 1
3895                        );
3896                        value = rgba2String(rgba);
3897                    }
3898                    else if (isValueString) {
3899                        // String is step(0.5)
3900                        return interpolateString(p1, p2, w);
3901                    }
3902                    else {
3903                        value = catmullRomInterpolate(
3904                            p0, p1, p2, p3, w, w * w, w * w * w
3905                        );
3906                    }
3907                    setter(
3908                        target,
3909                        propName,
3910                        value
3911                    );
3912                }
3913            }
3914            else {
3915                if (isValueArray) {
3916                    interpolateArray(
3917                        kfValues[frame], kfValues[frame + 1], w,
3918                        getter(target, propName),
3919                        arrDim
3920                    );
3921                }
3922                else {
3923                    var value;
3924                    if (isValueColor) {
3925                        interpolateArray(
3926                            kfValues[frame], kfValues[frame + 1], w,
3927                            rgba, 1
3928                        );
3929                        value = rgba2String(rgba);
3930                    }
3931                    else if (isValueString) {
3932                        // String is step(0.5)
3933                        return interpolateString(kfValues[frame], kfValues[frame + 1], w);
3934                    }
3935                    else {
3936                        value = interpolateNumber(kfValues[frame], kfValues[frame + 1], w);
3937                    }
3938                    setter(
3939                        target,
3940                        propName,
3941                        value
3942                    );
3943                }
3944            }
3945        };
3946
3947        var clip = new Clip({
3948            target: animator._target,
3949            life: trackMaxTime,
3950            loop: animator._loop,
3951            delay: animator._delay,
3952            onframe: onframe,
3953            ondestroy: oneTrackDone
3954        });
3955
3956        if (easing && easing !== 'spline') {
3957            clip.easing = easing;
3958        }
3959
3960        return clip;
3961    }
3962
3963    /**
3964     * @alias module:zrender/animation/Animator
3965     * @constructor
3966     * @param {Object} target
3967     * @param {boolean} loop
3968     * @param {Function} getter
3969     * @param {Function} setter
3970     */
3971    var Animator = function(target, loop, getter, setter) {
3972        this._tracks = {};
3973        this._target = target;
3974
3975        this._loop = loop || false;
3976
3977        this._getter = getter || defaultGetter;
3978        this._setter = setter || defaultSetter;
3979
3980        this._clipCount = 0;
3981
3982        this._delay = 0;
3983
3984        this._doneList = [];
3985
3986        this._onframeList = [];
3987
3988        this._clipList = [];
3989    };
3990
3991    Animator.prototype = {
3992        /**
3993         * 璁剧疆鍔ㄧ敾鍏抽敭甯�
3994         * @param  {number} time 鍏抽敭甯ф椂闂达紝鍗曚綅鏄痬s
3995         * @param  {Object} props 鍏抽敭甯х殑灞炴€у€硷紝key-value琛ㄧず
3996         * @return {module:zrender/animation/Animator}
3997         */
3998        when: function(time /* ms */, props) {
3999            var tracks = this._tracks;
4000            for (var propName in props) {
4001                if (!props.hasOwnProperty(propName)) {
4002                    continue;
4003                }
4004
4005                if (!tracks[propName]) {
4006                    tracks[propName] = [];
4007                    // Invalid value
4008                    var value = this._getter(this._target, propName);
4009                    if (value == null) {
4010                        // zrLog('Invalid property ' + propName);
4011                        continue;
4012                    }
4013                    // If time is 0
4014                    //  Then props is given initialize value
4015                    // Else
4016                    //  Initialize value from current prop value
4017                    if (time !== 0) {
4018                        tracks[propName].push({
4019                            time: 0,
4020                            value: cloneValue(value)
4021                        });
4022                    }
4023                }
4024                tracks[propName].push({
4025                    time: time,
4026                    value: props[propName]
4027                });
4028            }
4029            return this;
4030        },
4031        /**
4032         * 娣诲姞鍔ㄧ敾姣忎竴甯х殑鍥炶皟鍑芥暟
4033         * @param  {Function} callback
4034         * @return {module:zrender/animation/Animator}
4035         */
4036        during: function (callback) {
4037            this._onframeList.push(callback);
4038            return this;
4039        },
4040
4041        pause: function () {
4042            for (var i = 0; i < this._clipList.length; i++) {
4043                this._clipList[i].pause();
4044            }
4045            this._paused = true;
4046        },
4047
4048        resume: function () {
4049            for (var i = 0; i < this._clipList.length; i++) {
4050                this._clipList[i].resume();
4051            }
4052            this._paused = false;
4053        },
4054
4055        isPaused: function () {
4056            return !!this._paused;
4057        },
4058
4059        _doneCallback: function () {
4060            // Clear all tracks
4061            this._tracks = {};
4062            // Clear all clips
4063            this._clipList.length = 0;
4064
4065            var doneList = this._doneList;
4066            var len = doneList.length;
4067            for (var i = 0; i < len; i++) {
4068                doneList[i].call(this);
4069            }
4070        },
4071        /**
4072         * 寮€濮嬫墽琛屽姩鐢�
4073         * @param  {string|Function} [easing]
4074         *         鍔ㄧ敾缂撳姩鍑芥暟锛岃瑙亄@link module:zrender/animation/easing}
4075         * @param  {boolean} forceAnimate
4076         * @return {module:zrender/animation/Animator}
4077         */
4078        start: function (easing, forceAnimate) {
4079
4080            var self = this;
4081            var clipCount = 0;
4082
4083            var oneTrackDone = function() {
4084                clipCount--;
4085                if (!clipCount) {
4086                    self._doneCallback();
4087                }
4088            };
4089
4090            var lastClip;
4091            for (var propName in this._tracks) {
4092                if (!this._tracks.hasOwnProperty(propName)) {
4093                    continue;
4094                }
4095                var clip = createTrackClip(
4096                    this, easing, oneTrackDone,
4097                    this._tracks[propName], propName, forceAnimate
4098                );
4099                if (clip) {
4100                    this._clipList.push(clip);
4101                    clipCount++;
4102
4103                    // If start after added to animation
4104                    if (this.animation) {
4105                        this.animation.addClip(clip);
4106                    }
4107
4108                    lastClip = clip;
4109                }
4110            }
4111
4112            // Add during callback on the last clip
4113            if (lastClip) {
4114                var oldOnFrame = lastClip.onframe;
4115                lastClip.onframe = function (target, percent) {
4116                    oldOnFrame(target, percent);
4117
4118                    for (var i = 0; i < self._onframeList.length; i++) {
4119                        self._onframeList[i](target, percent);
4120                    }
4121                };
4122            }
4123
4124            // This optimization will help the case that in the upper application
4125            // the view may be refreshed frequently, where animation will be
4126            // called repeatly but nothing changed.
4127            if (!clipCount) {
4128                this._doneCallback();
4129            }
4130            return this;
4131        },
4132        /**
4133         * 鍋滄鍔ㄧ敾
4134         * @param {boolean} forwardToLast If move to last frame before stop
4135         */
4136        stop: function (forwardToLast) {
4137            var clipList = this._clipList;
4138            var animation = this.animation;
4139            for (var i = 0; i < clipList.length; i++) {
4140                var clip = clipList[i];
4141                if (forwardToLast) {
4142                    // Move to last frame before stop
4143                    clip.onframe(this._target, 1);
4144                }
4145                animation && animation.removeClip(clip);
4146            }
4147            clipList.length = 0;
4148        },
4149        /**
4150         * 璁剧疆鍔ㄧ敾寤惰繜寮€濮嬬殑鏃堕棿
4151         * @param  {number} time 鍗曚綅ms
4152         * @return {module:zrender/animation/Animator}
4153         */
4154        delay: function (time) {
4155            this._delay = time;
4156            return this;
4157        },
4158        /**
4159         * 娣诲姞鍔ㄧ敾缁撴潫鐨勫洖璋�
4160         * @param  {Function} cb
4161         * @return {module:zrender/animation/Animator}
4162         */
4163        done: function(cb) {
4164            if (cb) {
4165                this._doneList.push(cb);
4166            }
4167            return this;
4168        },
4169
4170        /**
4171         * @return {Array.<module:zrender/animation/Clip>}
4172         */
4173        getClips: function () {
4174            return this._clipList;
4175        }
4176    };
4177
4178    var dpr = 1;
4179
4180// If in browser environment
4181    if (typeof window !== 'undefined') {
4182        dpr = Math.max(window.devicePixelRatio || 1, 1);
4183    }
4184
4185    /**
4186     * config榛樿閰嶇疆椤�
4187     * @exports zrender/config
4188     * @author Kener (@Kener-鏋楀嘲, [email protected])
4189     */
4190
4191    /**
4192     * debug鏃ュ織閫夐」锛歝atchBrushException涓簍rue涓嬫湁鏁�
4193     * 0 : 涓嶇敓鎴恉ebug鏁版嵁锛屽彂甯冪敤
4194     * 1 : 寮傚父鎶涘嚭锛岃皟璇曠敤
4195     * 2 : 鎺у埗鍙拌緭鍑猴紝璋冭瘯鐢�
4196     */
4197    var debugMode = 0;
4198
4199// retina 灞忓箷浼樺寲
4200    var devicePixelRatio = dpr;
4201
4202    var log = function () {
4203    };
4204
4205    if (debugMode === 1) {
4206        log = function () {
4207            for (var k in arguments) {
4208                throw new Error(arguments[k]);
4209            }
4210        };
4211    }
4212    else if (debugMode > 1) {
4213        log = function () {
4214            for (var k in arguments) {
4215                console.log(arguments[k]);
4216            }
4217        };
4218    }
4219
4220    var zrLog = log;
4221
4222    /**
4223     * @alias modue:zrender/mixin/Animatable
4224     * @constructor
4225     */
4226    var Animatable = function () {
4227
4228        /**
4229         * @type {Array.<module:zrender/animation/Animator>}
4230         * @readOnly
4231         */
4232        this.animators = [];
4233    };
4234
4235    Animatable.prototype = {
4236
4237        constructor: Animatable,
4238
4239        /**
4240         * 鍔ㄧ敾
4241         *
4242         * @param {string} path The path to fetch value from object, like 'a.b.c'.
4243         * @param {boolean} [loop] Whether to loop animation.
4244         * @return {module:zrender/animation/Animator}
4245         * @example:
4246         *     el.animate('style', false)
4247         *         .when(1000, {x: 10} )
4248         *         .done(function(){ // Animation done })
4249         *         .start()
4250         */
4251        animate: function (path, loop) {
4252            var target;
4253            var animatingShape = false;
4254            var el = this;
4255            var zr = this.__zr;
4256            if (path) {
4257                var pathSplitted = path.split('.');
4258                var prop = el;
4259                // If animating shape
4260                animatingShape = pathSplitted[0] === 'shape';
4261                for (var i = 0, l = pathSplitted.length; i < l; i++) {
4262                    if (!prop) {
4263                        continue;
4264                    }
4265                    prop = prop[pathSplitted[i]];
4266                }
4267                if (prop) {
4268                    target = prop;
4269                }
4270            }
4271            else {
4272                target = el;
4273            }
4274
4275            if (!target) {
4276                zrLog(
4277                    'Property "'
4278                    + path
4279                    + '" is not existed in element '
4280                    + el.id
4281                );
4282                return;
4283            }
4284
4285            var animators = el.animators;
4286
4287            var animator = new Animator(target, loop);
4288
4289            animator.during(function (target) {
4290                el.dirty(animatingShape);
4291            })
4292                .done(function () {
4293                    // FIXME Animator will not be removed if use `Animator#stop` to stop animation
4294                    animators.splice(indexOf(animators, animator), 1);
4295                });
4296
4297            animators.push(animator);
4298
4299            // If animate after added to the zrender
4300            if (zr) {
4301                zr.animation.addAnimator(animator);
4302            }
4303
4304            return animator;
4305        },
4306
4307        /**
4308         * 鍋滄鍔ㄧ敾
4309         * @param {boolean} forwardToLast If move to last frame before stop
4310         */
4311        stopAnimation: function (forwardToLast) {
4312            var animators = this.animators;
4313            var len = animators.length;
4314            for (var i = 0; i < len; i++) {
4315                animators[i].stop(forwardToLast);
4316            }
4317            animators.length = 0;
4318
4319            return this;
4320        },
4321
4322        /**
4323         * Caution: this method will stop previous animation.
4324         * So do not use this method to one element twice before
4325         * animation starts, unless you know what you are doing.
4326         * @param {Object} target
4327         * @param {number} [time=500] Time in ms
4328         * @param {string} [easing='linear']
4329         * @param {number} [delay=0]
4330         * @param {Function} [callback]
4331         * @param {Function} [forceAnimate] Prevent stop animation and callback
4332         *        immediently when target values are the same as current values.
4333         *
4334         * @example
4335         *  // Animate position
4336         *  el.animateTo({
4337     *      position: [10, 10]
4338     *  }, function () { // done })
4339         *
4340         *  // Animate shape, style and position in 100ms, delayed 100ms, with cubicOut easing
4341         *  el.animateTo({
4342     *      shape: {
4343     *          width: 500
4344     *      },
4345     *      style: {
4346     *          fill: 'red'
4347     *      }
4348     *      position: [10, 10]
4349     *  }, 100, 100, 'cubicOut', function () { // done })
4350         */
4351        // TODO Return animation key
4352        animateTo: function (target, time, delay, easing, callback, forceAnimate) {
4353            // animateTo(target, time, easing, callback);
4354            if (isString(delay)) {
4355                callback = easing;
4356                easing = delay;
4357                delay = 0;
4358            }
4359            // animateTo(target, time, delay, callback);
4360            else if (isFunction$1(easing)) {
4361                callback = easing;
4362                easing = 'linear';
4363                delay = 0;
4364            }
4365            // animateTo(target, time, callback);
4366            else if (isFunction$1(delay)) {
4367                callback = delay;
4368                delay = 0;
4369            }
4370            // animateTo(target, callback)
4371            else if (isFunction$1(time)) {
4372                callback = time;
4373                time = 500;
4374            }
4375            // animateTo(target)
4376            else if (!time) {
4377                time = 500;
4378            }
4379            // Stop all previous animations
4380            this.stopAnimation();
4381            this._animateToShallow('', this, target, time, delay);
4382
4383            // Animators may be removed immediately after start
4384            // if there is nothing to animate
4385            var animators = this.animators.slice();
4386            var count = animators.length;
4387            function done() {
4388                count--;
4389                if (!count) {
4390                    callback && callback();
4391                }
4392            }
4393
4394            // No animators. This should be checked before animators[i].start(),
4395            // because 'done' may be executed immediately if no need to animate.
4396            if (!count) {
4397                callback && callback();
4398            }
4399            // Start after all animators created
4400            // Incase any animator is done immediately when all animation properties are not changed
4401            for (var i = 0; i < animators.length; i++) {
4402                animators[i]
4403                    .done(done)
4404                    .start(easing, forceAnimate);
4405            }
4406        },
4407
4408        /**
4409         * @private
4410         * @param {string} path=''
4411         * @param {Object} source=this
4412         * @param {Object} target
4413         * @param {number} [time=500]
4414         * @param {number} [delay=0]
4415         *
4416         * @example
4417         *  // Animate position
4418         *  el._animateToShallow({
4419     *      position: [10, 10]
4420     *  })
4421         *
4422         *  // Animate shape, style and position in 100ms, delayed 100ms
4423         *  el._animateToShallow({
4424     *      shape: {
4425     *          width: 500
4426     *      },
4427     *      style: {
4428     *          fill: 'red'
4429     *      }
4430     *      position: [10, 10]
4431     *  }, 100, 100)
4432         */
4433        _animateToShallow: function (path, source, target, time, delay) {
4434            var objShallow = {};
4435            var propertyCount = 0;
4436            for (var name in target) {
4437                if (!target.hasOwnProperty(name)) {
4438                    continue;
4439                }
4440
4441                if (source[name] != null) {
4442                    if (isObject$1(target[name]) && !isArrayLike(target[name])) {
4443                        this._animateToShallow(
4444                            path ? path + '.' + name : name,
4445                            source[name],
4446                            target[name],
4447                            time,
4448                            delay
4449                        );
4450                    }
4451                    else {
4452                        objShallow[name] = target[name];
4453                        propertyCount++;
4454                    }
4455                }
4456                else if (target[name] != null) {
4457                    // Attr directly if not has property
4458                    // FIXME, if some property not needed for element ?
4459                    if (!path) {
4460                        this.attr(name, target[name]);
4461                    }
4462                    else {  // Shape or style
4463                        var props = {};
4464                        props[path] = {};
4465                        props[path][name] = target[name];
4466                        this.attr(props);
4467                    }
4468                }
4469            }
4470
4471            if (propertyCount > 0) {
4472                this.animate(path, false)
4473                    .when(time == null ? 500 : time, objShallow)
4474                    .delay(delay || 0);
4475            }
4476
4477            return this;
4478        }
4479    };
4480
4481    /**
4482     * @alias module:zrender/Element
4483     * @constructor
4484     * @extends {module:zrender/mixin/Animatable}
4485     * @extends {module:zrender/mixin/Transformable}
4486     * @extends {module:zrender/mixin/Eventful}
4487     */
4488    var Element = function (opts) { // jshint ignore:line
4489
4490        Transformable.call(this, opts);
4491        Eventful.call(this, opts);
4492        Animatable.call(this, opts);
4493
4494        /**
4495         * 鐢诲竷鍏冪礌ID
4496         * @type {string}
4497         */
4498        this.id = opts.id || guid();
4499    };
4500
4501    Element.prototype = {
4502
4503        /**
4504         * 鍏冪礌绫诲瀷
4505         * Element type
4506         * @type {string}
4507         */
4508        type: 'element',
4509
4510        /**
4511         * 鍏冪礌鍚嶅瓧
4512         * Element name
4513         * @type {string}
4514         */
4515        name: '',
4516
4517        /**
4518         * ZRender 瀹炰緥瀵硅薄锛屼細鍦� element 娣诲姞鍒� zrender 瀹炰緥涓悗鑷姩璧嬪€�
4519         * ZRender instance will be assigned when element is associated with zrender
4520         * @name module:/zrender/Element#__zr
4521         * @type {module:zrender/ZRender}
4522         */
4523        __zr: null,
4524
4525        /**
4526         * 鍥惧舰鏄惁蹇界暐锛屼负true鏃跺拷鐣ュ浘褰㈢殑缁樺埗浠ュ強浜嬩欢瑙﹀彂
4527         * If ignore drawing and events of the element object
4528         * @name module:/zrender/Element#ignore
4529         * @type {boolean}
4530         * @default false
4531         */
4532        ignore: false,
4533
4534        /**
4535         * 鐢ㄤ簬瑁佸壀鐨勮矾寰�(shape)锛屾墍鏈� Group 鍐呯殑璺緞鍦ㄧ粯鍒舵椂閮戒細琚繖涓矾寰勮鍓�
4536         * 璇ヨ矾寰勪細缁ф壙琚鍑忓璞$殑鍙樻崲
4537         * @type {module:zrender/graphic/Path}
4538         * @see http://www.w3.org/TR/2dcontext/#clipping-region
4539         * @readOnly
4540         */
4541        clipPath: null,
4542
4543        /**
4544         * 鏄惁鏄� Group
4545         * @type {boolean}
4546         */
4547        isGroup: false,
4548
4549        /**
4550         * Drift element
4551         * @param  {number} dx dx on the global space
4552         * @param  {number} dy dy on the global space
4553         */
4554        drift: function (dx, dy) {
4555            switch (this.draggable) {
4556                case 'horizontal':
4557                    dy = 0;
4558                    break;
4559                case 'vertical':
4560                    dx = 0;
4561                    break;
4562            }
4563
4564            var m = this.transform;
4565            if (!m) {
4566                m = this.transform = [1, 0, 0, 1, 0, 0];
4567            }
4568            m[4] += dx;
4569            m[5] += dy;
4570
4571            this.decomposeTransform();
4572            this.dirty(false);
4573        },
4574
4575        /**
4576         * Hook before update
4577         */
4578        beforeUpdate: function () {},
4579        /**
4580         * Hook after update
4581         */
4582        afterUpdate: function () {},
4583        /**
4584         * Update each frame
4585         */
4586        update: function () {
4587            this.updateTransform();
4588        },
4589
4590        /**
4591         * @param  {Function} cb
4592         * @param  {}   context
4593         */
4594        traverse: function (cb, context) {},
4595
4596        /**
4597         * @protected
4598         */
4599        attrKV: function (key, value) {
4600            if (key === 'position' || key === 'scale' || key === 'origin') {
4601                // Copy the array
4602                if (value) {
4603                    var target = this[key];
4604                    if (!target) {
4605                        target = this[key] = [];
4606                    }
4607                    target[0] = value[0];
4608                    target[1] = value[1];
4609                }
4610            }
4611            else {
4612                this[key] = value;
4613            }
4614        },
4615
4616        /**
4617         * Hide the element
4618         */
4619        hide: function () {
4620            this.ignore = true;
4621            this.__zr && this.__zr.refresh();
4622        },
4623
4624        /**
4625         * Show the element
4626         */
4627        show: function () {
4628            this.ignore = false;
4629            this.__zr && this.__zr.refresh();
4630        },
4631
4632        /**
4633         * @param {string|Object} key
4634         * @param {*} value
4635         */
4636        attr: function (key, value) {
4637            if (typeof key === 'string') {
4638                this.attrKV(key, value);
4639            }
4640            else if (isObject$1(key)) {
4641                for (var name in key) {
4642                    if (key.hasOwnProperty(name)) {
4643                        this.attrKV(name, key[name]);
4644                    }
4645                }
4646            }
4647
4648            this.dirty(false);
4649
4650            return this;
4651        },
4652
4653        /**
4654         * @param {module:zrender/graphic/Path} clipPath
4655         */
4656        setClipPath: function (clipPath) {
4657            var zr = this.__zr;
4658            if (zr) {
4659                clipPath.addSelfToZr(zr);
4660            }
4661
4662            // Remove previous clip path
4663            if (this.clipPath && this.clipPath !== clipPath) {
4664                this.removeClipPath();
4665            }
4666
4667            this.clipPath = clipPath;
4668            clipPath.__zr = zr;
4669            clipPath.__clipTarget = this;
4670
4671            this.dirty(false);
4672        },
4673
4674        /**
4675         */
4676        removeClipPath: function () {
4677            var clipPath = this.clipPath;
4678            if (clipPath) {
4679                if (clipPath.__zr) {
4680                    clipPath.removeSelfFromZr(clipPath.__zr);
4681                }
4682
4683                clipPath.__zr = null;
4684                clipPath.__clipTarget = null;
4685                this.clipPath = null;
4686
4687                this.dirty(false);
4688            }
4689        },
4690
4691        /**
4692         * Add self from zrender instance.
4693         * Not recursively because it will be invoked when element added to storage.
4694         * @param {module:zrender/ZRender} zr
4695         */
4696        addSelfToZr: function (zr) {
4697            this.__zr = zr;
4698            // 娣诲姞鍔ㄧ敾
4699            var animators = this.animators;
4700            if (animators) {
4701                for (var i = 0; i < animators.length; i++) {
4702                    zr.animation.addAnimator(animators[i]);
4703                }
4704            }
4705
4706            if (this.clipPath) {
4707                this.clipPath.addSelfToZr(zr);
4708            }
4709        },
4710
4711        /**
4712         * Remove self from zrender instance.
4713         * Not recursively because it will be invoked when element added to storage.
4714         * @param {module:zrender/ZRender} zr
4715         */
4716        removeSelfFromZr: function (zr) {
4717            this.__zr = null;
4718            // 绉婚櫎鍔ㄧ敾
4719            var animators = this.animators;
4720            if (animators) {
4721                for (var i = 0; i < animators.length; i++) {
4722                    zr.animation.removeAnimator(animators[i]);
4723                }
4724            }
4725
4726            if (this.clipPath) {
4727                this.clipPath.removeSelfFromZr(zr);
4728            }
4729        }
4730    };
4731
4732    mixin(Element, Animatable);
4733    mixin(Element, Transformable);
4734    mixin(Element, Eventful);
4735
4736    /**
4737     * @module echarts/core/BoundingRect
4738     */
4739
4740    var v2ApplyTransform = applyTransform;
4741    var mathMin = Math.min;
4742    var mathMax = Math.max;
4743
4744    /**
4745     * @alias module:echarts/core/BoundingRect
4746     */
4747    function BoundingRect(x, y, width, height) {
4748
4749        if (width < 0) {
4750            x = x + width;
4751            width = -width;
4752        }
4753        if (height < 0) {
4754            y = y + height;
4755            height = -height;
4756        }
4757
4758        /**
4759         * @type {number}
4760         */
4761        this.x = x;
4762        /**
4763         * @type {number}
4764         */
4765        this.y = y;
4766        /**
4767         * @type {number}
4768         */
4769        this.width = width;
4770        /**
4771         * @type {number}
4772         */
4773        this.height = height;
4774    }
4775
4776    BoundingRect.prototype = {
4777
4778        constructor: BoundingRect,
4779
4780        /**
4781         * @param {module:echarts/core/BoundingRect} other
4782         */
4783        union: function (other) {
4784            var x = mathMin(other.x, this.x);
4785            var y = mathMin(other.y, this.y);
4786
4787            this.width = mathMax(
4788                other.x + other.width,
4789                this.x + this.width
4790            ) - x;
4791            this.height = mathMax(
4792                other.y + other.height,
4793                this.y + this.height
4794            ) - y;
4795            this.x = x;
4796            this.y = y;
4797        },
4798
4799        /**
4800         * @param {Array.<number>} m
4801         * @methods
4802         */
4803        applyTransform: (function () {
4804            var lt = [];
4805            var rb = [];
4806            var lb = [];
4807            var rt = [];
4808            return function (m) {
4809                // In case usage like this
4810                // el.getBoundingRect().applyTransform(el.transform)
4811                // And element has no transform
4812                if (!m) {
4813                    return;
4814                }
4815                lt[0] = lb[0] = this.x;
4816                lt[1] = rt[1] = this.y;
4817                rb[0] = rt[0] = this.x + this.width;
4818                rb[1] = lb[1] = this.y + this.height;
4819
4820                v2ApplyTransform(lt, lt, m);
4821                v2ApplyTransform(rb, rb, m);
4822                v2ApplyTransform(lb, lb, m);
4823                v2ApplyTransform(rt, rt, m);
4824
4825                this.x = mathMin(lt[0], rb[0], lb[0], rt[0]);
4826                this.y = mathMin(lt[1], rb[1], lb[1], rt[1]);
4827                var maxX = mathMax(lt[0], rb[0], lb[0], rt[0]);
4828                var maxY = mathMax(lt[1], rb[1], lb[1], rt[1]);
4829                this.width = maxX - this.x;
4830                this.height = maxY - this.y;
4831            };
4832        })(),
4833
4834        /**
4835         * Calculate matrix of transforming from self to target rect
4836         * @param  {module:zrender/core/BoundingRect} b
4837         * @return {Array.<number>}
4838         */
4839        calculateTransform: function (b) {
4840            var a = this;
4841            var sx = b.width / a.width;
4842            var sy = b.height / a.height;
4843
4844            var m = create$1();
4845
4846            // 鐭╅樀鍙充箻
4847            translate(m, m, [-a.x, -a.y]);
4848            scale$1(m, m, [sx, sy]);
4849            translate(m, m, [b.x, b.y]);
4850
4851            return m;
4852        },
4853
4854        /**
4855         * @param {(module:echarts/core/BoundingRect|Object)} b
4856         * @return {boolean}
4857         */
4858        intersect: function (b) {
4859            if (!b) {
4860                return false;
4861            }
4862
4863            if (!(b instanceof BoundingRect)) {
4864                // Normalize negative width/height.
4865                b = BoundingRect.create(b);
4866            }
4867
4868            var a = this;
4869            var ax0 = a.x;
4870            var ax1 = a.x + a.width;
4871            var ay0 = a.y;
4872            var ay1 = a.y + a.height;
4873
4874            var bx0 = b.x;
4875            var bx1 = b.x + b.width;
4876            var by0 = b.y;
4877            var by1 = b.y + b.height;
4878
4879            return ! (ax1 < bx0 || bx1 < ax0 || ay1 < by0 || by1 < ay0);
4880        },
4881
4882        contain: function (x, y) {
4883            var rect = this;
4884            return x >= rect.x
4885                && x <= (rect.x + rect.width)
4886                && y >= rect.y
4887                && y <= (rect.y + rect.height);
4888        },
4889
4890        /**
4891         * @return {module:echarts/core/BoundingRect}
4892         */
4893        clone: function () {
4894            return new BoundingRect(this.x, this.y, this.width, this.height);
4895        },
4896
4897        /**
4898         * Copy from another rect
4899         */
4900        copy: function (other) {
4901            this.x = other.x;
4902            this.y = other.y;
4903            this.width = other.width;
4904            this.height = other.height;
4905        },
4906
4907        plain: function () {
4908            return {
4909                x: this.x,
4910                y: this.y,
4911                width: this.width,
4912                height: this.height
4913            };
4914        }
4915    };
4916
4917    /**
4918     * @param {Object|module:zrender/core/BoundingRect} rect
4919     * @param {number} rect.x
4920     * @param {number} rect.y
4921     * @param {number} rect.width
4922     * @param {number} rect.height
4923     * @return {module:zrender/core/BoundingRect}
4924     */
4925    BoundingRect.create = function (rect) {
4926        return new BoundingRect(rect.x, rect.y, rect.width, rect.height);
4927    };
4928
4929    /**
4930     * Group鏄竴涓鍣紝鍙互鎻掑叆瀛愯妭鐐癸紝Group鐨勫彉鎹篃浼氳搴旂敤鍒板瓙鑺傜偣涓�
4931     * @module zrender/graphic/Group
4932     * @example
4933     *     var Group = require('zrender/container/Group');
4934     *     var Circle = require('zrender/graphic/shape/Circle');
4935     *     var g = new Group();
4936     *     g.position[0] = 100;
4937     *     g.position[1] = 100;
4938     *     g.add(new Circle({
4939 *         style: {
4940 *             x: 100,
4941 *             y: 100,
4942 *             r: 20,
4943 *         }
4944 *     }));
4945     *     zr.add(g);
4946     */
4947
4948    /**
4949     * @alias module:zrender/graphic/Group
4950     * @constructor
4951     * @extends module:zrender/mixin/Transformable
4952     * @extends module:zrender/mixin/Eventful
4953     */
4954    var Group = function (opts) {
4955
4956        opts = opts || {};
4957
4958        Element.call(this, opts);
4959
4960        for (var key in opts) {
4961            if (opts.hasOwnProperty(key)) {
4962                this[key] = opts[key];
4963            }
4964        }
4965
4966        this._children = [];
4967
4968        this.__storage = null;
4969
4970        this.__dirty = true;
4971    };
4972
4973    Group.prototype = {
4974
4975        constructor: Group,
4976
4977        isGroup: true,
4978
4979        /**
4980         * @type {string}
4981         */
4982        type: 'group',
4983
4984        /**
4985         * 鎵€鏈夊瓙瀛欏厓绱犳槸鍚﹀搷搴旈紶鏍囦簨浠�
4986         * @name module:/zrender/container/Group#silent
4987         * @type {boolean}
4988         * @default false
4989         */
4990        silent: false,
4991
4992        /**
4993         * @return {Array.<module:zrender/Element>}
4994         */
4995        children: function () {
4996            return this._children.slice();
4997        },
4998
4999        /**
5000         * 鑾峰彇鎸囧畾 index 鐨勫効瀛愯妭鐐�
5001         * @param  {number} idx
5002         * @return {module:zrender/Element}
5003         */
5004        childAt: function (idx) {
5005            return this._children[idx];
5006        },
5007
5008        /**
5009         * 鑾峰彇鎸囧畾鍚嶅瓧鐨勫効瀛愯妭鐐�
5010         * @param  {string} name
5011         * @return {module:zrender/Element}
5012         */
5013        childOfName: function (name) {
5014            var children = this._children;
5015            for (var i = 0; i < children.length; i++) {
5016                if (children[i].name === name) {
5017                    return children[i];
5018                }
5019            }
5020        },
5021
5022        /**
5023         * @return {number}
5024         */
5025        childCount: function () {
5026            return this._children.length;
5027        },
5028
5029        /**
5030         * 娣诲姞瀛愯妭鐐瑰埌鏈€鍚�
5031         * @param {module:zrender/Element} child
5032         */
5033        add: function (child) {
5034            if (child && child !== this && child.parent !== this) {
5035
5036                this._children.push(child);
5037
5038                this._doAdd(child);
5039            }
5040
5041            return this;
5042        },
5043
5044        /**
5045         * 娣诲姞瀛愯妭鐐瑰湪 nextSibling 涔嬪墠
5046         * @param {module:zrender/Element} child
5047         * @param {module:zrender/Element} nextSibling
5048         */
5049        addBefore: function (child, nextSibling) {
5050            if (child && child !== this && child.parent !== this
5051                && nextSibling && nextSibling.parent === this) {
5052
5053                var children = this._children;
5054                var idx = children.indexOf(nextSibling);
5055
5056                if (idx >= 0) {
5057                    children.splice(idx, 0, child);
5058                    this._doAdd(child);
5059                }
5060            }
5061
5062            return this;
5063        },
5064
5065        _doAdd: function (child) {
5066            if (child.parent) {
5067                child.parent.remove(child);
5068            }
5069
5070            child.parent = this;
5071
5072            var storage = this.__storage;
5073            var zr = this.__zr;
5074            if (storage && storage !== child.__storage) {
5075
5076                storage.addToStorage(child);
5077
5078                if (child instanceof Group) {
5079                    child.addChildrenToStorage(storage);
5080                }
5081            }
5082
5083            zr && zr.refresh();
5084        },
5085
5086        /**
5087         * 绉婚櫎瀛愯妭鐐�
5088         * @param {module:zrender/Element} child
5089         */
5090        remove: function (child) {
5091            var zr = this.__zr;
5092            var storage = this.__storage;
5093            var children = this._children;
5094
5095            var idx = indexOf(children, child);
5096            if (idx < 0) {
5097                return this;
5098            }
5099            children.splice(idx, 1);
5100
5101            child.parent = null;
5102
5103            if (storage) {
5104
5105                storage.delFromStorage(child);
5106
5107                if (child instanceof Group) {
5108                    child.delChildrenFromStorage(storage);
5109                }
5110            }
5111
5112            zr && zr.refresh();
5113
5114            return this;
5115        },
5116
5117        /**
5118         * 绉婚櫎鎵€鏈夊瓙鑺傜偣
5119         */
5120        removeAll: function () {
5121            var children = this._children;
5122            var storage = this.__storage;
5123            var child;
5124            var i;
5125            for (i = 0; i < children.length; i++) {
5126                child = children[i];
5127                if (storage) {
5128                    storage.delFromStorage(child);
5129                    if (child instanceof Group) {
5130                        child.delChildrenFromStorage(storage);
5131                    }
5132                }
5133                child.parent = null;
5134            }
5135            children.length = 0;
5136
5137            return this;
5138        },
5139
5140        /**
5141         * 閬嶅巻鎵€鏈夊瓙鑺傜偣
5142         * @param  {Function} cb
5143         * @param  {}   context
5144         */
5145        eachChild: function (cb, context) {
5146            var children = this._children;
5147            for (var i = 0; i < children.length; i++) {
5148                var child = children[i];
5149                cb.call(context, child, i);
5150            }
5151            return this;
5152        },
5153
5154        /**
5155         * 娣卞害浼樺厛閬嶅巻鎵€鏈夊瓙瀛欒妭鐐�
5156         * @param  {Function} cb
5157         * @param  {}   context
5158         */
5159        traverse: function (cb, context) {
5160            for (var i = 0; i < this._children.length; i++) {
5161                var child = this._children[i];
5162                cb.call(context, child);
5163
5164                if (child.type === 'group') {
5165                    child.traverse(cb, context);
5166                }
5167            }
5168            return this;
5169        },
5170
5171        addChildrenToStorage: function (storage) {
5172            for (var i = 0; i < this._children.length; i++) {
5173                var child = this._children[i];
5174                storage.addToStorage(child);
5175                if (child instanceof Group) {
5176                    child.addChildrenToStorage(storage);
5177                }
5178            }
5179        },
5180
5181        delChildrenFromStorage: function (storage) {
5182            for (var i = 0; i < this._children.length; i++) {
5183                var child = this._children[i];
5184                storage.delFromStorage(child);
5185                if (child instanceof Group) {
5186                    child.delChildrenFromStorage(storage);
5187                }
5188            }
5189        },
5190
5191        dirty: function () {
5192            this.__dirty = true;
5193            this.__zr && this.__zr.refresh();
5194            return this;
5195        },
5196
5197        /**
5198         * @return {module:zrender/core/BoundingRect}
5199         */
5200        getBoundingRect: function (includeChildren) {
5201            // TODO Caching
5202            var rect = null;
5203            var tmpRect = new BoundingRect(0, 0, 0, 0);
5204            var children = includeChildren || this._children;
5205            var tmpMat = [];
5206
5207            for (var i = 0; i < children.length; i++) {
5208                var child = children[i];
5209                if (child.ignore || child.invisible) {
5210                    continue;
5211                }
5212
vendor: 18,107 bytes, lines 5213-5825
5213                var childRect = child.getBoundingRect();
5214                var transform = child.getLocalTransform(tmpMat);
5215                // TODO
5216                // The boundingRect cacluated by transforming original
5217                // rect may be bigger than the actual bundingRect when rotation
5218                // is used. (Consider a circle rotated aginst its center, where
5219                // the actual boundingRect should be the same as that not be
5220                // rotated.) But we can not find better approach to calculate
5221                // actual boundingRect yet, considering performance.
5222                if (transform) {
5223                    tmpRect.copy(childRect);
5224                    tmpRect.applyTransform(transform);
5225                    rect = rect || tmpRect.clone();
5226                    rect.union(tmpRect);
5227                }
5228                else {
5229                    rect = rect || childRect.clone();
5230                    rect.union(childRect);
5231                }
5232            }
5233            return rect || tmpRect;
5234        }
5235    };
5236
5237    inherits(Group, Element);
5238
5239// https://github.com/mziccard/node-timsort
5240    var DEFAULT_MIN_MERGE = 32;
5241
5242    var DEFAULT_MIN_GALLOPING = 7;
5243
5244    function minRunLength(n) {
5245        var r = 0;
5246
5247        while (n >= DEFAULT_MIN_MERGE) {
5248            r |= n & 1;
5249            n >>= 1;
5250        }
5251
5252        return n + r;
5253    }
5254
5255    function makeAscendingRun(array, lo, hi, compare) {
5256        var runHi = lo + 1;
5257
5258        if (runHi === hi) {
5259            return 1;
5260        }
5261
5262        if (compare(array[runHi++], array[lo]) < 0) {
5263            while (runHi < hi && compare(array[runHi], array[runHi - 1]) < 0) {
5264                runHi++;
5265            }
5266
5267            reverseRun(array, lo, runHi);
5268        }
5269        else {
5270            while (runHi < hi && compare(array[runHi], array[runHi - 1]) >= 0) {
5271                runHi++;
5272            }
5273        }
5274
5275        return runHi - lo;
5276    }
5277
5278    function reverseRun(array, lo, hi) {
5279        hi--;
5280
5281        while (lo < hi) {
5282            var t = array[lo];
5283            array[lo++] = array[hi];
5284            array[hi--] = t;
5285        }
5286    }
5287
5288    function binaryInsertionSort(array, lo, hi, start, compare) {
5289        if (start === lo) {
5290            start++;
5291        }
5292
5293        for (; start < hi; start++) {
5294            var pivot = array[start];
5295
5296            var left = lo;
5297            var right = start;
5298            var mid;
5299
5300            while (left < right) {
5301                mid = left + right >>> 1;
5302
5303                if (compare(pivot, array[mid]) < 0) {
5304                    right = mid;
5305                }
5306                else {
5307                    left = mid + 1;
5308                }
5309            }
5310
5311            var n = start - left;
5312
5313            switch (n) {
5314                case 3:
5315                    array[left + 3] = array[left + 2];
5316
5317                case 2:
5318                    array[left + 2] = array[left + 1];
5319
5320                case 1:
5321                    array[left + 1] = array[left];
5322                    break;
5323                default:
5324                    while (n > 0) {
5325                        array[left + n] = array[left + n - 1];
5326                        n--;
5327                    }
5328            }
5329
5330            array[left] = pivot;
5331        }
5332    }
5333
5334    function gallopLeft(value, array, start, length, hint, compare) {
5335        var lastOffset = 0;
5336        var maxOffset = 0;
5337        var offset = 1;
5338
5339        if (compare(value, array[start + hint]) > 0) {
5340            maxOffset = length - hint;
5341
5342            while (offset < maxOffset && compare(value, array[start + hint + offset]) > 0) {
5343                lastOffset = offset;
5344                offset = (offset << 1) + 1;
5345
5346                if (offset <= 0) {
5347                    offset = maxOffset;
5348                }
5349            }
5350
5351            if (offset > maxOffset) {
5352                offset = maxOffset;
5353            }
5354
5355            lastOffset += hint;
5356            offset += hint;
5357        }
5358        else {
5359            maxOffset = hint + 1;
5360            while (offset < maxOffset && compare(value, array[start + hint - offset]) <= 0) {
5361                lastOffset = offset;
5362                offset = (offset << 1) + 1;
5363
5364                if (offset <= 0) {
5365                    offset = maxOffset;
5366                }
5367            }
5368            if (offset > maxOffset) {
5369                offset = maxOffset;
5370            }
5371
5372            var tmp = lastOffset;
5373            lastOffset = hint - offset;
5374            offset = hint - tmp;
5375        }
5376
5377        lastOffset++;
5378        while (lastOffset < offset) {
5379            var m = lastOffset + (offset - lastOffset >>> 1);
5380
5381            if (compare(value, array[start + m]) > 0) {
5382                lastOffset = m + 1;
5383            }
5384            else {
5385                offset = m;
5386            }
5387        }
5388        return offset;
5389    }
5390
5391    function gallopRight(value, array, start, length, hint, compare) {
5392        var lastOffset = 0;
5393        var maxOffset = 0;
5394        var offset = 1;
5395
5396        if (compare(value, array[start + hint]) < 0) {
5397            maxOffset = hint + 1;
5398
5399            while (offset < maxOffset && compare(value, array[start + hint - offset]) < 0) {
5400                lastOffset = offset;
5401                offset = (offset << 1) + 1;
5402
5403                if (offset <= 0) {
5404                    offset = maxOffset;
5405                }
5406            }
5407
5408            if (offset > maxOffset) {
5409                offset = maxOffset;
5410            }
5411
5412            var tmp = lastOffset;
5413            lastOffset = hint - offset;
5414            offset = hint - tmp;
5415        }
5416        else {
5417            maxOffset = length - hint;
5418
5419            while (offset < maxOffset && compare(value, array[start + hint + offset]) >= 0) {
5420                lastOffset = offset;
5421                offset = (offset << 1) + 1;
5422
5423                if (offset <= 0) {
5424                    offset = maxOffset;
5425                }
5426            }
5427
5428            if (offset > maxOffset) {
5429                offset = maxOffset;
5430            }
5431
5432            lastOffset += hint;
5433            offset += hint;
5434        }
5435
5436        lastOffset++;
5437
5438        while (lastOffset < offset) {
5439            var m = lastOffset + (offset - lastOffset >>> 1);
5440
5441            if (compare(value, array[start + m]) < 0) {
5442                offset = m;
5443            }
5444            else {
5445                lastOffset = m + 1;
5446            }
5447        }
5448
5449        return offset;
5450    }
5451
5452    function TimSort(array, compare) {
5453        var minGallop = DEFAULT_MIN_GALLOPING;
5454        var runStart;
5455        var runLength;
5456        var stackSize = 0;
5457
5458        var tmp = [];
5459
5460        runStart = [];
5461        runLength = [];
5462
5463        function pushRun(_runStart, _runLength) {
5464            runStart[stackSize] = _runStart;
5465            runLength[stackSize] = _runLength;
5466            stackSize += 1;
5467        }
5468
5469        function mergeRuns() {
5470            while (stackSize > 1) {
5471                var n = stackSize - 2;
5472
5473                if (n >= 1 && runLength[n - 1] <= runLength[n] + runLength[n + 1] || n >= 2 && runLength[n - 2] <= runLength[n] + runLength[n - 1]) {
5474                    if (runLength[n - 1] < runLength[n + 1]) {
5475                        n--;
5476                    }
5477                }
5478                else if (runLength[n] > runLength[n + 1]) {
5479                    break;
5480                }
5481                mergeAt(n);
5482            }
5483        }
5484
5485        function forceMergeRuns() {
5486            while (stackSize > 1) {
5487                var n = stackSize - 2;
5488
5489                if (n > 0 && runLength[n - 1] < runLength[n + 1]) {
5490                    n--;
5491                }
5492
5493                mergeAt(n);
5494            }
5495        }
5496
5497        function mergeAt(i) {
5498            var start1 = runStart[i];
5499            var length1 = runLength[i];
5500            var start2 = runStart[i + 1];
5501            var length2 = runLength[i + 1];
5502
5503            runLength[i] = length1 + length2;
5504
5505            if (i === stackSize - 3) {
5506                runStart[i + 1] = runStart[i + 2];
5507                runLength[i + 1] = runLength[i + 2];
5508            }
5509
5510            stackSize--;
5511
5512            var k = gallopRight(array[start2], array, start1, length1, 0, compare);
5513            start1 += k;
5514            length1 -= k;
5515
5516            if (length1 === 0) {
5517                return;
5518            }
5519
5520            length2 = gallopLeft(array[start1 + length1 - 1], array, start2, length2, length2 - 1, compare);
5521
5522            if (length2 === 0) {
5523                return;
5524            }
5525
5526            if (length1 <= length2) {
5527                mergeLow(start1, length1, start2, length2);
5528            }
5529            else {
5530                mergeHigh(start1, length1, start2, length2);
5531            }
5532        }
5533
5534        function mergeLow(start1, length1, start2, length2) {
5535            var i = 0;
5536
5537            for (i = 0; i < length1; i++) {
5538                tmp[i] = array[start1 + i];
5539            }
5540
5541            var cursor1 = 0;
5542            var cursor2 = start2;
5543            var dest = start1;
5544
5545            array[dest++] = array[cursor2++];
5546
5547            if (--length2 === 0) {
5548                for (i = 0; i < length1; i++) {
5549                    array[dest + i] = tmp[cursor1 + i];
5550                }
5551                return;
5552            }
5553
5554            if (length1 === 1) {
5555                for (i = 0; i < length2; i++) {
5556                    array[dest + i] = array[cursor2 + i];
5557                }
5558                array[dest + length2] = tmp[cursor1];
5559                return;
5560            }
5561
5562            var _minGallop = minGallop;
5563            var count1, count2, exit;
5564
5565            while (1) {
5566                count1 = 0;
5567                count2 = 0;
5568                exit = false;
5569
5570                do {
5571                    if (compare(array[cursor2], tmp[cursor1]) < 0) {
5572                        array[dest++] = array[cursor2++];
5573                        count2++;
5574                        count1 = 0;
5575
5576                        if (--length2 === 0) {
5577                            exit = true;
5578                            break;
5579                        }
5580                    }
5581                    else {
5582                        array[dest++] = tmp[cursor1++];
5583                        count1++;
5584                        count2 = 0;
5585                        if (--length1 === 1) {
5586                            exit = true;
5587                            break;
5588                        }
5589                    }
5590                } while ((count1 | count2) < _minGallop);
5591
5592                if (exit) {
5593                    break;
5594                }
5595
5596                do {
5597                    count1 = gallopRight(array[cursor2], tmp, cursor1, length1, 0, compare);
5598
5599                    if (count1 !== 0) {
5600                        for (i = 0; i < count1; i++) {
5601                            array[dest + i] = tmp[cursor1 + i];
5602                        }
5603
5604                        dest += count1;
5605                        cursor1 += count1;
5606                        length1 -= count1;
5607                        if (length1 <= 1) {
5608                            exit = true;
5609                            break;
5610                        }
5611                    }
5612
5613                    array[dest++] = array[cursor2++];
5614
5615                    if (--length2 === 0) {
5616                        exit = true;
5617                        break;
5618                    }
5619
5620                    count2 = gallopLeft(tmp[cursor1], array, cursor2, length2, 0, compare);
5621
5622                    if (count2 !== 0) {
5623                        for (i = 0; i < count2; i++) {
5624                            array[dest + i] = array[cursor2 + i];
5625                        }
5626
5627                        dest += count2;
5628                        cursor2 += count2;
5629                        length2 -= count2;
5630
5631                        if (length2 === 0) {
5632                            exit = true;
5633                            break;
5634                        }
5635                    }
5636                    array[dest++] = tmp[cursor1++];
5637
5638                    if (--length1 === 1) {
5639                        exit = true;
5640                        break;
5641                    }
5642
5643                    _minGallop--;
5644                } while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING);
5645
5646                if (exit) {
5647                    break;
5648                }
5649
5650                if (_minGallop < 0) {
5651                    _minGallop = 0;
5652                }
5653
5654                _minGallop += 2;
5655            }
5656
5657            minGallop = _minGallop;
5658
5659            minGallop < 1 && (minGallop = 1);
5660
5661            if (length1 === 1) {
5662                for (i = 0; i < length2; i++) {
5663                    array[dest + i] = array[cursor2 + i];
5664                }
5665                array[dest + length2] = tmp[cursor1];
5666            }
5667            else if (length1 === 0) {
5668                throw new Error();
5669                // throw new Error('mergeLow preconditions were not respected');
5670            }
5671            else {
5672                for (i = 0; i < length1; i++) {
5673                    array[dest + i] = tmp[cursor1 + i];
5674                }
5675            }
5676        }
5677
5678        function mergeHigh (start1, length1, start2, length2) {
5679            var i = 0;
5680
5681            for (i = 0; i < length2; i++) {
5682                tmp[i] = array[start2 + i];
5683            }
5684
5685            var cursor1 = start1 + length1 - 1;
5686            var cursor2 = length2 - 1;
5687            var dest = start2 + length2 - 1;
5688            var customCursor = 0;
5689            var customDest = 0;
5690
5691            array[dest--] = array[cursor1--];
5692
5693            if (--length1 === 0) {
5694                customCursor = dest - (length2 - 1);
5695
5696                for (i = 0; i < length2; i++) {
5697                    array[customCursor + i] = tmp[i];
5698                }
5699
5700                return;
5701            }
5702
5703            if (length2 === 1) {
5704                dest -= length1;
5705                cursor1 -= length1;
5706                customDest = dest + 1;
5707                customCursor = cursor1 + 1;
5708
5709                for (i = length1 - 1; i >= 0; i--) {
5710                    array[customDest + i] = array[customCursor + i];
5711                }
5712
5713                array[dest] = tmp[cursor2];
5714                return;
5715            }
5716
5717            var _minGallop = minGallop;
5718
5719            while (true) {
5720                var count1 = 0;
5721                var count2 = 0;
5722                var exit = false;
5723
5724                do {
5725                    if (compare(tmp[cursor2], array[cursor1]) < 0) {
5726                        array[dest--] = array[cursor1--];
5727                        count1++;
5728                        count2 = 0;
5729                        if (--length1 === 0) {
5730                            exit = true;
5731                            break;
5732                        }
5733                    }
5734                    else {
5735                        array[dest--] = tmp[cursor2--];
5736                        count2++;
5737                        count1 = 0;
5738                        if (--length2 === 1) {
5739                            exit = true;
5740                            break;
5741                        }
5742                    }
5743                } while ((count1 | count2) < _minGallop);
5744
5745                if (exit) {
5746                    break;
5747                }
5748
5749                do {
5750                    count1 = length1 - gallopRight(tmp[cursor2], array, start1, length1, length1 - 1, compare);
5751
5752                    if (count1 !== 0) {
5753                        dest -= count1;
5754                        cursor1 -= count1;
5755                        length1 -= count1;
5756                        customDest = dest + 1;
5757                        customCursor = cursor1 + 1;
5758
5759                        for (i = count1 - 1; i >= 0; i--) {
5760                            array[customDest + i] = array[customCursor + i];
5761                        }
5762
5763                        if (length1 === 0) {
5764                            exit = true;
5765                            break;
5766                        }
5767                    }
5768
5769                    array[dest--] = tmp[cursor2--];
5770
5771                    if (--length2 === 1) {
5772                        exit = true;
5773                        break;
5774                    }
5775
5776                    count2 = length2 - gallopLeft(array[cursor1], tmp, 0, length2, length2 - 1, compare);
5777
5778                    if (count2 !== 0) {
5779                        dest -= count2;
5780                        cursor2 -= count2;
5781                        length2 -= count2;
5782                        customDest = dest + 1;
5783                        customCursor = cursor2 + 1;
5784
5785                        for (i = 0; i < count2; i++) {
5786                            array[customDest + i] = tmp[customCursor + i];
5787                        }
5788
5789                        if (length2 <= 1) {
5790                            exit = true;
5791                            break;
5792                        }
5793                    }
5794
5795                    array[dest--] = array[cursor1--];
5796
5797                    if (--length1 === 0) {
5798                        exit = true;
5799                        break;
5800                    }
5801
5802                    _minGallop--;
5803                } while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING);
5804
5805                if (exit) {
5806                    break;
5807                }
5808
5809                if (_minGallop < 0) {
5810                    _minGallop = 0;
5811                }
5812
5813                _minGallop += 2;
5814            }
5815
5816            minGallop = _minGallop;
5817
5818            if (minGallop < 1) {
5819                minGallop = 1;
5820            }
5821
5822            if (length2 === 1) {
5823                dest -= length1;
5824                cursor1 -= length1;
5825                customDest = dest + 1;
5826                customCursor = cursor1 + 1;
5827
5828                for (i = length1 - 1; i >= 0; i--) {
5829                    array[customDest + i] = array[customCursor + i];
5830                }
5831
5832                array[dest] = tmp[cursor2];
5833            }
5834            else if (length2 === 0) {
5835                throw new Error();
5836                // throw new Error('mergeHigh preconditions were not respected');
5837            }
5838            else {
5839                customCursor = dest - (length2 - 1);
5840                for (i = 0; i < length2; i++) {
5841                    array[customCursor + i] = tmp[i];
5842                }
5843            }
5844        }
5845
5846        this.mergeRuns = mergeRuns;
5847        this.forceMergeRuns = forceMergeRuns;
5848        this.pushRun = pushRun;
5849    }
5850
5851    function sort(array, compare, lo, hi) {
5852        if (!lo) {
5853            lo = 0;
5854        }
5855        if (!hi) {
5856            hi = array.length;
5857        }
5858
5859        var remaining = hi - lo;
5860
5861        if (remaining < 2) {
5862            return;
5863        }
5864
5865        var runLength = 0;
5866
5867        if (remaining < DEFAULT_MIN_MERGE) {
5868            runLength = makeAscendingRun(array, lo, hi, compare);
5869            binaryInsertionSort(array, lo, hi, lo + runLength, compare);
5870            return;
5871        }
5872
5873        var ts = new TimSort(array, compare);
5874
5875        var minRun = minRunLength(remaining);
5876
5877        do {
5878            runLength = makeAscendingRun(array, lo, hi, compare);
5879            if (runLength < minRun) {
5880                var force = remaining;
5881                if (force > minRun) {
5882                    force = minRun;
5883                }
5884
5885                binaryInsertionSort(array, lo, lo + force, lo + runLength, compare);
5886                runLength = force;
5887            }
5888
5889            ts.pushRun(lo, runLength);
5890            ts.mergeRuns();
5891
5892            remaining -= runLength;
5893            lo += runLength;
5894        } while (remaining !== 0);
5895
5896        ts.forceMergeRuns();
5897    }
5898
5899// Use timsort because in most case elements are partially sorted
5900// https://jsfiddle.net/pissang/jr4x7mdm/8/
5901    function shapeCompareFunc(a, b) {
5902        if (a.zlevel === b.zlevel) {
5903            if (a.z === b.z) {
5904                // if (a.z2 === b.z2) {
5905                //     // FIXME Slow has renderidx compare
5906                //     // http://stackoverflow.com/questions/20883421/sorting-in-javascript-should-every-compare-function-have-a-return-0-statement
5907                //     // https://github.com/v8/v8/blob/47cce544a31ed5577ffe2963f67acb4144ee0232/src/js/array.js#L1012
5908                //     return a.__renderidx - b.__renderidx;
5909                // }
5910                return a.z2 - b.z2;
5911            }
5912            return a.z - b.z;
5913        }
5914        return a.zlevel - b.zlevel;
5915    }
5916    /**
5917     * 鍐呭浠撳簱 (M)
5918     * @alias module:zrender/Storage
5919     * @constructor
5920     */
5921    var Storage = function () { // jshint ignore:line
5922        this._roots = [];
5923
5924        this._displayList = [];
5925
5926        this._displayListLen = 0;
5927    };
5928
5929    Storage.prototype = {
5930
5931        constructor: Storage,
5932
5933        /**
5934         * @param  {Function} cb
5935         *
5936         */
5937        traverse: function (cb, context) {
5938            for (var i = 0; i < this._roots.length; i++) {
5939                this._roots[i].traverse(cb, context);
5940            }
5941        },
5942
5943        /**
5944         * 杩斿洖鎵€鏈夊浘褰㈢殑缁樺埗闃熷垪
5945         * @param {boolean} [update=false] 鏄惁鍦ㄨ繑鍥炲墠鏇存柊璇ユ暟缁�
5946         * @param {boolean} [includeIgnore=false] 鏄惁鍖呭惈 ignore 鐨勬暟缁�, 鍦� update 涓� true 鐨勬椂鍊欐湁鏁�
5947         *
5948         * 璇﹁{@link module:zrender/graphic/Displayable.prototype.updateDisplayList}
5949         * @return {Array.<module:zrender/graphic/Displayable>}
5950         */
5951        getDisplayList: function (update, includeIgnore) {
5952            includeIgnore = includeIgnore || false;
5953            if (update) {
5954                this.updateDisplayList(includeIgnore);
5955            }
5956            return this._displayList;
5957        },
5958
5959        /**
5960         * 鏇存柊鍥惧舰鐨勭粯鍒堕槦鍒椼€�
5961         * 姣忔缁樺埗鍓嶉兘浼氳皟鐢紝璇ユ柟娉曚細鍏堟繁搴︿紭鍏堥亶鍘嗘暣涓爲锛屾洿鏂版墍鏈塆roup鍜孲hape鐨勫彉鎹㈠苟涓旀妸鎵€鏈夊彲瑙佺殑Shape淇濆瓨鍒版暟缁勪腑锛�
5962         * 鏈€鍚庢牴鎹粯鍒剁殑浼樺厛绾э紙zlevel > z > 鎻掑叆椤哄簭锛夋帓搴忓緱鍒扮粯鍒堕槦鍒�
5963         * @param {boolean} [includeIgnore=false] 鏄惁鍖呭惈 ignore 鐨勬暟缁�
5964         */
5965        updateDisplayList: function (includeIgnore) {
5966            this._displayListLen = 0;
5967
5968            var roots = this._roots;
5969            var displayList = this._displayList;
5970            for (var i = 0, len = roots.length; i < len; i++) {
5971                this._updateAndAddDisplayable(roots[i], null, includeIgnore);
5972            }
5973
5974            displayList.length = this._displayListLen;
5975
5976            env$1.canvasSupported && sort(displayList, shapeCompareFunc);
5977        },
5978
5979        _updateAndAddDisplayable: function (el, clipPaths, includeIgnore) {
5980
5981            if (el.ignore && !includeIgnore) {
5982                return;
5983            }
5984
5985            el.beforeUpdate();
5986
5987            if (el.__dirty) {
5988
5989                el.update();
5990
5991            }
5992
5993            el.afterUpdate();
5994
5995            var userSetClipPath = el.clipPath;
5996            if (userSetClipPath) {
5997
5998                // FIXME 鏁堢巼褰卞搷
5999                if (clipPaths) {
6000                    clipPaths = clipPaths.slice();
6001                }
6002                else {
6003                    clipPaths = [];
6004                }
6005
6006                var currentClipPath = userSetClipPath;
6007                var parentClipPath = el;
6008                // Recursively add clip path
6009                while (currentClipPath) {
6010                    // clipPath 鐨勫彉鎹㈡槸鍩轰簬浣跨敤杩欎釜 clipPath 鐨勫厓绱�
6011                    currentClipPath.parent = parentClipPath;
6012                    currentClipPath.updateTransform();
6013
6014                    clipPaths.push(currentClipPath);
6015
6016                    parentClipPath = currentClipPath;
6017                    currentClipPath = currentClipPath.clipPath;
6018                }
6019            }
6020
6021            if (el.isGroup) {
6022                var children = el._children;
6023
6024                for (var i = 0; i < children.length; i++) {
6025                    var child = children[i];
6026
6027                    // Force to mark as dirty if group is dirty
6028                    // FIXME __dirtyPath ?
6029                    if (el.__dirty) {
6030                        child.__dirty = true;
6031                    }
6032
6033                    this._updateAndAddDisplayable(child, clipPaths, includeIgnore);
6034                }
6035
6036                // Mark group clean here
6037                el.__dirty = false;
6038
6039            }
6040            else {
6041                el.__clipPaths = clipPaths;
6042
6043                this._displayList[this._displayListLen++] = el;
6044            }
6045        },
6046
6047        /**
6048         * 娣诲姞鍥惧舰(Shape)鎴栬€呯粍(Group)鍒版牴鑺傜偣
6049         * @param {module:zrender/Element} el
6050         */
6051        addRoot: function (el) {
6052            if (el.__storage === this) {
6053                return;
6054            }
6055
6056            if (el instanceof Group) {
6057                el.addChildrenToStorage(this);
6058            }
6059
6060            this.addToStorage(el);
6061            this._roots.push(el);
6062        },
6063
6064        /**
6065         * 鍒犻櫎鎸囧畾鐨勫浘褰�(Shape)鎴栬€呯粍(Group)
6066         * @param {string|Array.<string>} [el] 濡傛灉涓虹┖娓呯┖鏁翠釜Storage
6067         */
6068        delRoot: function (el) {
6069            if (el == null) {
6070                // 涓嶆寚瀹歟l娓呯┖
6071                for (var i = 0; i < this._roots.length; i++) {
6072                    var root = this._roots[i];
6073                    if (root instanceof Group) {
6074                        root.delChildrenFromStorage(this);
6075                    }
6076                }
6077
6078                this._roots = [];
6079                this._displayList = [];
6080                this._displayListLen = 0;
6081
6082                return;
6083            }
6084
6085            if (el instanceof Array) {
6086                for (var i = 0, l = el.length; i < l; i++) {
6087                    this.delRoot(el[i]);
6088                }
6089                return;
6090            }
6091
6092
6093            var idx = indexOf(this._roots, el);
6094            if (idx >= 0) {
6095                this.delFromStorage(el);
6096                this._roots.splice(idx, 1);
6097                if (el instanceof Group) {
6098                    el.delChildrenFromStorage(this);
6099                }
6100            }
6101        },
6102
6103        addToStorage: function (el) {
6104            if (el) {
6105                el.__storage = this;
6106                el.dirty(false);
6107            }
6108            return this;
6109        },
6110
6111        delFromStorage: function (el) {
6112            if (el) {
6113                el.__storage = null;
6114            }
6115
6116            return this;
6117        },
6118
6119        /**
6120         * 娓呯┖骞朵笖閲婃斁Storage
6121         */
6122        dispose: function () {
6123            this._renderList =
6124                this._roots = null;
6125        },
6126
6127        displayableSortFunc: shapeCompareFunc
6128    };
6129
6130    var SHADOW_PROPS = {
6131        'shadowBlur': 1,
6132        'shadowOffsetX': 1,
6133        'shadowOffsetY': 1,
6134        'textShadowBlur': 1,
6135        'textShadowOffsetX': 1,
6136        'textShadowOffsetY': 1,
6137        'textBoxShadowBlur': 1,
6138        'textBoxShadowOffsetX': 1,
6139        'textBoxShadowOffsetY': 1
6140    };
6141
6142    var fixShadow = function (ctx, propName, value) {
6143        if (SHADOW_PROPS.hasOwnProperty(propName)) {
6144            return value *= ctx.dpr;
6145        }
6146        return value;
6147    };
6148
6149    var STYLE_COMMON_PROPS = [
6150        ['shadowBlur', 0], ['shadowOffsetX', 0], ['shadowOffsetY', 0], ['shadowColor', '#000'],
6151        ['lineCap', 'butt'], ['lineJoin', 'miter'], ['miterLimit', 10]
6152    ];
6153
6154// var SHADOW_PROPS = STYLE_COMMON_PROPS.slice(0, 4);
6155// var LINE_PROPS = STYLE_COMMON_PROPS.slice(4);
6156
6157    var Style = function (opts, host) {
6158        this.extendFrom(opts, false);
6159        this.host = host;
6160    };
6161
6162    function createLinearGradient(ctx, obj, rect) {
6163        var x = obj.x == null ? 0 : obj.x;
6164        var x2 = obj.x2 == null ? 1 : obj.x2;
6165        var y = obj.y == null ? 0 : obj.y;
6166        var y2 = obj.y2 == null ? 0 : obj.y2;
6167
6168        if (!obj.global) {
6169            x = x * rect.width + rect.x;
6170            x2 = x2 * rect.width + rect.x;
6171            y = y * rect.height + rect.y;
6172            y2 = y2 * rect.height + rect.y;
6173        }
6174
6175        // Fix NaN when rect is Infinity
6176        x = isNaN(x) ? 0 : x;
6177        x2 = isNaN(x2) ? 1 : x2;
6178        y = isNaN(y) ? 0 : y;
6179        y2 = isNaN(y2) ? 0 : y2;
6180
6181        var canvasGradient = ctx.createLinearGradient(x, y, x2, y2);
6182
6183        return canvasGradient;
6184    }
6185
6186    function createRadialGradient(ctx, obj, rect) {
6187        var width = rect.width;
6188        var height = rect.height;
6189        var min = Math.min(width, height);
6190
6191        var x = obj.x == null ? 0.5 : obj.x;
6192        var y = obj.y == null ? 0.5 : obj.y;
6193        var r = obj.r == null ? 0.5 : obj.r;
6194        if (!obj.global) {
6195            x = x * width + rect.x;
6196            y = y * height + rect.y;
6197            r = r * min;
6198        }
6199
6200        var canvasGradient = ctx.createRadialGradient(x, y, 0, x, y, r);
6201
6202        return canvasGradient;
6203    }
6204
6205
6206    Style.prototype = {
6207
6208        constructor: Style,
6209
6210        /**
6211         * @type {module:zrender/graphic/Displayable}
6212         */
6213        host: null,
6214
6215        /**
6216         * @type {string}
6217         */
6218        fill: '#000',
6219
6220        /**
6221         * @type {string}
6222         */
6223        stroke: null,
6224
6225        /**
6226         * @type {number}
6227         */
6228        opacity: 1,
6229
6230        /**
6231         * @type {Array.<number>}
6232         */
6233        lineDash: null,
6234
6235        /**
6236         * @type {number}
6237         */
6238        lineDashOffset: 0,
6239
6240        /**
6241         * @type {number}
6242         */
6243        shadowBlur: 0,
6244
6245        /**
6246         * @type {number}
6247         */
6248        shadowOffsetX: 0,
6249
6250        /**
6251         * @type {number}
6252         */
6253        shadowOffsetY: 0,
6254
6255        /**
6256         * @type {number}
6257         */
6258        lineWidth: 1,
6259
6260        /**
6261         * If stroke ignore scale
6262         * @type {Boolean}
6263         */
6264        strokeNoScale: false,
6265
6266        // Bounding rect text configuration
6267        // Not affected by element transform
6268        /**
6269         * @type {string}
6270         */
6271        text: null,
6272
6273        /**
6274         * If `fontSize` or `fontFamily` exists, `font` will be reset by
6275         * `fontSize`, `fontStyle`, `fontWeight`, `fontFamily`.
6276         * So do not visit it directly in upper application (like echarts),
6277         * but use `contain/text#makeFont` instead.
6278         * @type {string}
6279         */
6280        font: null,
6281
6282        /**
6283         * The same as font. Use font please.
6284         * @deprecated
6285         * @type {string}
6286         */
6287        textFont: null,
6288
6289        /**
6290         * It helps merging respectively, rather than parsing an entire font string.
6291         * @type {string}
6292         */
6293        fontStyle: null,
6294
6295        /**
6296         * It helps merging respectively, rather than parsing an entire font string.
6297         * @type {string}
6298         */
6299        fontWeight: null,
6300
6301        /**
6302         * It helps merging respectively, rather than parsing an entire font string.
6303         * Should be 12 but not '12px'.
6304         * @type {number}
6305         */
6306        fontSize: null,
6307
6308        /**
6309         * It helps merging respectively, rather than parsing an entire font string.
6310         * @type {string}
6311         */
6312        fontFamily: null,
6313
6314        /**
6315         * Reserved for special functinality, like 'hr'.
6316         * @type {string}
6317         */
6318        textTag: null,
6319
6320        /**
6321         * @type {string}
6322         */
6323        textFill: '#000',
6324
6325        /**
6326         * @type {string}
6327         */
6328        textStroke: null,
6329
6330        /**
6331         * @type {number}
6332         */
6333        textWidth: null,
6334
6335        /**
6336         * Only for textBackground.
6337         * @type {number}
6338         */
6339        textHeight: null,
6340
6341        /**
6342         * textStroke may be set as some color as a default
6343         * value in upper applicaion, where the default value
6344         * of textStrokeWidth should be 0 to make sure that
6345         * user can choose to do not use text stroke.
6346         * @type {number}
6347         */
6348        textStrokeWidth: 0,
6349
6350        /**
6351         * @type {number}
6352         */
6353        textLineHeight: null,
6354
6355        /**
6356         * 'inside', 'left', 'right', 'top', 'bottom'
6357         * [x, y]
6358         * Based on x, y of rect.
6359         * @type {string|Array.<number>}
6360         * @default 'inside'
6361         */
6362        textPosition: 'inside',
6363
6364        /**
6365         * If not specified, use the boundingRect of a `displayable`.
6366         * @type {Object}
6367         */
6368        textRect: null,
6369
6370        /**
6371         * [x, y]
6372         * @type {Array.<number>}
6373         */
6374        textOffset: null,
6375
6376        /**
6377         * @type {string}
6378         */
6379        textAlign: null,
6380
6381        /**
6382         * @type {string}
6383         */
6384        textVerticalAlign: null,
6385
6386        /**
6387         * @type {number}
6388         */
6389        textDistance: 5,
6390
6391        /**
6392         * @type {string}
6393         */
6394        textShadowColor: 'transparent',
6395
6396        /**
6397         * @type {number}
6398         */
6399        textShadowBlur: 0,
6400
6401        /**
6402         * @type {number}
6403         */
6404        textShadowOffsetX: 0,
6405
6406        /**
6407         * @type {number}
6408         */
6409        textShadowOffsetY: 0,
6410
6411        /**
6412         * @type {string}
6413         */
6414        textBoxShadowColor: 'transparent',
6415
6416        /**
6417         * @type {number}
6418         */
6419        textBoxShadowBlur: 0,
6420
6421        /**
6422         * @type {number}
6423         */
6424        textBoxShadowOffsetX: 0,
6425
6426        /**
6427         * @type {number}
6428         */
6429        textBoxShadowOffsetY: 0,
6430
6431        /**
6432         * Whether transform text.
6433         * Only useful in Path and Image element
6434         * @type {boolean}
6435         */
6436        transformText: false,
6437
6438        /**
6439         * Text rotate around position of Path or Image
6440         * Only useful in Path and Image element and transformText is false.
6441         */
6442        textRotation: 0,
6443
6444        /**
6445         * Text origin of text rotation, like [10, 40].
6446         * Based on x, y of rect.
6447         * Useful in label rotation of circular symbol.
6448         * By default, this origin is textPosition.
6449         * Can be 'center'.
6450         * @type {string|Array.<number>}
6451         */
6452        textOrigin: null,
6453
6454        /**
6455         * @type {string}
6456         */
6457        textBackgroundColor: null,
6458
6459        /**
6460         * @type {string}
6461         */
6462        textBorderColor: null,
6463
6464        /**
6465         * @type {number}
6466         */
6467        textBorderWidth: 0,
6468
6469        /**
6470         * @type {number}
6471         */
6472        textBorderRadius: 0,
6473
6474        /**
6475         * Can be `2` or `[2, 4]` or `[2, 3, 4, 5]`
6476         * @type {number|Array.<number>}
6477         */
6478        textPadding: null,
6479
6480        /**
6481         * Text styles for rich text.
6482         * @type {Object}
6483         */
6484        rich: null,
6485
6486        /**
6487         * {outerWidth, outerHeight, ellipsis, placeholder}
6488         * @type {Object}
6489         */
6490        truncate: null,
6491
6492        /**
6493         * https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
6494         * @type {string}
6495         */
6496        blend: null,
6497
6498        /**
6499         * @param {CanvasRenderingContext2D} ctx
6500         */
6501        bind: function (ctx, el, prevEl) {
6502            var style = this;
6503            var prevStyle = prevEl && prevEl.style;
6504            var firstDraw = !prevStyle;
6505
6506            for (var i = 0; i < STYLE_COMMON_PROPS.length; i++) {
6507                var prop = STYLE_COMMON_PROPS[i];
6508                var styleName = prop[0];
6509
6510                if (firstDraw || style[styleName] !== prevStyle[styleName]) {
6511                    // FIXME Invalid property value will cause style leak from previous element.
6512                    ctx[styleName] =
6513                        fixShadow(ctx, styleName, style[styleName] || prop[1]);
6514                }
6515            }
6516
6517            if ((firstDraw || style.fill !== prevStyle.fill)) {
6518                ctx.fillStyle = style.fill;
6519            }
6520            if ((firstDraw || style.stroke !== prevStyle.stroke)) {
6521                ctx.strokeStyle = style.stroke;
6522            }
6523            if ((firstDraw || style.opacity !== prevStyle.opacity)) {
6524                ctx.globalAlpha = style.opacity == null ? 1 : style.opacity;
6525            }
6526
6527            if ((firstDraw || style.blend !== prevStyle.blend)) {
6528                ctx.globalCompositeOperation = style.blend || 'source-over';
6529            }
6530            if (this.hasStroke()) {
6531                var lineWidth = style.lineWidth;
6532                ctx.lineWidth = lineWidth / (
6533                    (this.strokeNoScale && el && el.getLineScale) ? el.getLineScale() : 1
6534                );
6535            }
6536        },
6537
6538        hasFill: function () {
6539            var fill = this.fill;
6540            return fill != null && fill !== 'none';
6541        },
6542
6543        hasStroke: function () {
6544            var stroke = this.stroke;
6545            return stroke != null && stroke !== 'none' && this.lineWidth > 0;
6546        },
6547
6548        /**
6549         * Extend from other style
6550         * @param {zrender/graphic/Style} otherStyle
6551         * @param {boolean} overwrite true: overwrirte any way.
6552         *                            false: overwrite only when !target.hasOwnProperty
6553         *                            others: overwrite when property is not null/undefined.
6554         */
6555        extendFrom: function (otherStyle, overwrite) {
6556            if (otherStyle) {
6557                for (var name in otherStyle) {
6558                    if (otherStyle.hasOwnProperty(name)
6559                        && (overwrite === true
6560                            || (
6561                                overwrite === false
6562                                    ? !this.hasOwnProperty(name)
6563                                    : otherStyle[name] != null
6564                            )
6565                        )
6566                    ) {
6567                        this[name] = otherStyle[name];
6568                    }
6569                }
6570            }
6571        },
6572
6573        /**
6574         * Batch setting style with a given object
6575         * @param {Object|string} obj
6576         * @param {*} [obj]
6577         */
6578        set: function (obj, value) {
6579            if (typeof obj === 'string') {
6580                this[obj] = value;
6581            }
6582            else {
6583                this.extendFrom(obj, true);
6584            }
6585        },
6586
6587        /**
6588         * Clone
6589         * @return {zrender/graphic/Style} [description]
6590         */
6591        clone: function () {
6592            var newStyle = new this.constructor();
6593            newStyle.extendFrom(this, true);
6594            return newStyle;
6595        },
6596
6597        getGradient: function (ctx, obj, rect) {
6598            var method = obj.type === 'radial' ? createRadialGradient : createLinearGradient;
6599            var canvasGradient = method(ctx, obj, rect);
6600            var colorStops = obj.colorStops;
6601            for (var i = 0; i < colorStops.length; i++) {
6602                canvasGradient.addColorStop(
6603                    colorStops[i].offset, colorStops[i].color
6604                );
6605            }
6606            return canvasGradient;
6607        }
6608
6609    };
6610
6611    var styleProto = Style.prototype;
6612    for (var i = 0; i < STYLE_COMMON_PROPS.length; i++) {
6613        var prop = STYLE_COMMON_PROPS[i];
6614        if (!(prop[0] in styleProto)) {
6615            styleProto[prop[0]] = prop[1];
6616        }
6617    }
6618
6619// Provide for others
6620    Style.getGradient = styleProto.getGradient;
6621
6622    var Pattern = function (image, repeat) {
6623        // Should do nothing more in this constructor. Because gradient can be
6624        // declard by `color: {image: ...}`, where this constructor will not be called.
6625
6626        this.image = image;
6627        this.repeat = repeat;
6628
6629        // Can be cloned
6630        this.type = 'pattern';
6631    };
6632
6633    Pattern.prototype.getCanvasPattern = function (ctx) {
6634        return ctx.createPattern(this.image, this.repeat || 'repeat');
6635    };
6636
6637    /**
6638     * @module zrender/Layer
6639     * @author pissang(https://www.github.com/pissang)
6640     */
6641
6642    function returnFalse() {
6643        return false;
6644    }
6645
6646    /**
6647     * 鍒涘缓dom
6648     *
6649     * @inner
6650     * @param {string} id dom id 寰呯敤
6651     * @param {Painter} painter painter instance
6652     * @param {number} number
6653     */
6654    function createDom(id, painter, dpr) {
6655        var newDom = createCanvas();
6656        var width = painter.getWidth();
6657        var height = painter.getHeight();
6658
6659        var newDomStyle = newDom.style;
6660        if (newDomStyle) {  // In node or some other non-browser environment
6661            newDomStyle.position = 'absolute';
6662            newDomStyle.left = 0;
6663            newDomStyle.top = 0;
6664            newDomStyle.width = width + 'px';
6665            newDomStyle.height = height + 'px';
6666
6667            newDom.setAttribute('data-zr-dom-id', id);
6668        }
6669
6670        newDom.width = width * dpr;
6671        newDom.height = height * dpr;
6672
6673        return newDom;
6674    }
6675
6676    /**
6677     * @alias module:zrender/Layer
6678     * @constructor
6679     * @extends module:zrender/mixin/Transformable
6680     * @param {string} id
6681     * @param {module:zrender/Painter} painter
6682     * @param {number} [dpr]
6683     */
6684    var Layer = function(id, painter, dpr) {
6685        var dom;
6686        dpr = dpr || devicePixelRatio;
6687        if (typeof id === 'string') {
6688            dom = createDom(id, painter, dpr);
6689        }
6690        // Not using isDom because in node it will return false
6691        else if (isObject$1(id)) {
6692            dom = id;
6693            id = dom.id;
6694        }
6695        this.id = id;
6696        this.dom = dom;
6697
6698        var domStyle = dom.style;
6699        if (domStyle) { // Not in node
6700            dom.onselectstart = returnFalse; // 閬垮厤椤甸潰閫変腑鐨勫按灏�
6701            domStyle['-webkit-user-select'] = 'none';
6702            domStyle['user-select'] = 'none';
6703            domStyle['-webkit-touch-callout'] = 'none';
6704            domStyle['-webkit-tap-highlight-color'] = 'rgba(0,0,0,0)';
6705            domStyle['padding'] = 0;
6706            domStyle['margin'] = 0;
6707            domStyle['border-width'] = 0;
6708        }
6709
6710        this.domBack = null;
6711        this.ctxBack = null;
6712
6713        this.painter = painter;
6714
6715        this.config = null;
6716
6717        // Configs
6718        /**
6719         * 姣忔娓呯┖鐢诲竷鐨勯鑹�
6720         * @type {string}
6721         * @default 0
6722         */
6723        this.clearColor = 0;
6724        /**
6725         * 鏄惁寮€鍚姩鎬佹ā绯�
6726         * @type {boolean}
6727         * @default false
6728         */
6729        this.motionBlur = false;
6730        /**
6731         * 鍦ㄥ紑鍚姩鎬佹ā绯婄殑鏃跺€欎娇鐢紝涓庝笂涓€甯ф贩鍚堢殑alpha鍊硷紝鍊艰秺澶у熬杩硅秺鏄庢樉
6732         * @type {number}
6733         * @default 0.7
6734         */
6735        this.lastFrameAlpha = 0.7;
6736
6737        /**
6738         * Layer dpr
6739         * @type {number}
6740         */
6741        this.dpr = dpr;
6742    };
6743
6744    Layer.prototype = {
6745
6746        constructor: Layer,
6747
6748        __dirty: true,
6749
6750        __used: false,
6751
6752        __drawIndex: 0,
6753        __startIndex: 0,
6754        __endIndex: 0,
6755
6756        incremental: false,
6757
6758        getElementCount: function () {
6759            return this.__endIndex - this.__startIndex;
6760        },
6761
6762        initContext: function () {
6763            this.ctx = this.dom.getContext('2d');
6764            this.ctx.dpr = this.dpr;
6765        },
6766
6767        createBackBuffer: function () {
6768            var dpr = this.dpr;
6769
6770            this.domBack = createDom('back-' + this.id, this.painter, dpr);
6771            this.ctxBack = this.domBack.getContext('2d');
6772
6773            if (dpr != 1) {
6774                this.ctxBack.scale(dpr, dpr);
6775            }
6776        },
6777
6778        /**
6779         * @param  {number} width
6780         * @param  {number} height
6781         */
6782        resize: function (width, height) {
6783            var dpr = this.dpr;
6784
6785            var dom = this.dom;
6786            var domStyle = dom.style;
6787            var domBack = this.domBack;
6788
6789            if (domStyle) {
6790                domStyle.width = width + 'px';
6791                domStyle.height = height + 'px';
6792            }
6793
6794            dom.width = width * dpr;
6795            dom.height = height * dpr;
6796
6797            if (domBack) {
6798                domBack.width = width * dpr;
6799                domBack.height = height * dpr;
6800
6801                if (dpr != 1) {
6802                    this.ctxBack.scale(dpr, dpr);
6803                }
6804            }
6805        },
6806
6807        /**
6808         * 娓呯┖璇ュ眰鐢诲竷
6809         * @param {boolean} [clearAll]=false Clear all with out motion blur
6810         * @param {Color} [clearColor]
6811         */
6812        clear: function (clearAll, clearColor) {
6813            var dom = this.dom;
6814            var ctx = this.ctx;
6815            var width = dom.width;
6816            var height = dom.height;
6817
6818            var clearColor = clearColor || this.clearColor;
6819            var haveMotionBLur = this.motionBlur && !clearAll;
6820            var lastFrameAlpha = this.lastFrameAlpha;
6821
6822            var dpr = this.dpr;
6823
6824            if (haveMotionBLur) {
6825                if (!this.domBack) {
6826                    this.createBackBuffer();
6827                }
6828
6829                this.ctxBack.globalCompositeOperation = 'copy';
6830                this.ctxBack.drawImage(
6831                    dom, 0, 0,
6832                    width / dpr,
6833                    height / dpr
6834                );
6835            }
6836
6837            ctx.clearRect(0, 0, width, height);
6838            if (clearColor && clearColor !== 'transparent') {
6839                var clearColorGradientOrPattern;
6840                // Gradient
6841                if (clearColor.colorStops) {
6842                    // Cache canvas gradient
6843                    clearColorGradientOrPattern = clearColor.__canvasGradient || Style.getGradient(ctx, clearColor, {
6844                        x: 0,
6845                        y: 0,
6846                        width: width,
6847                        height: height
6848                    });
6849
6850                    clearColor.__canvasGradient = clearColorGradientOrPattern;
6851                }
6852                // Pattern
6853                else if (clearColor.image) {
6854                    clearColorGradientOrPattern = Pattern.prototype.getCanvasPattern.call(clearColor, ctx);
6855                }
6856                ctx.save();
6857                ctx.fillStyle = clearColorGradientOrPattern || clearColor;
6858                ctx.fillRect(0, 0, width, height);
6859                ctx.restore();
6860            }
6861
6862            if (haveMotionBLur) {
6863                var domBack = this.domBack;
6864                ctx.save();
6865                ctx.globalAlpha = lastFrameAlpha;
6866                ctx.drawImage(domBack, 0, 0, width, height);
6867                ctx.restore();
6868            }
6869        }
6870    };
6871
6872    var requestAnimationFrame = (
6873        typeof window !== 'undefined'
6874        && (
6875            (window.requestAnimationFrame && window.requestAnimationFrame.bind(window))
6876            // https://github.com/ecomfe/zrender/issues/189#issuecomment-224919809
6877            || (window.msRequestAnimationFrame && window.msRequestAnimationFrame.bind(window))
6878            || window.mozRequestAnimationFrame
6879            || window.webkitRequestAnimationFrame
6880        )
6881    ) || function (func) {
6882        setTimeout(func, 16);
6883    };
6884
6885    var globalImageCache = new LRU(50);
6886
6887    /**
6888     * @param {string|HTMLImageElement|HTMLCanvasElement|Canvas} newImageOrSrc
6889     * @return {HTMLImageElement|HTMLCanvasElement|Canvas} image
6890     */
6891    function findExistImage(newImageOrSrc) {
6892        if (typeof newImageOrSrc === 'string') {
6893            var cachedImgObj = globalImageCache.get(newImageOrSrc);
6894            return cachedImgObj && cachedImgObj.image;
6895        }
6896        else {
6897            return newImageOrSrc;
6898        }
6899    }
6900
6901    /**
6902     * Caution: User should cache loaded images, but not just count on LRU.
6903     * Consider if required images more than LRU size, will dead loop occur?
6904     *
6905     * @param {string|HTMLImageElement|HTMLCanvasElement|Canvas} newImageOrSrc
6906     * @param {HTMLImageElement|HTMLCanvasElement|Canvas} image Existent image.
6907     * @param {module:zrender/Element} [hostEl] For calling `dirty`.
6908     * @param {Function} [cb] params: (image, cbPayload)
6909     * @param {Object} [cbPayload] Payload on cb calling.
6910     * @return {HTMLImageElement|HTMLCanvasElement|Canvas} image
6911     */
6912    function createOrUpdateImage(newImageOrSrc, image, hostEl, cb, cbPayload) {
6913        if (!newImageOrSrc) {
6914            return image;
6915        }
6916        else if (typeof newImageOrSrc === 'string') {
6917
6918            // Image should not be loaded repeatly.
6919            if ((image && image.__zrImageSrc === newImageOrSrc) || !hostEl) {
6920                return image;
6921            }
6922
6923            // Only when there is no existent image or existent image src
6924            // is different, this method is responsible for load.
6925            var cachedImgObj = globalImageCache.get(newImageOrSrc);
6926
6927            var pendingWrap = {hostEl: hostEl, cb: cb, cbPayload: cbPayload};
6928
6929            if (cachedImgObj) {
6930                image = cachedImgObj.image;
6931                !isImageReady(image) && cachedImgObj.pending.push(pendingWrap);
6932            }
6933            else {
6934                !image && (image = new Image());
6935                image.onload = imageOnLoad;
6936
6937                globalImageCache.put(
6938                    newImageOrSrc,
6939                    image.__cachedImgObj = {
6940                        image: image,
6941                        pending: [pendingWrap]
6942                    }
6943                );
6944
6945                image.src = image.__zrImageSrc = newImageOrSrc;
6946            }
6947
6948            return image;
6949        }
6950        // newImageOrSrc is an HTMLImageElement or HTMLCanvasElement or Canvas
6951        else {
6952            return newImageOrSrc;
6953        }
6954    }
6955
6956    function imageOnLoad() {
6957        var cachedImgObj = this.__cachedImgObj;
6958        this.onload = this.__cachedImgObj = null;
6959
6960        for (var i = 0; i < cachedImgObj.pending.length; i++) {
6961            var pendingWrap = cachedImgObj.pending[i];
6962            var cb = pendingWrap.cb;
6963            cb && cb(this, pendingWrap.cbPayload);
6964            pendingWrap.hostEl.dirty();
6965        }
6966        cachedImgObj.pending.length = 0;
6967    }
6968
6969    function isImageReady(image) {
6970        return image && image.width && image.height;
6971    }
6972
6973    var textWidthCache = {};
6974    var textWidthCacheCounter = 0;
6975
6976    var TEXT_CACHE_MAX = 5000;
6977    var STYLE_REG = /\{([a-zA-Z0-9_]+)\|([^}]*)\}/g;
6978
6979    var DEFAULT_FONT = '12px sans-serif';
6980
6981// Avoid assign to an exported variable, for transforming to cjs.
6982    var methods$1 = {};
6983
6984    function $override$1(name, fn) {
6985        methods$1[name] = fn;
6986    }
6987
6988    /**
6989     * @public
6990     * @param {string} text
6991     * @param {string} font
6992     * @return {number} width
6993     */
6994    function getWidth(text, font) {
6995        font = font || DEFAULT_FONT;
6996        var key = text + ':' + font;
6997        if (textWidthCache[key]) {
6998            return textWidthCache[key];
6999        }
7000
7001        var textLines = (text + '').split('\n');
7002        var width = 0;
7003
7004        for (var i = 0, l = textLines.length; i < l; i++) {
7005            // textContain.measureText may be overrided in SVG or VML
7006            width = Math.max(measureText(textLines[i], font).width, width);
7007        }
7008
7009        if (textWidthCacheCounter > TEXT_CACHE_MAX) {
7010            textWidthCacheCounter = 0;
7011            textWidthCache = {};
7012        }
7013        textWidthCacheCounter++;
7014        textWidthCache[key] = width;
7015
7016        return width;
7017    }
7018
7019    /**
7020     * @public
7021     * @param {string} text
7022     * @param {string} font
7023     * @param {string} [textAlign='left']
7024     * @param {string} [textVerticalAlign='top']
7025     * @param {Array.<number>} [textPadding]
7026     * @param {Object} [rich]
7027     * @param {Object} [truncate]
7028     * @return {Object} {x, y, width, height, lineHeight}
7029     */
7030    function getBoundingRect(text, font, textAlign, textVerticalAlign, textPadding, rich, truncate) {
7031        return rich
7032            ? getRichTextRect(text, font, textAlign, textVerticalAlign, textPadding, rich, truncate)
7033            : getPlainTextRect(text, font, textAlign, textVerticalAlign, textPadding, truncate);
7034    }
7035
7036    function getPlainTextRect(text, font, textAlign, textVerticalAlign, textPadding, truncate) {
7037        var contentBlock = parsePlainText(text, font, textPadding, truncate);
7038        var outerWidth = getWidth(text, font);
7039        if (textPadding) {
7040            outerWidth += textPadding[1] + textPadding[3];
7041        }
7042        var outerHeight = contentBlock.outerHeight;
7043
7044        var x = adjustTextX(0, outerWidth, textAlign);
7045        var y = adjustTextY(0, outerHeight, textVerticalAlign);
7046
7047        var rect = new BoundingRect(x, y, outerWidth, outerHeight);
7048        rect.lineHeight = contentBlock.lineHeight;
7049
7050        return rect;
7051    }
7052
7053    function getRichTextRect(text, font, textAlign, textVerticalAlign, textPadding, rich, truncate) {
7054        var contentBlock = parseRichText(text, {
7055            rich: rich,
7056            truncate: truncate,
7057            font: font,
7058            textAlign: textAlign,
7059            textPadding: textPadding
7060        });
7061        var outerWidth = contentBlock.outerWidth;
7062        var outerHeight = contentBlock.outerHeight;
7063
7064        var x = adjustTextX(0, outerWidth, textAlign);
7065        var y = adjustTextY(0, outerHeight, textVerticalAlign);
7066
7067        return new BoundingRect(x, y, outerWidth, outerHeight);
7068    }
7069
7070    /**
7071     * @public
7072     * @param {number} x
7073     * @param {number} width
7074     * @param {string} [textAlign='left']
7075     * @return {number} Adjusted x.
7076     */
7077    function adjustTextX(x, width, textAlign) {
7078        // FIXME Right to left language
7079        if (textAlign === 'right') {
7080            x -= width;
7081        }
7082        else if (textAlign === 'center') {
7083            x -= width / 2;
7084        }
7085        return x;
7086    }
7087
7088    /**
7089     * @public
7090     * @param {number} y
7091     * @param {number} height
7092     * @param {string} [textVerticalAlign='top']
7093     * @return {number} Adjusted y.
7094     */
7095    function adjustTextY(y, height, textVerticalAlign) {
7096        if (textVerticalAlign === 'middle') {
7097            y -= height / 2;
7098        }
7099        else if (textVerticalAlign === 'bottom') {
7100            y -= height;
7101        }
7102        return y;
7103    }
7104
7105    /**
7106     * @public
7107     * @param {stirng} textPosition
7108     * @param {Object} rect {x, y, width, height}
7109     * @param {number} distance
7110     * @return {Object} {x, y, textAlign, textVerticalAlign}
7111     */
7112    function adjustTextPositionOnRect(textPosition, rect, distance) {
7113
7114        var x = rect.x;
7115        var y = rect.y;
7116
7117        var height = rect.height;
7118        var width = rect.width;
7119        var halfHeight = height / 2;
7120
7121        var textAlign = 'left';
7122        var textVerticalAlign = 'top';
7123
7124        switch (textPosition) {
7125            case 'left':
7126                x -= distance;
7127                y += halfHeight;
7128                textAlign = 'right';
7129                textVerticalAlign = 'middle';
7130                break;
7131            case 'right':
7132                x += distance + width;
7133                y += halfHeight;
7134                textVerticalAlign = 'middle';
7135                break;
7136            case 'top':
7137                x += width / 2;
7138                y -= distance;
7139                textAlign = 'center';
7140                textVerticalAlign = 'bottom';
7141                break;
7142            case 'bottom':
7143                x += width / 2;
7144                y += height + distance;
7145                textAlign = 'center';
7146                break;
7147            case 'inside':
7148                x += width / 2;
7149                y += halfHeight;
7150                textAlign = 'center';
7151                textVerticalAlign = 'middle';
7152                break;
7153            case 'insideLeft':
7154                x += distance;
7155                y += halfHeight;
7156                textVerticalAlign = 'middle';
7157                break;
7158            case 'insideRight':
7159                x += width - distance;
7160                y += halfHeight;
7161                textAlign = 'right';
7162                textVerticalAlign = 'middle';
7163                break;
7164            case 'insideTop':
7165                x += width / 2;
7166                y += distance;
7167                textAlign = 'center';
7168                break;
7169            case 'insideBottom':
7170                x += width / 2;
7171                y += height - distance;
7172                textAlign = 'center';
7173                textVerticalAlign = 'bottom';
7174                break;
7175            case 'insideTopLeft':
7176                x += distance;
7177                y += distance;
7178                break;
7179            case 'insideTopRight':
7180                x += width - distance;
7181                y += distance;
7182                textAlign = 'right';
7183                break;
7184            case 'insideBottomLeft':
7185                x += distance;
7186                y += height - distance;
7187                textVerticalAlign = 'bottom';
7188                break;
7189            case 'insideBottomRight':
7190                x += width - distance;
7191                y += height - distance;
7192                textAlign = 'right';
7193                textVerticalAlign = 'bottom';
7194                break;
7195        }
7196
7197        return {
7198            x: x,
7199            y: y,
7200            textAlign: textAlign,
7201            textVerticalAlign: textVerticalAlign
7202        };
7203    }
7204
7205    /**
7206     * Show ellipsis if overflow.
7207     *
7208     * @public
7209     * @param  {string} text
7210     * @param  {string} containerWidth
7211     * @param  {string} font
7212     * @param  {number} [ellipsis='...']
7213     * @param  {Object} [options]
7214     * @param  {number} [options.maxIterations=3]
7215     * @param  {number} [options.minChar=0] If truncate result are less
7216     *                  then minChar, ellipsis will not show, which is
7217     *                  better for user hint in some cases.
7218     * @param  {number} [options.placeholder=''] When all truncated, use the placeholder.
7219     * @return {string}
7220     */
7221    function truncateText(text, containerWidth, font, ellipsis, options) {
7222        if (!containerWidth) {
7223            return '';
7224        }
7225
7226        var textLines = (text + '').split('\n');
7227        options = prepareTruncateOptions(containerWidth, font, ellipsis, options);
7228
7229        // FIXME
7230        // It is not appropriate that every line has '...' when truncate multiple lines.
7231        for (var i = 0, len = textLines.length; i < len; i++) {
7232            textLines[i] = truncateSingleLine(textLines[i], options);
7233        }
7234
7235        return textLines.join('\n');
7236    }
7237
7238    function prepareTruncateOptions(containerWidth, font, ellipsis, options) {
7239        options = extend({}, options);
7240
7241        options.font = font;
7242        var ellipsis = retrieve2(ellipsis, '...');
7243        options.maxIterations = retrieve2(options.maxIterations, 2);
7244        var minChar = options.minChar = retrieve2(options.minChar, 0);
7245        // FIXME
7246        // Other languages?
7247        options.cnCharWidth = getWidth('鍥�', font);
7248        // FIXME
7249        // Consider proportional font?
7250        var ascCharWidth = options.ascCharWidth = getWidth('a', font);
7251        options.placeholder = retrieve2(options.placeholder, '');
7252
7253        // Example 1: minChar: 3, text: 'asdfzxcv', truncate result: 'asdf', but not: 'a...'.
7254        // Example 2: minChar: 3, text: '缁村害', truncate result: '缁�', but not: '...'.
7255        var contentWidth = containerWidth = Math.max(0, containerWidth - 1); // Reserve some gap.
7256        for (var i = 0; i < minChar && contentWidth >= ascCharWidth; i++) {
7257            contentWidth -= ascCharWidth;
7258        }
7259
7260        var ellipsisWidth = getWidth(ellipsis);
7261        if (ellipsisWidth > contentWidth) {
7262            ellipsis = '';
7263            ellipsisWidth = 0;
7264        }
7265
7266        contentWidth = containerWidth - ellipsisWidth;
7267
7268        options.ellipsis = ellipsis;
7269        options.ellipsisWidth = ellipsisWidth;
7270        options.contentWidth = contentWidth;
7271        options.containerWidth = containerWidth;
7272
7273        return options;
7274    }
7275
7276    function truncateSingleLine(textLine, options) {
7277        var containerWidth = options.containerWidth;
7278        var font = options.font;
7279        var contentWidth = options.contentWidth;
7280
7281        if (!containerWidth) {
7282            return '';
7283        }
7284
7285        var lineWidth = getWidth(textLine, font);
7286
7287        if (lineWidth <= containerWidth) {
7288            return textLine;
7289        }
7290
7291        for (var j = 0;; j++) {
7292            if (lineWidth <= contentWidth || j >= options.maxIterations) {
7293                textLine += options.ellipsis;
7294                break;
7295            }
7296
7297            var subLength = j === 0
7298                ? estimateLength(textLine, contentWidth, options.ascCharWidth, options.cnCharWidth)
7299                : lineWidth > 0
7300                    ? Math.floor(textLine.length * contentWidth / lineWidth)
7301                    : 0;
7302
7303            textLine = textLine.substr(0, subLength);
7304            lineWidth = getWidth(textLine, font);
7305        }
7306
7307        if (textLine === '') {
7308            textLine = options.placeholder;
7309        }
7310
7311        return textLine;
7312    }
7313
7314    function estimateLength(text, contentWidth, ascCharWidth, cnCharWidth) {
7315        var width = 0;
7316        var i = 0;
7317        for (var len = text.length; i < len && width < contentWidth; i++) {
7318            var charCode = text.charCodeAt(i);
7319            width += (0 <= charCode && charCode <= 127) ? ascCharWidth : cnCharWidth;
7320        }
7321        return i;
7322    }
7323
7324    /**
7325     * @public
7326     * @param {string} font
7327     * @return {number} line height
7328     */
7329    function getLineHeight(font) {
7330        // FIXME A rough approach.
7331        return getWidth('鍥�', font);
7332    }
7333
7334    /**
7335     * @public
7336     * @param {string} text
7337     * @param {string} font
7338     * @return {Object} width
7339     */
7340    function measureText(text, font) {
7341        return methods$1.measureText(text, font);
7342    }
7343
7344// Avoid assign to an exported variable, for transforming to cjs.
7345    methods$1.measureText = function (text, font) {
7346        var ctx = getContext();
7347        ctx.font = font || DEFAULT_FONT;
7348        return ctx.measureText(text);
7349    };
7350
7351    /**
7352     * @public
7353     * @param {string} text
7354     * @param {string} font
7355     * @param {Object} [truncate]
7356     * @return {Object} block: {lineHeight, lines, height, outerHeight}
7357     *  Notice: for performance, do not calculate outerWidth util needed.
7358     */
7359    function parsePlainText(text, font, padding, truncate) {
7360        text != null && (text += '');
7361
7362        var lineHeight = getLineHeight(font);
7363        var lines = text ? text.split('\n') : [];
7364        var height = lines.length * lineHeight;
7365        var outerHeight = height;
7366
7367        if (padding) {
7368            outerHeight += padding[0] + padding[2];
7369        }
7370
7371        if (text && truncate) {
7372            var truncOuterHeight = truncate.outerHeight;
7373            var truncOuterWidth = truncate.outerWidth;
7374            if (truncOuterHeight != null && outerHeight > truncOuterHeight) {
7375                text = '';
7376                lines = [];
7377            }
7378            else if (truncOuterWidth != null) {
7379                var options = prepareTruncateOptions(
7380                    truncOuterWidth - (padding ? padding[1] + padding[3] : 0),
7381                    font,
7382                    truncate.ellipsis,
7383                    {minChar: truncate.minChar, placeholder: truncate.placeholder}
7384                );
7385
7386                // FIXME
7387                // It is not appropriate that every line has '...' when truncate multiple lines.
7388                for (var i = 0, len = lines.length; i < len; i++) {
7389                    lines[i] = truncateSingleLine(lines[i], options);
7390                }
7391            }
7392        }
7393
7394        return {
7395            lines: lines,
7396            height: height,
7397            outerHeight: outerHeight,
7398            lineHeight: lineHeight
7399        };
7400    }
7401
7402    /**
7403     * For example: 'some text {a|some text}other text{b|some text}xxx{c|}xxx'
7404     * Also consider 'bbbb{a|xxx\nzzz}xxxx\naaaa'.
7405     *
7406     * @public
7407     * @param {string} text
7408     * @param {Object} style
7409     * @return {Object} block
7410     * {
7411 *      width,
7412 *      height,
7413 *      lines: [{
7414 *          lineHeight,
7415 *          width,
7416 *          tokens: [[{
7417 *              styleName,
7418 *              text,
7419 *              width,      // include textPadding
7420 *              height,     // include textPadding
7421 *              textWidth, // pure text width
7422 *              textHeight, // pure text height
7423 *              lineHeihgt,
7424 *              font,
7425 *              textAlign,
7426 *              textVerticalAlign
7427 *          }], [...], ...]
7428 *      }, ...]
7429 * }
7430     * If styleName is undefined, it is plain text.
7431     */
7432    function parseRichText(text, style) {
7433        var contentBlock = {lines: [], width: 0, height: 0};
7434
7435        text != null && (text += '');
7436        if (!text) {
7437            return contentBlock;
7438        }
7439
7440        var lastIndex = STYLE_REG.lastIndex = 0;
7441        var result;
7442        while ((result = STYLE_REG.exec(text)) != null) {
7443            var matchedIndex = result.index;
7444            if (matchedIndex > lastIndex) {
7445                pushTokens(contentBlock, text.substring(lastIndex, matchedIndex));
7446            }
7447            pushTokens(contentBlock, result[2], result[1]);
7448            lastIndex = STYLE_REG.lastIndex;
7449        }
7450
7451        if (lastIndex < text.length) {
7452            pushTokens(contentBlock, text.substring(lastIndex, text.length));
7453        }
7454
7455        var lines = contentBlock.lines;
7456        var contentHeight = 0;
7457        var contentWidth = 0;
7458        // For `textWidth: 100%`
7459        var pendingList = [];
7460
7461        var stlPadding = style.textPadding;
7462
7463        var truncate = style.truncate;
7464        var truncateWidth = truncate && truncate.outerWidth;
7465        var truncateHeight = truncate && truncate.outerHeight;
7466        if (stlPadding) {
7467            truncateWidth != null && (truncateWidth -= stlPadding[1] + stlPadding[3]);
7468            truncateHeight != null && (truncateHeight -= stlPadding[0] + stlPadding[2]);
7469        }
7470
7471        // Calculate layout info of tokens.
7472        for (var i = 0; i < lines.length; i++) {
7473            var line = lines[i];
7474            var lineHeight = 0;
7475            var lineWidth = 0;
7476
7477            for (var j = 0; j < line.tokens.length; j++) {
7478                var token = line.tokens[j];
7479                var tokenStyle = token.styleName && style.rich[token.styleName] || {};
7480                // textPadding should not inherit from style.
7481                var textPadding = token.textPadding = tokenStyle.textPadding;
7482
7483                // textFont has been asigned to font by `normalizeStyle`.
7484                var font = token.font = tokenStyle.font || style.font;
7485
7486                // textHeight can be used when textVerticalAlign is specified in token.
7487                var tokenHeight = token.textHeight = retrieve2(
7488                    // textHeight should not be inherited, consider it can be specified
7489                    // as box height of the block.
7490                    tokenStyle.textHeight, getLineHeight(font)
7491                );
7492                textPadding && (tokenHeight += textPadding[0] + textPadding[2]);
7493                token.height = tokenHeight;
7494                token.lineHeight = retrieve3(
7495                    tokenStyle.textLineHeight, style.textLineHeight, tokenHeight
7496                );
7497
7498                token.textAlign = tokenStyle && tokenStyle.textAlign || style.textAlign;
7499                token.textVerticalAlign = tokenStyle && tokenStyle.textVerticalAlign || 'middle';
7500
7501                if (truncateHeight != null && contentHeight + token.lineHeight > truncateHeight) {
7502                    return {lines: [], width: 0, height: 0};
7503                }
7504
7505                token.textWidth = getWidth(token.text, font);
7506                var tokenWidth = tokenStyle.textWidth;
7507                var tokenWidthNotSpecified = tokenWidth == null || tokenWidth === 'auto';
7508
7509                // Percent width, can be `100%`, can be used in drawing separate
7510                // line when box width is needed to be auto.
7511                if (typeof tokenWidth === 'string' && tokenWidth.charAt(tokenWidth.length - 1) === '%') {
7512                    token.percentWidth = tokenWidth;
7513                    pendingList.push(token);
7514                    tokenWidth = 0;
7515                    // Do not truncate in this case, because there is no user case
7516                    // and it is too complicated.
7517                }
7518                else {
7519                    if (tokenWidthNotSpecified) {
7520                        tokenWidth = token.textWidth;
7521
7522                        // FIXME: If image is not loaded and textWidth is not specified, calling
7523                        // `getBoundingRect()` will not get correct result.
7524                        var textBackgroundColor = tokenStyle.textBackgroundColor;
7525                        var bgImg = textBackgroundColor && textBackgroundColor.image;
7526
7527                        // Use cases:
7528                        // (1) If image is not loaded, it will be loaded at render phase and call
7529                        // `dirty()` and `textBackgroundColor.image` will be replaced with the loaded
7530                        // image, and then the right size will be calculated here at the next tick.
7531                        // See `graphic/helper/text.js`.
7532                        // (2) If image loaded, and `textBackgroundColor.image` is image src string,
7533                        // use `imageHelper.findExistImage` to find cached image.
7534                        // `imageHelper.findExistImage` will always be called here before
7535                        // `imageHelper.createOrUpdateImage` in `graphic/helper/text.js#renderRichText`
7536                        // which ensures that image will not be rendered before correct size calcualted.
7537                        if (bgImg) {
7538                            bgImg = findExistImage(bgImg);
7539                            if (isImageReady(bgImg)) {
7540                                tokenWidth = Math.max(tokenWidth, bgImg.width * tokenHeight / bgImg.height);
7541                            }
7542                        }
7543                    }
7544
7545                    var paddingW = textPadding ? textPadding[1] + textPadding[3] : 0;
7546                    tokenWidth += paddingW;
7547
7548                    var remianTruncWidth = truncateWidth != null ? truncateWidth - lineWidth : null;
7549
7550                    if (remianTruncWidth != null && remianTruncWidth < tokenWidth) {
7551                        if (!tokenWidthNotSpecified || remianTruncWidth < paddingW) {
7552                            token.text = '';
7553                            token.textWidth = tokenWidth = 0;
7554                        }
7555                        else {
7556                            token.text = truncateText(
7557                                token.text, remianTruncWidth - paddingW, font, truncate.ellipsis,
7558                                {minChar: truncate.minChar}
7559                            );
7560                            token.textWidth = getWidth(token.text, font);
7561                            tokenWidth = token.textWidth + paddingW;
7562                        }
7563                    }
7564                }
7565
7566                lineWidth += (token.width = tokenWidth);
7567                tokenStyle && (lineHeight = Math.max(lineHeight, token.lineHeight));
7568            }
7569
7570            line.width = lineWidth;
7571            line.lineHeight = lineHeight;
7572            contentHeight += lineHeight;
7573            contentWidth = Math.max(contentWidth, lineWidth);
7574        }
7575
7576        contentBlock.outerWidth = contentBlock.width = retrieve2(style.textWidth, contentWidth);
7577        contentBlock.outerHeight = contentBlock.height = retrieve2(style.textHeight, contentHeight);
7578
7579        if (stlPadding) {
7580            contentBlock.outerWidth += stlPadding[1] + stlPadding[3];
7581            contentBlock.outerHeight += stlPadding[0] + stlPadding[2];
7582        }
7583
7584        for (var i = 0; i < pendingList.length; i++) {
7585            var token = pendingList[i];
7586            var percentWidth = token.percentWidth;
7587            // Should not base on outerWidth, because token can not be placed out of padding.
7588            token.width = parseInt(percentWidth, 10) / 100 * contentWidth;
7589        }
7590
7591        return contentBlock;
7592    }
7593
7594    function pushTokens(block, str, styleName) {
7595        var isEmptyStr = str === '';
7596        var strs = str.split('\n');
7597        var lines = block.lines;
7598
7599        for (var i = 0; i < strs.length; i++) {
7600            var text = strs[i];
7601            var token = {
7602                styleName: styleName,
7603                text: text,
7604                isLineHolder: !text && !isEmptyStr
7605            };
7606
7607            // The first token should be appended to the last line.
7608            if (!i) {
7609                var tokens = (lines[lines.length - 1] || (lines[0] = {tokens: []})).tokens;
7610
7611                // Consider cases:
7612                // (1) ''.split('\n') => ['', '\n', ''], the '' at the first item
7613                // (which is a placeholder) should be replaced by new token.
7614                // (2) A image backage, where token likes {a|}.
7615                // (3) A redundant '' will affect textAlign in line.
7616                // (4) tokens with the same tplName should not be merged, because
7617                // they should be displayed in different box (with border and padding).
7618                var tokensLen = tokens.length;
7619                (tokensLen === 1 && tokens[0].isLineHolder)
7620                    ? (tokens[0] = token)
7621                    // Consider text is '', only insert when it is the "lineHolder" or
7622                    // "emptyStr". Otherwise a redundant '' will affect textAlign in line.
7623                    : ((text || !tokensLen || isEmptyStr) && tokens.push(token));
7624            }
7625            // Other tokens always start a new line.
7626            else {
7627                // If there is '', insert it as a placeholder.
7628                lines.push({tokens: [token]});
7629            }
7630        }
7631    }
7632
7633    function makeFont(style) {
7634        // FIXME in node-canvas fontWeight is before fontStyle
7635        // Use `fontSize` `fontFamily` to check whether font properties are defined.
7636        var font = (style.fontSize || style.fontFamily) && [
7637            style.fontStyle,
7638            style.fontWeight,
7639            (style.fontSize || 12) + 'px',
7640            // If font properties are defined, `fontFamily` should not be ignored.
7641            style.fontFamily || 'sans-serif'
7642        ].join(' ');
7643        return font && trim(font) || style.textFont || style.font;
7644    }
7645
7646    function buildPath(ctx, shape) {
7647        var x = shape.x;
7648        var y = shape.y;
7649        var width = shape.width;
7650        var height = shape.height;
7651        var r = shape.r;
7652        var r1;
7653        var r2;
7654        var r3;
7655        var r4;
7656
7657        // Convert width and height to positive for better borderRadius
7658        if (width < 0) {
7659            x = x + width;
7660            width = -width;
7661        }
7662        if (height < 0) {
7663            y = y + height;
7664            height = -height;
7665        }
7666
7667        if (typeof r === 'number') {
7668            r1 = r2 = r3 = r4 = r;
7669        }
7670        else if (r instanceof Array) {
7671            if (r.length === 1) {
7672                r1 = r2 = r3 = r4 = r[0];
7673            }
7674            else if (r.length === 2) {
7675                r1 = r3 = r[0];
7676                r2 = r4 = r[1];
7677            }
7678            else if (r.length === 3) {
7679                r1 = r[0];
7680                r2 = r4 = r[1];
7681                r3 = r[2];
7682            }
7683            else {
7684                r1 = r[0];
7685                r2 = r[1];
7686                r3 = r[2];
7687                r4 = r[3];
7688            }
7689        }
7690        else {
7691            r1 = r2 = r3 = r4 = 0;
7692        }
7693
7694        var total;
7695        if (r1 + r2 > width) {
7696            total = r1 + r2;
7697            r1 *= width / total;
7698            r2 *= width / total;
7699        }
7700        if (r3 + r4 > width) {
7701            total = r3 + r4;
7702            r3 *= width / total;
7703            r4 *= width / total;
7704        }
7705        if (r2 + r3 > height) {
7706            total = r2 + r3;
7707            r2 *= height / total;
7708            r3 *= height / total;
7709        }
7710        if (r1 + r4 > height) {
7711            total = r1 + r4;
7712            r1 *= height / total;
7713            r4 *= height / total;
7714        }
7715        ctx.moveTo(x + r1, y);
7716        ctx.lineTo(x + width - r2, y);
7717        r2 !== 0 && ctx.arc(x + width - r2, y + r2, r2, -Math.PI / 2, 0);
7718        ctx.lineTo(x + width, y + height - r3);
7719        r3 !== 0 && ctx.arc(x + width - r3, y + height - r3, r3, 0, Math.PI / 2);
7720        ctx.lineTo(x + r4, y + height);
7721        r4 !== 0 && ctx.arc(x + r4, y + height - r4, r4, Math.PI / 2, Math.PI);
7722        ctx.lineTo(x, y + r1);
7723        r1 !== 0 && ctx.arc(x + r1, y + r1, r1, Math.PI, Math.PI * 1.5);
7724    }
7725
7726// TODO: Have not support 'start', 'end' yet.
7727    var VALID_TEXT_ALIGN = {left: 1, right: 1, center: 1};
7728    var VALID_TEXT_VERTICAL_ALIGN = {top: 1, bottom: 1, middle: 1};
7729
7730    /**
7731     * @param {module:zrender/graphic/Style} style
7732     * @return {module:zrender/graphic/Style} The input style.
7733     */
7734    function normalizeTextStyle(style) {
7735        normalizeStyle(style);
7736        each$1(style.rich, normalizeStyle);
7737        return style;
7738    }
7739
7740    function normalizeStyle(style) {
7741        if (style) {
7742
7743            style.font = makeFont(style);
7744
7745            var textAlign = style.textAlign;
7746            textAlign === 'middle' && (textAlign = 'center');
7747            style.textAlign = (
7748                textAlign == null || VALID_TEXT_ALIGN[textAlign]
7749            ) ? textAlign : 'left';
7750
7751            // Compatible with textBaseline.
7752            var textVerticalAlign = style.textVerticalAlign || style.textBaseline;
7753            textVerticalAlign === 'center' && (textVerticalAlign = 'middle');
7754            style.textVerticalAlign = (
7755                textVerticalAlign == null || VALID_TEXT_VERTICAL_ALIGN[textVerticalAlign]
7756            ) ? textVerticalAlign : 'top';
7757
7758            var textPadding = style.textPadding;
7759            if (textPadding) {
7760                style.textPadding = normalizeCssArray(style.textPadding);
7761            }
7762        }
7763    }
7764
7765    /**
7766     * @param {CanvasRenderingContext2D} ctx
7767     * @param {string} text
7768     * @param {module:zrender/graphic/Style} style
7769     * @param {Object|boolean} [rect] {x, y, width, height}
7770     *                  If set false, rect text is not used.
7771     */
7772    function renderText(hostEl, ctx, text, style, rect) {
7773        style.rich
7774            ? renderRichText(hostEl, ctx, text, style, rect)
7775            : renderPlainText(hostEl, ctx, text, style, rect);
7776    }
7777
7778    function renderPlainText(hostEl, ctx, text, style, rect) {
7779        var font = setCtx(ctx, 'font', style.font || DEFAULT_FONT);
7780
7781        var textPadding = style.textPadding;
7782
7783        var contentBlock = hostEl.__textCotentBlock;
7784        if (!contentBlock || hostEl.__dirty) {
7785            contentBlock = hostEl.__textCotentBlock = parsePlainText(
7786                text, font, textPadding, style.truncate
7787            );
7788        }
7789
7790        var outerHeight = contentBlock.outerHeight;
7791
7792        var textLines = contentBlock.lines;
7793        var lineHeight = contentBlock.lineHeight;
7794
7795        var boxPos = getBoxPosition(outerHeight, style, rect);
7796        var baseX = boxPos.baseX;
7797        var baseY = boxPos.baseY;
7798        var textAlign = boxPos.textAlign;
7799        var textVerticalAlign = boxPos.textVerticalAlign;
7800
7801        // Origin of textRotation should be the base point of text drawing.
7802        applyTextRotation(ctx, style, rect, baseX, baseY);
7803
7804        var boxY = adjustTextY(baseY, outerHeight, textVerticalAlign);
7805        var textX = baseX;
7806        var textY = boxY;
7807
7808        var needDrawBg = needDrawBackground(style);
7809        if (needDrawBg || textPadding) {
7810            // Consider performance, do not call getTextWidth util necessary.
7811            var textWidth = getWidth(text, font);
7812            var outerWidth = textWidth;
7813            textPadding && (outerWidth += textPadding[1] + textPadding[3]);
7814            var boxX = adjustTextX(baseX, outerWidth, textAlign);
7815
7816            needDrawBg && drawBackground(hostEl, ctx, style, boxX, boxY, outerWidth, outerHeight);
7817
7818            if (textPadding) {
7819                textX = getTextXForPadding(baseX, textAlign, textPadding);
7820                textY += textPadding[0];
7821            }
7822        }
7823
7824        setCtx(ctx, 'textAlign', textAlign || 'left');
7825        // Force baseline to be "middle". Otherwise, if using "top", the
7826        // text will offset downward a little bit in font "Microsoft YaHei".
7827        setCtx(ctx, 'textBaseline', 'middle');
7828
7829        // Always set shadowBlur and shadowOffset to avoid leak from displayable.
7830        setCtx(ctx, 'shadowBlur', style.textShadowBlur || 0);
7831        setCtx(ctx, 'shadowColor', style.textShadowColor || 'transparent');
7832        setCtx(ctx, 'shadowOffsetX', style.textShadowOffsetX || 0);
7833        setCtx(ctx, 'shadowOffsetY', style.textShadowOffsetY || 0);
7834
7835        // `textBaseline` is set as 'middle'.
7836        textY += lineHeight / 2;
7837
7838        var textStrokeWidth = style.textStrokeWidth;
7839        var textStroke = getStroke(style.textStroke, textStrokeWidth);
7840        var textFill = getFill(style.textFill);
7841
7842        if (textStroke) {
7843            setCtx(ctx, 'lineWidth', textStrokeWidth);
7844            setCtx(ctx, 'strokeStyle', textStroke);
7845        }
7846        if (textFill) {
7847            setCtx(ctx, 'fillStyle', textFill);
7848        }
7849
7850        for (var i = 0; i < textLines.length; i++) {
7851            // Fill after stroke so the outline will not cover the main part.
7852            textStroke && ctx.strokeText(textLines[i], textX, textY);
7853            textFill && ctx.fillText(textLines[i], textX, textY);
7854            textY += lineHeight;
7855        }
7856    }
7857
7858    function renderRichText(hostEl, ctx, text, style, rect) {
7859        var contentBlock = hostEl.__textCotentBlock;
7860
7861        if (!contentBlock || hostEl.__dirty) {
7862            contentBlock = hostEl.__textCotentBlock = parseRichText(text, style);
7863        }
7864
7865        drawRichText(hostEl, ctx, contentBlock, style, rect);
7866    }
7867
7868    function drawRichText(hostEl, ctx, contentBlock, style, rect) {
7869        var contentWidth = contentBlock.width;
7870        var outerWidth = contentBlock.outerWidth;
7871        var outerHeight = contentBlock.outerHeight;
7872        var textPadding = style.textPadding;
7873
7874        var boxPos = getBoxPosition(outerHeight, style, rect);
7875        var baseX = boxPos.baseX;
7876        var baseY = boxPos.baseY;
7877        var textAlign = boxPos.textAlign;
7878        var textVerticalAlign = boxPos.textVerticalAlign;
7879
7880        // Origin of textRotation should be the base point of text drawing.
7881        applyTextRotation(ctx, style, rect, baseX, baseY);
7882
7883        var boxX = adjustTextX(baseX, outerWidth, textAlign);
7884        var boxY = adjustTextY(baseY, outerHeight, textVerticalAlign);
7885        var xLeft = boxX;
7886        var lineTop = boxY;
7887        if (textPadding) {
7888            xLeft += textPadding[3];
7889            lineTop += textPadding[0];
7890        }
7891        var xRight = xLeft + contentWidth;
7892
7893        needDrawBackground(style) && drawBackground(
7894            hostEl, ctx, style, boxX, boxY, outerWidth, outerHeight
7895        );
7896
7897        for (var i = 0; i < contentBlock.lines.length; i++) {
7898            var line = contentBlock.lines[i];
7899            var tokens = line.tokens;
7900            var tokenCount = tokens.length;
7901            var lineHeight = line.lineHeight;
7902            var usedWidth = line.width;
7903
7904            var leftIndex = 0;
7905            var lineXLeft = xLeft;
7906            var lineXRight = xRight;
7907            var rightIndex = tokenCount - 1;
7908            var token;
7909
7910            while (
7911                leftIndex < tokenCount
7912                && (token = tokens[leftIndex], !token.textAlign || token.textAlign === 'left')
7913                ) {
7914                placeToken(hostEl, ctx, token, style, lineHeight, lineTop, lineXLeft, 'left');
7915                usedWidth -= token.width;
7916                lineXLeft += token.width;
7917                leftIndex++;
7918            }
7919
7920            while (
7921                rightIndex >= 0
7922                && (token = tokens[rightIndex], token.textAlign === 'right')
7923                ) {
7924                placeToken(hostEl, ctx, token, style, lineHeight, lineTop, lineXRight, 'right');
7925                usedWidth -= token.width;
7926                lineXRight -= token.width;
7927                rightIndex--;
7928            }
7929
7930            // The other tokens are placed as textAlign 'center' if there is enough space.
7931            lineXLeft += (contentWidth - (lineXLeft - xLeft) - (xRight - lineXRight) - usedWidth) / 2;
7932            while (leftIndex <= rightIndex) {
7933                token = tokens[leftIndex];
7934                // Consider width specified by user, use 'center' rather than 'left'.
7935                placeToken(hostEl, ctx, token, style, lineHeight, lineTop, lineXLeft + token.width / 2, 'center');
7936                lineXLeft += token.width;
7937                leftIndex++;
7938            }
7939
7940            lineTop += lineHeight;
7941        }
7942    }
7943
7944    function applyTextRotation(ctx, style, rect, x, y) {
7945        // textRotation only apply in RectText.
7946        if (rect && style.textRotation) {
7947            var origin = style.textOrigin;
7948            if (origin === 'center') {
7949                x = rect.width / 2 + rect.x;
7950                y = rect.height / 2 + rect.y;
7951            }
7952            else if (origin) {
7953                x = origin[0] + rect.x;
7954                y = origin[1] + rect.y;
7955            }
7956
7957            ctx.translate(x, y);
7958            // Positive: anticlockwise
7959            ctx.rotate(-style.textRotation);
7960            ctx.translate(-x, -y);
7961        }
7962    }
7963
7964    function placeToken(hostEl, ctx, token, style, lineHeight, lineTop, x, textAlign) {
7965        var tokenStyle = style.rich[token.styleName] || {};
7966
7967        // 'ctx.textBaseline' is always set as 'middle', for sake of
7968        // the bias of "Microsoft YaHei".
7969        var textVerticalAlign = token.textVerticalAlign;
7970        var y = lineTop + lineHeight / 2;
7971        if (textVerticalAlign === 'top') {
7972            y = lineTop + token.height / 2;
7973        }
7974        else if (textVerticalAlign === 'bottom') {
7975            y = lineTop + lineHeight - token.height / 2;
7976        }
7977
7978        !token.isLineHolder && needDrawBackground(tokenStyle) && drawBackground(
7979            hostEl,
7980            ctx,
7981            tokenStyle,
7982            textAlign === 'right'
7983                ? x - token.width
7984                : textAlign === 'center'
7985                ? x - token.width / 2
7986                : x,
7987            y - token.height / 2,
7988            token.width,
7989            token.height
7990        );
7991
7992        var textPadding = token.textPadding;
7993        if (textPadding) {
7994            x = getTextXForPadding(x, textAlign, textPadding);
7995            y -= token.height / 2 - textPadding[2] - token.textHeight / 2;
7996        }
7997
7998        setCtx(ctx, 'shadowBlur', retrieve3(tokenStyle.textShadowBlur, style.textShadowBlur, 0));
7999        setCtx(ctx, 'shadowColor', tokenStyle.textShadowColor || style.textShadowColor || 'transparent');
8000        setCtx(ctx, 'shadowOffsetX', retrieve3(tokenStyle.textShadowOffsetX, style.textShadowOffsetX, 0));
8001        setCtx(ctx, 'shadowOffsetY', retrieve3(tokenStyle.textShadowOffsetY, style.textShadowOffsetY, 0));
8002
8003        setCtx(ctx, 'textAlign', textAlign);
8004        // Force baseline to be "middle". Otherwise, if using "top", the
8005        // text will offset downward a little bit in font "Microsoft YaHei".
8006        setCtx(ctx, 'textBaseline', 'middle');
8007
8008        setCtx(ctx, 'font', token.font || DEFAULT_FONT);
8009
8010        var textStroke = getStroke(tokenStyle.textStroke || style.textStroke, textStrokeWidth);
8011        var textFill = getFill(tokenStyle.textFill || style.textFill);
8012        var textStrokeWidth = retrieve2(tokenStyle.textStrokeWidth, style.textStrokeWidth);
8013
8014        // Fill after stroke so the outline will not cover the main part.
8015        if (textStroke) {
8016            setCtx(ctx, 'lineWidth', textStrokeWidth);
8017            setCtx(ctx, 'strokeStyle', textStroke);
8018            ctx.strokeText(token.text, x, y);
8019        }
8020        if (textFill) {
8021            setCtx(ctx, 'fillStyle', textFill);
8022            ctx.fillText(token.text, x, y);
8023        }
8024    }
8025
8026    function needDrawBackground(style) {
8027        return style.textBackgroundColor
8028            || (style.textBorderWidth && style.textBorderColor);
8029    }
8030
8031// style: {textBackgroundColor, textBorderWidth, textBorderColor, textBorderRadius}
8032// shape: {x, y, width, height}
8033    function drawBackground(hostEl, ctx, style, x, y, width, height) {
8034        var textBackgroundColor = style.textBackgroundColor;
8035        var textBorderWidth = style.textBorderWidth;
8036        var textBorderColor = style.textBorderColor;
8037        var isPlainBg = isString(textBackgroundColor);
8038
8039        setCtx(ctx, 'shadowBlur', style.textBoxShadowBlur || 0);
8040        setCtx(ctx, 'shadowColor', style.textBoxShadowColor || 'transparent');
8041        setCtx(ctx, 'shadowOffsetX', style.textBoxShadowOffsetX || 0);
8042        setCtx(ctx, 'shadowOffsetY', style.textBoxShadowOffsetY || 0);
8043
8044        if (isPlainBg || (textBorderWidth && textBorderColor)) {
8045            ctx.beginPath();
8046            var textBorderRadius = style.textBorderRadius;
8047            if (!textBorderRadius) {
8048                ctx.rect(x, y, width, height);
8049            }
8050            else {
8051                buildPath(ctx, {
8052                    x: x, y: y, width: width, height: height, r: textBorderRadius
8053                });
8054            }
8055            ctx.closePath();
8056        }
8057
8058        if (isPlainBg) {
8059            setCtx(ctx, 'fillStyle', textBackgroundColor);
8060            ctx.fill();
8061        }
8062        else if (isObject$1(textBackgroundColor)) {
8063            var image = textBackgroundColor.image;
8064
8065            image = createOrUpdateImage(
8066                image, null, hostEl, onBgImageLoaded, textBackgroundColor
8067            );
8068            if (image && isImageReady(image)) {
8069                ctx.drawImage(image, x, y, width, height);
8070            }
8071        }
8072
8073        if (textBorderWidth && textBorderColor) {
8074            setCtx(ctx, 'lineWidth', textBorderWidth);
8075            setCtx(ctx, 'strokeStyle', textBorderColor);
8076            ctx.stroke();
8077        }
8078    }
8079
8080    function onBgImageLoaded(image, textBackgroundColor) {
8081        // Replace image, so that `contain/text.js#parseRichText`
8082        // will get correct result in next tick.
8083        textBackgroundColor.image = image;
8084    }
8085
8086    function getBoxPosition(blockHeiht, style, rect) {
8087        var baseX = style.x || 0;
8088        var baseY = style.y || 0;
8089        var textAlign = style.textAlign;
8090        var textVerticalAlign = style.textVerticalAlign;
8091
8092        // Text position represented by coord
8093        if (rect) {
8094            var textPosition = style.textPosition;
8095            if (textPosition instanceof Array) {
8096                // Percent
8097                baseX = rect.x + parsePercent(textPosition[0], rect.width);
8098                baseY = rect.y + parsePercent(textPosition[1], rect.height);
8099            }
8100            else {
8101                var res = adjustTextPositionOnRect(
8102                    textPosition, rect, style.textDistance
8103                );
8104                baseX = res.x;
8105                baseY = res.y;
8106                // Default align and baseline when has textPosition
8107                textAlign = textAlign || res.textAlign;
8108                textVerticalAlign = textVerticalAlign || res.textVerticalAlign;
8109            }
8110
8111            // textOffset is only support in RectText, otherwise
8112            // we have to adjust boundingRect for textOffset.
8113            var textOffset = style.textOffset;
8114            if (textOffset) {
8115                baseX += textOffset[0];
8116                baseY += textOffset[1];
8117            }
8118        }
8119
8120        return {
8121            baseX: baseX,
8122            baseY: baseY,
8123            textAlign: textAlign,
8124            textVerticalAlign: textVerticalAlign
8125        };
8126    }
8127
8128    function setCtx(ctx, prop, value) {
8129        ctx[prop] = fixShadow(ctx, prop, value);
8130        return ctx[prop];
8131    }
8132
8133    /**
8134     * @param {string} [stroke] If specified, do not check style.textStroke.
8135     * @param {string} [lineWidth] If specified, do not check style.textStroke.
8136     * @param {number} style
8137     */
8138    function getStroke(stroke, lineWidth) {
8139        return (stroke == null || lineWidth <= 0 || stroke === 'transparent' || stroke === 'none')
8140            ? null
8141            // TODO pattern and gradient?
8142            : (stroke.image || stroke.colorStops)
8143                ? '#000'
8144                : stroke;
8145    }
8146
8147    function getFill(fill) {
8148        return (fill == null || fill === 'none')
8149            ? null
8150            // TODO pattern and gradient?
8151            : (fill.image || fill.colorStops)
8152                ? '#000'
8153                : fill;
8154    }
8155
8156    function parsePercent(value, maxValue) {
8157        if (typeof value === 'string') {
8158            if (value.lastIndexOf('%') >= 0) {
8159                return parseFloat(value) / 100 * maxValue;
8160            }
8161            return parseFloat(value);
8162        }
8163        return value;
8164    }
8165
8166    function getTextXForPadding(x, textAlign, textPadding) {
8167        return textAlign === 'right'
8168            ? (x - textPadding[1])
8169            : textAlign === 'center'
8170                ? (x + textPadding[3] / 2 - textPadding[1] / 2)
8171                : (x + textPadding[3]);
8172    }
8173
8174    /**
8175     * @param {string} text
8176     * @param {module:zrender/Style} style
8177     * @return {boolean}
8178     */
8179    function needDrawText(text, style) {
8180        return text != null
8181            && (text
8182                || style.textBackgroundColor
8183                || (style.textBorderWidth && style.textBorderColor)
8184                || style.textPadding
8185            );
8186    }
8187
8188    /**
8189     * Mixin for drawing text in a element bounding rect
8190     * @module zrender/mixin/RectText
8191     */
8192
8193    var tmpRect$1 = new BoundingRect();
8194
8195    var RectText = function () {};
8196
8197    RectText.prototype = {
8198
8199        constructor: RectText,
8200
8201        /**
8202         * Draw text in a rect with specified position.
8203         * @param  {CanvasRenderingContext2D} ctx
8204         * @param  {Object} rect Displayable rect
8205         */
8206        drawRectText: function (ctx, rect) {
8207            var style = this.style;
8208
8209            rect = style.textRect || rect;
8210
8211            // Optimize, avoid normalize every time.
8212            this.__dirty && normalizeTextStyle(style, true);
8213
8214            var text = style.text;
8215
8216            // Convert to string
8217            text != null && (text += '');
8218
8219            if (!needDrawText(text, style)) {
8220                return;
8221            }
8222
8223            // FIXME
8224            ctx.save();
8225
8226            // Transform rect to view space
8227            var transform = this.transform;
8228            if (!style.transformText) {
8229                if (transform) {
8230                    tmpRect$1.copy(rect);
8231                    tmpRect$1.applyTransform(transform);
8232                    rect = tmpRect$1;
8233                }
8234            }
8235            else {
8236                this.setTransform(ctx);
8237            }
8238
8239            // transformText and textRotation can not be used at the same time.
8240            renderText(this, ctx, text, style, rect);
8241
8242            ctx.restore();
8243        }
8244    };
8245
8246    /**
8247     * 鍙粯鍒剁殑鍥惧舰鍩虹被
8248     * Base class of all displayable graphic objects
8249     * @module zrender/graphic/Displayable
8250     */
8251
8252
8253    /**
8254     * @alias module:zrender/graphic/Displayable
8255     * @extends module:zrender/Element
8256     * @extends module:zrender/graphic/mixin/RectText
8257     */
8258    function Displayable(opts) {
8259
8260        opts = opts || {};
8261
8262        Element.call(this, opts);
8263
8264        // Extend properties
8265        for (var name in opts) {
8266            if (
8267                opts.hasOwnProperty(name) &&
8268                name !== 'style'
8269            ) {
8270                this[name] = opts[name];
8271            }
8272        }
8273
8274        /**
8275         * @type {module:zrender/graphic/Style}
8276         */
8277        this.style = new Style(opts.style, this);
8278
8279        this._rect = null;
8280        // Shapes for cascade clipping.
8281        this.__clipPaths = [];
8282
8283        // FIXME Stateful must be mixined after style is setted
8284        // Stateful.call(this, opts);
8285    }
8286
8287    Displayable.prototype = {
8288
8289        constructor: Displayable,
8290
8291        type: 'displayable',
8292
8293        /**
8294         * Displayable 鏄惁涓鸿剰锛孭ainter 涓細鏍规嵁璇ユ爣璁板垽鏂槸鍚﹂渶瑕佹槸鍚﹂渶瑕侀噸鏂扮粯鍒�
8295         * Dirty flag. From which painter will determine if this displayable object needs brush
8296         * @name module:zrender/graphic/Displayable#__dirty
8297         * @type {boolean}
8298         */
8299        __dirty: true,
8300
8301        /**
8302         * 鍥惧舰鏄惁鍙锛屼负true鏃朵笉缁樺埗鍥惧舰锛屼絾鏄粛鑳借Е鍙戦紶鏍囦簨浠�
8303         * If ignore drawing of the displayable object. Mouse event will still be triggered
8304         * @name module:/zrender/graphic/Displayable#invisible
8305         * @type {boolean}
8306         * @default false
8307         */
8308        invisible: false,
8309
8310        /**
8311         * @name module:/zrender/graphic/Displayable#z
8312         * @type {number}
8313         * @default 0
8314         */
8315        z: 0,
8316
8317        /**
8318         * @name module:/zrender/graphic/Displayable#z
8319         * @type {number}
8320         * @default 0
8321         */
8322        z2: 0,
8323
8324        /**
8325         * z灞俵evel锛屽喅瀹氱粯鐢诲湪鍝眰canvas涓�
8326         * @name module:/zrender/graphic/Displayable#zlevel
8327         * @type {number}
8328         * @default 0
8329         */
8330        zlevel: 0,
8331
8332        /**
8333         * 鏄惁鍙嫋鎷�
8334         * @name module:/zrender/graphic/Displayable#draggable
8335         * @type {boolean}
8336         * @default false
8337         */
8338        draggable: false,
8339
8340        /**
8341         * 鏄惁姝e湪鎷栨嫿
8342         * @name module:/zrender/graphic/Displayable#draggable
8343         * @type {boolean}
8344         * @default false
8345         */
8346        dragging: false,
8347
8348        /**
8349         * 鏄惁鐩稿簲榧犳爣浜嬩欢
8350         * @name module:/zrender/graphic/Displayable#silent
8351         * @type {boolean}
8352         * @default false
8353         */
8354        silent: false,
8355
8356        /**
8357         * If enable culling
8358         * @type {boolean}
8359         * @default false
8360         */
8361        culling: false,
8362
8363        /**
8364         * Mouse cursor when hovered
8365         * @name module:/zrender/graphic/Displayable#cursor
8366         * @type {string}
8367         */
8368        cursor: 'pointer',
8369
8370        /**
8371         * If hover area is bounding rect
8372         * @name module:/zrender/graphic/Displayable#rectHover
8373         * @type {string}
8374         */
8375        rectHover: false,
8376
8377        /**
8378         * Render the element progressively when the value >= 0,
8379         * usefull for large data.
8380         * @type {boolean}
8381         */
8382        progressive: false,
8383
8384        /**
8385         * @type {boolean}
8386         */
8387        incremental: false,
8388        // inplace is used with incremental
8389        inplace: false,
8390
8391        beforeBrush: function (ctx) {},
8392
8393        afterBrush: function (ctx) {},
8394
8395        /**
8396         * 鍥惧舰缁樺埗鏂规硶
8397         * @param {CanvasRenderingContext2D} ctx
8398         */
8399        // Interface
8400        brush: function (ctx, prevEl) {},
8401
8402        /**
8403         * 鑾峰彇鏈€灏忓寘鍥寸洅
8404         * @return {module:zrender/core/BoundingRect}
8405         */
8406        // Interface
8407        getBoundingRect: function () {},
8408
8409        /**
8410         * 鍒ゆ柇鍧愭爣 x, y 鏄惁鍦ㄥ浘褰笂
8411         * If displayable element contain coord x, y
8412         * @param  {number} x
8413         * @param  {number} y
8414         * @return {boolean}
8415         */
8416        contain: function (x, y) {
8417            return this.rectContain(x, y);
8418        },
8419
8420        /**
8421         * @param  {Function} cb
8422         * @param  {}   context
8423         */
8424        traverse: function (cb, context) {
8425            cb.call(context, this);
8426        },
8427
8428        /**
8429         * 鍒ゆ柇鍧愭爣 x, y 鏄惁鍦ㄥ浘褰㈢殑鍖呭洿鐩掍笂
8430         * If bounding rect of element contain coord x, y
8431         * @param  {number} x
8432         * @param  {number} y
8433         * @return {boolean}
8434         */
8435        rectContain: function (x, y) {
8436            var coord = this.transformCoordToLocal(x, y);
8437            var rect = this.getBoundingRect();
8438            return rect.contain(coord[0], coord[1]);
8439        },
8440
8441        /**
8442         * 鏍囪鍥惧舰鍏冪礌涓鸿剰锛屽苟涓斿湪涓嬩竴甯ч噸缁�
8443         * Mark displayable element dirty and refresh next frame
8444         */
8445        dirty: function () {
8446            this.__dirty = true;
8447
8448            this._rect = null;
8449
8450            this.__zr && this.__zr.refresh();
8451        },
8452
8453        /**
8454         * 鍥惧舰鏄惁浼氳Е鍙戜簨浠�
8455         * If displayable object binded any event
8456         * @return {boolean}
8457         */
8458        // TODO, 閫氳繃 bind 缁戝畾鐨勪簨浠�
8459        // isSilent: function () {
8460        //     return !(
8461        //         this.hoverable || this.draggable
8462        //         || this.onmousemove || this.onmouseover || this.onmouseout
8463        //         || this.onmousedown || this.onmouseup || this.onclick
8464        //         || this.ondragenter || this.ondragover || this.ondragleave
8465        //         || this.ondrop
8466        //     );
8467        // },
8468        /**
8469         * Alias for animate('style')
8470         * @param {boolean} loop
8471         */
8472        animateStyle: function (loop) {
8473            return this.animate('style', loop);
8474        },
8475
8476        attrKV: function (key, value) {
8477            if (key !== 'style') {
8478                Element.prototype.attrKV.call(this, key, value);
8479            }
8480            else {
8481                this.style.set(value);
8482            }
8483        },
8484
8485        /**
8486         * @param {Object|string} key
8487         * @param {*} value
8488         */
8489        setStyle: function (key, value) {
8490            this.style.set(key, value);
8491            this.dirty(false);
8492            return this;
8493        },
8494
8495        /**
8496         * Use given style object
8497         * @param  {Object} obj
8498         */
8499        useStyle: function (obj) {
8500            this.style = new Style(obj, this);
8501            this.dirty(false);
8502            return this;
8503        }
8504    };
8505
8506    inherits(Displayable, Element);
8507
8508    mixin(Displayable, RectText);
8509
8510    /**
8511     * @alias zrender/graphic/Image
8512     * @extends module:zrender/graphic/Displayable
8513     * @constructor
8514     * @param {Object} opts
8515     */
8516    function ZImage(opts) {
8517        Displayable.call(this, opts);
8518    }
8519
8520    ZImage.prototype = {
8521
8522        constructor: ZImage,
8523
8524        type: 'image',
8525
8526        brush: function (ctx, prevEl) {
8527            var style = this.style;
8528            var src = style.image;
8529
8530            // Must bind each time
8531            style.bind(ctx, this, prevEl);
8532
8533            var image = this._image = createOrUpdateImage(
8534                src,
8535                this._image,
8536                this,
8537                this.onload
8538            );
8539
8540            if (!image || !isImageReady(image)) {
8541                return;
8542            }
8543
8544            // 鍥剧墖宸茬粡鍔犺浇瀹屾垚
8545            // if (image.nodeName.toUpperCase() == 'IMG') {
8546            //     if (!image.complete) {
8547            //         return;
8548            //     }
8549            // }
8550            // Else is canvas
8551
8552            var x = style.x || 0;
8553            var y = style.y || 0;
8554            var width = style.width;
8555            var height = style.height;
8556            var aspect = image.width / image.height;
8557            if (width == null && height != null) {
8558                // Keep image/height ratio
8559                width = height * aspect;
8560            }
8561            else if (height == null && width != null) {
8562                height = width / aspect;
8563            }
8564            else if (width == null && height == null) {
8565                width = image.width;
8566                height = image.height;
8567            }
8568
8569            // 璁剧疆transform
8570            this.setTransform(ctx);
8571
8572            if (style.sWidth && style.sHeight) {
8573                var sx = style.sx || 0;
8574                var sy = style.sy || 0;
8575                ctx.drawImage(
8576                    image,
8577                    sx, sy, style.sWidth, style.sHeight,
8578                    x, y, width, height
8579                );
8580            }
8581            else if (style.sx && style.sy) {
8582                var sx = style.sx;
8583                var sy = style.sy;
8584                var sWidth = width - sx;
8585                var sHeight = height - sy;
8586                ctx.drawImage(
8587                    image,
8588                    sx, sy, sWidth, sHeight,
8589                    x, y, width, height
8590                );
8591            }
8592            else {
8593                ctx.drawImage(image, x, y, width, height);
8594            }
8595
8596            // Draw rect text
8597            if (style.text != null) {
8598                // Only restore transform when needs draw text.
8599                this.restoreTransform(ctx);
8600                this.drawRectText(ctx, this.getBoundingRect());
8601            }
8602        },
8603
8604        getBoundingRect: function () {
8605            var style = this.style;
8606            if (! this._rect) {
8607                this._rect = new BoundingRect(
8608                    style.x || 0, style.y || 0, style.width || 0, style.height || 0
8609                );
8610            }
8611            return this._rect;
8612        }
8613    };
8614
8615    inherits(ZImage, Displayable);
8616
8617    var HOVER_LAYER_ZLEVEL = 1e5;
8618    var CANVAS_ZLEVEL = 314159;
8619
8620    var EL_AFTER_INCREMENTAL_INC = 0.01;
8621    var INCREMENTAL_INC = 0.001;
8622
8623    function parseInt10(val) {
8624        return parseInt(val, 10);
8625    }
8626
8627    function isLayerValid(layer) {
8628        if (!layer) {
8629            return false;
8630        }
8631
8632        if (layer.__builtin__) {
8633            return true;
8634        }
8635
8636        if (typeof(layer.resize) !== 'function'
8637            || typeof(layer.refresh) !== 'function'
8638        ) {
8639            return false;
8640        }
8641
8642        return true;
8643    }
8644
8645    var tmpRect = new BoundingRect(0, 0, 0, 0);
8646    var viewRect = new BoundingRect(0, 0, 0, 0);
8647    function isDisplayableCulled(el, width, height) {
8648        tmpRect.copy(el.getBoundingRect());
8649        if (el.transform) {
8650            tmpRect.applyTransform(el.transform);
8651        }
8652        viewRect.width = width;
8653        viewRect.height = height;
8654        return !tmpRect.intersect(viewRect);
8655    }
8656
8657    function isClipPathChanged(clipPaths, prevClipPaths) {
8658        if (clipPaths == prevClipPaths) { // Can both be null or undefined
8659            return false;
8660        }
8661
8662        if (!clipPaths || !prevClipPaths || (clipPaths.length !== prevClipPaths.length)) {
8663            return true;
8664        }
8665        for (var i = 0; i < clipPaths.length; i++) {
8666            if (clipPaths[i] !== prevClipPaths[i]) {
8667                return true;
8668            }
8669        }
8670    }
8671
8672    function doClip(clipPaths, ctx) {
8673        for (var i = 0; i < clipPaths.length; i++) {
8674            var clipPath = clipPaths[i];
8675
8676            clipPath.setTransform(ctx);
8677            ctx.beginPath();
8678            clipPath.buildPath(ctx, clipPath.shape);
8679            ctx.clip();
8680            // Transform back
8681            clipPath.restoreTransform(ctx);
8682        }
8683    }
8684
8685    function createRoot(width, height) {
8686        var domRoot = document.createElement('div');
8687
8688        // domRoot.onselectstart = returnFalse; // 閬垮厤椤甸潰閫変腑鐨勫按灏�
8689        domRoot.style.cssText = [
8690            'position:relative',
8691            'overflow:hidden',
8692            'width:' + width + 'px',
8693            'height:' + height + 'px',
8694            'padding:0',
8695            'margin:0',
8696            'border-width:0'
8697        ].join(';') + ';';
8698
8699        return domRoot;
8700    }
8701
8702
8703    /**
8704     * @alias module:zrender/Painter
8705     * @constructor
8706     * @param {HTMLElement} root 缁樺浘瀹瑰櫒
8707     * @param {module:zrender/Storage} storage
8708     * @param {Object} opts
8709     */
8710    var Painter = function (root, storage, opts) {
8711
8712        this.type = 'canvas';
8713
8714        // In node environment using node-canvas
8715        var singleCanvas = !root.nodeName // In node ?
8716            || root.nodeName.toUpperCase() === 'CANVAS';
8717
8718        this._opts = opts = extend({}, opts || {});
8719
8720        /**
8721         * @type {number}
8722         */
8723        this.dpr = opts.devicePixelRatio || devicePixelRatio;
8724        /**
8725         * @type {boolean}
8726         * @private
8727         */
8728        this._singleCanvas = singleCanvas;
8729        /**
8730         * 缁樺浘瀹瑰櫒
8731         * @type {HTMLElement}
8732         */
8733        this.root = root;
8734
8735        var rootStyle = root.style;
8736
8737        if (rootStyle) {
8738            rootStyle['-webkit-tap-highlight-color'] = 'transparent';
8739            rootStyle['-webkit-user-select'] =
8740                rootStyle['user-select'] =
8741                    rootStyle['-webkit-touch-callout'] = 'none';
8742
8743            root.innerHTML = '';
8744        }
8745
8746        /**
8747         * @type {module:zrender/Storage}
8748         */
8749        this.storage = storage;
8750
8751        /**
8752         * @type {Array.<number>}
8753         * @private
8754         */
8755        var zlevelList = this._zlevelList = [];
8756
8757        /**
8758         * @type {Object.<string, module:zrender/Layer>}
8759         * @private
8760         */
8761        var layers = this._layers = {};
8762
8763        /**
8764         * @type {Object.<string, Object>}
8765         * @private
8766         */
8767        this._layerConfig = {};
8768
8769        /**
8770         * zrender will do compositing when root is a canvas and have multiple zlevels.
8771         */
8772        this._needsManuallyCompositing = false;
8773
8774        if (!singleCanvas) {
8775            this._width = this._getSize(0);
8776            this._height = this._getSize(1);
8777
8778            var domRoot = this._domRoot = createRoot(
8779                this._width, this._height
8780            );
8781            root.appendChild(domRoot);
8782        }
8783        else {
8784            var width = root.width;
8785            var height = root.height;
8786
8787            if (opts.width != null) {
8788                width = opts.width;
8789            }
8790            if (opts.height != null) {
8791                height = opts.height;
8792            }
8793            this.dpr = opts.devicePixelRatio || 1;
8794
8795            // Use canvas width and height directly
8796            root.width = width * this.dpr;
8797            root.height = height * this.dpr;
8798
8799            this._width = width;
8800            this._height = height;
8801
8802            // Create layer if only one given canvas
8803            // Device can be specified to create a high dpi image.
8804            var mainLayer = new Layer(root, this, this.dpr);
8805            mainLayer.__builtin__ = true;
8806            mainLayer.initContext();
8807            // FIXME Use canvas width and height
8808            // mainLayer.resize(width, height);
8809            layers[CANVAS_ZLEVEL] = mainLayer;
8810            mainLayer.zlevel = CANVAS_ZLEVEL;
8811            // Not use common zlevel.
8812            zlevelList.push(CANVAS_ZLEVEL);
8813
8814            this._domRoot = root;
8815        }
8816
8817        /**
8818         * @type {module:zrender/Layer}
8819         * @private
8820         */
8821        this._hoverlayer = null;
8822
8823        this._hoverElements = [];
8824    };
8825
8826    Painter.prototype = {
8827
8828        constructor: Painter,
8829
8830        getType: function () {
8831            return 'canvas';
8832        },
8833
8834        /**
8835         * If painter use a single canvas
8836         * @return {boolean}
8837         */
8838        isSingleCanvas: function () {
8839            return this._singleCanvas;
8840        },
8841        /**
8842         * @return {HTMLDivElement}
8843         */
8844        getViewportRoot: function () {
8845            return this._domRoot;
8846        },
8847
8848        getViewportRootOffset: function () {
8849            var viewportRoot = this.getViewportRoot();
8850            if (viewportRoot) {
8851                return {
8852                    offsetLeft: viewportRoot.offsetLeft || 0,
8853                    offsetTop: viewportRoot.offsetTop || 0
8854                };
8855            }
8856        },
8857
8858        /**
8859         * 鍒锋柊
8860         * @param {boolean} [paintAll=false] 寮哄埗缁樺埗鎵€鏈塪isplayable
8861         */
8862        refresh: function (paintAll) {
8863
8864            var list = this.storage.getDisplayList(true);
8865
8866            var zlevelList = this._zlevelList;
8867
8868            this._redrawId = Math.random();
8869
8870            this._paintList(list, paintAll, this._redrawId);
8871
8872            // Paint custum layers
8873            for (var i = 0; i < zlevelList.length; i++) {
8874                var z = zlevelList[i];
8875                var layer = this._layers[z];
8876                if (!layer.__builtin__ && layer.refresh) {
8877                    var clearColor = i === 0 ? this._backgroundColor : null;
8878                    layer.refresh(clearColor);
8879                }
8880            }
8881
8882            this.refreshHover();
8883
8884            return this;
8885        },
8886
8887        addHover: function (el, hoverStyle) {
8888            if (el.__hoverMir) {
8889                return;
8890            }
8891            var elMirror = new el.constructor({
8892                style: el.style,
8893                shape: el.shape
8894            });
8895            elMirror.__from = el;
8896            el.__hoverMir = elMirror;
8897            elMirror.setStyle(hoverStyle);
8898            this._hoverElements.push(elMirror);
8899        },
8900
8901        removeHover: function (el) {
8902            var elMirror = el.__hoverMir;
8903            var hoverElements = this._hoverElements;
8904            var idx = indexOf(hoverElements, elMirror);
8905            if (idx >= 0) {
8906                hoverElements.splice(idx, 1);
8907            }
8908            el.__hoverMir = null;
8909        },
8910
8911        clearHover: function (el) {
8912            var hoverElements = this._hoverElements;
8913            for (var i = 0; i < hoverElements.length; i++) {
8914                var from = hoverElements[i].__from;
8915                if (from) {
8916                    from.__hoverMir = null;
8917                }
8918            }
8919            hoverElements.length = 0;
8920        },
8921
8922        refreshHover: function () {
8923            var hoverElements = this._hoverElements;
8924            var len = hoverElements.length;
8925            var hoverLayer = this._hoverlayer;
8926            hoverLayer && hoverLayer.clear();
8927
8928            if (!len) {
8929                return;
8930            }
8931            sort(hoverElements, this.storage.displayableSortFunc);
8932
8933            // Use a extream large zlevel
8934            // FIXME?
8935            if (!hoverLayer) {
8936                hoverLayer = this._hoverlayer = this.getLayer(HOVER_LAYER_ZLEVEL);
8937            }
8938
8939            var scope = {};
8940            hoverLayer.ctx.save();
8941            for (var i = 0; i < len;) {
8942                var el = hoverElements[i];
8943                var originalEl = el.__from;
8944                // Original el is removed
8945                // PENDING
8946                if (!(originalEl && originalEl.__zr)) {
8947                    hoverElements.splice(i, 1);
8948                    originalEl.__hoverMir = null;
8949                    len--;
8950                    continue;
8951                }
8952                i++;
8953
8954                // Use transform
8955                // FIXME style and shape ?
8956                if (!originalEl.invisible) {
8957                    el.transform = originalEl.transform;
8958                    el.invTransform = originalEl.invTransform;
8959                    el.__clipPaths = originalEl.__clipPaths;
8960                    // el.
8961                    this._doPaintEl(el, hoverLayer, true, scope);
8962                }
8963            }
8964            hoverLayer.ctx.restore();
8965        },
8966
8967        getHoverLayer: function () {
8968            return this.getLayer(HOVER_LAYER_ZLEVEL);
8969        },
8970
8971        _paintList: function (list, paintAll, redrawId) {
8972            if (this._redrawId !== redrawId) {
8973                return;
8974            }
8975
8976            paintAll = paintAll || false;
8977
8978            this._updateLayerStatus(list);
8979
8980            var finished = this._doPaintList(list, paintAll);
8981
8982            if (this._needsManuallyCompositing) {
8983                this._compositeManually();
8984            }
8985
8986            if (!finished) {
8987                var self = this;
8988                requestAnimationFrame(function () {
8989                    self._paintList(list, paintAll, redrawId);
8990                });
8991            }
8992        },
8993
8994        _compositeManually: function () {
8995            var ctx = this.getLayer(CANVAS_ZLEVEL).ctx;
8996            var width = this._domRoot.width;
8997            var height = this._domRoot.height;
8998            ctx.clearRect(0, 0, width, height);
8999            // PENDING, If only builtin layer?
9000            this.eachBuiltinLayer(function (layer) {
9001                if (layer.virtual) {
9002                    ctx.drawImage(layer.dom, 0, 0, width, height);
9003                }
9004            });
9005        },
9006
9007        _doPaintList: function (list, paintAll) {
9008            var layerList = [];
9009            for (var zi = 0; zi < this._zlevelList.length; zi++) {
9010                var zlevel = this._zlevelList[zi];
9011                var layer = this._layers[zlevel];
9012                if (layer.__builtin__
9013                    && layer !== this._hoverlayer
9014                    && (layer.__dirty || paintAll)
9015                ) {
9016                    layerList.push(layer);
9017                }
9018            }
9019
9020            var finished = true;
9021
9022            for (var k = 0; k < layerList.length; k++) {
9023                var layer = layerList[k];
9024                var ctx = layer.ctx;
9025                var scope = {};
9026                ctx.save();
9027
9028                var start = paintAll ? layer.__startIndex : layer.__drawIndex;
9029
9030                var useTimer = !paintAll && layer.incremental && Date.now;
9031                var startTime = useTimer && Date.now();
9032
9033                var clearColor = layer.zlevel === this._zlevelList[0]
9034                    ? this._backgroundColor : null;
9035                // All elements in this layer are cleared.
9036                if (layer.__startIndex === layer.__endIndex) {
9037                    layer.clear(false, clearColor);
9038                }
9039                else if (start === layer.__startIndex) {
9040                    var firstEl = list[start];
9041                    if (!firstEl.incremental || !firstEl.notClear || paintAll) {
9042                        layer.clear(false, clearColor);
9043                    }
9044                }
9045                if (start === -1) {
9046                    console.error('For some unknown reason. drawIndex is -1');
9047                    start = layer.__startIndex;
9048                }
9049                for (var i = start; i < layer.__endIndex; i++) {
9050                    var el = list[i];
9051                    this._doPaintEl(el, layer, paintAll, scope);
9052                    el.__dirty = false;
9053
9054                    if (useTimer) {
9055                        // Date.now can be executed in 13,025,305 ops/second.
9056                        var dTime = Date.now() - startTime;
9057                        // Give 15 millisecond to draw.
9058                        // The rest elements will be drawn in the next frame.
9059                        if (dTime > 15) {
9060                            break;
9061                        }
9062                    }
9063                }
9064
9065                layer.__drawIndex = i;
9066
9067                if (layer.__drawIndex < layer.__endIndex) {
9068                    finished = false;
9069                }
9070
9071                if (scope.prevElClipPaths) {
9072                    // Needs restore the state. If last drawn element is in the clipping area.
9073                    ctx.restore();
9074                }
9075
9076                ctx.restore();
9077            }
9078
9079            if (env$1.wxa) {
9080                // Flush for weixin application
9081                each$1(this._layers, function (layer) {
9082                    if (layer && layer.ctx && layer.ctx.draw) {
9083                        layer.ctx.draw();
9084                    }
9085                });
9086            }
9087
9088            return finished;
9089        },
9090
9091        _doPaintEl: function (el, currentLayer, forcePaint, scope) {
9092            var ctx = currentLayer.ctx;
9093            var m = el.transform;
9094            if (
9095                (currentLayer.__dirty || forcePaint)
9096                // Ignore invisible element
9097                && !el.invisible
9098                // Ignore transparent element
9099                && el.style.opacity !== 0
9100                // Ignore scale 0 element, in some environment like node-canvas
9101                // Draw a scale 0 element can cause all following draw wrong
9102                // And setTransform with scale 0 will cause set back transform failed.
9103                && !(m && !m[0] && !m[3])
9104                // Ignore culled element
9105                && !(el.culling && isDisplayableCulled(el, this._width, this._height))
9106            ) {
9107
9108                var clipPaths = el.__clipPaths;
9109
9110                // Optimize when clipping on group with several elements
9111                if (!scope.prevElClipPaths
9112                    || isClipPathChanged(clipPaths, scope.prevElClipPaths)
9113                ) {
9114                    // If has previous clipping state, restore from it
9115                    if (scope.prevElClipPaths) {
9116                        currentLayer.ctx.restore();
9117                        scope.prevElClipPaths = null;
9118
9119                        // Reset prevEl since context has been restored
9120                        scope.prevEl = null;
9121                    }
9122                    // New clipping state
9123                    if (clipPaths) {
9124                        ctx.save();
9125                        doClip(clipPaths, ctx);
9126                        scope.prevElClipPaths = clipPaths;
9127                    }
9128                }
9129                el.beforeBrush && el.beforeBrush(ctx);
9130
9131                el.brush(ctx, scope.prevEl || null);
9132                scope.prevEl = el;
9133
9134                el.afterBrush && el.afterBrush(ctx);
9135            }
9136        },
9137
9138        /**
9139         * 鑾峰彇 zlevel 鎵€鍦ㄥ眰锛屽鏋滀笉瀛樺湪鍒欎細鍒涘缓涓€涓柊鐨勫眰
9140         * @param {number} zlevel
9141         * @param {boolean} virtual Virtual layer will not be inserted into dom.
9142         * @return {module:zrender/Layer}
9143         */
9144        getLayer: function (zlevel, virtual) {
9145            if (this._singleCanvas && !this._needsManuallyCompositing) {
9146                zlevel = CANVAS_ZLEVEL;
9147            }
9148            var layer = this._layers[zlevel];
9149            if (!layer) {
9150                // Create a new layer
9151                layer = new Layer('zr_' + zlevel, this, this.dpr);
9152                layer.zlevel = zlevel;
9153                layer.__builtin__ = true;
9154
9155                if (this._layerConfig[zlevel]) {
9156                    merge(layer, this._layerConfig[zlevel], true);
9157                }
9158
9159                if (virtual) {
9160                    layer.virtual = virtual;
9161                }
9162
9163                this.insertLayer(zlevel, layer);
9164
9165                // Context is created after dom inserted to document
9166                // Or excanvas will get 0px clientWidth and clientHeight
9167                layer.initContext();
9168            }
9169
9170            return layer;
9171        },
9172
9173        insertLayer: function (zlevel, layer) {
9174
9175            var layersMap = this._layers;
9176            var zlevelList = this._zlevelList;
9177            var len = zlevelList.length;
9178            var prevLayer = null;
9179            var i = -1;
9180            var domRoot = this._domRoot;
9181
9182            if (layersMap[zlevel]) {
9183                zrLog('ZLevel ' + zlevel + ' has been used already');
9184                return;
9185            }
9186            // Check if is a valid layer
9187            if (!isLayerValid(layer)) {
9188                zrLog('Layer of zlevel ' + zlevel + ' is not valid');
9189                return;
9190            }
9191
9192            if (len > 0 && zlevel > zlevelList[0]) {
9193                for (i = 0; i < len - 1; i++) {
9194                    if (
9195                        zlevelList[i] < zlevel
9196                        && zlevelList[i + 1] > zlevel
9197                    ) {
9198                        break;
9199                    }
9200                }
9201                prevLayer = layersMap[zlevelList[i]];
9202            }
9203            zlevelList.splice(i + 1, 0, zlevel);
9204
9205            layersMap[zlevel] = layer;
9206
9207            // Vitual layer will not directly show on the screen.
9208            // (It can be a WebGL layer and assigned to a ZImage element)
9209            // But it still under management of zrender.
9210            if (!layer.virtual) {
9211                if (prevLayer) {
9212                    var prevDom = prevLayer.dom;
9213                    if (prevDom.nextSibling) {
9214                        domRoot.insertBefore(
9215                            layer.dom,
9216                            prevDom.nextSibling
9217                        );
9218                    }
9219                    else {
9220                        domRoot.appendChild(layer.dom);
9221                    }
9222                }
9223                else {
9224                    if (domRoot.firstChild) {
9225                        domRoot.insertBefore(layer.dom, domRoot.firstChild);
9226                    }
9227                    else {
9228                        domRoot.appendChild(layer.dom);
9229                    }
9230                }
9231            }
9232        },
9233
9234        // Iterate each layer
9235        eachLayer: function (cb, context) {
9236            var zlevelList = this._zlevelList;
9237            var z;
9238            var i;
9239            for (i = 0; i < zlevelList.length; i++) {
9240                z = zlevelList[i];
9241                cb.call(context, this._layers[z], z);
9242            }
9243        },
9244
9245        // Iterate each buildin layer
9246        eachBuiltinLayer: function (cb, context) {
9247            var zlevelList = this._zlevelList;
9248            var layer;
9249            var z;
9250            var i;
9251            for (i = 0; i < zlevelList.length; i++) {
9252                z = zlevelList[i];
9253                layer = this._layers[z];
9254                if (layer.__builtin__) {
9255                    cb.call(context, layer, z);
9256                }
9257            }
9258        },
9259
9260        // Iterate each other layer except buildin layer
9261        eachOtherLayer: function (cb, context) {
9262            var zlevelList = this._zlevelList;
9263            var layer;
9264            var z;
9265            var i;
9266            for (i = 0; i < zlevelList.length; i++) {
9267                z = zlevelList[i];
9268                layer = this._layers[z];
9269                if (!layer.__builtin__) {
9270                    cb.call(context, layer, z);
9271                }
9272            }
9273        },
9274
9275        /**
9276         * 鑾峰彇鎵€鏈夊凡鍒涘缓鐨勫眰
9277         * @param {Array.<module:zrender/Layer>} [prevLayer]
9278         */
9279        getLayers: function () {
9280            return this._layers;
9281        },
9282
9283        _updateLayerStatus: function (list) {
9284
9285            this.eachBuiltinLayer(function (layer, z) {
9286                layer.__dirty = layer.__used = false;
9287            });
9288
9289            function updatePrevLayer(idx) {
9290                if (prevLayer) {
9291                    if (prevLayer.__endIndex !== idx) {
9292                        prevLayer.__dirty = true;
9293                    }
9294                    prevLayer.__endIndex = idx;
9295                }
9296            }
9297
9298            if (this._singleCanvas) {
9299                for (var i = 1; i < list.length; i++) {
9300                    var el = list[i];
9301                    if (el.zlevel !== list[i - 1].zlevel || el.incremental) {
9302                        this._needsManuallyCompositing = true;
9303                        break;
9304                    }
9305                }
9306            }
9307
9308            var prevLayer = null;
9309            var incrementalLayerCount = 0;
9310            for (var i = 0; i < list.length; i++) {
9311                var el = list[i];
9312                var zlevel = el.zlevel;
9313                var layer;
9314                // PENDING If change one incremental element style ?
9315                // TODO Where there are non-incremental elements between incremental elements.
9316                if (el.incremental) {
9317                    layer = this.getLayer(zlevel + INCREMENTAL_INC, this._needsManuallyCompositing);
9318                    layer.incremental = true;
9319                    incrementalLayerCount = 1;
9320                }
9321                else {
9322                    layer = this.getLayer(zlevel + (incrementalLayerCount > 0 ? EL_AFTER_INCREMENTAL_INC : 0), this._needsManuallyCompositing);
9323                }
9324
9325                if (!layer.__builtin__) {
9326                    zrLog('ZLevel ' + zlevel + ' has been used by unkown layer ' + layer.id);
9327                }
9328
9329                if (layer !== prevLayer) {
9330                    layer.__used = true;
9331                    if (layer.__startIndex !== i) {
9332                        layer.__dirty = true;
9333                    }
9334                    layer.__startIndex = i;
9335                    if (!layer.incremental) {
9336                        layer.__drawIndex = i;
9337                    }
9338                    else {
9339                        // Mark layer draw index needs to update.
9340                        layer.__drawIndex = -1;
9341                    }
9342                    updatePrevLayer(i);
9343                    prevLayer = layer;
9344                }
9345                if (el.__dirty) {
9346                    layer.__dirty = true;
9347                    if (layer.incremental && layer.__drawIndex < 0) {
9348                        // Start draw from the first dirty element.
9349                        layer.__drawIndex = i;
9350                    }
9351                }
9352            }
9353
9354            updatePrevLayer(i);
9355
9356            this.eachBuiltinLayer(function (layer, z) {
9357                // Used in last frame but not in this frame. Needs clear
9358                if (!layer.__used && layer.getElementCount() > 0) {
9359                    layer.__dirty = true;
9360                    layer.__startIndex = layer.__endIndex = layer.__drawIndex = 0;
9361                }
9362                // For incremental layer. In case start index changed and no elements are dirty.
9363                if (layer.__dirty && layer.__drawIndex < 0) {
9364                    layer.__drawIndex = layer.__startIndex;
9365                }
9366            });
9367        },
9368
9369        /**
9370         * 娓呴櫎hover灞傚鎵€鏈夊唴瀹�
9371         */
9372        clear: function () {
9373            this.eachBuiltinLayer(this._clearLayer);
9374            return this;
9375        },
9376
9377        _clearLayer: function (layer) {
9378            layer.clear();
9379        },
9380
9381        setBackgroundColor: function (backgroundColor) {
9382            this._backgroundColor = backgroundColor;
9383        },
9384
9385        /**
9386         * 淇敼鎸囧畾zlevel鐨勭粯鍒跺弬鏁�
9387         *
9388         * @param {string} zlevel
9389         * @param {Object} config 閰嶇疆瀵硅薄
9390         * @param {string} [config.clearColor=0] 姣忔娓呯┖鐢诲竷鐨勯鑹�
9391         * @param {string} [config.motionBlur=false] 鏄惁寮€鍚姩鎬佹ā绯�
9392         * @param {number} [config.lastFrameAlpha=0.7]
9393         *                 鍦ㄥ紑鍚姩鎬佹ā绯婄殑鏃跺€欎娇鐢紝涓庝笂涓€甯ф贩鍚堢殑alpha鍊硷紝鍊艰秺澶у熬杩硅秺鏄庢樉
9394         */
9395        configLayer: function (zlevel, config) {
9396            if (config) {
9397                var layerConfig = this._layerConfig;
9398                if (!layerConfig[zlevel]) {
9399                    layerConfig[zlevel] = config;
9400                }
9401                else {
9402                    merge(layerConfig[zlevel], config, true);
9403                }
9404
9405                for (var i = 0; i < this._zlevelList.length; i++) {
9406                    var _zlevel = this._zlevelList[i];
9407                    if (_zlevel === zlevel || _zlevel === zlevel + EL_AFTER_INCREMENTAL_INC) {
9408                        var layer = this._layers[_zlevel];
9409                        merge(layer, layerConfig[zlevel], true);
9410                    }
9411                }
9412            }
9413        },
9414
9415        /**
9416         * 鍒犻櫎鎸囧畾灞�
9417         * @param {number} zlevel 灞傛墍鍦ㄧ殑zlevel
9418         */
9419        delLayer: function (zlevel) {
9420            var layers = this._layers;
9421            var zlevelList = this._zlevelList;
9422            var layer = layers[zlevel];
9423            if (!layer) {
9424                return;
9425            }
9426            layer.dom.parentNode.removeChild(layer.dom);
9427            delete layers[zlevel];
9428
9429            zlevelList.splice(indexOf(zlevelList, zlevel), 1);
9430        },
9431
9432        /**
9433         * 鍖哄煙澶у皬鍙樺寲鍚庨噸缁�
9434         */
9435        resize: function (width, height) {
9436            if (!this._domRoot.style) { // Maybe in node or worker
9437                if (width == null || height == null) {
9438                    return;
9439                }
9440                this._width = width;
9441                this._height = height;
9442
9443                this.getLayer(CANVAS_ZLEVEL).resize(width, height);
9444            }
9445            else {
9446                var domRoot = this._domRoot;
9447                // FIXME Why ?
9448                domRoot.style.display = 'none';
9449
9450                // Save input w/h
9451                var opts = this._opts;
9452                width != null && (opts.width = width);
9453                height != null && (opts.height = height);
9454
9455                width = this._getSize(0);
9456                height = this._getSize(1);
9457
9458                domRoot.style.display = '';
9459
9460                // 浼樺寲娌℃湁瀹為檯鏀瑰彉鐨剅esize
9461                if (this._width != width || height != this._height) {
9462                    domRoot.style.width = width + 'px';
9463                    domRoot.style.height = height + 'px';
9464
9465                    for (var id in this._layers) {
9466                        if (this._layers.hasOwnProperty(id)) {
9467                            this._layers[id].resize(width, height);
9468                        }
9469                    }
9470                    each$1(this._progressiveLayers, function (layer) {
9471                        layer.resize(width, height);
9472                    });
9473
9474                    this.refresh(true);
9475                }
9476
9477                this._width = width;
9478                this._height = height;
9479
9480            }
9481            return this;
9482        },
9483
9484        /**
9485         * 娓呴櫎鍗曠嫭鐨勪竴涓眰
9486         * @param {number} zlevel
9487         */
9488        clearLayer: function (zlevel) {
9489            var layer = this._layers[zlevel];
9490            if (layer) {
9491                layer.clear();
9492            }
9493        },
9494
9495        /**
9496         * 閲婃斁
9497         */
9498        dispose: function () {
9499            this.root.innerHTML = '';
9500
9501            this.root =
9502                this.storage =
9503
9504                    this._domRoot =
9505                        this._layers = null;
9506        },
9507
9508        /**
9509         * Get canvas which has all thing rendered
9510         * @param {Object} opts
9511         * @param {string} [opts.backgroundColor]
9512         * @param {number} [opts.pixelRatio]
9513         */
9514        getRenderedCanvas: function (opts) {
9515            opts = opts || {};
9516            if (this._singleCanvas && !this._compositeManually) {
9517                return this._layers[CANVAS_ZLEVEL].dom;
9518            }
9519
9520            var imageLayer = new Layer('image', this, opts.pixelRatio || this.dpr);
9521            imageLayer.initContext();
9522            imageLayer.clear(false, opts.backgroundColor || this._backgroundColor);
9523
9524            if (opts.pixelRatio <= this.dpr) {
9525                this.refresh();
9526
9527                var width = imageLayer.dom.width;
9528                var height = imageLayer.dom.height;
9529                var ctx = imageLayer.ctx;
9530                this.eachLayer(function (layer) {
9531                    if (layer.__builtin__) {
9532                        ctx.drawImage(layer.dom, 0, 0, width, height);
9533                    }
9534                    else if (layer.renderToCanvas) {
9535                        imageLayer.ctx.save();
9536                        layer.renderToCanvas(imageLayer.ctx);
9537                        imageLayer.ctx.restore();
9538                    }
9539                });
9540            }
9541            else {
9542                // PENDING, echarts-gl and incremental rendering.
9543                var scope = {};
9544                var displayList = this.storage.getDisplayList(true);
9545                for (var i = 0; i < displayList.length; i++) {
9546                    var el = displayList[i];
9547                    this._doPaintEl(el, imageLayer, true, scope);
9548                }
9549            }
9550
9551            return imageLayer.dom;
9552        },
9553        /**
9554         * 鑾峰彇缁樺浘鍖哄煙瀹藉害
9555         */
9556        getWidth: function () {
9557            return this._width;
9558        },
9559
9560        /**
9561         * 鑾峰彇缁樺浘鍖哄煙楂樺害
9562         */
9563        getHeight: function () {
9564            return this._height;
9565        },
9566
9567        _getSize: function (whIdx) {
9568            var opts = this._opts;
9569            var wh = ['width', 'height'][whIdx];
9570            var cwh = ['clientWidth', 'clientHeight'][whIdx];
9571            var plt = ['paddingLeft', 'paddingTop'][whIdx];
9572            var prb = ['paddingRight', 'paddingBottom'][whIdx];
9573
9574            if (opts[wh] != null && opts[wh] !== 'auto') {
9575                return parseFloat(opts[wh]);
9576            }
9577
9578            var root = this.root;
9579            // IE8 does not support getComputedStyle, but it use VML.
9580            var stl = document.defaultView.getComputedStyle(root);
9581
9582            return (
9583                (root[cwh] || parseInt10(stl[wh]) || parseInt10(root.style[wh]))
9584                - (parseInt10(stl[plt]) || 0)
9585                - (parseInt10(stl[prb]) || 0)
9586            ) | 0;
9587        },
9588
9589        pathToImage: function (path, dpr) {
9590            dpr = dpr || this.dpr;
9591
9592            var canvas = document.createElement('canvas');
9593            var ctx = canvas.getContext('2d');
9594            var rect = path.getBoundingRect();
9595            var style = path.style;
9596            var shadowBlurSize = style.shadowBlur * dpr;
9597            var shadowOffsetX = style.shadowOffsetX * dpr;
9598            var shadowOffsetY = style.shadowOffsetY * dpr;
9599            var lineWidth = style.hasStroke() ? style.lineWidth : 0;
9600
9601            var leftMargin = Math.max(lineWidth / 2, -shadowOffsetX + shadowBlurSize);
9602            var rightMargin = Math.max(lineWidth / 2, shadowOffsetX + shadowBlurSize);
9603            var topMargin = Math.max(lineWidth / 2, -shadowOffsetY + shadowBlurSize);
9604            var bottomMargin = Math.max(lineWidth / 2, shadowOffsetY + shadowBlurSize);
9605            var width = rect.width + leftMargin + rightMargin;
9606            var height = rect.height + topMargin + bottomMargin;
9607
9608            canvas.width = width * dpr;
9609            canvas.height = height * dpr;
9610
9611            ctx.scale(dpr, dpr);
9612            ctx.clearRect(0, 0, width, height);
9613            ctx.dpr = dpr;
9614
9615            var pathTransform = {
9616                position: path.position,
9617                rotation: path.rotation,
9618                scale: path.scale
9619            };
9620            path.position = [leftMargin - rect.x, topMargin - rect.y];
9621            path.rotation = 0;
9622            path.scale = [1, 1];
9623            path.updateTransform();
9624            if (path) {
9625                path.brush(ctx);
9626            }
9627
9628            var ImageShape = ZImage;
9629            var imgShape = new ImageShape({
9630                style: {
9631                    x: 0,
9632                    y: 0,
9633                    image: canvas
9634                }
9635            });
9636
9637            if (pathTransform.position != null) {
9638                imgShape.position = path.position = pathTransform.position;
9639            }
9640
9641            if (pathTransform.rotation != null) {
9642                imgShape.rotation = path.rotation = pathTransform.rotation;
9643            }
9644
9645            if (pathTransform.scale != null) {
9646                imgShape.scale = path.scale = pathTransform.scale;
9647            }
9648
9649            return imgShape;
9650        }
9651    };
9652
9653    /**
9654     * 浜嬩欢杈呭姪绫�
9655     * @module zrender/core/event
9656     * @author Kener (@Kener-鏋楀嘲, [email protected])
9657     */
9658
9659    var isDomLevel2 = (typeof window !== 'undefined') && !!window.addEventListener;
9660
9661    var MOUSE_EVENT_REG = /^(?:mouse|pointer|contextmenu|drag|drop)|click/;
9662
9663    function getBoundingClientRect(el) {
9664        // BlackBerry 5, iOS 3 (original iPhone) don't have getBoundingRect
9665        return el.getBoundingClientRect ? el.getBoundingClientRect() : {left: 0, top: 0};
9666    }
9667
9668// `calculate` is optional, default false
9669    function clientToLocal(el, e, out, calculate) {
9670        out = out || {};
9671
9672        // According to the W3C Working Draft, offsetX and offsetY should be relative
9673        // to the padding edge of the target element. The only browser using this convention
9674        // is IE. Webkit uses the border edge, Opera uses the content edge, and FireFox does
9675        // not support the properties.
9676        // (see http://www.jacklmoore.com/notes/mouse-position/)
9677        // In zr painter.dom, padding edge equals to border edge.
9678
9679        // FIXME
9680        // When mousemove event triggered on ec tooltip, target is not zr painter.dom, and
9681        // offsetX/Y is relative to e.target, where the calculation of zrX/Y via offsetX/Y
9682        // is too complex. So css-transfrom dont support in this case temporarily.
9683        if (calculate || !env$1.canvasSupported) {
9684            defaultGetZrXY(el, e, out);
9685        }
9686        // Caution: In FireFox, layerX/layerY Mouse position relative to the closest positioned
9687        // ancestor element, so we should make sure el is positioned (e.g., not position:static).
9688        // BTW1, Webkit don't return the same results as FF in non-simple cases (like add
9689        // zoom-factor, overflow / opacity layers, transforms ...)
9690        // BTW2, (ev.offsetY || ev.pageY - $(ev.target).offset().top) is not correct in preserve-3d.
9691        // <https://bugs.jquery.com/ticket/8523#comment:14>
9692        // BTW3, In ff, offsetX/offsetY is always 0.
9693        else if (env$1.browser.firefox && e.layerX != null && e.layerX !== e.offsetX) {
9694            out.zrX = e.layerX;
9695            out.zrY = e.layerY;
9696        }
9697        // For IE6+, chrome, safari, opera. (When will ff support offsetX?)
9698        else if (e.offsetX != null) {
9699            out.zrX = e.offsetX;
9700            out.zrY = e.offsetY;
9701        }
9702        // For some other device, e.g., IOS safari.
9703        else {
9704            defaultGetZrXY(el, e, out);
9705        }
9706
9707        return out;
9708    }
9709
9710    function defaultGetZrXY(el, e, out) {
9711        // This well-known method below does not support css transform.
9712        var box = getBoundingClientRect(el);
9713        out.zrX = e.clientX - box.left;
9714        out.zrY = e.clientY - box.top;
9715    }
9716
9717    /**
9718     * 濡傛灉瀛樺湪绗笁鏂瑰祵鍏ョ殑涓€浜沝om瑙﹀彂鐨勪簨浠讹紝鎴杢ouch浜嬩欢锛岄渶瑕佽浆鎹竴涓嬩簨浠跺潗鏍�.
9719     * `calculate` is optional, default false.
9720     */
9721    function normalizeEvent(el, e, calculate) {
9722
9723        e = e || window.event;
9724
9725        if (e.zrX != null) {
9726            return e;
9727        }
9728
9729        var eventType = e.type;
9730        var isTouch = eventType && eventType.indexOf('touch') >= 0;
9731
9732        if (!isTouch) {
9733            clientToLocal(el, e, e, calculate);
9734            e.zrDelta = (e.wheelDelta) ? e.wheelDelta / 120 : -(e.detail || 0) / 3;
9735        }
9736        else {
9737            var touch = eventType != 'touchend'
9738                ? e.targetTouches[0]
9739                : e.changedTouches[0];
9740            touch && clientToLocal(el, touch, e, calculate);
9741        }
9742
9743        // Add which for click: 1 === left; 2 === middle; 3 === right; otherwise: 0;
9744        // See jQuery: https://github.com/jquery/jquery/blob/master/src/event.js
9745        // If e.which has been defined, if may be readonly,
9746        // see: https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/which
9747        var button = e.button;
9748        if (e.which == null && button !== undefined && MOUSE_EVENT_REG.test(e.type)) {
9749            e.which = (button & 1 ? 1 : (button & 2 ? 3 : (button & 4 ? 2 : 0)));
9750        }
9751
9752        return e;
9753    }
9754
9755    /**
9756     * @param {HTMLElement} el
9757     * @param {string} name
9758     * @param {Function} handler
9759     */
9760    function addEventListener(el, name, handler) {
9761        if (isDomLevel2) {
9762            // Reproduct the console warning:
9763            // [Violation] Added non-passive event listener to a scroll-blocking <some> event.
9764            // Consider marking event handler as 'passive' to make the page more responsive.
9765            // Just set console log level: verbose in chrome dev tool.
9766            // then the warning log will be printed when addEventListener called.
9767            // See https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md
9768            // We have not yet found a neat way to using passive. Because in zrender the dom event
9769            // listener delegate all of the upper events of element. Some of those events need
9770            // to prevent default. For example, the feature `preventDefaultMouseMove` of echarts.
9771            // Before passive can be adopted, these issues should be considered:
9772            // (1) Whether and how a zrender user specifies an event listener passive. And by default,
9773            // passive or not.
9774            // (2) How to tread that some zrender event listener is passive, and some is not. If
9775            // we use other way but not preventDefault of mousewheel and touchmove, browser
9776            // compatibility should be handled.
9777
9778            // var opts = (env.passiveSupported && name === 'mousewheel')
9779            //     ? {passive: true}
9780            //     // By default, the third param of el.addEventListener is `capture: false`.
9781            //     : void 0;
9782            // el.addEventListener(name, handler /* , opts */);
9783            el.addEventListener(name, handler);
9784        }
9785        else {
9786            el.attachEvent('on' + name, handler);
9787        }
9788    }
9789
9790    function removeEventListener(el, name, handler) {
9791        if (isDomLevel2) {
9792            el.removeEventListener(name, handler);
9793        }
9794        else {
9795            el.detachEvent('on' + name, handler);
9796        }
9797    }
9798
9799    /**
9800     * preventDefault and stopPropagation.
9801     * Notice: do not do that in zrender. Upper application
9802     * do that if necessary.
9803     *
9804     * @memberOf module:zrender/core/event
9805     * @method
9806     * @param {Event} e : event瀵硅薄
9807     */
9808    var stop = isDomLevel2
9809        ? function (e) {
9810            e.preventDefault();
9811            e.stopPropagation();
9812            e.cancelBubble = true;
9813        }
9814        : function (e) {
9815            e.returnValue = false;
9816            e.cancelBubble = true;
9817        };
9818
9819    function notLeftMouse(e) {
9820        // If e.which is undefined, considered as left mouse event.
9821        return e.which > 1;
9822    }
9823
9824    /**
9825     * 鍔ㄧ敾涓荤被, 璋冨害鍜岀鐞嗘墍鏈夊姩鐢绘帶鍒跺櫒
9826     *
9827     * @module zrender/animation/Animation
9828     * @author pissang(https://github.com/pissang)
9829     */
9830// TODO Additive animation
9831// http://iosoteric.com/additive-animations-animatewithduration-in-ios-8/
9832// https://developer.apple.com/videos/wwdc2014/#236
9833
9834    /**
9835     * @typedef {Object} IZRenderStage
9836     * @property {Function} update
9837     */
9838
9839    /**
9840     * @alias module:zrender/animation/Animation
9841     * @constructor
9842     * @param {Object} [options]
9843     * @param {Function} [options.onframe]
9844     * @param {IZRenderStage} [options.stage]
9845     * @example
9846     *     var animation = new Animation();
9847     *     var obj = {
9848 *         x: 100,
9849 *         y: 100
9850 *     };
9851     *     animation.animate(node.position)
9852     *         .when(1000, {
9853 *             x: 500,
9854 *             y: 500
9855 *         })
9856     *         .when(2000, {
9857 *             x: 100,
9858 *             y: 100
9859 *         })
9860     *         .start('spline');
9861     */
9862    var Animation = function (options) {
9863
9864        options = options || {};
9865
9866        this.stage = options.stage || {};
9867
9868        this.onframe = options.onframe || function() {};
9869
9870        // private properties
9871        this._clips = [];
9872
9873        this._running = false;
9874
9875        this._time;
9876
9877        this._pausedTime;
9878
9879        this._pauseStart;
9880
9881        this._paused = false;
9882
9883        Eventful.call(this);
9884    };
9885
9886    Animation.prototype = {
9887
9888        constructor: Animation,
9889        /**
9890         * 娣诲姞 clip
9891         * @param {module:zrender/animation/Clip} clip
9892         */
9893        addClip: function (clip) {
9894            this._clips.push(clip);
9895        },
9896        /**
9897         * 娣诲姞 animator
9898         * @param {module:zrender/animation/Animator} animator
9899         */
9900        addAnimator: function (animator) {
9901            animator.animation = this;
9902            var clips = animator.getClips();
9903            for (var i = 0; i < clips.length; i++) {
9904                this.addClip(clips[i]);
9905            }
9906        },
9907        /**
9908         * 鍒犻櫎鍔ㄧ敾鐗囨
9909         * @param {module:zrender/animation/Clip} clip
9910         */
9911        removeClip: function(clip) {
9912            var idx = indexOf(this._clips, clip);
9913            if (idx >= 0) {
9914                this._clips.splice(idx, 1);
9915            }
9916        },
9917
9918        /**
9919         * 鍒犻櫎鍔ㄧ敾鐗囨
9920         * @param {module:zrender/animation/Animator} animator
9921         */
9922        removeAnimator: function (animator) {
9923            var clips = animator.getClips();
9924            for (var i = 0; i < clips.length; i++) {
9925                this.removeClip(clips[i]);
9926            }
9927            animator.animation = null;
9928        },
9929
9930        _update: function() {
9931            var time = new Date().getTime() - this._pausedTime;
9932            var delta = time - this._time;
9933            var clips = this._clips;
9934            var len = clips.length;
9935
9936            var deferredEvents = [];
9937            var deferredClips = [];
9938            for (var i = 0; i < len; i++) {
9939                var clip = clips[i];
9940                var e = clip.step(time, delta);
9941                // Throw out the events need to be called after
9942                // stage.update, like destroy
9943                if (e) {
9944                    deferredEvents.push(e);
9945                    deferredClips.push(clip);
9946                }
9947            }
9948
9949            // Remove the finished clip
9950            for (var i = 0; i < len;) {
9951                if (clips[i]._needsRemove) {
9952                    clips[i] = clips[len - 1];
9953                    clips.pop();
9954                    len--;
9955                }
9956                else {
9957                    i++;
9958                }
9959            }
9960
9961            len = deferredEvents.length;
9962            for (var i = 0; i < len; i++) {
9963                deferredClips[i].fire(deferredEvents[i]);
9964            }
9965
9966            this._time = time;
9967
9968            this.onframe(delta);
9969
9970            // 'frame' should be triggered before stage, because upper application
9971            // depends on the sequence (e.g., echarts-stream and finish
9972            // event judge)
9973            this.trigger('frame', delta);
9974
9975            if (this.stage.update) {
9976                this.stage.update();
9977            }
9978        },
9979
9980        _startLoop: function () {
9981            var self = this;
9982
9983            this._running = true;
9984
9985            function step() {
9986                if (self._running) {
9987
9988                    requestAnimationFrame(step);
9989
9990                    !self._paused && self._update();
9991                }
9992            }
9993
9994            requestAnimationFrame(step);
9995        },
9996
9997        /**
9998         * Start animation.
9999         */
10000        start: function () {
10001
10002            this._time = new Date().getTime();
10003            this._pausedTime = 0;
10004
10005            this._startLoop();
10006        },
10007
10008        /**
10009         * Stop animation.
10010         */
10011        stop: function () {
10012            this._running = false;
10013        },
10014
10015        /**
10016         * Pause animation.
10017         */
10018        pause: function () {
10019            if (!this._paused) {
10020                this._pauseStart = new Date().getTime();
10021                this._paused = true;
10022            }
10023        },
10024
10025        /**
10026         * Resume animation.
10027         */
10028        resume: function () {
10029            if (this._paused) {
10030                this._pausedTime += (new Date().getTime()) - this._pauseStart;
10031                this._paused = false;
10032            }
10033        },
10034
10035        /**
10036         * Clear animation.
10037         */
10038        clear: function () {
10039            this._clips = [];
10040        },
10041
10042        /**
10043         * Whether animation finished.
10044         */
10045        isFinished: function () {
10046            return !this._clips.length;
10047        },
10048
10049        /**
10050         * Creat animator for a target, whose props can be animated.
10051         *
10052         * @param  {Object} target
10053         * @param  {Object} options
10054         * @param  {boolean} [options.loop=false] Whether loop animation.
10055         * @param  {Function} [options.getter=null] Get value from target.
10056         * @param  {Function} [options.setter=null] Set value to target.
10057         * @return {module:zrender/animation/Animation~Animator}
10058         */
10059        // TODO Gap
10060        animate: function (target, options) {
10061            options = options || {};
10062
10063            var animator = new Animator(
10064                target,
10065                options.loop,
10066                options.getter,
10067                options.setter
10068            );
10069
10070            this.addAnimator(animator);
10071
10072            return animator;
10073        }
10074    };
10075
10076    mixin(Animation, Eventful);
10077
10078    /**
10079     * Only implements needed gestures for mobile.
10080     */
10081
10082    var GestureMgr = function () {
10083
10084        /**
10085         * @private
10086         * @type {Array.<Object>}
10087         */
10088        this._track = [];
10089    };
10090
10091    GestureMgr.prototype = {
10092
10093        constructor: GestureMgr,
10094
10095        recognize: function (event, target, root) {
10096            this._doTrack(event, target, root);
10097            return this._recognize(event);
10098        },
10099
10100        clear: function () {
10101            this._track.length = 0;
10102            return this;
10103        },
10104
10105        _doTrack: function (event, target, root) {
10106            var touches = event.touches;
10107
10108            if (!touches) {
10109                return;
10110            }
10111
10112            var trackItem = {
10113                points: [],
10114                touches: [],
10115                target: target,
10116                event: event
10117            };
10118
10119            for (var i = 0, len = touches.length; i < len; i++) {
10120                var touch = touches[i];
10121                var pos = clientToLocal(root, touch, {});
10122                trackItem.points.push([pos.zrX, pos.zrY]);
10123                trackItem.touches.push(touch);
10124            }
10125
10126            this._track.push(trackItem);
10127        },
10128
10129        _recognize: function (event) {
10130            for (var eventName in recognizers) {
10131                if (recognizers.hasOwnProperty(eventName)) {
10132                    var gestureInfo = recognizers[eventName](this._track, event);
10133                    if (gestureInfo) {
10134                        return gestureInfo;
10135                    }
10136                }
10137            }
10138        }
10139    };
10140
10141    function dist$1(pointPair) {
10142        var dx = pointPair[1][0] - pointPair[0][0];
10143        var dy = pointPair[1][1] - pointPair[0][1];
10144
10145        return Math.sqrt(dx * dx + dy * dy);
10146    }
10147
10148    function center(pointPair) {
10149        return [
10150            (pointPair[0][0] + pointPair[1][0]) / 2,
10151            (pointPair[0][1] + pointPair[1][1]) / 2
10152        ];
10153    }
10154
10155    var recognizers = {
10156
10157        pinch: function (track, event) {
10158            var trackLen = track.length;
10159
10160            if (!trackLen) {
10161                return;
10162            }
10163
10164            var pinchEnd = (track[trackLen - 1] || {}).points;
10165            var pinchPre = (track[trackLen - 2] || {}).points || pinchEnd;
10166
10167            if (pinchPre
10168                && pinchPre.length > 1
10169                && pinchEnd
10170                && pinchEnd.length > 1
10171            ) {
10172                var pinchScale = dist$1(pinchEnd) / dist$1(pinchPre);
10173                !isFinite(pinchScale) && (pinchScale = 1);
10174
10175                event.pinchScale = pinchScale;
10176
10177                var pinchCenter = center(pinchEnd);
10178                event.pinchX = pinchCenter[0];
10179                event.pinchY = pinchCenter[1];
10180
10181                return {
10182                    type: 'pinch',
10183                    target: track[0].target,
10184                    event: event
10185                };
10186            }
10187        }
10188
10189        // Only pinch currently.
10190    };
10191
10192    var TOUCH_CLICK_DELAY = 300;
10193
10194    var mouseHandlerNames = [
10195        'click', 'dblclick', 'mousewheel', 'mouseout',
10196        'mouseup', 'mousedown', 'mousemove', 'contextmenu'
10197    ];
10198
10199    var touchHandlerNames = [
10200        'touchstart', 'touchend', 'touchmove'
10201    ];
10202
10203    var pointerEventNames = {
10204        pointerdown: 1, pointerup: 1, pointermove: 1, pointerout: 1
10205    };
10206
10207    var pointerHandlerNames = map(mouseHandlerNames, function (name) {
10208        var nm = name.replace('mouse', 'pointer');
10209        return pointerEventNames[nm] ? nm : name;
10210    });
10211
10212    function eventNameFix(name) {
10213        return (name === 'mousewheel' && env$1.browser.firefox) ? 'DOMMouseScroll' : name;
10214    }
10215
10216    function processGesture(proxy, event, stage) {
10217        var gestureMgr = proxy._gestureMgr;
10218
10219        stage === 'start' && gestureMgr.clear();
10220
10221        var gestureInfo = gestureMgr.recognize(
10222            event,
10223            proxy.handler.findHover(event.zrX, event.zrY, null).target,
10224            proxy.dom
10225        );
10226
10227        stage === 'end' && gestureMgr.clear();
10228
10229        // Do not do any preventDefault here. Upper application do that if necessary.
10230        if (gestureInfo) {
10231            var type = gestureInfo.type;
10232            event.gestureEvent = type;
10233
10234            proxy.handler.dispatchToElement({target: gestureInfo.target}, type, gestureInfo.event);
10235        }
10236    }
10237
10238// function onMSGestureChange(proxy, event) {
10239//     if (event.translationX || event.translationY) {
10240//         // mousemove is carried by MSGesture to reduce the sensitivity.
10241//         proxy.handler.dispatchToElement(event.target, 'mousemove', event);
10242//     }
10243//     if (event.scale !== 1) {
10244//         event.pinchX = event.offsetX;
10245//         event.pinchY = event.offsetY;
10246//         event.pinchScale = event.scale;
10247//         proxy.handler.dispatchToElement(event.target, 'pinch', event);
10248//     }
10249// }
10250
10251    /**
10252     * Prevent mouse event from being dispatched after Touch Events action
10253     * @see <https://github.com/deltakosh/handjs/blob/master/src/hand.base.js>
10254     * 1. Mobile browsers dispatch mouse events 300ms after touchend.
10255     * 2. Chrome for Android dispatch mousedown for long-touch about 650ms
10256     * Result: Blocking Mouse Events for 700ms.
10257     */
10258    function setTouchTimer(instance) {
10259        instance._touching = true;
10260        clearTimeout(instance._touchTimer);
10261        instance._touchTimer = setTimeout(function () {
10262            instance._touching = false;
10263        }, 700);
10264    }
10265
10266
10267    var domHandlers = {
10268        /**
10269         * Mouse move handler
10270         * @inner
10271         * @param {Event} event
10272         */
10273        mousemove: function (event) {
10274            event = normalizeEvent(this.dom, event);
10275
10276            this.trigger('mousemove', event);
10277        },
10278
10279        /**
10280         * Mouse out handler
10281         * @inner
10282         * @param {Event} event
10283         */
10284        mouseout: function (event) {
10285            event = normalizeEvent(this.dom, event);
10286
10287            var element = event.toElement || event.relatedTarget;
10288            if (element != this.dom) {
10289                while (element && element.nodeType != 9) {
10290                    // 蹇界暐鍖呭惈鍦╮oot涓殑dom寮曡捣鐨刴ouseOut
10291                    if (element === this.dom) {
10292                        return;
10293                    }
10294
10295                    element = element.parentNode;
10296                }
10297            }
10298
10299            this.trigger('mouseout', event);
10300        },
10301
10302        /**
10303         * Touch寮€濮嬪搷搴斿嚱鏁�
10304         * @inner
10305         * @param {Event} event
10306         */
10307        touchstart: function (event) {
10308            // Default mouse behaviour should not be disabled here.
10309            // For example, page may needs to be slided.
10310            event = normalizeEvent(this.dom, event);
10311
10312            // Mark touch, which is useful in distinguish touch and
10313            // mouse event in upper applicatoin.
10314            event.zrByTouch = true;
10315
10316            this._lastTouchMoment = new Date();
10317
10318            processGesture(this, event, 'start');
10319
10320            // In touch device, trigger `mousemove`(`mouseover`) should
10321            // be triggered, and must before `mousedown` triggered.
10322            domHandlers.mousemove.call(this, event);
10323
10324            domHandlers.mousedown.call(this, event);
10325
10326            setTouchTimer(this);
10327        },
10328
10329        /**
10330         * Touch绉诲姩鍝嶅簲鍑芥暟
10331         * @inner
10332         * @param {Event} event
10333         */
10334        touchmove: function (event) {
10335
10336            event = normalizeEvent(this.dom, event);
10337
10338            // Mark touch, which is useful in distinguish touch and
10339            // mouse event in upper applicatoin.
10340            event.zrByTouch = true;
10341
10342            processGesture(this, event, 'change');
10343
10344            // Mouse move should always be triggered no matter whether
10345            // there is gestrue event, because mouse move and pinch may
10346            // be used at the same time.
10347            domHandlers.mousemove.call(this, event);
10348
10349            setTouchTimer(this);
10350        },
10351
10352        /**
10353         * Touch缁撴潫鍝嶅簲鍑芥暟
10354         * @inner
10355         * @param {Event} event
10356         */
10357        touchend: function (event) {
10358
10359            event = normalizeEvent(this.dom, event);
10360
10361            // Mark touch, which is useful in distinguish touch and
10362            // mouse event in upper applicatoin.
10363            event.zrByTouch = true;
10364
10365            processGesture(this, event, 'end');
10366
10367            domHandlers.mouseup.call(this, event);
10368
10369            // Do not trigger `mouseout` here, in spite of `mousemove`(`mouseover`) is
10370            // triggered in `touchstart`. This seems to be illogical, but by this mechanism,
10371            // we can conveniently implement "hover style" in both PC and touch device just
10372            // by listening to `mouseover` to add "hover style" and listening to `mouseout`
10373            // to remove "hover style" on an element, without any additional code for
10374            // compatibility. (`mouseout` will not be triggered in `touchend`, so "hover
10375            // style" will remain for user view)
10376
10377            // click event should always be triggered no matter whether
10378            // there is gestrue event. System click can not be prevented.
10379            if (+new Date() - this._lastTouchMoment < TOUCH_CLICK_DELAY) {
10380                domHandlers.click.call(this, event);
10381            }
10382
10383            setTouchTimer(this);
10384        },
10385
10386        pointerdown: function (event) {
10387            domHandlers.mousedown.call(this, event);
10388
10389            // if (useMSGuesture(this, event)) {
10390            //     this._msGesture.addPointer(event.pointerId);
10391            // }
10392        },
10393
10394        pointermove: function (event) {
10395            // FIXME
10396            // pointermove is so sensitive that it always triggered when
10397            // tap(click) on touch screen, which affect some judgement in
10398            // upper application. So, we dont support mousemove on MS touch
10399            // device yet.
10400            if (!isPointerFromTouch(event)) {
10401                domHandlers.mousemove.call(this, event);
10402            }
10403        },
10404
10405        pointerup: function (event) {
10406            domHandlers.mouseup.call(this, event);
10407        },
10408
10409        pointerout: function (event) {
10410            // pointerout will be triggered when tap on touch screen
10411            // (IE11+/Edge on MS Surface) after click event triggered,
10412            // which is inconsistent with the mousout behavior we defined
10413            // in touchend. So we unify them.
10414            // (check domHandlers.touchend for detailed explanation)
10415            if (!isPointerFromTouch(event)) {
10416                domHandlers.mouseout.call(this, event);
10417            }
10418        }
10419    };
10420
10421    function isPointerFromTouch(event) {
10422        var pointerType = event.pointerType;
10423        return pointerType === 'pen' || pointerType === 'touch';
10424    }
10425
10426// function useMSGuesture(handlerProxy, event) {
10427//     return isPointerFromTouch(event) && !!handlerProxy._msGesture;
10428// }
10429
10430// Common handlers
10431    each$1(['click', 'mousedown', 'mouseup', 'mousewheel', 'dblclick', 'contextmenu'], function (name) {
10432        domHandlers[name] = function (event) {
10433            event = normalizeEvent(this.dom, event);
10434            this.trigger(name, event);
10435        };
10436    });
10437
10438    /**
10439     * 涓烘帶鍒剁被瀹炰緥鍒濆鍖杁om 浜嬩欢澶勭悊鍑芥暟
10440     *
10441     * @inner
10442     * @param {module:zrender/Handler} instance 鎺у埗绫诲疄渚�
10443     */
10444    function initDomHandler(instance) {
10445        each$1(touchHandlerNames, function (name) {
10446            instance._handlers[name] = bind(domHandlers[name], instance);
10447        });
10448
10449        each$1(pointerHandlerNames, function (name) {
10450            instance._handlers[name] = bind(domHandlers[name], instance);
10451        });
10452
10453        each$1(mouseHandlerNames, function (name) {
10454            instance._handlers[name] = makeMouseHandler(domHandlers[name], instance);
10455        });
10456
10457        function makeMouseHandler(fn, instance) {
10458            return function () {
10459                if (instance._touching) {
10460                    return;
10461                }
10462                return fn.apply(instance, arguments);
10463            };
10464        }
10465    }
10466
10467
10468    function HandlerDomProxy(dom) {
10469        Eventful.call(this);
10470
10471        this.dom = dom;
10472
10473        /**
10474         * @private
10475         * @type {boolean}
10476         */
10477        this._touching = false;
10478
10479        /**
10480         * @private
10481         * @type {number}
10482         */
10483        this._touchTimer;
10484
10485        /**
10486         * @private
10487         * @type {module:zrender/core/GestureMgr}
10488         */
10489        this._gestureMgr = new GestureMgr();
10490
10491        this._handlers = {};
10492
10493        initDomHandler(this);
10494
10495        if (env$1.pointerEventsSupported) { // Only IE11+/Edge
10496            // 1. On devices that both enable touch and mouse (e.g., MS Surface and lenovo X240),
10497            // IE11+/Edge do not trigger touch event, but trigger pointer event and mouse event
10498            // at the same time.
10499            // 2. On MS Surface, it probablely only trigger mousedown but no mouseup when tap on
10500            // screen, which do not occurs in pointer event.
10501            // So we use pointer event to both detect touch gesture and mouse behavior.
10502            mountHandlers(pointerHandlerNames, this);
10503
10504            // FIXME
10505            // Note: MS Gesture require CSS touch-action set. But touch-action is not reliable,
10506            // which does not prevent defuault behavior occasionally (which may cause view port
10507            // zoomed in but use can not zoom it back). And event.preventDefault() does not work.
10508            // So we have to not to use MSGesture and not to supp
10508ort touchmove and pinch on MS
10509            // touch screen. And we only support click behavior on MS touch screen now.
10510
10511            // MS Gesture Event is only supported on IE11+/Edge and on Windows 8+.
10512            // We dont support touch on IE on win7.
10513            // See <https://msdn.microsoft.com/en-us/library/dn433243(v=vs.85).aspx>
10514            // if (typeof MSGesture === 'function') {
10515            //     (this._msGesture = new MSGesture()).target = dom; // jshint ignore:line
10516            //     dom.addEventListener('MSGestureChange', onMSGestureChange);
10517            // }
10518        }
10519        else {
10520            if (env$1.touchEventsSupported) {
10521                mountHandlers(touchHandlerNames, this);
10522                // Handler of 'mouseout' event is needed in touch mode, which will be mounted below.
10523                // addEventListener(root, 'mouseout', this._mouseoutHandler);
10524            }
10525
10526            // 1. Considering some devices that both enable touch and mouse event (like on MS Surface
10527            // and lenovo X240, @see #2350), we make mouse event be always listened, otherwise
10528            // mouse event can not be handle in those devices.
10529            // 2. On MS Surface, Chrome will trigger both touch event and mouse event. How to prevent
10530            // mouseevent after touch event triggered, see `setTouchTimer`.
10531            mountHandlers(mouseHandlerNames, this);
10532        }
10533
10534        function mountHandlers(handlerNames, instance) {
10535            each$1(handlerNames, function (name) {
10536                addEventListener(dom, eventNameFix(name), instance._handlers[name]);
10537            }, instance);
10538        }
10539    }
10540
10541    var handlerDomProxyProto = HandlerDomProxy.prototype;
10542    handlerDomProxyProto.dispose = function () {
10543        var handlerNames = mouseHandlerNames.concat(touchHandlerNames);
10544
10545        for (var i = 0; i < handlerNames.length; i++) {
10546            var name = handlerNames[i];
10547            removeEventListener(this.dom, eventNameFix(name), this._handlers[name]);
10548        }
10549    };
10550
10551    handlerDomProxyProto.setCursor = function (cursorStyle) {
10552        this.dom.style && (this.dom.style.cursor = cursorStyle || 'default');
10553    };
10554
10555    mixin(HandlerDomProxy, Eventful);
10556
10557    /*!
10558* ZRender, a high performance 2d drawing library.
10559*
10560* Copyright (c) 2013, Baidu Inc.
10561* All rights reserved.
10562*
10563* LICENSE
10564* https://github.com/ecomfe/zrender/blob/master/LICENSE.txt
10565*/
10566
10567    var useVML = !env$1.canvasSupported;
10568
10569    var painterCtors = {
10570        canvas: Painter
10571    };
10572
10573    var instances$1 = {};    // ZRender瀹炰緥map绱㈠紩
10574
10575    /**
10576     * @type {string}
10577     */
10578    var version$1 = '4.0.4';
10579
10580    /**
10581     * Initializing a zrender instance
10582     * @param {HTMLElement} dom
10583     * @param {Object} opts
10584     * @param {string} [opts.renderer='canvas'] 'canvas' or 'svg'
10585     * @param {number} [opts.devicePixelRatio]
10586     * @param {number|string} [opts.width] Can be 'auto' (the same as null/undefined)
10587     * @param {number|string} [opts.height] Can be 'auto' (the same as null/undefined)
10588     * @return {module:zrender/ZRender}
10589     */
10590    function init$1(dom, opts) {
10591        var zr = new ZRender(guid(), dom, opts);
10592        instances$1[zr.id] = zr;
10593        return zr;
10594    }
10595
10596    /**
10597     * Dispose zrender instance
10598     * @param {module:zrender/ZRender} zr
10599     */
10600    function dispose$1(zr) {
10601        if (zr) {
10602            zr.dispose();
10603        }
10604        else {
10605            for (var key in instances$1) {
10606                if (instances$1.hasOwnProperty(key)) {
10607                    instances$1[key].dispose();
10608                }
10609            }
10610            instances$1 = {};
10611        }
10612
10613        return this;
10614    }
10615
10616    /**
10617     * Get zrender instance by id
10618     * @param {string} id zrender instance id
10619     * @return {module:zrender/ZRender}
10620     */
10621    function getInstance(id) {
10622        return instances$1[id];
10623    }
10624
10625    function registerPainter(name, Ctor) {
10626        painterCtors[name] = Ctor;
10627    }
10628
10629    function delInstance(id) {
10630        delete instances$1[id];
10631    }
10632
10633    /**
10634     * @module zrender/ZRender
10635     */
10636    /**
10637     * @constructor
10638     * @alias module:zrender/ZRender
10639     * @param {string} id
10640     * @param {HTMLElement} dom
10641     * @param {Object} opts
10642     * @param {string} [opts.renderer='canvas'] 'canvas' or 'svg'
10643     * @param {number} [opts.devicePixelRatio]
10644     * @param {number} [opts.width] Can be 'auto' (the same as null/undefined)
10645     * @param {number} [opts.height] Can be 'auto' (the same as null/undefined)
10646     */
10647    var ZRender = function (id, dom, opts) {
10648
10649        opts = opts || {};
10650
10651        /**
10652         * @type {HTMLDomElement}
10653         */
10654        this.dom = dom;
10655
10656        /**
10657         * @type {string}
10658         */
10659        this.id = id;
10660
10661        var self = this;
10662        var storage = new Storage();
10663
10664        var rendererType = opts.renderer;
10665        // TODO WebGL
10666        if (useVML) {
10667            if (!painterCtors.vml) {
10668                throw new Error('You need to require \'zrender/vml/vml\' to supp
10668ort IE8');
10669            }
10670            rendererType = 'vml';
10671        }
10672        else if (!rendererType || !painterCtors[rendererType]) {
10673            rendererType = 'canvas';
10674        }
10675        var painter = new painterCtors[rendererType](dom, storage, opts, id);
10676
10677        this.storage = storage;
10678        this.painter = painter;
10679
10680        var handerProxy = (!env$1.node && !env$1.worker) ? new HandlerDomProxy(painter.getViewportRoot()) : null;
10681        this.handler = new Handler(storage, painter, handerProxy, painter.root);
10682
10683        /**
10684         * @type {module:zrender/animation/Animation}
10685         */
10686        this.animation = new Animation({
10687            stage: {
10688                update: bind(this.flush, this)
10689            }
10690        });
10691        this.animation.start();
10692
10693        /**
10694         * @type {boolean}
10695         * @private
10696         */
10697        this._needsRefresh;
10698
10699        // 淇敼 storage.delFromStorage, 姣忔鍒犻櫎鍏冪礌涔嬪墠鍒犻櫎鍔ㄧ敾
10700        // FIXME 鏈夌偣ugly
10701        var oldDelFromStorage = storage.delFromStorage;
10702        var oldAddToStorage = storage.addToStorage;
10703
10704        storage.delFromStorage = function (el) {
10705            oldDelFromStorage.call(storage, el);
10706
10707            el && el.removeSelfFromZr(self);
10708        };
10709
10710        storage.addToStorage = function (el) {
10711            oldAddToStorage.call(storage, el);
10712
10713            el.addSelfToZr(self);
10714        };
10715    };
10716
10717    ZRender.prototype = {
10718
10719        constructor: ZRender,
10720        /**
10721         * 鑾峰彇瀹炰緥鍞竴鏍囪瘑
10722         * @return {string}
10723         */
10724        getId: function () {
10725            return this.id;
10726        },
10727
10728        /**
10729         * 娣诲姞鍏冪礌
10730         * @param  {module:zrender/Element} el
10731         */
10732        add: function (el) {
10733            this.storage.addRoot(el);
10734            this._needsRefresh = true;
10735        },
10736
10737        /**
10738         * 鍒犻櫎鍏冪礌
10739         * @param  {module:zrender/Element} el
10740         */
10741        remove: function (el) {
10742            this.storage.delRoot(el);
10743            this._needsRefresh = true;
10744        },
10745
10746        /**
10747         * Change configuration of layer
10748         * @param {string} zLevel
10749         * @param {Object} config
10750         * @param {string} [config.clearColor=0] Clear color
10751         * @param {string} [config.motionBlur=false] If enable motion blur
10752         * @param {number} [config.lastFrameAlpha=0.7] Motion blur factor. Larger value cause longer trailer
10753         */
10754        configLayer: function (zLevel, config) {
10755            if (this.painter.configLayer) {
10756                this.painter.configLayer(zLevel, config);
10757            }
10758            this._needsRefresh = true;
10759        },
10760
10761        /**
10762         * Set background color
10763         * @param {string} backgroundColor
10764         */
10765        setBackgroundColor: function (backgroundColor) {
10766            if (this.painter.setBackgroundColor) {
10767                this.painter.setBackgroundColor(backgroundColor);
10768            }
10769            this._needsRefresh = true;
10770        },
10771
10772        /**
10773         * Repaint the canvas immediately
10774         */
10775        refreshImmediately: function () {
10776            // var start = new Date();
10777            // Clear needsRefresh ahead to avoid something wrong happens in refresh
10778            // Or it will cause zrender refreshes again and again.
10779            this._needsRefresh = false;
10780            this.painter.refresh();
10781            /**
10782             * Avoid trigger zr.refresh in Element#beforeUpdate hook
10783             */
10784            this._needsRefresh = false;
10785            // var end = new Date();
10786            // var log = document.getElementById('log');
10787            // if (log) {
10788            //     log.innerHTML = log.innerHTML + '<br>' + (end - start);
10789            // }
10790        },
10791
10792        /**
10793         * Mark and repaint the canvas in the next frame of browser
10794         */
10795        refresh: function() {
10796            this._needsRefresh = true;
10797        },
10798
10799        /**
10800         * Perform all refresh
10801         */
10802        flush: function () {
10803            var triggerRendered;
10804
10805            if (this._needsRefresh) {
10806                triggerRendered = true;
10807                this.refreshImmediately();
10808            }
10809            if (this._needsRefreshHover) {
10810                triggerRendered = true;
10811                this.refreshHoverImmediately();
10812            }
10813
10814            triggerRendered && this.trigger('rendered');
10815        },
10816
10817        /**
10818         * Add element to hover layer
10819         * @param  {module:zrender/Element} el
10820         * @param {Object} style
10821         */
10822        addHover: function (el, style) {
10823            if (this.painter.addHover) {
10824                this.painter.addHover(el, style);
10825                this.refreshHover();
10826            }
10827        },
10828
10829        /**
10830         * Add element from hover layer
10831         * @param  {module:zrender/Element} el
10832         */
10833        removeHover: function (el) {
10834            if (this.painter.removeHover) {
10835                this.painter.removeHover(el);
10836                this.refreshHover();
10837            }
10838        },
10839
10840        /**
10841         * Clear all hover elements in hover layer
10842         * @param  {module:zrender/Element} el
10843         */
10844        clearHover: function () {
10845            if (this.painter.clearHover) {
10846                this.painter.clearHover();
10847                this.refreshHover();
10848            }
10849        },
10850
10851        /**
10852         * Refresh hover in next frame
10853         */
10854        refreshHover: function () {
10855            this._needsRefreshHover = true;
10856        },
10857
10858        /**
10859         * Refresh hover immediately
10860         */
10861        refreshHoverImmediately: function () {
10862            this._needsRefreshHover = false;
10863            this.painter.refreshHover && this.painter.refreshHover();
10864        },
10865
10866        /**
10867         * Resize the canvas.
10868         * Should be invoked when container size is changed
10869         * @param {Object} [opts]
10870         * @param {number|string} [opts.width] Can be 'auto' (the same as null/undefined)
10871         * @param {number|string} [opts.height] Can be 'auto' (the same as null/undefined)
10872         */
10873        resize: function(opts) {
10874            opts = opts || {};
10875            this.painter.resize(opts.width, opts.height);
10876            this.handler.resize();
10877        },
10878
10879        /**
10880         * Stop and clear all animation immediately
10881         */
10882        clearAnimation: function () {
10883            this.animation.clear();
10884        },
10885
10886        /**
10887         * Get container width
10888         */
10889        getWidth: function() {
10890            return this.painter.getWidth();
10891        },
10892
10893        /**
10894         * Get container height
10895         */
10896        getHeight: function() {
10897            return this.painter.getHeight();
10898        },
10899
10900        /**
10901         * Export the canvas as Base64 URL
10902         * @param {string} type
10903         * @param {string} [backgroundColor='#fff']
10904         * @return {string} Base64 URL
10905         */
10906        // toDataURL: function(type, backgroundColor) {
10907        //     return this.painter.getRenderedCanvas({
10908        //         backgroundColor: backgroundColor
10909        //     }).toDataURL(type);
10910        // },
10911
10912        /**
10913         * Converting a path to image.
10914         * It has much better performance of drawing image rather than drawing a vector path.
10915         * @param {module:zrender/graphic/Path} e
10916         * @param {number} width
10917         * @param {number} height
10918         */
10919        pathToImage: function(e, dpr) {
10920            return this.painter.pathToImage(e, dpr);
10921        },
10922
10923        /**
10924         * Set default cursor
10925         * @param {string} [cursorStyle='default'] 渚嬪 crosshair
10926         */
10927        setCursorStyle: function (cursorStyle) {
10928            this.handler.setCursorStyle(cursorStyle);
10929        },
10930
10931        /**
10932         * Find hovered element
10933         * @param {number} x
10934         * @param {number} y
10935         * @return {Object} {target, topTarget}
10936         */
10937        findHover: function (x, y) {
10938            return this.handler.findHover(x, y);
10939        },
10940
10941        /**
10942         * Bind event
10943         *
10944         * @param {string} eventName Event name
10945         * @param {Function} eventHandler Handler function
10946         * @param {Object} [context] Context object
10947         */
10948        on: function(eventName, eventHandler, context) {
10949            this.handler.on(eventName, eventHandler, context);
10950        },
10951
10952        /**
10953         * Unbind event
10954         * @param {string} eventName Event name
10955         * @param {Function} [eventHandler] Handler function
10956         */
10957        off: function(eventName, eventHandler) {
10958            this.handler.off(eventName, eventHandler);
10959        },
10960
10961        /**
10962         * Trigger event manually
10963         *
10964         * @param {string} eventName Event name
10965         * @param {event=} event Event object
10966         */
10967        trigger: function (eventName, event) {
10968            this.handler.trigger(eventName, event);
10969        },
10970
10971
10972        /**
10973         * Clear all objects and the canvas.
10974         */
10975        clear: function () {
10976            this.storage.delRoot();
10977            this.painter.clear();
10978        },
10979
10980        /**
10981         * Dispose self.
10982         */
10983        dispose: function () {
10984            this.animation.stop();
10985
10986            this.clear();
10987            this.storage.dispose();
10988            this.painter.dispose();
10989            this.handler.dispose();
10990
10991            this.animation =
10992                this.storage =
10993                    this.painter =
10994                        this.handler = null;
10995
10996            delInstance(this.id);
10997        }
10998    };
10999
11000
11001
11002    var zrender = (Object.freeze || Object)({
11003        version: version$1,
11004        init: init$1,
11005        dispose: dispose$1,
11006        getInstance: getInstance,
11007        registerPainter: registerPainter
11008    });
11009
11010    /*
11011* Licensed to the Apache Software Foundation (ASF) under one
11012* or more contributor license agreements.  See the NOTICE file
11013* distributed with this work for additional information
11014* regarding copyright ownership.  The ASF licenses this file
11015* to you under the Apache License, Version 2.0 (the
11016* "License"); you may not use this file except in compliance
11017* with the License.  You may obtain a copy of the License at
11018*
11019*   http://www.apache.org/licenses/LICENSE-2.0
11020*
11021* Unless required by applicable law or agreed to in writing,
11022* software distributed under the License is distributed on an
11023* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11024* KIND, either express or implied.  See the License for the
11025* specific language governing permissions and limitations
11026* under the License.
11027*/
11028
11029    var each$2 = each$1;
11030    var isObject$2 = isObject$1;
11031    var isArray$1 = isArray;
11032
11033    /**
11034     * Make the name displayable. But we should
11035     * make sure it is not duplicated with user
11036     * specified name, so use '\0';
11037     */
11038    var DUMMY_COMPONENT_NAME_PREFIX = 'series\0';
11039
11040    /**
11041     * If value is not array, then translate it to array.
11042     * @param  {*} value
11043     * @return {Array} [value] or value
11044     */
11045    function normalizeToArray(value) {
11046        return value instanceof Array
11047            ? value
11048            : value == null
11049                ? []
11050                : [value];
11051    }
11052
11053    /**
11054     * Sync default option between normal and emphasis like `position` and `show`
11055     * In case some one will write code like
11056     *     label: {
11057 *          show: false,
11058 *          position: 'outside',
11059 *          fontSize: 18
11060 *     },
11061     *     emphasis: {
11062 *          label: { show: true }
11063 *     }
11064     * @param {Object} opt
11065     * @param {string} key
11066     * @param {Array.<string>} subOpts
11067     */
11068    function defaultEmphasis(opt, key, subOpts) {
11069        // Caution: performance sensitive.
11070        if (opt) {
11071            opt[key] = opt[key] || {};
11072            opt.emphasis = opt.emphasis || {};
11073            opt.emphasis[key] = opt.emphasis[key] || {};
11074
11075            // Default emphasis option from normal
11076            for (var i = 0, len = subOpts.length; i < len; i++) {
11077                var subOptName = subOpts[i];
11078                if (!opt.emphasis[key].hasOwnProperty(subOptName)
11079                    && opt[key].hasOwnProperty(subOptName)
11080                ) {
11081                    opt.emphasis[key][subOptName] = opt[key][subOptName];
11082                }
11083            }
11084        }
11085    }
11086
11087    var TEXT_STYLE_OPTIONS = [
11088        'fontStyle', 'fontWeight', 'fontSize', 'fontFamily',
11089        'rich', 'tag', 'color', 'textBorderColor', 'textBorderWidth',
11090        'width', 'height', 'lineHeight', 'align', 'verticalAlign', 'baseline',
11091        'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY',
11092        'textShadowColor', 'textShadowBlur', 'textShadowOffsetX', 'textShadowOffsetY',
11093        'backgroundColor', 'borderColor', 'borderWidth', 'borderRadius', 'padding'
11094    ];
11095
11096// modelUtil.LABEL_OPTIONS = modelUtil.TEXT_STYLE_OPTIONS.concat([
11097//     'position', 'offset', 'rotate', 'origin', 'show', 'distance', 'formatter',
11098//     'fontStyle', 'fontWeight', 'fontSize', 'fontFamily',
11099//     // FIXME: deprecated, check and remove it.
11100//     'textStyle'
11101// ]);
11102
11103    /**
11104     * The method do not ensure performance.
11105     * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}]
11106     * This helper method retieves value from data.
11107     * @param {string|number|Date|Array|Object} dataItem
11108     * @return {number|string|Date|Array.<number|string|Date>}
11109     */
11110    function getDataItemValue(dataItem) {
11111        return (isObject$2(dataItem) && !isArray$1(dataItem) && !(dataItem instanceof Date))
11112            ? dataItem.value : dataItem;
11113    }
11114
11115    /**
11116     * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}]
11117     * This helper method determine if dataItem has extra option besides value
11118     * @param {string|number|Date|Array|Object} dataItem
11119     */
11120    function isDataItemOption(dataItem) {
11121        return isObject$2(dataItem)
11122            && !(dataItem instanceof Array);
11123        // // markLine data can be array
11124        // && !(dataItem[0] && isObject(dataItem[0]) && !(dataItem[0] instanceof Array));
11125    }
11126
11127    /**
11128     * Mapping to exists for merge.
11129     *
11130     * @public
11131     * @param {Array.<Object>|Array.<module:echarts/model/Component>} exists
11132     * @param {Object|Array.<Object>} newCptOptions
11133     * @return {Array.<Object>} Result, like [{exist: ..., option: ...}, {}],
11134     *                          index of which is the same as exists.
11135     */
11136    function mappingToExists(exists, newCptOptions) {
11137        // Mapping by the order by original option (but not order of
11138        // new option) in merge mode. Because we should ensure
11139        // some specified index (like xAxisIndex) is consistent with
11140        // original option, which is easy to understand, espatially in
11141        // media query. And in most case, merge option is used to
11142        // update partial option but not be expected to change order.
11143        newCptOptions = (newCptOptions || []).slice();
11144
11145        var result = map(exists || [], function (obj, index) {
11146            return {exist: obj};
11147        });
11148
11149        // Mapping by id or name if specified.
11150        each$2(newCptOptions, function (cptOption, index) {
11151            if (!isObject$2(cptOption)) {
11152                return;
11153            }
11154
11155            // id has highest priority.
11156            for (var i = 0; i < result.length; i++) {
11157                if (!result[i].option // Consider name: two map to one.
11158                    && cptOption.id != null
11159                    && result[i].exist.id === cptOption.id + ''
11160                ) {
11161                    result[i].option = cptOption;
11162                    newCptOptions[index] = null;
11163                    return;
11164                }
11165            }
11166
11167            for (var i = 0; i < result.length; i++) {
11168                var exist = result[i].exist;
11169                if (!result[i].option // Consider name: two map to one.
11170                    // Can not match when both ids exist but different.
11171                    && (exist.id == null || cptOption.id == null)
11172                    && cptOption.name != null
11173                    && !isIdInner(cptOption)
11174                    && !isIdInner(exist)
11175                    && exist.name === cptOption.name + ''
11176                ) {
11177                    result[i].option = cptOption;
11178                    newCptOptions[index] = null;
11179                    return;
11180                }
11181            }
11182        });
11183
11184        // Otherwise mapping by index.
11185        each$2(newCptOptions, function (cptOption, index) {
11186            if (!isObject$2(cptOption)) {
11187                return;
11188            }
11189
11190            var i = 0;
11191            for (; i < result.length; i++) {
11192                var exist = result[i].exist;
11193                if (!result[i].option
11194                    // Existing model that already has id should be able to
11195                    // mapped to (because after mapping performed model may
11196                    // be assigned with a id, whish should not affect next
11197                    // mapping), except those has inner id.
11198                    && !isIdInner(exist)
11199                    // Caution:
11200                    // Do not overwrite id. But name can be overwritten,
11201                    // because axis use name as 'show label text'.
11202                    // 'exist' always has id and name and we dont
11203                    // need to check it.
11204                    && cptOption.id == null
11205                ) {
11206                    result[i].option = cptOption;
11207                    break;
11208                }
11209            }
11210
11211            if (i >= result.length) {
11212                result.push({option: cptOption});
11213            }
11214        });
11215
11216        return result;
11217    }
11218
11219    /**
11220     * Make id and name for mapping result (result of mappingToExists)
11221     * into `keyInfo` field.
11222     *
11223     * @public
11224     * @param {Array.<Object>} Result, like [{exist: ..., option: ...}, {}],
11225     *                          which order is the same as exists.
11226     * @return {Array.<Object>} The input.
11227     */
11228    function makeIdAndName(mapResult) {
11229        // We use this id to hash component models and view instances
11230        // in echarts. id can be specified by user, or auto generated.
11231
11232        // The id generation rule ensures new view instance are able
11233        // to mapped to old instance when setOption are called in
11234        // no-merge mode. So we generate model id by name and plus
11235        // type in view id.
11236
11237        // name can be duplicated among components, which is convenient
11238        // to specify multi components (like series) by one name.
11239
11240        // Ensure that each id is distinct.
11241        var idMap = createHashMap();
11242
11243        each$2(mapResult, function (item, index) {
11244            var existCpt = item.exist;
11245            existCpt && idMap.set(existCpt.id, item);
11246        });
11247
11248        each$2(mapResult, function (item, index) {
11249            var opt = item.option;
11250
11251            assert$1(
11252                !opt || opt.id == null || !idMap.get(opt.id) || idMap.get(opt.id) === item,
11253                'id duplicates: ' + (opt && opt.id)
11254            );
11255
11256            opt && opt.id != null && idMap.set(opt.id, item);
11257            !item.keyInfo && (item.keyInfo = {});
11258        });
11259
11260        // Make name and id.
11261        each$2(mapResult, function (item, index) {
11262            var existCpt = item.exist;
11263            var opt = item.option;
11264            var keyInfo = item.keyInfo;
11265
11266            if (!isObject$2(opt)) {
11267                return;
11268            }
11269
11270            // name can be overwitten. Consider case: axis.name = '20km'.
11271            // But id generated by name will not be changed, which affect
11272            // only in that case: setOption with 'not merge mode' and view
11273            // instance will be recreated, which can be accepted.
11274            keyInfo.name = opt.name != null
11275                ? opt.name + ''
11276                : existCpt
11277                    ? existCpt.name
11278                    // Avoid diffferent series has the same name,
11279                    // because name may be used like in color pallet.
11280                    : DUMMY_COMPONENT_NAME_PREFIX + index;
11281
11282            if (existCpt) {
11283                keyInfo.id = existCpt.id;
11284            }
11285            else if (opt.id != null) {
11286                keyInfo.id = opt.id + '';
11287            }
11288            else {
11289                // Consider this situatoin:
11290                //  optionA: [{name: 'a'}, {name: 'a'}, {..}]
11291                //  optionB [{..}, {name: 'a'}, {name: 'a'}]
11292                // Series with the same name between optionA and optionB
11293                // should be mapped.
11294                var idNum = 0;
11295                do {
11296                    keyInfo.id = '\0' + keyInfo.name + '\0' + idNum++;
11297                }
11298                while (idMap.get(keyInfo.id));
11299            }
11300
11301            idMap.set(keyInfo.id, item);
11302        });
11303    }
11304
11305    function isNameSpecified(componentModel) {
11306        var name = componentModel.name;
11307        // Is specified when `indexOf` get -1 or > 0.
11308        return !!(name && name.indexOf(DUMMY_COMPONENT_NAME_PREFIX));
11309    }
11310
11311    /**
11312     * @public
11313     * @param {Object} cptOption
11314     * @return {boolean}
11315     */
11316    function isIdInner(cptOption) {
11317        return isObject$2(cptOption)
11318            && cptOption.id
11319            && (cptOption.id + '').indexOf('\0_ec_\0') === 0;
11320    }
11321
11322    /**
11323     * A helper for removing duplicate items between batchA and batchB,
11324     * and in themselves, and categorize by series.
11325     *
11326     * @param {Array.<Object>} batchA Like: [{seriesId: 2, dataIndex: [32, 4, 5]}, ...]
11327     * @param {Array.<Object>} batchB Like: [{seriesId: 2, dataIndex: [32, 4, 5]}, ...]
11328     * @return {Array.<Array.<Object>, Array.<Object>>} result: [resultBatchA, resultBatchB]
11329     */
11330    function compressBatches(batchA, batchB) {
11331        var mapA = {};
11332        var mapB = {};
11333
11334        makeMap(batchA || [], mapA);
11335        makeMap(batchB || [], mapB, mapA);
11336
11337        return [mapToArray(mapA), mapToArray(mapB)];
11338
11339        function makeMap(sourceBatch, map$$1, otherMap) {
11340            for (var i = 0, len = sourceBatch.length; i < len; i++) {
11341                var seriesId = sourceBatch[i].seriesId;
11342                var dataIndices = normalizeToArray(sourceBatch[i].dataIndex);
11343                var otherDataIndices = otherMap && otherMap[seriesId];
11344
11345                for (var j = 0, lenj = dataIndices.length; j < lenj; j++) {
11346                    var dataIndex = dataIndices[j];
11347
11348                    if (otherDataIndices && otherDataIndices[dataIndex]) {
11349                        otherDataIndices[dataIndex] = null;
11350                    }
11351                    else {
11352                        (map$$1[seriesId] || (map$$1[seriesId] = {}))[dataIndex] = 1;
11353                    }
11354                }
11355            }
11356        }
11357
11358        function mapToArray(map$$1, isData) {
11359            var result = [];
11360            for (var i in map$$1) {
11361                if (map$$1.hasOwnProperty(i) && map$$1[i] != null) {
11362                    if (isData) {
11363                        result.push(+i);
11364                    }
11365                    else {
11366                        var dataIndices = mapToArray(map$$1[i], true);
11367                        dataIndices.length && result.push({seriesId: i, dataIndex: dataIndices});
11368                    }
11369                }
11370            }
11371            return result;
11372        }
11373    }
11374
11375    /**
11376     * @param {module:echarts/data/List} data
11377     * @param {Object} payload Contains dataIndex (means rawIndex) / dataIndexInside / name
11378     *                         each of which can be Array or primary type.
11379     * @return {number|Array.<number>} dataIndex If not found, return undefined/null.
11380     */
11381    function queryDataIndex(data, payload) {
11382        if (payload.dataIndexInside != null) {
11383            return payload.dataIndexInside;
11384        }
11385        else if (payload.dataIndex != null) {
11386            return isArray(payload.dataIndex)
11387                ? map(payload.dataIndex, function (value) {
11388                    return data.indexOfRawIndex(value);
11389                })
11390                : data.indexOfRawIndex(payload.dataIndex);
11391        }
11392        else if (payload.name != null) {
11393            return isArray(payload.name)
11394                ? map(payload.name, function (value) {
11395                    return data.indexOfName(value);
11396                })
11397                : data.indexOfName(payload.name);
11398        }
11399    }
11400
11401    /**
11402     * Enable property storage to any host object.
11403     * Notice: Serialization is not supported.
11404     *
11405     * For example:
11406     * var inner = zrUitl.makeInner();
11407     *
11408     * function some1(hostObj) {
11409 *      inner(hostObj).someProperty = 1212;
11410 *      ...
11411 * }
11412     * function some2() {
11413 *      var fields = inner(this);
11414 *      fields.someProperty1 = 1212;
11415 *      fields.someProperty2 = 'xx';
11416 *      ...
11417 * }
11418     *
11419     * @return {Function}
11420     */
11421    function makeInner() {
11422        // Consider different scope by es module import.
11423        var key = '__\0ec_inner_' + innerUniqueIndex++ + '_' + Math.random().toFixed(5);
11424        return function (hostObj) {
11425            return hostObj[key] || (hostObj[key] = {});
11426        };
11427    }
11428    var innerUniqueIndex = 0;
11429
11430    /**
11431     * @param {module:echarts/model/Global} ecModel
11432     * @param {string|Object} finder
11433     *        If string, e.g., 'geo', means {geoIndex: 0}.
11434     *        If Object, could contain some of these properties below:
11435     *        {
11436 *            seriesIndex, seriesId, seriesName,
11437 *            geoIndex, geoId, geoName,
11438 *            bmapIndex, bmapId, bmapName,
11439 *            xAxisIndex, xAxisId, xAxisName,
11440 *            yAxisIndex, yAxisId, yAxisName,
11441 *            gridIndex, gridId, gridName,
11442 *            ... (can be extended)
11443 *        }
11444     *        Each properties can be number|string|Array.<number>|Array.<string>
11445     *        For example, a finder could be
11446     *        {
11447 *            seriesIndex: 3,
11448 *            geoId: ['aa', 'cc'],
11449 *            gridName: ['xx', 'rr']
11450 *        }
11451     *        xxxIndex can be set as 'all' (means all xxx) or 'none' (means not specify)
11452     *        If nothing or null/undefined specified, return nothing.
11453     * @param {Object} [opt]
11454     * @param {string} [opt.defaultMainType]
11455     * @param {Array.<string>} [opt.includeMainTypes]
11456     * @return {Object} result like:
11457     *        {
11458 *            seriesModels: [seriesModel1, seriesModel2],
11459 *            seriesModel: seriesModel1, // The first model
11460 *            geoModels: [geoModel1, geoModel2],
11461 *            geoModel: geoModel1, // The first model
11462 *            ...
11463 *        }
11464     */
11465    function parseFinder(ecModel, finder, opt) {
11466        if (isString(finder)) {
11467            var obj = {};
11468            obj[finder + 'Index'] = 0;
11469            finder = obj;
11470        }
11471
11472        var defaultMainType = opt && opt.defaultMainType;
11473        if (defaultMainType
11474            && !has(finder, defaultMainType + 'Index')
11475            && !has(finder, defaultMainType + 'Id')
11476            && !has(finder, defaultMainType + 'Name')
11477        ) {
11478            finder[defaultMainType + 'Index'] = 0;
11479        }
11480
11481        var result = {};
11482
11483        each$2(finder, function (value, key) {
11484            var value = finder[key];
11485
11486            // Exclude 'dataIndex' and other illgal keys.
11487            if (key === 'dataIndex' || key === 'dataIndexInside') {
11488                result[key] = value;
11489                return;
11490            }
11491
11492            var parsedKey = key.match(/^(\w+)(Index|Id|Name)$/) || [];
11493            var mainType = parsedKey[1];
11494            var queryType = (parsedKey[2] || '').toLowerCase();
11495
11496            if (!mainType
11497                || !queryType
11498                || value == null
11499                || (queryType === 'index' && value === 'none')
11500                || (opt && opt.includeMainTypes && indexOf(opt.includeMainTypes, mainType) < 0)
11501            ) {
11502                return;
11503            }
11504
11505            var queryParam = {mainType: mainType};
11506            if (queryType !== 'index' || value !== 'all') {
11507                queryParam[queryType] = value;
11508            }
11509
11510            var models = ecModel.queryComponents(queryParam);
11511            result[mainType + 'Models'] = models;
11512            result[mainType + 'Model'] = models[0];
11513        });
11514
11515        return result;
11516    }
11517
11518    function has(obj, prop) {
11519        return obj && obj.hasOwnProperty(prop);
11520    }
11521
11522    function setAttribute(dom, key, value) {
11523        dom.setAttribute
11524            ? dom.setAttribute(key, value)
11525            : (dom[key] = value);
11526    }
11527
11528    function getAttribute(dom, key) {
11529        return dom.getAttribute
11530            ? dom.getAttribute(key)
11531            : dom[key];
11532    }
11533
11534    /*
11535* Licensed to the Apache Software Foundation (ASF) under one
11536* or more contributor license agreements.  See the NOTICE file
11537* distributed with this work for additional information
11538* regarding copyright ownership.  The ASF licenses this file
11539* to you under the Apache License, Version 2.0 (the
11540* "License"); you may not use this file except in compliance
11541* with the License.  You may obtain a copy of the License at
11542*
11543*   http://www.apache.org/licenses/LICENSE-2.0
11544*
11545* Unless required by applicable law or agreed to in writing,
11546* software distributed under the License is distributed on an
11547* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11548* KIND, either express or implied.  See the License for the
11549* specific language governing permissions and limitations
11550* under the License.
11551*/
11552
11553    var TYPE_DELIMITER = '.';
11554    var IS_CONTAINER = '___EC__COMPONENT__CONTAINER___';
11555
11556    /**
11557     * Notice, parseClassType('') should returns {main: '', sub: ''}
11558     * @public
11559     */
11560    function parseClassType$1(componentType) {
11561        var ret = {main: '', sub: ''};
11562        if (componentType) {
11563            componentType = componentType.split(TYPE_DELIMITER);
11564            ret.main = componentType[0] || '';
11565            ret.sub = componentType[1] || '';
11566        }
11567        return ret;
11568    }
11569
11570    /**
11571     * @public
11572     */
11573    function checkClassType(componentType) {
11574        assert$1(
11575            /^[a-zA-Z0-9_]+([.][a-zA-Z0-9_]+)?$/.test(componentType),
11576            'componentType "' + componentType + '" illegal'
11577        );
11578    }
11579
11580    /**
11581     * @public
11582     */
11583    function enableClassExtend(RootClass, mandatoryMethods) {
11584
11585        RootClass.$constructor = RootClass;
11586        RootClass.extend = function (proto) {
11587
11588            if (__DEV__) {
11589                each$1(mandatoryMethods, function (method) {
11590                    if (!proto[method]) {
11591                        console.warn(
11592                            'Method `' + method + '` should be implemented'
11593                            + (proto.type ? ' in ' + proto.type : '') + '.'
11594                        );
11595                    }
11596                });
11597            }
11598
11599            var superClass = this;
11600            var ExtendedClass = function () {
11601                if (!proto.$constructor) {
11602                    superClass.apply(this, arguments);
11603                }
11604                else {
11605                    proto.$constructor.apply(this, arguments);
11606                }
11607            };
11608
11609            extend(ExtendedClass.prototype, proto);
11610
11611            ExtendedClass.extend = this.extend;
11612            ExtendedClass.superCall = superCall;
11613            ExtendedClass.superApply = superApply;
11614            inherits(ExtendedClass, this);
11615            ExtendedClass.superClass = superClass;
11616
11617            return ExtendedClass;
11618        };
11619    }
11620
11621    var classBase = 0;
11622
11623    /**
11624     * Can not use instanceof, consider different scope by
11625     * cross domain or es module import in ec extensions.
11626     * Mount a method "isInstance()" to Clz.
11627     */
11628    function enableClassCheck(Clz) {
11629        var classAttr = ['__\0is_clz', classBase++, Math.random().toFixed(3)].join('_');
11630        Clz.prototype[classAttr] = true;
11631
11632        if (__DEV__) {
11633            assert$1(!Clz.isInstance, 'The method "is" can not be defined.');
11634        }
11635
11636        Clz.isInstance = function (obj) {
11637            return !!(obj && obj[classAttr]);
11638        };
11639    }
11640
11641// superCall should have class info, which can not be fetch from 'this'.
11642// Consider this case:
11643// class A has method f,
11644// class B inherits class A, overrides method f, f call superApply('f'),
11645// class C inherits class B, do not overrides method f,
11646// then when method of class C is called, dead loop occured.
11647    function superCall(context, methodName) {
11648        var args = slice(arguments, 2);
11649        return this.superClass.prototype[methodName].apply(context, args
11649);
11650    }
11651
11652    function superApply(context, methodName, args) {
11653        return this.superClass.prototype[methodName].apply(context, args);
11654    }
11655
11656    /**
11657     * @param {Object} entity
11658     * @param {Object} options
11659     * @param {boolean} [options.registerWhenExtend]
11660     * @public
11661     */
11662    function enableClassManagement(entity, options) {
11663        options = options || {};
11664
11665        /**
11666         * Component model classes
11667         * key: componentType,
11668         * value:
11669         *     componentClass, when componentType is 'xxx'
11670         *     or Object.<subKey, componentClass>, when componentType is 'xxx.yy'
11671         * @type {Object}
11672         */
11673        var storage = {};
11674
11675        entity.registerClass = function (Clazz, componentType) {
11676            if (componentType) {
11677                checkClassType(componentType);
11678                componentType = parseClassType$1(componentType);
11679
11680                if (!componentType.sub) {
11681                    if (__DEV__) {
11682                        if (storage[componentType.main]) {
11683                            console.warn(componentType.main + ' exists.');
11684                        }
11685                    }
11686                    storage[componentType.main] = Clazz;
11687                }
11688                else if (componentType.sub !== IS_CONTAINER) {
11689                    var container = makeContainer(componentType);
11690                    container[componentType.sub] = Clazz;
11691                }
11692            }
11693            return Clazz;
11694        };
11695
11696        entity.getClass = function (componentMainType, subType, throwWhenNotFound) {
11697            var Clazz = storage[componentMainType];
11698
11699            if (Clazz && Clazz[IS_CONTAINER]) {
11700                Clazz = subType ? Clazz[subType] : null;
11701            }
11702
11703            if (throwWhenNotFound && !Clazz) {
11704                throw new Error(
11705                    !subType
11706                        ? componentMainType + '.' + 'type should be specified.'
11707                        : 'Component ' + componentMainType + '.' + (subType || '') + ' not exists. Load it first.'
11708                );
11709            }
11710
11711            return Clazz;
11712        };
11713
11714        entity.getClassesByMainType = function (componentType) {
11715            componentType = parseClassType$1(componentType);
11716
11717            var result = [];
11718            var obj = storage[componentType.main];
11719
11720            if (obj && obj[IS_CONTAINER]) {
11721                each$1(obj, function (o, type) {
11722                    type !== IS_CONTAINER && result.push(o);
11723                });
11724            }
11725            else {
11726                result.push(obj);
11727            }
11728
11729            return result;
11730        };
11731
11732        entity.hasClass = function (componentType) {
11733            // Just consider componentType.main.
11734            componentType = parseClassType$1(componentType);
11735            return !!storage[componentType.main];
11736        };
11737
11738        /**
11739         * @return {Array.<string>} Like ['aa', 'bb'], but can not be ['aa.xx']
11740         */
11741        entity.getAllClassMainTypes = function () {
11742            var types = [];
11743            each$1(storage, function (obj, type) {
11744                types.push(type);
11745            });
11746            return types;
11747        };
11748
11749        /**
11750         * If a main type is container and has sub types
11751         * @param  {string}  mainType
11752         * @return {boolean}
11753         */
11754        entity.hasSubTypes = function (componentType) {
11755            componentType = parseClassType$1(componentType);
11756            var obj = storage[componentType.main];
11757            return obj && obj[IS_CONTAINER];
11758        };
11759
11760        entity.parseClassType = parseClassType$1;
11761
11762        function makeContainer(componentType) {
11763            var container = storage[componentType.main];
11764            if (!container || !container[IS_CONTAINER]) {
11765                container = storage[componentType.main] = {};
11766                container[IS_CONTAINER] = true;
11767            }
11768            return container;
11769        }
11770
11771        if (options.registerWhenExtend) {
11772            var originalExtend = entity.extend;
11773            if (originalExtend) {
11774                entity.extend = function (proto) {
11775                    var ExtendedClass = originalExtend.call(this, proto);
11776                    return entity.registerClass(ExtendedClass, proto.type);
11777                };
11778            }
11779        }
11780
11781        return entity;
11782    }
11783
11784    /**
11785     * @param {string|Array.<string>} properties
11786     */
11787
11788    /*
11789* Licensed to the Apache Software Foundation (ASF) under one
11790* or more contributor license agreements.  See the NOTICE file
11791* distributed with this work for additional information
11792* regarding copyright ownership.  The ASF licenses this file
11793* to you under the Apache License, Version 2.0 (the
11794* "License");
11794 you may not use this file except in compliance
11795* with the License.  You may obtain a copy of the License at
11796*
11797*   http://www.apache.org/licenses/LICENSE-2.0
11798*
11799* Unless required by applicable law or agreed to in writing,
11800* software distributed under the License is distributed on an
11801* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11802* KIND, either express or implied.  See the License for the
11803* specific language governing permissions and limitations
11804* under the License.
11805*/
11806
11807// TODO Parse shadow style
11808// TODO Only shallow path support
11809    var makeStyleMapper = function (properties) {
11810        // Normalize
11811        for (var i = 0; i < properties.length; i++) {
11812            if (!properties[i][1]) {
11813                properties[i][1] = properties[i][0];
11814            }
11815        }
11816        return function (model, excludes, includes) {
11817            var style = {};
11818            for (var i = 0; i < properties.length; i++) {
11819                var propName = properties[i][1];
11820                if ((excludes && indexOf(excludes, propName) >= 0)
11821                    || (includes && indexOf(includes, propName) < 0)
11822                ) {
11823                    continue;
11824                }
11825                var val = model.getShallow(propName);
11826                if (val != null) {
11827                    style[properties[i][0]] = val;
11828                }
11829            }
11830            return style;
11831        };
11832    };
11833
11834    /*
11835* Licensed to the Apache Software Foundation (ASF) under one
11836* or more contributor license agreements.  See the NOTICE file
11837* distributed with this work for additional information
11838* regarding copyright ownership.  The ASF licenses this file
11839* to you under the Apache License, Version 2.0 (the
11840* "License"); you may not use this file except in compliance
11841* with the License.  You may obtain a copy of the License at
11842*
11843*   http://www.apache.org/licenses/LICENSE-2.0
11844*
11845* Unless required by applicable law or agreed to in writing,
11846* software distributed under the License is distributed on an
11847* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11848* KIND, either express or implied.  See the License for the
11849* specific language governing permissions and limitations
11850* under the License.
11851*/
11852
11853    var getLineStyle = makeStyleMapper(
11854        [
11855            ['lineWidth', 'width'],
11856            ['stroke', 'color'],
11857            ['opacity'],
11858            ['shadowBlur'],
11859            ['shadowOffsetX'],
11860            ['shadowOffsetY'],
11861            ['shadowColor']
11862        ]
11863    );
11864
11865    var lineStyleMixin = {
11866        getLineStyle: function (excludes) {
11867            var style = getLineStyle(this, excludes);
11868            var lineDash = this.getLineDash(style.lineWidth);
11869            lineDash && (style.lineDash = lineDash);
11870            return style;
11871        },
11872
11873        getLineDash: function (lineWidth) {
11874            if (lineWidth == null) {
11875                lineWidth = 1;
11876            }
11877            var lineType = this.get('type');
11878            var dotSize = Math.max(lineWidth, 2);
11879            var dashSize = lineWidth * 4;
11880            return (lineType === 'solid' || lineType == null) ? null
11881                : (lineType === 'dashed' ? [dashSize, dashSize] : [dotSize, dotSize]);
11882        }
11883    };
11884
11885    /*
11886* Licensed to the Apache Software Foundation (ASF) under one
11887* or more contributor license agreements.  See the NOTICE file
11888* distributed with this work for additional information
11889* regarding copyright ownership.  The ASF licenses this file
11890* to you under the Apache License, Version 2.0 (the
11891* "License"); you may not use this file except in compliance
11892* with the License.  You may obtain a copy of the License at
11893*
11894*   http://www.apache.org/licenses/LICENSE-2.0
11895*
11896* Unless required by applicable law or agreed to in writing,
11897* software distributed under the License is distributed on an
11898* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11899* KIND, either express or implied.  See the License for the
11900* specific language governing permissions and limitations
11901* under the License.
11902*/
11903
11904    var getAreaStyle = makeStyleMapper(
11905        [
11906            ['fill', 'color'],
11907            ['shadowBlur'],
11908            ['shadowOffsetX'],
11909            ['shadowOffsetY'],
11910            ['opacity'],
11911            ['shadowColor']
11912        ]
11913    );
11914
11915    var areaStyleMixin = {
11916        getAreaStyle: function (excludes, includes) {
11917            return getAreaStyle(this, excludes, includes);
11918        }
11919    };
11920
11921    /**
11922     * 鏇茬嚎杈呭姪妯″潡
11923     * @module zrender/core/curve
11924     * @author pissang(https://www.github.com/pissang)
11925     */
11926
11927    var mathPow = Math.pow;
11928    var mathSqrt$2 = Math.sqrt;
11929
11930    var EPSILON$1 = 1e-8;
11931    var EPSILON_NUMERIC = 1e-4;
11932
11933    var THREE_SQRT = mathSqrt$2(3);
11934    var ONE_THIRD = 1 / 3;
11935
11936// 涓存椂鍙橀噺
11937    var _v0 = create();
11938    var _v1 = create();
11939    var _v2 = create();
11940
11941    function isAroundZero(val) {
11942        return val > -EPSILON$1 && val < EPSILON$1;
11943    }
11944    function isNotAroundZero$1(val) {
11945        return val > EPSILON$1 || val < -EPSILON$1;
11946    }
11947    /**
11948     * 璁$畻涓夋璐濆灏斿€�
11949     * @memberOf module:zrender/core/curve
11950     * @param  {number} p0
11951     * @param  {number} p1
11952     * @param  {number} p2
11953     * @param  {number} p3
11954     * @param  {number} t
11955     * @return {number}
11956     */
11957    function cubicAt(p0, p1, p2, p3, t) {
11958        var onet = 1 - t;
11959        return onet * onet * (onet * p0 + 3 * t * p1)
11960            + t * t * (t * p3 + 3 * onet * p2);
11961    }
11962
11963    /**
11964     * 璁$畻涓夋璐濆灏斿鏁板€�
11965     * @memberOf module:zrender/core/curve
11966     * @param  {number} p0
11967     * @param  {number} p1
11968     * @param  {number} p2
11969     * @param  {number} p3
11970     * @param  {number} t
11971     * @return {number}
11972     */
11973    function cubicDerivativeAt(p0, p1, p2, p3, t) {
11974        var onet = 1 - t;
11975        return 3 * (
11976            ((p1 - p0) * onet + 2 * (p2 - p1) * t) * onet
11977            + (p3 - p2) * t * t
11978        );
11979    }
11980
11981    /**
11982     * 璁$畻涓夋璐濆灏旀柟绋嬫牴锛屼娇鐢ㄧ洓閲戝叕寮�
11983     * @memberOf module:zrender/core/curve
11984     * @param  {number} p0
11985     * @param  {number} p1
11986     * @param  {number} p2
11987     * @param  {number} p3
11988     * @param  {number} val
11989     * @param  {Array.<number>} roots
11990     * @return {number} 鏈夋晥鏍规暟鐩�
11991     */
11992    function cubicRootAt(p0, p1, p2, p3, val, roots) {
11993        // Evaluate roots of cubic functions
11994        var a = p3 + 3 * (p1 - p2) - p0;
11995        var b = 3 * (p2 - p1 * 2 + p0);
11996        var c = 3 * (p1  - p0);
11997        var d = p0 - val;
11998
11999        var A = b * b - 3 * a * c;
12000        var B = b * c - 9 * a * d;
12001        var C = c * c - 3 * b * d;
12002
12003        var n = 0;
12004
12005        if (isAroundZero(A) && isAroundZero(B)) {
12006            if (isAroundZero(b)) {
12007                roots[0] = 0;
12008            }
12009            else {
12010                var t1 = -c / b;  //t1, t2, t3, b is not zero
12011                if (t1 >= 0 && t1 <= 1) {
12012                    roots[n++] = t1;
12013                }
12014            }
12015        }
12016        else {
12017            var disc = B * B - 4 * A * C;
12018
12019            if (isAroundZero(disc)) {
12020                var K = B / A;
12021                var t1 = -b / a + K;  // t1, a is not zero
12022                var t2 = -K / 2;  // t2, t3
12023                if (t1 >= 0 && t1 <= 1) {
12024                    roots[n++] = t1;
12025                }
12026                if (t2 >= 0 && t2 <= 1) {
12027                    roots[n++] = t2;
12028                }
12029            }
12030            else if (disc > 0) {
12031                var discSqrt = mathSqrt$2(disc);
12032                var Y1 = A * b + 1.5 * a * (-B + discSqrt);
12033                var Y2 = A * b + 1.5 * a * (-B - discSqrt);
12034                if (Y1 < 0) {
vendor: 11,967 bytes, lines 12035-12435
12035                    Y1 = -mathPow(-Y1, ONE_THIRD);
12036                }
12037                else {
12038                    Y1 = mathPow(Y1, ONE_THIRD);
12039                }
12040                if (Y2 < 0) {
12041                    Y2 = -mathPow(-Y2, ONE_THIRD);
12042                }
12043                else {
12044                    Y2 = mathPow(Y2, ONE_THIRD);
12045                }
12046                var t1 = (-b - (Y1 + Y2)) / (3 * a);
12047                if (t1 >= 0 && t1 <= 1) {
12048                    roots[n++] = t1;
12049                }
12050            }
12051            else {
12052                var T = (2 * A * b - 3 * a * B) / (2 * mathSqrt$2(A * A * A));
12053                var theta = Math.acos(T) / 3;
12054                var ASqrt = mathSqrt$2(A);
12055                var tmp = Math.cos(theta);
12056
12057                var t1 = (-b - 2 * ASqrt * tmp) / (3 * a);
12058                var t2 = (-b + ASqrt * (tmp + THREE_SQRT * Math.sin(theta))) / (3 * a);
12059                var t3 = (-b + ASqrt * (tmp - THREE_SQRT * Math.sin(theta))) / (3 * a);
12060                if (t1 >= 0 && t1 <= 1) {
12061                    roots[n++] = t1;
12062                }
12063                if (t2 >= 0 && t2 <= 1) {
12064                    roots[n++] = t2;
12065                }
12066                if (t3 >= 0 && t3 <= 1) {
12067                    roots[n++] = t3;
12068                }
12069            }
12070        }
12071        return n;
12072    }
12073
12074    /**
12075     * 璁$畻涓夋璐濆灏旀柟绋嬫瀬闄愬€肩殑浣嶇疆
12076     * @memberOf module:zrender/core/curve
12077     * @param  {number} p0
12078     * @param  {number} p1
12079     * @param  {number} p2
12080     * @param  {number} p3
12081     * @param  {Array.<number>} extrema
12082     * @return {number} 鏈夋晥鏁扮洰
12083     */
12084    function cubicExtrema(p0, p1, p2, p3, extrema) {
12085        var b = 6 * p2 - 12 * p1 + 6 * p0;
12086        var a = 9 * p1 + 3 * p3 - 3 * p0 - 9 * p2;
12087        var c = 3 * p1 - 3 * p0;
12088
12089        var n = 0;
12090        if (isAroundZero(a)) {
12091            if (isNotAroundZero$1(b)) {
12092                var t1 = -c / b;
12093                if (t1 >= 0 && t1 <=1) {
12094                    extrema[n++] = t1;
12095                }
12096            }
12097        }
12098        else {
12099            var disc = b * b - 4 * a * c;
12100            if (isAroundZero(disc)) {
12101                extrema[0] = -b / (2 * a);
12102            }
12103            else if (disc > 0) {
12104                var discSqrt = mathSqrt$2(disc);
12105                var t1 = (-b + discSqrt) / (2 * a);
12106                var t2 = (-b - discSqrt) / (2 * a);
12107                if (t1 >= 0 && t1 <= 1) {
12108                    extrema[n++] = t1;
12109                }
12110                if (t2 >= 0 && t2 <= 1) {
12111                    extrema[n++] = t2;
12112                }
12113            }
12114        }
12115        return n;
12116    }
12117
12118    /**
12119     * 缁嗗垎涓夋璐濆灏旀洸绾�
12120     * @memberOf module:zrender/core/curve
12121     * @param  {number} p0
12122     * @param  {number} p1
12123     * @param  {number} p2
12124     * @param  {number} p3
12125     * @param  {number} t
12126     * @param  {Array.<number>} out
12127     */
12128    function cubicSubdivide(p0, p1, p2, p3, t, out) {
12129        var p01 = (p1 - p0) * t + p0;
12130        var p12 = (p2 - p1) * t + p1;
12131        var p23 = (p3 - p2) * t + p2;
12132
12133        var p012 = (p12 - p01) * t + p01;
12134        var p123 = (p23 - p12) * t + p12;
12135
12136        var p0123 = (p123 - p012) * t + p012;
12137        // Seg0
12138        out[0] = p0;
12139        out[1] = p01;
12140        out[2] = p012;
12141        out[3] = p0123;
12142        // Seg1
12143        out[4] = p0123;
12144        out[5] = p123;
12145        out[6] = p23;
12146        out[7] = p3;
12147    }
12148
12149    /**
12150     * 鎶曞皠鐐瑰埌涓夋璐濆灏旀洸绾夸笂锛岃繑鍥炴姇灏勮窛绂汇€�
12151     * 鎶曞皠鐐规湁鍙兘浼氭湁涓€涓垨鑰呭涓紝杩欓噷鍙繑鍥炲叾涓窛绂绘渶鐭殑涓€涓€�
12152     * @param {number} x0
12153     * @param {number} y0
12154     * @param {number} x1
12155     * @param {number} y1
12156     * @param {number} x2
12157     * @param {number} y2
12158     * @param {number} x3
12159     * @param {number} y3
12160     * @param {number} x
12161     * @param {number} y
12162     * @param {Array.<number>} [out] 鎶曞皠鐐�
12163     * @return {number}
12164     */
12165    function cubicProjectPoint(
12166        x0, y0, x1, y1, x2, y2, x3, y3,
12167        x, y, out
12168    ) {
12169        // http://pomax.github.io/bezierinfo/#projections
12170        var t;
12171        var interval = 0.005;
12172        var d = Infinity;
12173        var prev;
12174        var next;
12175        var d1;
12176        var d2;
12177
12178        _v0[0] = x;
12179        _v0[1] = y;
12180
12181        // 鍏堢矖鐣ヤ及璁′竴涓嬪彲鑳界殑鏈€灏忚窛绂荤殑 t 鍊�
12182        // PENDING
12183        for (var _t = 0; _t < 1; _t += 0.05) {
12184            _v1[0] = cubicAt(x0, x1, x2, x3, _t);
12185            _v1[1] = cubicAt(y0, y1, y2, y3, _t);
12186            d1 = distSquare(_v0, _v1);
12187            if (d1 < d) {
12188                t = _t;
12189                d = d1;
12190            }
12191        }
12192        d = Infinity;
12193
12194        // At most 32 iteration
12195        for (var i = 0; i < 32; i++) {
12196            if (interval < EPSILON_NUMERIC) {
12197                break;
12198            }
12199            prev = t - interval;
12200            next = t + interval;
12201            // t - interval
12202            _v1[0] = cubicAt(x0, x1, x2, x3, prev);
12203            _v1[1] = cubicAt(y0, y1, y2, y3, prev);
12204
12205            d1 = distSquare(_v1, _v0);
12206
12207            if (prev >= 0 && d1 < d) {
12208                t = prev;
12209                d = d1;
12210            }
12211            else {
12212                // t + interval
12213                _v2[0] = cubicAt(x0, x1, x2, x3, next);
12214                _v2[1] = cubicAt(y0, y1, y2, y3, next);
12215                d2 = distSquare(_v2, _v0);
12216
12217                if (next <= 1 && d2 < d) {
12218                    t = next;
12219                    d = d2;
12220                }
12221                else {
12222                    interval *= 0.5;
12223                }
12224            }
12225        }
12226        // t
12227        if (out) {
12228            out[0] = cubicAt(x0, x1, x2, x3, t);
12229            out[1] = cubicAt(y0, y1, y2, y3, t);
12230        }
12231        // console.log(interval, i);
12232        return mathSqrt$2(d);
12233    }
12234
12235    /**
12236     * 璁$畻浜屾鏂硅礉濉炲皵鍊�
12237     * @param  {number} p0
12238     * @param  {number} p1
12239     * @param  {number} p2
12240     * @param  {number} t
12241     * @return {number}
12242     */
12243    function quadraticAt(p0, p1, p2, t) {
12244        var onet = 1 - t;
12245        return onet * (onet * p0 + 2 * t * p1) + t * t * p2;
12246    }
12247
12248    /**
12249     * 璁$畻浜屾鏂硅礉濉炲皵瀵兼暟鍊�
12250     * @param  {number} p0
12251     * @param  {number} p1
12252     * @param  {number} p2
12253     * @param  {number} t
12254     * @return {number}
12255     */
12256    function quadraticDerivativeAt(p0, p1, p2, t) {
12257        return 2 * ((1 - t) * (p1 - p0) + t * (p2 - p1));
12258    }
12259
12260    /**
12261     * 璁$畻浜屾鏂硅礉濉炲皵鏂圭▼鏍�
12262     * @param  {number} p0
12263     * @param  {number} p1
12264     * @param  {number} p2
12265     * @param  {number} t
12266     * @param  {Array.<number>} roots
12267     * @return {number} 鏈夋晥鏍规暟鐩�
12268     */
12269    function quadraticRootAt(p0, p1, p2, val, roots) {
12270        var a = p0 - 2 * p1 + p2;
12271        var b = 2 * (p1 - p0);
12272        var c = p0 - val;
12273
12274        var n = 0;
12275        if (isAroundZero(a)) {
12276            if (isNotAroundZero$1(b)) {
12277                var t1 = -c / b;
12278                if (t1 >= 0 && t1 <= 1) {
12279                    roots[n++] = t1;
12280                }
12281            }
12282        }
12283        else {
12284            var disc = b * b - 4 * a * c;
12285            if (isAroundZero(disc)) {
12286                var t1 = -b / (2 * a);
12287                if (t1 >= 0 && t1 <= 1) {
12288                    roots[n++] = t1;
12289                }
12290            }
12291            else if (disc > 0) {
12292                var discSqrt = mathSqrt$2(disc);
12293                var t1 = (-b + discSqrt) / (2 * a);
12294                var t2 = (-b - discSqrt) / (2 * a);
12295                if (t1 >= 0 && t1 <= 1) {
12296                    roots[n++] = t1;
12297                }
12298                if (t2 >= 0 && t2 <= 1) {
12299                    roots[n++] = t2;
12300                }
12301            }
12302        }
12303        return n;
12304    }
12305
12306    /**
12307     * 璁$畻浜屾璐濆灏旀柟绋嬫瀬闄愬€�
12308     * @memberOf module:zrender/core/curve
12309     * @param  {number} p0
12310     * @param  {number} p1
12311     * @param  {number} p2
12312     * @return {number}
12313     */
12314    function quadraticExtremum(p0, p1, p2) {
12315        var divider = p0 + p2 - 2 * p1;
12316        if (divider === 0) {
12317            // p1 is center of p0 and p2
12318            return 0.5;
12319        }
12320        else {
12321            return (p0 - p1) / divider;
12322        }
12323    }
12324
12325    /**
12326     * 缁嗗垎浜屾璐濆灏旀洸绾�
12327     * @memberOf module:zrender/core/curve
12328     * @param  {number} p0
12329     * @param  {number} p1
12330     * @param  {number} p2
12331     * @param  {number} t
12332     * @param  {Array.<number>} out
12333     */
12334    function quadraticSubdivide(p0, p1, p2, t, out) {
12335        var p01 = (p1 - p0) * t + p0;
12336        var p12 = (p2 - p1) * t + p1;
12337        var p012 = (p12 - p01) * t + p01;
12338
12339        // Seg0
12340        out[0] = p0;
12341        out[1] = p01;
12342        out[2] = p012;
12343
12344        // Seg1
12345        out[3] = p012;
12346        out[4] = p12;
12347        out[5] = p2;
12348    }
12349
12350    /**
12351     * 鎶曞皠鐐瑰埌浜屾璐濆灏旀洸绾夸笂锛岃繑鍥炴姇灏勮窛绂汇€�
12352     * 鎶曞皠鐐规湁鍙兘浼氭湁涓€涓垨鑰呭涓紝杩欓噷鍙繑鍥炲叾涓窛绂绘渶鐭殑涓€涓€�
12353     * @param {number} x0
12354     * @param {number} y0
12355     * @param {number} x1
12356     * @param {number} y1
12357     * @param {number} x2
12358     * @param {number} y2
12359     * @param {number} x
12360     * @param {number} y
12361     * @param {Array.<number>} out 鎶曞皠鐐�
12362     * @return {number}
12363     */
12364    function quadraticProjectPoint(
12365        x0, y0, x1, y1, x2, y2,
12366        x, y, out
12367    ) {
12368        // http://pomax.github.io/bezierinfo/#projections
12369        var t;
12370        var interval = 0.005;
12371        var d = Infinity;
12372
12373        _v0[0] = x;
12374        _v0[1] = y;
12375
12376        // 鍏堢矖鐣ヤ及璁′竴涓嬪彲鑳界殑鏈€灏忚窛绂荤殑 t 鍊�
12377        // PENDING
12378        for (var _t = 0; _t < 1; _t += 0.05) {
12379            _v1[0] = quadraticAt(x0, x1, x2, _t);
12380            _v1[1] = quadraticAt(y0, y1, y2, _t);
12381            var d1 = distSquare(_v0, _v1);
12382            if (d1 < d) {
12383                t = _t;
12384                d = d1;
12385            }
12386        }
12387        d = Infinity;
12388
12389        // At most 32 iteration
12390        for (var i = 0; i < 32; i++) {
12391            if (interval < EPSILON_NUMERIC) {
12392                break;
12393            }
12394            var prev = t - interval;
12395            var next = t + interval;
12396            // t - interval
12397            _v1[0] = quadraticAt(x0, x1, x2, prev);
12398            _v1[1] = quadraticAt(y0, y1, y2, prev);
12399
12400            var d1 = distSquare(_v1, _v0);
12401
12402            if (prev >= 0 && d1 < d) {
12403                t = prev;
12404                d = d1;
12405            }
12406            else {
12407                // t + interval
12408                _v2[0] = quadraticAt(x0, x1, x2, next);
12409                _v2[1] = quadraticAt(y0, y1, y2, next);
12410                var d2 = distSquare(_v2, _v0);
12411                if (next <= 1 && d2 < d) {
12412                    t = next;
12413                    d = d2;
12414                }
12415                else {
12416                    interval *= 0.5;
12417                }
12418            }
12419        }
12420        // t
12421        if (out) {
12422            out[0] = quadraticAt(x0, x1, x2, t);
12423            out[1] = quadraticAt(y0, y1, y2, t);
12424        }
12425        // console.log(interval, i);
12426        return mathSqrt$2(d);
12427    }
12428
12429    /**
12430     * @author Yi Shen(https://github.com/pissang)
12431     */
12432
12433    var mathMin$3 = Math.min;
12434    var mathMax$3 = Math.max;
12435    var mathSin$2 = Math.sin;
vendor: 5,463 bytes, lines 12436-12608
12436    var mathCos$2 = Math.cos;
12437    var PI2 = Math.PI * 2;
12438
12439    var start = create();
12440    var end = create();
12441    var extremity = create();
12442
12443    /**
12444     * 浠庨《鐐规暟缁勪腑璁$畻鍑烘渶灏忓寘鍥寸洅锛屽啓鍏min`鍜宍max`涓�
12445     * @module zrender/core/bbox
12446     * @param {Array<Object>} points 椤剁偣鏁扮粍
12447     * @param {number} min
12448     * @param {number} max
12449     */
12450    function fromPoints(points, min$$1, max$$1) {
12451        if (points.length === 0) {
12452            return;
12453        }
12454        var p = points[0];
12455        var left = p[0];
12456        var right = p[0];
12457        var top = p[1];
12458        var bottom = p[1];
12459        var i;
12460
12461        for (i = 1; i < points.length; i++) {
12462            p = points[i];
12463            left = mathMin$3(left, p[0]);
12464            right = mathMax$3(right, p[0]);
12465            top = mathMin$3(top, p[1]);
12466            bottom = mathMax$3(bottom, p[1]);
12467        }
12468
12469        min$$1[0] = left;
12470        min$$1[1] = top;
12471        max$$1[0] = right;
12472        max$$1[1] = bottom;
12473    }
12474
12475    /**
12476     * @memberOf module:zrender/core/bbox
12477     * @param {number} x0
12478     * @param {number} y0
12479     * @param {number} x1
12480     * @param {number} y1
12481     * @param {Array.<number>} min
12482     * @param {Array.<number>} max
12483     */
12484    function fromLine(x0, y0, x1, y1, min$$1, max$$1) {
12485        min$$1[0] = mathMin$3(x0, x1);
12486        min$$1[1] = mathMin$3(y0, y1);
12487        max$$1[0] = mathMax$3(x0, x1);
12488        max$$1[1] = mathMax$3(y0, y1);
12489    }
12490
12491    var xDim = [];
12492    var yDim = [];
12493    /**
12494     * 浠庝笁闃惰礉濉炲皵鏇茬嚎(p0, p1, p2, p3)涓绠楀嚭鏈€灏忓寘鍥寸洅锛屽啓鍏min`鍜宍max`涓�
12495     * @memberOf module:zrender/core/bbox
12496     * @param {number} x0
12497     * @param {number} y0
12498     * @param {number} x1
12499     * @param {number} y1
12500     * @param {number} x2
12501     * @param {number} y2
12502     * @param {number} x3
12503     * @param {number} y3
12504     * @param {Array.<number>} min
12505     * @param {Array.<number>} max
12506     */
12507    function fromCubic(
12508        x0, y0, x1, y1, x2, y2, x3, y3, min$$1, max$$1
12509    ) {
12510        var cubicExtrema$$1 = cubicExtrema;
12511        var cubicAt$$1 = cubicAt;
12512        var i;
12513        var n = cubicExtrema$$1(x0, x1, x2, x3, xDim);
12514        min$$1[0] = Infinity;
12515        min$$1[1] = Infinity;
12516        max$$1[0] = -Infinity;
12517        max$$1[1] = -Infinity;
12518
12519        for (i = 0; i < n; i++) {
12520            var x = cubicAt$$1(x0, x1, x2, x3, xDim[i]);
12521            min$$1[0] = mathMin$3(x, min$$1[0]);
12522            max$$1[0] = mathMax$3(x, max$$1[0]);
12523        }
12524        n = cubicExtrema$$1(y0, y1, y2, y3, yDim);
12525        for (i = 0; i < n; i++) {
12526            var y = cubicAt$$1(y0, y1, y2, y3, yDim[i]);
12527            min$$1[1] = mathMin$3(y, min$$1[1]);
12528            max$$1[1] = mathMax$3(y, max$$1[1]);
12529        }
12530
12531        min$$1[0] = mathMin$3(x0, min$$1[0]);
12532        max$$1[0] = mathMax$3(x0, max$$1[0]);
12533        min$$1[0] = mathMin$3(x3, min$$1[0]);
12534        max$$1[0] = mathMax$3(x3, max$$1[0]);
12535
12536        min$$1[1] = mathMin$3(y0, min$$1[1]);
12537        max$$1[1] = mathMax$3(y0, max$$1[1]);
12538        min$$1[1] = mathMin$3(y3, min$$1[1]);
12539        max$$1[1] = mathMax$3(y3, max$$1[1]);
12540    }
12541
12542    /**
12543     * 浠庝簩闃惰礉濉炲皵鏇茬嚎(p0, p1, p2)涓绠楀嚭鏈€灏忓寘鍥寸洅锛屽啓鍏min`鍜宍max`涓�
12544     * @memberOf module:zrender/core/bbox
12545     * @param {number} x0
12546     * @param {number} y0
12547     * @param {number} x1
12548     * @param {number} y1
12549     * @param {number} x2
12550     * @param {number} y2
12551     * @param {Array.<number>} min
12552     * @param {Array.<number>} max
12553     */
12554    function fromQuadratic(x0, y0, x1, y1, x2, y2, min$$1, max$$1) {
12555        var quadraticExtremum$$1 = quadraticExtremum;
12556        var quadraticAt$$1 = quadraticAt;
12557        // Find extremities, where derivative in x dim or y dim is zero
12558        var tx =
12559            mathMax$3(
12560                mathMin$3(quadraticExtremum$$1(x0, x1, x2), 1), 0
12561            );
12562        var ty =
12563            mathMax$3(
12564                mathMin$3(quadraticExtremum$$1(y0, y1, y2), 1), 0
12565            );
12566
12567        var x = quadraticAt$$1(x0, x1, x2, tx);
12568        var y = quadraticAt$$1(y0, y1, y2, ty);
12569
12570        min$$1[0] = mathMin$3(x0, x2, x);
12571        min$$1[1] = mathMin$3(y0, y2, y);
12572        max$$1[0] = mathMax$3(x0, x2, x);
12573        max$$1[1] = mathMax$3(y0, y2, y);
12574    }
12575
12576    /**
12577     * 浠庡渾寮т腑璁$畻鍑烘渶灏忓寘鍥寸洅锛屽啓鍏min`鍜宍max`涓�
12578     * @method
12579     * @memberOf module:zrender/core/bbox
12580     * @param {number} x
12581     * @param {number} y
12582     * @param {number} rx
12583     * @param {number} ry
12584     * @param {number} startAngle
12585     * @param {number} endAngle
12586     * @param {number} anticlockwise
12587     * @param {Array.<number>} min
12588     * @param {Array.<number>} max
12589     */
12590    function fromArc(
12591        x, y, rx, ry, startAngle, endAngle, anticlockwise, min$$1, max$$1
12592    ) {
12593        var vec2Min = min;
12594        var vec2Max = max;
12595
12596        var diff = Math.abs(startAngle - endAngle);
12597
12598
12599        if (diff % PI2 < 1e-4 && diff > 1e-4) {
12600            // Is a circle
12601            min$$1[0] = x - rx;
12602            min$$1[1] = y - ry;
12603            max$$1[0] = x + rx;
12604            max$$1[1] = y + ry;
12605            return;
12606        }
12607
12608        start[0] = mathCos$2(startAngle) * rx + x;
12609        start[1] = mathSin$2(startAngle) * ry + y;
12610
12611        end[0] = mathCos$2(endAngle) * rx + x;
12612        end[1] = mathSin$2(endAngle) * ry + y;
12613
12614        vec2Min(min$$1, start, end);
12615        vec2Max(max$$1, start, end);
12616
12617        // Thresh to [0, Math.PI * 2]
12618        startAngle = startAngle % (PI2);
12619        if (startAngle < 0) {
12620            startAngle = startAngle + PI2;
12621        }
12622        endAngle = endAngle % (PI2);
12623        if (endAngle < 0) {
12624            endAngle = endAngle + PI2;
12625        }
12626
12627        if (startAngle > endAngle && !anticlockwise) {
12628            endAngle += PI2;
12629        }
12630        else if (startAngle < endAngle && anticlockwise) {
12631            startAngle += PI2;
12632        }
12633        if (anticlockwise) {
12634            var tmp = endAngle;
12635            endAngle = startAngle;
12636            startAngle = tmp;
12637        }
12638
12639        // var number = 0;
12640        // var step = (anticlockwise ? -Math.PI : Math.PI) / 2;
12641        for (var angle = 0; angle < endAngle; angle += Math.PI / 2) {
12642            if (angle > startAngle) {
12643                extremity[0] = mathCos$2(angle) * rx + x;
12644                extremity[1] = mathSin$2(angle) * ry + y;
12645
12646                vec2Min(min$$1, extremity, min$$1);
12647                vec2Max(max$$1, extremity, max$$1);
12648            }
12649        }
12650    }
12651
12652    /**
12653     * Path 浠g悊锛屽彲浠ュ湪`buildPath`涓敤浜庢浛浠ctx`, 浼氫繚瀛樻瘡涓猵ath鎿嶄綔鐨勫懡浠ゅ埌pathCommands灞炴€т腑
12654     * 鍙互鐢ㄤ簬 isInsidePath 鍒ゆ柇浠ュ強鑾峰彇boundingRect
12655     *
12656     * @module zrender/core/PathProxy
12657     * @author Yi Shen (http://www.github.com/pissang)
12658     */
12659
12660// TODO getTotalLength, getPointAtLength
12661
12662    var CMD = {
12663        M: 1,
12664        L: 2,
12665        C: 3,
12666        Q: 4,
12667        A: 5,
12668        Z: 6,
12669        // Rect
12670        R: 7
12671    };
12672
12673// var CMD_MEM_SIZE = {
12674//     M: 3,
12675//     L: 3,
12676//     C: 7,
12677//     Q: 5,
12678//     A: 9,
12679//     R: 5,
12680//     Z: 1
12681// };
12682
12683    var min$1 = [];
12684    var max$1 = [];
12685    var min2 = [];
12686    var max2 = [];
12687    var mathMin$2 = Math.min;
12688    var mathMax$2 = Math.max;
12689    var mathCos$1 = Math.cos;
12690    var mathSin$1 = Math.sin;
12691    var mathSqrt$1 = Math.sqrt;
12692    var mathAbs = Math.abs;
12693
12694    var hasTypedArray = typeof Float32Array != 'undefined';
12695
12696    /**
12697     * @alias module:zrender/core/PathProxy
12698     * @constructor
12699     */
12700    var PathProxy = function (notSaveData) {
12701
12702        this._saveData = !(notSaveData || false);
12703
12704        if (this._saveData) {
12705            /**
12706             * Path data. Stored as flat array
12707             * @type {Array.<Object>}
12708             */
12709            this.data = [];
12710        }
12711
12712        this._ctx = null;
12713    };
12714
12715    /**
12716     * 蹇€熻绠桺ath鍖呭洿鐩掞紙骞朵笉鏄渶灏忓寘鍥寸洅锛�
12717     * @return {Object}
12718     */
12719    PathProxy.prototype = {
12720
12721        constructor: PathProxy,
12722
12723        _xi: 0,
12724        _yi: 0,
12725
12726        _x0: 0,
12727        _y0: 0,
12728        // Unit x, Unit y. Provide for avoiding drawing that too short line segment
12729        _ux: 0,
12730        _uy: 0,
12731
12732        _len: 0,
12733
12734        _lineDash: null,
12735
12736        _dashOffset: 0,
12737
12738        _dashIdx: 0,
12739
12740        _dashSum: 0,
12741
12742        /**
12743         * @readOnly
12744         */
12745        setScale: function (sx, sy) {
12746            this._ux = mathAbs(1 / devicePixelRatio / sx) || 0;
12747            this._uy = mathAbs(1 / devicePixelRatio / sy) || 0;
12748        },
12749
12750        getContext: function () {
12751            return this._ctx;
12752        },
12753
12754        /**
12755         * @param  {CanvasRenderingContext2D} ctx
12756         * @return {module:zrender/core/PathProxy}
12757         */
12758        beginPath: function (ctx) {
12759
12760            this._ctx = ctx;
12761
12762            ctx && ctx.beginPath();
12763
12764            ctx && (this.dpr = ctx.dpr);
12765
12766            // Reset
12767            if (this._saveData) {
12768                this._len = 0;
12769            }
12770
12771            if (this._lineDash) {
12772                this._lineDash = null;
12773
12774                this._dashOffset = 0;
12775            }
12776
12777            return this;
12778        },
12779
12780        /**
12781         * @param  {number} x
12782         * @param  {number} y
12783         * @return {module:zrender/core/PathProxy}
12784         */
12785        moveTo: function (x, y) {
12786            this.addData(CMD.M, x, y);
12787            this._ctx && this._ctx.moveTo(x, y);
12788
12789            // x0, y0, xi, yi 鏄褰曞湪 _dashedXXXXTo 鏂规硶涓娇鐢�
12790            // xi, yi 璁板綍褰撳墠鐐�, x0, y0 鍦� closePath 鐨勬椂鍊欏洖鍒拌捣濮嬬偣銆�
12791            // 鏈夊彲鑳藉湪 beginPath 涔嬪悗鐩存帴璋冪敤 lineTo锛岃繖鏃跺€� x0, y0 闇€瑕�
12792            // 鍦� lineTo 鏂规硶涓褰曪紝杩欓噷鍏堜笉鑰冭檻杩欑鎯呭喌锛宒ashed line 涔熷彧鍦� IE10- 涓笉鏀寔
12793            this._x0 = x;
12794            this._y0 = y;
12795
12796            this._xi = x;
12797            this._yi = y;
12798
12799            return this;
12800        },
12801
12802        /**
12803         * @param  {number} x
12804         * @param  {number} y
12805         * @return {module:zrender/core/PathProxy}
12806         */
12807        lineTo: function (x, y) {
12808            var exceedUnit = mathAbs(x - this._xi) > this._ux
12809                || mathAbs(y - this._yi) > this._uy
12810                // Force draw the first segment
12811                || this._len < 5;
12812
12813            this.addData(CMD.L, x, y);
12814
12815            if (this._ctx && exceedUnit) {
12816                this._needsDash() ? this._dashedLineTo(x, y)
12817                    : this._ctx.lineTo(x, y);
12818            }
12819            if (exceedUnit) {
12820                this._xi = x;
12821                this._yi = y;
12822            }
12823
12824            return this;
12825        },
12826
12827        /**
12828         * @param  {number} x1
12829         * @param  {number} y1
12830         * @param  {number} x2
12831         * @param  {number} y2
12832         * @param  {number} x3
12833         * @param  {number} y3
12834         * @return {module:zrender/core/PathProxy}
12835         */
12836        bezierCurveTo: function (x1, y1, x2, y2, x3, y3) {
12837            this.addData(CMD.C, x1, y1, x2, y2, x3, y3);
12838            if (this._ctx) {
12839                this._needsDash() ? this._dashedBezierTo(x1, y1, x2, y2, x3, y3)
12840                    : this._ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3);
12841            }
12842            this._xi = x3;
12843            this._yi = y3;
12844            return this;
12845        },
12846
12847        /**
12848         * @param  {number} x1
12849         * @param  {number} y1
12850         * @param  {number} x2
12851         * @param  {number} y2
12852         * @return {module:zrender/core/PathProxy}
12853         */
12854        quadraticCurveTo: function (x1, y1, x2, y2) {
12855            this.addData(CMD.Q, x1, y1, x2, y2);
12856            if (this._ctx) {
12857                this._needsDash() ? this._dashedQuadraticTo(x1, y1, x2, y2)
12858                    : this._ctx.quadraticCurveTo(x1, y1, x2, y2);
12859            }
12860            this._xi = x2;
12861            this._yi = y2;
12862            return this;
12863        },
12864
12865        /**
12866         * @param  {number} cx
12867         * @param  {number} cy
12868         * @param  {number} r
12869         * @param  {number} startAngle
12870         * @param  {number} endAngle
12871         * @param  {boolean} anticlockwise
12872         * @return {module:zrender/core/PathProxy}
12873         */
12874        arc: function (cx, cy, r, startAngle, endAngle, anticlockwise) {
12875            this.addData(
12876                CMD.A, cx, cy, r, r, startAngle, endAngle - startAngle, 0, anticlockwise ? 0 : 1
12877            );
12878            this._ctx && this._ctx.arc(cx, cy, r, startAngle, endAngle, anticlockwise);
12879
12880            this._xi = mathCos$1(endAngle) * r + cx;
12881            this._yi = mathSin$1(endAngle) * r + cx;
12882            return this;
12883        },
12884
12885        // TODO
12886        arcTo: function (x1, y1, x2, y2, radius) {
12887            if (this._ctx) {
12888                this._ctx.arcTo(x1, y1, x2, y2, radius);
12889            }
12890            return this;
12891        },
12892
12893        // TODO
12894        rect: function (x, y, w, h) {
12895            this._ctx && this._ctx.rect(x, y, w, h);
12896            this.addData(CMD.R, x, y, w, h);
12897            return this;
12898        },
12899
12900        /**
12901         * @return {module:zrender/core/PathProxy}
12902         */
12903        closePath: function () {
12904            this.addData(CMD.Z);
12905
12906            var ctx = this._ctx;
12907            var x0 = this._x0;
12908            var y0 = this._y0;
12909            if (ctx) {
12910                this._needsDash() && this._dashedLineTo(x0, y0);
12911                ctx.closePath();
12912            }
12913
12914            this._xi = x0;
12915            this._yi = y0;
12916            return this;
12917        },
12918
12919        /**
12920         * Context 浠庡閮ㄤ紶鍏ワ紝鍥犱负鏈夊彲鑳芥槸 rebuildPath 瀹屼箣鍚庡啀 fill銆�
12921         * stroke 鍚屾牱
12922         * @param {CanvasRenderingContext2D} ctx
12923         * @return {module:zrender/core/PathProxy}
12924         */
12925        fill: function (ctx) {
12926            ctx && ctx.fill();
12927            this.toStatic();
12928        },
12929
12930        /**
12931         * @param {CanvasRenderingContext2D} ctx
12932         * @return {module:zrender/core/PathProxy}
12933         */
12934        stroke: function (ctx) {
12935            ctx && ctx.stroke();
12936            this.toStatic();
12937        },
12938
12939        /**
12940         * 蹇呴』鍦ㄥ叾瀹冪粯鍒跺懡浠ゅ墠璋冪敤
12941         * Must be invoked before all other path drawing methods
12942         * @return {module:zrender/core/PathProxy}
12943         */
12944        setLineDash: function (lineDash) {
12945            if (lineDash instanceof Array) {
12946                this._lineDash = lineDash;
12947
12948                this._dashIdx = 0;
12949
12950                var lineDashSum = 0;
12951                for (var i = 0; i < lineDash.length; i++) {
12952                    lineDashSum += lineDash[i];
12953                }
12954                this._dashSum = lineDashSum;
12955            }
12956            return this;
12957        },
12958
12959        /**
12960         * 蹇呴』鍦ㄥ叾瀹冪粯鍒跺懡浠ゅ墠璋冪敤
12961         * Must be invoked before all other path drawing methods
12962         * @return {module:zrender/core/PathProxy}
12963         */
12964        setLineDashOffset: function (offset) {
12965            this._dashOffset = offset;
12966            return this;
12967        },
12968
12969        /**
12970         *
12971         * @return {boolean}
12972         */
12973        len: function () {
12974            return this._len;
12975        },
12976
12977        /**
12978         * 鐩存帴璁剧疆 Path 鏁版嵁
12979         */
12980        setData: function (data) {
12981
12982            var len$$1 = data.length;
12983
12984            if (! (this.data && this.data.length == len$$1) && hasTypedArray) {
12985                this.data = new Float32Array(len$$1);
12986            }
12987
12988            for (var i = 0; i < len$$1; i++) {
12989                this.data[i] = data[i];
12990            }
12991
12992            this._len = len$$1;
12993        },
12994
12995        /**
12996         * 娣诲姞瀛愯矾寰�
12997         * @param {module:zrender/core/PathProxy|Array.<module:zrender/core/PathProxy>} path
12998         */
12999        appendPath: function (path) {
13000            if (!(path instanceof Array)) {
13001                path = [path];
13002            }
13003            var len$$1 = path.length;
13004            var appendSize = 0;
13005            var offset = this._len;
13006            for (var i = 0; i < len$$1; i++) {
13007                appendSize += path[i].len();
13008            }
13009            if (hasTypedArray && (this.data instanceof Float32Array)) {
13010                this.data = new Float32Array(offset + appendSize);
13011            }
13012            for (var i = 0; i < len$$1; i++) {
13013                var appendPathData = path[i].data;
13014                for (var k = 0; k < appendPathData.length; k++) {
13015                    this.data[offset++] = appendPathData[k];
13016                }
13017            }
13018            this._len = offset;
13019        },
13020
13021        /**
13022         * 濉厖 Path 鏁版嵁銆�
13023         * 灏介噺澶嶇敤鑰屼笉鐢虫槑鏂扮殑鏁扮粍銆傚ぇ閮ㄥ垎鍥惧舰閲嶇粯鐨勬寚浠ゆ暟鎹暱搴﹂兘鏄笉鍙樼殑銆�
13024         */
13025        addData: function (cmd) {
13026            if (!this._saveData) {
13027                return;
13028            }
13029
13030            var data = this.data;
13031            if (this._len + arguments.length > data.length) {
13032                // 鍥犱负涔嬪墠鐨勬暟缁勫凡缁忚浆鎹㈡垚闈欐€佺殑 Float32Array
13033                // 鎵€浠ヤ笉澶熺敤鏃堕渶瑕佹墿灞曚竴涓柊鐨勫姩鎬佹暟缁�
13034                this._expandData();
13035                data = this.data;
13036            }
13037            for (var i = 0; i < arguments.length; i++) {
13038                data[this._len++] = arguments[i];
13039            }
13040
13041            this._prevCmd = cmd;
13042        },
13043
13044        _expandData: function () {
13045            // Only if data is Float32Array
13046            if (!(this.data instanceof Array)) {
13047                var newData = [];
13048                for (var i = 0; i < this._len; i++) {
13049                    newData[i] = this.data[i];
13050                }
13051                this.data = newData;
13052            }
13053        },
13054
13055        /**
13056         * If needs js implemented dashed line
13057         * @return {boolean}
13058         * @private
13059         */
13060        _needsDash: function () {
13061            return this._lineDash;
13062        },
13063
13064        _dashedLineTo: function (x1, y1) {
13065            var dashSum = this._dashSum;
13066            var offset = this._dashOffset;
13067            var lineDash = this._lineDash;
13068            var ctx = this._ctx;
13069
13070            var x0 = this._xi;
13071            var y0 = this._yi;
13072            var dx = x1 - x0;
13073            var dy = y1 - y0;
13074            var dist$$1 = mathSqrt$1(dx * dx + dy * dy);
13075            var x = x0;
13076            var y = y0;
13077            var dash;
13078            var nDash = lineDash.length;
13079            var idx;
13080            dx /= dist$$1;
13081            dy /= dist$$1;
13082
13083            if (offset < 0) {
13084                // Convert to positive offset
13085                offset = dashSum + offset;
13086            }
13087            offset %= dashSum;
13088            x -= offset * dx;
13089            y -= offset * dy;
13090
13091            while ((dx > 0 && x <= x1) || (dx < 0 && x >= x1)
13092            || (dx == 0 && ((dy > 0 && y <= y1) || (dy < 0 && y >= y1)))) {
13093                idx = this._dashIdx;
13094                dash = lineDash[idx];
13095                x += dx * dash;
13096                y += dy * dash;
13097                this._dashIdx = (idx + 1) % nDash;
13098                // Skip positive offset
13099                if ((dx > 0 && x < x0) || (dx < 0 && x > x0) || (dy > 0 && y < y0) || (dy < 0 && y > y0)) {
13100                    continue;
13101                }
13102                ctx[idx % 2 ? 'moveTo' : 'lineTo'](
13103                    dx >= 0 ? mathMin$2(x, x1) : mathMax$2(x, x1),
13104                    dy >= 0 ? mathMin$2(y, y1) : mathMax$2(y, y1)
13105                );
13106            }
13107            // Offset for next lineTo
13108            dx = x - x1;
13109            dy = y - y1;
13110            this._dashOffset = -mathSqrt$1(dx * dx + dy * dy);
13111        },
13112
13113        // Not accurate dashed line to
13114        _dashedBezierTo: function (x1, y1, x2, y2, x3, y3) {
13115            var dashSum = this._dashSum;
13116            var offset = this._dashOffset;
13117            var lineDash = this._lineDash;
13118            var ctx = this._ctx;
13119
13120            var x0 = this._xi;
13121            var y0 = this._yi;
13122            var t;
13123            var dx;
13124            var dy;
13125            var cubicAt$$1 = cubicAt;
13126            var bezierLen = 0;
13127            var idx = this._dashIdx;
13128            var nDash = lineDash.length;
13129
13130            var x;
13131            var y;
13132
13133            var tmpLen = 0;
13134
13135            if (offset < 0) {
13136                // Convert to positive offset
13137                offset = dashSum + offset;
13138            }
13139            offset %= dashSum;
13140            // Bezier approx length
13141            for (t = 0; t < 1; t += 0.1) {
13142                dx = cubicAt$$1(x0, x1, x2, x3, t + 0.1)
13143                    - cubicAt$$1(x0, x1, x2, x3, t);
13144                dy = cubicAt$$1(y0, y1, y2, y3, t + 0.1)
13145                    - cubicAt$$1(y0, y1, y2, y3, t);
13146                bezierLen += mathSqrt$1(dx * dx + dy * dy);
13147            }
13148
13149            // Find idx after add offset
13150            for (; idx < nDash; idx++) {
13151                tmpLen += lineDash[idx];
13152                if (tmpLen > offset) {
13153                    break;
13154                }
13155            }
13156            t = (tmpLen - offset) / bezierLen;
13157
13158            while (t <= 1) {
13159
13160                x = cubicAt$$1(x0, x1, x2, x3, t);
13161                y = cubicAt$$1(y0, y1, y2, y3, t);
13162
13163                // Use line to approximate dashed bezier
13164                // Bad result if dash is long
13165                idx % 2 ? ctx.moveTo(x, y)
13166                    : ctx.lineTo(x, y);
13167
13168                t += lineDash[idx] / bezierLen;
13169
13170                idx = (idx + 1) % nDash;
13171            }
13172
13173            // Finish the last segment and calculate the new offset
13174            (idx % 2 !== 0) && ctx.lineTo(x3, y3);
13175            dx = x3 - x;
13176            dy = y3 - y;
13177            this._dashOffset = -mathSqrt$1(dx * dx + dy * dy);
13178        },
13179
13180        _dashedQuadraticTo: function (x1, y1, x2, y2) {
13181            // Convert quadratic to cubic using degree elevation
13182            var x3 = x2;
13183            var y3 = y2;
13184            x2 = (x2 + 2 * x1) / 3;
13185            y2 = (y2 + 2 * y1) / 3;
13186            x1 = (this._xi + 2 * x1) / 3;
13187            y1 = (this._yi + 2 * y1) / 3;
13188
13189            this._dashedBezierTo(x1, y1, x2, y2, x3, y3);
13190        },
13191
13192        /**
13193         * 杞垚闈欐€佺殑 Float32Array 鍑忓皯鍫嗗唴瀛樺崰鐢�
13194         * Convert dynamic array to static Float32Array
13195         */
13196        toStatic: function () {
13197            var data = this.data;
13198            if (data instanceof Array) {
13199                data.length = this._len;
13200                if (hasTypedArray) {
13201                    this.data = new Float32Array(data);
13202                }
13203            }
13204        },
13205
13206        /**
13207         * @return {module:zrender/core/BoundingRect}
13208         */
13209        getBoundingRect: function () {
13210            min$1[0] = min$1[1] = min2[0] = min2[1] = Number.MAX_VALUE;
13211            max$1[0] = max$1[1] = max2[0] = max2[1] = -Number.MAX_VALUE;
13212
13213            var data = this.data;
13214            var xi = 0;
13215            var yi = 0;
13216            var x0 = 0;
13217            var y0 = 0;
13218
13219            for (var i = 0; i < data.length;) {
13220                var cmd = data[i++];
13221
13222                if (i == 1) {
13223                    // 濡傛灉绗竴涓懡浠ゆ槸 L, C, Q
13224                    // 鍒� previous point 鍚岀粯鍒跺懡浠ょ殑绗竴涓� point
13225                    //
13226                    // 绗竴涓懡浠や负 Arc 鐨勬儏鍐典笅浼氬湪鍚庨潰鐗规畩澶勭悊
13227                    xi = data[i];
13228                    yi = data[i + 1];
13229
13230                    x0 = xi;
13231                    y0 = yi;
13232                }
13233
13234                switch (cmd) {
13235                    case CMD.M:
13236                        // moveTo 鍛戒护閲嶆柊鍒涘缓涓€涓柊鐨� subpath, 骞朵笖鏇存柊鏂扮殑璧风偣
13237                        // 鍦� closePath 鐨勬椂鍊欎娇鐢�
13238                        x0 = data[i++];
13239                        y0 = data[i++];
13240                        xi = x0;
13241                        yi = y0;
13242                        min2[0] = x0;
13243                        min2[1] = y0;
13244                        max2[0] = x0;
13245                        max2[1] = y0;
13246                        break;
13247                    case CMD.L:
13248                        fromLine(xi, yi, data[i], data[i + 1], min2, max2);
13249                        xi = data[i++];
13250                        yi = data[i++];
13251                        break;
13252                    case CMD.C:
13253                        fromCubic(
13254                            xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1],
13255                            min2, max2
13256                        );
13257                        xi = data[i++];
13258                        yi = data[i++];
13259                        break;
13260                    case CMD.Q:
13261                        fromQuadratic(
13262                            xi, yi, data[i++], data[i++], data[i], data[i + 1],
13263                            min2, max2
13264                        );
13265                        xi = data[i++];
13266                        yi = data[i++];
13267                        break;
13268                    case CMD.A:
13269                        // TODO Arc 鍒ゆ柇鐨勫紑閿€姣旇緝澶�
13270                        var cx = data[i++];
13271                        var cy = data[i++];
13272                        var rx = data[i++];
13273                        var ry = data[i++];
13274                        var startAngle = data[i++];
13275                        var endAngle = data[i++] + startAngle;
13276                        // TODO Arc 鏃嬭浆
13277                        var psi = data[i++];
13278                        var anticlockwise = 1 - data[i++];
13279
13280                        if (i == 1) {
13281                            // 鐩存帴浣跨敤 arc 鍛戒护
13282                            // 绗竴涓懡浠よ捣鐐硅繕鏈畾涔�
13283                            x0 = mathCos$1(startAngle) * rx + cx;
13284                            y0 = mathSin$1(startAngle) * ry + cy;
13285                        }
13286
13287                        fromArc(
13288                            cx, cy, rx, ry, startAngle, endAngle,
13289                            anticlockwise, min2, max2
13290                        );
13291
13292                        xi = mathCos$1(endAngle) * rx + cx;
13293                        yi = mathSin$1(endAngle) * ry + cy;
13294                        break;
13295                    case CMD.R:
13296                        x0 = xi = data[i++];
13297                        y0 = yi = data[i++];
13298                        var width = data[i++];
13299                        var height = data[i++];
13300                        // Use fromLine
13301                        fromLine(x0, y0, x0 + width, y0 + height, min2, max2);
13302                        break;
13303                    case CMD.Z:
13304                        xi = x0;
13305                        yi = y0;
13306                        break;
13307                }
13308
13309                // Union
13310                min(min$1, min$1, min2);
13311                max(max$1, max$1, max2);
13312            }
13313
13314            // No data
13315            if (i === 0) {
13316                min$1[0] = min$1[1] = max$1[0] = max$1[1] = 0;
13317            }
13318
13319            return new BoundingRect(
13320                min$1[0], min$1[1], max$1[0] - min$1[0], max$1[1] - min$1[1]
13321            );
13322        },
13323
13324        /**
13325         * Rebuild path from current data
13326         * Rebuild path will not consider javascript implemented line dash.
13327         * @param {CanvasRenderingContext2D} ctx
13328         */
13329        rebuildPath: function (ctx) {
13330            var d = this.data;
13331            var x0, y0;
13332            var xi, yi;
13333            var x, y;
13334            var ux = this._ux;
13335            var uy = this._uy;
13336            var len$$1 = this._len;
13337            for (var i = 0; i < len$$1;) {
13338                var cmd = d[i++];
13339
13340                if (i == 1) {
13341                    // 濡傛灉绗竴涓懡浠ゆ槸 L, C, Q
13342                    // 鍒� previous point 鍚岀粯鍒跺懡浠ょ殑绗竴涓� point
13343                    //
13344                    // 绗竴涓懡浠や负 Arc 鐨勬儏鍐典笅浼氬湪鍚庨潰鐗规畩澶勭悊
13345                    xi = d[i];
13346                    yi = d[i + 1];
13347
13348                    x0 = xi;
13349                    y0 = yi;
13350                }
13351                switch (cmd) {
13352                    case CMD.M:
13353                        x0 = xi = d[i++];
13354                        y0 = yi = d[i++];
13355                        ctx.moveTo(xi, yi);
13356                        break;
13357                    case CMD.L:
13358                        x = d[i++];
13359                        y = d[i++];
13360                        // Not draw too small seg between
13361                        if (mathAbs(x - xi) > ux || mathAbs(y - yi) > uy || i === len$$1 - 1) {
13362                            ctx.lineTo(x, y);
13363                            xi = x;
13364                            yi = y;
13365                        }
13366                        break;
13367                    case CMD.C:
13368                        ctx.bezierCurveTo(
13369                            d[i++], d[i++], d[i++], d[i++], d[i++], d[i++]
13370                        );
13371                        xi = d[i - 2];
13372                        yi = d[i - 1];
13373                        break;
13374                    case CMD.Q:
13375                        ctx.quadraticCurveTo(d[i++], d[i++], d[i++], d[i++]);
13376                        xi = d[i - 2];
13377                        yi = d[i - 1];
13378                        break;
13379                    case CMD.A:
13380                        var cx = d[i++];
13381                        var cy = d[i++];
13382                        var rx = d[i++];
13383                        var ry = d[i++];
13384                        var theta = d[i++];
13385                        var dTheta = d[i++];
13386                        var psi = d[i++];
13387                        var fs = d[i++];
13388                        var r = (rx > ry) ? rx : ry;
13389                        var scaleX = (rx > ry) ? 1 : rx / ry;
13390                        var scaleY = (rx > ry) ? ry / rx : 1;
13391                        var isEllipse = Math.abs(rx - ry) > 1e-3;
13392                        var endAngle = theta + dTheta;
13393                        if (isEllipse) {
13394                            ctx.translate(cx, cy);
13395                            ctx.rotate(psi);
13396                            ctx.scale(scaleX, scaleY);
13397                            ctx.arc(0, 0, r, theta, endAngle, 1 - fs);
13398                            ctx.scale(1 / scaleX, 1 / scaleY);
13399                            ctx.rotate(-psi);
13400                            ctx.translate(-cx, -cy);
13401                        }
13402                        else {
13403                            ctx.arc(cx, cy, r, theta, endAngle, 1 - fs);
13404                        }
13405
13406                        if (i == 1) {
13407                            // 鐩存帴浣跨敤 arc 鍛戒护
vendor: 13,297 bytes, lines 13408-13843
13408                            // 绗竴涓懡浠よ捣鐐硅繕鏈畾涔�
13409                            x0 = mathCos$1(theta) * rx + cx;
13410                            y0 = mathSin$1(theta) * ry + cy;
13411                        }
13412                        xi = mathCos$1(endAngle) * rx + cx;
13413                        yi = mathSin$1(endAngle) * ry + cy;
13414                        break;
13415                    case CMD.R:
13416                        x0 = xi = d[i];
13417                        y0 = yi = d[i + 1];
13418                        ctx.rect(d[i++], d[i++], d[i++], d[i++]);
13419                        break;
13420                    case CMD.Z:
13421                        ctx.closePath();
13422                        xi = x0;
13423                        yi = y0;
13424                }
13425            }
13426        }
13427    };
13428
13429    PathProxy.CMD = CMD;
13430
13431    /**
13432     * 绾挎鍖呭惈鍒ゆ柇
13433     * @param  {number}  x0
13434     * @param  {number}  y0
13435     * @param  {number}  x1
13436     * @param  {number}  y1
13437     * @param  {number}  lineWidth
13438     * @param  {number}  x
13439     * @param  {number}  y
13440     * @return {boolean}
13441     */
13442    function containStroke$1(x0, y0, x1, y1, lineWidth, x, y) {
13443        if (lineWidth === 0) {
13444            return false;
13445        }
13446        var _l = lineWidth;
13447        var _a = 0;
13448        var _b = x0;
13449        // Quick reject
13450        if (
13451            (y > y0 + _l && y > y1 + _l)
13452            || (y < y0 - _l && y < y1 - _l)
13453            || (x > x0 + _l && x > x1 + _l)
13454            || (x < x0 - _l && x < x1 - _l)
13455        ) {
13456            return false;
13457        }
13458
13459        if (x0 !== x1) {
13460            _a = (y0 - y1) / (x0 - x1);
13461            _b = (x0 * y1 - x1 * y0) / (x0 - x1) ;
13462        }
13463        else {
13464            return Math.abs(x - x0) <= _l / 2;
13465        }
13466        var tmp = _a * x - y + _b;
13467        var _s = tmp * tmp / (_a * _a + 1);
13468        return _s <= _l / 2 * _l / 2;
13469    }
13470
13471    /**
13472     * 涓夋璐濆灏旀洸绾挎弿杈瑰寘鍚垽鏂�
13473     * @param  {number}  x0
13474     * @param  {number}  y0
13475     * @param  {number}  x1
13476     * @param  {number}  y1
13477     * @param  {number}  x2
13478     * @param  {number}  y2
13479     * @param  {number}  x3
13480     * @param  {number}  y3
13481     * @param  {number}  lineWidth
13482     * @param  {number}  x
13483     * @param  {number}  y
13484     * @return {boolean}
13485     */
13486    function containStroke$2(x0, y0, x1, y1, x2, y2, x3, y3, lineWidth, x, y) {
13487        if (lineWidth === 0) {
13488            return false;
13489        }
13490        var _l = lineWidth;
13491        // Quick reject
13492        if (
13493            (y > y0 + _l && y > y1 + _l && y > y2 + _l && y > y3 + _l)
13494            || (y < y0 - _l && y < y1 - _l && y < y2 - _l && y < y3 - _l)
13495            || (x > x0 + _l && x > x1 + _l && x > x2 + _l && x > x3 + _l)
13496            || (x < x0 - _l && x < x1 - _l && x < x2 - _l && x < x3 - _l)
13497        ) {
13498            return false;
13499        }
13500        var d = cubicProjectPoint(
13501            x0, y0, x1, y1, x2, y2, x3, y3,
13502            x, y, null
13503        );
13504        return d <= _l / 2;
13505    }
13506
13507    /**
13508     * 浜屾璐濆灏旀洸绾挎弿杈瑰寘鍚垽鏂�
13509     * @param  {number}  x0
13510     * @param  {number}  y0
13511     * @param  {number}  x1
13512     * @param  {number}  y1
13513     * @param  {number}  x2
13514     * @param  {number}  y2
13515     * @param  {number}  lineWidth
13516     * @param  {number}  x
13517     * @param  {number}  y
13518     * @return {boolean}
13519     */
13520    function containStroke$3(x0, y0, x1, y1, x2, y2, lineWidth, x, y) {
13521        if (lineWidth === 0) {
13522            return false;
13523        }
13524        var _l = lineWidth;
13525        // Quick reject
13526        if (
13527            (y > y0 + _l && y > y1 + _l && y > y2 + _l)
13528            || (y < y0 - _l && y < y1 - _l && y < y2 - _l)
13529            || (x > x0 + _l && x > x1 + _l && x > x2 + _l)
13530            || (x < x0 - _l && x < x1 - _l && x < x2 - _l)
13531        ) {
13532            return false;
13533        }
13534        var d = quadraticProjectPoint(
13535            x0, y0, x1, y1, x2, y2,
13536            x, y, null
13537        );
13538        return d <= _l / 2;
13539    }
13540
13541    var PI2$3 = Math.PI * 2;
13542
13543    function normalizeRadian(angle) {
13544        angle %= PI2$3;
13545        if (angle < 0) {
13546            angle += PI2$3;
13547        }
13548        return angle;
13549    }
13550
13551    var PI2$2 = Math.PI * 2;
13552
13553    /**
13554     * 鍦嗗姬鎻忚竟鍖呭惈鍒ゆ柇
13555     * @param  {number}  cx
13556     * @param  {number}  cy
13557     * @param  {number}  r
13558     * @param  {number}  startAngle
13559     * @param  {number}  endAngle
13560     * @param  {boolean}  anticlockwise
13561     * @param  {number} lineWidth
13562     * @param  {number}  x
13563     * @param  {number}  y
13564     * @return {Boolean}
13565     */
13566    function containStroke$4(
13567        cx, cy, r, startAngle, endAngle, anticlockwise,
13568        lineWidth, x, y
13569    ) {
13570
13571        if (lineWidth === 0) {
13572            return false;
13573        }
13574        var _l = lineWidth;
13575
13576        x -= cx;
13577        y -= cy;
13578        var d = Math.sqrt(x * x + y * y);
13579
13580        if ((d - _l > r) || (d + _l < r)) {
13581            return false;
13582        }
13583        if (Math.abs(startAngle - endAngle) % PI2$2 < 1e-4) {
13584            // Is a circle
13585            return true;
13586        }
13587        if (anticlockwise) {
13588            var tmp = startAngle;
13589            startAngle = normalizeRadian(endAngle);
13590            endAngle = normalizeRadian(tmp);
13591        } else {
13592            startAngle = normalizeRadian(startAngle);
13593            endAngle = normalizeRadian(endAngle);
13594        }
13595        if (startAngle > endAngle) {
13596            endAngle += PI2$2;
13597        }
13598
13599        var angle = Math.atan2(y, x);
13600        if (angle < 0) {
13601            angle += PI2$2;
13602        }
13603        return (angle >= startAngle && angle <= endAngle)
13604            || (angle + PI2$2 >= startAngle && angle + PI2$2 <= endAngle);
13605    }
13606
13607    function windingLine(x0, y0, x1, y1, x, y) {
13608        if ((y > y0 && y > y1) || (y < y0 && y < y1)) {
13609            return 0;
13610        }
13611        // Ignore horizontal line
13612        if (y1 === y0) {
13613            return 0;
13614        }
13615        var dir = y1 < y0 ? 1 : -1;
13616        var t = (y - y0) / (y1 - y0);
13617
13618        // Avoid winding error when intersection point is the connect point of two line of polygon
13619        if (t === 1 || t === 0) {
13620            dir = y1 < y0 ? 0.5 : -0.5;
13621        }
13622
13623        var x_ = t * (x1 - x0) + x0;
13624
13625        // If (x, y) on the line, considered as "contain".
13626        return x_ === x ? Infinity : x_ > x ? dir : 0;
13627    }
13628
13629    var CMD$1 = PathProxy.CMD;
13630    var PI2$1 = Math.PI * 2;
13631
13632    var EPSILON$2 = 1e-4;
13633
13634    function isAroundEqual(a, b) {
13635        return Math.abs(a - b) < EPSILON$2;
13636    }
13637
13638// 涓存椂鏁扮粍
13639    var roots = [-1, -1, -1];
13640    var extrema = [-1, -1];
13641
13642    function swapExtrema() {
13643        var tmp = extrema[0];
13644        extrema[0] = extrema[1];
13645        extrema[1] = tmp;
13646    }
13647
13648    function windingCubic(x0, y0, x1, y1, x2, y2, x3, y3, x, y) {
13649        // Quick reject
13650        if (
13651            (y > y0 && y > y1 && y > y2 && y > y3)
13652            || (y < y0 && y < y1 && y < y2 && y < y3)
13653        ) {
13654            return 0;
13655        }
13656        var nRoots = cubicRootAt(y0, y1, y2, y3, y, roots);
13657        if (nRoots === 0) {
13658            return 0;
13659        }
13660        else {
13661            var w = 0;
13662            var nExtrema = -1;
13663            var y0_, y1_;
13664            for (var i = 0; i < nRoots; i++) {
13665                var t = roots[i];
13666
13667                // Avoid winding error when intersection point is the connect point of two line of polygon
13668                var unit = (t === 0 || t === 1) ? 0.5 : 1;
13669
13670                var x_ = cubicAt(x0, x1, x2, x3, t);
13671                if (x_ < x) { // Quick reject
13672                    continue;
13673                }
13674                if (nExtrema < 0) {
13675                    nExtrema = cubicExtrema(y0, y1, y2, y3, extrema);
13676                    if (extrema[1] < extrema[0] && nExtrema > 1) {
13677                        swapExtrema();
13678                    }
13679                    y0_ = cubicAt(y0, y1, y2, y3, extrema[0]);
13680                    if (nExtrema > 1) {
13681                        y1_ = cubicAt(y0, y1, y2, y3, extrema[1]);
13682                    }
13683                }
13684                if (nExtrema == 2) {
13685                    // 鍒嗘垚涓夋鍗曡皟鍑芥暟
13686                    if (t < extrema[0]) {
13687                        w += y0_ < y0 ? unit : -unit;
13688                    }
13689                    else if (t < extrema[1]) {
13690                        w += y1_ < y0_ ? unit : -unit;
13691                    }
13692                    else {
13693                        w += y3 < y1_ ? unit : -unit;
13694                    }
13695                }
13696                else {
13697                    // 鍒嗘垚涓ゆ鍗曡皟鍑芥暟
13698                    if (t < extrema[0]) {
13699                        w += y0_ < y0 ? unit : -unit;
13700                    }
13701                    else {
13702                        w += y3 < y0_ ? unit : -unit;
13703                    }
13704                }
13705            }
13706            return w;
13707        }
13708    }
13709
13710    function windingQuadratic(x0, y0, x1, y1, x2, y2, x, y) {
13711        // Quick reject
13712        if (
13713            (y > y0 && y > y1 && y > y2)
13714            || (y < y0 && y < y1 && y < y2)
13715        ) {
13716            return 0;
13717        }
13718        var nRoots = quadraticRootAt(y0, y1, y2, y, roots);
13719        if (nRoots === 0) {
13720            return 0;
13721        }
13722        else {
13723            var t = quadraticExtremum(y0, y1, y2);
13724            if (t >= 0 && t <= 1) {
13725                var w = 0;
13726                var y_ = quadraticAt(y0, y1, y2, t);
13727                for (var i = 0; i < nRoots; i++) {
13728                    // Remove one endpoint.
13729                    var unit = (roots[i] === 0 || roots[i] === 1) ? 0.5 : 1;
13730
13731                    var x_ = quadraticAt(x0, x1, x2, roots[i]);
13732                    if (x_ < x) {   // Quick reject
13733                        continue;
13734                    }
13735                    if (roots[i] < t) {
13736                        w += y_ < y0 ? unit : -unit;
13737                    }
13738                    else {
13739                        w += y2 < y_ ? unit : -unit;
13740                    }
13741                }
13742                return w;
13743            }
13744            else {
13745                // Remove one endpoint.
13746                var unit = (roots[0] === 0 || roots[0] === 1) ? 0.5 : 1;
13747
13748                var x_ = quadraticAt(x0, x1, x2, roots[0]);
13749                if (x_ < x) {   // Quick reject
13750                    return 0;
13751                }
13752                return y2 < y0 ? unit : -unit;
13753            }
13754        }
13755    }
13756
13757// TODO
13758// Arc 鏃嬭浆
13759    function windingArc(
13760        cx, cy, r, startAngle, endAngle, anticlockwise, x, y
13761    ) {
13762        y -= cy;
13763        if (y > r || y < -r) {
13764            return 0;
13765        }
13766        var tmp = Math.sqrt(r * r - y * y);
13767        roots[0] = -tmp;
13768        roots[1] = tmp;
13769
13770        var diff = Math.abs(startAngle - endAngle);
13771        if (diff < 1e-4) {
13772            return 0;
13773        }
13774        if (diff % PI2$1 < 1e-4) {
13775            // Is a circle
13776            startAngle = 0;
13777            endAngle = PI2$1;
13778            var dir = anticlockwise ? 1 : -1;
13779            if (x >= roots[0] + cx && x <= roots[1] + cx) {
13780                return dir;
13781            } else {
13782                return 0;
13783            }
13784        }
13785
13786        if (anticlockwise) {
13787            var tmp = startAngle;
13788            startAngle = normalizeRadian(endAngle);
13789            endAngle = normalizeRadian(tmp);
13790        }
13791        else {
13792            startAngle = normalizeRadian(startAngle);
13793            endAngle = normalizeRadian(endAngle);
13794        }
13795        if (startAngle > endAngle) {
13796            endAngle += PI2$1;
13797        }
13798
13799        var w = 0;
13800        for (var i = 0; i < 2; i++) {
13801            var x_ = roots[i];
13802            if (x_ + cx > x) {
13803                var angle = Math.atan2(y, x_);
13804                var dir = anticlockwise ? 1 : -1;
13805                if (angle < 0) {
13806                    angle = PI2$1 + angle;
13807                }
13808                if (
13809                    (angle >= startAngle && angle <= endAngle)
13810                    || (angle + PI2$1 >= startAngle && angle + PI2$1 <= endAngle)
13811                ) {
13812                    if (angle > Math.PI / 2 && angle < Math.PI * 1.5) {
13813                        dir = -dir;
13814                    }
13815                    w += dir;
13816                }
13817            }
13818        }
13819        return w;
13820    }
13821
13822    function containPath(data, lineWidth, isStroke, x, y) {
13823        var w = 0;
13824        var xi = 0;
13825        var yi = 0;
13826        var x0 = 0;
13827        var y0 = 0;
13828
13829        for (var i = 0; i < data.length;) {
13830            var cmd = data[i++];
13831            // Begin a new subpath
13832            if (cmd === CMD$1.M && i > 1) {
13833                // Close previous subpath
13834                if (!isStroke) {
13835                    w += windingLine(xi, yi, x0, y0, x, y);
13836                }
13837                // 濡傛灉琚换浣曚竴涓� subpath 鍖呭惈
13838                // if (w !== 0) {
13839                //     return true;
13840                // }
13841            }
13842
13843            if (i == 1) {
13844                // 濡傛灉绗竴涓懡浠ゆ槸 L, C, Q
13845                // 鍒� previous point 鍚岀粯鍒跺懡浠ょ殑绗竴涓� point
13846                //
13847                // 绗竴涓懡浠や负 Arc 鐨勬儏鍐典笅浼氬湪鍚庨潰鐗规畩澶勭悊
13848                xi = data[i];
13849                yi = data[i + 1];
13850
13851                x0 = xi;
13852                y0 = yi;
13853            }
13854
13855            switch (cmd) {
13856                case CMD$1.M:
13857                    // moveTo 鍛戒护閲嶆柊鍒涘缓涓€涓柊鐨� subpath, 骞朵笖鏇存柊鏂扮殑璧风偣
13858                    // 鍦� closePath 鐨勬椂鍊欎娇鐢�
13859                    x0 = data[i++];
13860                    y0 = data[i++];
13861                    xi = x0;
13862                    yi = y0;
13863                    break;
13864                case CMD$1.L:
13865                    if (isStroke) {
13866                        if (containStroke$1(xi, yi, data[i], data[i + 1], lineWidth, x, y)) {
13867                            return true;
13868                        }
13869                    }
13870                    else {
13871                        // NOTE 鍦ㄧ涓€涓懡浠や负 L, C, Q 鐨勬椂鍊欎細璁$畻鍑� NaN
13872                        w += windingLine(xi, yi, data[i], data[i + 1], x, y) || 0;
13873                    }
13874                    xi = data[i++];
13875                    yi = data[i++];
13876                    break;
13877                case CMD$1.C:
13878                    if (isStroke) {
13879                        if (containStroke$2(xi, yi,
13880                                data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1],
13881                                lineWidth, x, y
13882                            )) {
13883                            return true;
13884                        }
13885                    }
13886                    else {
13887                        w += windingCubic(
13888                            xi, yi,
13889                            data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1],
13890                            x, y
13891                        ) || 0;
13892                    }
13893                    xi = data[i++];
13894                    yi = data[i++];
13895                    break;
13896                case CMD$1.Q:
13897                    if (isStroke) {
13898                        if (containStroke$3(xi, yi,
13899                                data[i++], data[i++], data[i], data[i + 1],
13900                                lineWidth, x, y
13901                            )) {
13902                            return true;
13903                        }
13904                    }
13905                    else {
13906                        w += windingQuadratic(
13907                            xi, yi,
13908                            data[i++], data[i++], data[i], data[i + 1],
13909                            x, y
13910                        ) || 0;
13911                    }
13912                    xi = data[i++];
13913                    yi = data[i++];
13914                    break;
13915                case CMD$1.A:
13916                    // TODO Arc 鍒ゆ柇鐨勫紑閿€姣旇緝澶�
13917                    var cx = data[i++];
13918                    var cy = data[i++];
13919                    var rx = data[i++];
13920                    var ry = data[i++];
13921                    var theta = data[i++];
13922                    var dTheta = data[i++];
13923                    // TODO Arc 鏃嬭浆
13924                    var psi = data[i++];
13925                    var anticlockwise = 1 - data[i++];
13926                    var x1 = Math.cos(theta) * rx + cx;
13927                    var y1 = Math.sin(theta) * ry + cy;
13928                    // 涓嶆槸鐩存帴浣跨敤 arc 鍛戒护
13929                    if (i > 1) {
13930                        w += windingLine(xi, yi, x1, y1, x, y);
13931                    }
13932                    else {
13933                        // 绗竴涓懡浠よ捣鐐硅繕鏈畾涔�
13934                        x0 = x1;
13935                        y0 = y1;
13936                    }
13937                    // zr 浣跨敤scale鏉ユā鎷熸き鍦�, 杩欓噷涔熷x鍋氫竴瀹氱殑缂╂斁
13938                    var _x = (x - cx) * ry / rx + cx;
13939                    if (isStroke) {
13940                        if (containStroke$4(
13941                                cx, cy, ry, theta, theta + dTheta, anticlockwise,
13942                                lineWidth, _x, y
13943                            )) {
13944                            return true;
13945                        }
13946                    }
13947                    else {
13948                        w += windingArc(
13949                            cx, cy, ry, theta, theta + dTheta, anticlockwise,
13950                            _x, y
13951                        );
13952                    }
13953                    xi = Math.cos(theta + dTheta) * rx + cx;
13954                    yi = Math.sin(theta + dTheta) * ry + cy;
13955                    break;
13956                case CMD$1.R:
13957                    x0 = xi = data[i++];
13958                    y0 = yi = data[i++];
13959                    var width = data[i++];
13960                    var height = data[i++];
13961                    var x1 = x0 + width;
13962                    var y1 = y0 + height;
13963                    if (isStroke) {
13964                        if (containStroke$1(x0, y0, x1, y0, lineWidth, x, y)
13965                            || containStroke$1(x1, y0, x1, y1, lineWidth, x, y)
13966                            || containStroke$1(x1, y1, x0, y1, lineWidth, x, y)
13967                            || containStroke$1(x0, y1, x0, y0, lineWidth, x, y)
13968                        ) {
13969                            return true;
13970                        }
13971                    }
13972                    else {
13973                        // FIXME Clockwise ?
13974                        w += windingLine(x1, y0, x1, y1, x, y);
13975                        w += windingLine(x0, y1, x0, y0, x, y);
13976                    }
13977                    break;
13978                case CMD$1.Z:
13979                    if (isStroke) {
13980                        if (containStroke$1(
13981                                xi, yi, x0, y0, lineWidth, x, y
13982                            )) {
13983                            return true;
13984                        }
13985                    }
13986                    else {
13987                        // Close a subpath
13988                        w += windingLine(xi, yi, x0, y0, x, y);
13989                        // 濡傛灉琚换浣曚竴涓� subpath 鍖呭惈
13990                        // FIXME subpaths may overlap
13991                        // if (w !== 0) {
13992                        //     return true;
13993                        // }
13994                    }
13995                    xi = x0;
13996                    yi = y0;
13997                    break;
13998            }
13999        }
14000        if (!isStroke && !isAroundEqual(yi, y0)) {
14001            w += windingLine(xi, yi, x0, y0, x, y) || 0;
14002        }
14003        return w !== 0;
14004    }
14005
14006    function contain(pathData, x, y) {
14007        return containPath(pathData, 0, false, x, y);
14008    }
14009
14010    function containStroke(pathData, lineWidth, x, y) {
14011        return containPath(pathData, lineWidth, true, x, y);
14012    }
14013
14014    var getCanvasPattern = Pattern.prototype.getCanvasPattern;
14015
14016    var abs = Math.abs;
14017
14018    var pathProxyForDraw = new PathProxy(true);
14019    /**
14020     * @alias module:zrender/graphic/Path
14021     * @extends module:zrender/graphic/Displayable
14022     * @constructor
14023     * @param {Object} opts
14024     */
14025    function Path(opts) {
14026        Displayable.call(this, opts);
14027
14028        /**
14029         * @type {module:zrender/core/PathProxy}
14030         * @readOnly
14031         */
14032        this.path = null;
14033    }
14034
14035    Path.prototype = {
14036
14037        constructor: Path,
14038
14039        type: 'path',
14040
14041        __dirtyPath: true,
14042
14043        strokeContainThreshold: 5,
14044
14045        brush: function (ctx, prevEl) {
14046            var style = this.style;
14047            var path = this.path || pathProxyForDraw;
14048            var hasStroke = style.hasStroke();
14049            var hasFill = style.hasFill();
14050            var fill = style.fill;
14051            var stroke = style.stroke;
14052            var hasFillGradient = hasFill && !!(fill.colorStops);
14053            var hasStrokeGradient = hasStroke && !!(stroke.colorStops);
14054            var hasFillPattern = hasFill && !!(fill.image);
14055            var hasStrokePattern = hasStroke && !!(stroke.image);
14056
14057            style.bind(ctx, this, prevEl);
14058            this.setTransform(ctx);
14059
14060            if (this.__dirty) {
14061                var rect;
14062                // Update gradient because bounding rect may changed
14063                if (hasFillGradient) {
14064                    rect = rect || this.getBoundingRect();
14065                    this._fillGradient = style.getGradient(ctx, fill, rect);
14066                }
14067                if (hasStrokeGradient) {
14068                    rect = rect || this.getBoundingRect();
14069                    this._strokeGradient = style.getGradient(ctx, stroke, rect);
14070                }
14071            }
14072            // Use the gradient or pattern
14073            if (hasFillGradient) {
14074                // PENDING If may have affect the state
14075                ctx.fillStyle = this._fillGradient;
14076            }
14077            else if (hasFillPattern) {
14078                ctx.fillStyle = getCanvasPattern.call(fill, ctx);
14079            }
14080            if (hasStrokeGradient) {
14081                ctx.strokeStyle = this._strokeGradient;
14082            }
14083            else if (hasStrokePattern) {
14084                ctx.strokeStyle = getCanvasPattern.call(stroke, ctx);
14085            }
14086
14087            var lineDash = style.lineDash;
14088            var lineDashOffset = style.lineDashOffset;
14089
14090            var ctxLineDash = !!ctx.setLineDash;
14091
14092            // Update path sx, sy
14093            var scale = this.getGlobalScale();
14094            path.setScale(scale[0], scale[1]);
14095
14096            // Proxy context
14097            // Rebuild path in following 2 cases
14098            // 1. Path is dirty
14099            // 2. Path needs javascript implemented lineDash stroking.
14100            //    In this case, lineDash information will not be saved in PathProxy
14101            if (this.__dirtyPath
14102                || (lineDash && !ctxLineDash && hasStroke)
14103            ) {
14104                path.beginPath(ctx);
14105
14106                // Setting line dash before build path
14107                if (lineDash && !ctxLineDash) {
14108                    path.setLineDash(lineDash);
14109                    path.setLineDashOffset(lineDashOffset);
14110                }
14111
14112                this.buildPath(path, this.shape, false);
14113
14114                // Clear path dirty flag
14115                if (this.path) {
14116                    this.__dirtyPath = false;
14117                }
14118            }
14119            else {
14120                // Replay path building
14121                ctx.beginPath();
14122                this.path.rebuildPath(ctx);
14123            }
14124
14125            hasFill && path.fill(ctx);
14126
14127            if (lineDash && ctxLineDash) {
14128                ctx.setLineDash(lineDash);
14129                ctx.lineDashOffset = lineDashOffset;
14130            }
14131
14132            hasStroke && path.stroke(ctx);
14133
14134            if (lineDash && ctxLineDash) {
14135                // PENDING
14136                // Remove lineDash
14137                ctx.setLineDash([]);
14138            }
14139
14140            // Draw rect text
14141            if (style.text != null) {
14142                // Only restore transform when needs draw text.
14143                this.restoreTransform(ctx);
14144                this.drawRectText(ctx, this.getBoundingRect());
14145            }
14146        },
14147
14148        // When bundling path, some shape may decide if use moveTo to begin a new subpath or closePath
14149        // Like in circle
14150        buildPath: function (ctx, shapeCfg, inBundle) {},
14151
14152        createPathProxy: function () {
14153            this.path = new PathProxy();
14154        },
14155
14156        getBoundingRect: function () {
14157            var rect = this._rect;
14158            var style = this.style;
14159            var needsUpdateRect = !rect;
14160            if (needsUpdateRect) {
14161                var path = this.path;
14162                if (!path) {
14163                    // Create path on demand.
14164                    path = this.path = new PathProxy();
14165                }
14166                if (this.__dirtyPath) {
14167                    path.beginPath();
14168                    this.buildPath(path, this.shape, false);
14169                }
14170                rect = path.getBoundingRect();
14171            }
14172            this._rect = rect;
14173
14174            if (style.hasStroke()) {
14175                // Needs update rect with stroke lineWidth when
14176                // 1. Element changes scale or lineWidth
14177                // 2. Shape is changed
14178                var rectWithStroke = this._rectWithStroke || (this._rectWithStroke = rect.clone());
14179                if (this.__dirty || needsUpdateRect) {
14180                    rectWithStroke.copy(rect);
14181                    // FIXME Must after updateTransform
14182                    var w = style.lineWidth;
14183                    // PENDING, Min line width is needed when line is horizontal or vertical
14184                    var lineScale = style.strokeNoScale ? this.getLineScale() : 1;
14185
14186                    // Only add extra hover lineWidth when there are no fill
14187                    if (!style.hasFill()) {
14188                        w = Math.max(w, this.strokeContainThreshold || 4);
14189                    }
14190                    // Consider line width
14191                    // Line scale can't be 0;
14192                    if (lineScale > 1e-10) {
14193                        rectWithStroke.width += w / lineScale;
14194                        rectWithStroke.height += w / lineScale;
14195                        rectWithStroke.x -= w / lineScale / 2;
14196                        rectWithStroke.y -= w / lineScale / 2;
14197                    }
14198                }
14199
14200                // Return rect with stroke
14201                return rectWithStroke;
14202            }
14203
14204            return rect;
14205        },
14206
14207        contain: function (x, y) {
14208            var localPos = this.transformCoordToLocal(x, y);
14209            var rect = this.getBoundingRect();
14210            var style = this.style;
14211            x = localPos[0];
14212            y = localPos[1];
14213
14214            if (rect.contain(x, y)) {
14215                var pathData = this.path.data;
14216                if (style.hasStroke()) {
14217                    var lineWidth = style.lineWidth;
14218                    var lineScale = style.strokeNoScale ? this.getLineScale() : 1;
14219                    // Line scale can't be 0;
14220                    if (lineScale > 1e-10) {
14221                        // Only add extra hover lineWidth when there are no fill
14222                        if (!style.hasFill()) {
14223                            lineWidth = Math.max(lineWidth, this.strokeContainThreshold);
14224                        }
14225                        if (containStroke(
14226                                pathData, lineWidth / lineScale, x, y
14227                            )) {
14228                            return true;
14229                        }
14230                    }
14231                }
14232                if (style.hasFill()) {
14233                    return contain(pathData, x, y);
14234                }
14235            }
14236            return false;
14237        },
14238
14239        /**
14240         * @param  {boolean} dirtyPath
14241         */
14242        dirty: function (dirtyPath) {
14243            if (dirtyPath == null) {
14244                dirtyPath = true;
14245            }
14246            // Only mark dirty, not mark clean
14247            if (dirtyPath) {
14248                this.__dirtyPath = dirtyPath;
14249                this._rect = null;
14250            }
14251
14252            this.__dirty = true;
14253
14254            this.__zr && this.__zr.refresh();
14255
14256            // Used as a clipping path
14257            if (this.__clipTarget) {
14258                this.__clipTarget.dirty();
14259            }
14260        },
14261
14262        /**
14263         * Alias for animate('shape')
14264         * @param {boolean} loop
14265         */
14266        animateShape: function (loop) {
14267            return this.animate('shape', loop);
14268        },
14269
14270        // Overwrite attrKV
14271        attrKV: function (key, value) {
14272            // FIXME
14273            if (key === 'shape') {
14274                this.setShape(value);
14275                this.__dirtyPath = true;
14276                this._rect = null;
14277            }
14278            else {
14279                Displayable.prototype.attrKV.call(this, key, value);
14280            }
14281        },
14282
14283        /**
14284         * @param {Object|string} key
14285         * @param {*} value
14286         */
14287        setShape: function (key, value) {
14288            var shape = this.shape;
14289            // Path from string may not have shape
14290            if (shape) {
14291                if (isObject$1(key)) {
14292                    for (var name in key) {
14293                        if (key.hasOwnProperty(name)) {
14294                            shape[name] = key[name];
14295                        }
14296                    }
14297                }
14298                else {
14299                    shape[key] = value;
14300                }
14301                this.dirty(true);
14302            }
14303            return this;
14304        },
14305
14306        getLineScale: function () {
14307            var m = this.transform;
14308            // Get the line scale.
14309            // Determinant of `m` means how much the area is enlarged by the
14310            // transformation. So its square root can be used as a scale factor
14311            // for width.
14312            return m && abs(m[0] - 1) > 1e-10 && abs(m[3] - 1) > 1e-10
14313                ? Math.sqrt(abs(m[0] * m[3] - m[2] * m[1]))
14314                : 1;
14315        }
14316    };
14317
14318    /**
14319     * 鎵╁睍涓€涓� Path element, 姣斿鏄熷舰锛屽渾绛夈€�
14320     * Extend a path element
14321     * @param {Object} props
14322     * @param {string} props.type Path type
14323     * @param {Function} props.init Initialize
14324     * @param {Function} props.buildPath Overwrite buildPath method
14325     * @param {Object} [props.style] Extended default style config
14326     * @param {Object} [props.shape] Extended default shape config
14327     */
14328    Path.extend = function (defaults$$1) {
14329        var Sub = function (opts) {
14330            Path.call(this, opts);
14331
14332            if (defaults$$1.style) {
14333                // Extend default style
14334                this.style.extendFrom(defaults$$1.style, false);
14335            }
14336
14337            // Extend default shape
14338            var defaultShape = defaults$$1.shape;
14339            if (defaultShape) {
14340                this.shape = this.shape || {};
14341                var thisShape = this.shape;
14342                for (var name in defaultShape) {
14343                    if (
14344                        ! thisShape.hasOwnProperty(name)
14345                        && defaultShape.hasOwnProperty(name)
14346                    ) {
14347                        thisShape[name] = defaultShape[name];
14348                    }
14349                }
14350            }
14351
14352            defaults$$1.init && defaults$$1.init.call(this, opts);
14353        };
14354
14355        inherits(Sub, Path);
14356
14357        // FIXME 涓嶈兘 extend position, rotation 绛夊紩鐢ㄥ璞�
14358        for (var name in defaults$$1) {
14359            // Extending prototype values and methods
14360            if (name !== 'style' && name !== 'shape') {
14361                Sub.prototype[name] = defaults$$1[name];
14362            }
14363        }
14364
14365        return Sub;
14366    };
14367
14368    inherits(Path, Displayable);
14369
14370    var CMD$2 = PathProxy.CMD;
14371
14372    var points = [[], [], []];
14373    var mathSqrt$3 = Math.sqrt;
14374    var mathAtan2 = Math.atan2;
14375
14376    var transformPath = function (path, m) {
14377        var data = path.data;
14378        var cmd;
14379        var nPoint;
14380        var i;
14381        var j;
14382        var k;
14383        var p;
14384
14385        var M = CMD$2.M;
14386        var C = CMD$2.C;
14387        var L = CMD$2.L;
14388        var R = CMD$2.R;
14389        var A = CMD$2.A;
14390        var Q = CMD$2.Q;
14391
14392        for (i = 0, j = 0; i < data.length;) {
14393            cmd = data[i++];
14394            j = i;
14395            nPoint = 0;
14396
14397            switch (cmd) {
14398                case M:
14399                    nPoint = 1;
14400                    break;
14401                case L:
14402                    nPoint = 1;
14403                    break;
14404                case C:
14405                    nPoint = 3;
14406                    break;
14407                case Q:
14408                    nPoint = 2;
14409                    break;
14410                case A:
14411                    var x = m[4];
14412                    var y = m[5];
14413                    var sx = mathSqrt$3(m[0] * m[0] + m[1] * m[1]);
14414                    var sy = mathSqrt$3(m[2] * m[2] + m[3] * m[3]);
14415                    var angle = mathAtan2(-m[1] / sy, m[0] / sx);
14416                    // cx
14417                    data[i] *= sx;
14418                    data[i++] += x;
14419                    // cy
14420                    data[i] *= sy;
14421                    data[i++] += y;
14422                    // Scale rx and ry
14423                    // FIXME Assume psi is 0 here
14424                    data[i++] *= sx;
14425                    data[i++] *= sy;
14426
14427                    // Start angle
14428                    data[i++] += angle;
14429                    // end angle
14430                    data[i++] += angle;
14431                    // FIXME psi
14432                    i += 2;
14433                    j = i;
14434                    break;
14435                case R:
14436                    // x0, y0
14437                    p[0] = data[i++];
14438                    p[1] = data[i++];
14439                    applyTransform(p, p, m);
14440                    data[j++] = p[0];
14441                    data[j++] = p[1];
14442                    // x1, y1
14443                    p[0] += data[i++];
14444                    p[1] += data[i++];
14445                    applyTransform(p, p, m);
14446                    data[j++] = p[0];
14447                    data[j++] = p[1];
14448            }
14449
14450            for (k = 0; k < nPoint; k++) {
14451                var p = points[k];
14452                p[0] = data[i++];
14453                p[1] = data[i++];
14454
14455                applyTransform(p, p, m);
14456                // Write back
14457                data[j++] = p[0];
14458                data[j++] = p[1];
14459            }
14460        }
14461    };
14462
14463// command chars
14464    var cc = [
14465        'm', 'M', 'l', 'L', 'v', 'V', 'h', 'H', 'z', 'Z',
14466        'c', 'C', 'q', 'Q', 't', 'T', 's', 'S', 'a', 'A'
14467    ];
14468
14469    var mathSqrt = Math.sqrt;
14470    var mathSin = Math.sin;
14471    var mathCos = Math.cos;
vendor: 1,418 bytes, lines 14472-14515
14472    var PI = Math.PI;
14473
14474    var vMag = function(v) {
14475        return Math.sqrt(v[0] * v[0] + v[1] * v[1]);
14476    };
14477    var vRatio = function(u, v) {
14478        return (u[0] * v[0] + u[1] * v[1]) / (vMag(u) * vMag(v));
14479    };
14480    var vAngle = function(u, v) {
14481        return (u[0] * v[1] < u[1] * v[0] ? -1 : 1)
14482            * Math.acos(vRatio(u, v));
14483    };
14484
14485    function processArc(x1, y1, x2, y2, fa, fs, rx, ry, psiDeg, cmd, path) {
14486        var psi = psiDeg * (PI / 180.0);
14487        var xp = mathCos(psi) * (x1 - x2) / 2.0
14488            + mathSin(psi) * (y1 - y2) / 2.0;
14489        var yp = -1 * mathSin(psi) * (x1 - x2) / 2.0
14490            + mathCos(psi) * (y1 - y2) / 2.0;
14491
14492        var lambda = (xp * xp) / (rx * rx) + (yp * yp) / (ry * ry);
14493
14494        if (lambda > 1) {
14495            rx *= mathSqrt(lambda);
14496            ry *= mathSqrt(lambda);
14497        }
14498
14499        var f = (fa === fs ? -1 : 1)
14500            * mathSqrt((((rx * rx) * (ry * ry))
14501                - ((rx * rx) * (yp * yp))
14502                - ((ry * ry) * (xp * xp))) / ((rx * rx) * (yp * yp)
14503                + (ry * ry) * (xp * xp))
14504            ) || 0;
14505
14506        var cxp = f * rx * yp / ry;
14507        var cyp = f * -ry * xp / rx;
14508
14509        var cx = (x1 + x2) / 2.0
14510            + mathCos(psi) * cxp
14511            - mathSin(psi) * cyp;
14512        var cy = (y1 + y2) / 2.0
14513            + mathSin(psi) * cxp
14514            + mathCos(psi) * cyp;
14515
14516        var theta = vAngle([ 1, 0 ], [ (xp - cxp) / rx, (yp - cyp) / ry ]);
14517        var u = [ (xp - cxp) / rx, (yp - cyp) / ry ];
14518        var v = [ (-1 * xp - cxp) / rx, (-1 * yp - cyp) / ry ];
14519        var dTheta = vAngle(u, v);
14520
14521        if (vRatio(u, v) <= -1) {
14522            dTheta = PI;
14523        }
14524        if (vRatio(u, v) >= 1) {
14525            dTheta = 0;
14526        }
14527        if (fs === 0 && dTheta > 0) {
14528            dTheta = dTheta - 2 * PI;
14529        }
14530        if (fs === 1 && dTheta < 0) {
14531            dTheta = dTheta + 2 * PI;
14532        }
14533
14534        path.addData(cmd, cx, cy, rx, ry, theta, dTheta, psi, fs);
14535    }
14536
14537    function createPathProxyFromString(data) {
14538        if (!data) {
14539            return [];
14540        }
14541
14542        // command string
14543        var cs = data.replace(/-/g, ' -')
14544            .replace(/  /g, ' ')
14545            .replace(/ /g, ',')
14546            .replace(/,,/g, ',');
14547
14548        var n;
14549        // create pipes so that we can split the data
14550        for (n = 0; n < cc.length; n++) {
14551            cs = cs.replace(new RegExp(cc[n], 'g'), '|' + cc[n]);
14552        }
14553
14554        // create array
14555        var arr = cs.split('|');
14556        // init context point
14557        var cpx = 0;
14558        var cpy = 0;
14559
14560        var path = new PathProxy();
14561        var CMD = PathProxy.CMD;
14562
14563        var prevCmd;
14564        for (n = 1; n < arr.length; n++) {
14565            var str = arr[n];
14566            var c = str.charAt(0);
14567            var off = 0;
14568            var p = str.slice(1).replace(/e,-/g, 'e-').split(',');
14569            var cmd;
14570
14571            if (p.length > 0 && p[0] === '') {
14572                p.shift();
14573            }
14574
14575            for (var i = 0; i < p.length; i++) {
14576                p[i] = parseFloat(p[i]);
14577            }
14578            while (off < p.length && !isNaN(p[off])) {
14579                if (isNaN(p[0])) {
14580                    break;
14581                }
14582                var ctlPtx;
14583                var ctlPty;
14584
14585                var rx;
14586                var ry;
14587                var psi;
14588                var fa;
14589                var fs;
14590
14591                var x1 = cpx;
14592                var y1 = cpy;
14593
14594                // convert l, H, h, V, and v to L
14595                switch (c) {
14596                    case 'l':
14597                        cpx += p[off++];
14598                        cpy += p[off++];
14599                        cmd = CMD.L;
14600                        path.addData(cmd, cpx, cpy);
14601                        break;
14602                    case 'L':
14603                        cpx = p[off++];
14604                        cpy = p[off++];
14605                        cmd = CMD.L;
14606                        path.addData(cmd, cpx, cpy);
14607                        break;
14608                    case 'm':
14609                        cpx += p[off++];
14610                        cpy += p[off++];
14611                        cmd = CMD.M;
14612                        path.addData(cmd, cpx, cpy);
14613                        c = 'l';
14614                        break;
14615                    case 'M':
14616                        cpx = p[off++];
14617                        cpy = p[off++];
14618                        cmd = CMD.M;
14619                        path.addData(cmd, cpx, cpy);
14620                        c = 'L';
14621                        break;
14622                    case 'h':
14623                        cpx += p[off++];
14624                        cmd = CMD.L;
14625                        path.addData(cmd, cpx, cpy);
14626                        break;
14627                    case 'H':
14628                        cpx = p[off++];
14629                        cmd = CMD.L;
14630                        path.addData(cmd, cpx, cpy);
14631                        break;
14632                    case 'v':
14633                        cpy += p[off++];
14634                        cmd = CMD.L;
14635                        path.addData(cmd, cpx, cpy);
14636                        break;
14637                    case 'V':
14638                        cpy = p[off++];
14639                        cmd = CMD.L;
14640                        path.addData(cmd, cpx, cpy);
14641                        break;
14642                    case 'C':
14643                        cmd = CMD.C;
14644                        path.addData(
14645                            cmd, p[off++], p[off++], p[off++], p[off++], p[off++], p[off++]
14646                        );
14647                        cpx = p[off - 2];
14648                        cpy = p[off - 1];
14649                        break;
14650                    case 'c':
14651                        cmd = CMD.C;
14652                        path.addData(
14653                            cmd,
14654                            p[off++] + cpx, p[off++] + cpy,
14655                            p[off++] + cpx, p[off++] + cpy,
14656                            p[off++] + cpx, p[off++] + cpy
14657                        );
14658                        cpx += p[off - 2];
14659                        cpy += p[off - 1];
14660                        break;
14661                    case 'S':
14662                        ctlPtx = cpx;
14663                        ctlPty = cpy;
14664                        var len = path.len();
14665                        var pathData = path.data;
14666                        if (prevCmd === CMD.C) {
14667                            ctlPtx += cpx - pathData[len - 4];
14668                            ctlPty += cpy - pathData[len - 3];
14669                        }
14670                        cmd = CMD.C;
14671                        x1 = p[off++];
14672                        y1 = p[off++];
14673                        cpx = p[off++];
14674                        cpy = p[off++];
14675                        path.addData(cmd, ctlPtx, ctlPty, x1, y1, cpx, cpy);
14676                        break;
14677                    case 's':
14678                        ctlPtx = cpx;
14679                        ctlPty = cpy;
14680                        var len = path.len();
14681                        var pathData = path.data;
14682                        if (prevCmd === CMD.C) {
14683                            ctlPtx += cpx - pathData[len - 4];
14684                            ctlPty += cpy - pathData[len - 3];
14685                        }
14686                        cmd = CMD.C;
14687                        x1 = cpx + p[off++];
14688                        y1 = cpy + p[off++];
14689                        cpx += p[off++];
14690                        cpy += p[off++];
14691                        path.addData(cmd, ctlPtx, ctlPty, x1, y1, cpx, cpy);
14692                        break;
14693                    case 'Q':
14694                        x1 = p[off++];
14695                        y1 = p[off++];
14696                        cpx = p[off++];
14697                        cpy = p[off++];
14698                        cmd = CMD.Q;
14699                        path.addData(cmd, x1, y1, cpx, cpy);
14700                        break;
14701                    case 'q':
14702                        x1 = p[off++] + cpx;
14703                        y1 = p[off++] + cpy;
14704                        cpx += p[off++];
14705                        cpy += p[off++];
14706                        cmd = CMD.Q;
14707                        path.addData(cmd, x1, y1, cpx, cpy);
14708                        break;
14709                    case 'T':
14710                        ctlPtx = cpx;
14711                        ctlPty = cpy;
14712                        var len = path.len();
14713                        var pathData = path.data;
14714                        if (prevCmd === CMD.Q) {
14715                            ctlPtx += cpx - pathData[len - 4];
14716                            ctlPty += cpy - pathData[len - 3];
14717                        }
14718                        cpx = p[off++];
14719                        cpy = p[off++];
14720                        cmd = CMD.Q;
14721                        path.addData(cmd, ctlPtx, ctlPty, cpx, cpy);
14722                        break;
14723                    case 't':
14724                        ctlPtx = cpx;
14725                        ctlPty = cpy;
14726                        var len = path.len();
14727                        var pathData = path.data;
14728                        if (prevCmd === CMD.Q) {
14729                            ctlPtx += cpx - pathData[len - 4];
14730                            ctlPty += cpy - pathData[len - 3];
14731                        }
14732                        cpx += p[off++];
14733                        cpy += p[off++];
14734                        cmd = CMD.Q;
14735                        path.addData(cmd, ctlPtx, ctlPty, cpx, cpy);
14736                        break;
14737                    case 'A':
14738                        rx = p[off++];
14739                        ry = p[off++];
14740                        psi = p[off++];
14741                        fa = p[off++];
14742                        fs = p[off++];
14743
14744                        x1 = cpx, y1 = cpy;
14745                        cpx = p[off++];
14746                        cpy = p[off++];
14747                        cmd = CMD.A;
14748                        processArc(
14749                            x1, y1, cpx, cpy, fa, fs, rx, ry, psi, cmd, path
14750                        );
14751                        break;
14752                    case 'a':
14753                        rx = p[off++];
14754                        ry = p[off++];
14755                        psi = p[off++];
14756                        fa = p[off++];
14757                        fs = p[off++];
14758
14759                        x1 = cpx, y1 = cpy;
14760                        cpx += p[off++];
14761                        cpy += p[off++];
14762                        cmd = CMD.A;
14763                        processArc(
14764                            x1, y1, cpx, cpy, fa, fs, rx, ry, psi, cmd, path
14765                        );
14766                        break;
14767                }
14768            }
14769
14770            if (c === 'z' || c === 'Z') {
14771                cmd = CMD.Z;
14772                path.addData(cmd);
14773            }
14774
14775            prevCmd = cmd;
14776        }
14777
14778        path.toStatic();
14779
14780        return path;
14781    }
14782
14783// TODO Optimize double memory cost problem
14784    function createPathOptions(str, opts) {
14785        var pathProxy = createPathProxyFromString(str);
14786        opts = opts || {};
14787        opts.buildPath = function (path) {
14788            if (path.setData) {
14789                path.setData(pathProxy.data);
14790                // Svg and vml renderer don't have context
14791                var ctx = path.getContext();
14792                if (ctx) {
14793                    path.rebuildPath(ctx);
14794                }
14795            }
14796            else {
14797                var ctx = path;
14798                pathProxy.rebuildPath(ctx);
14799            }
14800        };
14801
14802        opts.applyTransform = function (m) {
14803            transformPath(pathProxy, m);
14804            this.dirty(true);
14805        };
14806
14807        return opts;
14808    }
14809
14810    /**
14811     * Create a Path object from path string data
14812     * http://www.w3.org/TR/SVG/paths.html#PathData
14813     * @param  {Object} opts Other options
14814     */
14815    function createFromString(str, opts) {
14816        return new Path(createPathOptions(str, opts));
14817    }
14818
14819    /**
14820     * Create a Path class from path string data
14821     * @param  {string} str
14822     * @param  {Object} opts Other options
14823     */
14824    function extendFromString(str, opts) {
14825        return Path.extend(createPathOptions(str, opts));
14826    }
14827
14828    /**
14829     * Merge multiple paths
14830     */
14831// TODO Apply transform
14832// TODO stroke dash
14833// TODO Optimize double memory cost problem
14834    function mergePath$1(pathEls, opts) {
14835        var pathList = [];
14836        var len = pathEls.length;
14837        for (var i = 0; i < len; i++) {
14838            var pathEl = pathEls[i];
14839            if (!pathEl.path) {
14840                pathEl.createPathProxy();
14841            }
14842            if (pathEl.__dirtyPath) {
14843                pathEl.buildPath(pathEl.path, pathEl.shape, true);
14844            }
14845            pathList.push(pathEl.path);
14846        }
14847
14848        var pathBundle = new Path(opts);
14849        // Need path proxy.
14850        pathBundle.createPathProxy();
14851        pathBundle.buildPath = function (path) {
14852            path.appendPath(pathList);
14853            // Svg and vml renderer don't have context
14854            var ctx = path.getContext();
14855            if (ctx) {
14856                path.rebuildPath(ctx);
14857            }
14858        };
14859
14860        return pathBundle;
14861    }
14862
14863    /**
14864     * @alias zrender/graphic/Text
14865     * @extends module:zrender/graphic/Displayable
14866     * @constructor
14867     * @param {Object} opts
14868     */
14869    var Text = function (opts) { // jshint ignore:line
14870        Displayable.call(this, opts);
14871    };
14872
14873    Text.prototype = {
14874
14875        constructor: Text,
14876
14877        type: 'text',
14878
14879        brush: function (ctx, prevEl) {
14880            var style = this.style;
14881
14882            // Optimize, avoid normalize every time.
14883            this.__dirty && normalizeTextStyle(style, true);
14884
14885            // Use props with prefix 'text'.
14886            style.fill = style.stroke = style.shadowBlur = style.shadowColor =
14887                style.shadowOffsetX = style.shadowOffsetY = null;
14888
14889            var text = style.text;
14890            // Convert to string
14891            text != null && (text += '');
14892
14893            // Always bind style
14894            style.bind(ctx, this, prevEl);
14895
14896            if (!needDrawText(text, style)) {
14897                return;
14898            }
14899
14900            this.setTransform(ctx);
14901
14902            renderText(this, ctx, text, style);
14903
14904            this.restoreTransform(ctx);
14905        },
14906
14907        getBoundingRect: function () {
14908            var style = this.style;
14909
14910            // Optimize, avoid normalize every time.
14911            this.__dirty && normalizeTextStyle(style, true);
14912
14913            if (!this._rect) {
14914                var text = style.text;
14915                text != null ? (text += '') : (text = '');
14916
14917                var rect = getBoundingRect(
14918                    style.text + '',
14919                    style.font,
14920                    style.textAlign,
14921                    style.textVerticalAlign,
14922                    style.textPadding,
14923                    style.rich
14924                );
14925
14926                rect.x += style.x || 0;
14927                rect.y += style.y || 0;
14928
14929                if (getStroke(style.textStroke, style.textStrokeWidth)) {
14930                    var w = style.textStrokeWidth;
14931                    rect.x -= w / 2;
14932                    rect.y -= w / 2;
14933                    rect.width += w;
14934                    rect.height += w;
14935                }
14936
14937                this._rect = rect;
14938            }
14939
14940            return this._rect;
14941        }
14942    };
14943
14944    inherits(Text, Displayable);
14945
14946    /**
14947     * 鍦嗗舰
14948     * @module zrender/shape/Circle
14949     */
14950
14951    var Circle = Path.extend({
14952
14953        type: 'circle',
14954
14955        shape: {
14956            cx: 0,
14957            cy: 0,
14958            r: 0
14959        },
14960
14961
14962        buildPath : function (ctx, shape, inBundle) {
14963            // Better stroking in ShapeBundle
14964            // Always do it may have performence issue ( fill may be 2x more cost)
14965            if (inBundle) {
14966                ctx.moveTo(shape.cx + shape.r, shape.cy);
14967            }
14968            // else {
14969            //     if (ctx.allocate && !ctx.data.length) {
14970            //         ctx.allocate(ctx.CMD_MEM_SIZE.A);
14971            //     }
14972            // }
14973            // Better stroking in ShapeBundle
14974            // ctx.moveTo(shape.cx + shape.r, shape.cy);
14975            ctx.arc(shape.cx, shape.cy, shape.r, 0, Math.PI * 2, true);
14976        }
14977    });
14978
14979// Fix weird bug in some version of IE11 (like 11.0.9600.178**),
14980// where exception "unexpected call to method or property access"
14981// might be thrown when calling ctx.fill or ctx.stroke after a path
14982// whose area size is zero is drawn and ctx.clip() is called and
14983// shadowBlur is set. See #4572, #3112, #5777.
14984// (e.g.,
14985//  ctx.moveTo(10, 10);
14986//  ctx.lineTo(20, 10);
14987//  ctx.closePath();
14988//  ctx.clip();
14989//  ctx.shadowBlur = 10;
14990//  ...
14991//  ctx.fill();
14992// )
14993
14994    var shadowTemp = [
14995        ['shadowBlur', 0],
14996        ['shadowColor', '#000'],
14997        ['shadowOffsetX', 0],
14998        ['shadowOffsetY', 0]
14999    ];
15000
15001    var fixClipWithShadow = function (orignalBrush) {
15002
15003        // version string can be: '11.0'
15004        return (env$1.browser.ie && env$1.browser.version >= 11)
15005
15006            ? function () {
15007                var clipPaths = this.__clipPaths;
15008                var style = this.style;
15009                var modified;
15010
15011                if (clipPaths) {
15012                    for (var i = 0; i < clipPaths.length; i++) {
15013                        var clipPath = clipPaths[i];
15014                        var shape = clipPath && clipPath.shape;
15015                        var type = clipPath && clipPath.type;
15016
15017                        if (shape && (
15018                                (type === 'sector' && shape.startAngle === shape.endAngle)
15019                                || (type === 'rect' && (!shape.width || !shape.height))
15020                            )) {
15021                            for (var j = 0; j < shadowTemp.length; j++) {
15022                                // It is save to put shadowTemp static, because shadowTemp
15023                                // will be all modified each item brush called.
15024                                shadowTemp[j][2] = style[shadowTemp[j][0]];
15025                                style[shadowTemp[j][0]] = shadowTemp[j][1];
15026                            }
15027                            modified = true;
15028                            break;
15029                        }
15030                    }
15031                }
15032
15033                orignalBrush.apply(this, arguments);
15034
15035                if (modified) {
15036                    for (var j = 0; j < shadowTemp.length; j++) {
15037                        style[shadowTemp[j][0]] = shadowTemp[j][2];
15038                    }
15039                }
15040            }
15041
15042            : orignalBrush;
15043    };
15044
15045    /**
15046     * 鎵囧舰
15047     * @module zrender/graphic/shape/Sector
15048     */
15049
15050    var Sector = Path.extend({
15051
15052        type: 'sector',
15053
15054        shape: {
15055
15056            cx: 0,
15057
15058            cy: 0,
15059
15060            r0: 0,
15061
15062            r: 0,
15063
15064            startAngle: 0,
15065
15066            endAngle: Math.PI * 2,
15067
15068            clockwise: true
15069        },
15070
15071        brush: fixClipWithShadow(Path.prototype.brush),
15072
15073        buildPath: function (ctx, shape) {
15074
15075            var x = shape.cx;
15076            var y = shape.cy;
15077            var r0 = Math.max(shape.r0 || 0, 0);
15078            var r = Math.max(shape.r, 0);
15079            var startAngle = shape.startAngle;
15080            var endAngle = shape.endAngle;
15081            var clockwise = shape.clockwise;
15082
15083            var unitX = Math.cos(startAngle);
15084            var unitY = Math.sin(startAngle);
15085
15086            ctx.moveTo(unitX * r0 + x, unitY * r0 + y);
15087
15088            ctx.lineTo(unitX * r + x, unitY * r + y);
15089
15090            ctx.arc(x, y, r, startAngle, endAngle, !clockwise);
15091
15092            ctx.lineTo(
15093                Math.cos(endAngle) * r0 + x,
15094                Math.sin(endAngle) * r0 + y
15095            );
15096
15097            if (r0 !== 0) {
15098                ctx.arc(x, y, r0, endAngle, startAngle, clockwise);
15099            }
15100
15101            ctx.closePath();
15102        }
15103    });
15104
15105    /**
15106     * 鍦嗙幆
15107     * @module zrender/graphic/shape/Ring
15108     */
15109
15110    var Ring = Path.extend({
15111
15112        type: 'ring',
15113
15114        shape: {
15115            cx: 0,
15116            cy: 0,
15117            r: 0,
15118            r0: 0
15119        },
15120
15121        buildPath: function (ctx, shape) {
15122            var x = shape.cx;
15123            var y = shape.cy;
15124            var PI2 = Math.PI * 2;
15125            ctx.moveTo(x + shape.r, y);
15126            ctx.arc(x, y, shape.r, 0, PI2, false);
15127            ctx.moveTo(x + shape.r0, y);
15128            ctx.arc(x, y, shape.r0, 0, PI2, true);
15129        }
15130    });
15131
15132    /**
15133     * Catmull-Rom spline 鎻掑€兼姌绾�
15134     * @module zrender/shape/util/smoothSpline
15135     * @author pissang (https://www.github.com/pissang)
15136     *         Kener (@Kener-鏋楀嘲, [email protected])
15137     *         errorrik ([email protected])
15138     */
15139
15140    /**
15141     * @inner
15142     */
15143    function interpolate(p0, p1, p2, p3, t, t2, t3) {
15144        var v0 = (p2 - p0) * 0.5;
15145        var v1 = (p3 - p1) * 0.5;
15146        return (2 * (p1 - p2) + v0 + v1) * t3
15147            + (-3 * (p1 - p2) - 2 * v0 - v1) * t2
15148            + v0 * t + p1;
15149    }
15150
15151    /**
15152     * @alias module:zrender/shape/util/smoothSpline
15153     * @param {Array} points 绾挎椤剁偣鏁扮粍
15154     * @param {boolean} isLoop
15155     * @return {Array}
15156     */
15157    var smoothSpline = function (points, isLoop) {
15158        var len$$1 = points.length;
15159        var ret = [];
15160
15161        var distance$$1 = 0;
15162        for (var i = 1; i < len$$1; i++) {
15163            distance$$1 += distance(points[i - 1], points[i]);
15164        }
15165
15166        var segs = distance$$1 / 2;
15167        segs = segs < len$$1 ? len$$1 : segs;
15168        for (var i = 0; i < segs; i++) {
15169            var pos = i / (segs - 1) * (isLoop ? len$$1 : len$$1 - 1);
15170            var idx = Math.floor(pos);
15171
15172            var w = pos - idx;
15173
15174            var p0;
15175            var p1 = points[idx % len$$1];
15176            var p2;
15177            var p3;
15178            if (!isLoop) {
15179                p0 = points[idx === 0 ? idx : idx - 1];
15180                p2 = points[idx > len$$1 - 2 ? len$$1 - 1 : idx + 1];
vendor: 5,398 bytes, lines 15181-15348
15181                p3 = points[idx > len$$1 - 3 ? len$$1 - 1 : idx + 2];
15182            }
15183            else {
15184                p0 = points[(idx - 1 + len$$1) % len$$1];
15185                p2 = points[(idx + 1) % len$$1];
15186                p3 = points[(idx + 2) % len$$1];
15187            }
15188
15189            var w2 = w * w;
15190            var w3 = w * w2;
15191
15192            ret.push([
15193                interpolate(p0[0], p1[0], p2[0], p3[0], w, w2, w3),
15194                interpolate(p0[1], p1[1], p2[1], p3[1], w, w2, w3)
15195            ]);
15196        }
15197        return ret;
15198    };
15199
15200    /**
15201     * 璐濆灏斿钩婊戞洸绾�
15202     * @module zrender/shape/util/smoothBezier
15203     * @author pissang (https://www.github.com/pissang)
15204     *         Kener (@Kener-鏋楀嘲, [email protected])
15205     *         errorrik ([email protected])
15206     */
15207
15208    /**
15209     * 璐濆灏斿钩婊戞洸绾�
15210     * @alias module:zrender/shape/util/smoothBezier
15211     * @param {Array} points 绾挎椤剁偣鏁扮粍
15212     * @param {number} smooth 骞虫粦绛夌骇, 0-1
15213     * @param {boolean} isLoop
15214     * @param {Array} constraint 灏嗚绠楀嚭鏉ョ殑鎺у埗鐐圭害鏉熷湪涓€涓寘鍥寸洅鍐�
15215     *                           姣斿 [[0, 0], [100, 100]], 杩欎釜鍖呭洿鐩掍細涓�
15216     *                           鏁翠釜鎶樼嚎鐨勫寘鍥寸洅鍋氫竴涓苟闆嗙敤鏉ョ害鏉熸帶鍒剁偣銆�
15217     * @param {Array} 璁$畻鍑烘潵鐨勬帶鍒剁偣鏁扮粍
15218     */
15219    var smoothBezier = function (points, smooth, isLoop, constraint) {
15220        var cps = [];
15221
15222        var v = [];
15223        var v1 = [];
15224        var v2 = [];
15225        var prevPoint;
15226        var nextPoint;
15227
15228        var min$$1, max$$1;
15229        if (constraint) {
15230            min$$1 = [Infinity, Infinity];
15231            max$$1 = [-Infinity, -Infinity];
15232            for (var i = 0, len$$1 = points.length; i < len$$1; i++) {
15233                min(min$$1, min$$1, points[i]);
15234                max(max$$1, max$$1, points[i]);
15235            }
15236            // 涓庢寚瀹氱殑鍖呭洿鐩掑仛骞堕泦
15237            min(min$$1, min$$1, constraint[0]);
15238            max(max$$1, max$$1, constraint[1]);
15239        }
15240
15241        for (var i = 0, len$$1 = points.length; i < len$$1; i++) {
15242            var point = points[i];
15243
15244            if (isLoop) {
15245                prevPoint = points[i ? i - 1 : len$$1 - 1];
15246                nextPoint = points[(i + 1) % len$$1];
15247            }
15248            else {
15249                if (i === 0 || i === len$$1 - 1) {
15250                    cps.push(clone$1(points[i]));
15251                    continue;
15252                }
15253                else {
15254                    prevPoint = points[i - 1];
15255                    nextPoint = points[i + 1];
15256                }
15257            }
15258
15259            sub(v, nextPoint, prevPoint);
15260
15261            // use degree to scale the handle length
15262            scale(v, v, smooth);
15263
15264            var d0 = distance(point, prevPoint);
15265            var d1 = distance(point, nextPoint);
15266            var sum = d0 + d1;
15267            if (sum !== 0) {
15268                d0 /= sum;
15269                d1 /= sum;
15270            }
15271
15272            scale(v1, v, -d0);
15273            scale(v2, v, d1);
15274            var cp0 = add([], point, v1);
15275            var cp1 = add([], point, v2);
15276            if (constraint) {
15277                max(cp0, cp0, min$$1);
15278                min(cp0, cp0, max$$1);
15279                max(cp1, cp1, min$$1);
15280                min(cp1, cp1, max$$1);
15281            }
15282            cps.push(cp0);
15283            cps.push(cp1);
15284        }
15285
15286        if (isLoop) {
15287            cps.push(cps.shift());
15288        }
15289
15290        return cps;
15291    };
15292
15293    function buildPath$1(ctx, shape, closePath) {
15294        var points = shape.points;
15295        var smooth = shape.smooth;
15296        if (points && points.length >= 2) {
15297            if (smooth && smooth !== 'spline') {
15298                var controlPoints = smoothBezier(
15299                    points, smooth, closePath, shape.smoothConstraint
15300                );
15301
15302                ctx.moveTo(points[0][0], points[0][1]);
15303                var len = points.length;
15304                for (var i = 0; i < (closePath ? len : len - 1); i++) {
15305                    var cp1 = controlPoints[i * 2];
15306                    var cp2 = controlPoints[i * 2 + 1];
15307                    var p = points[(i + 1) % len];
15308                    ctx.bezierCurveTo(
15309                        cp1[0], cp1[1], cp2[0], cp2[1], p[0], p[1]
15310                    );
15311                }
15312            }
15313            else {
15314                if (smooth === 'spline') {
15315                    points = smoothSpline(points, closePath);
15316                }
15317
15318                ctx.moveTo(points[0][0], points[0][1]);
15319                for (var i = 1, l = points.length; i < l; i++) {
15320                    ctx.lineTo(points[i][0], points[i][1]);
15321                }
15322            }
15323
15324            closePath && ctx.closePath();
15325        }
15326    }
15327
15328    /**
15329     * 澶氳竟褰�
15330     * @module zrender/shape/Polygon
15331     */
15332
15333    var Polygon = Path.extend({
15334
15335        type: 'polygon',
15336
15337        shape: {
15338            points: null,
15339
15340            smooth: false,
15341
15342            smoothConstraint: null
15343        },
15344
15345        buildPath: function (ctx, shape) {
15346            buildPath$1(ctx, shape, true);
15347        }
15348    });
15349
15350    /**
15351     * @module zrender/graphic/shape/Polyline
15352     */
15353
15354    var Polyline = Path.extend({
15355
15356        type: 'polyline',
15357
15358        shape: {
15359            points: null,
15360
15361            smooth: false,
15362
15363            smoothConstraint: null
15364        },
15365
15366        style: {
15367            stroke: '#000',
15368
15369            fill: null
15370        },
15371
15372        buildPath: function (ctx, shape) {
15373            buildPath$1(ctx, shape, false);
15374        }
15375    });
15376
15377    /**
15378     * 鐭╁舰
15379     * @module zrender/graphic/shape/Rect
15380     */
15381
15382    var Rect = Path.extend({
15383
15384        type: 'rect',
15385
15386        shape: {
15387            // 宸︿笂銆佸彸涓娿€佸彸涓嬨€佸乏涓嬭鐨勫崐寰勪緷娆′负r1銆乺2銆乺3銆乺4
15388            // r缂╁啓涓�1         鐩稿綋浜� [1, 1, 1, 1]
15389            // r缂╁啓涓篬1]       鐩稿綋浜� [1, 1, 1, 1]
15390            // r缂╁啓涓篬1, 2]    鐩稿綋浜� [1, 2, 1, 2]
15391            // r缂╁啓涓篬1, 2, 3] 鐩稿綋浜� [1, 2, 3, 2]
15392            r: 0,
15393
15394            x: 0,
15395            y: 0,
15396            width: 0,
15397            height: 0
15398        },
15399
15400        buildPath: function (ctx, shape) {
15401            var x = shape.x;
15402            var y = shape.y;
15403            var width = shape.width;
15404            var height = shape.height;
15405            if (!shape.r) {
15406                ctx.rect(x, y, width, height);
15407            }
15408            else {
15409                buildPath(ctx, shape);
15410            }
15411            ctx.closePath();
15412            return;
15413        }
15414    });
15415
15416    /**
15417     * 鐩寸嚎
15418     * @module zrender/graphic/shape/Line
15419     */
15420
15421    var Line = Path.extend({
15422
15423        type: 'line',
15424
15425        shape: {
15426            // Start point
15427            x1: 0,
15428            y1: 0,
15429            // End point
15430            x2: 0,
15431            y2: 0,
15432
15433            percent: 1
15434        },
15435
15436        style: {
15437            stroke: '#000',
15438            fill: null
15439        },
15440
15441        buildPath: function (ctx, shape) {
15442            var x1 = shape.x1;
15443            var y1 = shape.y1;
15444            var x2 = shape.x2;
15445            var y2 = shape.y2;
15446            var percent = shape.percent;
15447
15448            if (percent === 0) {
15449                return;
15450            }
15451
15452            ctx.moveTo(x1, y1);
15453
15454            if (percent < 1) {
15455                x2 = x1 * (1 - percent) + x2 * percent;
15456                y2 = y1 * (1 - percent) + y2 * percent;
15457            }
15458            ctx.lineTo(x2, y2);
15459        },
15460
15461        /**
15462         * Get point at percent
15463         * @param  {number} percent
15464         * @return {Array.<number>}
15465         */
15466        pointAt: function (p) {
15467            var shape = this.shape;
15468            return [
15469                shape.x1 * (1 - p) + shape.x2 * p,
15470                shape.y1 * (1 - p) + shape.y2 * p
15471            ];
15472        }
15473    });
15474
15475    /**
15476     * 璐濆灏旀洸绾�
15477     * @module zrender/shape/BezierCurve
15478     */
15479
15480    var out = [];
15481
15482    function someVectorAt(shape, t, isTangent) {
15483        var cpx2 = shape.cpx2;
15484        var cpy2 = shape.cpy2;
15485        if (cpx2 === null || cpy2 === null) {
15486            return [
15487                (isTangent ? cubicDerivativeAt : cubicAt)(shape.x1, shape.cpx1, shape.cpx2, shape.x2, t),
15488                (isTangent ? cubicDerivativeAt : cubicAt)(shape.y1, shape.cpy1, shape.cpy2, shape.y2, t)
15489            ];
15490        }
15491        else {
15492            return [
15493                (isTangent ? quadraticDerivativeAt : quadraticAt)(shape.x1, shape.cpx1, shape.x2, t),
15494                (isTangent ? quadraticDerivativeAt : quadraticAt)(shape.y1, shape.cpy1, shape.y2, t)
15495            ];
15496        }
15497    }
15498
15499    var BezierCurve = Path.extend({
15500
15501        type: 'bezier-curve',
15502
15503        shape: {
15504            x1: 0,
15505            y1: 0,
15506            x2: 0,
15507            y2: 0,
15508            cpx1: 0,
15509            cpy1: 0,
15510            // cpx2: 0,
15511            // cpy2: 0
15512
15513            // Curve show percent, for animating
15514            percent: 1
15515        },
15516
15517        style: {
15518            stroke: '#000',
15519            fill: null
15520        },
15521
15522        buildPath: function (ctx, shape) {
15523            var x1 = shape.x1;
15524            var y1 = shape.y1;
15525            var x2 = shape.x2;
15526            var y2 = shape.y2;
15527            var cpx1 = shape.cpx1;
15528            var cpy1 = shape.cpy1;
15529            var cpx2 = shape.cpx2;
15530            var cpy2 = shape.cpy2;
15531            var percent = shape.percent;
15532            if (percent === 0) {
15533                return;
15534            }
15535
15536            ctx.moveTo(x1, y1);
15537
15538            if (cpx2 == null || cpy2 == null) {
15539                if (percent < 1) {
15540                    quadraticSubdivide(
15541                        x1, cpx1, x2, percent, out
15542                    );
15543                    cpx1 = out[1];
15544                    x2 = out[2];
15545                    quadraticSubdivide(
15546                        y1, cpy1, y2, percent, out
15547                    );
15548                    cpy1 = out[1];
15549                    y2 = out[2];
15550                }
15551
15552                ctx.quadraticCurveTo(
15553                    cpx1, cpy1,
15554                    x2, y2
15555                );
15556            }
15557            else {
15558                if (percent < 1) {
15559                    cubicSubdivide(
15560                        x1, cpx1, cpx2, x2, percent, out
15561                    );
15562                    cpx1 = out[1];
15563                    cpx2 = out[2];
15564                    x2 = out[3];
15565                    cubicSubdivide(
15566                        y1, cpy1, cpy2, y2, percent, out
15567                    );
15568                    cpy1 = out[1];
15569                    cpy2 = out[2];
15570                    y2 = out[3];
15571                }
15572                ctx.bezierCurveTo(
15573                    cpx1, cpy1,
15574                    cpx2, cpy2,
15575                    x2, y2
15576                );
15577            }
15578        },
15579
15580        /**
15581         * Get point at percent
15582         * @param  {number} t
15583         * @return {Array.<number>}
15584         */
15585        pointAt: function (t) {
15586            return someVectorAt(this.shape, t, false);
15587        },
15588
15589        /**
15590         * Get tangent at percent
15591         * @param  {number} t
15592         * @return {Array.<number>}
15593         */
15594        tangentAt: function (t) {
15595            var p = someVectorAt(this.shape, t, true);
15596            return normalize(p, p);
15597        }
15598    });
15599
15600    /**
15601     * 鍦嗗姬
15602     * @module zrender/graphic/shape/Arc
15603     */
15604
15605    var Arc = Path.extend({
15606
15607        type: 'arc',
15608
15609        shape: {
15610
15611            cx: 0,
15612
15613            cy: 0,
15614
15615            r: 0,
15616
15617            startAngle: 0,
15618
15619            endAngle: Math.PI * 2,
15620
15621            clockwise: true
15622        },
15623
15624        style: {
15625
15626            stroke: '#000',
15627
15628            fill: null
15629        },
15630
15631        buildPath: function (ctx, shape) {
15632
15633            var x = shape.cx;
15634            var y = shape.cy;
15635            var r = Math.max(shape.r, 0);
15636            var startAngle = shape.startAngle;
15637            var endAngle = shape.endAngle;
15638            var clockwise = shape.clockwise;
15639
15640            var unitX = Math.cos(startAngle);
15641            var unitY = Math.sin(startAngle);
15642
15643            ctx.moveTo(unitX * r + x, unitY * r + y);
15644            ctx.arc(x, y, r, startAngle, endAngle, !clockwise);
15645        }
15646    });
15647
15648// CompoundPath to improve performance
15649
15650    var CompoundPath = Path.extend({
15651
15652        type: 'compound',
15653
15654        shape: {
15655
15656            paths: null
15657        },
15658
15659        _updatePathDirty: function () {
15660            var dirtyPath = this.__dirtyPath;
15661            var paths = this.shape.paths;
15662            for (var i = 0; i < paths.length; i++) {
15663                // Mark as dirty if any subpath is dirty
15664                dirtyPath = dirtyPath || paths[i].__dirtyPath;
15665            }
15666            this.__dirtyPath = dirtyPath;
15667            this.__dirty = this.__dirty || dirtyPath;
15668        },
15669
15670        beforeBrush: function () {
15671            this._updatePathDirty();
15672            var paths = this.shape.paths || [];
15673            var scale = this.getGlobalScale();
15674            // Update path scale
15675            for (var i = 0; i < paths.length; i++) {
15676                if (!paths[i].path) {
15677                    paths[i].createPathProxy();
15678                }
15679                paths[i].path.setScale(scale[0], scale[1]);
15680            }
15681        },
15682
15683        buildPath: function (ctx, shape) {
15684            var paths = shape.paths || [];
15685            for (var i = 0; i < paths.length; i++) {
15686                paths[i].buildPath(ctx, paths[i].shape, true);
15687            }
15688        },
15689
15690        afterBrush: function () {
15691            var paths = this.shape.paths || [];
15692            for (var i = 0; i < paths.length; i++) {
15693                paths[i].__dirtyPath = false;
15694            }
15695        },
15696
15697        getBoundingRect: function () {
15698            this._updatePathDirty();
15699            return Path.prototype.getBoundingRect.call(this);
15700        }
15701    });
15702
15703    /**
15704     * @param {Array.<Object>} colorStops
15705     */
15706    var Gradient = function (colorStops) {
15707
15708        this.colorStops = colorStops || [];
15709
15710    };
15711
15712    Gradient.prototype = {
15713
15714        constructor: Gradient,
15715
15716        addColorStop: function (offset, color) {
15717            this.colorStops.push({
15718
15719                offset: offset,
15720
15721                color: color
15722            });
15723        }
15724
15725    };
15726
15727    /**
15728     * x, y, x2, y2 are all percent from 0 to 1
15729     * @param {number} [x=0]
15730     * @param {number} [y=0]
15731     * @param {number} [x2=1]
15732     * @param {number} [y2=0]
15733     * @param {Array.<Object>} colorStops
15734     * @param {boolean} [globalCoord=false]
15735     */
15736    var LinearGradient = function (x, y, x2, y2, colorStops, globalCoord) {
15737        // Should do nothing more in this constructor. Because gradient can be
15738        // declard by `color: {type: 'linear', colorStops: ...}`, where
15739        // this constructor will not be called.
15740
15741        this.x = x == null ? 0 : x;
15742
15743        this.y = y == null ? 0 : y;
15744
15745        this.x2 = x2 == null ? 1 : x2;
15746
15747        this.y2 = y2 == null ? 0 : y2;
15748
15749        // Can be cloned
15750        this.type = 'linear';
15751
15752        // If use global coord
15753        this.global = globalCoord || false;
15754
15755        Gradient.call(this, colorStops);
15756    };
15757
15758    LinearGradient.prototype = {
15759
15760        constructor: LinearGradient
15761    };
15762
15763    inherits(LinearGradient, Gradient);
15764
15765    /**
15766     * x, y, r are all percent from 0 to 1
15767     * @param {number} [x=0.5]
15768     * @param {number} [y=0.5]
15769     * @param {number} [r=0.5]
15770     * @param {Array.<Object>} [colorStops]
15771     * @param {boolean} [globalCoord=false]
15772     */
15773    var RadialGradient = function (x, y, r, colorStops, globalCoord) {
15774        // Should do nothing more in this constructor. Because gradient can be
15775        // declard by `color: {type: 'radial', colorStops: ...}`, where
15776        // this constructor will not be called.
15777
15778        this.x = x == null ? 0.5 : x;
15779
15780        this.y = y == null ? 0.5 : y;
15781
15782        this.r = r == null ? 0.5 : r;
15783
15784        // Can be cloned
15785        this.type = 'radial';
15786
15787        // If use global coord
15788        this.global = globalCoord || false;
15789
15790        Gradient.call(this, colorStops);
15791    };
15792
15793    RadialGradient.prototype = {
15794
15795        constructor: RadialGradient
15796    };
15797
15798    inherits(RadialGradient, Gradient);
15799
15800    /**
15801     * Displayable for incremental rendering. It will be rendered in a separate layer
15802     * IncrementalDisplay have too main methods. `clearDisplayables` and `addDisplayables`
15803     * addDisplayables will render the added displayables incremetally.
15804     *
15805     * It use a not clearFlag to tell the painter don't clear the layer if it's the first element.
15806     */
15807// TODO Style override ?
15808    function IncrementalDisplayble(opts) {
15809
15810        Displayable.call(this, opts);
15811
15812        this._displayables = [];
15813
15814        this._temporaryDisplayables = [];
15815
15816        this._cursor = 0;
15817
15818        this.notClear = true;
15819    }
15820
15821    IncrementalDisplayble.prototype.incremental = true;
15822
15823    IncrementalDisplayble.prototype.clearDisplaybles = function () {
15824        this._displayables = [];
15825        this._temporaryDisplayables = [];
15826        this._cursor = 0;
15827        this.dirty();
15828
15829        this.notClear = false;
15830    };
15831
15832    IncrementalDisplayble.prototype.addDisplayable = function (displayable, notPersistent) {
15833        if (notPersistent) {
15834            this._temporaryDisplayables.push(displayable);
15835        }
15836        else {
15837            this._displayables.push(displayable);
15838        }
15839        this.dirty();
15840    };
15841
15842    IncrementalDisplayble.prototype.addDisplayables = function (displayables, notPersistent) {
15843        notPersistent = notPersistent || false;
15844        for (var i = 0; i < displayables.length; i++) {
15845            this.addDisplayable(displayables[i], notPersistent);
15846        }
15847    };
15848
15849    IncrementalDisplayble.prototype.eachPendingDisplayable = function  (cb) {
15850        for (var i = this._cursor; i < this._displayables.length; i++) {
15851            cb && cb(this._displayables[i]);
15852        }
15853        for (var i = 0; i < this._temporaryDisplayables.length; i++) {
15854            cb && cb(this._temporaryDisplayables[i]);
15855        }
15856    };
15857
15858    IncrementalDisplayble.prototype.update = function () {
15859        this.updateTransform();
15860        for (var i = this._cursor; i < this._displayables.length; i++) {
15861            var displayable = this._displayables[i];
15862            // PENDING
15863            displayable.parent = this;
15864            displayable.update();
15865            displayable.parent = null;
15866        }
15867        for (var i = 0; i < this._temporaryDisplayables.length; i++) {
15868            var displayable = this._temporaryDisplayables[i];
15869            // PENDING
15870            displayable.parent = this;
15871            displayable.update();
15872            displayable.parent = null;
15873        }
15874    };
15875
15876    IncrementalDisplayble.prototype.brush = function (ctx, prevEl) {
15877        // Render persistant displayables.
15878        for (var i = this._cursor; i < this._displayables.length; i++) {
15879            var displayable = this._displayables[i];
15880            displayable.beforeBrush && displayable.beforeBrush(ctx);
15881            displayable.brush(ctx, i === this._cursor ? null : this._displayables[i - 1]);
15882            displayable.afterBrush && displayable.afterBrush(ctx);
15883        }
15884        this._cursor = i;
15885        // Render temporary displayables.
15886        for (var i = 0; i < this._temporaryDisplayables.length; i++) {
15887            var displayable = this._temporaryDisplayables[i];
15888            displayable.beforeBrush && displayable.beforeBrush(ctx);
15889            displayable.brush(ctx, i === 0 ? null : this._temporaryDisplayables[i - 1]);
15890            displayable.afterBrush && displayable.afterBrush(ctx);
15891        }
15892
15893        this._temporaryDisplayables = [];
15894
15895        this.notClear = true;
15896    };
15897
15898    var m = [];
15899    IncrementalDisplayble.prototype.getBoundingRect = function () {
15900        if (!this._rect) {
15901            var rect = new BoundingRect(Infinity, Infinity, -Infinity, -Infinity);
15902            for (var i = 0; i < this._displayables.length; i++) {
15903                var displayable = this._displayables[i];
15904                var childRect = displayable.getBoundingRect().clone();
15905                if (displayable.needLocalTransform()) {
15906                    childRect.applyTransform(displayable.getLocalTransform(m));
15907                }
15908                rect.union(childRect);
15909            }
15910            this._rect = rect;
15911        }
15912        return this._rect;
15913    };
15914
15915    IncrementalDisplayble.prototype.contain = function (x, y) {
15916        var localPos = this.transformCoordToLocal(x, y);
15917        var rect = this.getBoundingRect();
15918
15919        if (rect.contain(localPos[0], localPos[1])) {
15920            for (var i = 0; i < this._displayables.length; i++) {
15921                var displayable = this._displayables[i];
15922                if (displayable.contain(x, y)) {
15923                    return true;
15924                }
15925            }
15926        }
15927        return false;
15928    };
15929
15930    inherits(IncrementalDisplayble, Displayable);
15931
15932    /*
15933* Licensed to the Apache Software Foundation (ASF) under one
15934* or more contributor license agreements.  See the NOTICE file
15935* distributed with this work for additional information
15936* regarding copyright ownership.  The ASF licenses this file
15937* to you under the Apache License, Version 2.0 (the
15938* "License"); you may not use this file except in compliance
15939* with the License.  You may obtain a copy of the License at
15940*
15941*   http://www.apache.org/licenses/LICENSE-2.0
15942*
15943* Unless required by applicable law or agreed to in writing,
15944* software distributed under the License is distributed on an
15945* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15946* KIND, either express or implied.  See the License for the
15947* specific language governing permissions and limitations
15948* under the License.
15949*/
15950
15951    var round = Math.round;
15952    var mathMax$1 = Math.max;
15953    var mathMin$1 = Math.min;
15954
15955    var EMPTY_OBJ = {};
15956
15957    /**
15958     * Extend shape with parameters
15959     */
15960    function extendShape(opts) {
15961        return Path.extend(opts);
15962    }
15963
15964    /**
15965     * Extend path
15966     */
15967    function extendPath(pathData, opts) {
15968        return extendFromString(pathData, opts);
15969    }
15970
15971    /**
15972     * Create a path element from path data string
15973     * @param {string} pathData
15974     * @param {Object} opts
15975     * @param {module:zrender/core/BoundingRect} rect
15976     * @param {string} [layout=cover] 'center' or 'cover'
15977     */
15978    function makePath(pathData, opts, rect, layout) {
15979        var path = createFromString(pathData, opts);
15980        var boundingRect = path.getBoundingRect();
15981        if (rect) {
15982            if (layout === 'center') {
15983                rect = centerGraphic(rect, boundingRect);
15984            }
15985
15986            resizePath(path, rect);
15987        }
15988        return path;
15989    }
15990
15991    /**
15992     * Create a image element from image url
15993     * @param {string} imageUrl image url
15994     * @param {Object} opts options
15995     * @param {module:zrender/core/BoundingRect} rect constrain rect
15996     * @param {string} [layout=cover] 'center' or 'cover'
15997     */
15998    function makeImage(imageUrl, rect, layout) {
15999        var path = new ZImage({
16000            style: {
16001                image: imageUrl,
16002                x: rect.x,
16003                y: rect.y,
16004                width: rect.width,
16005                height: rect.height
16006            },
16007            onload: function (img) {
16008                if (layout === 'center') {
16009                    var boundingRect = {
16010                        width: img.width,
16011                        height: img.height
16012                    };
16013                    path.setStyle(centerGraphic(rect, boundingRect));
16014                }
16015            }
16016        });
16017        return path;
16018    }
16019
16020    /**
16021     * Get position of centered element in bounding box.
16022     *
16023     * @param  {Object} rect         element local bounding box
16024     * @param  {Object} boundingRect constraint bounding box
16025     * @return {Object} element position containing x, y, width, and height
16026     */
16027    function centerGraphic(rect, boundingRect) {
16028        // Set rect to center, keep width / height ratio.
16029        var aspect = boundingRect.width / boundingRect.height;
16030        var width = rect.height * aspect;
16031        var height;
16032        if (width <= rect.width) {
16033            height = rect.height;
16034        }
16035        else {
16036            width = rect.width;
16037            height = width / aspect;
16038        }
16039        var cx = rect.x + rect.width / 2;
16040        var cy = rect.y + rect.height / 2;
16041
16042        return {
16043            x: cx - width / 2,
16044            y: cy - height / 2,
16045            width: width,
16046            height: height
16047        };
16048    }
16049
16050    var mergePath = mergePath$1;
16051
16052    /**
16053     * Resize a path to fit the rect
16054     * @param {module:zrender/graphic/Path} path
16055     * @param {Object} rect
16056     */
16057    function resizePath(path, rect) {
16058        if (!path.applyTransform) {
16059            return;
16060        }
16061
16062        var pathRect = path.getBoundingRect();
16063
16064        var m = pathRect.calculateTransform(rect);
16065
16066        path.applyTransform(m);
16067    }
16068
16069    /**
16070     * Sub pixel optimize line for canvas
16071     *
16072     * @param {Object} param
16073     * @param {Object} [param.shape]
16074     * @param {number} [param.shape.x1]
16075     * @param {number} [param.shape.y1]
16076     * @param {number} [param.shape.x2]
16077     * @param {number} [param.shape.y2]
16078     * @param {Object} [param.style]
16079     * @param {number} [param.style.lineWidth]
16080     * @return {Object} Modified param
16081     */
16082    function subPixelOptimizeLine(param) {
16083        var shape = param.shape;
16084        var lineWidth = param.style.lineWidth;
16085
16086        if (round(shape.x1 * 2) === round(shape.x2 * 2)) {
16087            shape.x1 = shape.x2 = subPixelOptimize(shape.x1, lineWidth, true);
16088        }
16089        if (round(shape.y1 * 2) === round(shape.y2 * 2)) {
16090            shape.y1 = shape.y2 = subPixelOptimize(shape.y1, lineWidth, true);
16091        }
16092        return param;
16093    }
16094
16095    /**
16096     * Sub pixel optimize rect for canvas
16097     *
16098     * @param {Object} param
16099     * @param {Object} [param.shape]
16100     * @param {number} [param.shape.x]
16101     * @param {number} [param.shape.y]
16102     * @param {number} [param.shape.width]
16103     * @param {number} [param.shape.height]
16104     * @param {Object} [param.style]
16105     * @param {number} [param.style.lineWidth]
16106     * @return {Object} Modified param
16107     */
16108    function subPixelOptimizeRect(param) {
16109        var shape = param.shape;
16110        var lineWidth = param.style.lineWidth;
16111        var originX = shape.x;
16112        var originY = shape.y;
16113        var originWidth = shape.width;
16114        var originHeight = shape.height;
16115        shape.x = subPixelOptimize(shape.x, lineWidth, true);
16116        shape.y = subPixelOptimize(shape.y, lineWidth, true);
16117        shape.width = Math.max(
16118            subPixelOptimize(originX + originWidth, lineWidth, false) - shape.x,
16119            originWidth === 0 ? 0 : 1
16120        );
16121        shape.height = Math.max(
16122            subPixelOptimize(originY + originHeight, lineWidth, false) - shape.y,
16123            originHeight === 0 ? 0 : 1
16124        );
16125        return param;
16126    }
16127
16128    /**
16129     * Sub pixel optimize for canvas
16130     *
16131     * @param {number} position Coordinate, such as x, y
16132     * @param {number} lineWidth Should be nonnegative integer.
16133     * @param {boolean=} positiveOrNegative Default false (negative).
16134     * @return {number} Optimized position.
16135     */
16136    function subPixelOptimize(position, lineWidth, positiveOrNegative) {
16137        // Assure that (position + lineWidth / 2) is near integer edge,
16138        // otherwise line will be fuzzy in canvas.
16139        var doubledPosition = round(position * 2);
16140        return (doubledPosition + round(lineWidth)) % 2 === 0
16141            ? doubledPosition / 2
16142            : (doubledPosition + (positiveOrNegative ? 1 : -1)) / 2;
16143    }
16144
16145    function hasFillOrStroke(fillOrStroke) {
16146        return fillOrStroke != null && fillOrStroke != 'none';
16147    }
16148
16149    function liftColor(color) {
16150        return typeof color === 'string' ? lift(color, -0.1) : color;
16151    }
16152
16153    /**
16154     * @private
16155     */
16156    function cacheElementStl(el) {
16157        if (el.__hoverStlDirty) {
16158            var stroke = el.style.stroke;
16159            var fill = el.style.fill;
16160
16161            // Create hoverStyle on mouseover
16162            var hoverStyle = el.__hoverStl;
16163            hoverStyle.fill = hoverStyle.fill
16164                || (hasFillOrStroke(fill) ? liftColor(fill) : null);
16165            hoverStyle.stroke = hoverStyle.stroke
16166                || (hasFillOrStroke(stroke) ? liftColor(stroke) : null);
16167
16168            var normalStyle = {};
16169            for (var name in hoverStyle) {
16170                // See comment in `doSingleEnterHover`.
16171                if (hoverStyle[name] != null) {
16172                    normalStyle[name] = el.style[name];
16173                }
16174            }
16175
16176            el.__normalStl = normalStyle;
16177
16178            el.__hoverStlDirty = false;
16179        }
16180    }
16181
16182    /**
16183     * @private
16184     */
16185    function doSingleEnterHover(el) {
16186        if (el.__isHover) {
16187            return;
16188        }
16189
16190        cacheElementStl(el);
16191
16192        if (el.useHoverLayer) {
16193            el.__zr && el.__zr.addHover(el, el.__hoverStl);
16194        }
16195        else {
16196            var style = el.style;
16197            var insideRollbackOpt = style.insideRollbackOpt;
16198
16199            // Consider case: only `position: 'top'` is set on emphasis, then text
16200            // color should be returned to `autoColor`, rather than remain '#fff'.
16201            // So we should rollback then apply again after style merging.
16202            insideRollbackOpt && rollbackInsideStyle(style);
16203
16204            // styles can be:
16205            // {
16206            //    label: {
16207            //        show: false,
16208            //        position: 'outside',
16209            //        fontSize: 18
16210            //    },
16211            //    emphasis: {
16212            //        label: {
16213            //            show: true
16214            //        }
16215            //    }
16216            // },
16217            // where properties of `emphasis` may not appear in `normal`. We previously use
16218            // module:echarts/util/model#defaultEmphasis to merge `normal` to `emphasis`.
16219            // But consider rich text and setOption in merge mode, it is impossible to cover
16220            // all properties in merge. So we use merge mode when setting style here, where
16221            // only properties that is not `null/undefined` can be set. The disadventage:
16222            // null/undefined can not be used to remove style any more in `emphasis`.
16223            style.extendFrom(el.__hoverStl);
16224
16225            // Do not save `insideRollback`.
16226            if (insideRollbackOpt) {
16227                applyInsideStyle(style, style.insideOriginalTextPosition, insideRollbackOpt);
16228
16229                // textFill may be rollbacked to null.
16230                if (style.textFill == null) {
16231                    style.textFill = insideRollbackOpt.autoColor;
16232                }
16233            }
16234
16235            el.dirty(false);
16236            el.z2 += 1;
16237        }
16238
16239        el.__isHover = true;
16240    }
16241
16242    /**
16243     * @inner
16244     */
16245    function doSingleLeaveHover(el) {
16246        if (!el.__isHover) {
16247            return;
16248        }
16249
16250        var normalStl = el.__normalStl;
16251        if (el.useHoverLayer) {
16252            el.__zr && el.__zr.removeHover(el);
16253        }
16254        else {
16255            // Consider null/undefined value, should use
16256            // `setStyle` but not `extendFrom(stl, true)`.
16257            normalStl && el.setStyle(normalStl);
16258            el.z2 -= 1;
16259        }
16260
16261        el.__isHover = false;
16262    }
16263
16264    /**
16265     * @inner
16266     */
16267    function doEnterHover(el) {
16268        el.type === 'group'
16269            ? el.traverse(function (child) {
16270                if (child.type !== 'group') {
16271                    doSingleEnterHover(child);
16272                }
16273            })
16274            : doSingleEnterHover(el);
16275    }
16276
16277    function doLeaveHover(el) {
16278        el.type === 'group'
16279            ? el.traverse(function (child) {
16280                if (child.type !== 'group') {
16281                    doSingleLeaveHover(child);
16282                }
16283            })
16284            : doSingleLeaveHover(el);
16285    }
16286
16287    /**
16288     * @inner
16289     */
16290    function setElementHoverStl(el, hoverStl) {
16291        // If element has sepcified hoverStyle, then use it instead of given hoverStyle
16292        // Often used when item group has a label element and it's hoverStyle is different
16293        el.__hoverStl = el.hoverStyle || hoverStl || {};
16294        el.__hoverStlDirty = true;
16295
16296        if (el.__isHover) {
16297            cacheElementStl(el);
16298        }
16299    }
16300
16301    /**
16302     * @inner
16303     */
16304    function onElementMouseOver(e) {
16305        if (this.__hoverSilentOnTouch && e.zrByTouch) {
16306            return;
16307        }
16308
16309        // Only if element is not in emphasis status
16310        !this.__isEmphasis && doEnterHover(this);
16311    }
16312
16313    /**
16314     * @inner
16315     */
16316    function onElementMouseOut(e) {
16317        if (this.__hoverSilentOnTouch && e.zrByTouch) {
16318            return;
16319        }
16320
16321        // Only if element is not in emphasis status
16322        !this.__isEmphasis && doLeaveHover(this);
16323    }
16324
16325    /**
16326     * @inner
16327     */
16328    function enterEmphasis() {
16329        this.__isEmphasis = true;
16330        doEnterHover(this);
16331    }
16332
16333    /**
16334     * @inner
16335     */
16336    function leaveEmphasis() {
16337        this.__isEmphasis = false;
16338        doLeaveHover(this);
16339    }
16340
16341    /**
16342     * Set hover style of element.
16343     * This method can be called repeatly without side-effects.
16344     * @param {module:zrender/Element} el
16345     * @param {Object} [hoverStyle]
16346     * @param {Object} [opt]
16347     * @param {boolean} [opt.hoverSilentOnTouch=false]
16348     *        In touch device, mouseover event will be trigger on touchstart event
16349     *        (see module:zrender/dom/HandlerProxy). By this mechanism, we can
16350     *        conviniently use hoverStyle when tap on touch screen without additional
16351     *        code for compatibility.
16352     *        But if the chart/component has select feature, which usually also use
16353     *        hoverStyle, there might be conflict between 'select-highlight' and
16354     *        'hover-highlight' especially when roam is enabled (see geo for example).
16355     *        In this case, hoverSilentOnTouch should be used to disable hover-highlight
16356     *        on touch device.
16357     */
16358    function setHoverStyle(el, hoverStyle, opt) {
16359        el.__hoverSilentOnTouch = opt && opt.hoverSilentOnTouch;
16360
16361        el.type === 'group'
16362            ? el.traverse(function (child) {
16363                if (child.type !== 'group') {
16364                    setElementHoverStl(child, hoverStyle);
16365                }
16366            })
16367            : setElementHoverStl(el, hoverStyle);
16368
16369        // Duplicated function will be auto-ignored, see Eventful.js.
16370        el.on('mouseover', onElementMouseOver)
16371            .on('mouseout', onElementMouseOut);
16372
16373        // Emphasis, normal can be triggered manually
16374        el.on('emphasis', enterEmphasis)
16375            .on('normal', leaveEmphasis);
16376    }
16377
16378    /**
16379     * @param {Object|module:zrender/graphic/Style} normalStyle
16380     * @param {Object} emphasisStyle
16381     * @param {module:echarts/model/Model} normalModel
16382     * @param {module:echarts/model/Model} emphasisModel
16383     * @param {Object} opt Check `opt` of `setTextStyleCommon` to find other props.
16384     * @param {string|Function} [opt.defaultText]
16385     * @param {module:echarts/model/Model} [opt.labelFetcher] Fetch text by
16386     *      `opt.labelFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex)`
16387     * @param {module:echarts/model/Model} [opt.labelDataIndex] Fetch text by
16388     *      `opt.textFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex)`
16389     * @param {module:echarts/model/Model} [opt.labelDimIndex] Fetch text by
16390     *      `opt.textFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex)`
16391     * @param {Object} [normalSpecified]
16392     * @param {Object} [emphasisSpecified]
16393     */
16394    function setLabelStyle(
16395        normalStyle, emphasisStyle,
16396        normalModel, emphasisModel,
16397        opt,
16398        normalSpecified, emphasisSpecified
16399    ) {
16400        opt = opt || EMPTY_OBJ;
16401        var labelFetcher = opt.labelFetcher;
16402        var labelDataIndex = opt.labelDataIndex;
16403        var labelDimIndex = opt.labelDimIndex;
16404
16405        // This scenario, `label.normal.show = true; label.emphasis.show = false`,
16406        // is not supported util someone requests.
16407
16408        var showNormal = normalModel.getShallow('show');
16409        var showEmphasis = emphasisModel.getShallow('show');
16410
16411        // Consider performance, only fetch label when necessary.
16412        // If `normal.show` is `false` and `emphasis.show` is `true` and `emphasis.formatter` is not set,
16413        // label should be displayed, where text is fetched by `normal.formatter` or `opt.defaultText`.
16414        var baseText;
16415        if (showNormal || showEmphasis) {
16416            if (labelFetcher) {
16417                baseText = labelFetcher.getFormattedLabel(labelDataIndex, 'normal', null, labelDimIndex);
16418            }
16419            if (baseText == null) {
16420                baseText = isFunction$1(opt.defaultText) ? opt.defaultText(labelDataIndex, opt) : opt.defaultText;
16421            }
16422        }
16423        var normalStyleText = showNormal ? baseText : null;
16424        var emphasisStyleText = showEmphasis
16425            ? retrieve2(
16426                labelFetcher
16427                    ? labelFetcher.getFormattedLabel(labelDataIndex, 'emphasis', null, labelDimIndex)
16428                    : null,
16429                baseText
16430            )
16431            : null;
16432
16433        // Optimize: If style.text is null, text will not be drawn.
16434        if (normalStyleText != null || emphasisStyleText != null) {
16435            // Always set `textStyle` even if `normalStyle.text` is null, because default
16436            // values have to be set on `normalStyle`.
16437            // If we set default values on `emphasisStyle`, consider case:
16438            // Firstly, `setOption(... label: {normal: {text: null}, emphasis: {show: true}} ...);`
16439            // Secondly, `setOption(... label: {noraml: {show: true, text: 'abc', color: 'red'} ...);`
16440            // Then the 'red' will not work on emphasis.
16441            setTextStyle(normalStyle, normalModel, normalSpecified, opt);
16442            setTextStyle(emphasisStyle, emphasisModel, emphasisSpecified, opt, true);
16443        }
16444
16445        normalStyle.text = normalStyleText;
16446        emphasisStyle.text = emphasisStyleText;
16447    }
16448
16449    /**
16450     * Set basic textStyle properties.
16451     * @param {Object|module:zrender/graphic/Style} textStyle
16452     * @param {module:echarts/model/Model} model
16453     * @param {Object} [specifiedTextStyle] Can be overrided by settings in model.
16454     * @param {Object} [opt] See `opt` of `setTextStyleCommon`.
16455     * @param {boolean} [isEmphasis]
16456     */
16457    function setTextStyle(
16458        textStyle, textStyleModel, specifiedTextStyle, opt, isEmphasis
16459    ) {
16460        setTextStyleCommon(textStyle, textStyleModel, opt, isEmphasis);
16461        specifiedTextStyle && extend(textStyle, specifiedTextStyle);
16462        textStyle.host && textStyle.host.dirty && textStyle.host.dirty(false);
16463
16464        return textStyle;
16465    }
16466
16467    /**
16468     * Set text option in the style.
16469     * @deprecated
16470     * @param {Object} textStyle
16471     * @param {module:echarts/model/Model} labelModel
16472     * @param {string|boolean} defaultColor Default text color.
16473     *        If set as false, it will be processed as a emphasis style.
16474     */
16475    function setText(textStyle, labelModel, defaultColor) {
16476        var opt = {isRectText: true};
16477        var isEmphasis;
16478
16479        if (defaultColor === false) {
16480            isEmphasis = true;
16481        }
16482        else {
16483            // Support setting color as 'auto' to get visual color.
16484            opt.autoColor = defaultColor;
16485        }
16486        setTextStyleCommon(textStyle, labelModel, opt, isEmphasis);
16487        textStyle.host && textStyle.host.dirty && textStyle.host.dirty(false);
16488    }
16489
16490    /**
16491     * {
16492 *      disableBox: boolean, Whether diable drawing box of block (outer most).
16493 *      isRectText: boolean,
16494 *      autoColor: string, specify a color when color is 'auto',
16495 *              for textFill, textStroke, textBackgroundColor, and textBorderColor.
16496 *              If autoColor specified, it is used as default textFill.
16497 *      useInsideStyle:
16498 *              `true`: Use inside style (textFill, textStroke, textStrokeWidth)
16499 *                  if `textFill` is not specified.
16500 *              `false`: Do not use inside style.
16501 *              `null/undefined`: use inside style if `isRectText` is true and
16502 *                  `textFill` is not specified and textPosition contains `'inside'`.
16503 *      forceRich: boolean
16504 * }
16505     */
16506    function setTextStyleCommon(textStyle, textStyleModel, opt, isEmphasis) {
16507        // Consider there will be abnormal when merge hover style to normal style if given default value.
16508        opt = opt || EMPTY_OBJ;
16509
16510        if (opt.isRectText) {
16511            var textPosition = textStyleModel.getShallow('position')
16512                || (isEmphasis ? null : 'inside');
16513            // 'outside' is not a valid zr textPostion value, but used
16514            // in bar series, and magric type should be considered.
16515            textPosition === 'outside' && (textPosition = 'top');
16516            textStyle.textPosition = textPosition;
16517            textStyle.textOffset = textStyleModel.getShallow('offset');
16518            var labelRotate = textStyleModel.getShallow('rotate');
16519            labelRotate != null && (labelRotate *= Math.PI / 180);
16520            textStyle.textRotation = labelRotate;
16521            textStyle.textDistance = retrieve2(
16522                textStyleModel.getShallow('distance'), isEmphasis ? null : 5
16523            );
16524        }
16525
16526        var ecModel = textStyleModel.ecModel;
16527        var globalTextStyle = ecModel && ecModel.option.textStyle;
16528
16529        // Consider case:
16530        // {
16531        //     data: [{
16532        //         value: 12,
16533        //         label: {
16534        //             rich: {
16535        //                 // no 'a' here but using parent 'a'.
16536        //             }
16537        //         }
16538        //     }],
16539        //     rich: {
16540        //         a: { ... }
16541        //     }
16542        // }
16543        var richItemNames = getRichItemNames(textStyleModel);
16544        var richResult;
16545        if (richItemNames) {
16546            richResult = {};
16547            for (var name in richItemNames) {
16548                if (richItemNames.hasOwnProperty(name)) {
16549                    // Cascade is supported in rich.
16550                    var richTextStyle = textStyleModel.getModel(['rich', name]);
16551                    // In rich, never `disableBox`.
16552                    setTokenTextStyle(richResult[name] = {}, richTextStyle, globalTextStyle, opt, isEmphasis);
16553                }
16554            }
16555        }
16556        textStyle.rich = richResult;
16557
16558        setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isEmphasis, true);
16559
16560        if (opt.forceRich && !opt.textStyle) {
16561            opt.textStyle = {};
16562        }
16563
16564        return textStyle;
16565    }
16566
16567// Consider case:
16568// {
16569//     data: [{
16570//         value: 12,
16571//         label: {
16572//             rich: {
16573//                 // no 'a' here but using parent 'a'.
16574//             }
16575//         }
16576//     }],
16577//     rich: {
16578//         a: { ... }
16579//     }
16580// }
16581    function getRichItemNames(textStyleModel) {
16582        // Use object to remove duplicated names.
16583        var richItemNameMap;
16584        while (textStyleModel && textStyleModel !== textStyleModel.ecModel) {
16585            var rich = (textStyleModel.option || EMPTY_OBJ).rich;
16586            if (rich) {
16587                richItemNameMap = richItemNameMap || {};
16588                for (var name in rich) {
16589                    if (rich.hasOwnProperty(name)) {
16590                        richItemNameMap[name] = 1;
16591                    }
16592                }
16593            }
16594            textStyleModel = textStyleModel.parentModel;
16595        }
16596        return richItemNameMap;
16597    }
16598
16599    function setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isEmphasis, isBlock) {
16600        // In merge mode, default value should not be given.
16601        globalTextStyle = !isEmphasis && globalTextStyle || EMPTY_OBJ;
16602
16603        textStyle.textFill = getAutoColor(textStyleModel.getShallow('color'), opt)
16604            || globalTextStyle.color;
16605        textStyle.textStroke = getAutoColor(textStyleModel.getShallow('textBorderColor'), opt)
16606            || globalTextStyle.textBorderColor;
16607        textStyle.textStrokeWidth = retrieve2(
16608            textStyleModel.getShallow('textBorderWidth'),
16609            globalTextStyle.textBorderWidth
16610        );
16611
16612        if (!isEmphasis) {
16613            if (isBlock) {
16614                // Always set `insideRollback`, for clearing previous.
16615                var originalTextPosition = textStyle.textPosition;
16616                textStyle.insideRollback = applyInsideStyle(textStyle, originalTextPosition, opt);
16617                // Save original textPosition, because style.textPosition will be repalced by
16618                // real location (like [10, 30]) in zrender.
16619                textStyle.insideOriginalTextPosition = originalTextPosition;
16620                textStyle.insideRollbackOpt = opt;
16621            }
16622
16623            // Set default finally.
16624            if (textStyle.textFill == null) {
16625                textStyle.textFill = opt.autoColor;
16626            }
16627        }
16628
16629        // Do not use `getFont` here, because merge should be supported, where
16630        // part of these properties may be changed in emphasis style, and the
16631        // others should remain their original value got from normal style.
16632        textStyle.fontStyle = textStyleModel.getShallow('fontStyle') || globalTextStyle.fontStyle;
16633        textStyle.fontWeight = textStyleModel.getShallow('fontWeight') || globalTextStyle.fontWeight;
16634        textStyle.fontSize = textStyleModel.getShallow('fontSize') || globalTextStyle.fontSize;
16635        textStyle.fontFamily = textStyleModel.getShallow('fontFamily') || globalTextStyle.fontFamily;
16636
16637        textStyle.textAlign = textStyleModel.getShallow('align');
16638        textStyle.textVerticalAlign = textStyleModel.getShallow('verticalAlign')
16639            || textStyleModel.getShallow('baseline');
16640
16641        textStyle.textLineHeight = textStyleModel.getShallow('lineHeight');
16642        textStyle.textWidth = textStyleModel.getShallow('width');
16643        textStyle.textHeight = textStyleModel.getShallow('height');
16644        textStyle.textTag = textStyleModel.getShallow('tag');
16645
16646        if (!isBlock || !opt.disableBox) {
16647            textStyle.textBackgroundColor = getAutoColor(textStyleModel.getShallow('backgroundColor'), opt);
16648            textStyle.textPadding = textStyleModel.getShallow('padding');
16649            textStyle.textBorderColor = getAutoColor(textStyleModel.getShallow('borderColor'), opt);
16650            textStyle.textBorderWidth = textStyleModel.getShallow('borderWidth');
16651            textStyle.textBorderRadius = textStyleModel.getShallow('borderRadius');
16652
16653            textStyle.textBoxShadowColor = textStyleModel.getShallow('shadowColor');
16654            textStyle.textBoxShadowBlur = textStyleModel.getShallow('shadowBlur');
16655            textStyle.textBoxShadowOffsetX = textStyleModel.getShallow('shadowOffsetX');
16656            textStyle.textBoxShadowOffsetY = textStyleModel.getShallow('shadowOffsetY');
16657        }
16658
16659        textStyle.textShadowColor = textStyleModel.getShallow('textShadowColor')
16660            || globalTextStyle.textShadowColor;
16661        textStyle.textShadowBlur = textStyleModel.getShallow('textShadowBlur')
16662            || globalTextStyle.textShadowBlur;
16663        textStyle.textShadowOffsetX = textStyleModel.getShallow('textShadowOffsetX')
16664            || globalTextStyle.textShadowOffsetX;
16665        textStyle.textShadowOffsetY = textStyleModel.getShallow('textShadowOffsetY')
16666            || globalTextStyle.textShadowOffsetY;
16667    }
16668
16669    function getAutoColor(color, opt) {
16670        return color !== 'auto' ? color : (opt && opt.autoColor) ? opt.autoColor : null;
16671    }
16672
16673    function applyInsideStyle(textStyle, textPosition, opt) {
16674        var useInsideStyle = opt.useInsideStyle;
16675        var insideRollback;
16676
16677        if (textStyle.textFill == null
16678            && useInsideStyle !== false
16679            && (useInsideStyle === true
16680                || (opt.isRectText
16681                    && textPosition
16682                    // textPosition can be [10, 30]
16683                    && typeof textPosition === 'string'
16684                    && textPosition.indexOf('inside') >= 0
16685                )
16686            )
16687        ) {
16688            insideRollback = {
16689                textFill: null,
16690                textStroke: textStyle.textStroke,
16691                textStrokeWidth: textStyle.textStrokeWidth
16692            };
16693            textStyle.textFill = '#fff';
16694            // Consider text with #fff overflow its container.
16695            if (textStyle.textStroke == null) {
16696                textStyle.textStroke = opt.autoColor;
16697                textStyle.textStrokeWidth == null && (textStyle.textStrokeWidth = 2);
16698            }
16699        }
16700
16701        return insideRollback;
16702    }
16703
16704    function rollbackInsideStyle(style) {
16705        var insideRollback = style.insideRollback;
16706        if (insideRollback) {
16707            style.textFill = insideRollback.textFill;
16708            style.textStroke = insideRollback.textStroke;
16709            style.textStrokeWidth = insideRollback.textStrokeWidth;
16710        }
16711    }
16712
16713    function getFont(opt, ecModel) {
16714        // ecModel or default text style model.
16715        var gTextStyleModel = ecModel || ecModel.getModel('textStyle');
16716        return trim([
16717            // FIXME in node-canvas fontWeight is before fontStyle
16718            opt.fontStyle || gTextStyleModel && gTextStyleModel.getShallow('fontStyle') || '',
16719            opt.fontWeight || gTextStyleModel && gTextStyleModel.getShallow('fontWeight') || '',
16720            (opt.fontSize || gTextStyleModel && gTextStyleModel.getShallow('fontSize') || 12) + 'px',
16721            opt.fontFamily || gTextStyleModel && gTextStyleModel.getShallow('fontFamily') || 'sans-serif'
16722        ].join(' '));
16723    }
16724
16725    function animateOrSetProps(isUpdate, el, props, animatableModel, dataIndex, cb) {
16726        if (typeof dataIndex === 'function') {
16727            cb = dataIndex;
16728            dataIndex = null;
16729        }
16730        // Do not check 'animation' property directly here. Consider this case:
16731        // animation model is an `itemModel`, whose does not have `isAnimationEnabled`
16732        // but its parent model (`seriesModel`) does.
16733        var animationEnabled = animatableModel && animatableModel.isAnimationEnabled();
16734
16735        if (animationEnabled) {
16736            var postfix = isUpdate ? 'Update' : '';
16737            var duration = animatableModel.getShallow('animationDuration' + postfix);
16738            var animationEasing = animatableModel.getShallow('animationEasing' + postfix);
16739            var animationDelay = animatableModel.getShallow('animationDelay' + postfix);
16740            if (typeof animationDelay === 'function') {
16741                animationDelay = animationDelay(
16742                    dataIndex,
16743                    animatableModel.getAnimationDelayParams
16744                        ? animatableModel.getAnimationDelayParams(el, dataIndex)
16745                        : null
16746                );
16747            }
16748            if (typeof duration === 'function') {
16749                duration = duration(dataIndex);
16750            }
16751
16752            duration > 0
16753                ? el.animateTo(props, duration, animationDelay || 0, animationEasing, cb, !!cb)
16754                : (el.stopAnimation(), el.attr(props), cb && cb());
16755        }
16756        else {
16757            el.stopAnimation();
16758            el.attr(props);
16759            cb && cb();
16760        }
16761    }
16762
16763    /**
16764     * Update graphic element properties with or without animation according to the
16765     * configuration in series.
16766     *
16767     * Caution: this method will stop previous animation.
16768     * So if do not use this method to one element twice before
16769     * animation starts, unless you know what you are doing.
16770     *
16771     * @param {module:zrender/Element} el
16772     * @param {Object} props
16773     * @param {module:echarts/model/Model} [animatableModel]
16774     * @param {number} [dataIndex]
16775     * @param {Function} [cb]
16776     * @example
16777     *     graphic.updateProps(el, {
16778 *         position: [100, 100]
16779 *     }, seriesModel, dataIndex, function () { console.log('Animation done!'); });
16780     *     // Or
16781     *     graphic.updateProps(el, {
16782 *         position: [100, 100]
16783 *     }, seriesModel, function () { console.log('Animation done!'); });
16784     */
16785    function updateProps(el, props, animatableModel, dataIndex, cb) {
16786        animateOrSetProps(true, el, props, animatableModel, dataIndex, cb);
16787    }
16788
16789    /**
16790     * Init graphic element properties with or without animation according to the
16791     * configuration in series.
16792     *
16793     * Caution: this method will stop previous animation.
16794     * So if do not use this method to one element twice before
16795     * animation starts, unless you know what you are doing.
16796     *
16797     * @param {module:zrender/Element} el
16798     * @param {Object} props
16799     * @param {module:echarts/model/Model} [animatableModel]
16800     * @param {number} [dataIndex]
16801     * @param {Function} cb
16802     */
16803    function initProps(el, props, animatableModel, dataIndex, cb) {
16804        animateOrSetProps(false, el, props, animatableModel, dataIndex, cb);
16805    }
16806
16807    /**
16808     * Get transform matrix of target (param target),
16809     * in coordinate of its ancestor (param ancestor)
16810     *
16811     * @param {module:zrender/mixin/Transformable} target
16812     * @param {module:zrender/mixin/Transformable} [ancestor]
16813     */
16814    function getTransform(target, ancestor) {
16815        var mat = identity([]);
16816
16817        while (target && target !== ancestor) {
16818            mul$1(mat, target.getLocalTransform(), mat);
16819            target = target.parent;
16820        }
16821
16822        return mat;
16823    }
16824
16825    /**
16826     * Apply transform to an vertex.
16827     * @param {Array.<number>} target [x, y]
16828     * @param {Array.<number>|TypedArray.<number>|Object} transform Can be:
16829     *      + Transform matrix: like [1, 0, 0, 1, 0, 0]
16830     *      + {position, rotation, scale}, the same as `zrender/Transformable`.
16831     * @param {boolean=} invert Whether use invert matrix.
16832     * @return {Array.<number>} [x, y]
16833     */
16834    function applyTransform$1(target, transform, invert$$1) {
16835        if (transform && !isArrayLike(transform)) {
16836            transform = Transformable.getLocalTransform(transform);
16837        }
16838
16839        if (invert$$1) {
16840            transform = invert([], transform);
16841        }
16842        return applyTransform([], target, transform);
16843    }
16844
16845    /**
16846     * @param {string} direction 'left' 'right' 'top' 'bottom'
16847     * @param {Array.<number>} transform Transform matrix: like [1, 0, 0, 1, 0, 0]
16848     * @param {boolean=} invert Whether use invert matrix.
16849     * @return {string} Transformed direction. 'left' 'right' 'top' 'bottom'
16850     */
16851    function transformDirection(direction, transform, invert$$1) {
16852
16853        // Pick a base, ensure that transform result will not be (0, 0).
16854        var hBase = (transform[4] === 0 || transform[5] === 0 || transform[0] === 0)
16855            ? 1 : Math.abs(2 * transform[4] / transform[0]);
16856        var vBase = (transform[4] === 0 || transform[5] === 0 || transform[2] === 0)
16857            ? 1 : Math.abs(2 * transform[4] / transform[2]);
16858
16859        var vertex = [
16860            direction === 'left' ? -hBase : direction === 'right' ? hBase : 0,
16861            direction === 'top' ? -vBase : direction === 'bottom' ? vBase : 0
16862        ];
16863
16864        vertex = applyTransform$1(vertex, transform, invert$$1);
16865
16866        return Math.abs(vertex[0]) > Math.abs(vertex[1])
16867            ? (vertex[0] > 0 ? 'right' : 'left')
16868            : (vertex[1] > 0 ? 'bottom' : 'top');
16869    }
16870
16871    /**
16872     * Apply group transition animation from g1 to g2.
16873     * If no animatableModel, no animation.
16874     */
16875    function groupTransition(g1, g2, animatableModel, cb) {
16876        if (!g1 || !g2) {
16877            return;
16878        }
16879
16880        function getElMap(g) {
16881            var elMap = {};
16882            g.traverse(function (el) {
16883                if (!el.isGroup && el.anid) {
16884                    elMap[el.anid] = el;
16885                }
16886            });
16887            return elMap;
16888        }
16889        function getAnimatableProps(el) {
16890            var obj = {
16891                position: clone$1(el.position),
16892                rotation: el.rotation
16893            };
16894            if (el.shape) {
16895                obj.shape = extend({}, el.shape);
16896            }
16897            return obj;
16898        }
16899        var elMap1 = getElMap(g1);
16900
16901        g2.traverse(function (el) {
16902            if (!el.isGroup && el.anid) {
16903                var oldEl = elMap1[el.anid];
16904                if (oldEl) {
16905                    var newProp = getAnimatableProps(el);
16906                    el.attr(getAnimatableProps(oldEl));
16907                    updateProps(el, newProp, animatableModel, el.dataIndex);
16908                }
16909                // else {
16910                //     if (el.previousProps) {
16911                //         graphic.updateProps
16912                //     }
16913                // }
16914            }
16915        });
16916    }
16917
16918    /**
16919     * @param {Array.<Array.<number>>} points Like: [[23, 44], [53, 66], ...]
16920     * @param {Object} rect {x, y, width, height}
16921     * @return {Array.<Array.<number>>} A new clipped points.
16922     */
16923    function clipPointsByRect(points, rect) {
16924        return map(points, function (point) {
16925            var x = point[0];
16926            x = mathMax$1(x, rect.x);
16927            x = mathMin$1(x, rect.x + rect.width);
16928            var y = point[1];
16929            y = mathMax$1(y, rect.y);
16930            y = mathMin$1(y, rect.y + rect.height);
16931            return [x, y];
16932        });
16933    }
16934
16935    /**
16936     * @param {Object} targetRect {x, y, width, height}
16937     * @param {Object} rect {x, y, width, height}
16938     * @return {Object} A new clipped rect. If rect size are negative, return undefined.
16939     */
16940    function clipRectByRect(targetRect, rect) {
16941        var x = mathMax$1(targetRect.x, rect.x);
16942        var x2 = mathMin$1(targetRect.x + targetRect.width, rect.x + rect.width);
16943        var y = mathMax$1(targetRect.y, rect.y);
16944        var y2 = mathMin$1(targetRect.y + targetRect.height, rect.y + rect.height);
16945
16946        if (x2 >= x && y2 >= y) {
16947            return {
16948                x: x,
16949                y: y,
16950                width: x2 - x,
16951                height: y2 - y
16952            };
16953        }
16954    }
16955
16956    /**
16957     * @param {string} iconStr Support 'image://' or 'path://' or direct svg path.
16958     * @param {Object} [opt] Properties of `module:zrender/Element`, except `style`.
16959     * @param {Object} [rect] {x, y, width, height}
16960     * @return {module:zrender/Element} Icon path or image element.
16961     */
16962    function createIcon(iconStr, opt, rect) {
16963        opt = extend({rectHover: true}, opt);
16964        var style = opt.style = {strokeNoScale: true};
16965        rect = rect || {x: -1, y: -1, width: 2, height: 2};
16966
16967        if (iconStr) {
16968            return iconStr.indexOf('image://') === 0
16969                ? (
16970                    style.image = iconStr.slice(8),
16971                        defaults(style, rect),
16972                        new ZImage(opt)
16973                )
16974                : (
16975                    makePath(
16976                        iconStr.replace('path://', ''),
16977                        opt,
16978                        rect,
16979                        'center'
16980                    )
16981                );
16982        }
16983    }
16984
16985
16986
16987
16988    var graphic = (Object.freeze || Object)({
16989        extendShape: extendShape,
16990        extendPath: extendPath,
16991        makePath: makePath,
16992        makeImage: makeImage,
16993        mergePath: mergePath,
16994        resizePath: resizePath,
16995        subPixelOptimizeLine: subPixelOptimizeLine,
16996        subPixelOptimizeRect: subPixelOptimizeRect,
16997        subPixelOptimize: subPixelOptimize,
16998        setHoverStyle: setHoverStyle,
16999        setLabelStyle: setLabelStyle,
17000        setTextStyle: setTextStyle,
17001        setText: setText,
17002        getFont: getFont,
17003        updateProps: updateProps,
17004        initProps: initProps,
17005        getTransform: getTransform,
17006        applyTransform: applyTransform$1,
17007        transformDirection: transformDirection,
17008        groupTransition: groupTransition,
17009        clipPointsByRect: clipPointsByRect,
17010        clipRectByRect: clipRectByRect,
17011        createIcon: createIcon,
17012        Group: Group,
17013        Image: ZImage,
17014        Text: Text,
17015        Circle: Circle,
17016        Sector: Sector,
17017        Ring: Ring,
17018        Polygon: Polygon,
17019        Polyline: Polyline,
17020        Rect: Rect,
17021        Line: Line,
17022        BezierCurve: BezierCurve,
17023        Arc: Arc,
17024        IncrementalDisplayable: IncrementalDisplayble,
17025        CompoundPath: CompoundPath,
17026        LinearGradient: LinearGradient,
17027        RadialGradient: RadialGradient,
17028        BoundingRect: BoundingRect
17029    });
17030
17031    /*
17032* Licensed to the Apache Software Foundation (ASF) under one
17033* or more contributor license agreements.  See the NOTICE file
17034* distributed with this work for additional information
17035* regarding copyright ownership.  The ASF licenses this file
17036* to you under the Apache License, Version 2.0 (the
17037* "License"); you may not use this file except in compliance
17038* with the License.  You may obtain a copy of the License at
17039*
17040*   http://www.apache.org/licenses/LICENSE-2.0
17041*
17042* Unless required by applicable law or agreed to in writing,
17043* software distributed under the License is distributed on an
17044* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17045* KIND, either express or implied.  See the License for the
17046* specific language governing permissions and limitations
17047* under the License.
17048*/
17049
17050    var PATH_COLOR = ['textStyle', 'color'];
17051
17052    var textStyleMixin = {
17053        /**
17054         * Get color property or get color from option.textStyle.color
17055         * @param {boolean} [isEmphasis]
17056         * @return {string}
17057         */
17058        getTextColor: function (isEmphasis) {
17059            var ecModel = this.ecModel;
17060            return this.getShallow('color')
17061                || (
17062                    (!isEmphasis && ecModel) ? ecModel.get(PATH_COLOR) : null
17063                );
17064        },
17065
17066        /**
17067         * Create font string from fontStyle, fontWeight, fontSize, fontFamily
17068         * @return {string}
17069         */
17070        getFont: function () {
17071            return getFont({
17072                fontStyle: this.getShallow('fontStyle'),
17073                fontWeight: this.getShallow('fontWeight'),
17074                fontSize: this.getShallow('fontSize'),
17075                fontFamily: this.getShallow('fontFamily')
17076            }, this.ecModel);
17077        },
17078
17079        getTextRect: function (text) {
17080            return getBoundingRect(
17081                text,
17082                this.getFont(),
17083                this.getShallow('align'),
17084                this.getShallow('verticalAlign') || this.getShallow('baseline'),
17085                this.getShallow('padding'),
17086                this.getShallow('rich'),
17087                this.getShallow('truncateText')
17088            );
17089        }
17090    };
17091
17092    /*
17093* Licensed to the Apache Software Foundation (ASF) under one
17094* or more contributor license agreements.  See the NOTICE file
17095* distributed with this work for additional information
17096* regarding copyright ownership.  The ASF licenses this file
17097* to you under the Apache License, Version 2.0 (the
17098* "License"); you may not use this file except in compliance
17099* with the License.  You may obtain a copy of the License at
17100*
17101*   http://www.apache.org/licenses/LICENSE-2.0
17102*
17103* Unless required by applicable law or agreed to in writing,
17104* software distributed under the License is distributed on an
17105* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17106* KIND, either express or implied.  See the License for the
17107* specific language governing permissions and limitations
17108* under the License.
17109*/
17110
17111    var getItemStyle = makeStyleMapper(
17112        [
17113            ['fill', 'color'],
17114            ['stroke', 'borderColor'],
17115            ['lineWidth', 'borderWidth'],
17116            ['opacity'],
17117            ['shadowBlur'],
17118            ['shadowOffsetX'],
17119            ['shadowOffsetY'],
17120            ['shadowColor'],
17121            ['textPosition'],
17122            ['textAlign']
17123        ]
17124    );
17125
17126    var itemStyleMixin = {
17127        getItemStyle: function (excludes, includes) {
17128            var style = getItemStyle(this, excludes, includes);
17129            var lineDash = this.getBorderLineDash();
17130            lineDash && (style.lineDash = lineDash);
17131            return style;
17132        },
17133
17134        getBorderLineDash: function () {
17135            var lineType = this.get('borderType');
17136            return (lineType === 'solid' || lineType == null) ? null
17137                : (lineType === 'dashed' ? [5, 5] : [1, 1]);
17138        }
17139    };
17140
17141    /*
17142* Licensed to the Apache Software Foundation (ASF) under one
17143* or more contributor license agreements.  See the NOTICE file
17144* distributed with this work for additional information
17145* regarding copyright ownership.  The ASF licenses this file
17146* to you under the Apache License, Version 2.0 (the
17147* "License"); you may not use this file except in compliance
17148* with the License.  You may obtain a copy of the License at
17149*
17150*   http://www.apache.org/licenses/LICENSE-2.0
17151*
17152* Unless required by applicable law or agreed to in writing,
17153* software distributed under the License is distributed on an
17154* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17155* KIND, either express or implied.  See the License for the
17156* specific language governing permissions and limitations
17157* under the License.
17158*/
17159
17160    /**
17161     * @module echarts/model/Model
17162     */
17163
17164    var mixin$1 = mixin;
17165    var inner = makeInner();
17166
17167    /**
17168     * @alias module:echarts/model/Model
17169     * @constructor
17170     * @param {Object} option
17171     * @param {module:echarts/model/Model} [parentModel]
17172     * @param {module:echarts/model/Global} [ecModel]
17173     */
17174    function Model(option, parentModel, ecModel) {
17175        /**
17176         * @type {module:echarts/model/Model}
17177         * @readOnly
17178         */
17179        this.parentModel = parentModel;
17180
17181        /**
17182         * @type {module:echarts/model/Global}
17183         * @readOnly
17184         */
17185        this.ecModel = ecModel;
17186
17187        /**
17188         * @type {Object}
17189         * @protected
17190         */
17191        this.option = option;
17192
17193        // Simple optimization
17194        // if (this.init) {
17195        //     if (arguments.length <= 4) {
17196        //         this.init(option, parentModel, ecModel, extraOpt);
17197        //     }
17198        //     else {
17199        //         this.init.apply(this, arguments);
17200        //     }
17201        // }
17202    }
17203
17204    Model.prototype = {
17205
17206        constructor: Model,
17207
17208        /**
17209         * Model 鐨勫垵濮嬪寲鍑芥暟
17210         * @param {Object} option
17211         */
17212        init: null,
17213
17214        /**
17215         * 浠庢柊鐨� Option merge
17216         */
17217        mergeOption: function (option) {
17218            merge(this.option, option, true);
17219        },
17220
17221        /**
17222         * @param {string|Array.<string>} path
17223         * @param {boolean} [ignoreParent=false]
17224         * @return {*}
17225         */
17226        get: function (path, ignoreParent) {
17227            if (path == null) {
17228                return this.option;
17229            }
17230
17231            return doGet(
17232                this.option,
17233                this.parsePath(path),
17234                !ignoreParent && getParent(this, path)
17235            );
17236        },
17237
17238        /**
17239         * @param {string} key
17240         * @param {boolean} [ignoreParent=false]
17241         * @return {*}
17242         */
17243        getShallow: function (key, ignoreParent) {
17244            var option = this.option;
17245
17246            var val = option == null ? option : option[key];
17247            var parentModel = !ignoreParent && getParent(this, key);
17248            if (val == null && parentModel) {
17249                val = parentModel.getShallow(key);
17250            }
17251            return val;
17252        },
17253
17254        /**
17255         * @param {string|Array.<string>} [path]
17256         * @param {module:echarts/model/Model} [parentModel]
17257         * @return {module:echarts/model/Model}
17258         */
17259        getModel: function (path, parentModel) {
17260            var obj = path == null
17261                ? this.option
17262                : doGet(this.option, path = this.parsePath(path));
17263
17264            var thisParentModel;
17265            parentModel = parentModel || (
17266                (thisParentModel = getParent(this, path))
17267                && thisParentModel.getModel(path)
17268            );
17269
17270            return new Model(obj, parentModel, this.ecModel);
17271        },
17272
17273        /**
17274         * If model has option
17275         */
17276        isEmpty: function () {
17277            return this.option == null;
17278        },
17279
17280        restoreData: function () {},
17281
17282        // Pending
17283        clone: function () {
17284            var Ctor = this.constructor;
17285            return new Ctor(clone(this.option));
17286        },
17287
17288        setReadOnly: function (properties) {
17289            // clazzUtil.setReadOnly(this, properties);
17290        },
17291
17292        // If path is null/undefined, return null/undefined.
17293        parsePath: function(path) {
17294            if (typeof path === 'string') {
17295                path = path.split('.');
17296            }
17297            return path;
17298        },
17299
17300        /**
17301         * @param {Function} getParentMethod
17302         *        param {Array.<string>|string} path
17303         *        return {module:echarts/model/Model}
17304         */
17305        customizeGetParent: function (getParentMethod) {
17306            inner(this).getParent = getParentMethod;
17307        },
17308
17309        isAnimationEnabled: function () {
17310            if (!env$1.node) {
17311                if (this.option.animation != null) {
17312                    return !!this.option.animation;
17313                }
17314                else if (this.parentModel) {
17315                    return this.parentModel.isAnimationEnabled();
17316                }
17317            }
17318        }
17319
17320    };
17321
17322    function doGet(obj, pathArr, parentModel) {
17323        for (var i = 0; i < pathArr.length; i++) {
17324            // Ignore empty
17325            if (!pathArr[i]) {
17326                continue;
17327            }
17328            // obj could be number/string/... (like 0)
17329            obj = (obj && typeof obj === 'object') ? obj[pathArr[i]] : null;
17330            if (obj == null) {
17331                break;
17332            }
17333        }
17334        if (obj == null && parentModel) {
17335            obj = parentModel.get(pathArr);
17336        }
17337        return obj;
17338    }
17339
17340// `path` can be null/undefined
17341    function getParent(model, path) {
17342        var getParentMethod = inner(model).getParent;
17343        return getParentMethod ? getParentMethod.call(model, path) : model.parentModel;
17344    }
17345
17346// Enable Model.extend.
17347    enableClassExtend(Model);
17348    enableClassCheck(Model);
17349
17350    mixin$1(Model, lineStyleMixin);
17351    mixin$1(Model, areaStyleMixin);
17352    mixin$1(Model, textStyleMixin);
17353    mixin$1(Model, itemStyleMixin);
17354
17355    /*
17356* Licensed to the Apache Software Foundation (ASF) under one
17357* or more contributor license agreements.  See the NOTICE file
17358* distributed with this work for additional information
17359* regarding copyright ownership.  The ASF licenses this file
17360* to you under the Apache License, Version 2.0 (the
17361* "License"); you may not use this file except in compliance
17362* with the License.  You may obtain a copy of the License at
17363*
17364*   http://www.apache.org/licenses/LICENSE-2.0
17365*
17366* Unless required by applicable law or agreed to in writing,
17367* software distributed under the License is distributed on an
17368* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17369* KIND, either express or implied.  See the License for the
17370* specific language governing permissions and limitations
17371* under the License.
17372*/
17373
17374    var base = 0;
17375
17376    /**
17377     * @public
17378     * @param {string} type
17379     * @return {string}
17380     */
17381    function getUID(type) {
17382        // Considering the case of crossing js context,
17383        // use Math.random to make id as unique as possible.
17384        return [(type || ''), base++, Math.random().toFixed(5)].join('_');
17385    }
17386
17387    /**
17388     * @inner
17389     */
17390    function enableSubTypeDefaulter(entity) {
17391
17392        var subTypeDefaulters = {};
17393
17394        entity.registerSubTypeDefaulter = function (componentType, defaulter) {
17395            componentType = parseClassType$1(componentType);
17396            subTypeDefaulters[componentType.main] = defaulter;
17397        };
17398
17399        entity.determineSubType = function (componentType, option) {
17400            var type = option.type;
17401            if (!type) {
17402                var componentTypeMain = parseClassType$1(componentType).main;
17403                if (entity.hasSubTypes(componentType) && subTypeDefaulters[componentTypeMain]) {
17404                    type = subTypeDefaulters[componentTypeMain](option);
17405                }
17406            }
17407            return type;
17408        };
17409
17410        return entity;
17411    }
17412
17413    /**
17414     * Topological travel on Activity Network (Activity On Vertices).
17415     * Dependencies is defined in Model.prototype.dependencies, like ['xAxis', 'yAxis'].
17416     *
17417     * If 'xAxis' or 'yAxis' is absent in componentTypeList, just ignore it in topology.
17418     *
17419     * If there is circle dependencey, Error will be thrown.
17420     *
17421     */
17422    function enableTopologicalTravel(entity, dependencyGetter) {
17423
17424        /**
17425         * @public
17426         * @param {Array.<string>} targetNameList Target Component type list.
17427         *                                           Can be ['aa', 'bb', 'aa.xx']
17428         * @param {Array.<string>} fullNameList By which we can build dependency graph.
17429         * @param {Function} callback Params: componentType, dependencies.
17430         * @param {Object} context Scope of callback.
17431         */
17432        entity.topologicalTravel = function (targetNameList, fullNameList, callback, context) {
17433            if (!targetNameList.length) {
17434                return;
17435            }
17436
17437            var result = makeDepndencyGraph(fullNameList);
17438            var graph = result.graph;
17439            var stack = result.noEntryList;
17440
17441            var targetNameSet = {};
17442            each$1(targetNameList, function (name) {
17443                targetNameSet[name] = true;
17444            });
17445
17446            while (stack.length) {
17447                var currComponentType = stack.pop();
17448                var currVertex = graph[currComponentType];
17449                var isInTargetNameSet = !!targetNameSet[currComponentType];
17450                if (isInTargetNameSet) {
17451                    callback.call(context, currComponentType, currVertex.originalDeps.slice());
17452                    delete targetNameSet[currComponentType];
17453                }
17454                each$1(
17455                    currVertex.successor,
17456                    isInTargetNameSet ? removeEdgeAndAdd : removeEdge
17457                );
17458            }
17459
17460            each$1(targetNameSet, function () {
17461                throw new Error('Circle dependency may exists');
17462            });
17463
17464            function removeEdge(succComponentType) {
17465                graph[succComponentType].entryCount--;
17466                if (graph[succComponentType].entryCount === 0) {
17467                    stack.push(succComponentType);
17468                }
17469            }
17470
17471            // Consider this case: legend depends on series, and we call
17472            // chart.setOption({series: [...]}), where only series is in option.
17473            // If we do not have 'removeEdgeAndAdd', legendModel.mergeOption will
17474            // not be called, but only sereis.mergeOption is called. Thus legend
17475            // have no chance to update its local record about series (like which
17476            // name of series is available in legend).
17477            function removeEdgeAndAdd(succComponentType) {
17478                targetNameSet[succComponentType] = true;
17479                removeEdge(succComponentType);
17480            }
17481        };
17482
17483        /**
17484         * DepndencyGraph: {Object}
17485         * key: conponentType,
17486         * value: {
17487     *     successor: [conponentTypes...],
17488     *     originalDeps: [conponentTypes...],
17489     *     entryCount: {number}
17490     * }
17491         */
17492        function makeDepndencyGraph(fullNameList) {
17493            var graph = {};
17494            var noEntryList = [];
17495
17496            each$1(fullNameList, function (name) {
17497
17498                var thisItem = createDependencyGraphItem(graph, name);
17499                var originalDeps = thisItem.originalDeps = dependencyGetter(name);
17500
17501                var availableDeps = getAvailableDependencies(originalDeps, fullNameList);
17502                thisItem.entryCount = availableDeps.length;
17503                if (thisItem.entryCount === 0) {
17504                    noEntryList.push(name);
17505                }
17506
17507                each$1(availableDeps, function (dependentName) {
17508                    if (indexOf(thisItem.predecessor, dependentName) < 0) {
17509                        thisItem.predecessor.push(dependentName);
17510                    }
17511                    var thatItem = createDependencyGraphItem(graph, dependentName);
17512                    if (indexOf(thatItem.successor, dependentName) < 0) {
17513                        thatItem.successor.push(name);
17514                    }
17515                });
17516            });
17517
17518            return {graph: graph, noEntryList: noEntryList};
17519        }
17520
17521        function createDependencyGraphItem(graph, name) {
17522            if (!graph[name]) {
17523                graph[name] = {predecessor: [], successor: []};
17524            }
17525            return graph[name];
17526        }
17527
17528        function getAvailableDependencies(originalDeps, fullNameList) {
17529            var availableDeps = [];
17530            each$1(originalDeps, function (dep) {
17531                indexOf(fullNameList, dep) >= 0 && availableDeps.push(dep);
17532            });
17533            return availableDeps;
17534        }
17535    }
17536
17537    /*
17538* Licensed to the Apache Software Foundation (ASF) under one
17539* or more contributor license agreements.  See the NOTICE file
17540* distributed with this work for additional information
17541* regarding copyright ownership.  The ASF licenses this file
17542* to you under the Apache License, Version 2.0 (the
17543* "License");
17543 you may not use this file except in compliance
17544* with the License.  You may obtain a copy of the License at
17545*
17546*   http://www.apache.org/licenses/LICENSE-2.0
17547*
17548* Unless required by applicable law or agreed to in writing,
17549* software distributed under the License is distributed on an
17550* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17551* KIND, either express or implied.  See the License for the
17552* specific language governing permissions and limitations
17553* under the License.
17554*/
17555
17556    var RADIAN_EPSILON = 1e-4;
17557
17558    function _trim(str) {
17559        return str.replace(/^\s+/, '').replace(/\s+$/, '');
17560    }
17561
17562    /**
17563     * Linear mapping a value from domain to range
17564     * @memberOf module:echarts/util/number
17565     * @param  {(number|Array.<number>)} val
17566     * @param  {Array.<number>} domain Domain extent domain[0] can be bigger than domain[1]
17567     * @param  {Array.<number>} range  Range extent range[0] can be bigger than range[1]
17568     * @param  {boolean} clamp
17569     * @return {(number|Array.<number>}
17570     */
17571    function linearMap(val, domain, range, clamp) {
17572        var subDomain = domain[1] - domain[0];
17573        var subRange = range[1] - range[0];
17574
17575        if (subDomain === 0) {
17576            return subRange === 0
17577                ? range[0]
17578                : (range[0] + range[1]) / 2;
17579        }
17580
17581        // Avoid accuracy problem in edge, such as
17582        // 146.39 - 62.83 === 83.55999999999999.
17583        // See echarts/test/ut/spec/util/number.js#linearMap#accuracyError
17584        // It is a little verbose for efficiency considering this method
17585        // is a hotspot.
17586        if (clamp) {
17587            if (subDomain > 0) {
17588                if (val <= domain[0]) {
17589                    return range[0];
17590                }
17591                else if (val >= domain[1]) {
17592                    return range[1];
17593                }
17594            }
17595            else {
17596                if (val >= domain[0]) {
17597                    return range[0];
17598                }
17599                else if (val <= domain[1]) {
17600                    return range[1];
17601                }
17602            }
17603        }
17604        else {
17605            if (val === domain[0]) {
17606                return range[0];
17607            }
17608            if (val === domain[1]) {
17609                return range[1];
17610            }
17611        }
17612
17613        return (val - domain[0]) / subDomain * subRange + range[0];
17614    }
17615
17616    /**
17617     * Convert a percent string to absolute number.
17618     * Returns NaN if percent is not a valid string or number
17619     * @memberOf module:echarts/util/number
17620     * @param {string|number} percent
17621     * @param {number} all
17622     * @return {number}
17623     */
17624    function parsePercent$1(percent, all) {
17625        switch (percent) {
17626            case 'center':
17627            case 'middle':
17628                percent = '50%';
17629                break;
17630            case 'left':
17631            case 'top':
17632                percent = '0%';
17633                break;
17634            case 'right':
17635            case 'bottom':
17636                percent = '100%';
17637                break;
17638        }
17639        if (typeof percent === 'string') {
17640            if (_trim(percent).match(/%$/)) {
17641                return parseFloat(percent) / 100 * all;
17642            }
17643
17644            return parseFloat(percent);
17645        }
17646
17647        return percent == null ? NaN : +percent;
17648    }
17649
17650    /**
17651     * (1) Fix rounding error of float numbers.
17652     * (2) Support return string to avoid scientific notation like '3.5e-7'.
17653     *
17654     * @param {number} x
17655     * @param {number} [precision]
17656     * @param {boolean} [returnStr]
17657     * @return {number|string}
17658     */
17659    function round$1(x, precision, returnStr) {
17660        if (precision == null) {
17661            precision = 10;
17662        }
17663        // Avoid range error
17664        precision = Math.min(Math.max(0, precision), 20);
17665        x = (+x).toFixed(precision);
17666        return returnStr ? x : +x;
17667    }
17668
17669    function asc(arr) {
17670        arr.sort(function (a, b) {
17671            return a - b;
17672        });
17673        return arr;
17674    }
17675
17676    /**
17677     * Get precision
17678     * @param {number} val
17679     */
17680    function getPrecision(val) {
17681        val = +val;
17682        if (isNaN(val)) {
17683            return 0;
17684        }
vendor: 4,213 bytes, lines 17685-17814
17685        // It is much faster than methods converting number to string as follows
17686        //      var tmp = val.toString();
17687        //      return tmp.length - 1 - tmp.indexOf('.');
17688        // especially when precision is low
17689        var e = 1;
17690        var count = 0;
17691        while (Math.round(val * e) / e !== val) {
17692            e *= 10;
17693            count++;
17694        }
17695        return count;
17696    }
17697
17698    /**
17699     * @param {string|number} val
17700     * @return {number}
17701     */
17702    function getPrecisionSafe(val) {
17703        var str = val.toString();
17704
17705        // Consider scientific notation: '3.4e-12' '3.4e+12'
17706        var eIndex = str.indexOf('e');
17707        if (eIndex > 0) {
17708            var precision = +str.slice(eIndex + 1);
17709            return precision < 0 ? -precision : 0;
17710        }
17711        else {
17712            var dotIndex = str.indexOf('.');
17713            return dotIndex < 0 ? 0 : str.length - 1 - dotIndex;
17714        }
17715    }
17716
17717    /**
17718     * Minimal dicernible data precisioin according to a single pixel.
17719     *
17720     * @param {Array.<number>} dataExtent
17721     * @param {Array.<number>} pixelExtent
17722     * @return {number} precision
17723     */
17724    function getPixelPrecision(dataExtent, pixelExtent) {
17725        var log = Math.log;
17726        var LN10 = Math.LN10;
17727        var dataQuantity = Math.floor(log(dataExtent[1] - dataExtent[0]) / LN10);
17728        var sizeQuantity = Math.round(log(Math.abs(pixelExtent[1] - pixelExtent[0])) / LN10);
17729        // toFixed() digits argument must be between 0 and 20.
17730        var precision = Math.min(Math.max(-dataQuantity + sizeQuantity, 0), 20);
17731        return !isFinite(precision) ? 20 : precision;
17732    }
17733
17734    /**
17735     * Get a data of given precision, assuring the sum of percentages
17736     * in valueList is 1.
17737     * The largest remainer method is used.
17738     * https://en.wikipedia.org/wiki/Largest_remainder_method
17739     *
17740     * @param {Array.<number>} valueList a list of all data
17741     * @param {number} idx index of the data to be processed in valueList
17742     * @param {number} precision integer number showing digits of precision
17743     * @return {number} percent ranging from 0 to 100
17744     */
17745    function getPercentWithPrecision(valueList, idx, precision) {
17746        if (!valueList[idx]) {
17747            return 0;
17748        }
17749
17750        var sum = reduce(valueList, function (acc, val) {
17751            return acc + (isNaN(val) ? 0 : val);
17752        }, 0);
17753        if (sum === 0) {
17754            return 0;
17755        }
17756
17757        var digits = Math.pow(10, precision);
17758        var votesPerQuota = map(valueList, function (val) {
17759            return (isNaN(val) ? 0 : val) / sum * digits * 100;
17760        });
17761        var targetSeats = digits * 100;
17762
17763        var seats = map(votesPerQuota, function (votes) {
17764            // Assign automatic seats.
17765            return Math.floor(votes);
17766        });
17767        var currentSum = reduce(seats, function (acc, val) {
17768            return acc + val;
17769        }, 0);
17770
17771        var remainder = map(votesPerQuota, function (votes, idx) {
17772            return votes - seats[idx];
17773        });
17774
17775        // Has remainding votes.
17776        while (currentSum < targetSeats) {
17777            // Find next largest remainder.
17778            var max = Number.NEGATIVE_INFINITY;
17779            var maxId = null;
17780            for (var i = 0, len = remainder.length; i < len; ++i) {
17781                if (remainder[i] > max) {
17782                    max = remainder[i];
17783                    maxId = i;
17784                }
17785            }
17786
17787            // Add a vote to max remainder.
17788            ++seats[maxId];
17789            remainder[maxId] = 0;
17790            ++currentSum;
17791        }
17792
17793        return seats[idx] / digits;
17794    }
17795
17796// Number.MAX_SAFE_INTEGER, ie do not support.
17797    var MAX_SAFE_INTEGER = 9007199254740991;
17798
17799    /**
17800     * To 0 - 2 * PI, considering negative radian.
17801     * @param {number} radian
17802     * @return {number}
17803     */
17804    function remRadian(radian) {
17805        var pi2 = Math.PI * 2;
17806        return (radian % pi2 + pi2) % pi2;
17807    }
17808
17809    /**
17810     * @param {type} radian
17811     * @return {boolean}
17812     */
17813    function isRadianAroundZero(val) {
17814        return val >
17814 -RADIAN_EPSILON && val < RADIAN_EPSILON;
17815    }
17816
17817    var 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
17818
17819    /**
17820     * @param {string|Date|number} value These values can be accepted:
17821     *   + An instance of Date, represent a time in its own time zone.
17822     *   + Or string in a subset of ISO 8601, only including:
17823     *     + only year, month, date: '2012-03', '2012-03-01', '2012-03-01 05', '2012-03-01 05:06',
17824     *     + separated with T or space: '2012-03-01T12:22:33.123', '2012-03-01 12:22:33.123',
17825     *     + time zone: '2012-03-01T12:22:33Z', '2012-03-01T12:22:33+8000', '2012-03-01T12:22:33-05:00',
17826     *     all of which will be treated as local time if time zone is not specified
17827     *     (see <https://momentjs.com/>).
17828     *   + Or other string format, including (all of which will be treated as loacal time):
17829     *     '2012', '2012-3-1', '2012/3/1', '2012/03/01',
17830     *     '2009/6/12 2:00', '2009/6/12 2:05:08', '2009/6/12 2:05:08.123'
17831     *   + a timestamp, which represent a time in UTC.
17832     * @return {Date} date
17833     */
17834    function parseDate(value) {
17835        if (value instanceof Date) {
17836            return value;
17837        }
17838        else if (typeof value === 'string') {
17839            // Different browsers parse date in different way, so we parse it manually.
17840            // Some other issues:
17841            // new Date('1970-01-01') is UTC,
17842            // new Date('1970/01/01') and new Date('1970-1-01') is local.
17843            // See issue #3623
17844            var match = TIME_REG.exec(value);
17845
17846            if (!match) {
17847                // return Invalid Date.
17848                return new Date(NaN);
17849            }
17850
17851            // Use local time when no timezone offset specifed.
17852            if (!match[8]) {
17853                // match[n] can only be string or undefined.
17854                // But take care of '12' + 1 => '121'.
17855                return new Date(
17856                    +match[1],
17857                    +(match[2] || 1) - 1,
17858                    +match[3] || 1,
17859                    +match[4] || 0,
17860                    +(match[5] || 0),
17861                    +match[6] || 0,
17862                    +match[7] || 0
17863                );
17864            }
17865            // Timezoneoffset of Javascript Date has considered DST (Daylight Saving Time,
17866            // https://tc39.github.io/ecma262/#sec-daylight-saving-time-adjustment).
17867            // For example, system timezone is set as "Time Zone: America/Toronto",
17868            // then these code will get different result:
17869            // `new Date(1478411999999).getTimezoneOffset();  // get 240`
17870            // `new Date(1478412000000).getTimezoneOffset();  // get 300`
17871            // So we should not use `new Date`, but use `Date.UTC`.
17872            else {
17873                var hour = +match[4] || 0;
17874                if (match[8].toUpperCase() !== 'Z') {
17875                    hour -= match[8].slice(0, 3);
17876                }
17877                return new Date(Date.UTC(
17878                    +match[1],
17879                    +(match[2] || 1) - 1,
17880                    +match[3] || 1,
17881                    hour,
17882                    +(match[5] || 0),
17883                    +match[6] || 0,
17884                    +match[7] || 0
17885                ));
17886            }
17887        }
17888        else if (value == null) {
17889            return new Date(NaN);
17890        }
17891
17892        return new Date(Math.round(value));
17893    }
17894
17895    /**
17896     * Quantity of a number. e.g. 0.1, 1, 10, 100
17897     *
17898     * @param  {number} val
17899     * @return {number}
17900     */
17901    function quantity(val) {
17902        return Math.pow(10, quantityExponent(val));
17903    }
17904
17905    function quantityExponent(val) {
17906        return Math.floor(Math.log(val) / Math.LN10);
17907    }
17908
17909    /**
17910     * find a 鈥渘ice鈥� number approximately equal to x. Round the number if round = true,
17911     * take ceiling if round = false. The primary observation is that the 鈥渘icest鈥�
17912     * numbers in decimal are 1, 2, and 5, and all power-of-ten multiples of these numbers.
17913     *
17914     * See "Nice Numbers for Graph Labels" of Graphic Gems.
17915     *
17916     * @param  {number} val Non-negative value.
17917     * @param  {boolean} round
17918     * @return {number}
17919     */
17920    function nice(val, round) {
17921        var exponent = quantityExponent(val);
17922        var exp10 = Math.pow(10, exponent);
17923        var f = val / exp10; // 1 <= f < 10
17924        var nf;
17925        if (round) {
17926            if (f < 1.5) { nf = 1; }
17927            else if (f < 2.5) { nf = 2; }
17928            else if (f < 4) { nf = 3; }
17929            else if (f < 7) { nf = 5; }
17930            else { nf = 10; }
17931        }
17932        else {
17933            if (f < 1) { nf = 1; }
17934            else if (f < 2) { nf = 2; }
17935            else if (f < 3) { nf = 3; }
17936            else if (f < 5) { nf = 5; }
17937            else { nf = 10; }
17938        }
17939        val = nf * exp10;
17940
17941        // Fix 3 * 0.1 === 0.30000000000000004 issue (see IEEE 754).
17942        // 20 is the uppper bound of toFixed.
17943        return exponent >= -20 ? +val.toFixed(exponent < 0 ? -exponent : 0) : val;
17944    }
17945
17946    /**
17947     * Order intervals asc, and split them when overlap.
17948     * expect(numberUtil.reformIntervals([
17949     *     {interval: [18, 62], close: [1, 1]},
17950     *     {interval: [-Infinity, -70], close: [0, 0]},
17951     *     {interval: [-70, -26], close: [1, 1]},
17952     *     {interval: [-26, 18], close: [1, 1]},
17953     *     {interval: [62, 150], close: [1, 1]},
17954     *     {interval: [106, 150], close: [1, 1]},
17955     *     {interval: [150, Infinity], close: [0, 0]}
17956     * ])).toEqual([
17957     *     {interval: [-Infinity, -70], close: [0, 0]},
17958     *     {interval: [-70, -26], close: [1, 1]},
17959     *     {interval: [-26, 18], close: [0, 1]},
17960     *     {interval: [18, 62], close: [0, 1]},
17961     *     {interval: [62, 150], close: [0, 1]},
17962     *     {interval: [150, Infinity], close: [0, 0]}
17963     * ]);
17964     * @param {Array.<Object>} list, where `close` mean open or close
17965     *        of the interval, and Infinity can be used.
17966     * @return {Array.<Object>} The origin list, which has been reformed.
17967     */
17968    function reformIntervals(list) {
17969        list.sort(function (a, b) {
17970            return littleThan(a, b, 0) ? -1 : 1;
17971        });
17972
17973        var curr = -Infinity;
17974        var currClose = 1;
17975        for (var i = 0; i < list.length;) {
17976            var interval = list[i].interval;
17977            var close = list[i].close;
17978
17979            for (var lg = 0; lg < 2; lg++) {
17980                if (interval[lg] <= curr) {
17981                    interval[lg] = curr;
17982                    close[lg] = !lg ? 1 - currClose : 1;
17983                }
17984                curr = interval[lg];
17985                currClose = close[lg];
17986            }
17987
17988            if (interval[0] === interval[1] && close[0] * close[1] !== 1) {
17989                list.splice(i, 1);
17990            }
17991            else {
17992                i++;
17993            }
17994        }
17995
17996        return list;
17997
17998        function littleThan(a, b, lg) {
17999            return a.interval[lg] < b.interval[lg]
18000                || (
18001                    a.interval[lg] === b.interval[lg]
18002                    && (
18003                        (a.close[lg] - b.close[lg] === (!lg ? 1 : -1))
18004                        || (!lg && littleThan(a, b, 1))
18005                    )
18006                );
18007        }
18008    }
18009
18010    /**
18011     * parseFloat NaNs numeric-cast false positives (null|true|false|"")
18012     * ...but misinterprets leading-number strings, particularly hex literals ("0x...")
18013     * subtraction forces infinities to NaN
18014     *
18015     * @param {*} v
18016     * @return {boolean}
18017     */
18018    function isNumeric(v) {
18019        return v - parseFloat(v) >= 0;
18020    }
18021
18022
18023    var number = (Object.freeze || Object)({
18024        linearMap: linearMap,
18025        parsePercent: parsePercent$1,
18026        round: round$1,
18027        asc: asc,
18028        getPrecision: getPrecision,
18029        getPrecisionSafe: getPrecisionSafe,
18030        getPixelPrecision: getPixelPrecision,
18031        getPercentWithPrecision: getPercentWithPrecision,
18032        MAX_SAFE_INTEGER: MAX_SAFE_INTEGER,
18033        remRadian: remRadian,
18034        isRadianAroundZero: isRadianAroundZero,
18035        parseDate: parseDate,
18036        quantity: quantity,
18037        nice: nice,
18038        reformIntervals: reformIntervals,
18039        isNumeric: isNumeric
18040    });
18041
18042    /*
18043* Licensed to the Apache Software Foundation (ASF) under one
18044* or more contributor license agreements.  See the NOTICE file
18045* distributed with this work for additional information
18046* regarding copyright ownership.  The ASF licenses this file
18047* to you under the Apache License, Version 2.0 (the
18048* "License"); you may not use this file except in compliance
18049* with the License.  You may obtain a copy of the License at
18050*
18051*   http://www.apache.org/licenses/LICENSE-2.0
18052*
18053* Unless required by applicable law or agreed to in writing,
18054* software distributed under the License is distributed on an
18055* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18056* KIND, either express or implied.  See the License for the
18057* specific language governing permissions and limitations
18058* under the License.
18059*/
18060
18061    /**
18062     * 姣忎笁浣嶉粯璁ゅ姞,鏍煎紡鍖�
18063     * @param {string|number} x
18064     * @return {string}
18065     */
18066    function addCommas(x) {
18067        if (isNaN(x)) {
18068            return '-';
18069        }
18070        x = (x + '').split('.');
18071        return x[0].replace(/(\d{1,3})(?=(?:\d{3})+(?!\d))/g,'$1,')
18072            + (x.length > 1 ? ('.' + x[1]) : '');
18073    }
18074
18075    /**
18076     * @param {string} str
18077     * @param {boolean} [upperCaseFirst=false]
18078     * @return {string} str
18079     */
18080    function toCamelCase(str, upperCaseFirst) {
18081        str = (str || '').toLowerCase().replace(/-(.)/g, function(match, group1) {
18082            return group1.toUpperCase();
18083        });
18084
18085        if (upperCaseFirst && str) {
18086            str = str.charAt(0).toUpperCase() + str.slice(1);
18087        }
18088
18089        return str;
18090    }
18091
18092    var normalizeCssArray$1 = normalizeCssArray;
18093
18094
18095    var replaceReg = /([&<>"'])/g;
18096    var replaceMap = {
18097        '&': '&amp;',
18098        '<': '&lt;',
18099        '>': '&gt;',
18100        '"': '&quot;',
18101        '\'': '&#39;'
18102    };
18103
18104    function encodeHTML(source) {
18105        return source == null
18106            ? ''
18107            : (source + '').replace(replaceReg, function (str, c) {
18108                return replaceMap[c];
18109            });
18110    }
18111
18112    var TPL_VAR_ALIAS = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
18113
18114    var wrapVar = function (varName, seriesIdx) {
18115        return '{' + varName + (seriesIdx == null ? '' : seriesIdx) + '}';
18116    };
18117
18118    /**
18119     * Template formatter
18120     * @param {string} tpl
18121     * @param {Array.<Object>|Object} paramsList
18122     * @param {boolean} [encode=false]
18123     * @return {string}
18124     */
18125    function formatTpl(tpl, paramsList, encode) {
18126        if (!isArray(paramsList)) {
18127            paramsList = [paramsList];
18128        }
18129        var seriesLen = paramsList.length;
18130        if (!seriesLen) {
18131            return '';
18132        }
18133
18134        var $vars = paramsList[0].$vars || [];
18135        for (var i = 0; i < $vars.length; i++) {
18136            var alias = TPL_VAR_ALIAS[i];
18137            tpl = tpl.replace(wrapVar(alias), wrapVar(alias, 0));
18138        }
18139        for (var seriesIdx = 0; seriesIdx < seriesLen; seriesIdx++) {
18140            for (var k = 0; k < $vars.length; k++) {
18141                var val = paramsList[seriesIdx][$vars[k]];
18142                tpl = tpl.replace(
18143                    wrapVar(TPL_VAR_ALIAS[k], seriesIdx),
18144                    encode ? encodeHTML(val) : val
18145                );
18146            }
18147        }
18148
18149        return tpl;
18150    }
18151
18152    /**
18153     * simple Template formatter
18154     *
18155     * @param {string} tpl
18156     * @param {Object} param
18157     * @param {boolean} [encode=false]
18158     * @return {string}
18159     */
18160    function formatTplSimple(tpl, param, encode) {
18161        each$1(param, function (value, key) {
18162            tpl = tpl.replace(
18163                '{' + key + '}',
18164                encode ? encodeHTML(value) : value
18165            );
18166        });
18167        return tpl;
18168    }
18169
18170    /**
18171     * @param {Object|string} [opt] If string, means color.
18172     * @param {string} [opt.color]
18173     * @param {string} [opt.extraCssText]
18174     * @param {string} [opt.type='item'] 'item' or 'subItem'
18175     * @return {string}
18176     */
18177    function getTooltipMarker(opt, extraCssText) {
18178        opt = isString(opt) ? {color: opt, extraCssText: extraCssText} : (opt || {});
18179        var color = opt.color;
18180        var type = opt.type;
18181        var extraCssText = opt.extraCssText;
18182
18183        if (!color) {
18184            return '';
18185        }
18186
18187        return type === 'subItem'
18188            ? '<span style="display:inline-block;vertical-align:middle;margin-right:8px;margin-left:3px;'
18189            + 'border-radius:4px;width:4px;height:4px;background-color:'
18190            + encodeHTML(color) + ';' + (extraCssText || '') + '"></span>'
18191            : '<span style="display:inline-block;margin-right:5px;'
18192            + 'border-radius:10px;width:10px;height:10px;background-color:'
18193            + encodeHTML(color) + ';' + (extraCssText || '') + '"></span>';
18194    }
18195
18196    function pad(str, len) {
18197        str += '';
18198        return '0000'.substr(0, len - str.length) + str;
18199    }
18200
18201
18202    /**
18203     * ISO Date format
18204     * @param {string} tpl
18205     * @param {number} value
18206     * @param {boolean} [isUTC=false] Default in local time.
18207     *           see `module:echarts/scale/Time`
18208     *           and `module:echarts/util/number#parseDate`.
18209     * @inner
18210     */
18211    function formatTime(tpl, value, isUTC) {
18212        if (tpl === 'week'
18213            || tpl === 'month'
18214            || tpl === 'quarter'
18215            || tpl === 'half-year'
18216            || tpl === 'year'
18217        ) {
18218            tpl = 'MM-dd\nyyyy';
18219        }
18220
18221        var date = parseDate(value);
18222        var utc = isUTC ? 'UTC' : '';
18223        var y = date['get' + utc + 'FullYear']();
18224        var M = date['get' + utc + 'Month']() + 1;
18225        var d = date['get' + utc + 'Date']();
18226        var h = date['get' + utc + 'Hours']();
18227        var m = date['get' + utc + 'Minutes']();
18228        var s = date['get' + utc + 'Seconds']();
18229        var S = date['get' + utc + 'Milliseconds']();
18230
18231        tpl = tpl.replace('MM', pad(M, 2))
18232            .replace('M', M)
18233            .replace('yyyy', y)
18234            .replace('yy', y % 100)
18235            .replace('dd', pad(d, 2))
18236            .replace('d', d)
18237            .replace('hh', pad(h, 2))
18238            .replace('h', h)
18239            .replace('mm', pad(m, 2))
18240            .replace('m', m)
18241            .replace('ss', pad(s, 2))
18242            .replace('s', s)
18243            .replace('SSS', pad(S, 3));
18244
18245        return tpl;
18246    }
18247
18248    /**
18249     * Capital first
18250     * @param {string} str
18251     * @return {string}
18252     */
18253    function capitalFirst(str) {
18254        return str ? str.charAt(0).toUpperCase() + str.substr(1) : str;
18255    }
18256
18257    var truncateText$1 = truncateText;
18258
18259    var getTextRect = getBoundingRect;
18260
18261
18262    var format = (Object.freeze || Object)({
18263        addCommas: addCommas,
18264        toCamelCase: toCamelCase,
18265        normalizeCssArray: normalizeCssArray$1,
18266        encodeHTML: encodeHTML,
18267        formatTpl: formatTpl,
18268        formatTplSimple: formatTplSimple,
18269        getTooltipMarker: getTooltipMarker,
18270        formatTime: formatTime,
18271        capitalFirst: capitalFirst,
18272        truncateText: truncateText$1,
18273        getTextRect: getTextRect
18274    });
18275
18276    /*
18277* Licensed to the Apache Software Foundation (ASF) under one
18278* or more contributor license agreements.  See the NOTICE file
18279* distributed with this work for additional information
18280* regarding copyright ownership.  The ASF licenses this file
18281* to you under the Apache License, Version 2.0 (the
18282* "License"); you may not use this file except in compliance
18283* with the License.  You may obtain a copy of the License at
18284*
18285*   http://www.apache.org/licenses/LICENSE-2.0
18286*
18287* Unless required by applicable law or agreed to in writing,
18288* software distributed under the License is distributed on an
18289* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18290* KIND, either express or implied.  See the License for the
18291* specific language governing permissions and limitations
18292* under the License.
18293*/
18294
18295// Layout helpers for each component positioning
18296
18297    var each$3 = each$1;
18298
18299    /**
18300     * @public
18301     */
18302    var LOCATION_PARAMS = [
18303        'left', 'right', 'top', 'bottom', 'width', 'height'
18304    ];
18305
18306    /**
18307     * @public
18308     */
18309    var HV_NAMES = [
18310        ['width', 'left', 'right'],
18311        ['height', 'top', 'bottom']
18312    ];
18313
18314    function boxLayout(orient, group, gap, maxWidth, maxHeight) {
18315        var x = 0;
18316        var y = 0;
18317
18318        if (maxWidth == null) {
18319            maxWidth = Infinity;
18320        }
18321        if (maxHeight == null) {
18322            maxHeight = Infinity;
18323        }
18324        var currentLineMaxSize = 0;
18325
18326        group.eachChild(function (child, idx) {
18327            var position = child.position;
18328            var rect = child.getBoundingRect();
18329            var nextChild = group.childAt(idx + 1);
18330            var nextChildRect = nextChild && nextChild.getBoundingRect();
18331            var nextX;
18332            var nextY;
18333
18334            if (orient === 'horizontal') {
18335                var moveX = rect.width + (nextChildRect ? (-nextChildRect.x + rect.x) : 0);
18336                nextX = x + moveX;
18337                // Wrap when width exceeds maxWidth or meet a `newline` group
18338                // FIXME compare before adding gap?
18339                if (nextX > maxWidth || child.newline) {
18340                    x = 0;
18341                    nextX = moveX;
18342                    y += currentLineMaxSize + gap;
18343                    currentLineMaxSize = rect.height;
18344                }
18345                else {
18346                    // FIXME: consider rect.y is not `0`?
18347                    currentLineMaxSize = Math.max(currentLineMaxSize, rect.height);
18348                }
18349            }
18350            else {
18351                var moveY = rect.height + (nextChildRect ? (-nextChildRect.y + rect.y) : 0);
18352                nextY = y + moveY;
18353                // Wrap when width exceeds maxHeight or meet a `newline` group
18354                if (nextY > maxHeight || child.newline) {
18355                    x += currentLineMaxSize + gap;
18356                    y = 0;
18357                    nextY = moveY;
18358                    currentLineMaxSize = rect.width;
18359                }
18360                else {
18361                    currentLineMaxSize = Math.max(currentLineMaxSize, rect.width);
18362                }
18363            }
18364
18365            if (child.newline) {
18366                return;
18367            }
18368
18369            position[0] = x;
18370            position[1] = y;
18371
18372            orient === 'horizontal'
18373                ? (x = nextX + gap)
18374                : (y = nextY + gap);
18375        });
18376    }
18377
18378    /**
18379     * VBox or HBox layouting
18380     * @param {string} orient
18381     * @param {module:zrender/container/Group} group
18382     * @param {number} gap
18383     * @param {number} [width=Infinity]
18384     * @param {number} [height=Infinity]
18385     */
18386    var box = boxLayout;
18387
18388    /**
18389     * VBox layouting
18390     * @param {module:zrender/container/Group} group
18391     * @param {number} gap
18392     * @param {number} [width=Infinity]
18393     * @param {number} [height=Infinity]
18394     */
18395    var vbox = curry(boxLayout, 'vertical');
18396
18397    /**
18398     * HBox layouting
18399     * @param {module:zrender/container/Group} group
18400     * @param {number} gap
18401     * @param {number} [width=Infinity]
18402     * @param {number} [height=Infinity]
18403     */
18404    var hbox = curry(boxLayout, 'horizontal');
18405
18406    /**
18407     * If x or x2 is not specified or 'center' 'left' 'right',
18408     * the width would be as long as possible.
18409     * If y or y2 is not specified or 'middle' 'top' 'bottom',
18410     * the height would be as long as possible.
18411     *
18412     * @param {Object} positionInfo
18413     * @param {number|string} [positionInfo.x]
18414     * @param {number|string} [positionInfo.y]
18415     * @param {number|string} [positionInfo.x2]
18416     * @param {number|string} [positionInfo.y2]
18417     * @param {Object} containerRect {width, height}
18418     * @param {string|number} margin
18419     * @return {Object} {width, height}
18420     */
18421    function getAvailableSize(positionInfo, containerRect, margin) {
18422        var containerWidth = containerRect.width;
18423        var containerHeight = containerRect.height;
18424
18425        var x = parsePercent$1(positionInfo.x, containerWidth);
vendor: 4,578 bytes, lines 18426-18541
18426        var y = parsePercent$1(positionInfo.y, containerHeight);
18427        var x2 = parsePercent$1(positionInfo.x2, containerWidth);
18428        var y2 = parsePercent$1(positionInfo.y2, containerHeight);
18429
18430        (isNaN(x) || isNaN(parseFloat(positionInfo.x))) && (x = 0);
18431        (isNaN(x2) || isNaN(parseFloat(positionInfo.x2))) && (x2 = containerWidth);
18432        (isNaN(y) || isNaN(parseFloat(positionInfo.y))) && (y = 0);
18433        (isNaN(y2) || isNaN(parseFloat(positionInfo.y2))) && (y2 = containerHeight);
18434
18435        margin = normalizeCssArray$1(margin || 0);
18436
18437        return {
18438            width: Math.max(x2 - x - margin[1] - margin[3], 0),
18439            height: Math.max(y2 - y - margin[0] - margin[2], 0)
18440        };
18441    }
18442
18443    /**
18444     * Parse position info.
18445     *
18446     * @param {Object} positionInfo
18447     * @param {number|string} [positionInfo.left]
18448     * @param {number|string} [positionInfo.top]
18449     * @param {number|string} [positionInfo.right]
18450     * @param {number|string} [positionInfo.bottom]
18451     * @param {number|string} [positionInfo.width]
18452     * @param {number|string} [positionInfo.height]
18453     * @param {number|string} [positionInfo.aspect] Aspect is width / height
18454     * @param {Object} containerRect
18455     * @param {string|number} [margin]
18456     *
18457     * @return {module:zrender/core/BoundingRect}
18458     */
18459    function getLayoutRect(
18460        positionInfo, containerRect, margin
18461    ) {
18462        margin = normalizeCssArray$1(margin || 0);
18463
18464        var containerWidth = containerRect.width;
18465        var containerHeight = containerRect.height;
18466
18467        var left = parsePercent$1(positionInfo.left, containerWidth);
18468        var top = parsePercent$1(positionInfo.top, containerHeight);
18469        var right = parsePercent$1(positionInfo.right, containerWidth);
18470        var bottom = parsePercent$1(positionInfo.bottom, containerHeight);
18471        var width = parsePercent$1(positionInfo.width, containerWidth);
18472        var height = parsePercent$1(positionInfo.height, containerHeight);
18473
18474        var verticalMargin = margin[2] + margin[0];
18475        var horizontalMargin = margin[1] + margin[3];
18476        var aspect = positionInfo.aspect;
18477
18478        // If width is not specified, calculate width from left and right
18479        if (isNaN(width)) {
18480            width = containerWidth - right - horizontalMargin - left;
18481        }
18482        if (isNaN(height)) {
18483            height = containerHeight - bottom - verticalMargin - top;
18484        }
18485
18486        if (aspect != null) {
18487            // If width and height are not given
18488            // 1. Graph should not exceeds the container
18489            // 2. Aspect must be keeped
18490            // 3. Graph should take the space as more as possible
18491            // FIXME
18492            // Margin is not considered, because there is no case that both
18493            // using margin and aspect so far.
18494            if (isNaN(width) && isNaN(height)) {
18495                if (aspect > containerWidth / containerHeight) {
18496                    width = containerWidth * 0.8;
18497                }
18498                else {
18499                    height = containerHeight * 0.8;
18500                }
18501            }
18502
18503            // Calculate width or height with given aspect
18504            if (isNaN(width)) {
18505                width = aspect * height;
18506            }
18507            if (isNaN(height)) {
18508                height = width / aspect;
18509            }
18510        }
18511
18512        // If left is not specified, calculate left from right and width
18513        if (isNaN(left)) {
18514            left = containerWidth - right - width - horizontalMargin;
18515        }
18516        if (isNaN(top)) {
18517            top = containerHeight - bottom - height - verticalMargin;
18518        }
18519
18520        // Align left and top
18521        switch (positionInfo.left || positionInfo.right) {
18522            case 'center':
18523                left = containerWidth / 2 - width / 2 - margin[3];
18524                break;
18525            case 'right':
18526                left = containerWidth - width - horizontalMargin;
18527                break;
18528        }
18529        switch (positionInfo.top || positionInfo.bottom) {
18530            case 'middle':
18531            case 'center':
18532                top = containerHeight / 2 - height / 2 - margin[0];
18533                break;
18534            case 'bottom':
18535                top = containerHeight - height - verticalMargin;
18536                break;
18537        }
18538        // If something is wrong and left, top, width, height are calculated as NaN
18539        left = left || 0;
18540        top = top || 0;
18541        if (isNaN(width)) {
18542            // Width may be NaN if only one value is given except width
18543            width = containerWidth - horizontalMargin - left - (right || 0);
18544        }
18545        if (isNaN(height)) {
18546            // Height may be NaN if only one value is given except height
18547            height = containerHeight - verticalMargin - top - (bottom || 0);
18548        }
18549
18550        var rect = new BoundingRect(left + margin[3], top + margin[0], width, height);
18551        rect.margin = margin;
18552        return rect;
18553    }
18554
18555
18556    /**
18557     * Position a zr element in viewport
18558     *  Group position is specified by either
18559     *  {left, top}, {right, bottom}
18560     *  If all properties exists, right and bottom will be igonred.
18561     *
18562     * Logic:
18563     *     1. Scale (against origin point in parent coord)
18564     *     2. Rotate (against origin point in parent coord)
18565     *     3. Traslate (with el.position by this method)
18566     * So this method only fixes the last step 'Traslate', which does not affect
18567     * scaling and rotating.
18568     *
18569     * If be called repeatly with the same input el, the same result will be gotten.
18570     *
18571     * @param {module:zrender/Element} el Should have `getBoundingRect` method.
18572     * @param {Object} positionInfo
18573     * @param {number|string} [positionInfo.left]
18574     * @param {number|string} [positionInfo.top]
18575     * @param {number|string} [positionInfo.right]
18576     * @param {number|string} [positionInfo.bottom]
18577     * @param {number|string} [positionInfo.width] Only for opt.boundingModel: 'raw'
18578     * @param {number|string} [positionInfo.height] Only for opt.boundingModel: 'raw'
18579     * @param {Object} containerRect
18580     * @param {string|number} margin
18581     * @param {Object} [opt]
18582     * @param {Array.<number>} [opt.hv=[1,1]] Only horizontal or only vertical.
18583     * @param {Array.<number>} [opt.boundingMode='all']
18584     *        Specify how to calculate boundingRect when locating.
18585     *        'all': Position the boundingRect that is transformed and uioned
18586     *               both itself and its descendants.
18587     *               This mode simplies confine the elements in the bounding
18588     *               of their container (e.g., using 'right: 0').
18589     *        'raw': Position the boundingRect that is not transformed and only itself.
18590     *               This mode is useful when you want a element can overflow its
18591     *               container. (Consider a rotated circle needs to be located in a corner.)
18592     *               In this mode positionInfo.width/height can only be number.
18593     */
18594    function positionElement(el, positionInfo, containerRect, margin, opt) {
18595        var h = !opt || !opt.hv || opt.hv[0];
18596        var v = !opt || !opt.hv || opt.hv[1];
18597        var boundingMode = opt && opt.boundingMode || 'all';
18598
18599        if (!h && !v) {
18600            return;
18601        }
18602
18603        var rect;
18604        if (boundingMode === 'raw') {
18605            rect = el.type === 'group'
18606                ? new BoundingRect(0, 0, +positionInfo.width || 0, +positionInfo.height || 0)
18607                : el.getBoundingRect();
18608        }
18609        else {
18610            rect = el.getBoundingRect();
18611            if (el.needLocalTransform()) {
18612                var transform = el.getLocalTransform();
18613                // Notice: raw rect may be inner object of el,
18614                // which should not be modified.
18615                rect = rect.clone();
18616                rect.applyTransform(transform);
18617            }
18618        }
18619
18620        // The real width and height can not be specified but calculated by the given el.
18621        positionInfo = getLayoutRect(
18622            defaults(
18623                {width: rect.width, height: rect.height},
18624                positionInfo
18625            ),
18626            containerRect,
18627            margin
18628        );
18629
18630        // Because 'tranlate' is the last step in transform
18631        // (see zrender/core/Transformable#getLocalTransform),
18632        // we can just only modify el.position to get final result.
18633        var elPos = el.position;
18634        var dx = h ? positionInfo.x - rect.x : 0;
18635        var dy = v ? positionInfo.y - rect.y : 0;
18636
18637        el.attr('position', boundingMode === 'raw' ? [dx, dy] : [elPos[0] + dx, elPos[1] + dy
vendor: 5,464 bytes, lines 18637-18783
18637]);
18638    }
18639
18640    /**
18641     * @param {Object} option Contains some of the properties in HV_NAMES.
18642     * @param {number} hvIdx 0: horizontal; 1: vertical.
18643     */
18644    function sizeCalculable(option, hvIdx) {
18645        return option[HV_NAMES[hvIdx][0]] != null
18646            || (option[HV_NAMES[hvIdx][1]] != null && option[HV_NAMES[hvIdx][2]] != null);
18647    }
18648
18649    /**
18650     * Consider Case:
18651     * When defulat option has {left: 0, width: 100}, and we set {right: 0}
18652     * through setOption or media query, using normal zrUtil.merge will cause
18653     * {right: 0} does not take effect.
18654     *
18655     * @example
18656     * ComponentModel.extend({
18657 *     init: function () {
18658 *         ...
18659 *         var inputPositionParams = layout.getLayoutParams(option);
18660 *         this.mergeOption(inputPositionParams);
18661 *     },
18662 *     mergeOption: function (newOption) {
18663 *         newOption && zrUtil.merge(thisOption, newOption, true);
18664 *         layout.mergeLayoutParam(thisOption, newOption);
18665 *     }
18666 * });
18667     *
18668     * @param {Object} targetOption
18669     * @param {Object} newOption
18670     * @param {Object|string} [opt]
18671     * @param {boolean|Array.<boolean>} [opt.ignoreSize=false] Used for the components
18672     *  that width (or height) should not be calculated by left and right (or top and bottom).
18673     */
18674    function mergeLayoutParam(targetOption, newOption, opt) {
18675        !isObject$1(opt) && (opt = {});
18676
18677        var ignoreSize = opt.ignoreSize;
18678        !isArray(ignoreSize) && (ignoreSize = [ignoreSize, ignoreSize]);
18679
18680        var hResult = merge$$1(HV_NAMES[0], 0);
18681        var vResult = merge$$1(HV_NAMES[1], 1);
18682
18683        copy(HV_NAMES[0], targetOption, hResult);
18684        copy(HV_NAMES[1], targetOption, vResult);
18685
18686        function merge$$1(names, hvIdx) {
18687            var newParams = {};
18688            var newValueCount = 0;
18689            var merged = {};
18690            var mergedValueCount = 0;
18691            var enoughParamNumber = 2;
18692
18693            each$3(names, function (name) {
18694                merged[name] = targetOption[name];
18695            });
18696            each$3(names, function (name) {
18697                // Consider case: newOption.width is null, which is
18698                // set by user for removing width setting.
18699                hasProp(newOption, name) && (newParams[name] = merged[name] = newOption[name]);
18700                hasValue(newParams, name) && newValueCount++;
18701                hasValue(merged, name) && mergedValueCount++;
18702            });
18703
18704            if (ignoreSize[hvIdx]) {
18705                // Only one of left/right is premitted to exist.
18706                if (hasValue(newOption, names[1])) {
18707                    merged[names[2]] = null;
18708                }
18709                else if (hasValue(newOption, names[2])) {
18710                    merged[names[1]] = null;
18711                }
18712                return merged;
18713            }
18714
18715            // Case: newOption: {width: ..., right: ...},
18716            // or targetOption: {right: ...} and newOption: {width: ...},
18717            // There is no conflict when merged only has params count
18718            // little than enoughParamNumber.
18719            if (mergedValueCount === enoughParamNumber || !newValueCount) {
18720                return merged;
18721            }
18722            // Case: newOption: {width: ..., right: ...},
18723            // Than we can make sure user only want those two, and ignore
18724            // all origin params in targetOption.
18725            else if (newValueCount >= enoughParamNumber) {
18726                return newParams;
18727            }
18728            else {
18729                // Chose another param from targetOption by priority.
18730                for (var i = 0; i < names.length; i++) {
18731                    var name = names[i];
18732                    if (!hasProp(newParams, name) && hasProp(targetOption, name)) {
18733                        newParams[name] = targetOption[name];
18734                        break;
18735                    }
18736                }
18737                return newParams;
18738            }
18739        }
18740
18741        function hasProp(obj, name) {
18742            return obj.hasOwnProperty(name);
18743        }
18744
18745        function hasValue(obj, name) {
18746            return obj[name] != null && obj[name] !== 'auto';
18747        }
18748
18749        function copy(names, target, source) {
18750            each$3(names, function (name) {
18751                target[name] = source[name];
18752            });
18753        }
18754    }
18755
18756    /**
18757     * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object.
18758     * @param {Object} source
18759     * @return {Object} Result contains those props.
18760     */
18761    function getLayoutParams(source) {
18762        return copyLayoutParams({}, source);
18763    }
18764
18765    /**
18766     * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object.
18767     * @param {Object} source
18768     * @return {Object} Result contains those props.
18769     */
18770    function copyLayoutParams(target, source) {
18771        source && target && each$3(LOCATION_PARAMS, function (name) {
18772            source.hasOwnProperty(name) && (target[name] = source[name]);
18773        });
18774        return target;
18775    }
18776
18777    /*
18778* Licensed to the Apache Software Foundation (ASF) under one
18779* or more contributor license agreements.  See the NOTICE file
18780* distributed with this work for additional information
18781* regarding copyright ownership.  The ASF licenses this file
18782* to you under the Apache License, Version 2.0 (the
18783* "License");
18783 you may not use this file except in compliance
18784* with the License.  You may obtain a copy of the License at
18785*
18786*   http://www.apache.org/licenses/LICENSE-2.0
18787*
18788* Unless required by applicable law or agreed to in writing,
18789* software distributed under the License is distributed on an
18790* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18791* KIND, either express or implied.  See the License for the
18792* specific language governing permissions and limitations
18793* under the License.
18794*/
18795
18796
18797    var boxLayoutMixin = {
18798        getBoxLayoutParams: function () {
18799            return {
18800                left: this.get('left'),
18801                top: this.get('top'),
18802                right: this.get('right'),
18803                bottom: this.get('bottom'),
18804                width: this.get('width'),
18805                height: this.get('height')
18806            };
18807        }
18808    };
18809
18810    /*
18811* Licensed to the Apache Software Foundation (ASF) under one
18812* or more contributor license agreements.  See the NOTICE file
18813* distributed with this work for additional information
18814* regarding copyright ownership.  The ASF licenses this file
18815* to you under the Apache License, Version 2.0 (the
18816* "License"); you may not use this file except in compliance
18817* with the License.  You may obtain a copy of the License at
18818*
18819*   http://www.apache.org/licenses/LICENSE-2.0
18820*
18821* Unless required by applicable law or agreed to in writing,
18822* software distributed under the License is distributed on an
18823* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18824* KIND, either express or implied.  See the License for the
18825* specific language governing permissions and limitations
18826* under the License.
18827*/
18828
18829    /**
18830     * Component model
18831     *
18832     * @module echarts/model/Component
18833     */
18834
18835    var inner$1 = makeInner();
18836
18837    /**
18838     * @alias module:echarts/model/Component
18839     * @constructor
18840     * @param {Object} option
18841     * @param {module:echarts/model/Model} parentModel
18842     * @param {module:echarts/model/Model} ecModel
18843     */
18844    var ComponentModel = Model.extend({
18845
18846        type: 'component',
18847
18848        /**
18849         * @readOnly
18850         * @type {string}
18851         */
18852        id: '',
18853
18854        /**
18855         * Because simplified concept is probably better, series.name (or component.name)
18856         * has been having too many resposibilities:
18857         * (1) Generating id (which requires name in option should not be modified).
18858         * (2) As an index to mapping series when merging option or calling API (a name
18859         * can refer to more then one components, which is convinient is some case).
18860         * (3) Display.
18861         * @readOnly
18862         */
18863        name: '',
18864
18865        /**
18866         * @readOnly
18867         * @type {string}
18868         */
18869        mainType: '',
18870
18871        /**
18872         * @readOnly
18873         * @type {string}
18874         */
18875        subType: '',
18876
18877        /**
18878         * @readOnly
18879         * @type {number}
18880         */
18881        componentIndex: 0,
18882
18883        /**
18884         * @type {Object}
18885         * @protected
18886         */
18887        defaultOption: null,
18888
18889        /**
18890         * @type {module:echarts/model/Global}
18891         * @readOnly
18892         */
18893        ecModel: null,
18894
18895        /**
18896         * key: componentType
18897         * value:  Component model list, can not be null.
18898         * @type {Object.<string, Array.<module:echarts/model/Model>>}
18899         * @readOnly
18900         */
18901        dependentModels: [],
18902
18903        /**
18904         * @type {string}
18905         * @readOnly
18906         */
18907        uid: null,
18908
18909        /**
18910         * Support merge layout params.
18911         * Only support 'box' now (left/right/top/bottom/width/height).
18912         * @type {string|Object} Object can be {ignoreSize: true}
18913         * @readOnly
18914         */
18915        layoutMode: null,
18916
18917        $constructor: function (option, parentModel, ecModel, extraOpt) {
18918            Model.call(this, option, parentModel, ecModel, extraOpt);
18919
18920            this.uid = getUID('ec_cpt_model');
18921        },
18922
18923        init: function (option, parentModel, ecModel, extraOpt) {
18924            this.mergeDefaultAndTheme(option, ecModel);
18925        },
18926
18927        mergeDefaultAndTheme: function (option, ecModel) {
18928            var layoutMode = this.layoutMode;
18929            var inputPositionParams = layoutMode
18930                ? getLayoutParams(option) : {};
18931
18932            var themeModel = ecModel.getTheme();
18933            merge(option, themeModel.get(this.mainType));
18934            merge(option, this.getDefaultOption());
18935
18936            if (layoutMode) {
18937                mergeLayoutParam(option, inputPositionParams, layoutMode);
18938            }
18939        },
18940
18941        mergeOption: function (option, extraOpt) {
18942            merge(this.option, option, true);
18943
18944            var layoutMode = this.layoutMode;
18945            if (layoutMode) {
18946                mergeLayoutParam(this.option, option, layoutMode);
18947            }
18948        },
18949
18950        // Hooker after init or mergeOption
18951        optionUpdated: function (newCptOption, isInit) {},
18952
18953        getDefaultOption: function () {
18954            var fields = inner$1(this);
18955            if (!fields.defaultOption) {
18956                var optList = [];
18957                var Class = this.constructor;
18958                while (Class) {
18959                    var opt = Class.prototype.defaultOption;
18960                    opt && optList.push(opt);
18961                    Class = Class.superClass;
18962                }
18963
18964                var defaultOption = {};
18965                for (var i = optList.length - 1; i >= 0; i--) {
18966                    defaultOption = merge(defaultOption, optList[i], true);
18967                }
18968                fields.defaultOption = defaultOption;
18969            }
18970            return fields.defaultOption;
18971        },
18972
18973        getReferringComponents: function (mainType) {
18974            return this.ecModel.queryComponents({
18975                mainType: mainType,
18976                index: this.get(mainType + 'Index', true),
18977                id: this.get(mainType + 'Id', true)
18978            });
18979        }
18980
18981    });
18982
18983// Reset ComponentModel.extend, add preConstruct.
18984// clazzUtil.enableClassExtend(
18985//     ComponentModel,
18986//     function (option, parentModel, ecModel, extraOpt) {
18987//         // Set dependentModels, componentIndex, name, id, mainType, subType.
18988//         zrUtil.extend(this, extraOpt);
18989
18990//         this.uid = componentUtil.getUID('componentModel');
18991
18992//         // this.setReadOnly([
18993//         //     'type', 'id', 'uid', 'name', 'mainType', 'subType',
18994//         //     'dependentModels', 'componentIndex'
18995//         // ]);
18996//     }
18997// );
18998
18999// Add capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on.
19000    enableClassManagement(
19001        ComponentModel, {registerWhenExtend: true}
19002    );
19003    enableSubTypeDefaulter(ComponentModel);
19004
19005// Add capability of ComponentModel.topologicalTravel.
19006    enableTopologicalTravel(ComponentModel, getDependencies);
19007
19008    function getDependencies(componentType) {
19009        var deps = [];
19010        each$1(ComponentModel.getClassesByMainType(componentType), function (Clazz) {
19011            deps = deps.concat(Clazz.prototype.dependencies || []);
19012        });
19013
19014        // Ensure main type.
19015        deps = map(deps, function (type) {
19016            return parseClassType$1(type).main;
19017        });
19018
19019        // Hack dataset for convenience.
19020        if (componentType !== 'dataset' && indexOf(deps, 'dataset') <= 0) {
19021            deps.unshift('dataset');
19022        }
19023
19024        return deps;
19025    }
19026
19027    mixin(ComponentModel, boxLayoutMixin);
19028
19029    /*
19030* Licensed to the Apache Software Foundation (ASF) under one
19031* or more contributor license agreements.  See the NOTICE file
19032* distributed with this work for additional information
19033* regarding copyright ownership.  The ASF licenses this file
19034* to you under the Apache License, Version 2.0 (the
19035* "License"); you may not use this file except in compliance
19036* with the License.  You may obtain a copy of the License at
19037*
19038*   http://www.apache.org/licenses/LICENSE-2.0
19039*
19040* Unless required by applicable law or agreed to in writing,
19041* software distributed under the License is distributed on an
19042* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
19043* KIND, either express or implied.  See the License for the
19044* specific language governing permissions and limitations
19045* under the License.
19046*/
19047
19048
19049    var platform = '';
19050// Navigator not exists in node
19051    if (typeof navigator !== 'undefined') {
19052        platform = navigator.platform || '';
19053    }
19054
19055    var globalDefault = {
19056        // backgroundColor: 'rgba(0,0,0,0)',
19057
19058        // https://dribbble.com/shots/1065960-Infographic-Pie-chart-visualization
19059        // color: ['#5793f3', '#d14a61', '#fd9c35', '#675bba', '#fec42c', '#dd4444', '#d4df5a', '#cd4870'],
19060        // Light colors:
19061        // color: ['#bcd3bb', '#e88f70', '#edc1a5', '#9dc5c8', '#e1e8c8', '#7b7c68', '#e5b5b5', '#f0b489', '#928ea8', '#bda29a'],
19062        // color: ['#cc5664', '#9bd6ec', '#ea946e', '#8acaaa', '#f1ec64', '#ee8686', '#a48dc1', '#5da6bc', '#b9dcae'],
19063        // Dark colors:
19064        color: ['#c23531','#2f4554', '#61a0a8', '#d48265', '#91c7ae','#749f83',  '#ca8622', '#bda29a','#6e7074', '#546570', '#c4ccd3'],
19065
19066        gradientColor: ['#f6efa6', '#d88273', '#bf444c'],
19067
19068        // If xAxis and yAxis declared, grid is created by default.
19069        // grid: {},
19070
19071        textStyle: {
19072            // color: '#000',
19073            // decoration: 'none',
19074            // PENDING
19075            fontFamily: platform.match(/^Win/) ? 'Microsoft YaHei' : 'sans-serif',
19076            // fontFamily: 'Arial, Verdana, sans-serif',
19077            fontSize: 12,
19078            fontStyle: 'normal',
19079            fontWeight: 'normal'
19080        },
19081
19082        // http://blogs.adobe.com/webplatform/2014/02/24/using-blend-modes-in-html-canvas/
19083        // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
19084        // Default is source-over
19085        blendMode: null,
19086
19087        animation: 'auto',
19088        animationDuration: 1000,
19089        animationDurationUpdate: 300,
19090        animationEasing: 'exponentialOut',
19091        animationEasingUpdate: 'cubicOut',
19092
19093        animationThreshold: 2000,
19094        // Configuration for progressive/incremental rendering
19095        progressiveThreshold: 3000,
19096        progressive: 400,
19097
19098        // Threshold of if use single hover layer to optimize.
19099        // It is recommended that `hoverLayerThreshold` is equivalent to or less than
19100        // `progressiveThreshold`, otherwise hover will cause restart of progressive,
19101        // which is unexpected.
19102        // see example <echarts/test/heatmap-large.html>.
19103        hoverLayerThreshold: 3000,
19104
19105        // See: module:echarts/scale/Time
19106        useUTC: false
19107    };
19108
19109    /*
19110* Licensed to the Apache Software Foundation (ASF) under one
19111* or more contributor license agreements.  See the NOTICE file
19112* distributed with this work for additional information
19113* regarding copyright ownership.  The ASF licenses this file
19114* to you under the Apache License, Version 2.0 (the
19115* "License"); you may not use this file except in compliance
19116* with the License.  You may obtain a copy of the License at
19117*
19118*   http://www.apache.org/licenses/LICENSE-2.0
19119*
19120* Unless required by applicable law or agreed to in writing,
19121* software distributed under the License is distributed on an
19122* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
19123* KIND, either express or implied.  See the License for the
19124* specific language governing permissions and limitations
19125* under the License.
19126*/
19127
19128    var inner$2 = makeInner();
19129
19130    function getNearestColorPalette(colors, requestColorNum) {
19131        var paletteNum = colors.length;
19132        // TODO colors must be in order
19133        for (var i = 0; i < paletteNum; i++) {
19134            if (colors[i].length > requestColorNum) {
19135                return colors[i];
19136            }
19137        }
19138        return colors[paletteNum - 1];
19139    }
19140
19141    var colorPaletteMixin = {
19142        clearColorPalette: function () {
19143            inner$2(this).colorIdx = 0;
19144            inner$2(this).colorNameMap = {};
19145        },
19146
19147        /**
19148         * @param {string} name MUST NOT be null/undefined. Otherwise call this function
19149         *                 twise with the same parameters will get different result.
19150         * @param {Object} [scope=this]
19151         * @param {Object} [requestColorNum]
19152         * @return {string} color string.
19153         */
19154        getColorFromPalette: function (name, scope, requestColorNum) {
19155            scope = scope || this;
19156            var scopeFields = inner$2(scope);
19157            var colorIdx = scopeFields.colorIdx || 0;
19158            var colorNameMap = scopeFields.colorNameMap = scopeFields.colorNameMap || {};
19159            // Use `hasOwnProperty` to avoid conflict with Object.prototype.
19160            if (colorNameMap.hasOwnProperty(name)) {
19161                return colorNameMap[name];
19162            }
19163            var defaultColorPalette = normalizeToArray(this.get('color', true));
19164            var layeredColorPalette = this.get('colorLayer', true);
19165            var colorPalette = ((requestColorNum == null || !layeredColorPalette)
19166                ? defaultColorPalette : getNearestColorPalette(layeredColorPalette, requestColorNum));
19167
19168            // In case can't find in layered color palette.
19169            colorPalette = colorPalette || defaultColorPalette;
19170
19171            if (!colorPalette || !colorPalette.length) {
19172                return;
19173            }
19174
19175            var color = colorPalette[colorIdx];
19176            if (name) {
19177                colorNameMap[name] = color;
19178            }
19179            scopeFields.colorIdx = (colorIdx + 1) % colorPalette.length;
19180
19181            return color;
19182        }
19183    };
19184
19185    /*
19186* Licensed to the Apache Software Foundation (ASF) under one
19187* or more contributor license agreements.  See the NOTICE file
19188* distributed with this work for additional information
19189* regarding copyright ownership.  The ASF licenses this file
19190* to you under the Apache License, Version 2.0 (the
19191* "License"); you may not use this file except in compliance
19192* with the License.  You may obtain a copy of the License at
19193*
19194*   http://www.apache.org/licenses/LICENSE-2.0
19195*
19196* Unless required by applicable law or agreed to in writing,
19197* software distributed under the License is distributed on an
19198* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
19199* KIND, either express or implied.  See the License for the
19200* specific language governing permissions and limitations
19201* under the License.
19202*/
19203
19204    /**
19205     * Helper for model references.
19206     * There are many manners to refer axis/coordSys.
19207     */
19208
19209// TODO
19210// merge relevant logic to this file?
19211// check: "modelHelper" of tooltip and "BrushTargetManager".
19212
19213    /**
19214     * @return {Object} For example:
19215     * {
19216 *     coordSysName: 'cartesian2d',
19217 *     coordSysDims: ['x', 'y', ...],
19218 *     axisMap: HashMap({
19219 *         x: xAxisModel,
19220 *         y: yAxisModel
19221 *     }),
19222 *     categoryAxisMap: HashMap({
19223 *         x: xAxisModel,
19224 *         y: undefined
19225 *     }),
19226 *     // It also indicate that whether there is category axis.
19227 *     firstCategoryDimIndex: 1,
19228 *     // To replace user specified encode.
19229 * }
19230     */
19231    function getCoordSysDefineBySeries(seriesModel) {
19232        var coordSysName = seriesModel.get('coordinateSystem');
19233        var result = {
19234            coordSysName: coordSysName,
19235            coordSysDims: [],
19236            axisMap: createHashMap(),
19237            categoryAxisMap: createHashMap()
19238        };
19239        var fetch = fetchers[coordSysName];
19240        if (fetch) {
19241            fetch(seriesModel, result, result.axisMap, result.categoryAxisMap);
19242            return result;
19243        }
19244    }
19245
19246    var fetchers = {
19247
19248        cartesian2d: function (seriesModel, result, axisMap, categoryAxisMap) {
19249            var xAxisModel = seriesModel.getReferringComponents('xAxis')[0];
19250            var yAxisModel = seriesModel.getReferringComponents('yAxis')[0];
19251
19252            if (__DEV__) {
19253                if (!xAxisModel) {
19254                    throw new Error('xAxis "' + retrieve(
19255                        seriesModel.get('xAxisIndex'),
19256                        seriesModel.get('xAxisId'),
19257                        0
19258                    ) + '" not found');
19259                }
19260                if (!yAxisModel) {
19261                    throw new Error('yAxis "' + retrieve(
19262                        seriesModel.get('xAxisIndex'),
19263                        seriesModel.get('yAxisId'),
19264                        0
19265                    ) + '" not found');
19266                }
19267            }
19268
19269            result.coordSysDims = ['x', 'y'];
19270            axisMap.set('x', xAxisModel);
19271            axisMap.set('y', yAxisModel);
19272
19273            if (isCategory(xAxisModel)) {
19274                categoryAxisMap.set('x', xAxisModel);
19275                result.firstCategoryDimIndex = 0;
19276            }
19277            if (isCategory(yAxisModel)) {
19278                categoryAxisMap.set('y', yAxisModel);
19279                result.firstCategoryDimIndex = 1;
19280            }
19281        },
19282
19283        singleAxis: function (seriesModel, result, axisMap, categoryAxisMap) {
19284            var singleAxisModel = seriesModel.getReferringComponents('singleAxis')[0];
19285
19286            if (__DEV__) {
19287                if (!singleAxisModel) {
19288                    throw new Error('singleAxis should be specified.');
19289                }
19290            }
19291
19292            result.coordSysDims = ['single'];
19293            axisMap.set('single', singleAxisModel);
19294
19295            if (isCategory(singleAxisModel)) {
19296                categoryAxisMap.set('single', singleAxisModel);
19297                result.firstCategoryDimIndex = 0;
19298            }
19299        },
19300
19301        polar: function (seriesModel, result, axisMap, categoryAxisMap) {
19302            var polarModel = seriesModel.getReferringComponents('polar')[0];
19303            var radiusAxisModel = polarModel.findAxisModel('radiusAxis');
19304            var angleAxisModel = polarModel.findAxisModel('angleAxis');
19305
19306            if (__DEV__) {
19307                if (!angleAxisModel) {
19308                    throw new Error('angleAxis option not found');
19309                }
19310                if (!radiusAxisModel) {
19311                    throw new Error('radiusAxis option not found');
19312                }
19313            }
19314
19315            result.coordSysDims = ['radius', 'angle'];
19316            axisMap.set('radius', radiusAxisModel);
19317            axisMap.set('angle', angleAxisModel);
19318
19319            if (isCategory(radiusAxisModel)) {
19320                categoryAxisMap.set('radius', radiusAxisModel);
19321                result.firstCategoryDimIndex = 0;
19322            }
19323            if (isCategory(angleAxisModel)) {
19324                categoryAxisMap.set('angle', angleAxisModel);
19325                result.firstCategoryDimIndex = 1;
19326            }
19327        },
19328
19329        geo: function (seriesModel, result, axisMap, categoryAxisMap) {
19330            result.coordSysDims = ['lng', 'lat'];
19331        },
19332
19333        parallel: function (seriesModel, result, axisMap, categoryAxisMap) {
19334            var ecModel = seriesModel.ecModel;
19335            var parallelModel = ecModel.getComponent(
19336                'parallel', seriesModel.get('parallelIndex')
19337            );
19338            var coordSysDims = result.coordSysDims = parallelModel.dimensions.slice();
19339
19340            each$1(parallelModel.parallelAxisIndex, function (axisIndex, index) {
19341                var axisModel = ecModel.getComponent('parallelAxis', axisIndex);
19342                var axisDim = coordSysDims[index];
19343                axisMap.set(axisDim, axisModel);
19344
19345                if (isCategory(axisModel) && result.firstCategoryDimIndex == null) {
19346                    categoryAxisMap.set(axisDim, axisModel);
19347                    result.firstCategoryDimIndex = index;
19348                }
19349            });
19350        }
19351    };
19352
19353    function isCategory(axisModel) {
19354        return axisModel.get('type') === 'category';
19355    }
19356
19357    /*
19358* Licensed to the Apache Software Foundation (ASF) under one
19359* or more contributor license agreements.  See the NOTICE file
19360* distributed with this work for additional information
19361* regarding copyright ownership.  The ASF licenses this file
19362* to you under the Apache License, Version 2.0 (the
19363* "License");
19363 you may not use this file except in compliance
19364* with the License.  You may obtain a copy of the License at
19365*
19366*   http://www.apache.org/licenses/LICENSE-2.0
19367*
19368* Unless required by applicable law or agreed to in writing,
19369* software distributed under the License is distributed on an
19370* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
19371* KIND, either express or implied.  See the License for the
19372* specific language governing permissions and limitations
19373* under the License.
19374*/
19375
19376// Avoid typo.
19377    var SOURCE_FORMAT_ORIGINAL = 'original';
19378    var SOURCE_FORMAT_ARRAY_ROWS = 'arrayRows';
19379    var SOURCE_FORMAT_OBJECT_ROWS = 'objectRows';
19380    var SOURCE_FORMAT_KEYED_COLUMNS = 'keyedColumns';
19381    var SOURCE_FORMAT_UNKNOWN = 'unknown';
19382// ??? CHANGE A NAME
19383    var SOURCE_FORMAT_TYPED_ARRAY = 'typedArray';
19384
19385    var SERIES_LAYOUT_BY_COLUMN = 'column';
19386    var SERIES_LAYOUT_BY_ROW = 'row';
19387
19388    /*
19389* Licensed to the Apache Software Foundation (ASF) under one
19390* or more contributor license agreements.  See the NOTICE file
19391* distributed with this work for additional information
19392* regarding copyright ownership.  The ASF licenses this file
19393* to you under the Apache License, Version 2.0 (the
19394* "License"); you may not use this file except in compliance
19395* with the License.  You may obtain a copy of the License at
19396*
19397*   http://www.apache.org/licenses/LICENSE-2.0
19398*
19399* Unless required by applicable law or agreed to in writing,
19400* software distributed under the License is distributed on an
19401* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
19402* KIND, either express or implied.  See the License for the
19403* specific language governing permissions and limitations
19404* under the License.
19405*/
19406
19407    /**
19408     * [sourceFormat]
19409     *
19410     * + "original":
19411     * This format is only used in series.data, where
19412     * itemStyle can be specified in data item.
19413     *
19414     * + "arrayRows":
19415     * [
19416     *     ['product', 'score', 'amount'],
19417     *     ['Matcha Latte', 89.3, 95.8],
19418     *     ['Milk Tea', 92.1, 89.4],
19419     *     ['Cheese Cocoa', 94.4, 91.2],
19420     *     ['Walnut Brownie', 85.4, 76.9]
19421     * ]
19422     *
19423     * + "objectRows":
19424     * [
19425     *     {product: 'Matcha Latte', score: 89.3, amount: 95.8},
19426     *     {product: 'Milk Tea', score: 92.1, amount: 89.4},
19427     *     {product: 'Cheese Cocoa', score: 94.4, amount: 91.2},
19428     *     {product: 'Walnut Brownie', score: 85.4, amount: 76.9}
19429     * ]
19430     *
19431     * + "keyedColumns":
19432     * {
19433 *     'product': ['Matcha Latte', 'Milk Tea', 'Cheese Cocoa', 'Walnut Brownie'],
19434 *     'count': [823, 235, 1042, 988],
19435 *     'score': [95.8, 81.4, 91.2, 76.9]
19436 * }
19437     *
19438     * + "typedArray"
19439     *
19440     * + "unknown"
19441     */
19442
19443    /**
19444     * @constructor
19445     * @param {Object} fields
19446     * @param {string} fields.sourceFormat
19447     * @param {Array|Object} fields.fromDataset
19448     * @param {Array|Object} [fields.data]
19449     * @param {string} [seriesLayoutBy='column']
19450     * @param {Array.<Object|string>} [dimensionsDefine]
19451     * @param {Objet|HashMap} [encodeDefine]
19452     * @param {number} [startIndex=0]
19453     * @param {number} [dimensionsDetectCount]
19454     */
19455    function Source(fields) {
19456
19457        /**
19458         * @type {boolean}
19459         */
19460        this.fromDataset = fields.fromDataset;
19461
19462        /**
19463         * Not null/undefined.
19464         * @type {Array|Object}
19465         */
19466        this.data = fields.data || (
19467            fields.sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS ? {} : []
19468        );
19469
19470        /**
19471         * See also "detectSourceFormat".
19472         * Not null/undefined.
19473         * @type {string}
19474         */
19475        this.sourceFormat = fields.sourceFormat || SOURCE_FORMAT_UNKNOWN;
19476
19477        /**
19478         * 'row' or 'column'
19479         * Not null/undefined.
19480         * @type {string} seriesLayoutBy
19481         */
19482        this.seriesLayoutBy = fields.seriesLayoutBy || SERIES_LAYOUT_BY_COLUMN;
19483
19484        /**
19485         * dimensions definition in option.
19486         * can be null/undefined.
19487         * @type {Array.<Object|string>}
19488         */
19489        this.dimensionsDefine = fields.dimensionsDefine;
19490
19491        /**
19492         * encode definition in option.
19493         * can be null/undefined.
19494         * @type {Objet|HashMap}
19495         */
19496        this.encodeDefine = fields.encodeDefine && createHashMap(fields.encodeDefine);
19497
19498        /**
19499         * Not null/undefined, uint.
19500         * @type {number}
19501         */
19502        this.startIndex = fields.startIndex || 0;
19503
19504        /**
19505         * Can be null/undefined (when unknown), uint.
19506         * @type {number}
19507         */
19508        this.dimensionsDetectCount = fields.dimensionsDetectCount;
19509    }
19510
19511    /**
19512     * Wrap original series data for some compatibility cases.
19513     */
19514    Source.seriesDataToSource = function (data) {
19515        return new Source({
19516            data: data,
19517            sourceFormat: isTypedArray(data)
19518                ? SOURCE_FORMAT_TYPED_ARRAY
19519                : SOURCE_FORMAT_ORIGINAL,
19520            fromDataset: false
19521        });
19522    };
19523
19524    enableClassCheck(Source);
19525
19526    /*
19527* Licensed to the Apache Software Foundation (ASF) under one
19528* or more contributor license agreements.  See the NOTICE file
19529* distributed with this work for additional information
19530* regarding copyright ownership.  The ASF licenses this file
19531* to you under the Apache License, Version 2.0 (the
19532* "License");
19532 you may not use this file except in compliance
19533* with the License.  You may obtain a copy of the License at
19534*
19535*   http://www.apache.org/licenses/LICENSE-2.0
19536*
19537* Unless required by applicable law or agreed to in writing,
19538* software distributed under the License is distributed on an
19539* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
19540* KIND, either express or implied.  See the License for the
19541* specific language governing permissions and limitations
19542* under the License.
19543*/
19544
19545    var inner$3 = makeInner();
19546
19547    /**
19548     * @see {module:echarts/data/Source}
19549     * @param {module:echarts/component/dataset/DatasetModel} datasetModel
19550     * @return {string} sourceFormat
19551     */
19552    function detectSourceFormat(datasetModel) {
19553        var data = datasetModel.option.source;
19554        var sourceFormat = SOURCE_FORMAT_UNKNOWN;
19555
19556        if (isTypedArray(data)) {
19557            sourceFormat = SOURCE_FORMAT_TYPED_ARRAY;
19558        }
19559        else if (isArray(data)) {
19560            // FIXME Whether tolerate null in top level array?
19561            for (var i = 0, len = data.length; i < len; i++) {
19562                var item = data[i];
19563
19564                if (item == null) {
19565                    continue;
19566                }
19567                else if (isArray(item)) {
19568                    sourceFormat = SOURCE_FORMAT_ARRAY_ROWS;
19569                    break;
19570                }
19571                else if (isObject$1(item)) {
19572                    sourceFormat = SOURCE_FORMAT_OBJECT_ROWS;
19573                    break;
19574                }
19575            }
19576        }
19577        else if (isObject$1(data)) {
19578            for (var key in data) {
19579                if (data.hasOwnProperty(key) && isArrayLike(data[key])) {
19580                    sourceFormat = SOURCE_FORMAT_KEYED_COLUMNS;
19581                    break;
19582                }
19583            }
19584        }
19585        else if (data != null) {
19586            throw new Error('Invalid data');
19587        }
19588
19589        inner$3(datasetModel).sourceFormat = sourceFormat;
19590    }
19591
19592    /**
19593     * [Scenarios]:
19594     * (1) Provide source data directly:
19595     *     series: {
19596 *         encode: {...},
19597 *         dimensions: [...]
19598 *         seriesLayoutBy: 'row',
19599 *         data: [[...]]
19600 *     }
19601     * (2) Refer to datasetModel.
19602     *     series: [{
19603 *         encode: {...}
19604 *         // Ignore datasetIndex means `datasetIndex: 0`
19605 *         // and the dimensions defination in dataset is used
19606 *     }, {
19607 *         encode: {...},
19608 *         seriesLayoutBy: 'column',
19609 *         datasetIndex: 1
19610 *     }]
19611     *
19612     * Get data from series itself or datset.
19613     * @return {module:echarts/data/Source} source
19614     */
19615    function getSource(seriesModel) {
19616        return inner$3(seriesModel).source;
19617    }
19618
19619    /**
19620     * MUST be called before mergeOption of all series.
19621     * @param {module:echarts/model/Global} ecModel
19622     */
19623    function resetSourceDefaulter(ecModel) {
19624        // `datasetMap` is used to make default encode.
19625        inner$3(ecModel).datasetMap = createHashMap();
19626    }
19627
19628    /**
19629     * [Caution]:
19630     * MUST be called after series option merged and
19631     * before "series.getInitailData()" called.
19632     *
19633     * [The rule of making default encode]:
19634     * Category axis (if exists) alway map to the first dimension.
19635     * Each other axis occupies a subsequent dimension.
19636     *
19637     * [Why make default encode]:
19638     * Simplify the typing of encode in option, avoiding the case like that:
19639     * series: [{encode: {x: 0, y: 1}}, {encode: {x: 0, y: 2}}, {encode: {x: 0, y: 3}}],
19640     * where the "y" have to be manually typed as "1, 2, 3, ...".
19641     *
19642     * @param {module:echarts/model/Series} seriesModel
19643     */
19644    function prepareSource(seriesModel) {
19645        var seriesOption = seriesModel.option;
19646
19647        var data = seriesOption.data;
19648        var sourceFormat = isTypedArray(data)
19649            ? SOURCE_FORMAT_TYPED_ARRAY : SOURCE_FORMAT_ORIGINAL;
19650        var fromDataset = false;
19651
19652        var seriesLayoutBy = seriesOption.seriesLayoutBy;
19653        var sourceHeader = seriesOption.sourceHeader;
19654        var dimensionsDefine = seriesOption.dimensions;
19655
19656        var datasetModel = getDatasetModel(seriesModel);
19657        if (datasetModel) {
19658            var datasetOption = datasetModel.option;
19659
19660            data = datasetOption.source;
19661            sourceFormat = inner$3(datasetModel).sourceFormat;
19662            fromDataset = true;
19663
19664            // These settings from series has higher priority.
19665            seriesLayoutBy = seriesLayoutBy || datasetOption.seriesLayoutBy;
19666            sourceHeader == null && (sourceHeader = datasetOption.sourceHeader);
19667            dimensionsDefine = dimensionsDefine || datasetOption.dimensions;
19668        }
19669
19670        var completeResult = completeBySourceData(
19671            data, sourceFormat, seriesLayoutBy, sourceHeader, dimensionsDefine
19672        );
19673
19674        // Note: dataset option does not have `encode`.
19675        var encodeDefine = seriesOption.encode;
19676        if (!encodeDefine && datasetModel) {
19677            encodeDefine = makeDefaultEncode(
19678                seriesModel, datasetModel, data, sourceFormat, seriesLayoutBy, completeResult
19679            );
19680        }
19681
19682        inner$3(seriesModel).source = new Source({
19683            data: data,
19684            fromDataset: fromDataset,
19685            seriesLayoutBy: seriesLayoutBy,
19686            sourceFormat: sourceFormat,
19687            dimensionsDefine: completeResult.dimensionsDefine,
19688            startIndex: completeResult.startIndex,
19689            dimensionsDetectCount: completeResult.dimensionsDetectCount,
19690            encodeDefine: encodeDefine
19691        });
19692    }
19693
19694// return {startIndex, dimensionsDefine, dimensionsCount}
19695    function completeBySourceData(data, sourceFormat, seriesLayoutBy, sourceHeader, dimensionsDefine) {
19696        if (!data) {
19697            return {dimensionsDefine: normalizeDimensionsDefine(dimensionsDefine)};
19698        }
19699
19700        var dimensionsDetectCount;
19701        var startIndex;
19702        var findPotentialName;
19703
19704        if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) {
19705            // Rule: Most of the first line are string: it is header.
19706            // Caution: consider a line with 5 string and 1 number,
19707            // it still can not be sure it is a head, because the
19708            // 5 string may be 5 values of category columns.
19709            if (sourceHeader === 'auto' || sourceHeader == null) {
19710                arrayRowsTravelFirst(function (val) {
19711                    // '-' is regarded as null/undefined.
19712                    if (val != null && val !== '-') {
19713                        if (isString(val)) {
19714                            startIndex == null && (startIndex = 1);
19715                        }
19716                        else {
19717                            startIndex = 0;
19718                        }
19719                    }
19720                    // 10 is an experience number, avoid long loop.
19721                }, seriesLayoutBy, data, 10);
19722            }
19723            else {
19724                startIndex = sourceHeader ? 1 : 0;
19725            }
19726
19727            if (!dimensionsDefine && startIndex === 1) {
19728                dimensionsDefine = [];
19729                arrayRowsTravelFirst(function (val, index) {
19730                    dimensionsDefine[index] = val != null ? val : '';
19731                }, seriesLayoutBy, data);
19732            }
19733
19734            dimensionsDetectCount = dimensionsDefine
19735                ? dimensionsDefine.length
19736                : seriesLayoutBy === SERIES_LAYOUT_BY_ROW
19737                    ? data.length
19738                    : data[0]
19739                        ? data[0].length
19740                        : null;
19741        }
19742        else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) {
19743            if (!dimensionsDefine) {
19744                dimensionsDefine = objectRowsCollectDimensions(data);
19745                findPotentialName = true;
19746            }
19747        }
19748        else if (sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS) {
19749            if (!dimensionsDefine) {
19750                dimensionsDefine = [];
19751                findPotentialName = true;
19752                each$1(data, function (colArr, key) {
19753                    dimensionsDefine.push(key);
19754                });
19755            }
19756        }
19757        else if (sourceFormat === SOURCE_FORMAT_ORIGINAL) {
19758            var value0 = getDataItemValue(data[0]);
19759            dimensionsDetectCount = isArray(value0) && value0.length || 1;
19760        }
19761        else if (sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) {
19762            if (__DEV__) {
19763                assert$1(!!dimensionsDefine, 'dimensions must be given if data is TypedArray.');
19764            }
19765        }
19766
19767        var potentialNameDimIndex;
19768        if (findPotentialName) {
19769            each$1(dimensionsDefine, function (dim, idx) {
19770                if ((isObject$1(dim) ? dim.name : dim) === 'name') {
19771                    potentialNameDimIndex = idx;
19772                }
19773            });
19774        }
19775
19776        return {
19777            startIndex: startIndex,
19778            dimensionsDefine: normalizeDimensionsDefine(dimensionsDefine),
19779            dimensionsDetectCount: dimensionsDetectCount,
19780            potentialNameDimIndex: potentialNameDimIndex
19781            // TODO: potentialIdDimIdx
19782        };
19783    }
19784
19785// Consider dimensions defined like ['A', 'price', 'B', 'price', 'C', 'price'],
19786// which is reasonable. But dimension name is duplicated.
19787// Returns undefined or an array contains only object without null/undefiend or string.
19788    function normalizeDimensionsDefine(dimensionsDefine) {
19789        if (!dimensionsDefine) {
19790            // The meaning of null/undefined is different from empty array.
19791            return;
19792        }
19793        var nameMap = createHashMap();
19794        return map(dimensionsDefine, function (item, index) {
19795            item = extend({}, isObject$1(item) ? item : {name: item});
19796
19797            // User can set null in dimensions.
19798            // We dont auto specify name, othewise a given name may
19799            // cause it be refered unexpectedly.
19800            if (item.name == null) {
19801                return item;
19802            }
19803
19804            // Also consider number form like 2012.
19805            item.name += '';
19806            // User may also specify displayName.
19807            // displayName will always exists except user not
19808            // specified or dim name is not specified or detected.
19809            // (A auto generated dim name will not be used as
19810            // displayName).
19811            if (item.displayName == null) {
19812                item.displayName = item.name;
19813            }
19814
19815            var exist = nameMap.get(item.name);
19816            if (!exist) {
19817                nameMap.set(item.name, {count: 1});
19818            }
19819            else {
19820                item.name += '-' + exist.count++;
19821            }
19822
19823            return item;
19824        });
19825    }
19826
19827    function arrayRowsTravelFirst(cb, seriesLayoutBy, data, maxLoop) {
19828        maxLoop == null && (maxLoop = Infinity);
19829        if (seriesLayoutBy === SERIES_LAYOUT_BY_ROW) {
19830            for (var i = 0; i < data.length && i < maxLoop; i++) {
19831                cb(data[i] ? data[i][0] : null, i);
19832            }
19833        }
19834        else {
19835            var value0 = data[0] || [];
19836            for (var i = 0; i < value0.length && i < maxLoop; i++) {
19837                cb(value0[i], i);
19838            }
19839        }
19840    }
19841
19842    function objectRowsCollectDimensions(data) {
19843        var firstIndex = 0;
19844        var obj;
19845        while (firstIndex < data.length && !(obj = data[firstIndex++])) {} // jshint ignore: line
19846        if (obj) {
19847            var dimensions = [];
19848            each$1(obj, function (value, key) {
19849                dimensions.push(key);
19850            });
19851            return dimensions;
19852        }
19853    }
19854
19855// ??? TODO merge to completedimensions, where also has
19856// default encode making logic. And the default rule
19857// should depends on series? consider 'map'.
19858    function makeDefaultEncode(
19859        seriesModel, datasetModel, data, sourceFormat, seriesLayoutBy, completeResult
19860    ) {
19861        var coordSysDefine = getCoordSysDefineBySeries(seriesModel);
19862        var encode = {};
19863        // var encodeTooltip = [];
19864        // var encodeLabel = [];
19865        var encodeItemName = [];
19866        var encodeSeriesName = [];
19867        var seriesType = seriesModel.subType;
19868
19869        // ??? TODO refactor: provide by series itself.
19870        // Consider the case: 'map' series is based on geo coordSys,
19871        // 'graph', 'heatmap' can be based on cartesian. But can not
19872        // give default rule simply here.
19873        var nSeriesMap = createHashMap(['pie', 'map', 'funnel']);
19874        var cSeriesMap = createHashMap([
19875            'line', 'bar', 'pictorialBar', 'scatter', 'effectScatter', 'candlestick', 'boxplot'
19876        ]);
19877
19878        // Usually in this case series will use the first data
19879        // dimension as the "value" dimension, or other default
19880        // processes respectively.
19881        if (coordSysDefine && cSeriesMap.get(seriesType) != null) {
19882            var ecModel = seriesModel.ecModel;
19883            var datasetMap = inner$3(ecModel).datasetMap;
19884            var key = datasetModel.uid + '_' + seriesLayoutBy;
19885            var datasetRecord = datasetMap.get(key)
19886                || datasetMap.set(key, {categoryWayDim: 1, valueWayDim: 0});
19887
19888            // TODO
19889            // Auto detect first time axis and do arrangement.
19890            each$1(coordSysDefine.coordSysDims, function (coordDim) {
19891                // In value way.
19892                if (coordSysDefine.firstCategoryDimIndex == null) {
19893                    var dataDim = datasetRecord.valueWayDim++;
19894                    encode[coordDim] = dataDim;
19895
19896                    // ??? TODO give a better default series name rule?
19897                    // especially when encode x y specified.
19898                    // consider: when mutiple series share one dimension
19899                    // category axis, series name should better use
19900                    // the other dimsion name. On the other hand, use
19901                    // both dimensions name.
19902
19903                    encodeSeriesName.push(dataDim);
19904                    // encodeTooltip.push(dataDim);
19905                    // encodeLabel.push(dataDim);
19906                }
19907                // In category way, category axis.
19908                else if (coordSysDefine.categoryAxisMap.get(coordDim)) {
19909                    encode[coordDim] = 0;
19910                    encodeItemName.push(0);
19911                }
19912                // In category way, non-category axis.
19913                else {
19914                    var dataDim = datasetRecord.categoryWayDim++;
19915                    encode[coordDim] = dataDim;
19916                    // encodeTooltip.push(dataDim);
19917                    // encodeLabel.push(dataDim);
19918                    encodeSeriesName.push(dataDim);
19919                }
19920            });
19921        }
19922        // Do not make a complex rule! Hard to code maintain and not necessary.
19923        // ??? TODO refactor: provide by series itself.
19924        // [{name: ..., value: ...}, ...] like:
19925        else if (nSeriesMap.get(seriesType) != null) {
19926            // Find the first not ordinal. (5 is an experience value)
19927            var firstNotOrdinal;
19928            for (var i = 0; i < 5 && firstNotOrdinal == null; i++) {
19929                if (!doGuessOrdinal(
19930                        data, sourceFormat, seriesLayoutBy,
19931                        completeResult.dimensionsDefine, completeResult.startIndex, i
19932                    )) {
19933                    firstNotOrdinal = i;
19934                }
19935            }
19936            if (firstNotOrdinal != null) {
19937                encode.value = firstNotOrdinal;
19938                var nameDimIndex = completeResult.potentialNameDimIndex
19939                    || Math.max(firstNotOrdinal - 1, 0);
19940                // By default, label use itemName in charts.
19941                // So we dont set encodeLabel here.
19942                encodeSeriesName.push(nameDimIndex);
19943                encodeItemName.push(nameDimIndex);
19944                // encodeTooltip.push(firstNotOrdinal);
19945            }
19946        }
19947
19948        // encodeTooltip.length && (encode.tooltip = encodeTooltip);
19949        // encodeLabel.length && (encode.label = encodeLabel);
19950        encodeItemName.length && (encode.itemName = encodeItemName);
19951        encodeSeriesName.length && (encode.seriesName = encodeSeriesName);
19952
19953        return encode;
19954    }
19955
19956    /**
19957     * If return null/undefined, indicate that should not use datasetModel.
19958     */
19959    function getDatasetModel(seriesModel) {
19960        var option = seriesModel.option;
19961        // Caution: consider the scenario:
19962        // A dataset is declared and a series is not expected to use the dataset,
19963        // and at the beginning `setOption({series: { noData })` (just prepare other
19964        // option but no data), then `setOption({series: {data: [...]}); In this case,
19965        // the user should set an empty array to avoid that dataset is used by default.
19966        var thisData = option.data;
19967        if (!thisData) {
19968            return seriesModel.ecModel.getComponent('dataset', option.datasetIndex || 0);
19969        }
19970    }
19971
19972    /**
19973     * The rule should not be complex, otherwise user might not
19974     * be able to known where the data is wrong.
19975     * The code is ugly, but how to make it neat?
19976     *
19977     * @param {module:echars/data/Source} source
19978     * @param {number} dimIndex
19979     * @return {boolean} Whether ordinal.
19980     */
19981    function guessOrdinal(source, dimIndex) {
19982        return doGuessOrdinal(
19983            source.data,
19984            source.sourceFormat,
19985            source.seriesLayoutBy,
19986            source.dimensionsDefine,
19987            source.startIndex,
19988            dimIndex
19989        );
19990    }
19991
19992// dimIndex may be overflow source data.
19993    function doGuessOrdinal(
19994        data, sourceFormat, seriesLayoutBy, dimensionsDefine, startIndex, dimIndex
19995    ) {
19996        var result;
19997        // Experience value.
19998        var maxLoop = 5;
19999
20000        if (isTypedArray(data)) {
20001            return false;
20002        }
20003
20004        // When sourceType is 'objectRows' or 'keyedColumns', dimensionsDefine
20005        // always exists in source.
20006        var dimName;
20007        if (dimensionsDefine) {
20008            dimName = dimensionsDefine[dimIndex];
20009            dimName = isObject$1(dimName) ? dimName.name : dimName;
20010        }
20011
20012        if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) {
20013            if (seriesLayoutBy === SERIES_LAYOUT_BY_ROW) {
20014                var sample = data[dimIndex];
20015                for (var i = 0; i < (sample || []).length && i < maxLoop; i++) {
20016                    if ((result = detectValue(sample[startIndex + i])) != null) {
20017                        return result;
20018                    }
20019                }
20020            }
20021            else {
20022                for (var i = 0; i < data.length && i < maxLoop; i++) {
20023                    var row = data[startIndex + i];
20024                    if (row && (result = detectValue(row[dimIndex])) != null) {
20025                        return result;
20026                    }
20027                }
20028            }
20029        }
20030        else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) {
20031            if (!dimName) {
20032                return;
20033            }
20034            for (var i = 0; i < data.length && i < maxLoop; i++) {
20035                var item = data[i];
20036                if (item && (result = detectValue(item[dimName])) != null) {
20037                    return result;
20038                }
20039            }
20040        }
20041        else if (sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS) {
20042            if (!dimName) {
20043                return;
20044            }
20045            var sample = data[dimName];
20046            if (!sample || isTypedArray(sample)) {
20047                return false;
20048            }
20049            for (var i = 0; i < sample.length && i < maxLoop; i++) {
20050                if ((result = detectValue(sample[i])) != null) {
20051                    return result;
20052                }
20053            }
20054        }
20055        else if (sourceFormat === SOURCE_FORMAT_ORIGINAL) {
20056            for (var i = 0; i < data.length && i < maxLoop; i++) {
20057                var item = data[i];
20058                var val = getDataItemValue(item);
20059                if (!isArray(val)) {
20060                    return false;
20061                }
20062                if ((result = detectValue(val[dimIndex])) != null) {
20063                    return result;
20064                }
20065            }
20066        }
20067
20068        function detectValue(val) {
20069            // Consider usage convenience, '1', '2' will be treated as "number".
20070            // `isFinit('')` get `true`.
20071            if (val != null && isFinite(val) && val !== '') {
20072                return false;
20073            }
20074            else if (isString(val) && val !== '-') {
20075                return true;
20076            }
20077        }
20078
20079        return false;
20080    }
20081
20082    /*
20083* Licensed to the Apache Software Foundation (ASF) under one
20084* or more contributor license agreements.  See the NOTICE file
20085* distributed with this work for additional information
20086* regarding copyright ownership.  The ASF licenses this file
20087* to you under the Apache License, Version 2.0 (the
20088* "License"); you may not use this file except in compliance
20089* with the License.  You may obtain a copy of the License at
20090*
20091*   http://www.apache.org/licenses/LICENSE-2.0
20092*
20093* Unless required by applicable law or agreed to in writing,
20094* software distributed under the License is distributed on an
20095* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20096* KIND, either express or implied.  See the License for the
20097* specific language governing permissions and limitations
20098* under the License.
20099*/
20100
20101    /**
20102     * ECharts global model
20103     *
20104     * @module {echarts/model/Global}
20105     */
20106
20107
20108    /**
20109     * Caution: If the mechanism should be changed some day, these cases
20110     * should be considered:
20111     *
20112     * (1) In `merge option` mode, if using the same option to call `setOption`
20113     * many times, the result should be the same (try our best to ensure that).
20114     * (2) In `merge option` mode, if a component has no id/name specified, it
20115     * will be merged by index, and the result sequence of the components is
20116     * consistent to the original sequence.
20117     * (3) `reset` feature (in toolbox). Find detailed info in comments about
20118     * `mergeOption` in module:echarts/model/OptionManager.
20119     */
20120
20121    var OPTION_INNER_KEY = '\0_ec_inner';
20122
20123    /**
20124     * @alias module:echarts/model/Global
20125     *
20126     * @param {Object} option
20127     * @param {module:echarts/model/Model} parentModel
20128     * @param {Object} theme
20129     */
20130    var GlobalModel = Model.extend({
20131
20132        init: function (option, parentModel, theme, optionManager) {
20133            theme = theme || {};
20134
20135            this.option = null; // Mark as not initialized.
20136
20137            /**
20138             * @type {module:echarts/model/Model}
20139             * @private
20140             */
20141            this._theme = new Model(theme);
20142
20143            /**
20144             * @type {module:echarts/model/OptionManager}
20145             */
20146            this._optionManager = optionManager;
20147        },
20148
20149        setOption: function (option, optionPreprocessorFuncs) {
20150            assert$1(
20151                !(OPTION_INNER_KEY in option),
20152                'please use chart.getOption()'
20153            );
20154
20155            this._optionManager.setOption(option, optionPreprocessorFuncs);
20156
20157            this.resetOption(null);
20158        },
20159
20160        /**
20161         * @param {string} type null/undefined: reset all.
20162         *                      'recreate': force recreate all.
20163         *                      'timeline': only reset timeline option
20164         *                      'media': only reset media query option
20165         * @return {boolean} Whether option changed.
20166         */
20167        resetOption: function (type) {
20168            var optionChanged = false;
20169            var optionManager = this._optionManager;
20170
20171            if (!type || type === 'recreate') {
20172                var baseOption = optionManager.mountOption(type === 'recreate');
20173
20174                if (!this.option || type === 'recreate') {
20175                    initBase.call(this, baseOption);
20176                }
20177                else {
20178                    this.restoreData();
20179                    this.mergeOption(baseOption);
20180                }
20181                optionChanged = true;
20182            }
20183
20184            if (type === 'timeline' || type === 'media') {
20185                this.restoreData();
20186            }
20187
20188            if (!type || type === 'recreate' || type === 'timeline') {
20189                var timelineOption = optionManager.getTimelineOption(this);
20190                timelineOption && (this.mergeOption(timelineOption), optionChanged = true);
20191            }
20192
20193            if (!type || type === 'recreate' || type === 'media') {
20194                var mediaOptions = optionManager.getMediaOption(this, this._api);
20195                if (mediaOptions.length) {
20196                    each$1(mediaOptions, function (mediaOption) {
20197                        this.mergeOption(mediaOption, optionChanged = true);
20198                    }, this);
20199                }
20200            }
20201
20202            return optionChanged;
20203        },
20204
20205        /**
20206         * @protected
20207         */
20208        mergeOption: function (newOption) {
20209            var option = this.option;
20210            var componentsMap = this._componentsMap;
20211            var newCptTypes = [];
20212
20213            resetSourceDefaulter(this);
20214
20215            // If no component class, merge directly.
20216            // For example: color, animaiton options, etc.
20217            each$1(newOption, function (componentOption, mainType) {
20218                if (componentOption == null) {
20219                    return;
20220                }
20221
20222                if (!ComponentModel.hasClass(mainType)) {
20223                    // globalSettingTask.dirty();
20224                    option[mainType] = option[mainType] == null
20225                        ? clone(componentOption)
20226                        : merge(option[mainType], componentOption, true);
20227                }
20228                else if (mainType) {
20229                    newCptTypes.push(mainType);
20230                }
20231            });
20232
20233            ComponentModel.topologicalTravel(
20234                newCptTypes, ComponentModel.getAllClassMainTypes(), visit
20234Component, this
20235            );
20236
20237            function visitComponent(mainType, dependencies) {
20238
20239                var newCptOptionList = normalizeToArray(newOption[mainType]);
20240
20241                var mapResult = mappingToExists(
20242                    componentsMap.get(mainType), newCptOptionList
20243                );
20244
20245                makeIdAndName(mapResult);
20246
20247                // Set mainType and complete subType.
20248                each$1(mapResult, function (item, index) {
20249                    var opt = item.option;
20250                    if (isObject$1(opt)) {
20251                        item.keyInfo.mainType = mainType;
20252                        item.keyInfo.subType = determineSubType(mainType, opt, item.exist);
20253                    }
20254                });
20255
20256                var dependentModels = getComponentsByTypes(
20257                    componentsMap, dependencies
20258                );
20259
20260                option[mainType] = [];
20261                componentsMap.set(mainType, []);
20262
20263                each$1(mapResult, function (resultItem, index) {
20264                    var componentModel = resultItem.exist;
20265                    var newCptOption = resultItem.option;
20266
20267                    assert$1(
20268                        isObject$1(newCptOption) || componentModel,
20269                        'Empty component definition'
20270                    );
20271
20272                    // Consider where is no new option and should be merged using {},
20273                    // see removeEdgeAndAdd in topologicalTravel and
20274                    // ComponentModel.getAllClassMainTypes.
20275                    if (!newCptOption) {
20276                        componentModel.mergeOption({}, this);
20277                        componentModel.optionUpdated({}, false);
20278                    }
20279                    else {
20280                        var ComponentModelClass = ComponentModel.getClass(
20281                            mainType, resultItem.keyInfo.subType, true
20282                        );
20283
20284                        if (componentModel && componentModel instanceof ComponentModelClass) {
20285                            componentModel.name = resultItem.keyInfo.name;
20286                            // componentModel.settingTask && componentModel.settingTask.dirty();
20287                            componentModel.mergeOption(newCptOption, this);
20288                            componentModel.optionUpdated(newCptOption, false);
20289                        }
20290                        else {
20291                            // PENDING Global as parent ?
20292                            var extraOpt = extend(
20293                                {
20294                                    dependentModels: dependentModels,
20295                                    componentIndex: index
20296                                },
20297                                resultItem.keyInfo
20298                            );
20299                            componentModel = new ComponentModelClass(
20300                                newCptOption, this, this, extraOpt
20301                            );
20302                            extend(componentModel, extraOpt);
20303                            componentModel.init(newCptOption, this, this, extraOpt);
20304
20305                            // Call optionUpdated after init.
20306                            // newCptOption has been used as componentModel.option
20307                            // and may be merged with theme and default, so pass null
20308                            // to avoid confusion.
20309                            componentModel.optionUpdated(null, true);
20310                        }
20311                    }
20312
20313                    componentsMap.get(mainType)[index] = componentModel;
20314                    option[mainType][index] = componentModel.option;
20315                }, this);
20316
20317                // Backup series for filtering.
20318                if (mainType === 'series') {
20319                    createSeriesIndices(this, componentsMap.get('series'));
20320                }
20321            }
20322
20323            this._seriesIndicesMap = createHashMap(
20324                this._seriesIndices = this._seriesIndices || []
20325            );
20326        },
20327
20328        /**
20329         * Get option for output (cloned option and inner info removed)
20330         * @public
20331         * @return {Object}
20332         */
20333        getOption: function () {
20334            var option = clone(this.option);
20335
20336            each$1(option, function (opts, mainType) {
20337                if (ComponentModel.hasClass(mainType)) {
20338                    var opts = normalizeToArray(opts);
20339                    for (var i = opts.length - 1; i >= 0; i--) {
20340                        // Remove options with inner id.
20341                        if (isIdInner(opts[i])) {
20342                            opts.splice(i, 1);
20343                        }
20344                    }
20345                    option[mainType] = opts;
20346                }
20347            });
20348
20349            delete option[OPTION_INNER_KEY];
20350
20351            return option;
20352        },
20353
20354        /**
20355         * @return {module:echarts/model/Model}
20356         */
20357        getTheme: function () {
20358            return this._theme;
20359        },
20360
20361        /**
20362         * @param {string} mainType
20363         * @param {number} [idx=0]
20364         * @return {module:echarts/model/Component}
20365         */
20366        getComponent: function (mainType, idx) {
20367            var list = this._componentsMap.get(mainType);
20368            if (list) {
20369                return list[idx || 0];
20370            }
20371        },
20372
20373        /**
20374         * If none of index and id and name used, return all components with mainType.
20375         * @param {Object} condition
20376         * @param {string} condition.mainType
20377         * @param {string} [condition.subType] If ignore, only query by mainType
20378         * @param {number|Array.<number>} [condition.index] Either input index or id or name.
20379         * @param {string|Array.<string>} [condition.id] Either input index or id or name.
20380         * @param {string|Array.<string>} [condition.name] Either input index or id or name.
20381         * @return {Array.<module:echarts/model/Component>}
20382         */
20383        queryComponents: function (condition) {
20384            var mainType = condition.mainType;
20385            if (!mainType) {
20386                return [];
20387            }
20388
20389            var index = condition.index;
20390            var id = condition.id;
20391            var name = condition.name;
20392
20393            var cpts = this._componentsMap.get(mainType);
20394
20395            if (!cpts || !cpts.length) {
20396                return [];
20397            }
20398
20399            var result;
20400
20401            if (index != null) {
20402                if (!isArray(index)) {
20403                    index = [index];
20404                }
20405                result = filter(map(index, function (idx) {
20406                    return cpts[idx];
20407                }), function (val) {
20408                    return !!val;
20409                });
20410            }
20411            else if (id != null) {
20412                var isIdArray = isArray(id);
20413                result = filter(cpts, function (cpt) {
20414                    return (isIdArray && indexOf(id, cpt.id) >= 0)
20415                        || (!isIdArray && cpt.id === id);
20416                });
20417            }
20418            else if (name != null) {
20419                var isNameArray = isArray(name);
20420                result = filter(cpts, function (cpt) {
20421                    return (isNameArray && indexOf(name, cpt.name) >= 0)
20422                        || (!isNameArray && cpt.name === name);
20423                });
20424            }
20425            else {
20426                // Return all components with mainType
20427                result = cpts.slice();
20428            }
20429
20430            return filterBySubType(result, condition);
20431        },
20432
20433        /**
20434         * The interface is different from queryComponents,
20435         * which is convenient for inner usage.
20436         *
20437         * @usage
20438         * var result = findComponents(
20439         *     {mainType: 'dataZoom', query: {dataZoomId: 'abc'}}
20440         * );
20441         * var result = findComponents(
20442         *     {mainType: 'series', subType: 'pie', query: {seriesName: 'uio'}}
20443         * );
20444         * var result = findComponents(
20445         *     {mainType: 'series'},
20446         *     function (model, index) {...}
20447         * );
20448         * // result like [component0, componnet1, ...]
20449         *
20450         * @param {Object} condition
20451         * @param {string} condition.mainType Mandatory.
20452         * @param {string} [condition.subType] Optional.
20453         * @param {Object} [condition.query] like {xxxIndex, xxxId, xxxName},
20454         *        where xxx is mainType.
20455         *        If query attribute is null/undefined or has no index/id/name,
20456         *        do not filtering by query conditions, which is convenient for
20457         *        no-payload situations or when target of action is global.
20458         * @param {Function} [condition.filter] parameter: component, return boolean.
20459         * @return {Array.<module:echarts/model/Component>}
20460         */
20461        findComponents: function (condition) {
20462            var query = condition.query;
20463            var mainType = condition.mainType;
20464
20465            var queryCond = getQueryCond(query);
20466            var result = queryCond
20467                ? this.queryComponents(queryCond)
20468                : this._componentsMap.get(mainType);
20469
20470            return doFilter(filterBySubType(result, condition));
20471
20472            function getQueryCond(q) {
20473                var indexAttr = mainType + 'Index';
20474                var idAttr = mainType + 'Id';
20475                var nameAttr = mainType + 'Name';
20476                return q && (
20477                    q[indexAttr] != null
20478                    || q[idAttr] != null
20479                    || q[nameAttr] != null
20480                )
20481                    ? {
20482                        mainType: mainType,
20483                        // subType will be filtered finally.
20484                        index: q[indexAttr],
20485                        id: q[idAttr],
20486                        name: q[nameAttr]
20487                    }
20488                    : null;
20489            }
20490
20491            function doFilter(res) {
20492                return condition.filter
20493                    ? filter(res, condition.filter)
20494                    : res;
20495            }
20496        },
20497
20498        /**
20499         * @usage
20500         * eachComponent('legend', function (legendModel, index) {
20501     *     ...
20502     * });
20503         * eachComponent(function (componentType, model, index) {
20504     *     // componentType does not include subType
20505     *     // (componentType is 'xxx' but not 'xxx.aa')
20506     * });
20507         * eachComponent(
20508         *     {mainType: 'dataZoom', query: {dataZoomId: 'abc'}},
20509         *     function (model, index) {...}
20510         * );
20511         * eachComponent(
20512         *     {mainType: 'series', subType: 'pie', query: {seriesName: 'uio'}},
20513         *     function (model, index) {...}
20514         * );
20515         *
20516         * @param {string|Object=} mainType When mainType is object, the definition
20517         *                                  is the same as the method 'findComponents'.
20518         * @param {Function} cb
20519         * @param {*} context
20520         */
20521        eachComponent: function (mainType, cb, context) {
20522            var componentsMap = this._componentsMap;
20523
20524            if (typeof mainType === 'function') {
20525                context = cb;
20526                cb = mainType;
20527                componentsMap.each(function (components, componentType) {
20528                    each$1(components, function (component, index) {
20529                        cb.call(context, componentType, component, index);
20530                    });
20531                });
20532            }
20533            else if (isString(mainType)) {
20534                each$1(componentsMap.get(mainType), cb, context);
20535            }
20536            else if (isObject$1(mainType)) {
20537                var queryResult = this.findComponents(mainType);
20538                each$1(queryResult, cb, context);
20539            }
20540        },
20541
20542        /**
20543         * @param {string} name
20544         * @return {Array.<module:echarts/model/Series>}
20545         */
20546        getSeriesByName: function (name) {
20547            var series = this._componentsMap.get('series');
20548            return filter(series, function (oneSeries) {
20549                return oneSeries.name === name;
20550            });
20551        },
20552
20553        /**
20554         * @param {number} seriesIndex
20555         * @return {module:echarts/model/Series}
20556         */
20557        getSeriesByIndex: function (seriesIndex) {
20558            return this._componentsMap.get('series')[seriesIndex];
20559        },
20560
20561        /**
20562         * Get series list before filtered by type.
20563         * FIXME: rename to getRawSeriesByType?
20564         *
20565         * @param {string} subType
20566         * @return {Array.<module:echarts/model/Series>}
20567         */
20568        getSeriesByType: function (subType) {
20569            var series = this._componentsMap.get('series');
20570            return filter(series, function (oneSeries) {
20571                return oneSeries.subType === subType;
20572            });
20573        },
20574
20575        /**
20576         * @return {Array.<module:echarts/model/Series>}
20577         */
20578        getSeries: function () {
20579            return this._componentsMap.get('series').slice();
20580        },
20581
20582        /**
20583         * @return {number}
20584         */
20585        getSeriesCount: function () {
20586            return this._componentsMap.get('series').length;
20587        },
20588
20589        /**
20590         * After filtering, series may be different
20591         * frome raw series.
20592         *
20593         * @param {Function} cb
20594         * @param {*} context
20595         */
20596        eachSeries: function (cb, context) {
20597            assertSeriesInitialized(this);
20598            each$1(this._seriesIndices, function (rawSeriesIndex) {
20599                var series = this._componentsMap.get('series')[rawSeriesIndex];
20600                cb.call(context, series, rawSeriesIndex);
20601            }, this);
20602        },
20603
20604        /**
20605         * Iterate raw series before filtered.
20606         *
20607         * @param {Function} cb
20608         * @param {*} context
20609         */
20610        eachRawSeries: function (cb, context) {
20611            each$1(this._componentsMap.get('series'), cb, context);
20612        },
20613
20614        /**
20615         * After filtering, series may be different.
20616         * frome raw series.
20617         *
20618         * @parma {string} subType
20619         * @param {Function} cb
20620         * @param {*} context
20621         */
20622        eachSeriesByType: function (subType, cb, context) {
20623            assertSeriesInitialized(this);
20624            each$1(this._seriesIndices, function (rawSeriesIndex) {
20625                var series = this._componentsMap.get('series')[rawSeriesIndex];
20626                if (series.subType === subType) {
20627                    cb.call(context, series, rawSeriesIndex);
20628                }
20629            }, this);
20630        },
20631
20632        /**
20633         * Iterate raw series before filtered of given type.
20634         *
20635         * @parma {string} subType
20636         * @param {Function} cb
20637         * @param {*} context
20638         */
20639        eachRawSeriesByType: function (subType, cb, context) {
20640            return each$1(this.getSeriesByType(subType), cb, context);
20641        },
20642
20643        /**
20644         * @param {module:echarts/model/Series} seriesModel
20645         */
20646        isSeriesFiltered: function (seriesModel) {
20647            assertSeriesInitialized(this);
20648            return this._seriesIndicesMap.get(seriesModel.componentIndex) == null;
20649        },
20650
20651        /**
20652         * @return {Array.<number>}
20653         */
20654        getCurrentSeriesIndices: function () {
20655            return (this._seriesIndices || []).slice();
20656        },
20657
20658        /**
20659         * @param {Function} cb
20660         * @param {*} context
20661         */
20662        filterSeries: function (cb, context) {
20663            assertSeriesInitialized(this);
20664            var filteredSeries = filter(
20665                this._componentsMap.get('series'), cb, context
20666            );
20667            createSeriesIndices(this, filteredSeries);
20668        },
20669
20670        restoreData: function (payload) {
20671            var componentsMap = this._componentsMap;
20672
20673            createSeriesIndices(this, componentsMap.get('series'));
20674
20675            var componentTypes = [];
20676            componentsMap.each(function (components, componentType) {
20677                componentTypes.push(componentType);
20678            });
20679
20680            ComponentModel.topologicalTravel(
20681                componentTypes,
20682                ComponentModel.getAllClassMainTypes(),
20683                function (componentType, dependencies) {
20684                    each$1(componentsMap.get(componentType), function (component) {
20685                        (componentType !== 'series' || !isNotTargetSeries(component, payload))
20686                        && component.restoreData();
20687                    });
20688                }
20689            );
20690        }
20691
20692    });
20693
20694    function isNotTargetSeries(seriesModel, payload) {
20695        if (payload) {
20696            var index = payload.seiresIndex;
20697            var id = payload.seriesId;
20698            var name = payload.seriesName;
20699            return (index != null && seriesModel.componentIndex !== index)
20700                || (id != null && seriesModel.id !== id)
20701                || (name != null && seriesModel.name !== name);
20702        }
20703    }
20704
20705    /**
20706     * @inner
20707     */
20708    function mergeTheme(option, theme) {
20709        // PENDING
20710        // NOT use `colorLayer` in theme if option has `color`
20711        var notMergeColorLayer = option.color && !option.colorLayer;
20712
20713        each$1(theme, function (themeItem, name) {
20714            if (name === 'colorLayer' && notMergeColorLayer) {
20715                return;
20716            }
20717            // 濡傛灉鏈� component model 鍒欐妸鍏蜂綋鐨� merge 閫昏緫浜ょ粰璇� model 澶勭悊
20718            if (!ComponentModel.hasClass(name)) {
20719                if (typeof themeItem === 'object') {
20720                    option[name] = !option[name]
20721                        ? clone(themeItem)
20722                        : merge(option[name], themeItem, false);
20723                }
20724                else {
20725                    if (option[name] == null) {
20726                        option[name] = themeItem;
20727                    }
20728                }
20729            }
20730        });
20731    }
20732
20733    function initBase(baseOption) {
20734        baseOption = baseOption;
20735
20736        // Using OPTION_INNER_KEY to mark that this option can not be used outside,
20737        // i.e. `chart.setOption(chart.getModel().option);` is forbiden.
20738        this.option = {};
20739        this.option[OPTION_INNER_KEY] = 1;
20740
20741        /**
20742         * Init with series: [], in case of calling findSeries method
20743         * before series initialized.
20744         * @type {Object.<string, Array.<module:echarts/model/Model>>}
20745         * @private
20746         */
20747        this._componentsMap = createHashMap({series: []});
20748
20749        /**
20750         * Mapping between filtered series list and raw series list.
20751         * key: filtered series indices, value: raw series indices.
20752         * @type {Array.<nubmer>}
20753         * @private
20754         */
20755        this._seriesIndices;
20756
20757        this._seriesIndicesMap;
20758
20759        mergeTheme(baseOption, this._theme.option);
20760
20761        // TODO Needs clone when merging to the unexisted property
20762        merge(baseOption, globalDefault, false);
20763
20764        this.mergeOption(baseOption);
20765    }
20766
20767    /**
20768     * @inner
20769     * @param {Array.<string>|string} types model types
20770     * @return {Object} key: {string} type, value: {Array.<Object>} models
20771     */
20772    function getComponentsByTypes(componentsMap, types) {
20773        if (!isArray(types)) {
20774            types = types ? [types] : [];
20775        }
20776
20777        var ret = {};
20778        each$1(types, function (type) {
20779            ret[type] = (componentsMap.get(type) || []).slice();
20780        });
20781
20782        return ret;
20783    }
20784
20785    /**
20786     * @inner
20787     */
20788    function determineSubType(mainType, newCptOption, existComponent) {
20789        var subType = newCptOption.type
20790            ? newCptOption.type
20791            : existComponent
20792                ? existComponent.subType
20793                // Use determineSubType only when there is no existComponent.
20794                : ComponentModel.determineSubType(mainType, newCptOption);
20795
20796        // tooltip, markline, markpoint may always has no subType
20797        return subType;
20798    }
20799
20800    /**
20801     * @inner
20802     */
20803    function createSeriesIndices(ecModel, seriesModels) {
20804        ecModel._seriesIndicesMap = createHashMap(
20805            ecModel._seriesIndices = map(seriesModels, function (series) {
20806                return series.componentIndex;
20807            }) || []
20808        );
20809    }
20810
20811    /**
20812     * @inner
20813     */
20814    function filterBySubType(components, condition) {
20815        // Using hasOwnProperty for restrict. Consider
20816        // subType is undefined in user payload.
20817        return condition.hasOwnProperty('subType')
20818            ? filter(components, function (cpt) {
20819                return cpt.subType === condition.subType;
20820            })
20821            : components;
20822    }
20823
20824    /**
20825     * @inner
20826     */
20827    function assertSeriesInitialized(ecModel) {
20828        // Components that use _seriesIndices should depends on series component,
20829        // which make sure that their initialization is after series.
20830        if (__DEV__) {
20831            if (!ecModel._seriesIndices) {
20832                throw new Error('Option should contains series.');
20833            }
20834        }
20835    }
20836
20837    mixin(GlobalModel, colorPaletteMixin);
20838
20839    /*
20840* Licensed to the Apache Software Foundation (ASF) under one
20841* or more contributor license agreements.  See the NOTICE file
20842* distributed with this work for additional information
20843* regarding copyright ownership.  The ASF licenses this file
20844* to you under the Apache License, Version 2.0 (the
20845* "License"); you may not use this file except in compliance
20846* with the License.  You may obtain a copy of the License at
20847*
20848*   http://www.apache.org/licenses/LICENSE-2.0
20849*
20850* Unless required by applicable law or agreed to in writing,
20851* software distributed under the License is distributed on an
20852* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20853* KIND, either express or implied.  See the License for the
20854* specific language governing permissions and limitations
20855* under the License.
20856*/
20857
20858    var echartsAPIList = [
20859        'getDom', 'getZr', 'getWidth', 'getHeight', 'getDevicePixelRatio', 'dispatchAction', 'isDisposed',
20860        'on', 'off', 'getDataURL', 'getConnectedDataURL', 'getModel', 'getOption',
20861        'getViewOfComponentModel', 'getViewOfSeriesModel'
20862    ];
20863// And `getCoordinateSystems` and `getComponentByElement` will be injected in echarts.js
20864
20865    function ExtensionAPI(chartInstance) {
20866        each$1(echartsAPIList, function (name) {
20867            this[name] = bind(chartInstance[name], chartInstance);
20868        }, this);
20869    }
20870
20871    /*
20872* Licensed to the Apache Software Foundation (ASF) under one
20873* or more contributor license agreements.  See the NOTICE file
20874* distributed with this work for additional information
20875* regarding copyright ownership.  The ASF licenses this file
20876* to you under the Apache License, Version 2.0 (the
20877* "License"); you may not use this file except in compliance
20878* with the License.  You may obtain a copy of the License at
20879*
20880*   http://www.apache.org/licenses/LICENSE-2.0
20881*
20882* Unless required by applicable law or agreed to in writing,
20883* software distributed under the License is distributed on an
20884* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20885* KIND, either express or implied.  See the License for the
20886* specific language governing permissions and limitations
20887* under the License.
20888*/
20889
20890    var coordinateSystemCreators = {};
20891
20892    function CoordinateSystemManager() {
20893
20894        this._coordinateSystems = [];
20895    }
20896
20897    CoordinateSystemManager.prototype = {
20898
20899        constructor: CoordinateSystemManager,
20900
20901        create: function (ecModel, api) {
20902            var coordinateSystems = [];
20903            each$1(coordinateSystemCreators, function (creater, type) {
20904                var list = creater.create(ecModel, api);
20905                coordinateSystems = coordinateSystems.concat(list || []);
20906            });
20907
20908            this._coordinateSystems = coordinateSystems;
20909        },
20910
20911        update: function (ecModel, api) {
20912            each$1(this._coordinateSystems, function (coordSys) {
20913                coordSys.update && coordSys.update(ecModel, api);
20914            });
20915        },
20916
20917        getCoordinateSystems: function () {
20918            return this._coordinateSystems.slice();
20919        }
20920    };
20921
20922    CoordinateSystemManager.register = function (type, coordinateSystemCreator) {
20923        coordinateSystemCreators[type] = coordinateSystemCreator;
20924    };
20925
20926    CoordinateSystemManager.get = function (type) {
20927        return coordinateSystemCreators[type];
20928    };
20929
20930    /*
20931* Licensed to the Apache Software Foundation (ASF) under one
20932* or more contributor license agreements.  See the NOTICE file
20933* distributed with this work for additional information
20934* regarding copyright ownership.  The ASF licenses this file
20935* to you under the Apache License, Version 2.0 (the
20936* "License");
20936 you may not use this file except in compliance
20937* with the License.  You may obtain a copy of the License at
20938*
20939*   http://www.apache.org/licenses/LICENSE-2.0
20940*
20941* Unless required by applicable law or agreed to in writing,
20942* software distributed under the License is distributed on an
20943* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20944* KIND, either express or implied.  See the License for the
20945* specific language governing permissions and limitations
20946* under the License.
20947*/
20948
20949    /**
20950     * ECharts option manager
20951     *
20952     * @module {echarts/model/OptionManager}
20953     */
20954
20955
20956    var each$4 = each$1;
20957    var clone$3 = clone;
20958    var map$1 = map;
20959    var merge$1 = merge;
20960
20961    var QUERY_REG = /^(min|max)?(.+)$/;
20962
20963    /**
20964     * TERM EXPLANATIONS:
20965     *
20966     * [option]:
20967     *
20968     *     An object that contains definitions of components. For example:
20969     *     var option = {
20970 *         title: {...},
20971 *         legend: {...},
20972 *         visualMap: {...},
20973 *         series: [
20974 *             {data: [...]},
20975 *             {data: [...]},
20976 *             ...
20977 *         ]
20978 *     };
20979     *
20980     * [rawOption]:
20981     *
20982     *     An object input to echarts.setOption. 'rawOption' may be an
20983     *     'option', or may be an object contains multi-options. For example:
20984     *     var option = {
20985 *         baseOption: {
20986 *             title: {...},
20987 *             legend: {...},
20988 *             series: [
20989 *                 {data: [...]},
20990 *                 {data: [...]},
20991 *                 ...
20992 *             ]
20993 *         },
20994 *         timeline: {...},
20995 *         options: [
20996 *             {title: {...}, series: {data: [...]}},
20997 *             {title: {...}, series: {data: [...]}},
20998 *             ...
20999 *         ],
21000 *         media: [
21001 *             {
21002 *                 query: {maxWidth: 320},
21003 *                 option: {series: {x: 20}, visualMap: {show: false}}
21004 *             },
21005 *             {
21006 *                 query: {minWidth: 320, maxWidth: 720},
21007 *                 option: {series: {x: 500}, visualMap: {show: true}}
21008 *             },
21009 *             {
21010 *                 option: {series: {x: 1200}, visualMap: {show: true}}
21011 *             }
21012 *         ]
21013 *     };
21014     *
21015     * @alias module:echarts/model/OptionManager
21016     * @param {module:echarts/ExtensionAPI} api
21017     */
21018    function OptionManager(api) {
21019
21020        /**
21021         * @private
21022         * @type {module:echarts/ExtensionAPI}
21023         */
21024        this._api = api;
21025
21026        /**
21027         * @private
21028         * @type {Array.<number>}
21029         */
21030        this._timelineOptions = [];
21031
21032        /**
21033         * @private
21034         * @type {Array.<Object>}
21035         */
21036        this._mediaList = [];
21037
21038        /**
21039         * @private
21040         * @type {Object}
21041         */
21042        this._mediaDefault;
21043
21044        /**
21045         * -1, means default.
21046         * empty means no media.
21047         * @private
21048         * @type {Array.<number>}
21049         */
21050        this._currentMediaIndices = [];
21051
21052        /**
21053         * @private
21054         * @type {Object}
21055         */
21056        this._optionBackup;
21057
21058        /**
21059         * @private
21060         * @type {Object}
21061         */
21062        this._newBaseOption;
21063    }
21064
21065// timeline.notMerge is not supported in ec3. Firstly there is rearly
21066// case that notMerge is needed. Secondly supporting 'notMerge' requires
21067// rawOption cloned and backuped when timeline changed, which does no
21068// good to performance. What's more, that both timeline and setOption
21069// method supply 'notMerge' brings complex and some problems.
21070// Consider this case:
21071// (step1) chart.setOption({timeline: {notMerge: false}, ...}, false);
21072// (step2) chart.setOption({timeline: {notMerge: true}, ...}, false);
21073
21074    OptionManager.prototype = {
21075
21076        constructor: OptionManager,
21077
21078        /**
21079         * @public
21080         * @param {Object} rawOption Raw option.
21081         * @param {module:echarts/model/Global} ecModel
21082         * @param {Array.<Function>} optionPreprocessorFuncs
21083         * @return {Object} Init option
21084         */
21085        setOption: function (rawOption, optionPreprocessorFuncs) {
21086            if (rawOption) {
21087                // That set dat primitive is dangerous if user reuse the data when setOption again.
21088                each$1(normalizeToArray(rawOption.series), function (series) {
21089                    series && series.data && isTypedArray(series.data) && setAsPrimitive(series.data);
21090                });
21091            }
21092
21093            // Caution: some series modify option data, if do not clone,
21094            // it should ensure that the repeat modify correctly
21095            // (create a new object when modify itself).
21096            rawOption = clone$3(rawOption, true);
21097
21098            // FIXME
21099            // 濡傛灉 timeline options 鎴栬€� media 涓缃簡鏌愪釜灞炴€э紝鑰宐aseOption涓病鏈夎缃紝鍒欒繘琛岃鍛娿€�
21100
21101            var oldOptionBackup = this._optionBackup;
21102            var newParsedOption = parseRawOption.call(
21103                this, rawOption, optionPreprocessorFuncs, !oldOptionBackup
21104            );
21105            this._newBaseOption = newParsedOption.baseOption;
21106
21107            // For setOption at second time (using merge mode);
21108            if (oldOptionBackup) {
21109                // Only baseOption can be merged.
21110                mergeOption(oldOptionBackup.baseOption, newParsedOption.baseOption);
21111
21112                // For simplicity, timeline options and media options do not support merge,
21113                // that is, if you `setOption` twice and both has timeline options, the latter
21114                // timeline opitons will not be merged to the formers, but just substitude them.
21115                if (newParsedOption.timelineOptions.length) {
21116                    oldOptionBackup.timelineOptions = newParsedOption.timelineOptions;
21117                }
21118                if (newParsedOption.mediaList.length) {
21119                    oldOptionBackup.mediaList = newParsedOption.mediaList;
21120                }
21121                if (newParsedOption.mediaDefault) {
21122                    oldOptionBackup.mediaDefault = newParsedOption.mediaDefault;
21123                }
21124            }
21125            else {
21126                this._optionBackup = newParsedOption;
21127            }
21128        },
21129
21130        /**
21131         * @param {boolean} isRecreate
21132         * @return {Object}
21133         */
21134        mountOption: function (isRecreate) {
21135            var optionBackup = this._optionBackup;
21136
21137            // TODO
21138            // 濡傛灉娌℃湁reset鍔熻兘鍒欎笉clone銆�
21139
21140            this._timelineOptions = map$1(optionBackup.timelineOptions, clone$3);
21141            this._mediaList = map$1(optionBackup.mediaList, clone$3);
21142            this._mediaDefault = clone$3(optionBackup.mediaDefault);
21143            this._currentMediaIndices = [];
21144
21145            return clone$3(isRecreate
21146                // this._optionBackup.baseOption, which is created at the first `setOption`
21147                // called, and is merged into every new option by inner method `mergeOption`
21148                // each time `setOption` called, can be only used in `isRecreate`, because
21149                // its reliability is under suspicion. In other cases option merge is
21150                // performed by `model.mergeOption`.
21151                ? optionBackup.baseOption : this._newBaseOption
21152            );
21153        },
21154
21155        /**
21156         * @param {module:echarts/model/Global} ecModel
21157         * @return {Object}
21158         */
21159        getTimelineOption: function (ecModel) {
21160            var option;
21161            var timelineOptions = this._timelineOptions;
21162
21163            if (timelineOptions.length) {
21164                // getTimelineOption can only be called after ecModel inited,
21165                // so we can get currentIndex from timelineModel.
21166                var timelineModel = ecModel.getComponent('timeline');
21167                if (timelineModel) {
21168                    option = clone$3(
21169                        timelineOptions[timelineModel.getCurrentIndex()],
21170                        true
21171                    );
21172                }
21173            }
21174
21175            return option;
21176        },
21177
21178        /**
21179         * @param {module:echarts/model/Global} ecModel
21180         * @return {Array.<Object>}
21181         */
21182        getMediaOption: function (ecModel) {
21183            var ecWidth = this._api.getWidth();
21184            var ecHeight = this._api.getHeight();
21185            var mediaList = this._mediaList;
21186            var mediaDefault = this._mediaDefault;
21187            var indices = [];
21188            var result = [];
21189
21190            // No media defined.
21191            if (!mediaList.length && !mediaDefault) {
21192                return result;
21193            }
21194
21195            // Multi media may be applied, the latter defined media has higher priority.
21196            for (var i = 0, len = mediaList.length; i < len; i++) {
21197                if (applyMediaQuery(mediaList[i].query, ecWidth, ecHeight)) {
21198                    indices.push(i);
21199                }
21200            }
21201
21202            // FIXME
21203            // 鏄惁mediaDefault搴旇寮哄埗鐢ㄦ埛璁剧疆锛屽惁鍒欏彲鑳戒慨鏀逛笉鑳藉洖褰掋€�
21204            if (!indices.length && mediaDefault) {
21205                indices = [-1];
21206            }
21207
21208            if (indices.length && !indicesEquals(indices, this._currentMediaIndices)) {
21209                result = map$1(indices, function (index) {
21210                    return clone$3(
21211                        index === -1 ? mediaDefault.option : mediaList[index].option
21212                    );
21213                });
21214            }
21215            // Otherwise return nothing.
21216
21217            this._currentMediaIndices = indices;
21218
21219            return result;
21220        }
21221    };
21222
21223    function parseRawOption(rawOption, optionPreprocessorFuncs, isNew) {
21224        var timelineOptions = [];
21225        var mediaList = [];
21226        var mediaDefault;
21227        var baseOption;
21228
21229        // Compatible with ec2.
21230        var timelineOpt = rawOption.timeline;
21231
21232        if (rawOption.baseOption) {
21233            baseOption = rawOption.baseOption;
21234        }
21235
21236        // For timeline
21237        if (timelineOpt || rawOption.options) {
21238            baseOption = baseOption || {};
21239            timelineOptions = (rawOption.options || []).slice();
21240        }
21241
21242        // For media query
21243        if (rawOption.media) {
21244            baseOption = baseOption || {};
21245            var media = rawOption.media;
21246            each$4(media, function (singleMedia) {
21247                if (singleMedia && singleMedia.option) {
21248                    if (singleMedia.query) {
21249                        mediaList.push(singleMedia);
21250                    }
21251                    else if (!mediaDefault) {
21252                        // Use the first media default.
21253                        mediaDefault = singleMedia;
21254                    }
21255                }
21256            });
21257        }
21258
21259        // For normal option
21260        if (!baseOption) {
21261            baseOption = rawOption;
21262        }
21263
21264        // Set timelineOpt to baseOption in ec3,
21265        // which is convenient for merge option.
21266        if (!baseOption.timeline) {
21267            baseOption.timeline = timelineOpt;
21268        }
21269
21270        // Preprocess.
21271        each$4([baseOption].concat(timelineOptions)
21272                .concat(map(mediaList, function (media) {
21273                    return media.option;
21274                })),
21275            function (option) {
21276                each$4(optionPreprocessorFuncs, function (preProcess) {
21277                    preProcess(option, isNew);
21278                });
21279            }
21280        );
21281
21282        return {
21283            baseOption: baseOption,
21284            timelineOptions: timelineOptions,
21285            mediaDefault: mediaDefault,
21286            mediaList: mediaList
21287        };
21288    }
21289
21290    /**
21291     * @see <http://www.w3.org/TR/css3-mediaqueries/#media1>
21292     * Support: width, height, aspectRatio
21293     * Can use max or min as prefix.
21294     */
21295    function applyMediaQuery(query, ecWidth, ecHeight) {
21296        var realMap = {
21297            width: ecWidth,
21298            height: ecHeight,
21299            aspectratio: ecWidth / ecHeight // lowser case for convenientce.
21300        };
21301
21302        var applicatable = true;
21303
21304        each$1(query, function (value, attr) {
21305            var matched = attr.match(QUERY_REG);
21306
21307            if (!matched || !matched[1] || !matched[2]) {
21308                return;
21309            }
21310
21311            var operator = matched[1];
21312            var realAttr = matched[2].toLowerCase();
21313
21314            if (!compare(realMap[realAttr], value, operator)) {
21315                applicatable = false;
21316            }
21317        });
21318
21319        return applicatable;
21320    }
21321
21322    function compare(real, expect, operator) {
21323        if (operator === 'min') {
21324            return real >= expect;
21325        }
21326        else if (operator === 'max') {
21327            return real <= expect;
21328        }
21329        else { // Equals
21330            return real === expect;
21331        }
21332    }
21333
21334    function indicesEquals(indices1, indices2) {
21335        // indices is always order by asc and has only finite number.
21336        return indices1.join(',') === indices2.join(',');
21337    }
21338
21339    /**
21340     * Consider case:
21341     * `chart.setOption(opt1);`
21342     * Then user do some interaction like dataZoom, dataView changing.
21343     * `chart.setOption(opt2);`
21344     * Then user press 'reset button' in toolbox.
21345     *
21346     * After doing that all of the interaction effects should be reset, the
21347     * chart should be the same as the result of invoke
21348     * `chart.setOption(opt1); chart.setOption(opt2);`.
21349     *
21350     * Although it is not able ensure that
21351     * `chart.setOption(opt1); chart.setOption(opt2);` is equivalents to
21352     * `chart.setOption(merge(opt1, opt2));` exactly,
21353     * this might be the only simple way to implement that feature.
21354     *
21355     * MEMO: We've considered some other approaches:
21356     * 1. Each model handle its self restoration but not uniform treatment.
21357     *     (Too complex in logic and error-prone)
21358     * 2. Use a shadow ecModel. (Performace expensive)
21359     */
21360    function mergeOption(oldOption, newOption) {
21361        newOption = newOption || {};
21362
21363        each$4(newOption, function (newCptOpt, mainType) {
21364            if (newCptOpt == null) {
21365                return;
21366            }
21367
21368            var oldCptOpt = oldOption[mainType];
21369
21370            if (!ComponentModel.hasClass(mainType)) {
21371                oldOption[mainType] = merge$1(oldCptOpt, newCptOpt, true);
21372            }
21373            else {
21374                newCptOpt = normalizeToArray(newCptOpt);
21375                oldCptOpt = normalizeToArray(oldCptOpt);
21376
21377                var mapResult = mappingToExists(oldCptOpt, newCptOpt);
21378
21379                oldOption[mainType] = map$1(mapResult, function (item) {
21380                    return (item.option && item.exist)
21381                        ? merge$1(item.exist, item.option, true)
21382                        : (item.exist || item.option);
21383                });
21384            }
21385        });
21386    }
21387
21388    /*
21389* Licensed to the Apache Software Foundation (ASF) under one
21390* or more contributor license agreements.  See the NOTICE file
21391* distributed with this work for additional information
21392* regarding copyright ownership.  The ASF licenses this file
21393* to you under the Apache License, Version 2.0 (the
21394* "License");
vendor: 11,072 bytes, lines 21394-21692
21394 you may not use this file except in compliance
21395* with the License.  You may obtain a copy of the License at
21396*
21397*   http://www.apache.org/licenses/LICENSE-2.0
21398*
21399* Unless required by applicable law or agreed to in writing,
21400* software distributed under the License is distributed on an
21401* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21402* KIND, either express or implied.  See the License for the
21403* specific language governing permissions and limitations
21404* under the License.
21405*/
21406
21407    var each$5 = each$1;
21408    var isObject$3 = isObject$1;
21409
21410    var POSSIBLE_STYLES = [
21411        'areaStyle', 'lineStyle', 'nodeStyle', 'linkStyle',
21412        'chordStyle', 'label', 'labelLine'
21413    ];
21414
21415    function compatEC2ItemStyle(opt) {
21416        var itemStyleOpt = opt && opt.itemStyle;
21417        if (!itemStyleOpt) {
21418            return;
21419        }
21420        for (var i = 0, len = POSSIBLE_STYLES.length; i < len; i++) {
21421            var styleName = POSSIBLE_STYLES[i];
21422            var normalItemStyleOpt = itemStyleOpt.normal;
21423            var emphasisItemStyleOpt = itemStyleOpt.emphasis;
21424            if (normalItemStyleOpt && normalItemStyleOpt[styleName]) {
21425                opt[styleName] = opt[styleName] || {};
21426                if (!opt[styleName].normal) {
21427                    opt[styleName].normal = normalItemStyleOpt[styleName];
21428                }
21429                else {
21430                    merge(opt[styleName].normal, normalItemStyleOpt[styleName]);
21431                }
21432                normalItemStyleOpt[styleName] = null;
21433            }
21434            if (emphasisItemStyleOpt && emphasisItemStyleOpt[styleName]) {
21435                opt[styleName] = opt[styleName] || {};
21436                if (!opt[styleName].emphasis) {
21437                    opt[styleName].emphasis = emphasisItemStyleOpt[styleName];
21438                }
21439                else {
21440                    merge(opt[styleName].emphasis, emphasisItemStyleOpt[styleName]);
21441                }
21442                emphasisItemStyleOpt[styleName] = null;
21443            }
21444        }
21445    }
21446
21447    function convertNormalEmphasis(opt, optType, useExtend) {
21448        if (opt && opt[optType] && (opt[optType].normal || opt[optType].emphasis)) {
21449            var normalOpt = opt[optType].normal;
21450            var emphasisOpt = opt[optType].emphasis;
21451
21452            if (normalOpt) {
21453                // Timeline controlStyle has other properties besides normal and emphasis
21454                if (useExtend) {
21455                    opt[optType].normal = opt[optType].emphasis = null;
21456                    defaults(opt[optType], normalOpt);
21457                }
21458                else {
21459                    opt[optType] = normalOpt;
21460                }
21461            }
21462            if (emphasisOpt) {
21463                opt.emphasis = opt.emphasis || {};
21464                opt.emphasis[optType] = emphasisOpt;
21465            }
21466        }
21467    }
21468    function removeEC3NormalStatus(opt) {
21469        convertNormalEmphasis(opt, 'itemStyle');
21470        convertNormalEmphasis(opt, 'lineStyle');
21471        convertNormalEmphasis(opt, 'areaStyle');
21472        convertNormalEmphasis(opt, 'label');
21473        convertNormalEmphasis(opt, 'labelLine');
21474        // treemap
21475        convertNormalEmphasis(opt, 'upperLabel');
21476        // graph
21477        convertNormalEmphasis(opt, 'edgeLabel');
21478    }
21479
21480    function compatTextStyle(opt, propName) {
21481        // Check whether is not object (string\null\undefined ...)
21482        var labelOptSingle = isObject$3(opt) && opt[propName];
21483        var textStyle = isObject$3(labelOptSingle) && labelOptSingle.textStyle;
21484        if (textStyle) {
21485            for (var i = 0, len = TEXT_STYLE_OPTIONS.length; i < len; i++) {
21486                var propName = TEXT_STYLE_OPTIONS[i];
21487                if (textStyle.hasOwnProperty(propName)) {
21488                    labelOptSingle[propName] = textStyle[propName];
21489                }
21490            }
21491        }
21492    }
21493
21494    function compatEC3CommonStyles(opt) {
21495        if (opt) {
21496            removeEC3NormalStatus(opt);
21497            compatTextStyle(opt, 'label');
21498            opt.emphasis && compatTextStyle(opt.emphasis, 'label');
21499        }
21500    }
21501
21502    function processSeries(seriesOpt) {
21503        if (!isObject$3(seriesOpt)) {
21504            return;
21505        }
21506
21507        compatEC2ItemStyle(seriesOpt);
21508        removeEC3NormalStatus(seriesOpt);
21509
21510        compatTextStyle(seriesOpt, 'label');
21511        // treemap
21512        compatTextStyle(seriesOpt, 'upperLabel');
21513        // graph
21514        compatTextStyle(seriesOpt, 'edgeLabel');
21515        if (seriesOpt.emphasis) {
21516            compatTextStyle(seriesOpt.emphasis, 'label');
21517            // treemap
21518            compatTextStyle(seriesOpt.emphasis, 'upperLabel');
21519            // graph
21520            compatTextStyle(seriesOpt.emphasis, 'edgeLabel');
21521        }
21522
21523        var markPoint = seriesOpt.markPoint;
21524        if (markPoint) {
21525            compatEC2ItemStyle(markPoint);
21526            compatEC3CommonStyles(markPoint);
21527        }
21528
21529        var markLine = seriesOpt.markLine;
21530        if (markLine) {
21531            compatEC2ItemStyle(markLine);
21532            compatEC3CommonStyles(markLine);
21533        }
21534
21535        var markArea = seriesOpt.markArea;
21536        if (markArea) {
21537            compatEC3CommonStyles(markArea);
21538        }
21539
21540        var data = seriesOpt.data;
21541
21542        // Break with ec3: if `setOption` again, there may be no `type` in option,
21543        // then the backward compat based on option type will not be performed.
21544
21545        if (seriesOpt.type === 'graph') {
21546            data = data || seriesOpt.nodes;
21547            var edgeData = seriesOpt.links || seriesOpt.edges;
21548            if (edgeData && !isTypedArray(edgeData)) {
21549                for (var i = 0; i < edgeData.length; i++) {
21550                    compatEC3CommonStyles(edgeData[i]);
21551                }
21552            }
21553            each$1(seriesOpt.categories, function (opt) {
21554                removeEC3NormalStatus(opt);
21555            });
21556        }
21557
21558        if (data && !isTypedArray(data)) {
21559            for (var i = 0; i < data.length; i++) {
21560                compatEC3CommonStyles(data[i]);
21561            }
21562        }
21563
21564        // mark point data
21565        var markPoint = seriesOpt.markPoint;
21566        if (markPoint && markPoint.data) {
21567            var mpData = markPoint.data;
21568            for (var i = 0; i < mpData.length; i++) {
21569                compatEC3CommonStyles(mpData[i]);
21570            }
21571        }
21572        // mark line data
21573        var markLine = seriesOpt.markLine;
21574        if (markLine && markLine.data) {
21575            var mlData = markLine.data;
21576            for (var i = 0; i < mlData.length; i++) {
21577                if (isArray(mlData[i])) {
21578                    compatEC3CommonStyles(mlData[i][0]);
21579                    compatEC3CommonStyles(mlData[i][1]);
21580                }
21581                else {
21582                    compatEC3CommonStyles(mlData[i]);
21583                }
21584            }
21585        }
21586
21587        // Series
21588        if (seriesOpt.type === 'gauge') {
21589            compatTextStyle(seriesOpt, 'axisLabel');
21590            compatTextStyle(seriesOpt, 'title');
21591            compatTextStyle(seriesOpt, 'detail');
21592        }
21593        else if (seriesOpt.type === 'treemap') {
21594            convertNormalEmphasis(seriesOpt.breadcrumb, 'itemStyle');
21595            each$1(seriesOpt.levels, function (opt) {
21596                removeEC3NormalStatus(opt);
21597            });
21598        }
21599        else if (seriesOpt.type === 'tree') {
21600            removeEC3NormalStatus(seriesOpt.leaves);
21601        }
21602        // sunburst starts from ec4, so it does not need to compat levels.
21603    }
21604
21605    function toArr(o) {
21606        return isArray(o) ? o : o ? [o] : [];
21607    }
21608
21609    function toObj(o) {
21610        return (isArray(o) ? o[0] : o) || {};
21611    }
21612
21613    var compatStyle = function (option, isTheme) {
21614        each$5(toArr(option.series), function (seriesOpt) {
21615            isObject$3(seriesOpt) && processSeries(seriesOpt);
21616        });
21617
21618        var axes = ['xAxis', 'yAxis', 'radiusAxis', 'angleAxis', 'singleAxis', 'parallelAxis', 'radar'];
21619        isTheme && axes.push('valueAxis', 'categoryAxis', 'logAxis', 'timeAxis');
21620
21621        each$5(
21622            axes,
21623            function (axisName) {
21624                each$5(toArr(option[axisName]), function (axisOpt) {
21625                    if (axisOpt) {
21626                        compatTextStyle(axisOpt, 'axisLabel');
21627                        compatTextStyle(axisOpt.axisPointer, 'label');
21628                    }
21629                });
21630            }
21631        );
21632
21633        each$5(toArr(option.parallel), function (parallelOpt) {
21634            var parallelAxisDefault = parallelOpt && parallelOpt.parallelAxisDefault;
21635            compatTextStyle(parallelAxisDefault, 'axisLabel');
21636            compatTextStyle(parallelAxisDefault && parallelAxisDefault.axisPointer, 'label');
21637        });
21638
21639        each$5(toArr(option.calendar), function (calendarOpt) {
21640            convertNormalEmphasis(calendarOpt, 'itemStyle');
21641            compatTextStyle(calendarOpt, 'dayLabel');
21642            compatTextStyle(calendarOpt, 'monthLabel');
21643            compatTextStyle(calendarOpt, 'yearLabel');
21644        });
21645
21646        // radar.name.textStyle
21647        each$5(toArr(option.radar), function (radarOpt) {
21648            compatTextStyle(radarOpt, 'name');
21649        });
21650
21651        each$5(toArr(option.geo), function (geoOpt) {
21652            if (isObject$3(geoOpt)) {
21653                compatEC3CommonStyles(geoOpt);
21654                each$5(toArr(geoOpt.regions), function (regionObj) {
21655                    compatEC3CommonStyles(regionObj);
21656                });
21657            }
21658        });
21659
21660        each$5(toArr(option.timeline), function (timelineOpt) {
21661            compatEC3CommonStyles(timelineOpt);
21662            convertNormalEmphasis(timelineOpt, 'label');
21663            convertNormalEmphasis(timelineOpt, 'itemStyle');
21664            convertNormalEmphasis(timelineOpt, 'controlStyle', true);
21665
21666            var data = timelineOpt.data;
21667            isArray(data) && each$1(data, function (item) {
21668                if (isObject$1(item)) {
21669                    convertNormalEmphasis(item, 'label');
21670                    convertNormalEmphasis(item, 'itemStyle');
21671                }
21672            });
21673        });
21674
21675        each$5(toArr(option.toolbox), function (toolboxOpt) {
21676            convertNormalEmphasis(toolboxOpt, 'iconStyle');
21677            each$5(toolboxOpt.feature, function (featureOpt) {
21678                convertNormalEmphasis(featureOpt, 'iconStyle');
21679            });
21680        });
21681
21682        compatTextStyle(toObj(option.axisPointer), 'label');
21683        compatTextStyle(toObj(option.tooltip).axisPointer, 'label');
21684    };
21685
21686    /*
21687* Licensed to the Apache Software Foundation (ASF) under one
21688* or more contributor license agreements.  See the NOTICE file
21689* distributed with this work for additional information
21690* regarding copyright ownership.  The ASF licenses this file
21691* to you under the Apache License, Version 2.0 (the
21692* "License");
21692 you may not use this file except in compliance
21693* with the License.  You may obtain a copy of the License at
21694*
21695*   http://www.apache.org/licenses/LICENSE-2.0
21696*
21697* Unless required by applicable law or agreed to in writing,
21698* software distributed under the License is distributed on an
21699* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21700* KIND, either express or implied.  See the License for the
21701* specific language governing permissions and limitations
21702* under the License.
21703*/
21704
21705// Compatitable with 2.0
21706
21707    function get(opt, path) {
21708        path = path.split(',');
21709        var obj = opt;
21710        for (var i = 0; i < path.length; i++) {
21711            obj = obj && obj[path[i]];
21712            if (obj == null) {
21713                break;
21714            }
21715        }
21716        return obj;
21717    }
21718
21719    function set$1(opt, path, val, overwrite) {
21720        path = path.split(',');
21721        var obj = opt;
21722        var key;
21723        for (var i = 0; i < path.length - 1; i++) {
21724            key = path[i];
21725            if (obj[key] == null) {
21726                obj[key] = {};
21727            }
21728            obj = obj[key];
21729        }
21730        if (overwrite || obj[path[i]] == null) {
21731            obj[path[i]] = val;
21732        }
21733    }
21734
21735    function compatLayoutProperties(option) {
21736        each$1(LAYOUT_PROPERTIES, function (prop) {
21737            if (prop[0] in option && !(prop[1] in option)) {
21738                option[prop[1]] = option[prop[0]];
21739            }
21740        });
21741    }
21742
21743    var LAYOUT_PROPERTIES = [
21744        ['x', 'left'], ['y', 'top'], ['x2', 'right'], ['y2', 'bottom']
21745    ];
21746
21747    var COMPATITABLE_COMPONENTS = [
21748        'grid', 'geo', 'parallel', 'legend', 'toolbox', 'title', 'visualMap', 'dataZoom', 'timeline'
21749    ];
21750
21751    var backwardCompat = function (option, isTheme) {
21752        compatStyle(option, isTheme);
21753
21754        // Make sure series array for model initialization.
21755        option.series = normalizeToArray(option.series);
21756
21757        each$1(option.series, function (seriesOpt) {
21758            if (!isObject$1(seriesOpt)) {
21759                return;
21760            }
21761
21762            var seriesType = seriesOpt.type;
21763
21764            if (seriesType === 'pie' || seriesType === 'gauge') {
21765                if (seriesOpt.clockWise != null) {
21766                    seriesOpt.clockwise = seriesOpt.clockWise;
21767                }
21768            }
21769            if (seriesType === 'gauge') {
21770                var pointerColor = get(seriesOpt, 'pointer.color');
21771                pointerColor != null
21772                && set$1(seriesOpt, 'itemStyle.normal.color', pointerColor);
21773            }
21774
21775            compatLayoutProperties(seriesOpt);
21776        });
21777
21778        // dataRange has changed to visualMap
21779        if (option.dataRange) {
21780            option.visualMap = option.dataRange;
21781        }
21782
21783        each$1(COMPATITABLE_COMPONENTS, function (componentName) {
21784            var options = option[componentName];
21785            if (options) {
21786                if (!isArray(options)) {
21787                    options = [options];
21788                }
21789                each$1(options, function (option) {
21790                    compatLayoutProperties(option);
21791                });
21792            }
21793        });
21794    };
21795
21796    /*
21797* Licensed to the Apache Software Foundation (ASF) under one
21798* or more contributor license agreements.  See the NOTICE file
21799* distributed with this work for additional information
21800* regarding copyright ownership.  The ASF licenses this file
21801* to you under the Apache License, Version 2.0 (the
21802* "License"); you may not use this file except in compliance
21803* with the License.  You may obtain a copy of the License at
21804*
21805*   http://www.apache.org/licenses/LICENSE-2.0
21806*
21807* Unless required by applicable law or agreed to in writing,
21808* software distributed under the License is distributed on an
21809* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21810* KIND, either express or implied.  See the License for the
21811* specific language governing permissions and limitations
21812* under the License.
21813*/
21814
21815// (1) [Caution]: the logic is correct based on the premises:
21816//     data processing stage is blocked in stream.
21817//     See <module:echarts/stream/Scheduler#performDataProcessorTasks>
21818// (2) Only register once when import repeatly.
21819//     Should be executed before after series filtered and before stack calculation.
21820    var dataStack = function (ecModel) {
21821        var stackInfoMap = createHashMap();
21822        ecModel.eachSeries(function (seriesModel) {
21823            var stack = seriesModel.get('stack');
21824            // Compatibal: when `stack` is set as '', do not stack.
21825            if (stack) {
21826                var stackInfoList = stackInfoMap.get(stack) || stackInfoMap.set(stack, []);
21827                var data = seriesModel.getData();
21828
21829                var stackInfo = {
21830                    // Used for calculate axis extent automatically.
21831                    stackResultDimension: data.getCalculationInfo('stackResultDimension'),
21832                    stackedOverDimension: data.getCalculationInfo('stackedOverDimension'),
21833                    stackedDimension: data.getCalculationInfo('stackedDimension'),
21834                    stackedByDimension: data.getCalculationInfo('stackedByDimension'),
21835                    isStackedByIndex: data.getCalculationInfo('isStackedByIndex'),
21836                    data: data,
21837                    seriesModel: seriesModel
21838                };
21839
21840                // If stacked on axis that do not support data stack.
21841                if (!stackInfo.stackedDimension
21842                    || !(stackInfo.isStackedByIndex || stackInfo.stackedByDimension)
21843                ) {
21844                    return;
21845                }
21846
21847                stackInfoList.length && data.setCalculationInfo(
21848                    'stackedOnSeries', stackInfoList[stackInfoList.length - 1].seriesModel
21849                );
21850
21851                stackInfoList.push(stackInfo);
21852            }
21853        });
21854
21855        stackInfoMap.each(calculateStack);
21856    };
21857
21858    function calculateStack(stackInfoList) {
21859        each$1(stackInfoList, function (targetStackInfo, idxInStack) {
21860            var resultVal = [];
21861            var resultNaN = [NaN, NaN];
21862            var dims = [targetStackInfo.stackResultDimension, targetStackInfo.stackedOverDimension];
21863            var targetData = targetStackInfo.data;
21864            var isStackedByIndex = targetStackInfo.isStackedByIndex;
21865
21866            // Should not write on raw data, because stack series model list changes
21867            // depending on legend selection.
21868            var newData = targetData.map(dims, function (v0, v1, dataIndex) {
21869                var sum = targetData.get(targetStackInfo.stackedDimension, dataIndex);
21870
21871                // Consider `connectNulls` of line area, if value is NaN, stackedOver
21872                // should also be NaN, to draw a appropriate belt area.
21873                if (isNaN(sum)) {
21874                    return resultNaN;
21875                }
21876
21877                var byValue;
21878                var stackedDataRawIndex;
21879
21880                if (isStackedByIndex) {
21881                    stackedDataRawIndex = targetData.getRawIndex(dataIndex);
21882                }
21883                else {
21884                    byValue = targetData.get(targetStackInfo.stackedByDimension, dataIndex);
21885                }
21886
21887                // If stackOver is NaN, chart view will render point on value start.
21888                var stackedOver = NaN;
21889
21890                for (var j = idxInStack - 1; j >= 0; j--) {
21891                    var stackInfo = stackInfoList[j];
21892
21893                    // Has been optimized by inverted indices on `stackedByDimension`.
21894                    if (!isStackedByIndex) {
21895                        stackedDataRawIndex = stackInfo.data.rawIndexOf(stackInfo.stackedByDimension, byValue);
21896                    }
21897
21898                    if (stackedDataRawIndex >= 0) {
21899                        var val = stackInfo.data.getByRawIndex(stackInfo.stackResultDimension, stackedDataRawIndex);
21900
21901                        // Considering positive stack, negative stack and empty data
21902                        if ((sum >= 0 && val > 0) // Positive stack
21903                            || (sum <= 0 && val < 0) // Negative stack
21904                        ) {
21905                            sum += val;
21906                            stackedOver = val;
21907                            break;
21908                        }
21909                    }
21910                }
21911
21912                resultVal[0] = sum;
21913                resultVal[1] = stackedOver;
21914
21915                return resultVal;
21916            });
21917
21918            targetData.hostModel.setData(newData);
21919            // Update for consequent calculation
21920            targetStackInfo.data = newData;
21921        });
21922    }
21923
21924    /*
21925* Licensed to the Apache Software Foundation (ASF) under one
21926* or more contributor license agreements.  See the NOTICE file
21927* distributed with this work for additional information
21928* regarding copyright ownership.  The ASF licenses this file
21929* to you under the Apache License, Version 2.0 (the
21930* "License");
21930 you may not use this file except in compliance
21931* with the License.  You may obtain a copy of the License at
21932*
21933*   http://www.apache.org/licenses/LICENSE-2.0
21934*
21935* Unless required by applicable law or agreed to in writing,
21936* software distributed under the License is distributed on an
21937* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21938* KIND, either express or implied.  See the License for the
21939* specific language governing permissions and limitations
21940* under the License.
21941*/
21942
21943// TODO
21944// ??? refactor? check the outer usage of data provider.
21945// merge with defaultDimValueGetter?
21946
21947    /**
21948     * If normal array used, mutable chunk size is supported.
21949     * If typed array used, chunk size must be fixed.
21950     */
21951    function DefaultDataProvider(source, dimSize) {
21952        if (!Source.isInstance(source)) {
21953            source = Source.seriesDataToSource(source);
21954        }
21955        this._source = source;
21956
21957        var data = this._data = source.data;
21958        var sourceFormat = source.sourceFormat;
21959
21960        // Typed array. TODO IE10+?
21961        if (sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) {
21962            if (__DEV__) {
21963                if (dimSize == null) {
21964                    throw new Error('Typed array data must specify dimension size');
21965                }
21966            }
21967            this._offset = 0;
21968            this._dimSize = dimSize;
21969            this._data = data;
21970        }
21971
21972        var methods = providerMethods[
21973            sourceFormat === SOURCE_FORMAT_ARRAY_ROWS
21974                ? sourceFormat + '_' + source.seriesLayoutBy
21975                : sourceFormat
21976            ];
21977
21978        if (__DEV__) {
21979            assert$1(methods, 'Invalide sourceFormat: ' + sourceFormat);
21980        }
21981
21982        extend(this, methods);
21983    }
21984
21985    var providerProto = DefaultDataProvider.prototype;
21986// If data is pure without style configuration
21987    providerProto.pure = false;
21988// If data is persistent and will not be released after use.
21989    providerProto.persistent = true;
21990
21991// ???! FIXME legacy data provider do not has method getSource
21992    providerProto.getSource = function () {
21993        return this._source;
21994    };
21995
21996    var providerMethods = {
21997
21998        'arrayRows_column': {
21999            pure: true,
22000            count: function () {
22001                return Math.max(0, this._data.length - this._source.startIndex);
22002            },
22003            getItem: function (idx) {
22004                return this._data[idx + this._source.startIndex];
22005            },
22006            appendData: appendDataSimply
22007        },
22008
22009        'arrayRows_row': {
22010            pure: true,
22011            count: function () {
22012                var row = this._data[0];
22013                return row ? Math.max(0, row.length - this._source.startIndex) : 0;
22014            },
22015            getItem: function (idx) {
22016                idx += this._source.startIndex;
22017                var item = [];
22018                var data = this._data;
22019                for (var i = 0; i < data.length; i++) {
22020                    var row = data[i];
22021                    item.push(row ? row[idx] : null);
22022                }
22023                return item;
22024            },
22025            appendData: function () {
22026                throw new Error('Do not support appendData when set seriesLayoutBy: "row".');
22027            }
22028        },
22029
22030        'objectRows': {
22031            pure: true,
22032            count: countSimply,
22033            getItem: getItemSimply,
22034            appendData: appendDataSimply
22035        },
22036
22037        'keyedColumns': {
22038            pure: true,
22039            count: function () {
22040                var dimName = this._source.dimensionsDefine[0].name;
22041                var col = this._data[dimName];
22042                return col ? col.length : 0;
22043            },
22044            getItem: function (idx) {
22045                var item = [];
22046                var dims = this._source.dimensionsDefine;
22047                for (var i = 0; i < dims.length; i++) {
22048                    var col = this._data[dims[i].name];
22049                    item.push(col ? col[idx] : null);
22050                }
22051                return item;
22052            },
22053            appendData: function (newData) {
22054                var data = this._data;
22055                each$1(newData, function (newCol, key) {
22056                    var oldCol = data[key] || (data[key] = []);
22057                    for (var i = 0; i < (newCol || []).length; i++) {
22058                        oldCol.push(newCol[i]);
22059                    }
22060                });
22061            }
22062        },
22063
22064        'original': {
22065            count: countSimply,
22066            getItem: getItemSimply,
22067            appendData: appendDataSimply
22068        },
22069
22070        'typedArray': {
22071            persistent: false,
22072            pure: true,
22073            count: function () {
22074                return this._data ? (this._data.length / this._dimSize) : 0;
22075            },
22076            getItem: function (idx, out) {
22077                idx = idx - this._offset;
22078                out = out || [];
22079                var offset = this._dimSize * idx;
22080                for (var i = 0; i < this._dimSize; i++) {
22081                    out[i] = this._data[offset + i];
22082                }
22083                return out;
22084            },
22085            appendData: function (newData) {
22086                if (__DEV__) {
22087                    assert$1(
22088                        isTypedArray(newData),
22089                        'Added data must be TypedArray if data in initialization is TypedArray'
22090                    );
22091                }
22092
22093                this._data = newData;
22094            },
22095
22096            // Clean self if data is already used.
22097            clean: function () {
22098                // PENDING
22099                this._offset += this.count();
22100                this._data = null;
22101            }
22102        }
22103    };
22104
22105    function countSimply() {
22106        return this._data.length;
22107    }
22108    function getItemSimply(idx) {
22109        return this._data[idx];
22110    }
22111    function appendDataSimply(newData) {
22112        for (var i = 0; i < newData.length; i++) {
22113            this._data.push(newData[i]);
22114        }
22115    }
22116
22117
22118
22119    var rawValueGetters = {
22120
22121        arrayRows: getRawValueSimply,
22122
22123        objectRows: function (dataItem, dataIndex, dimIndex, dimName) {
22124            return dimIndex != null ? dataItem[dimName] : dataItem;
22125        },
22126
22127        keyedColumns: getRawValueSimply,
22128
22129        original: function (dataItem, dataIndex, dimIndex, dimName) {
22130            // FIXME
22131            // In some case (markpoint in geo (geo-map.html)), dataItem
22132            // is {coord: [...]}
22133            var value = getDataItemValue(dataItem);
22134            return (dimIndex == null || !(value instanceof Array))
22135                ? value
22136                : value[dimIndex];
22137        },
22138
22139        typedArray: getRawValueSimply
22140    };
22141
22142    function getRawValueSimply(dataItem, dataIndex, dimIndex, dimName) {
22143        return dimIndex != null ? dataItem[dimIndex] : dataItem;
22144    }
22145
22146
22147    var defaultDimValueGetters = {
22148
22149        arrayRows: getDimValueSimply,
22150
22151        objectRows: function (dataItem, dimName, dataIndex, dimIndex) {
22152            return converDataValue(dataItem[dimName], this._dimensionInfos[dimName]);
22153        },
22154
22155        keyedColumns: getDimValueSimply,
22156
22157        original: function (dataItem, dimName, dataIndex, dimIndex) {
22158            // Performance sensitive, do not use modelUtil.getDataItemValue.
22159            // If dataItem is an plain object with no value field, the var `value`
22160            // will be assigned with the object, but it will be tread correctly
22161            // in the `convertDataValue`.
22162            var value = dataItem && (dataItem.value == null ? dataItem : dataItem.value);
22163
22164            // If any dataItem is like { value: 10 }
22165            if (!this._rawData.pure && isDataItemOption(dataItem)) {
22166                this.hasItemOption = true;
22167            }
22168            return converDataValue(
22169                (value instanceof Array)
22170                    ? value[dimIndex]
22171                    // If value is a single number or something else not array.
22172                    : value,
22173                this._dimensionInfos[dimName]
22174            );
22175        },
22176
22177        typedArray: function (dataItem, dimName, dataIndex, dimIndex) {
22178            return dataItem[dimIndex];
22179        }
22180
22181    };
22182
22183    function getDimValueSimply(dataItem, dimName, dataIndex, dimIndex) {
22184        return converDataValue(dataItem[dimIndex], this._dimensionInfos[dimName]);
22185    }
22186
22187    /**
22188     * This helper method convert value in data.
22189     * @param {string|number|Date} value
22190     * @param {Object|string}
22190 [dimInfo] If string (like 'x'), dimType defaults 'number'.
22191     *        If "dimInfo.ordinalParseAndSave", ordinal value can be parsed.
22192     */
22193    function converDataValue(value, dimInfo) {
22194        // Performance sensitive.
22195        var dimType = dimInfo && dimInfo.type;
22196        if (dimType === 'ordinal') {
22197            // If given value is a category string
22198            var ordinalMeta = dimInfo && dimInfo.ordinalMeta;
22199            return ordinalMeta
22200                ? ordinalMeta.parseAndCollect(value)
22201                : value;
22202        }
22203
22204        if (dimType === 'time'
22205            // spead up when using timestamp
22206            && typeof value !== 'number'
22207            && value != null
22208            && value !== '-'
22209        ) {
22210            value = +parseDate(value);
22211        }
22212
22213        // dimType defaults 'number'.
22214        // If dimType is not ordinal and value is null or undefined or NaN or '-',
22215        // parse to NaN.
22216        return (value == null || value === '')
22217            ? NaN
22218            // If string (like '-'), using '+' parse to NaN
22219            // If object, also parse to NaN
22220            : +value;
22221    }
22222
22223// ??? FIXME can these logic be more neat: getRawValue, getRawDataItem,
22224// Consider persistent.
22225// Caution: why use raw value to display on label or tooltip?
22226// A reason is to avoid format. For example time value we do not know
22227// how to format is expected. More over, if stack is used, calculated
22228// value may be 0.91000000001, which have brings trouble to display.
22229// TODO: consider how to treat null/undefined/NaN when display?
22230    /**
22231     * @param {module:echarts/data/List} data
22232     * @param {number} dataIndex
22233     * @param {string|number} [dim] dimName or dimIndex
22234     * @return {Array.<number>|string|number} can be null/undefined.
22235     */
22236    function retrieveRawValue(data, dataIndex, dim) {
22237        if (!data) {
22238            return;
22239        }
22240
22241        // Consider data may be not persistent.
22242        var dataItem = data.getRawDataItem(dataIndex);
22243
22244        if (dataItem == null) {
22245            return;
22246        }
22247
22248        var sourceFormat = data.getProvider().getSource().sourceFormat;
22249        var dimName;
22250        var dimIndex;
22251
22252        var dimInfo = data.getDimensionInfo(dim);
22253        if (dimInfo) {
22254            dimName = dimInfo.name;
22255            dimIndex = dimInfo.index;
22256        }
22257
22258        return rawValueGetters[sourceFormat](dataItem, dataIndex, dimIndex, dimName);
22259    }
22260
22261    /**
22262     * Compatible with some cases (in pie, map) like:
22263     * data: [{name: 'xx', value: 5, selected: true}, ...]
22264     * where only sourceFormat is 'original' and 'objectRows' supported.
22265     *
22266     * ??? TODO
22267     * Supported detail options in data item when using 'arrayRows'.
22268     *
22269     * @param {module:echarts/data/List} data
22270     * @param {number} dataIndex
22271     * @param {string} attr like 'selected'
22272     */
22273    function retrieveRawAttr(data, dataIndex, attr) {
22274        if (!data) {
22275            return;
22276        }
22277
22278        var sourceFormat = data.getProvider().getSource().sourceFormat;
22279
22280        if (sourceFormat !== SOURCE_FORMAT_ORIGINAL
22281            && sourceFormat !== SOURCE_FORMAT_OBJECT_ROWS
22282        ) {
22283            return;
22284        }
22285
22286        var dataItem = data.getRawDataItem(dataIndex);
22287        if (sourceFormat === SOURCE_FORMAT_ORIGINAL && !isObject$1(dataItem)) {
22288            dataItem = null;
22289        }
22290        if (dataItem) {
22291            return dataItem[attr];
22292        }
22293    }
22294
22295    /*
22296* Licensed to the Apache Software Foundation (ASF) under one
22297* or more contributor license agreements.  See the NOTICE file
22298* distributed with this work for additional information
22299* regarding copyright ownership.  The ASF licenses this file
22300* to you under the Apache License, Version 2.0 (the
22301* "License"); you may not use this file except in compliance
22302* with the License.  You may obtain a copy of the License at
22303*
22304*   http://www.apache.org/licenses/LICENSE-2.0
22305*
22306* Unless required by applicable law or agreed to in writing,
22307* software distributed under the License is distributed on an
22308* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
22309* KIND, either express or implied.  See the License for the
22310* specific language governing permissions and limitations
22311* under the License.
22312*/
22313
22314    var DIMENSION_LABEL_REG = /\{@(.+?)\}/g;
22315
22316// PENDING A little ugly
22317    var dataFormatMixin = {
22318        /**
22319         * Get params for formatter
22320         * @param {number} dataIndex
22321         * @param {string} [dataType]
22322         * @return {Object}
22323         */
22324        getDataParams: function (dataIndex, dataType) {
22325            var data = this.getData(dataType);
22326            var rawValue = this.getRawValue(dataIndex, dataType);
22327            var rawDataIndex = data.getRawIndex(dataIndex);
22328            var name = data.getName(dataIndex);
22329            var itemOpt = data.getRawDataItem(dataIndex);
22330            var color = data.getItemVisual(dataIndex, 'color');
22331
22332            return {
22333                componentType: this.mainType,
22334                componentSubType: this.subType,
22335                seriesType: this.mainType === 'series' ? this.subType : null,
22336                seriesIndex: this.seriesIndex,
22337                seriesId: this.id,
22338                seriesName: this.name,
22339                name: name,
22340                dataIndex: rawDataIndex,
22341                data: itemOpt,
22342                dataType: dataType,
22343                value: rawValue,
22344                color: color,
22345                marker: getTooltipMarker(color),
22346
22347                // Param name list for mapping `a`, `b`, `c`, `d`, `e`
22348                $vars: ['seriesName', 'name', 'value']
22349            };
22350        },
22351
22352        /**
22353         * Format label
22354         * @param {number} dataIndex
22355         * @param {string}
22355 [status='normal'] 'normal' or 'emphasis'
22356         * @param {string} [dataType]
22357         * @param {number} [dimIndex]
22358         * @param {string} [labelProp='label']
22359         * @return {string} If not formatter, return null/undefined
22360         */
22361        getFormattedLabel: function (dataIndex, status, dataType, dimIndex, labelProp) {
22362            status = status || 'normal';
22363            var data = this.getData(dataType);
22364            var itemModel = data.getItemModel(dataIndex);
22365
22366            var params = this.getDataParams(dataIndex, dataType);
22367            if (dimIndex != null && (params.value instanceof Array)) {
22368                params.value = params.value[dimIndex];
22369            }
22370
22371            var formatter = itemModel.get(
22372                status === 'normal'
22373                    ? [labelProp || 'label', 'formatter']
22374                    : [status, labelProp || 'label', 'formatter']
22375            );
22376
22377            if (typeof formatter === 'function') {
22378                params.status = status;
22379                return formatter(params);
22380            }
22381            else if (typeof formatter === 'string') {
22382                var str = formatTpl(formatter, params);
22383
22384                // Support 'aaa{@[3]}bbb{@product}ccc'.
22385                // Do not support '}' in dim name util have to.
22386                return str.replace(DIMENSION_LABEL_REG, function (origin, dim) {
22387                    var len = dim.length;
22388                    if (dim.charAt(0) === '[' && dim.charAt(len - 1) === ']') {
22389                        dim = +dim.slice(1, len - 1); // Also: '[]' => 0
22390                    }
22391                    return retrieveRawValue(data, dataIndex, dim);
22392                });
22393            }
22394        },
22395
22396        /**
22397         * Get raw value in option
22398         * @param {number} idx
22399         * @param {string} [dataType]
22400         * @return {Array|number|string}
22401         */
22402        getRawValue: function (idx, dataType) {
22403            return retrieveRawValue(this.getData(dataType), idx);
22404        },
22405
22406        /**
22407         * Should be implemented.
22408         * @param {number} dataIndex
22409         * @param {boolean} [multipleSeries=false]
22410         * @param {number} [dataType]
22411         * @return {string} tooltip string
22412         */
22413        formatTooltip: function () {
22414            // Empty function
22415        }
22416    };
22417
22418    /*
22419* Licensed to the Apache Software Foundation (ASF) under one
22420* or more contributor license agreements.  See the NOTICE file
22421* distributed with this work for additional information
22422* regarding copyright ownership.  The ASF licenses this file
22423* to you under the Apache License, Version 2.0 (the
22424* "License"); you may not use this file except in compliance
22425* with the License.  You may obtain a copy of the License at
22426*
22427*   http://www.apache.org/licenses/LICENSE-2.0
22428*
22429* Unless required by applicable law or agreed to in writing,
22430* software distributed under the License is distributed on an
22431* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
22432* KIND, either express or implied.  See the License for the
22433* specific language governing permissions and limitations
22434* under the License.
22435*/
22436
22437    /**
22438     * @param {Object} define
22439     * @return See the return of `createTask`.
22440     */
22441    function createTask(define) {
22442        return new Task(define);
22443    }
22444
22445    /**
22446     * @constructor
22447     * @param {Object} define
22448     * @param {Function} define.reset Custom reset
22449     * @param {Function} [define.plan] Returns 'reset' indicate reset immediately.
22450     * @param {Function} [define.count] count is used to determin data task.
22451     * @param {Function} [define.onDirty] count is used to determin data task.
22452     */
22453    function Task(define) {
22454        define = define || {};
22455
22456        this._reset = define.reset;
22457        this._plan = define.plan;
22458        this._count = define.count;
22459        this._onDirty = define.onDirty;
22460
22461        this._dirty = true;
22462
22463        // Context must be specified implicitly, to
22464        // avoid miss update context when model changed.
22465        this.context;
22466    }
22467
22468    var taskProto = Task.prototype;
22469
22470    /**
22471     * @param {Object} performArgs
22472     * @param {number} [performArgs.step] Specified step.
22473     * @param {number} [performArgs.skip] Skip customer perform call.
22474     * @param {number} [performArgs.modBy] Sampling window size.
22475     * @param {number} [performArgs.modDataCount] Sampling count.
22476     */
22477    taskProto.perform = function (performArgs) {
22478        var upTask = this._upstream;
22479        var skip = performArgs && performArgs.skip;
22480
22481        // TODO some refactor.
22482        // Pull data. Must pull data each time, because context.data
22483        // may be updated by Series.setData.
22484        if (this._dirty && upTask) {
22485            var context = this.context;
22486            context.data = context.outputData = upTask.context.outputData;
22487        }
22488
22489        if (this.__pipeline) {
22490            this.__pipeline.currentTask = this;
22491        }
22492
22493        var planResult;
22494        if (this._plan && !skip) {
22495            planResult = this._plan(this.context);
22496        }
22497
22498        // Support sharding by mod, which changes the render sequence and makes the rendered graphic
22499        // elements uniformed distributed when progress, especially when moving or zooming.
22500        var lastModBy = normalizeModBy(this._modBy);
22501        var lastModDataCount = this._modDataCount || 0;
22502        var modBy = normalizeModBy(performArgs && performArgs.modBy);
22503        var modDataCount = performArgs && performArgs.modDataCount || 0;
22504        if (lastModBy !== modBy || lastModDataCount !== modDataCount) {
22505            planResult = 'reset';
22506        }
22507
22508        function normalizeModBy(val) {
22509            !(val >= 1) && (val = 1); // jshint ignore:line
22510            return val;
22511        }
22512
22513        var forceFirstProgress;
22514        if (this._dirty || planResult === 'reset') {
22515            this._dirty = false;
22516            forceFirstProgress = reset(this, skip);
22517        }
22518
22519        this._modBy = modBy;
22520        this._modDataCount = modDataCount;
22521
22522        var step = performArgs && performArgs.step;
22523
22524        if (upTask) {
22525
22526            if (__DEV__) {
22527                assert$1(upTask._outputDueEnd != null);
22528            }
22529            this._dueEnd = upTask._outputDueEnd;
22530        }
22531        // DataTask or overallTask
22532        else {
22533            if (__DEV__) {
22534                assert$1(!this._progress || this._count);
22535            }
22536            this._dueEnd = this._count ? this._count(this.context) : Infinity;
22537        }
22538
22539        // Note: Stubs, that its host overall task let it has progress, has progress.
22540        // If no progress, pass index from upstream to downstream each time plan called.
22541        if (this._progress) {
22542            var start = this._dueIndex;
22543            var end = Math.min(
22544                step != null ? this._dueIndex + step : Infinity,
22545                this._dueEnd
22546            );
22547
22548            if (!skip && (forceFirstProgress || start < end)) {
22549                var progress = this._progress;
22550                if (isArray(progress)) {
22551                    for (var i = 0; i < progress.length; i++) {
22552                        doProgress(this, progress[i], start, end, modBy, modDataCount);
22553                    }
22554                }
22555                else {
22556                    doProgress(this, progress, start, end, modBy, modDataCount);
22557                }
22558            }
22559
22560            this._dueIndex = end;
22561            // If no `outputDueEnd`, assume that output data and
22562            // input data is the same, so use `dueIndex` as `outputDueEnd`.
22563            var outputDueEnd = this._settedOutputEnd != null
22564                ? this._settedOutputEnd : end;
22565
22566            if (__DEV__) {
22567                // ??? Can not rollback.
22568                assert$1(outputDueEnd >= this._outputDueEnd);
22569            }
22570
22571            this._outputDueEnd = outputDueEnd;
22572        }
22573        else {
22574            // (1) Some overall task has no progress.
22575            // (2) Stubs, that its host overall task do not let it has progress, has no progress.
22576            // This should always be performed so it can be passed to downstream.
22577            this._dueIndex = this._outputDueEnd = this._settedOutputEnd != null
22578                ? this._settedOutputEnd : this._dueEnd;
22579        }
22580
22581        return this.unfinished();
22582    };
22583
22584    var iterator = (function () {
22585
22586        var end;
22587        var current;
22588        var modBy;
22589        var modDataCount;
22590        var winCount;
22591
22592        var it = {
22593            reset: function (s, e, sStep, sCount) {
22594                current = s;
22595                end = e;
22596
22597                modBy = sStep;
22598                modDataCount = sCount;
22599                winCount = Math.ceil(modDataCount / modBy);
22600
22601                it.next = (modBy > 1 && modDataCount > 0) ? modNext : sequentialNext;
22602            }
22603        };
22604
22605        return it;
22606
22607        function sequentialNext() {
22608            return current < end ? current++ : null;
22609        }
22610
22611        function modNext() {
22612            var dataIndex = (current % winCount) * modBy + Math.ceil(current / winCount);
22613            var result = current >= end
22614                ? null
22615                : dataIndex < modDataCount
22616                    ? dataIndex
22617                    // If modDataCount is smaller than data.count() (consider `appendData` case),
22618                    // Use normal linear rendering mode.
22619                    : current;
22620            current++;
22621            return result;
22622        }
22623    })();
22624
22625    taskProto.dirty = function () {
22626        this._dirty = true;
22627        this._onDirty && this._onDirty(this.context);
22628    };
22629
22630    function doProgress(taskIns, progress, start, end, modBy, modDataCount) {
22631        iterator.reset(start, end, modBy, modDataCount);
22632        taskIns._callingProgress = progress;
22633        taskIns._callingProgress({
22634            start: start, end: end, count: end - start, next: iterator.next
22635        }, taskIns.context);
22636    }
22637
22638    function reset(taskIns, skip) {
22639        taskIns._dueIndex = taskIns._outputDueEnd = taskIns._dueEnd = 0;
22640        taskIns._settedOutputEnd = null;
22641
22642        var progress;
22643        var forceFirstProgress;
22644
22645        if (!skip && taskIns._reset) {
22646            progress = taskIns._reset(taskIns.context);
22647            if (progress && progress.progress) {
22648                forceFirstProgress = progress.forceFirstProgress;
22649                progress = progress.progress;
22650            }
22651            // To simplify no progress checking, array must has item.
22652            if (isArray(progress) && !progress.length) {
22653                progress = null;
22654            }
22655        }
22656
22657        taskIns._progress = progress;
22658        taskIns._modBy = taskIns._modDataCount = null;
22659
22660        var downstream = taskIns._downstream;
22661        downstream && downstream.dirty();
22662
22663        return forceFirstProgress;
22664    }
22665
22666    /**
22667     * @return {boolean}
22668     */
22669    taskProto.unfinished = function () {
22670        return this._progress && this._dueIndex < this._dueEnd;
22671    };
22672
22673    /**
22674     * @param {Object} downTask The downstream task.
22675     * @return {Object} The downstream task.
22676     */
22677    taskProto.pipe = function (downTask) {
22678        if (__DEV__) {
22679            assert$1(downTask && !downTask._disposed && downTask !== this);
22680        }
22681
22682        // If already downstream, do not dirty downTask.
22683        if (this._downstream !== downTask || this._dirty) {
22684            this._downstream = downTask;
22685            downTask._upstream = this;
22686            downTask.dirty();
22687        }
22688    };
22689
22690    taskProto.dispose = function () {
22691        if (this._disposed) {
22692            return;
22693        }
22694
22695        this._upstream && (this._upstream._downstream = null);
22696        this._downstream && (this._downstream._upstream = null);
22697
22698        this._dirty = false;
22699        this._disposed = true;
22700    };
22701
22702    taskProto.getUpstream = function () {
22703        return this._upstream;
22704    };
22705
22706    taskProto.getDownstream = function () {
22707        return this._downstream;
22708    };
22709
22710    taskProto.setOutputEnd = function (end) {
22711        // This only happend in dataTask, dataZoom, map, currently.
22712        // where dataZoom do not set end each time, but only set
22713        // when reset. So we should record the setted end, in case
22714        // that the stub of dataZoom perform again and earse the
22715        // setted end by upstream.
22716        this._outputDueEnd = this._settedOutputEnd = end;
22717    };
22718
22719
22720///////////////////////////////////////////////////////////
22721// For stream debug (Should be commented out after used!)
22722// Usage: printTask(this, 'begin');
22723// Usage: printTask(this, null, {someExtraProp});
22724// function printTask(task, prefix, extra) {
22725//     window.ecTaskUID == null && (window.ecTaskUID = 0);
22726//     task.uidDebug == null && (task.uidDebug = `task_${window.ecTaskUID++}`);
22727//     task.agent && task.agent.uidDebug == null && (task.agent.uidDebug = `task_${window.ecTaskUID++}`);
22728//     var props = [];
22729//     if (task.__pipeline) {
22730//         var val = `${task.__idxInPipeline}/${task.__pipeline.tail.__idxInPipeline} ${task.agent ? '(stub)' : ''}`;
22731//         props.push({text: 'idx', value: val});
22732//     } else {
22733//         var stubCount = 0;
22734//         task.agentStubMap.each(() => stubCount++);
22735//         props.push({text: 'idx', value: `overall (stubs: ${stubCount})`});
22736//     }
22737//     props.push({text: 'uid', value: task.uidDebug});
22738//     if (task.__pipeline) {
22739//         props.push({text: 'pid', value: task.__pipeline.id});
22740//         task.agent && props.push(
22741//             {text: 'stubFor', value: task.agent.uidDebug}
22742//         );
22743//     }
22744//     props.push(
22745//         {text: 'dirty', value: task._dirty},
22746//         {text: 'dueIndex', value: task._dueIndex},
22747//         {text: 'dueEnd', value: task._dueEnd},
22748//         {text: 'outputDueEnd', value: task._outputDueEnd}
22749//     );
22750//     if (extra) {
22751//         Object.keys(extra).forEach(key => {
22752//             props.push({text: key, value: extra[key]});
22753//         });
22754//     }
22755//     var args = ['color: blue'];
22756//     var msg = `%c[${prefix || 'T'}] %c` + props.map(item => (
22757//         args.push('color: black', 'color: red'),
22758//         `${item.text}: %c${item.value}`
22759//     )).join('%c, ');
22760//     console.log.apply(console, [msg].concat(args));
22761//     // console.log(this);
22762// }
22763
22764    /*
22765* Licensed to the Apache Software Foundation (ASF) under one
22766* or more contributor license agreements.  See the NOTICE file
22767* distributed with this work for additional information
22768* regarding copyright ownership.  The ASF licenses this file
22769* to you under the Apache License, Version 2.0 (the
22770* "License"); you may not use this file except in compliance
22771* with the License.  You may obtain a copy of the License at
22772*
22773*   http://www.apache.org/licenses/LICENSE-2.0
22774*
22775* Unless required by applicable law or agreed to in writing,
22776* software distributed under the License is distributed on an
22777* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
22778* KIND, either express or implied.  See the License for the
22779* specific language governing permissions and limitations
22780* under the License.
22781*/
22782
22783    var inner$4 = makeInner();
22784
22785    var SeriesModel = ComponentModel.extend({
22786
22787        type: 'series.__base__',
22788
22789        /**
22790         * @readOnly
22791         */
22792        seriesIndex: 0,
22793
22794        // coodinateSystem will be injected in the echarts/CoordinateSystem
22795        coordinateSystem: null,
22796
22797        /**
22798         * @type {Object}
22799         * @protected
22800         */
22801        defaultOption: null,
22802
22803        /**
22804         * Data provided for legend
22805         * @type {Function}
22806         */
22807        // PENDING
22808        legendDataProvider: null,
22809
22810        /**
22811         * Access path of color for visual
22812         */
22813        visualColorAccessPath: 'itemStyle.color',
22814
22815        /**
22816         * Support merge layout params.
22817         * Only support 'box' now (left/right/top/bottom/width/height).
22818         * @type {string|Object} Object can be {ignoreSize: true}
22819         * @readOnly
22820         */
22821        layoutMode: null,
22822
22823        init: function (option, parentModel, ecModel, extraOpt) {
22824
22825            /**
22826             * @type {number}
22827             * @readOnly
22828             */
22829            this.seriesIndex = this.componentIndex;
22830
22831            this.dataTask = createTask({
22832                count: dataTaskCount,
22833                reset: dataTaskReset
22834            });
22835            this.dataTask.context = {model: this};
22836
22837            this.mergeDefaultAndTheme(option, ecModel);
22838
22839            prepareSource(this);
22840
22841
22842            var data = this.getInitialData(option, ecModel);
22843            wrapData(data, this);
22844            this.dataTask.context.data = data;
22845
22846            if (__DEV__) {
22847                assert$1(data, 'getInitialData returned invalid data.');
22848            }
22849
22850            /**
22851             * @type {module:echarts/data/List|module:echarts/data/Tree|module:echarts/data/Graph}
22852             * @private
22853             */
22854            inner$4(this).dataBeforeProcessed = data;
22855
22856            // If we reverse the order (make data firstly, and then make
22857            // dataBeforeProcessed by cloneShallow), cloneShallow will
22858            // cause data.graph.data !== data when using
22859            // module:echarts/data/Graph or module:echarts/data/Tree.
22860            // See module:echarts/data/helper/linkList
22861
22862            // Theoretically, it is unreasonable to call `seriesModel.getData()` in the model
22863            // init or merge stage, because the data can be restored. So we do not `restoreData`
22864            // and `setData` here, which forbids calling `seriesModel.getData()` in this stage.
22865            // Call `seriesModel.getRawData()` instead.
22866            // this.restoreData();
22867
22868            autoSeriesName(this);
22869        },
22870
22871        /**
22872         * Util for merge default and theme to option
22873         * @param  {Object} option
22874         * @param  {module:echarts/model/Global} ecModel
22875         */
22876        mergeDefaultAndTheme: function (option, ecModel) {
22877            var layoutMode = this.layoutMode;
22878            var inputPositionParams = layoutMode
22879                ? getLayoutParams(option) : {};
22880
22881            // Backward compat: using subType on theme.
22882            // But if name duplicate between series subType
22883            // (for example: parallel) add component mainType,
22884            // add suffix 'Series'.
22885            var themeSubType = this.subType;
22886            if (ComponentModel.hasClass(themeSubType)) {
22887                themeSubType += 'Series';
22888            }
22889            merge(
22890                option,
22891                ecModel.getTheme().get(this.subType)
22892            );
22893            merge(option, this.getDefaultOption());
22894
22895            // Default label emphasis `show`
22896            defaultEmphasis(option, 'label', ['show']);
22897
22898            this.fillDataTextStyle(option.data);
22899
22900            if (layoutMode) {
22901                mergeLayoutParam(option, inputPositionParams, layoutMode);
22902            }
22903        },
22904
22905        mergeOption: function (newSeriesOption, ecModel) {
22906            // this.settingTask.dirty();
22907
22908            newSeriesOption = merge(this.option, newSeriesOption, true);
22909            this.fillDataTextStyle(newSeriesOption.data);
22910
22911            var layoutMode = this.layoutMode;
22912            if (layoutMode) {
22913                mergeLayoutParam(this.option, newSeriesOption, layoutMode);
22914            }
22915
22916            prepareSource(this);
22917
22918            var data = this.getInitialData(newSeriesOption, ecModel);
22919            wrapData(data, this);
22920            this.dataTask.dirty();
22921            this.dataTask.context.data = data;
22922
22923            inner$4(this).dataBeforeProcessed = data;
22924
22925            autoSeriesName(this);
22926        },
22927
22928        fillDataTextStyle: function (data) {
22929            // Default data label emphasis `show`
22930            // FIXME Tree structure data ?
22931            // FIXME Performance ?
22932            if (data && !isTypedArray(data)) {
22933                var props = ['show'];
22934                for (var i = 0; i < data.length; i++) {
22935                    if (data[i] && data[i].label) {
22936                        defaultEmphasis(data[i], 'label', props);
22937                    }
22938                }
22939            }
22940        },
22941
22942        /**
22943         * Init a data structure from data related option in series
22944         * Must be overwritten
22945         */
22946        getInitialData: function () {},
22947
22948        /**
22949         * Append data to list
22950         * @param {Object} params
22951         * @param {Array|TypedArray} params.data
22952         */
22953        appendData: function (params) {
22954            // FIXME ???
22955            // (1) If data from dataset, forbidden append.
22956            // (2) support append data of dataset.
22957            var data = this.getRawData();
22958            data.appendData(params.data);
22959        },
22960
22961        /**
22962         * Consider some method like `filter`, `map` need make new data,
22963         * We should make sure that `seriesModel.getData()` get correct
22964         * data in the stream procedure. So we fetch data from upstream
22965         * each time `task.perform` called.
22966         * @param {string} [dataType]
22967         * @return {module:echarts/data/List}
22968         */
22969        getData: function (dataType) {
22970            var task = getCurrentTask(this);
22971            if (task) {
22972                var data = task.context.data;
22973                return dataType == null ? data : data.getLinkedData(dataType);
22974            }
22975            else {
22976                // When series is not alive (that may happen when click toolbox
22977                // restore or setOption with not merge mode), series data may
22978                // be still need to judge animation or something when graphic
22979                // elements want to know whether fade out.
22980                return inner$4(this).data;
22981            }
22982        },
22983
22984        /**
22985         * @param {module:echarts/data/List} data
22986         */
22987        setData: function (data) {
22988            var task = getCurrentTask(this);
22989            if (task) {
22990                var context = task.context;
22991                // Consider case: filter, data sample.
22992                if (context.data !== data && task.modifyOutputEnd) {
22993                    task.setOutputEnd(data.count());
22994                }
22995                context.outputData = data;
22996                // Caution: setData should update context.data,
22997                // Because getData may be called multiply in a
22998                // single stage and expect to get the data just
22999                // set. (For example, AxisProxy, x y both call
23000                // getData and setDate sequentially).
23001                // So the context.data should be fetched from
23002                // upstream each time when a stage starts to be
23003                // performed.
23004                if (task !== this.dataTask) {
23005                    context.data = data;
23006                }
23007            }
23008            inner$4(this).data = data;
23009        },
23010
23011        /**
23012         * @see {module:echarts/data/helper/sourceHelper#getSource}
23013         * @return {module:echarts/data/Source} source
23014         */
23015        getSource: function () {
23016            return getSource(this);
23017        },
23018
23019        /**
23020         * Get data before processed
23021         * @return {module:echarts/data/List}
23022         */
23023        getRawData: function () {
23024            return inner$4(this).dataBeforeProcessed;
23025        },
23026
23027        /**
23028         * Get base axis if has coordinate system and has axis.
23029         * By default use coordSys.getBaseAxis();
23030         * Can be overrided for some chart.
23031         * @return {type} description
23032         */
23033        getBaseAxis: function () {
23034            var coordSys = this.coordinateSystem;
23035            return coordSys && coordSys.getBaseAxis && coordSys.getBaseAxis();
23036        },
23037
23038        // FIXME
23039        /**
23040         * Default tooltip formatter
23041         *
23042         * @param {number} dataIndex
23043         * @param {boolean} [multipleSeries=false]
23044         * @param {number} [dataType]
23045         */
23046        formatTooltip: function (dataIndex, multipleSeries, dataType) {
23047
23048            function formatArrayValue(value) {
23049                // ??? TODO refactor these logic.
23050                // check: category-no-encode-has-axis-data in dataset.html
23051                var vertially = reduce(value, function (vertially, val, idx) {
23052                    var dimItem = data.getDimensionInfo(idx);
23053                    return vertially |= dimItem && dimItem.tooltip !== false && dimItem.displayName != null;
23054                }, 0);
23055
23056                var result = [];
23057
23058                tooltipDims.length
23059                    ? each$1(tooltipDims, function (dim) {
23060                        setEachItem(retrieveRawValue(data, dataIndex, dim), dim);
23061                    })
23062                    // By default, all dims is used on tooltip.
23063                    : each$1(value, setEachItem);
23064
23065                function setEachItem(val, dim) {
23066                    var dimInfo = data.getDimensionInfo(dim);
23067                    // If `dimInfo.tooltip` is not set, show tooltip.
23068                    if (!dimInfo || dimInfo.otherDims.tooltip === false) {
23069                        return;
23070                    }
23071                    var dimType = dimInfo.type;
23072                    var dimHead = getTooltipMarker({color: color, type: 'subItem'});
23073                    var valStr = (vertially
23074                            ? dimHead + encodeHTML(dimInfo.displayName || '-') + ': '
23075                            : ''
23076                        )
23077                        // FIXME should not format time for raw data?
23078                        + encodeHTML(dimType === 'ordinal'
23079                            ? val + ''
23080                            : dimType === 'time'
23081                                ? (multipleSeries ? '' : formatTime('yyyy/MM/dd hh:mm:ss', val))
23082                                : addCommas(val)
23083                        );
23084                    valStr && result.push(valStr);
23085                }
23086
23087                return (vertially ? '<br/>' : '') + result.join(vertially ? '<br/>' : ', ');
23088            }
23089
23090            function formatSingleValue(val) {
23091                return encodeHTML(addCommas(val));
23092            }
23093
23094            var data = this.getData();
23095            var tooltipDims = data.mapDimension('defaultedTooltip', true);
23096            var tooltipDimLen = tooltipDims.length;
23097            var value = this.getRawValue(dataIndex);
23098            var isValueArr = isArray(value);
23099
23100            var color = data.getItemVisual(dataIndex, 'color');
23101            if (isObject$1(color) && color.colorStops) {
23102                color = (color.colorStops[0] || {}).color;
23103            }
23104            color = color || 'transparent';
23105
23106            // Complicated rule for pretty tooltip.
23107            var formattedValue = (tooltipDimLen > 1 || (isValueArr && !tooltipDimLen))
23108                ? formatArrayValue(value)
23109                : tooltipDimLen
23110                    ? formatSingleValue(retrieveRawValue(data, dataIndex, tooltipDims[0]))
23111                    : formatSingleValue(isValueArr ? value[0] : value);
23112
23113            var colorEl = getTooltipMarker(color);
23114
23115            var name = data.getName(dataIndex);
23116
23117            var seriesName = this.name;
23118            if (!isNameSpecified(this)) {
23119                seriesName = '';
23120            }
23121            seriesName = seriesName
23122                ? encodeHTML(seriesName) + (!multipleSeries ? '<br/>' : ': ')
23123                : '';
23124
23125            return !multipleSeries
23126                ? seriesName + colorEl
23127                + (name
23128                        ? encodeHTML(name) + ': ' + formattedValue
23129                        : formattedValue
23130                )
23131                : colorEl + seriesName + formattedValue;
23132        },
23133
23134        /**
23135         * @return {boolean}
23136         */
23137        isAnimationEnabled: function () {
23138            if (env$1.node) {
23139                return false;
23140            }
23141
23142            var animationEnabled = this.getShallow('animation');
23143            if (animationEnabled) {
23144                if (this.getData().count() > this.getShallow('animationThreshold')) {
23145                    animationEnabled = false;
23146                }
23147            }
23148            return animationEnabled;
23149        },
23150
23151        restoreData: function () {
23152            this.dataTask.dirty();
23153        },
23154
23155        getColorFromPalette: function (name, scope, requestColorNum) {
23156            var ecModel = this.ecModel;
23157            // PENDING
23158            var color = colorPaletteMixin.getColorFromPalette.call(this, name, scope, requestColorNum);
23159            if (!color) {
23160                color = ecModel.getColorFromPalette(name, scope, requestColorNum);
23161            }
23162            return color;
23163        },
23164
23165        /**
23166         * Use `data.mapDimension(coordDim, true)` instead.
23167         * @deprecated
23168         */
23169        coordDimToDataDim: function (coordDim) {
23170            return this.getRawData().mapDimension(coordDim, true);
23171        },
23172
23173        /**
23174         * Get progressive rendering count each step
23175         * @return {number}
23176         */
23177        getProgressive: function () {
23178            return this.get('progressive');
23179        },
23180
23181        /**
23182         * Get progressive rendering count each step
23183         * @return {number}
23184         */
23185        getProgressiveThreshold: function () {
23186            return this.get('progressiveThreshold');
23187        },
23188
23189        /**
23190         * Get data indices for show tooltip content. See tooltip.
23191         * @abstract
23192         * @param {Array.<string>|string} dim
23193         * @param {Array.<number>} value
23194         * @param {module:echarts/coord/single/SingleAxis} baseAxis
23195         * @return {Object} {dataIndices, nestestValue}.
23196         */
23197        getAxisTooltipData: null,
23198
23199        /**
23200         * See tooltip.
23201         * @abstract
23202         * @param {number} dataIndex
23203         * @return {Array.<number>} Point of tooltip. null/undefined can be returned.
23204         */
23205        getTooltipPosition: null,
23206
23207        /**
23208         * @see {module:echarts/stream/Scheduler}
23209         */
23210        pipeTask: null,
23211
23212        /**
23213         * Convinient for override in extended class.
23214         * @protected
23215         * @type {Function}
23216         */
23217        preventIncremental: null,
23218
23219        /**
23220         * @public
23221         * @readOnly
23222         * @type {Object}
23223         */
23224        pipelineContext: null
23225
23226    });
23227
23228
23229    mixin(SeriesModel, dataFormatMixin);
23230    mixin(SeriesModel, colorPaletteMixin);
23231
23232    /**
23233     * MUST be called after `prepareSource` called
23234     * Here we need to make auto series, especially for auto legend. But we
23235     * do not modify series.name in option to avoid side effects.
23236     */
23237    function autoSeriesName(seriesModel) {
23238        // User specified name has higher priority, otherwise it may cause
23239        // series can not be queried unexpectedly.
23240        var name = seriesModel.name;
23241        if (!isNameSpecified(seriesModel)) {
23242            seriesModel.name = getSeriesAutoName(seriesModel) || name;
23243        }
23244    }
23245
23246    function getSeriesAutoName(seriesModel) {
23247        var data = seriesModel.getRawData();
23248        var dataDims = data.mapDimension('seriesName', true);
23249        var nameArr = [];
23250        each$1(dataDims, function (dataDim) {
23251            var dimInfo = data.getDimensionInfo(dataDim);
23252            dimInfo.displayName && nameArr.push(dimInfo.displayName);
23253        });
23254        return nameArr.join(' ');
23255    }
23256
23257    function dataTaskCount(context) {
23258        return context.model.getRawData().count();
23259    }
23260
23261    function dataTaskReset(context) {
23262        var seriesModel = context.model;
23263        seriesModel.setData(seriesModel.getRawData().cloneShallow());
23264        return dataTaskProgress;
23265    }
23266
23267    function dataTaskProgress(param, context) {
23268        // Avoid repead cloneShallow when data just created in reset.
23269        if (param.end > context.outputData.count()) {
23270            context.model.getRawData().cloneShallow(context.outputData);
23271        }
23272    }
23273
23274// TODO refactor
23275    function wrapData(data, seriesModel) {
23276        each$1(data.CHANGABLE_METHODS, function (methodName) {
23277            data.wrapMethod(methodName, curry(onDataSelfChange, seriesModel));
23278        });
23279    }
23280
23281    function onDataSelfChange(seriesModel) {
23282        var task = getCurrentTask(seriesModel);
23283        if (task) {
23284            // Consider case: filter, selectRange
23285            task.setOutputEnd(this.count());
23286        }
23287    }
23288
23289    function getCurrentTask(seriesModel) {
23290        var scheduler = (seriesModel.ecModel || {}).scheduler;
23291        var pipeline = scheduler && scheduler.getPipeline(seriesModel.uid);
23292
23293        if (pipeline) {
23294            // When pipline finished, the currrentTask keep the last
23295            // task (renderTask).
23296            var task = pipeline.currentTask;
23297            if (task) {
23298                var agentStubMap = task.agentStubMap;
23299                if (agentStubMap) {
23300                    task = agentStubMap.get(seriesModel.uid);
23301                }
23302            }
23303            return task;
23304        }
23305    }
23306
23307    /*
23308* Licensed to the Apache Software Foundation (ASF) under one
23309* or more contributor license agreements.  See the NOTICE file
23310* distributed with this work for additional information
23311* regarding copyright ownership.  The ASF licenses this file
23312* to you under the Apache License, Version 2.0 (the
23313* "License"); you may not use this file except in compliance
23314* with the License.  You may obtain a copy of the License at
23315*
23316*   http://www.apache.org/licenses/LICENSE-2.0
23317*
23318* Unless required by applicable law or agreed to in writing,
23319* software distributed under the License is distributed on an
23320* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
23321* KIND, either express or implied.  See the License for the
23322* specific language governing permissions and limitations
23323* under the License.
23324*/
23325
23326    var Component = function () {
23327        /**
23328         * @type {module:zrender/container/Group}
23329         * @readOnly
23330         */
23331        this.group = new Group();
23332
23333        /**
23334         * @type {string}
23335         * @readOnly
23336         */
23337        this.uid = getUID('viewComponent');
23338    };
23339
23340    Component.prototype = {
23341
23342        constructor: Component,
23343
23344        init: function (ecModel, api) {},
23345
23346        render: function (componentModel, ecModel, api, payload) {},
23347
23348        dispose: function () {}
23349
23350    };
23351
23352    var componentProto = Component.prototype;
23353    componentProto.updateView
23354        = componentProto.updateLayout
23355        = componentProto.updateVisual
23356        = function (seriesModel, ecModel, api, payload) {
23357        // Do nothing;
23358    };
23359// Enable Component.extend.
23360    enableClassExtend(Component);
23361
23362// Enable capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on.
23363    enableClassManagement(Component, {registerWhenExtend: true});
23364
23365    /*
23366* Licensed to the Apache Software Foundation (ASF) under one
23367* or more contributor license agreements.  See the NOTICE file
23368* distributed with this work for additional information
23369* regarding copyright ownership.  The ASF licenses this file
23370* to you under the Apache License, Version 2.0 (the
23371* "License"); you may not use this file except in compliance
23372* with the License.  You may obtain a copy of the License at
23373*
23374*   http://www.apache.org/licenses/LICENSE-2.0
23375*
23376* Unless required by applicable law or agreed to in writing,
23377* software distributed under the License is distributed on an
23378* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
23379* KIND, either express or implied.  See the License for the
23380* specific language governing permissions and limitations
23381* under the License.
23382*/
23383
23384    /**
23385     * @return {string} If large mode changed, return string 'reset';
23386     */
23387    var createRenderPlanner = function () {
23388        var inner = makeInner();
23389
23390        return function (seriesModel) {
23391            var fields = inner(seriesModel);
23392            var pipelineContext = seriesModel.pipelineContext;
23393
23394            var originalLarge = fields.large;
23395            var originalProgressive = fields.progressiveRender;
23396
23397            var large = fields.large = pipelineContext.large;
23398            var progressive = fields.progressiveRender = pipelineContext.progressiveRender;
23399
23400            return !!((originalLarge ^ large) || (originalProgressive ^ progressive)) && 'reset';
23401        };
23402    };
23403
23404    /*
23405* Licensed to the Apache Software Foundation (ASF) under one
23406* or more contributor license agreements.  See the NOTICE file
23407* distributed with this work for additional information
23408* regarding copyright ownership.  The ASF licenses this file
23409* to you under the Apache License, Version 2.0 (the
23410* "License");
23410 you may not use this file except in compliance
23411* with the License.  You may obtain a copy of the License at
23412*
23413*   http://www.apache.org/licenses/LICENSE-2.0
23414*
23415* Unless required by applicable law or agreed to in writing,
23416* software distributed under the License is distributed on an
23417* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
23418* KIND, either express or implied.  See the License for the
23419* specific language governing permissions and limitations
23420* under the License.
23421*/
23422
23423    var inner$5 = makeInner();
23424    var renderPlanner = createRenderPlanner();
23425
23426    function Chart() {
23427
23428        /**
23429         * @type {module:zrender/container/Group}
23430         * @readOnly
23431         */
23432        this.group = new Group();
23433
23434        /**
23435         * @type {string}
23436         * @readOnly
23437         */
23438        this.uid = getUID('viewChart');
23439
23440        this.renderTask = createTask({
23441            plan: renderTaskPlan,
23442            reset: renderTaskReset
23443        });
23444        this.renderTask.context = {view: this};
23445    }
23446
23447    Chart.prototype = {
23448
23449        type: 'chart',
23450
23451        /**
23452         * Init the chart.
23453         * @param  {module:echarts/model/Global} ecModel
23454         * @param  {module:echarts/ExtensionAPI} api
23455         */
23456        init: function (ecModel, api) {},
23457
23458        /**
23459         * Render the chart.
23460         * @param  {module:echarts/model/Series} seriesModel
23461         * @param  {module:echarts/model/Global} ecModel
23462         * @param  {module:echarts/ExtensionAPI} api
23463         * @param  {Object} payload
23464         */
23465        render: function (seriesModel, ecModel, api, payload) {},
23466
23467        /**
23468         * Highlight series or specified data item.
23469         * @param  {module:echarts/model/Series} seriesModel
23470         * @param  {module:echarts/model/Global} ecModel
23471         * @param  {module:echarts/ExtensionAPI} api
23472         * @param  {Object} payload
23473         */
23474        highlight: function (seriesModel, ecModel, api, payload) {
23475            toggleHighlight(seriesModel.getData(), payload, 'emphasis');
23476        },
23477
23478        /**
23479         * Downplay series or specified data item.
23480         * @param  {module:echarts/model/Series} seriesModel
23481         * @param  {module:echarts/model/Global} ecModel
23482         * @param  {module:echarts/ExtensionAPI} api
23483         * @param  {Object} payload
23484         */
23485        downplay: function (seriesModel, ecModel, api, payload) {
23486            toggleHighlight(seriesModel.getData(), payload, 'normal');
23487        },
23488
23489        /**
23490         * Remove self.
23491         * @param  {module:echarts/model/Global} ecModel
23492         * @param  {module:echarts/ExtensionAPI} api
23493         */
23494        remove: function (ecModel, api) {
23495            this.group.removeAll();
23496        },
23497
23498        /**
23499         * Dispose self.
23500         * @param  {module:echarts/model/Global} ecModel
23501         * @param  {module:echarts/ExtensionAPI} api
23502         */
23503        dispose: function () {},
23504
23505        /**
23506         * Rendering preparation in progressive mode.
23507         * @param  {module:echarts/model/Series} seriesModel
23508         * @param  {module:echarts/model/Global} ecModel
23509         * @param  {module:echarts/ExtensionAPI} api
23510         * @param  {Object} payload
23511         */
23512        incrementalPrepareRender: null,
23513
23514        /**
23515         * Render in progressive mode.
23516         * @param  {module:echarts/model/Series} seriesModel
23517         * @param  {module:echarts/model/Global} ecModel
23518         * @param  {module:echarts/ExtensionAPI} api
23519         * @param  {Object} payload
23520         */
23521        incrementalRender: null,
23522
23523        /**
23524         * Update transform directly.
23525         * @param  {module:echarts/model/Series} seriesModel
23526         * @param  {module:echarts/model/Global} ecModel
23527         * @param  {module:echarts/ExtensionAPI} api
23528         * @param  {Object} payload
23529         * @return {Object} {update: true}
23530         */
23531        updateTransform: null
23532
23533        /**
23534         * The view contains the given point.
23535         * @interface
23536         * @param {Array.<number>} point
23537         * @return {boolean}
23538         */
23539        // containPoint: function () {}
23540
23541    };
23542
23543    var chartProto = Chart.prototype;
23544    chartProto.updateView
23545        = chartProto.updateLayout
23546        = chartProto.updateVisual
23547        = function (seriesModel, ecModel, api, payload) {
23548        this.render(seriesModel, ecModel, api, payload);
23549    };
23550
23551    /**
23552     * Set state of single element
23553     * @param  {module:zrender/Element} el
23554     * @param  {string} state
23555     */
23556    function elSetState(el, state) {
23557        if (el) {
23558            el.trigger(state);
23559            if (el.type === 'group') {
23560                for (var i = 0; i < el.childCount(); i++) {
23561                    elSetState(el.childAt(i), state);
23562                }
23563            }
23564        }
23565    }
23566    /**
23567     * @param  {module:echarts/data/List} data
23568     * @param  {Object} payload
23569     * @param  {string} state 'normal'|'emphasis'
23570     */
23571    function toggleHighlight(data, payload, state) {
23572        var dataIndex = queryDataIndex(data, payload);
23573
23574        if (dataIndex != null) {
23575            each$1(normalizeToArray(dataIndex), function (dataIdx) {
23576                elSetState(data.getItemGraphicEl(dataIdx), state);
23577            });
23578        }
23579        else {
23580            data.eachItemGraphicEl(function (el) {
23581                elSetState(el, state);
23582            });
23583        }
23584    }
23585
23586// Enable Chart.extend.
23587    enableClassExtend(Chart, ['dispose']);
23588
23589// Add capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on.
23590    enableClassManagement(Chart, {registerWhenExtend: true});
23591
23592    Chart.markUpdateMethod = function (payload, methodName) {
23593        inner$5(payload).updateMethod = methodName;
23594    };
23595
23596    function renderTaskPlan(context) {
23597        return renderPlanner(context.model);
23598    }
23599
23600    function renderTaskReset(context) {
23601        var seriesModel = context.model;
23602        var ecModel = context.ecModel;
23603        var api = context.api;
23604        var payload = context.payload;
23605        // ???! remove updateView updateVisual
23606        var progressiveRender = seriesModel.pipelineContext.progressiveRender;
23607        var view = context.view;
23608
23609        var updateMethod = payload && inner$5(payload).updateMethod;
23610        var methodName = progressiveRender
23611            ? 'incrementalPrepareRender'
23612            : (updateMethod && view[updateMethod])
23613                ? updateMethod
23614                // `appendData` is also supported when data amount
23615                // is less than progressive threshold.
23616                : 'render';
23617
23618        if (methodName !== 'render') {
23619            view[methodName](seriesModel, ecModel, api, payload);
23620        }
23621
23622        return progressMethodMap[methodName];
23623    }
23624
23625    var progressMethodMap = {
23626        incrementalPrepareRender: {
23627            progress: function (params, context) {
23628                context.view.incrementalRender(
23629                    params, context.model, context.ecModel, context.api, context.payload
23630                );
23631            }
23632        },
23633        render: {
23634            // Put view.render in `progress` to support appendData. But in this case
23635            // view.render should not be called in reset, otherwise it will be called
23636            // twise. Use `forceFirstProgress` to make sure that view.render is called
23637            // in any cases.
23638            forceFirstProgress: true,
23639            progress: function (params, context) {
23640                context.view.render(
23641                    context.model, context.ecModel, context.api, context.payload
23642                );
23643            }
23644        }
23645    };
23646
23647    /*
23648* Licensed to the Apache Software Foundation (ASF) under one
23649* or more contributor license agreements.  See the NOTICE file
23650* distributed with this work for additional information
23651* regarding copyright ownership.  The ASF licenses this file
23652* to you under the Apache License, Version 2.0 (the
23653* "License"); you may not use this file except in compliance
23654* with the License.  You may obtain a copy of the License at
23655*
23656*   http://www.apache.org/licenses/LICENSE-2.0
23657*
23658* Unless required by applicable law or agreed to in writing,
23659* software distributed under the License is distributed on an
23660* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
23661* KIND, either express or implied.  See the License for the
23662* specific language governing permissions and limitations
23663* under the License.
23664*/
23665
23666
23667    var ORIGIN_METHOD = '\0__throttleOriginMethod';
23668    var RATE = '\0__throttleRate';
23669    var THROTTLE_TYPE = '\0__throttleType';
23670
23671    /**
23672     * @public
23673     * @param {(Function)} fn
23674     * @param {number} [delay=0] Unit: ms.
23675     * @param {boolean} [debounce=false]
23676     *        true: If call interval less than `delay`, only the last call works.
23677     *        false: If call interval less than `delay, call works on fixed rate.
23678     * @return {(Function)} throttled fn.
23679     */
23680    function throttle(fn, delay, debounce) {
23681
23682        var currCall;
23683        var lastCall = 0;
23684        var lastExec = 0;
23685        var timer = null;
23686        var diff;
23687        var scope;
23688        var args;
23689        var debounceNextCall;
23690
23691        delay = delay || 0;
23692
23693        function exec() {
23694            lastExec = (new Date()).getTime();
23695            timer = null;
23696            fn.apply(scope, args || []);
23697        }
23698
23699        var cb = function () {
23700            currCall = (new Date()).getTime();
23701            scope = this;
23702            args = arguments;
23703            var thisDelay = debounceNextCall || delay;
23704            var thisDebounce = debounceNextCall || debounce;
23705            debounceNextCall = null;
23706            diff = currCall - (thisDebounce ? lastCall : lastExec) - thisDelay;
23707
23708            clearTimeout(timer);
23709
23710            // Here we should make sure that: the `exec` SHOULD NOT be called later
23711            // than a new call of `cb`, that is, preserving the command order. Consider
23712            // calculating "scale rate" when roaming as an example. When a call of `cb`
23713            // happens, either the `exec` is called dierectly, or the call is delayed.
23714            // But the delayed call should never be later than next call of `cb`. Under
23715            // this assurance, we can simply update view state each time `dispatchAction`
23716            // triggered by user roaming, but not need to add extra code to avoid the
23717            // state being "rolled-back".
23718            if (thisDebounce) {
23719                timer = setTimeout(exec, thisDelay);
23720            }
23721            else {
23722                if (diff >= 0) {
23723                    exec();
23724                }
23725                else {
23726                    timer = setTimeout(exec, -diff);
23727                }
23728            }
23729
23730            lastCall = currCall;
23731        };
23732
23733        /**
23734         * Clear throttle.
23735         * @public
23736         */
23737        cb.clear = function () {
23738            if (timer) {
23739                clearTimeout(timer);
23740                timer = null;
23741            }
23742        };
23743
23744        /**
23745         * Enable debounce once.
23746         */
23747        cb.debounceNextCall = function (debounceDelay) {
23748            debounceNextCall = debounceDelay;
23749        };
23750
23751        return cb;
23752    }
23753
23754    /**
23755     * Create throttle method or update throttle rate.
23756     *
23757     * @example
23758     * ComponentView.prototype.render = function () {
23759 *     ...
23760 *     throttle.createOrUpdate(
23761 *         this,
23762 *         '_dispatchAction',
23763 *         this.model.get('throttle'),
23764 *         'fixRate'
23765 *     );
23766 * };
23767     * ComponentView.prototype.remove = function () {
23768 *     throttle.clear(this, '_dispatchAction');
23769 * };
23770     * ComponentView.prototype.dispose = function () {
23771 *     throttle.clear(this, '_dispatchAction');
23772 * };
23773     *
23774     * @public
23775     * @param {Object} obj
23776     * @param {string} fnAttr
23777     * @param {number} [rate]
23778     * @param {string} [throttleType='fixRate'] 'fixRate' or 'debounce'
23779     * @return {Function} throttled function.
23780     */
23781    function createOrUpdate(obj, fnAttr, rate, throttleType) {
23782        var fn = obj[fnAttr];
23783
23784        if (!fn) {
23785            return;
23786        }
23787
23788        var originFn = fn[ORIGIN_METHOD] || fn;
23789        var lastThrottleType = fn[THROTTLE_TYPE];
23790        var lastRate = fn[RATE];
23791
23792        if (lastRate !== rate || lastThrottleType !== throttleType) {
23793            if (rate == null || !throttleType) {
23794                return (obj[fnAttr] = originFn);
23795            }
23796
23797            fn = obj[fnAttr] = throttle(
23798                originFn, rate, throttleType === 'debounce'
23799            );
23800            fn[ORIGIN_METHOD] = originFn;
23801            fn[THROTTLE_TYPE] = throttleType;
23802            fn[RATE] = rate;
23803        }
23804
23805        return fn;
23806    }
23807
23808    /**
23809     * Clear throttle. Example see throttle.createOrUpdate.
23810     *
23811     * @public
23812     * @param {Object} obj
23813     * @param {string} fnAttr
23814     */
23815    function clear(obj, fnAttr) {
23816        var fn = obj[fnAttr];
23817        if (fn && fn[ORIGIN_METHOD]) {
23818            obj[fnAttr] = fn[ORIGIN_METHOD];
23819        }
23820    }
23821
23822    /*
23823* Licensed to the Apache Software Foundation (ASF) under one
23824* or more contributor license agreements.  See the NOTICE file
23825* distributed with this work for additional information
23826* regarding copyright ownership.  The ASF licenses this file
23827* to you under the Apache License, Version 2.0 (the
23828* "License");
23828 you may not use this file except in compliance
23829* with the License.  You may obtain a copy of the License at
23830*
23831*   http://www.apache.org/licenses/LICENSE-2.0
23832*
23833* Unless required by applicable law or agreed to in writing,
23834* software distributed under the License is distributed on an
23835* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
23836* KIND, either express or implied.  See the License for the
23837* specific language governing permissions and limitations
23838* under the License.
23839*/
23840
23841    var seriesColor = {
23842        createOnAllSeries: true,
23843        performRawSeries: true,
23844        reset: function (seriesModel, ecModel) {
23845            var data = seriesModel.getData();
23846            var colorAccessPath = (seriesModel.visualColorAccessPath || 'itemStyle.color').split('.');
23847            var color = seriesModel.get(colorAccessPath) // Set in itemStyle
23848                || seriesModel.getColorFromPalette(
23849                    // TODO series count changed.
23850                    seriesModel.name, null, ecModel.getSeriesCount()
23851                );  // Default color
23852
23853            // FIXME Set color function or use the platte color
23854            data.setVisual('color', color);
23855
23856            // Only visible series has each data be visual encoded
23857            if (!ecModel.isSeriesFiltered(seriesModel)) {
23858                if (typeof color === 'function' && !(color instanceof Gradient)) {
23859                    data.each(function (idx) {
23860                        data.setItemVisual(
23861                            idx, 'color', color(seriesModel.getDataParams(idx))
23862                        );
23863                    });
23864                }
23865
23866                // itemStyle in each data item
23867                var dataEach = function (data, idx) {
23868                    var itemModel = data.getItemModel(idx);
23869                    var color = itemModel.get(colorAccessPath, true);
23870                    if (color != null) {
23871                        data.setItemVisual(idx, 'color', color);
23872                    }
23873                };
23874
23875                return { dataEach: data.hasItemOption ? dataEach : null };
23876            }
23877        }
23878    };
23879
23880    /*
23881* Licensed to the Apache Software Foundation (ASF) under one
23882* or more contributor license agreements.  See the NOTICE file
23883* distributed with this work for additional information
23884* regarding copyright ownership.  The ASF licenses this file
23885* to you under the Apache License, Version 2.0 (the
23886* "License"); you may not use this file except in compliance
23887* with the License.  You may obtain a copy of the License at
23888*
23889*   http://www.apache.org/licenses/LICENSE-2.0
23890*
23891* Unless required by applicable law or agreed to in writing,
23892* software distributed under the License is distributed on an
23893* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
23894* KIND, either express or implied.  See the License for the
23895* specific language governing permissions and limitations
23896* under the License.
23897*/
23898
23899
23900    var lang = {
23901        toolbox: {
23902            brush: {
23903                title: {
23904                    rect: '鐭╁舰閫夋嫨',
23905                    polygon: '鍦堥€�',
23906                    lineX: '妯悜閫夋嫨',
23907                    lineY: '绾靛悜閫夋嫨',
23908                    keep: '淇濇寔閫夋嫨',
23909                    clear: '娓呴櫎閫夋嫨'
23910                }
23911            },
23912            dataView: {
23913                title: '鏁版嵁瑙嗗浘',
23914                lang: ['鏁版嵁瑙嗗浘', '鍏抽棴', '鍒锋柊']
23915            },
23916            dataZoom: {
23917                title: {
23918                    zoom: '鍖哄煙缂╂斁',
23919                    back: '鍖哄煙缂╂斁杩樺師'
23920                }
23921            },
23922            magicType: {
23923                title: {
23924                    line: '鍒囨崲涓烘姌绾垮浘',
23925                    bar: '鍒囨崲涓烘煴鐘跺浘',
23926                    stack: '鍒囨崲涓哄爢鍙�',
23927                    tiled: '鍒囨崲涓哄钩閾�'
23928                }
23929            },
23930            restore: {
23931                title: '杩樺師'
23932            },
23933            saveAsImage: {
23934                title: '淇濆瓨涓哄浘鐗�',
23935                lang: ['鍙抽敭鍙﹀瓨涓哄浘鐗�']
23936            }
23937        },
23938        series: {
23939            typeNames: {
23940                pie: '楗煎浘',
23941                bar: '鏌辩姸鍥�',
23942                line: '鎶樼嚎鍥�',
23943                scatter: '鏁g偣鍥�',
23944                effectScatter: '娑熸吉鏁g偣鍥�',
23945                radar: '闆疯揪鍥�',
23946                tree: '鏍戝浘',
23947                treemap: '鐭╁舰鏍戝浘',
23948                boxplot: '绠卞瀷鍥�',
23949                candlestick: 'K绾垮浘',
23950                k: 'K绾垮浘',
23951                heatmap: '鐑姏鍥�',
23952                map: '鍦板浘',
23953                parallel: '骞宠鍧愭爣鍥�',
23954                lines: '绾垮浘',
23955                graph: '鍏崇郴鍥�',
23956                sankey: '妗戝熀鍥�',
23957                funnel: '婕忔枟鍥�',
23958                gauge: '浠〃鐩樺浘',
23959                pictorialBar: '璞″舰鏌卞浘',
23960                themeRiver: '涓婚娌虫祦鍥�',
23961                sunburst: '鏃棩鍥�'
23962            }
23963        },
23964        aria: {
23965            general: {
23966                withTitle: '杩欐槸涓€涓叧浜庘€渰title}鈥濈殑鍥捐〃銆�',
23967                withoutTitle: '杩欐槸涓€涓浘琛紝'
23968            },
23969            series: {
23970                single: {
23971                    prefix: '',
23972                    withName: '鍥捐〃绫诲瀷鏄瘂seriesType}锛岃〃绀簕seriesName}銆�',
23973                    withoutName: '鍥捐〃绫诲瀷鏄瘂seriesType}銆�'
23974                },
23975                multiple: {
23976                    prefix: '瀹冪敱{seriesCount}涓浘琛ㄧ郴鍒楃粍鎴愩€�',
23977                    withName: '绗瑊seriesId}涓郴鍒楁槸涓€涓〃绀簕seriesName}鐨剓seriesType}锛�',
23978                    withoutName: '绗瑊seriesId}涓郴鍒楁槸涓€涓獅seriesType}锛�',
23979                    separator: {
23980                        middle: '锛�',
23981                        end: '銆�'
23982                    }
23983                }
23984            },
23985            data: {
23986                allData: '鍏舵暟鎹槸鈥斺€�',
23987                partialData: '鍏朵腑锛屽墠{displayCnt}椤规槸鈥斺€�',
23988                withName: '{name}鐨勬暟鎹槸{value}',
23989                withoutName: '{value}',
23990                separator: {
23991                    middle: '锛�',
23992                    end: ''
23993                }
23994            }
23995        }
23996    };
23997
23998    /*
23999* Licensed to the Apache Software Foundation (ASF) under one
24000* or more contributor license agreements.  See the NOTICE file
24001* distributed with this work for additional information
24002* regarding copyright ownership.  The ASF licenses this file
24003* to you under the Apache License, Version 2.0 (the
24004* "License"); you may not use this file except in compliance
24005* with the License.  You may obtain a copy of the License at
24006*
24007*   http://www.apache.org/licenses/LICENSE-2.0
24008*
24009* Unless required by applicable law or agreed to in writing,
24010* software distributed under the License is distributed on an
24011* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
24012* KIND, either express or implied.  See the License for the
24013* specific language governing permissions and limitations
24014* under the License.
24015*/
24016
24017    var aria = function (dom, ecModel) {
24018        var ariaModel = ecModel.getModel('aria');
24019        if (!ariaModel.get('show')) {
24020            return;
24021        }
24022        else if (ariaModel.get('description')) {
24023            dom.setAttribute('aria-label', ariaModel.get('description'));
24024            return;
24025        }
24026
24027        var seriesCnt = 0;
24028        ecModel.eachSeries(function (seriesModel, idx) {
24029            ++seriesCnt;
24030        }, this);
24031
24032        var maxDataCnt = ariaModel.get('data.maxCount') || 10;
24033        var maxSeriesCnt = ariaModel.get('series.maxCount') || 10;
24034        var displaySeriesCnt = Math.min(seriesCnt, maxSeriesCnt);
24035
24036        var ariaLabel;
24037        if (seriesCnt < 1) {
24038            // No series, no aria label
24039            return;
24040        }
24041        else {
24042            var title = getTitle();
24043            if (title) {
24044                ariaLabel = replace(getConfig('general.withTitle'), {
24045                    title: title
24046                });
24047            }
24048            else {
24049                ariaLabel = getConfig('general.withoutTitle');
24050            }
24051
24052            var seriesLabels = [];
24053            var prefix = seriesCnt > 1
24054                ? 'series.multiple.prefix'
24055                : 'series.single.prefix';
24056            ariaLabel += replace(getConfig(prefix), { seriesCount: seriesCnt });
24057
24058            ecModel.eachSeries(function (seriesModel, idx) {
24059                if (idx < displaySeriesCnt) {
24060                    var seriesLabel;
24061
24062                    var seriesName = seriesModel.get('name');
24063                    var seriesTpl = 'series.'
24064                        + (seriesCnt > 1 ? 'multiple' : 'single') + '.';
24065                    seriesLabel = getConfig(seriesName
24066                        ? seriesTpl + 'withName'
24067                        : seriesTpl + 'withoutName');
24068
24069                    seriesLabel = replace(seriesLabel, {
24070                        seriesId: seriesModel.seriesIndex,
24071                        seriesName: seriesModel.get('name'),
24072                        seriesType: getSeriesTypeName(seriesModel.subType)
24073                    });
24074
24075                    var data = seriesModel.getData();
24076                    window.data = data;
24077                    if (data.count() > maxDataCnt) {
24078                        // Show part of data
24079                        seriesLabel += replace(getConfig('data.partialData'), {
24080                            displayCnt: maxDataCnt
24081                        });
24082                    }
24083                    else {
24084                        seriesLabel += getConfig('data.allData');
24085                    }
24086
24087                    var dataLabels = [];
24088                    for (var i = 0; i < data.count(); i++) {
24089                        if (i < maxDataCnt) {
24090                            var name = data.getName(i);
24091                            var value = retrieveRawValue(data, i);
24092                            dataLabels.push(
24093                                replace(
24094                                    name
24095                                        ? getConfig('data.withName')
24096                                        : getConfig('data.withoutName'),
24097                                    {
24098                                        name: name,
24099                                        value: value
24100                                    }
24101                                )
24102                            );
24103                        }
24104                    }
24105                    seriesLabel += dataLabels
24106                            .join(getConfig('data.separator.middle'))
24107                        + getConfig('data.separator.end');
24108
24109                    seriesLabels.push(seriesLabel);
24110                }
24111            });
24112
24113            ariaLabel += seriesLabels
24114                    .join(getConfig('series.multiple.separator.middle'))
24115                + getConfig('series.multiple.separator.end');
24116
24117            dom.setAttribute('aria-label', ariaLabel);
24118        }
24119
24120        function replace(str, keyValues) {
24121            if (typeof str !== 'string') {
24122                return str;
24123            }
24124
24125            var result = str;
24126            each$1(keyValues, function (value, key) {
24127                result = result.replace(
24128                    new RegExp('\\{\\s*' + key + '\\s*\\}', 'g'),
24129                    value
24130                );
24131            });
24132            return result;
24133        }
24134
24135        function getConfig(path) {
24136            var userConfig = ariaModel.get(path);
24137            if (userConfig == null) {
24138                var pathArr = path.split('.');
24139                var result = lang.aria;
24140                for (var i = 0; i < pathArr.length; ++i) {
24141                    result = result[pathArr[i]];
24142                }
24143                return result;
24144            }
24145            else {
24146                return userConfig;
24147            }
24148        }
24149
24150        function getTitle() {
24151            var title = ecModel.getModel('title').option;
24152            if (title && title.length) {
24153                title = title[0];
24154            }
24155            return title && title.text;
24156        }
24157
24158        function getSeriesTypeName(type) {
24159            return lang.series.typeNames[type] || '鑷畾涔夊浘';
24160        }
24161    };
24162
24163    /*
24164* Licensed to the Apache Software Foundation (ASF) under one
24165* or more contributor license agreements.  See the NOTICE file
24166* distributed with this work for additional information
24167* regarding copyright ownership.  The ASF licenses this file
24168* to you under the Apache License, Version 2.0 (the
24169* "License");
24169 you may not use this file except in compliance
24170* with the License.  You may obtain a copy of the License at
24171*
24172*   http://www.apache.org/licenses/LICENSE-2.0
24173*
24174* Unless required by applicable law or agreed to in writing,
24175* software distributed under the License is distributed on an
24176* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
24177* KIND, either express or implied.  See the License for the
24178* specific language governing permissions and limitations
24179* under the License.
24180*/
24181
24182    var PI$1 = Math.PI;
24183
24184    /**
24185     * @param {module:echarts/ExtensionAPI} api
24186     * @param {Object} [opts]
24187     * @param {string} [opts.text]
24188     * @param {string} [opts.color]
24189     * @param {string} [opts.textColor]
24190     * @return {module:zrender/Element}
24191     */
24192    var loadingDefault = function (api, opts) {
24193        opts = opts || {};
24194        defaults(opts, {
24195            text: 'loading',
24196            color: '#c23531',
24197            textColor: '#000',
24198            maskColor: 'rgba(255, 255, 255, 0.8)',
24199            zlevel: 0
24200        });
24201        var mask = new Rect({
24202            style: {
24203                fill: opts.maskColor
24204            },
24205            zlevel: opts.zlevel,
24206            z: 10000
24207        });
24208        var arc = new Arc({
24209            shape: {
24210                startAngle: -PI$1 / 2,
24211                endAngle: -PI$1 / 2 + 0.1,
24212                r: 10
24213            },
24214            style: {
24215                stroke: opts.color,
24216                lineCap: 'round',
24217                lineWidth: 5
24218            },
24219            zlevel: opts.zlevel,
24220            z: 10001
24221        });
24222        var labelRect = new Rect({
24223            style: {
24224                fill: 'none',
24225                text: opts.text,
24226                textPosition: 'right',
24227                textDistance: 10,
24228                textFill: opts.textColor
24229            },
24230            zlevel: opts.zlevel,
24231            z: 10001
24232        });
24233
24234        arc.animateShape(true)
24235            .when(1000, {
24236                endAngle: PI$1 * 3 / 2
24237            })
24238            .start('circularInOut');
24239        arc.animateShape(true)
24240            .when(1000, {
24241                startAngle: PI$1 * 3 / 2
24242            })
24243            .delay(300)
24244            .start('circularInOut');
24245
24246        var group = new Group();
24247        group.add(arc);
24248        group.add(labelRect);
24249        group.add(mask);
24250        // Inject resize
24251        group.resize = function () {
24252            var cx = api.getWidth() / 2;
24253            var cy = api.getHeight() / 2;
24254            arc.setShape({
24255                cx: cx,
24256                cy: cy
24257            });
24258            var r = arc.shape.r;
24259            labelRect.setShape({
24260                x: cx - r,
24261                y: cy - r,
24262                width: r * 2,
24263                height: r * 2
24264            });
24265
24266            mask.setShape({
24267                x: 0,
24268                y: 0,
24269                width: api.getWidth(),
24270                height: api.getHeight()
24271            });
24272        };
24273        group.resize();
24274        return group;
24275    };
24276
24277    /*
24278* Licensed to the Apache Software Foundation (ASF) under one
24279* or more contributor license agreements.  See the NOTICE file
24280* distributed with this work for additional information
24281* regarding copyright ownership.  The ASF licenses this file
24282* to you under the Apache License, Version 2.0 (the
24283* "License"); you may not use this file except in compliance
24284* with the License.  You may obtain a copy of the License at
24285*
24286*   http://www.apache.org/licenses/LICENSE-2.0
24287*
24288* Unless required by applicable law or agreed to in writing,
24289* software distributed under the License is distributed on an
24290* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
24291* KIND, either express or implied.  See the License for the
24292* specific language governing permissions and limitations
24293* under the License.
24294*/
24295
24296    /**
24297     * @module echarts/stream/Scheduler
24298     */
24299
24300    /**
24301     * @constructor
24302     */
24303    function Scheduler(ecInstance, api, dataProcessorHandlers, visualHandlers) {
24304        this.ecInstance = ecInstance;
24305        this.api = api;
24306        this.unfinished;
24307
24308        // Fix current processors in case that in some rear cases that
24309        // processors might be registered after echarts instance created.
24310        // Register processors incrementally for a echarts instance is
vendor: 4,354 bytes, lines 24311-24406
24311        // not supported by this stream architecture.
24312        var dataProcessorHandlers = this._dataProcessorHandlers = dataProcessorHandlers.slice();
24313        var visualHandlers = this._visualHandlers = visualHandlers.slice();
24314        this._allHandlers = dataProcessorHandlers.concat(visualHandlers);
24315
24316        /**
24317         * @private
24318         * @type {
24319     *     [handlerUID: string]: {
24320     *         seriesTaskMap?: {
24321     *             [seriesUID: string]: Task
24322     *         },
24323     *         overallTask?: Task
24324     *     }
24325     * }
24326         */
24327        this._stageTaskMap = createHashMap();
24328    }
24329
24330    var proto = Scheduler.prototype;
24331
24332    /**
24333     * @param {module:echarts/model/Global} ecModel
24334     * @param {Object} payload
24335     */
24336    proto.restoreData = function (ecModel, payload) {
24337        // TODO: Only restroe needed series and components, but not all components.
24338        // Currently `restoreData` of all of the series and component will be called.
24339        // But some independent components like `title`, `legend`, `graphic`, `toolbox`,
24340        // `tooltip`, `axisPointer`, etc, do not need series refresh when `setOption`,
24341        // and some components like coordinate system, axes, dataZoom, visualMap only
24342        // need their target series refresh.
24343        // (1) If we are implementing this feature some day, we should consider these cases:
24344        // if a data processor depends on a component (e.g., dataZoomProcessor depends
24345        // on the settings of `dataZoom`), it should be re-performed if the component
24346        // is modified by `setOption`.
24347        // (2) If a processor depends on sevral series, speicified by its `getTargetSeries`,
24348        // it should be re-performed when the result array of `getTargetSeries` changed.
24349        // We use `dependencies` to cover these issues.
24350        // (3) How to update target series when coordinate system related components modified.
24351
24352        // TODO: simply the dirty mechanism? Check whether only the case here can set tasks dirty,
24353        // and this case all of the tasks will be set as dirty.
24354
24355        ecModel.restoreData(payload);
24356
24357        // Theoretically an overall task not only depends on each of its target series, but also
24358        // depends on all of the series.
24359        // The overall task is not in pipeline, and `ecModel.restoreData` only set pipeline tasks
24360        // dirty. If `getTargetSeries` of an overall task returns nothing, we should also ensure
24361        // that the overall task is set as dirty and to be performed, otherwise it probably cause
24362        // state chaos. So we have to set dirty of all of the overall tasks manually, otherwise it
24363        // probably cause state chaos (consider `dataZoomProcessor`).
24364        this._stageTaskMap.each(function (taskRecord) {
24365            var overallTask = taskRecord.overallTask;
24366            overallTask && overallTask.dirty();
24367        });
24368    };
24369
24370// If seriesModel provided, incremental threshold is check by series data.
24371    proto.getPerformArgs = function (task, isBlock) {
24372        // For overall task
24373        if (!task.__pipeline) {
24374            return;
24375        }
24376
24377        var pipeline = this._pipelineMap.get(task.__pipeline.id);
24378        var pCtx = pipeline.context;
24379        var incremental = !isBlock
24380            && pipeline.progressiveEnabled
24381            && (!pCtx || pCtx.progressiveRender)
24382            && task.__idxInPipeline > pipeline.blockIndex;
24383
24384        var step = incremental ? pipeline.step : null;
24385        var modDataCount = pCtx && pCtx.modDataCount;
24386        var modBy = modDataCount != null ? Math.ceil(modDataCount / step): null;
24387
24388        return {step: step, modBy: modBy, modDataCount: modDataCount};
24389    };
24390
24391    proto.getPipeline = function (pipelineId) {
24392        return this._pipelineMap.get(pipelineId);
24393    };
24394
24395    /**
24396     * Current, progressive rendering starts from visual and layout.
24397     * Always detect render mode in the same stage, avoiding that incorrect
24398     * detection caused by data filtering.
24399     * Caution:
24400     * `updateStreamModes` use `seriesModel.getData()`.
24401     */
24402    proto.updateStreamModes = function (seriesModel, view) {
24403        var pipeline = this._pipelineMap.get(seriesModel.uid);
24404        var data = seriesModel.getData();
24405        var dataLen = data.count();
24406
24407        // `progressiveRender` means that can render progressively in each
24408        // animation frame. Note that some types of series do not provide
24409        // `view.incrementalPrepareRender` but support `chart.appendData`. We
24410        // use the term `incremental` but not `progressive` to describe the
24411        // case that `chart.appendData`.
24412        var progressiveRender = pipeline.progressiveEnabled
24413            && view.incrementalPrepareRender
24414            && dataLen >= pipeline.threshold;
24415
24416        var large = seriesModel.get('large') && dataLen >= seriesModel.get('largeThreshold');
24417
24418        // TODO: modDataCount should not updated if `appendData`, otherwise cause whole repaint.
24419        // see `test/candlestick-large3.html`
24420        var modDataCount = seriesModel.get('progressiveChunkMode') === 'mod' ? dataLen : null;
24421
24422        seriesModel.pipelineContext = pipeline.context = {
24423            progressiveRender: progressiveRender,
24424            modDataCount: modDataCount,
24425            large: large
24426        };
24427    };
24428
24429    proto.restorePipelines = function (ecModel) {
24430        var scheduler = this;
24431        var pipelineMap = scheduler._pipelineMap = createHashMap();
24432
24433        ecModel.eachSeries(function (seriesModel) {
24434            var progressive = seriesModel.getProgressive();
24435            var pipelineId = seriesModel.uid;
24436
24437            pipelineMap.set(pipelineId, {
24438                id: pipelineId,
24439                head: null,
24440                tail: null,
24441                threshold: seriesModel.getProgressiveThreshold(),
24442                progressiveEnabled: progressive
24443                && !(seriesModel.preventIncremental && seriesModel.preventIncremental()),
24444                blockIndex: -1,
24445                step: Math.round(progressive || 700),
24446                count: 0
24447            });
24448
24449            pipe(scheduler, seriesModel, seriesModel.dataTask);
24450        });
24451    };
24452
24453    proto.prepareStageTasks = function () {
24454        var stageTaskMap = this._stageTaskMap;
24455        var ecModel = this.ecInstance.getModel();
24456        var api = this.api;
24457
24458        each$1(this._allHandlers, function (handler) {
24459            var record = stageTaskMap.get(handler.uid) || stageTaskMap.set(handler.uid, []);
24460
24461            handler.reset && createSeriesStageTask(this, handler, record, ecModel, api);
24462            handler.overallReset && createOverallStageTask(this, handler, record, ecModel, api);
24463        }, this);
24464    };
24465
24466    proto.prepareView = function (view, model, ecModel, api) {
24467        var renderTask = view.renderTask;
24468        var context = renderTask.context;
24469
24470        context.model = model;
24471        context.ecModel = ecModel;
24472        context.api = api;
24473
24474        renderTask.__block = !view.incrementalPrepareRender;
24475
24476        pipe(this, model, renderTask);
24477    };
24478
24479
24480    proto.performDataProcessorTasks = function (ecModel, payload) {
24481        // If we do not use `block` here, it should be considered when to update modes.
24482        performStageTasks(this, this._dataProcessorHandlers, ecM
24482odel, payload, {block: true});
24483    };
24484
24485// opt
24486// opt.visualType: 'visual' or 'layout'
24487// opt.setDirty
24488    proto.performVisualTasks = function (ecModel, payload, opt) {
24489        performStageTasks(this, this._visualHandlers, ecModel, payload, opt);
24490    };
24491
24492    function performStageTasks(scheduler, stageHandlers, ecModel, payload, opt) {
24493        opt = opt || {};
24494        var unfinished;
24495
24496        each$1(stageHandlers, function (stageHandler, idx) {
24497            if (opt.visualType && opt.visualType !== stageHandler.visualType) {
24498                return;
24499            }
24500
24501            var stageHandlerRecord = scheduler._stageTaskMap.get(stageHandler.uid);
24502            var seriesTaskMap = stageHandlerRecord.seriesTaskMap;
24503            var overallTask = stageHandlerRecord.overallTask;
24504
24505            if (overallTask) {
24506                var overallNeedDirty;
24507                var agentStubMap = overallTask.agentStubMap;
24508                agentStubMap.each(function (stub) {
24509                    if (needSetDirty(opt, stub)) {
24510                        stub.dirty();
24511                        overallNeedDirty = true;
24512                    }
24513                });
24514                overallNeedDirty && overallTask.dirty();
24515                updatePayload(overallTask, payload);
24516                var performArgs = scheduler.getPerformArgs(overallTask, opt.block);
24517                // Execute stubs firstly, which may set the overall task dirty,
24518                // then execute the overall task. And stub will call seriesModel.setData,
24519                // which ensures that in the overallTask seriesModel.getData() will not
24520                // return incorrect data.
24521                agentStubMap.each(function (stub) {
24522                    stub.perform(performArgs);
24523                });
24524                unfinished |= overallTask.perform(performArgs);
24525            }
24526            else if (seriesTaskMap) {
24527                seriesTaskMap.each(function (task, pipelineId) {
24528                    if (needSetDirty(opt, task)) {
24529                        task.dirty();
24530                    }
24531                    var performArgs = scheduler.getPerformArgs(task, opt.block);
24532                    performArgs.skip = !stageHandler.performRawSeries
24533                        && ecModel.isSeriesFiltered(task.context.model);
24534                    updatePayload(task, payload);
24535                    unfinished |= task.perform(performArgs);
24536                });
24537            }
24538        });
24539
24540        function needSetDirty(opt, task) {
24541            return opt.setDirty && (!opt.dirtyMap || opt.dirtyMap.get(task.__pipeline.id));
24542        }
24543
24544        scheduler.unfinished |= unfinished;
24545    }
24546
24547    proto.performSeriesTasks = function (ecModel) {
24548        var unfinished;
24549
24550        ecModel.eachSeries(function (seriesModel) {
24551            // Progress to the end for dataInit and dataRestore.
24552            unfinished |= seriesModel.dataTask.perform();
24553        });
24554
24555        this.unfinished |= unfinished;
24556    };
24557
24558    proto.plan = function () {
24559        // Travel pipelines, check block.
24560        this._pipelineMap.each(function (pipeline) {
24561            var task = pipeline.tail;
24562            do {
24563                if (task.__block) {
24564                    pipeline.blockIndex = task.__idxInPipeline;
24565                    break;
24566                }
24567                task = task.getUpstream();
24568            }
24569            while (task);
24570        });
24571    };
24572
24573    var updatePayload = proto.updatePayload = function (task, payload) {
24574        payload !== 'remain' && (task.context.payload = payload);
24575    };
24576
24577    function createSeriesStageTask(scheduler, stageHandler, stageHandlerRecord, ecModel, api) {
24578        var seriesTaskMap = stageHandlerRecord.seriesTaskMap
24579            || (stageHandlerRecord.seriesTaskMap = createHashMap());
24580        var seriesType = stageHandler.seriesType;
24581        var getTargetSeries = stageHandler.getTargetSeries;
24582
24583        // If a stageHandler should cover all series, `createOnAllSeries` should be declared mandatorily,
24584        // to avoid some typo or abuse. Otherwise if an extension do not specify a `seriesType`,
24585        // it works but it may cause other irrelevant charts blocked.
24586        if (stageHandler.createOnAllSeries) {
24587            ecModel.eachRawSeries(create);
24588        }
24589        else if (seriesType) {
24590            ecModel.eachRawSeriesByType(seriesType, create);
24591        }
24592        else if (getTargetSeries) {
24593            getTargetSeries(ecModel, api).each(create);
24594        }
24595
24596        function create(seriesModel) {
24597            var pipelineId = seriesModel.uid;
24598
24599            // Init tasks for each seriesModel only once.
24600            // Reuse original task instance.
24601            var task = seriesTaskMap.get(pipelineId)
24602                || seriesTaskMap.set(pipelineId, createTask({
24603                    plan: seriesTaskPlan,
24604                    reset: seriesTaskReset,
24605                    count: seriesTaskCount
24606                }));
24607            task.context = {
24608                model: seriesModel,
24609                ecModel: ecModel,
24610                api: api,
24611                useClearVisual: stageHandler.isVisual && !stageHandler.isLayout,
24612                plan: stageHandler.plan,
24613                reset: stageHandler.reset,
24614                scheduler: scheduler
24615            };
24616            pipe(scheduler, seriesModel, task);
24617        }
24618
24619        // Clear unused series tasks.
24620        var pipelineMap = scheduler._pipelineMap;
24621        seriesTaskMap.each(function (task, pipelineId) {
24622            if (!pipelineMap.get(pipelineId)) {
24623                task.dispose();
24624                seriesTaskMap.removeKey(pipelineId);
24625            }
24626        });
24627    }
24628
24629    function createOverallStageTask(scheduler, stageHandler, stageHandlerRecord, ecModel, api) {
24630        var overallTask = stageHandlerRecord.overallTask = stageHandlerRecord.overallTask
24631            // For overall task, the function only be called on reset stage.
24632            || createTask({reset: overallTaskReset});
24633
24634        overallTask.context = {
24635            ecModel: ecModel,
24636            api: api,
24637            overallReset: stageHandler.overallReset,
24638            scheduler: scheduler
24639        };
24640
24641        // Reuse orignal stubs.
24642        var agentStubMap = overallTask.agentStubMap = overallTask.agentStubMap || createHashMap();
24643
24644        var seriesType = stageHandler.seriesType;
24645        var getTargetSeries = stageHandler.getTargetSeries;
24646        var overallProgress = true;
24647        var modifyOutputEnd = stageHandler.modifyOutputEnd;
24648
24649        // An overall task with seriesType detected or has `getTargetSeries`, we add
24650        // stub in each pipelines, it will set the overall task dirty when the pipeline
24651        // progress. Moreover, to avoid call the overall task each frame (too frequent),
24652        // we set the pipeline block.
24653        if (seriesType) {
24654            ecModel.eachRawSeriesByType(seriesType, createStub);
24655        }
24656        else if (getTargetSeries) {
24657            getTargetSeries(ecModel, api).each(createStub);
24658        }
24659        // Otherwise, (usually it is legancy case), the overall task will only be
24660        // executed when upstream dirty. Otherwise the progressive rendering of all
24661        // pipelines will be disabled unexpectedly. But it still needs stubs to receive
24662        // dirty info from upsteam.
24663        else {
24664            overallProgress = false;
24665            each$1(ecModel.getSeries(), createStub);
24666        }
24667
24668        function createStub(seriesModel) {
24669            var pipelineId = seriesModel.uid;
24670            var stub = agentStubMap.get(pipelineId);
24671            if (!stub) {
24672                stub = agentStubMap.set(pipelineId, createTask(
24673                    {reset: stubReset, onDirty: stubOnDirty}
24674                ));
24675                // When the result of `getTargetSeries` changed, the overallTask
24676                // should be set as dirty and re-performed.
24677                overallTask.dirty();
24678            }
24679            stub.context = {
24680                model: seriesModel,
24681                overallProgress: overallProgress,
24682                modifyOutputEnd: modifyOutputEnd
24683            };
24684            stub.agent = overallTask;
24685            stub.__block = overallProgress;
24686
24687            pipe(scheduler, seriesModel, stub);
24688        }
24689
24690        // Clear unused stubs.
24691        var pipelineMap = scheduler._pipelineMap;
24692        agentStubMap.each(function (stub, pipelineId) {
24693            if (!pipelineMap.get(pipelineId)) {
24694                stub.dispose();
24695                // When the result of `getTargetSeries` changed, the overallTask
24696                // should be set as dirty and re-performed.
24697                overallTask.dirty();
24698                agentStubMap.removeKey(pipelineId);
24699            }
24700        });
24701    }
24702
24703    function overallTaskReset(context) {
24704        context.overallReset(
24705            context.ecModel, context.api, context.payload
24706        );
24707    }
24708
24709    function stubReset(context, upstreamContext) {
24710        return context.overallProgress && stubProgress;
24711    }
24712
24713    function stubProgress() {
24714        this.agent.dirty();
24715        this.getDownstream().dirty();
24716    }
24717
24718    function stubOnDirty() {
24719        this.agent && this.agent.dirty();
24720    }
24721
24722    function seriesTaskPlan(context) {
24723        return context.plan && context.plan(
24724            context.model, context.ecModel, context.api, context.payload
24725        );
24726    }
24727
24728    function seriesTaskReset(context) {
24729        if (context.useClearVisual) {
24730            context.data.clearAllVisual();
24731        }
24732        var resetDefines = context.resetDefines = normalizeToArray(context.reset(
24733            context.model, context.ecModel, context.api, context.payload
24734        ));
24735        return resetDefines.length > 1
24736            ? map(resetDefines, function (v, idx) {
24737                return makeSeriesTaskProgress(idx);
24738            })
24739            : singleSeriesTaskProgress;
24740    }
24741
24742    var singleSeriesTaskProgress = makeSeriesTaskProgress(0);
24743
24744    function makeSeriesTaskProgress(resetDefineIdx) {
24745        return function (params, context) {
24746            var data = context.data;
24747            var resetDefine = context.resetDefines[resetDefineIdx];
24748
24749            if (resetDefine && resetDefine.dataEach) {
24750                for (var i = params.start; i < params.end; i++) {
24751                    resetDefine.dataEach(data, i);
24752                }
24753            }
24754            else if (resetDefine && resetDefine.progress) {
24755                resetDefine.progress(params, data);
24756            }
24757        };
24758    }
24759
24760    function seriesTaskCount(context) {
24761        return context.data.count();
24762    }
24763
24764    function pipe(scheduler, seriesModel, task) {
24765        var pipelineId = seriesModel.uid;
24766        var pipeline = scheduler._pipelineMap.get(pipelineId);
24767        !pipeline.head && (pipeline.head = task);
24768        pipeline.tail && pipeline.tail.pipe(task);
24769        pipeline.tail = task;
24770        task.__idxInPipeline = pipeline.count++;
24771        task.__pipeline = pipeline;
24772    }
24773
24774    Scheduler.wrapStageHandler = function (stageHandler, visualType) {
24775        if (isFunction$1(stageHandler)) {
24776            stageHandler = {
24777                overallReset: stageHandler,
24778                seriesType: detectSeriseType(stageHandler)
24779            };
24780        }
24781
24782        stageHandler.uid = getUID('stageHandler');
24783        visualType && (stageHandler.visualType = visualType);
24784
24785        return stageHandler;
24786    };
24787
24788
24789
24790    /**
24791     * Only some legacy stage handlers (usually in echarts extensions) are pure function.
24792     * To ensure that they can work normally, they should work in block mode, that is,
24793     * they should not be started util the previous tasks finished. So they cause the
24794     * progressive rendering disabled. We try to detect the series type, to narrow down
24795     * the block range to only the series type they concern, but not all series.
24796     */
24797    function detectSeriseType(legacyFunc) {
24798        seriesType = null;
24799        try {
24800            // Assume there is no async when calling `eachSeriesByType`.
24801            legacyFunc(ecModelMock, apiMock);
24802        }
24803        catch (e) {
24804        }
24805        return seriesType;
24806    }
24807
24808    var ecModelMock = {};
24809    var apiMock = {};
24810    var seriesType;
24811
24812    mockMethods(ecModelMock, GlobalModel);
24813    mockMethods(apiMock, ExtensionAPI);
24814    ecModelMock.eachSeriesByType = ecModelMock.eachRawSeriesByType = function (type) {
24815        seriesType = type;
24816    };
24817    ecModelMock.eachComponent = function (cond) {
24818        if (cond.mainType === 'series' && cond.subType) {
24819            seriesType = cond.subType;
24820        }
24821    };
24822
24823    function mockMethods(target, Clz) {
24824        for (var name in Clz.prototype) {
24825            // Do not use hasOwnProperty
24826            target[name] = noop;
24827        }
24828    }
24829
24830    /*
24831* Licensed to the Apache Software Foundation (ASF) under one
24832* or more contributor license agreements.  See the NOTICE file
24833* distributed with this work for additional information
24834* regarding copyright ownership.  The ASF licenses this file
24835* to you under the Apache License, Version 2.0 (the
24836* "License"); you may not use this file except in compliance
24837* with the License.  You may obtain a copy of the License at
24838*
24839*   http://www.apache.org/licenses/LICENSE-2.0
24840*
24841* Unless required by applicable law or agreed to in writing,
24842* software distributed under the License is distributed on an
24843* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
24844* KIND, either express or implied.  See the License for the
24845* specific language governing permissions and limitations
24846* under the License.
24847*/
24848
24849    var colorAll = ['#37A2DA', '#32C5E9', '#67E0E3', '#9FE6B8', '#FFDB5C','#ff9f7f', '#fb7293', '#E062AE', '#E690D1', '#e7bcf3', '#9d96f5', '#8378EA', '#96BFFF'];
24850
24851    var lightTheme = {
24852
24853        color: colorAll,
24854
24855        colorLayer: [
24856            ['#37A2DA', '#ffd85c', '#fd7b5f'],
24857            ['#37A2DA', '#67E0E3', '#FFDB5C', '#ff9f7f', '#E062AE', '#9d96f5'],
24858            ['#37A2DA', '#32C5E9', '#9FE6B8', '#FFDB5C', '#ff9f7f', '#fb7293', '#e7bcf3', '#8378EA', '#96BFFF'],
24859            colorAll
24860        ]
24861    };
24862
24863    /*
24864* Licensed to the Apache Software Foundation (ASF) under one
24865* or more contributor license agreements.  See the NOTICE file
24866* distributed with this work for additional information
24867* regarding copyright ownership.  The ASF licenses this file
24868* to you under the Apache License, Version 2.0 (the
24869* "License"); you may not use this file except in compliance
24870* with the License.  You may obtain a copy of the License at
24871*
24872*   http://www.apache.org/licenses/LICENSE-2.0
24873*
24874* Unless required by applicable law or agreed to in writing,
24875* software distributed under the License is distributed on an
24876* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
24877* KIND, either express or implied.  See the License for the
24878* specific language governing permissions and limitations
24879* under the License.
24880*/
24881
24882    var contrastColor = '#eee';
24883    var axisCommon = function () {
24884        return {
24885            axisLine: {
24886                lineStyle: {
24887                    color: contrastColor
24888                }
24889            },
24890            axisTick: {
24891                lineStyle: {
24892                    color: contrastColor
24893                }
24894            },
24895            axisLabel: {
24896                textStyle: {
24897                    color: contrastColor
24898                }
24899            },
24900            splitLine: {
24901                lineStyle: {
24902                    type: 'dashed',
24903                    color: '#aaa'
24904                }
24905            },
24906            splitArea: {
24907                areaStyle: {
24908                    color: contrastColor
24909                }
24910            }
24911        };
24912    };
24913
24914    var colorPalette = ['#dd6b66','#759aa0','#e69d87','#8dc1a9','#ea7e53','#eedd78','#73a373','#73b9bc','#7289ab', '#91ca8c','#f49f42'];
24915    var theme = {
24916        color: colorPalette,
24917        backgroundColor: '#333',
24918        tooltip: {
24919            axisPointer: {
24920                lineStyle: {
24921                    color: contrastColor
24922                },
24923                crossStyle: {
24924                    color: contrastColor
24925                }
24926            }
24927        },
24928        legend: {
24929            textStyle: {
24930                color: contrastColor
24931            }
24932        },
24933        textStyle: {
24934            color: contrastColor
24935        },
24936        title: {
24937            textStyle: {
24938                color: contrastColor
24939            }
24940        },
24941        toolbox: {
24942            iconStyle: {
24943                normal: {
24944                    borderColor: contrastColor
24945                }
24946            }
24947        },
24948        dataZoom: {
24949            textStyle: {
24950                color: contrastColor
24951            }
24952        },
24953        visualMap: {
24954            textStyle: {
24955                color: contrastColor
24956            }
24957        },
24958        timeline: {
24959            lineStyle: {
24960                color: contrastColor
24961            },
24962            itemStyle: {
24963                normal: {
24964                    color: colorPalette[1]
24965                }
24966            },
24967            label: {
24968                normal: {
24969                    textStyle: {
24970                        color: contrastColor
24971                    }
24972                }
24973            },
24974            controlStyle: {
24975                normal: {
24976                    color: contrastColor,
24977                    borderColor: contrastColor
24978                }
24979            }
24980        },
24981        timeAxis: axisCommon(),
24982        logAxis: axisCommon(),
24983        valueAxis: axisCommon(),
24984        categoryAxis: axisCommon(),
24985
24986        line: {
24987            symbol: 'circle'
24988        },
24989        graph: {
24990            color: colorPalette
24991        },
24992        gauge: {
24993            title: {
24994                textStyle: {
24995                    color: contrastColor
24996                }
24997            }
24998        },
24999        candlestick: {
25000            itemStyle: {
25001                normal: {
25002                    color: '#FD1050',
25003                    color0: '#0CF49B',
25004                    borderColor: '#FD1050',
25005                    borderColor0: '#0CF49B'
25006                }
25007            }
25008        }
25009    };
25010    theme.categoryAxis.splitLine.show = false;
25011
25012    /*
25013* Licensed to the Apache Software Foundation (ASF) under one
25014* or more contributor license agreements.  See the NOTICE file
25015* distributed with this work for additional information
25016* regarding copyright ownership.  The ASF licenses this file
25017* to you under the Apache License, Version 2.0 (the
25018* "License");
25018 you may not use this file except in compliance
25019* with the License.  You may obtain a copy of the License at
25020*
25021*   http://www.apache.org/licenses/LICENSE-2.0
25022*
25023* Unless required by applicable law or agreed to in writing,
25024* software distributed under the License is distributed on an
25025* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25026* KIND, either express or implied.  See the License for the
25027* specific language governing permissions and limitations
25028* under the License.
25029*/
25030
25031    /**
25032     * This module is imported by echarts directly.
25033     *
25034     * Notice:
25035     * Always keep this file exists for backward compatibility.
25036     * Because before 4.1.0, dataset is an optional component,
25037     * some users may import this module manually.
25038     */
25039
25040    ComponentModel.extend({
25041
25042        type: 'dataset',
25043
25044        /**
25045         * @protected
25046         */
25047        defaultOption: {
25048
25049            // 'row', 'column'
25050            seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN,
25051
25052            // null/'auto': auto detect header, see "module:echarts/data/helper/sourceHelper"
25053            sourceHeader: null,
25054
25055            dimensions: null,
25056
25057            source: null
25058        },
25059
25060        optionUpdated: function () {
25061            detectSourceFormat(this);
25062        }
25063
25064    });
25065
25066    Component.extend({
25067
25068        type: 'dataset'
25069
25070    });
25071
25072    /*
25073* Licensed to the Apache Software Foundation (ASF) under one
25074* or more contributor license agreements.  See the NOTICE file
25075* distributed with this work for additional information
25076* regarding copyright ownership.  The ASF licenses this file
25077* to you under the Apache License, Version 2.0 (the
25078* "License"); you may not use this file except in compliance
25079* with the License.  You may obtain a copy of the License at
25080*
25081*   http://www.apache.org/licenses/LICENSE-2.0
25082*
25083* Unless required by applicable law or agreed to in writing,
25084* software distributed under the License is distributed on an
25085* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25086* KIND, either express or implied.  See the License for the
25087* specific language governing permissions and limitations
25088* under the License.
25089*/
25090    var assert = assert$1;
25091    var each = each$1;
25092    var isFunction = isFunction$1;
25093    var isObject = isObject$1;
25094    var parseClassType = ComponentModel.parseClassType;
25095
25096    var version = '4.1.0';
25097
25098    var dependencies = {
25099        zrender: '4.0.4'
25100    };
25101
25102    var TEST_FRAME_REMAIN_TIME = 1;
25103
25104    var PRIORITY_PROCESSOR_FILTER = 1000;
25105    var PRIORITY_PROCESSOR_STATISTIC = 5000;
25106
25107    var PRIORITY_VISUAL_LAYOUT = 1000;
25108    var PRIORITY_VISUAL_GLOBAL = 2000;
25109    var PRIORITY_VISUAL_CHART = 3000;
25110    var PRIORITY_VISUAL_COMPONENT = 4000;
25111// FIXME
25112// necessary?
25113    var PRIORITY_VISUAL_BRUSH = 5000;
25114
25115    var PRIORITY = {
25116        PROCESSOR: {
25117            FILTER: PRIORITY_PROCESSOR_FILTER,
25118            STATISTIC: PRIORITY_PROCESSOR_STATISTIC
25119        },
25120        VISUAL: {
25121            LAYOUT: PRIORITY_VISUAL_LAYOUT,
25122            GLOBAL: PRIORITY_VISUAL_GLOBAL,
25123            CHART: PRIORITY_VISUAL_CHART,
25124            COMPONENT: PRIORITY_VISUAL_COMPONENT,
25125            BRUSH: PRIORITY_VISUAL_BRUSH
25126        }
25127    };
25128
25129// Main process have three entries: `setOption`, `dispatchAction` and `resize`,
25130// where they must not be invoked nestedly, except the only case: invoke
25131// dispatchAction with updateMethod "none" in main process.
25132// This flag is used to carry out this rule.
25133// All events will be triggered out side main process (i.e. when !this[IN_MAIN_PROCESS]).
25134    var IN_MAIN_PROCESS = '__flagInMainProcess';
25135    var OPTION_UPDATED = '__optionUpdated';
25136    var ACTION_REG = /^[a-zA-Z0-9_]+$/;
25137
25138
25139    function createRegisterEventWithLowercaseName(method) {
25140        return function (eventName, handler, context) {
25141            // Event name is all lowercase
25142            eventName = eventName && eventName.toLowerCase();
25143            Eventful.prototype[method].call(this, eventName, handler, context);
25144        };
25145    }
25146
25147    /**
25148     * @module echarts~MessageCenter
25149     */
25150    function MessageCenter() {
25151        Eventful.call(this);
25152    }
25153    MessageCenter.prototype.on = createRegisterEventWithLowercaseName('on');
25154    MessageCenter.prototype.off = createRegisterEventWithLowercaseName('off');
25155    MessageCenter.prototype.one = createRegisterEventWithLowercaseName('one');
25156    mixin(MessageCenter, Eventful);
25157
25158    /**
25159     * @module echarts~ECharts
25160     */
25161    function ECharts(dom, theme$$1, opts) {
25162        opts = opts || {};
25163
25164        // Get theme by name
25165        if (typeof theme$$1 === 'string') {
25166            theme$$1 = themeStorage[theme$$1];
25167        }
25168
25169        /**
25170         * @type {string}
25171         */
25172        this.id;
25173
25174        /**
25175         * Group id
25176         * @type {string}
25177         */
25178        this.group;
25179
25180        /**
25181         * @type {HTMLElement}
25182         * @private
25183         */
25184        this._dom = dom;
25185
25186        var defaultRenderer = 'canvas';
25187        if (__DEV__) {
25188            defaultRenderer = (
25189                typeof window === 'undefined' ? global : window
25190            ).__ECHARTS__DEFAULT__RENDERER__ || defaultRenderer;
25191        }
25192
25193        /**
25194         * @type {module:zrender/ZRender}
25195         * @private
25196         */
25197        var zr = this._zr = init$1(dom, {
25198            renderer: opts.renderer || defaultRenderer,
25199            devicePixelRatio: opts.devicePixelRatio,
25200            width: opts.width,
25201            height: opts.height
25202        });
25203
25204        /**
25205         * Expect 60 pfs.
25206         * @type {Function}
25207         * @private
25208         */
25209        this._throttledZrFlush = throttle(bind(zr.flush, zr), 17);
25210
25211        var theme$$1 = clone(theme$$1);
25212        theme$$1 && backwardCompat(theme$$1, true);
25213        /**
25214         * @type {Object}
25215         * @private
25216         */
25217        this._theme = theme$$1;
25218
25219        /**
25220         * @type {Array.<module:echarts/view/Chart>}
25221         * @private
25222         */
25223        this._chartsViews = [];
25224
25225        /**
25226         * @type {Object.<string, module:echarts/view/Chart>}
25227         * @private
25228         */
25229        this._chartsMap = {};
25230
25231        /**
25232         * @type {Array.<module:echarts/view/Component>}
25233         * @private
25234         */
25235        this._componentsViews = [];
25236
25237        /**
25238         * @type {Object.<string, module:echarts/view/Component>}
25239         * @private
25240         */
25241        this._componentsMap = {};
25242
25243        /**
25244         * @type {module:echarts/CoordinateSystem}
25245         * @private
25246         */
25247        this._coordSysMgr = new CoordinateSystemManager();
25248
25249        /**
25250         * @type {module:echarts/ExtensionAPI}
25251         * @private
25252         */
25253        var api = this._api = createExtensionAPI(this);
25254
25255        // Sort on demand
25256        function prioritySortFunc(a, b) {
25257            return a.__prio - b.__prio;
25258        }
25259        sort(visualFuncs, prioritySortFunc);
25260        sort(dataProcessorFuncs, prioritySortFunc);
25261
25262        /**
25263         * @type {module:echarts/stream/Scheduler}
25264         */
25265        this._scheduler = new Scheduler(this, api, dataProcessorFuncs, visualFuncs);
25266
25267        Eventful.call(this);
25268
25269        /**
25270         * @type {module:echarts~MessageCenter}
25271         * @private
25272         */
25273        this._messageCenter = new MessageCenter();
25274
25275        // Init mouse events
25276        this._initEvents();
25277
25278        // In case some people write `window.onresize = chart.resize`
25279        this.resize = bind(this.resize, this);
25280
25281        // Can't dispatch action during rendering procedure
25282        this._pendingActions = [];
25283
25284        zr.animation.on('frame', this._onframe, this);
25285
25286        bindRenderedEvent(zr, this);
25287
25288        // ECharts instance can be used as value.
25289        setAsPrimitive(this);
25290    }
25291
25292    var echartsProto = ECharts.prototype;
25293
25294    echartsProto._onframe = function () {
25295        if (this._disposed) {
25296            return;
25297        }
25298
25299        var scheduler = this._scheduler;
25300
25301        // Lazy update
25302        if (this[OPTION_UPDATED]) {
25303            var silent = this[OPTION_UPDATED].silent;
25304
25305            this[IN_MAIN_PROCESS] = true;
25306
25307            prepare(this);
25308            updateMethods.update.call(this);
25309
25310            this[IN_MAIN_PROCESS] = false;
25311
25312            this[OPTION_UPDATED] = false;
25313
25314            flushPendingActions.call(this, silent);
25315
25316            triggerUpdatedEvent.call(this, silent);
25317        }
25318        // Avoid do both lazy update and progress in one frame.
25319        else if (scheduler.unfinished) {
25320            // Stream progress.
25321            var remainTime = TEST_FRAME_REMAIN_TIME;
25322            var ecModel = this._model;
25323            var api = this._api;
25324            scheduler.unfinished = false;
25325            do {
25326                var startTime = +new Date();
25327
25328                scheduler.performSeriesTasks(ecModel);
25329
25330                // Currently dataProcessorFuncs do not check threshold.
25331                scheduler.performDataProcessorTasks(ecModel);
25332
25333                updateStreamModes(this, ecModel);
25334
25335                // Do not update coordinate system here. Because that coord system update in
25336                // each frame is not a good user experience. So we follow the rule that
25337                // the extent of the coordinate system is determin in the first frame (the
25338                // frame is executed immedietely after task reset.
25339                // this._coordSysMgr.update(ecModel, api);
25340
25341                // console.log('--- ec frame visual ---', remainTime);
25342                scheduler.performVisualTasks(ecModel);
25343
25344                renderSeries(this, this._model, api, 'remain');
25345
25346                remainTime -= (+new Date() - startTime);
25347            }
25348            while (remainTime > 0 && scheduler.unfinished);
25349
25350            // Call flush explicitly for trigger finished event.
25351            if (!scheduler.unfinished) {
25352                this._zr.flush();
25353            }
25354            // Else, zr flushing be ensue within the same frame,
25355            // because zr flushing is after onframe event.
25356        }
25357    };
25358
25359    /**
25360     * @return {HTMLElement}
25361     */
25362    echartsProto.getDom = function () {
25363        return this._dom;
25364    };
25365
25366    /**
25367     * @return {module:zrender~ZRender}
25368     */
25369    echartsProto.getZr = function () {
25370        return this._zr;
25371    };
25372
25373    /**
25374     * Usage:
25375     * chart.setOption(option, notMerge, lazyUpdate);
25376     * chart.setOption(option, {
25377 *     notMerge: ...,
25378 *     lazyUpdate: ...,
25379 *     silent: ...
25380 * });
25381     *
25382     * @param {Object} option
25383     * @param {Object|boolean} [opts] opts or notMerge.
25384     * @param {boolean} [opts.notMerge=false]
25385     * @param {boolean} [opts.lazyUpdate=false] Useful when setOption frequently.
25386     */
25387    echartsProto.setOption = function (option, notMerge, lazyUpdate) {
25388        if (__DEV__) {
25389            assert(!this[IN_MAIN_PROCESS], '`setOption` should not be called during main process.');
25390        }
25391
25392        var silent;
25393        if (isObject(notMerge)) {
25394            lazyUpdate = notMerge.lazyUpdate;
25395            silent = notMerge.silent;
25396            notMerge = notMerge.notMerge;
25397        }
25398
25399        this[IN_MAIN_PROCESS] = true;
25400
25401        if (!this._model || notMerge) {
25402            var optionManager = new OptionManager(this._api);
25403            var theme$$1 = this._theme;
25404            var ecModel = this._model = new GlobalModel(null, null, theme$$1, optionManager);
25405            ecModel.scheduler = this._scheduler;
25406            ecModel.init(null, null, theme$$1, optionManager);
25407        }
25408
25409        this._model.setOption(option, optionPreprocessorFuncs);
25410
25411        if (lazyUpdate) {
25412            this[OPTION_UPDATED] = {silent: silent};
25413            this[IN_MAIN_PROCESS] = false;
25414        }
25415        else {
25416            prepare(this);
25417
25418            updateMethods.update.call(this);
25419
25420            // Ensure zr refresh sychronously, and then pixel in canvas can be
25421            // fetched after `setOption`.
25422            this._zr.flush();
25423
25424            this[OPTION_UPDATED] = false;
25425            this[IN_MAIN_PROCESS] = false;
25426
25427            flushPendingActions.call(this, silent);
25428            triggerUpdatedEvent.call(this, silent);
25429        }
25430    };
25431
25432    /**
25433     * @DEPRECATED
25434     */
25435    echartsProto.setTheme = function () {
25436        console.log('ECharts#setTheme() is DEPRECATED in ECharts 3.0');
25437    };
25438
25439    /**
25440     * @return {module:echarts/model/Global}
25441     */
25442    echartsProto.getModel = function () {
25443        return this._model;
25444    };
25445
25446    /**
25447     * @return {Object}
25448     */
25449    echartsProto.getOption = function () {
25450        return this._model && this._model.getOption();
25451    };
25452
25453    /**
25454     * @return {number}
25455     */
25456    echartsProto.getWidth = function () {
25457        return this._zr.getWidth();
25458    };
25459
25460    /**
25461     * @return {number}
25462     */
25463    echartsProto.getHeight = function () {
25464        return this._zr.getHeight();
25465    };
25466
25467    /**
25468     * @return {number}
25469     */
25470    echartsProto.getDevicePixelRatio = function () {
25471        return this._zr.painter.dpr || window.devicePixelRatio || 1;
25472    };
25473
25474    /**
25475     * Get canvas which has all thing rendered
25476     * @param {Object} opts
25477     * @param {string} [opts.backgroundColor]
25478     * @return {string}
25479     */
25480    echartsProto.getRenderedCanvas = function (opts) {
25481        if (!env$1.canvasSupported) {
25482            return;
25483        }
25484        opts = opts || {};
25485        opts.pixelRatio = opts.pixelRatio || 1;
25486        opts.backgroundColor = opts.backgroundColor
25487            || this._model.get('backgroundColor');
25488        var zr = this._zr;
25489        // var list = zr.storage.getDisplayList();
25490        // Stop animations
25491        // Never works before in init animation, so remove it.
25492        // zrUtil.each(list, function (el) {
25493        //     el.stopAnimation(true);
25494        // });
25495        return zr.painter.getRenderedCanvas(opts);
25496    };
25497
25498    /**
25499     * Get svg data url
25500     * @return {string}
25501     */
25502    echartsProto.getSvgDataUrl = function () {
25503        if (!env$1.svgSupported) {
25504            return;
25505        }
25506
25507        var zr = this._zr;
25508        var list = zr.storage.getDisplayList();
25509        // Stop animations
25510        each$1(list, function (el) {
25511            el.stopAnimation(true);
25512        });
25513
25514        return zr.painter.pathToDataUrl();
25515    };
25516
25517    /**
25518     * @return {string}
25519     * @param {Object} opts
25520     * @param {string} [opts.type='png']
25521     * @param {string} [opts.pixelRatio=1]
25522     * @param {string} [opts.backgroundColor]
25523     * @param {string} [opts.excludeComponents]
25524     */
25525    echartsProto.getDataURL = function (opts) {
25526        opts = opts || {};
25527        var excludeComponents = opts.excludeComponents;
25528        var ecModel = this._model;
25529        var excludesComponentViews = [];
25530        var self = this;
25531
25532        each(excludeComponents, function (componentType) {
25533            ecModel.eachComponent({
25534                mainType: componentType
25535            }, function (component) {
25536                var view = self._componentsMap[component.__viewId];
25537                if (!view.group.ignore) {
25538                    excludesComponentViews.push(view);
25539                    view.group.ignore = true;
25540                }
25541            });
25542        });
25543
25544        var url = this._zr.painter.getType() === 'svg'
25545            ? this.getSvgDataUrl()
25546            : this.getRenderedCanvas(opts).toDataURL(
25547                'image/' + (opts && opts.type || 'png')
25548            );
25549
25550        each(excludesComponentViews, function (view) {
25551            view.group.ignore = false;
25552        });
25553
25554        return url;
25555    };
25556
25557
25558    /**
25559     * @return {string}
25560     * @param {Object} opts
25561     * @param {string} [opts.type='png']
25562     * @param {string} [opts.pixelRatio=1]
25563     * @param {string} [opts.backgroundColor]
25564     */
25565    echartsProto.getConnectedDataURL = function (opts) {
25566        if (!env$1.canvasSupported) {
25567            return;
25568        }
25569        var groupId = this.group;
25570        var mathMin = Math.min;
25571        var mathMax = Math.max;
25572        var MAX_NUMBER = Infinity;
25573        if (connectedGroups[groupId]) {
25574            var left = MAX_NUMBER;
25575            var top = MAX_NUMBER;
25576            var right = -MAX_NUMBER;
25577            var bottom = -MAX_NUMBER;
25578            var canvasList = [];
25579            var dpr = (opts && opts.pixelRatio) || 1;
25580
25581            each$1(instances, function (chart, id) {
25582                if (chart.group === groupId) {
25583                    var canvas = chart.getRenderedCanvas(
25584                        clone(opts)
25585                    );
25586                    var boundingRect = chart.getDom().getBoundingClientRect();
25587                    left = mathMin(boundingRect.left, left);
25588                    top = mathMin(boundingRect.top, top);
25589                    right = mathMax(boundingRect.right, right);
25590                    bottom = mathMax(boundingRect.bottom, bottom);
25591                    canvasList.push({
25592                        dom: canvas,
25593                        left: boundingRect.left,
25594                        top: boundingRect.top
25595                    });
25596                }
25597            });
25598
25599            left *= dpr;
25600            top *= dpr;
25601            right *= dpr;
25602            bottom *= dpr;
25603            var width = right - left;
25604            var height = bottom - top;
25605            var targetCanvas = createCanvas();
25606            targetCanvas.width = width;
25607            targetCanvas.height = height;
25608            var zr = init$1(targetCanvas);
25609
25610            each(canvasList, function (item) {
25611                var img = new ZImage({
25612                    style: {
25613                        x: item.left * dpr - left,
25614                        y: item.top * dpr - top,
25615                        image: item.dom
25616                    }
25617                });
25618                zr.add(img);
25619            });
25620            zr.refreshImmediately();
25621
25622            return targetCanvas.toDataURL('image/' + (opts && opts.type || 'png'));
25623        }
25624        else {
25625            return this.getDataURL(opts);
25626        }
25627    };
25628
25629    /**
25630     * Convert from logical coordinate system to pixel coordinate system.
25631     * See CoordinateSystem#convertToPixel.
25632     * @param {string|Object} finder
25633     *        If string, e.g., 'geo', means {geoIndex: 0}.
25634     *        If Object, could contain some of these properties below:
25635     *        {
25636 *            seriesIndex / seriesId / seriesName,
25637 *            geoIndex / geoId, geoName,
25638 *            bmapIndex / bmapId / bmapName,
25639 *            xAxisIndex / xAxisId / xAxisName,
25640 *            yAxisIndex / yAxisId / yAxisName,
25641 *            gridIndex / gridId / gridName,
25642 *            ... (can be extended)
25643 *        }
25644     * @param {Array|number} value
25645     * @return {Array|number} result
25646     */
25647    echartsProto.convertToPixel = curry(doConvertPixel, 'convertToPixel');
25648
25649    /**
25650     * Convert from pixel coordinate system to logical coordinate system.
25651     * See CoordinateSystem#convertFromPixel.
25652     * @param {string|Object} finder
25653     *        If string, e.g., 'geo', means {geoIndex: 0}.
25654     *        If Object, could contain some of these properties below:
25655     *        {
25656 *            seriesIndex / seriesId / seriesName,
25657 *            geoIndex / geoId / geoName,
25658 *            bmapIndex / bmapId / bmapName,
25659 *            xAxisIndex / xAxisId / xAxisName,
25660 *            yAxisIndex / yAxisId / yAxisName
25661 *            gridIndex / gridId / gridName,
25662 *            ... (can be extended)
25663 *        }
25664     * @param {Array|number} value
25665     * @return {Array|number} result
25666     */
25667    echartsProto.convertFromPixel = curry(doConvertPixel, 'convertFromPixel');
25668
25669    function doConvertPixel(methodName, finder, value) {
25670        var ecModel = this._model;
25671        var coordSysList = this._coordSysMgr.getCoordinateSystems();
25672        var result;
25673
25674        finder = parseFinder(ecModel, finder);
25675
25676        for (var i = 0; i < coordSysList.length; i++) {
25677            var coordSys = coordSysList[i];
25678            if (coordSys[methodName]
25679                && (result = coordSys[methodName](ecModel, finder, value)) != null
25680            ) {
25681                return result;
25682            }
25683        }
25684
25685        if (__DEV__) {
25686            console.warn(
25687                'No coordinate system that supports ' + methodName + ' found by the given finder.'
25688            );
25689        }
25690    }
25691
25692    /**
25693     * Is the specified coordinate systems or components contain the given pixel point.
25694     * @param {string|Object} finder
25695     *        If string, e.g., 'geo', means {geoIndex: 0}.
25696     *        If Object, could contain some of these properties below:
25697     *        {
25698 *            seriesIndex / seriesId / seriesName,
25699 *            geoIndex / geoId / geoName,
25700 *            bmapIndex / bmapId / bmapName,
25701 *            xAxisIndex / xAxisId / xAxisName,
25702 *            yAxisIndex / yAxisId / yAxisName,
25703 *            gridIndex / gridId / gridName,
25704 *            ... (can be extended)
25705 *        }
25706     * @param {Array|number} value
25707     * @return {boolean} result
25708     */
25709    echartsProto.containPixel = function (finder, value) {
25710        var ecModel = this._model;
25711        var result;
25712
25713        finder = parseFinder(ecModel, finder);
25714
25715        each$1(finder, function (models, key) {
25716            key.indexOf('Models') >= 0 && each$1(models, function (model) {
25717                var coordSys = model.coordinateSystem;
25718                if (coordSys && coordSys.containPoint) {
25719                    result |= !!coordSys.containPoint(value);
25720                }
25721                else if (key === 'seriesModels') {
25722                    var view = this._chartsMap[model.__viewId];
25723                    if (view && view.containPoint) {
25724                        result |= view.containPoint(value, model);
25725                    }
25726                    else {
25727                        if (__DEV__) {
25728                            console.warn(key + ': ' + (view
25729                                    ? 'The found component do not support containPoint.'
25730                                    : 'No view mapping to the found component.'
25731                            ));
25732                        }
25733                    }
25734                }
25735                else {
25736                    if (__DEV__) {
25737                        console.warn(key + ': containPoint is not supported');
25738                    }
25739                }
25740            }, this);
25741        }, this);
25742
25743        return !!result;
25744    };
25745
25746    /**
25747     * Get visual from series or data.
25748     * @param {string|Object} finder
25749     *        If string, e.g., 'series', means {seriesIndex: 0}.
25750     *        If Object, could contain some of these properties below:
25751     *        {
25752 *            seriesIndex / seriesId / seriesName,
25753 *            dataIndex / dataIndexInside
25754 *        }
25755     *        If dataIndex is not specified, series visual will be fetched,
25756     *        but not data item visual.
25757     *        If all of seriesIndex, seriesId, seriesName are not specified,
25758     *        visual will be fetched from first series.
25759     * @param {string} visualType 'color', 'symbol', 'symbolSize'
25760     */
25761    echartsProto.getVisual = function (finder, visualType) {
25762        var ecModel = this._model;
25763
25764        finder = parseFinder(ecModel, finder, {defaultMainType: 'series'});
25765
25766        var seriesModel = finder.seriesModel;
25767
25768        if (__DEV__) {
25769            if (!seriesModel) {
25770                console.warn('There is no specified seires model');
25771            }
25772        }
25773
25774        var data = seriesModel.getData();
25775
25776        var dataIndexInside = finder.hasOwnProperty('dataIndexInside')
25777            ? finder.dataIndexInside
25778            : finder.hasOwnProperty('dataIndex')
25779                ? data.indexOfRawIndex(finder.dataIndex)
25780                : null;
25781
25782        return dataIndexInside != null
25783            ? data.getItemVisual(dataIndexInside, visualType)
25784            : data.getVisual(visualType);
25785    };
25786
25787    /**
25788     * Get view of corresponding component model
25789     * @param  {module:echarts/model/Component} componentModel
25790     * @return {module:echarts/view/Component}
25791     */
25792    echartsProto.getViewOfComponentModel = function (componentModel) {
25793        return this._componentsMap[componentModel.__viewId];
25794    };
25795
25796    /**
25797     * Get view of corresponding series model
25798     * @param  {module:echarts/model/Series} seriesModel
25799     * @return {module:echarts/view/Chart}
25800     */
25801    echartsProto.getViewOfSeriesModel = function (seriesModel) {
25802        return this._chartsMap[seriesModel.__viewId];
25803    };
25804
25805    var updateMethods = {
25806
25807        prepareAndUpdate: function (payload) {
25808            prepare(this);
25809            updateMethods.update.call(this, payload);
25810        },
25811
25812        /**
25813         * @param {Object} payload
25814         * @private
25815         */
25816        update: function (payload) {
25817            // console.profile && console.profile('update');
25818
25819            var ecModel = this._model;
25820            var api = this._api;
25821            var zr = this._zr;
25822            var coordSysMgr = this._coordSysMgr;
25823            var scheduler = this._scheduler;
25824
25825            // update before setOption
25826            if (!ecModel) {
25827                return;
25828            }
25829
25830            scheduler.restoreData(ecModel, payload);
25831
25832            scheduler.performSeriesTasks(ecModel);
25833
25834            // TODO
25835            // Save total ecModel here for undo/redo (after restoring data and before processing data).
25836            // Undo (restoration of total ecModel) can be carried out in 'action' or outside API call.
25837
25838            // Create new coordinate system each update
25839            // In LineView may save the old coordinate system and use it to get the orignal point
25840            coordSysMgr.create(ecModel, api);
25841
25842            scheduler.performDataProcessorTasks(ecModel, payload);
25843
25844            // Current stream render is not supported in data process. So we can update
25845            // stream modes after data processing, where the filtered data is used to
25846            // deteming whether use progressive rendering.
25847            updateStreamModes(this, ecModel);
25848
25849            // We update stream modes before coordinate system updated, then the modes info
25850            // can be fetched when coord sys updating (consider the barGrid extent fix). But
25851            // the drawback is the full coord info can not be fetched. Fortunately this full
25852            // coord is not requied in stream mode updater currently.
25853            coordSysMgr.update(ecModel, api);
25854
25855            clearColorPalette(ecModel);
25856            scheduler.performVisualTasks(ecModel, payload);
25857
25858            render(this, ecModel, api, payload);
25859
25860            // Set background
25861            var backgroundColor = ecModel.get('backgroundColor') || 'transparent';
25862
25863            // In IE8
25864            if (!env$1.canvasSupported) {
25865                var colorArr = parse(backgroundColor);
25866                backgroundColor = stringify(colorArr, 'rgb');
25867                if (colorArr[3] === 0) {
25868                    backgroundColor = 'transparent';
25869                }
25870            }
25871            else {
25872                zr.setBackgroundColor(backgroundColor);
25873            }
25874
25875            performPostUpdateFuncs(ecModel, api);
25876
25877            // console.profile && console.profileEnd('update');
25878        },
25879
25880        /**
25881         * @param {Object} payload
25882         * @private
25883         */
25884        updateTransform: function (payload) {
25885            var ecModel = this._model;
25886            var ecIns = this;
25887            var api = this._api;
25888
25889            // update before setOption
25890            if (!ecModel) {
25891                return;
25892            }
25893
25894            // ChartView.markUpdateMethod(payload, 'updateTransform');
25895
25896            var componentDirtyList = [];
25897            ecModel.eachComponent(function (componentType, componentModel) {
25898                var componentView = ecIns.getViewOfComponentModel(componentModel);
25899                if (componentView && componentView.__alive) {
25900                    if (componentView.updateTransform) {
25901                        var result = componentView.updateTransform(componentModel, ecModel, api, payload);
25902                        result && result.update && componentDirtyList.push(componentView);
25903                    }
25904                    else {
25905                        componentDirtyList.push(componentView);
25906                    }
25907                }
25908            });
25909
25910            var seriesDirtyMap = createHashMap();
25911            ecModel.eachSeries(function (seriesModel) {
25912                var chartView = ecIns._chartsMap[seriesModel.__viewId];
25913                if (chartView.updateTransform) {
25914                    var result = chartView.updateTransform(seriesModel, ecModel, api, payload);
25915                    result && result.update && seriesDirtyMap.set(seriesModel.uid, 1);
25916                }
25917                else {
25918                    seriesDirtyMap.set(seriesModel.uid, 1);
25919                }
25920            });
25921
25922            clearColorPalette(ecModel);
25923            // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline.
25924            // this._scheduler.performVisualTasks(ecModel, payload, 'layout', true);
25925            this._scheduler.performVisualTasks(
25926                ecModel, payload, {setDirty: true, dirtyMap: seriesDirtyMap}
25927            );
25928
25929            // Currently, not call render of components. Geo render cost a lot.
25930            // renderComponents(ecIns, ecModel, api, payload, componentDirtyList);
25931            renderSeries(ecIns, ecModel, api, payload, seriesDirtyMap);
25932
25933            performPostUpdateFuncs(ecModel, this._api);
25934        },
25935
25936        /**
25937         * @param {Object} payload
25938         * @private
25939         */
25940        updateView: function (payload) {
25941            var ecModel = this._model;
25942
25943            // update before setOption
25944            if (!ecModel) {
25945                return;
25946            }
25947
25948            Chart.markUpdateMethod(payload, 'updateView');
25949
25950            clearColorPalette(ecModel);
25951
25952            // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline.
25953            this._scheduler.performVisualTasks(ecModel, payload, {setDirty: true});
25954
25955            render(this, this._model, this._api, payload);
25956
25957            performPostUpdateFuncs(ecModel, this._api);
25958        },
25959
25960        /**
25961         * @param {Object} payload
25962         * @private
25963         */
25964        updateVisual: function (payload) {
25965            updateMethods.update.call(this, payload);
25966
25967            // var ecModel = this._model;
25968
25969            // // update before setOption
25970            // if (!ecModel) {
25971            //     return;
25972            // }
25973
25974            // ChartView.markUpdateMethod(payload, 'updateVisual');
25975
25976            // clearColorPalette(ecModel);
25977
25978            // // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline.
25979            // this._scheduler.performVisualTasks(ecModel, payload, {visualType: 'visual', setDirty: true});
25980
25981            // render(this, this._model, this._api, payload);
25982
25983            // performPostUpdateFuncs(ecModel, this._api);
25984        },
25985
25986        /**
25987         * @param {Object} payload
25988         * @private
25989         */
25990        updateLayout: function (payload) {
25991            updateMethods.update.call(this, payload);
25992
25993            // var ecModel = this._model;
25994
25995            // // update before setOption
25996            // if (!ecModel) {
25997            //     return;
25998            // }
25999
26000            // ChartView.markUpdateMethod(payload, 'updateLayout');
26001
26002            // // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline.
26003            // // this._scheduler.performVisualTasks(ecModel, payload, 'layout', true);
26004            // this._scheduler.performVisualTasks(ecModel, payload, {setDirty: true});
26005
26006            // render(this, this._model, this._api, payload);
26007
26008            // performPostUpdateFuncs(ecModel, this._api);
26009        }
26010    };
26011
26012    function prepare(ecIns) {
26013        var ecModel = ecIns._model;
26014        var scheduler = ecIns._scheduler;
26015
26016        scheduler.restorePipelines(ecModel);
26017
26018        scheduler.prepareStageTasks();
26019
26020        prepareView(ecIns, 'component', ecModel, scheduler);
26021
26022        prepareView(ecIns, 'chart', ecModel, scheduler);
26023
26024        scheduler.plan();
26025    }
26026
26027    /**
26028     * @private
26029     */
26030    function updateDirectly(ecIns, method, payload, mainType, subType) {
26031        var ecModel = ecIns._model;
26032
26033        // broadcast
26034        if (!mainType) {
26035            // FIXME
26036            // Chart will not be update directly here, except set dirty.
26037            // But there is no such scenario now.
26038            each(ecIns._componentsViews.concat(ecIns._chartsViews), callView);
26039            return;
26040        }
26041
26042        var query = {};
26043        query[mainType + 'Id'] = payload[mainType + 'Id'];
26044        query[mainType + 'Index'] = payload[mainType + 'Index'];
26045        query[mainType + 'Name'] = payload[mainType + 'Name'];
26046
26047        var condition = {mainType: mainType, query: query};
26048        subType && (condition.subType = subType); // subType may be '' by parseClassType;
26049
26050        var excludeSeriesId = payload.excludeSeriesId;
26051        if (excludeSeriesId != null) {
26052            excludeSeriesId = createHashMap(normalizeToArray(excludeSeriesId));
26053        }
26054
26055        // If dispatchAction before setOption, do nothing.
26056        ecModel && ecModel.eachComponent(condition, function (model) {
26057            if (!excludeSeriesId || excludeSeriesId.get(model.id) == null) {
26058                callView(ecIns[
26059                    mainType === 'series' ? '_chartsMap' : '_componentsMap'
26060                    ][model.__viewId]);
26061            }
26062        }, ecIns);
26063
26064        function callView(view) {
26065            view && view.__alive && view[method] && view[method](
26066                view.__model, ecModel, ecIns._api, payload
26067            );
26068        }
26069    }
26070
26071    /**
26072     * Resize the chart
26073     * @param {Object} opts
26074     * @param {number} [opts.width] Can be 'auto' (the same as null/undefined)
26075     * @param {number} [opts.height] Can be 'auto' (the same as null/undefined)
26076     * @param {boolean} [opts.silent=false]
26077     */
26078    echartsProto.resize = function (opts) {
26079        if (__DEV__) {
26080            assert(!this[IN_MAIN_PROCESS], '`resize` should not be called during main process.');
26081        }
26082
26083        this._zr.resize(opts);
26084
26085        var ecModel = this._model;
26086
26087        // Resize loading effect
26088        this._loadingFX && this._loadingFX.resize();
26089
26090        if (!ecModel) {
26091            return;
26092        }
26093
26094        var optionChanged = ecModel.resetOption('media');
26095
26096        var silent = opts && opts.silent;
26097
26098        this[IN_MAIN_PROCESS] = true;
26099
26100        optionChanged && prepare(this);
26101        updateMethods.update.call(this);
26102
26103        this[IN_MAIN_PROCESS] = false;
26104
26105        flushPendingActions.call(this, silent);
26106
26107        triggerUpdatedEvent.call(this, silent);
26108    };
26109
26110    function updateStreamModes(ecIns, ecModel) {
26111        var chartsMap = ecIns._chartsMap;
26112        var scheduler = ecIns._scheduler;
26113        ecModel.eachSeries(function (seriesModel) {
26114            scheduler.updateStreamModes(seriesModel, chartsMap[seriesModel.__viewId]);
26115        });
26116    }
26117
26118    /**
26119     * Show loading effect
26120     * @param  {string} [name='default']
26121     * @param  {Object} [cfg]
26122     */
26123    echartsProto.showLoading = function (name, cfg) {
26124        if (isObject(name)) {
26125            cfg = name;
26126            name = '';
26127        }
26128        name = name || 'default';
26129
26130        this.hideLoading();
26131        if (!loadingEffects[name]) {
26132            if (__DEV__) {
26133                console.warn('Loading effects ' + name + ' not exists.');
26134            }
26135            return;
26136        }
26137        var el = loadingEffects[name](this._api, cfg);
26138        var zr = this._zr;
26139        this._loadingFX = el;
26140
26141        zr.add(el);
26142    };
26143
26144    /**
26145     * Hide loading effect
26146     */
26147    echartsProto.hideLoading = function () {
26148        this._loadingFX && this._zr.remove(this._loadingFX);
26149        this._loadingFX = null;
26150    };
26151
26152    /**
26153     * @param {Object} eventObj
26154     * @return {Object}
26155     */
26156    echartsProto.makeActionFromEvent = function (eventObj) {
26157        var payload = extend({}, eventObj);
26158        payload.type = eventActionMap[eventObj.type];
26159        return payload;
26160    };
26161
26162    /**
26163     * @pubilc
26164     * @param {Object} payload
26165     * @param {string} [payload.type] Action type
26166     * @param {Object|boolean} [opt] If pass boolean, means opt.silent
26167     * @param {boolean} [opt.silent=false] Whether trigger events.
26168     * @param {boolean} [opt.flush=undefined]
26169     *                  true: Flush immediately, and then pixel in canvas can be fetched
26170     *                      immediately. Caution: it might affect performance.
26171     *                  false: Not not flush.
26172     *                  undefined: Auto decide whether perform flush.
26173     */
26174    echartsProto.dispatchAction = function (payload, opt) {
26175        if (!isObject(opt)) {
26176            opt = {silent: !!opt};
26177        }
26178
26179        if (!actions[payload.type]) {
26180            return;
26181        }
26182
26183        // Avoid dispatch action before setOption. Especially in `connect`.
26184        if (!this._model) {
26185            return;
26186        }
26187
26188        // May dispatchAction in rendering procedure
26189        if (this[IN_MAIN_PROCESS]) {
26190            this._pendingActions.push(payload);
26191            return;
26192        }
26193
26194        doDispatchAction.call(this, payload, opt.silent);
26195
26196        if (opt.flush) {
26197            this._zr.flush(true);
26198        }
26199        else if (opt.flush !== false && env$1.browser.weChat) {
26200            // In WeChat embeded browser, `requestAnimationFrame` and `setInterval`
26201            // hang when sliding page (on touch event), which cause that zr does not
26202            // refresh util user interaction finished, which is not expected.
26203            // But `dispatchAction` may be called too frequently when pan on touch
26204            // screen, which impacts performance if do not throttle them.
26205            this._throttledZrFlush();
26206        }
26207
26208        flushPendingActions.call(this, opt.silent);
26209
26210        triggerUpdatedEvent.call(this, opt.silent);
26211    };
26212
26213    function doDispatchAction(payload, silent) {
26214        var payloadType = payload.type;
26215        var escapeConnect = payload.escapeConnect;
26216        var actionWrap = actions[payloadType];
26217        var actionInfo = actionWrap.actionInfo;
26218
26219        var cptType = (actionInfo.update || 'update').split(':');
26220        var updateMethod = cptType.pop();
26221        cptType = cptType[0] != null && parseClassType(cptType[0]);
26222
26223        this[IN_MAIN_PROCESS] = true;
26224
26225        var payloads = [payload];
26226        var batched = false;
26227        // Batch action
26228        if (payload.batch) {
26229            batched = true;
26230            payloads = map(payload.batch, function (item) {
26231                item = defaults(extend({}, item), payload);
26232                item.batch = null;
26233                return item;
26234            });
26235        }
26236
26237        var eventObjBatch = [];
26238        var eventObj;
26239        var isHighDown = payloadType === 'highlight' || payloadType === 'downplay';
26240
26241        each(payloads, function (batchItem) {
26242            // Action can specify the event by return it.
26243            eventObj = actionWrap.action(batchItem, this._model, this._api);
26244            // Emit event outside
26245            eventObj = eventObj || extend({}, batchItem);
26246            // Convert type to eventType
26247            eventObj.type = actionInfo.event || eventObj.type;
26248            eventObjBatch.push(eventObj);
26249
26250            // light update does not perform data process, layout and visual.
26251            if (isHighDown) {
26252                // method, payload, mainType, subType
26253                updateDirectly(this, updateMethod, batchItem, 'series');
26254            }
26255            else if (cptType) {
26256                updateDirectly(this, updateMethod, batchItem, cptType.main, cptType.sub);
26257            }
26258        }, this);
26259
26260        if (updateMethod !== 'none' && !isHighDown && !cptType) {
26261            // Still dirty
26262            if (this[OPTION_UPDATED]) {
26263                // FIXME Pass payload ?
26264                prepare(this);
26265                updateMethods.update.call(this, payload);
26266                this[OPTION_UPDATED] = false;
26267            }
26268            else {
26269                updateMethods[updateMethod].call(this, payload);
26270            }
26271        }
26272
26273        // Follow the rule of action batch
26274        if (batched) {
26275            eventObj = {
26276                type: actionInfo.event || payloadType,
26277                escapeConnect: escapeConnect,
26278                batch: eventObjBatch
26279            };
26280        }
26281        else {
26282            eventObj = eventObjBatch[0];
26283        }
26284
26285        this[IN_MAIN_PROCESS] = false;
26286
26287        !silent && this._messageCenter.trigger(eventObj.type, eventObj);
26288    }
26289
26290    function flushPendingActions(silent) {
26291        var pendingActions = this._pendingActions;
26292        while (pendingActions.length) {
26293            var payload = pendingActions.shift();
26294            doDispatchAction.call(this, payload, silent);
26295        }
26296    }
26297
26298    function triggerUpdatedEvent(silent) {
26299        !silent && this.trigger('updated');
26300    }
26301
26302    /**
26303     * Event `rendered` is triggered when zr
26304     * rendered. It is useful for realtime
26305     * snapshot (reflect animation).
26306     *
26307     * Event `finished` is triggered when:
26308     * (1) zrender rendering finished.
26309     * (2) initial animation finished.
26310     * (3) progressive rendering finished.
26311     * (4) no pending action.
26312     * (5) no delayed setOption needs to be processed.
26313     */
26314    function bindRenderedEvent(zr, ecIns) {
26315        zr.on('rendered', function () {
26316
26317            ecIns.trigger('rendered');
26318
26319            // The `finished` event should not be triggered repeatly,
26320            // so it should only be triggered when rendering indeed happend
26321            // in zrender. (Consider the case that dipatchAction is keep
26322            // triggering when mouse move).
26323            if (
26324                // Although zr is dirty if initial animation is not finished
26325            // and this checking is called on frame, we also check
26326            // animation finished for robustness.
26327            zr.animation.isFinished()
26328            && !ecIns[OPTION_UPDATED]
26329            && !ecIns._scheduler.unfinished
26330            && !ecIns._pendingActions.length
26331            ) {
26332                ecIns.trigger('finished');
26333            }
26334        });
26335    }
26336
26337    /**
26338     * @param {Object} params
26339     * @param {number} params.seriesIndex
26340     * @param {Array|TypedArray} params.data
26341     */
26342    echartsProto.appendData = function (params) {
26343        var seriesIndex = params.seriesIndex;
26344        var ecModel = this.getModel();
26345        var seriesModel = ecModel.getSeriesByIndex(seriesIndex);
26346
26347        if (__DEV__) {
26348            assert(params.data && seriesModel);
26349        }
26350
26351        seriesModel.appendData(params);
26352
26353        // Note: `appendData` does not support that update extent of coordinate
26354        // system, util some scenario require that. In the expected usage of
26355        // `appendData`, the initial extent of coordinate system should better
26356        // be fixed by axis `min`/`max` setting or initial data, otherwise if
26357        // the extent changed while `appendData`, the location of the painted
26358        // graphic elements have to be changed, which make the usage of
26359        // `appendData` meaningless.
26360
26361        this._scheduler.unfinished = true;
26362    };
26363
26364    /**
26365     * Register event
26366     * @method
26367     */
26368    echartsProto.on = createRegisterEventWithLowercaseName('on');
26369    echartsProto.off = createRegisterEventWithLowercaseName('off');
26370    echartsProto.one = createRegisterEventWithLowercaseName('one');
26371
26372    /**
26373     * Prepare view instances of charts and components
26374     * @param  {module:echarts/model/Global} ecModel
26375     * @private
26376     */
26377    function prepareView(ecIns, type, ecModel, scheduler) {
26378        var isComponent = type === 'component';
26379        var viewList = isComponent ? ecIns._componentsViews : ecIns._chartsViews;
26380        var viewMap = isComponent ? ecIns._componentsMap : ecIns._chartsMap;
26381        var zr = ecIns._zr;
26382        var api = ecIns._api;
26383
26384        for (var i = 0; i < viewList.length; i++) {
26385            viewList[i].__alive = false;
26386        }
26387
26388        isComponent
26389            ? ecModel.eachComponent(function (componentType, model) {
26390                componentType !== 'series' && doPrepare(model);
26391            })
26392            : ecModel.eachSeries(doPrepare);
26393
26394        function doPrepare(model) {
26395            // Consider: id same and type changed.
26396            var viewId = '_ec_' + model.id + '_' + model.type;
26397            var view = viewMap[viewId];
26398            if (!view) {
26399                var classType = parseClassType(model.type);
26400                var Clazz = isComponent
26401                    ? Component.getClass(classType.main, classType.sub)
26402                    : Chart.getClass(classType.sub);
26403
26404                if (__DEV__) {
26405                    assert(Clazz, classType.sub + ' does not exist.');
26406                }
26407
26408                view = new Clazz();
26409                view.init(ecModel, api);
26410                viewMap[viewId] = view;
26411                viewList.push(view);
26412                zr.add(view.group);
26413            }
26414
26415            model.__viewId = view.__id = viewId;
26416            view.__alive = true;
26417            view.__model = model;
26418            view.group.__ecComponentInfo = {
26419                mainType: model.mainType,
26420                index: model.componentIndex
26421            };
26422            !isComponent && scheduler.prepareView(view, model, ecModel, api);
26423        }
26424
26425        for (var i = 0; i < viewList.length;) {
26426            var view = viewList[i];
26427            if (!view.__alive) {
26428                !isComponent && view.renderTask.dispose();
26429                zr.remove(view.group);
26430                view.dispose(ecModel, api);
26431                viewList.splice(i, 1);
26432                delete viewMap[view.__id];
26433                view.__id = view.group.__ecComponentInfo = null;
26434            }
26435            else {
26436                i++;
26437            }
26438        }
26439    }
26440
26441// /**
26442//  * Encode visual infomation from data after data processing
26443//  *
26444//  * @param {module:echarts/model/Global} ecModel
26445//  * @param {object} layout
26446//  * @param {boolean} [layoutFilter] `true`: only layout,
26447//  *                                 `false`: only not layout,
26448//  *                                 `null`/`undefined`: all.
26449//  * @param {string} taskBaseTag
26450//  * @private
26451//  */
26452// function startVisualEncoding(ecIns, ecModel, api, payload, layoutFilter) {
26453//     each(visualFuncs, function (visual, index) {
26454//         var isLayout = visual.isLayout;
26455//         if (layoutFilter == null
26456//             || (layoutFilter === false && !isLayout)
26457//             || (layoutFilter === true && isLayout)
26458//         ) {
26459//             visual.func(ecModel, api, payload);
26460//         }
26461//     });
26462// }
26463
26464    function clearColorPalette(ecModel) {
26465        ecModel.clearColorPalette();
26466        ecModel.eachSeries(function (seriesModel) {
26467            seriesModel.clearColorPalette();
26468        });
26469    }
26470
26471    function render(ecIns, ecModel, api, payload) {
26472
26473        renderComponents(ecIns, ecModel, api, payload);
26474
26475        each(ecIns._chartsViews, function (chart) {
26476            chart.__alive = false;
26477        });
26478
26479        renderSeries(ecIns, ecModel, api, payload);
26480
26481        // Remove groups of unrendered charts
26482        each(ecIns._chartsViews, function (chart) {
26483            if (!chart.__alive) {
26484                chart.remove(ecModel, api);
26485            }
26486        });
26487    }
26488
26489    function renderComponents(ecIns, ecModel, api, payload, dirtyList) {
26490        each(dirtyList || ecIns._componentsViews, function (componentView) {
26491            var componentModel = componentView.__model;
26492            componentView.render(componentModel, ecModel, api, payload);
26493
26494            updateZ(componentModel, componentView);
26495        });
26496    }
26497
26498    /**
26499     * Render each chart and component
26500     * @private
26501     */
26502    function renderSeries(ecIns, ecModel, api, payload, dirtyMap) {
26503        // Render all charts
26504        var scheduler = ecIns._scheduler;
26505        var unfinished;
26506        ecModel.eachSeries(function (seriesModel) {
26507            var chartView = ecIns._chartsMap[seriesModel.__viewId];
26508            chartView.__alive = true;
26509
26510            var renderTask = chartView.renderTask;
26511            scheduler.updatePayload(renderTask, payload);
26512
26513            if (dirtyMap && dirtyMap.get(seriesModel.uid)) {
26514                renderTask.dirty();
26515            }
26516
26517            unfinished |= renderTask.perform(scheduler.getPerformArgs(renderTask));
26518
26519            chartView.group.silent = !!seriesModel.get('silent');
26520
26521            updateZ(seriesModel, chartView);
26522
26523            updateBlend(seriesModel, chartView);
26524        });
26525        scheduler.unfinished |= unfinished;
26526
26527        // If use hover layer
26528        updateHoverLayerStatus(ecIns._zr, ecModel);
26529
26530        // Add aria
26531        aria(ecIns._zr.dom, ecModel);
26532    }
26533
26534    function performPostUpdateFuncs(ecModel, api) {
26535        each(postUpdateFuncs, function (func) {
26536            func(ecModel, api);
26537        });
26538    }
26539
26540
26541    var MOUSE_EVENT_NAMES = [
26542        'click', 'dblclick', 'mouseover', 'mouseout', 'mousemove',
26543        'mousedown', 'mouseup', 'globalout', 'contextmenu'
26544    ];
26545
26546    /**
26547     * @private
26548     */
26549    echartsProto._initEvents = function () {
26550        each(MOUSE_EVENT_NAMES, function (eveName) {
26551            this._zr.on(eveName, function (e) {
26552                var ecModel = this.getModel();
26553                var el = e.target;
26554                var params;
26555
26556                // no e.target when 'globalout'.
26557                if (eveName === 'globalout') {
26558                    params = {};
26559                }
26560                else if (el && el.dataIndex != null) {
26561                    var dataModel = el.dataModel || ecModel.getSeriesByIndex(el.seriesIndex);
26562                    params = dataModel && dataModel.getDataParams(el.dataIndex, el.dataType) || {};
26563                }
26564                // If element has custom eventData of components
26565                else if (el && el.eventData) {
26566                    params = extend({}, el.eventData);
26567                }
26568
26569                if (params) {
26570                    params.event = e;
26571                    params.type = eveName;
26572                    this.trigger(eveName, params);
26573                }
26574
26575            }, this);
26576        }, this);
26577
26578        each(eventActionMap, function (actionType, eventType) {
26579            this._messageCenter.on(eventType, function (event) {
26580                this.trigger(eventType, event);
26581            }, this);
26582        }, this);
26583    };
26584
26585    /**
26586     * @return {boolean}
26587     */
26588    echartsProto.isDisposed = function () {
26589        return this._disposed;
26590    };
26591
26592    /**
26593     * Clear
26594     */
26595    echartsProto.clear = function () {
26596        this.setOption({ series: [] }, true);
26597    };
26598
26599    /**
26600     * Dispose instance
26601     */
26602    echartsProto.dispose = function () {
26603        if (this._disposed) {
26604            if (__DEV__) {
26605                console.warn('Instance ' + this.id + ' has been disposed');
26606            }
26607            return;
26608        }
26609        this._disposed = true;
26610
26611        setAttribute(this.getDom(), DOM_ATTRIBUTE_KEY, '');
26612
26613        var api = this._api;
26614        var ecModel = this._model;
26615
26616        each(this._componentsViews, function (component) {
26617            component.dispose(ecModel, api);
26618        });
26619        each(this._chartsViews, function (chart) {
26620            chart.dispose(ecModel, api);
26621        });
26622
26623        // Dispose after all views disposed
26624        this._zr.dispose();
26625
26626        delete instances[this.id];
26627    };
26628
26629    mixin(ECharts, Eventful);
26630
26631    function updateHoverLayerStatus(zr, ecModel) {
26632        var storage = zr.storage;
26633        var elCount = 0;
26634        storage.traverse(function (el) {
26635            if (!el.isGroup) {
26636                elCount++;
26637            }
26638        });
26639        if (elCount > ecModel.get('hoverLayerThreshold') && !env$1.node) {
26640            storage.traverse(function (el) {
26641                if (!el.isGroup) {
26642                    // Don't switch back.
26643                    el.useHoverLayer = true;
26644                }
26645            });
26646        }
26647    }
26648
26649    /**
26650     * Update chart progressive and blend.
26651     * @param {module:echarts/model/Series|module:echarts/model/Component} model
26652     * @param {module:echarts/view/Component|module:echarts/view/Chart} view
26653     */
26654    function updateBlend(seriesModel, chartView) {
26655        var blendMode = seriesModel.get('blendMode') || null;
26656        if (__DEV__) {
26657            if (!env$1.canvasSupported && blendMode && blendMode !== 'source-over') {
26658                console.warn('Only canvas support blendMode');
26659            }
26660        }
26661        chartView.group.traverse(function (el) {
26662            // FIXME marker and other components
26663            if (!el.isGroup) {
26664                // Only set if blendMode is changed. In case element is incremental and don't wan't to rerender.
26665                if (el.style.blend !== blendMode) {
26666                    el.setStyle('blend', blendMode);
26667                }
26668            }
26669            if (el.eachPendingDisplayable) {
26670                el.eachPendingDisplayable(function (displayable) {
26671                    displayable.setStyle('blend', blendMode);
26672                });
26673            }
26674        });
26675    }
26676
26677    /**
26678     * @param {module:echarts/model/Series|module:echarts/model/Component} model
26679     * @param {module:echarts/view/Component|module:echarts/view/Chart} view
26680     */
26681    function updateZ(model, view) {
26682        var z = model.get('z');
26683        var zlevel = model.get('zlevel');
26684        // Set z and zlevel
26685        view.group.traverse(function (el) {
26686            if (el.type !== 'group') {
26687                z != null && (el.z = z);
26688                zlevel != null && (el.zlevel = zlevel);
26689            }
26690        });
26691    }
26692
26693    function createExtensionAPI(ecInstance) {
26694        var coordSysMgr = ecInstance._coordSysMgr;
26695        return extend(new ExtensionAPI(ecInstance), {
26696            // Inject methods
26697            getCoordinateSystems: bind(
26698                coordSysMgr.getCoordinateSystems, coordSysMgr
26699            ),
26700            getComponentByElement: function (el) {
26701                while (el) {
26702                    var modelInfo = el.__ecComponentInfo;
26703                    if (modelInfo != null) {
26704                        return ecInstance._model.getComponent(modelInfo.mainType, modelInfo.index);
26705                    }
26706                    el = el.parent;
26707                }
26708            }
26709        });
26710    }
26711
26712    /**
26713     * @type {Object} key: actionType.
26714     * @inner
26715     */
26716    var actions = {};
26717
26718    /**
26719     * Map eventType to actionType
26720     * @type {Object}
26721     */
26722    var eventActionMap = {};
26723
26724    /**
26725     * Data processor functions of each stage
26726     * @type {Array.<Object.<string, Function>>}
26727     * @inner
26728     */
26729    var dataProcessorFuncs = [];
26730
26731    /**
26732     * @type {Array.<Function>}
26733     * @inner
26734     */
26735    var optionPreprocessorFuncs = [];
26736
26737    /**
26738     * @type {Array.<Function>}
26739     * @inner
26740     */
26741    var postUpdateFuncs = [];
26742
26743    /**
26744     * Visual encoding functions of each stage
26745     * @type {Array.<Object.<string, Function>>}
26746     */
26747    var visualFuncs = [];
26748
26749    /**
26750     * Theme storage
26751     * @type {Object.<key, Object>}
26752     */
26753    var themeStorage = {};
26754    /**
26755     * Loading effects
26756     */
26757    var loadingEffects = {};
26758
26759    var instances = {};
26760    var connectedGroups = {};
26761
26762    var idBase = new Date() - 0;
26763    var groupIdBase = new Date() - 0;
26764    var DOM_ATTRIBUTE_KEY = '_echarts_instance_';
26765
26766    var mapDataStores = {};
26767
26768    function enableConnect(chart) {
26769        var STATUS_PENDING = 0;
26770        var STATUS_UPDATING = 1;
26771        var STATUS_UPDATED = 2;
26772        var STATUS_KEY = '__connectUpdateStatus';
26773
26774        function updateConnectedChartsStatus(charts, status) {
26775            for (var i = 0; i < charts.length; i++) {
26776                var otherChart = charts[i];
26777                otherChart[STATUS_KEY] = status;
26778            }
26779        }
26780
26781        each(eventActionMap, function (actionType, eventType) {
26782            chart._messageCenter.on(eventType, function (event) {
26783                if (connectedGroups[chart.group] && chart[STATUS_KEY] !== STATUS_PENDING) {
26784                    if (event && event.escapeConnect) {
26785                        return;
26786                    }
26787
26788                    var action = chart.makeActionFromEvent(event);
26789                    var otherCharts = [];
26790
26791                    each(instances, function (otherChart) {
26792                        if (otherChart !== chart && otherChart.group === chart.group) {
26793                            otherCharts.push(otherChart);
26794                        }
26795                    });
26796
26797                    updateConnectedChartsStatus(otherCharts, STATUS_PENDING);
26798                    each(otherCharts, function (otherChart) {
26799                        if (otherChart[STATUS_KEY] !== STATUS_UPDATING) {
26800                            otherChart.dispatchAction(action);
26801                        }
26802                    });
26803                    updateConnectedChartsStatus(otherCharts, STATUS_UPDATED);
26804                }
26805            });
26806        });
26807    }
26808
26809    /**
26810     * @param {HTMLElement} dom
26811     * @param {Object} [theme]
26812     * @param {Object} opts
26813     * @param {number} [opts.devicePixelRatio] Use window.devicePixelRatio by default
26814     * @param {string} [opts.renderer] Currently only 'canvas' is supported.
26815     * @param {number} [opts.width] Use clientWidth of the input `dom` by default.
26816     *                              Can be 'auto' (the same as null/undefined)
26817     * @param {number} [opts.height] Use clientHeight of the input `dom` by default.
26818     *                               Can be 'auto' (the same as null/undefined)
26819     */
26820    function init(dom, theme$$1, opts) {
26821        if (__DEV__) {
26822            // Check version
26823            if ((version$1.replace('.', '') - 0) < (dependencies.zrender.replace('.', '') - 0)) {
26824                throw new Error(
26825                    'zrender/src ' + version$1
26826                    + ' is too old for ECharts ' + version
26827                    + '. Current version need ZRender '
26828                    + dependencies.zrender + '+'
26829                );
26830            }
26831
26832            if (!dom) {
26833                throw new Error('Initialize failed: invalid dom.');
26834            }
26835        }
26836
26837        var existInstance = getInstanceByDom(dom);
26838        if (existInstance) {
26839            if (__DEV__) {
26840                console.warn('There is a chart instance already initialized on the dom.');
26841            }
26842            return existInstance;
26843        }
26844
26845        if (__DEV__) {
26846            if (isDom(dom)
26847                && dom.nodeName.toUpperCase() !== 'CANVAS'
26848                && (
26849                    (!dom.clientWidth && (!opts || opts.width == null))
26850                    || (!dom.clientHeight && (!opts || opts.height == null))
26851                )
26852            ) {
26853                console.warn('Can\'t get dom width or height');
26854            }
26855        }
26856
26857        var chart = new ECharts(dom, theme$$1, opts);
26858        chart.id = 'ec_' + idBase++;
26859        instances[chart.id] = chart;
26860
26861        setAttribute(dom, DOM_ATTRIBUTE_KEY, chart.id);
26862
26863        enableConnect(chart);
26864
26865        return chart;
26866    }
26867
26868    /**
26869     * @return {string|Array.<module:echarts~ECharts>} groupId
26870     */
26871    function connect(groupId) {
26872        // Is array of charts
26873        if (isArray(groupId)) {
26874            var charts = groupId;
26875            groupId = null;
26876            // If any chart has group
26877            each(charts, function (chart) {
26878                if (chart.group != null) {
26879                    groupId = chart.group;
26880                }
26881            });
26882            groupId = groupId || ('g_' + groupIdBase++);
26883            each(charts, function (chart) {
26884                chart.group = groupId;
26885            });
26886        }
26887        connectedGroups[groupId] = true;
26888        return groupId;
26889    }
26890
26891    /**
26892     * @DEPRECATED
26893     * @return {string} groupId
26894     */
26895    function disConnect(groupId) {
26896        connectedGroups[groupId] = false;
26897    }
26898
26899    /**
26900     * @return {string} groupId
26901     */
26902    var disconnect = disConnect;
26903
26904    /**
26905     * Dispose a chart instance
26906     * @param  {module:echarts~ECharts|HTMLDomElement|string} chart
26907     */
26908    function dispose(chart) {
26909        if (typeof chart === 'string') {
26910            chart = instances[chart];
26911        }
26912        else if (!(chart instanceof ECharts)){
26913            // Try to treat as dom
26914            chart = getInstanceByDom(chart);
26915        }
26916        if ((chart instanceof ECharts) && !chart.isDisposed()) {
26917            chart.dispose();
26918        }
26919    }
26920
26921    /**
26922     * @param  {HTMLElement} dom
26923     * @return {echarts~ECharts}
26924     */
26925    function getInstanceByDom(dom) {
26926        return instances[getAttribute(dom, DOM_ATTRIBUTE_KEY)];
26927    }
26928
26929    /**
26930     * @param {string} key
26931     * @return {echarts~ECharts}
26932     */
26933    function getInstanceById(key) {
26934        return instances[key];
26935    }
26936
26937    /**
26938     * Register theme
26939     */
26940    function registerTheme(name, theme$$1) {
26941        themeStorage[name] = theme$$1;
26942    }
26943
26944    /**
26945     * Register option preprocessor
26946     * @param {Function} preprocessorFunc
26947     */
26948    function registerPreprocessor(preprocessorFunc) {
26949        optionPreprocessorFuncs.push(preprocessorFunc);
26950    }
26951
26952    /**
26953     * @param {number} [priority=1000]
26954     * @param {Object|Function} processor
26955     */
26956    function registerProcessor(priority, processor) {
26957        normalizeRegister(dataProcessorFuncs, priority, processor, PRIORITY_PROCESSOR_FILTER);
26958    }
26959
26960    /**
26961     * Register postUpdater
26962     * @param {Function} postUpdateFunc
26963     */
26964    function registerPostUpdate(postUpdateFunc) {
26965        postUpdateFuncs.push(postUpdateFunc);
26966    }
26967
26968    /**
26969     * Usage:
26970     * registerAction('someAction', 'someEvent', function () { ... });
26971     * registerAction('someAction', function () { ... });
26972     * registerAction(
26973     *     {type: 'someAction', event: 'someEvent', update: 'updateView'},
26974     *     function () { ... }
26975     * );
26976     *
26977     * @param {(string|Object)} actionInfo
26978     * @param {string} actionInfo.type
26979     * @param {string} [actionInfo.event]
26980     * @param {string} [actionInfo.update]
26981     * @param {string} [eventName]
26982     * @param {Function} action
26983     */
26984    function registerAction(actionInfo, eventName, action) {
26985        if (typeof eventName === 'function') {
26986            action = eventName;
26987            eventName = '';
26988        }
26989        var actionType = isObject(actionInfo)
26990            ? actionInfo.type
26991            : ([actionInfo, actionInfo = {
26992                event: eventName
26993            }][0]);
26994
26995        // Event name is all lowercase
26996        actionInfo.event = (actionInfo.event || actionType).toLowerCase();
26997        eventName = actionInfo.event;
26998
26999        // Validate action type and event name.
27000        assert(ACTION_REG.test(actionType) && ACTION_REG.test(eventName));
27001
27002        if (!actions[actionType]) {
27003            actions[actionType] = {action: action, actionInfo: actionInfo};
27004        }
27005        eventActionMap[eventName] = actionType;
27006    }
27007
27008    /**
27009     * @param {string} type
27010     * @param {*} CoordinateSystem
27011     */
27012    function registerCoordinateSystem(type, CoordinateSystem$$1) {
27013        CoordinateSystemManager.register(type, CoordinateSystem$$1);
27014    }
27015
27016    /**
27017     * Get dimensions of specified coordinate system.
27018     * @param {string} type
27019     * @return {Array.<string|Object>}
27020     */
27021    function getCoordinateSystemDimensions(type) {
27022        var coordSysCreator = CoordinateSystemManager.get(type);
27023        if (coordSysCreator) {
27024            return coordSysCreator.getDimensionsInfo
27025                ? coordSysCreator.getDimensionsInfo()
27026                : coordSysCreator.dimensions.slice();
27027        }
27028    }
27029
27030    /**
27031     * Layout is a special stage of visual encoding
27032     * Most visual encoding like color are common for different chart
27033     * But each chart has it's own layout algorithm
27034     *
27035     * @param {number} [priority=1000]
27036     * @param {Function} layoutTask
27037     */
27038    function registerLayout(priority, layoutTask) {
27039        normalizeRegister(visualFuncs, priority, layoutTask, PRIORITY_VISUAL_LAYOUT, 'layout');
27040    }
27041
27042    /**
27043     * @param {number} [priority=3000]
27044     * @param {module:echarts/stream/Task} visualTask
27045     */
27046    function registerVisual(priority, visualTask) {
27047        normalizeRegister(visualFuncs, priority, visualTask, PRIORITY_VISUAL_CHART, 'visual');
27048    }
27049
27050    /**
27051     * @param {Object|Function} fn: {seriesType, createOnAllSeries, performRawSeries, reset}
27052     */
27053    function normalizeRegister(targetList, priority, fn, defaultPriority, visualType) {
27054        if (isFunction(priority) || isObject(priority)) {
27055            fn = priority;
27056            priority = defaultPriority;
27057        }
27058
27059        if (__DEV__) {
27060            if (isNaN(priority) || priority == null) {
27061                throw new Error('Illegal priority');
27062            }
27063            // Check duplicate
27064            each(targetList, function (wrap) {
27065                assert(wrap.__raw !== fn);
27066            });
27067        }
27068
27069        var stageHandler = Scheduler.wrapStageHandler(fn, visualType);
27070
27071        stageHandler.__prio = priority;
27072        stageHandler.__raw = fn;
27073        targetList.push(stageHandler);
27074
27075        return stageHandler;
27076    }
27077
27078    /**
27079     * @param {string} name
27080     */
27081    function registerLoading(name, loadingFx) {
27082        loadingEffects[name] = loadingFx;
27083    }
27084
27085    /**
27086     * @param {Object} opts
27087     * @param {string} [superClass]
27088     */
27089    function extendComponentModel(opts/*, superClass*/) {
27090        // var Clazz = ComponentModel;
27091        // if (superClass) {
27092        //     var classType = parseClassType(superClass);
27093        //     Clazz = ComponentModel.getClass(classType.main, classType.sub, true);
27094        // }
27095        return ComponentModel.extend(opts);
27096    }
27097
27098    /**
27099     * @param {Object} opts
27100     * @param {string} [superClass]
27101     */
27102    function extendComponentView(opts/*, superClass*/) {
27103        // var Clazz = ComponentView;
27104        // if (superClass) {
27105        //     var classType = parseClassType(superClass);
27106        //     Clazz = ComponentView.getClass(classType.main, classType.sub, true);
27107        // }
27108        return Component.extend(opts);
27109    }
27110
27111    /**
27112     * @param {Object} opts
27113     * @param {string} [superClass]
27114     */
27115    function extendSeriesModel(opts/*, superClass*/) {
27116        // var Clazz = SeriesModel;
27117        // if (superClass) {
27118        //     superClass = 'series.' + superClass.replace('series.', '');
27119        //     var classType = parseClassType(superClass);
27120        //     Clazz = ComponentModel.getClass(classType.main, classType.sub, true);
27121        // }
27122        return SeriesModel.extend(opts);
27123    }
27124
27125    /**
27126     * @param {Object} opts
27127     * @param {string} [superClass]
27128     */
27129    function extendChartView(opts/*, superClass*/) {
27130        // var Clazz = ChartView;
27131        // if (superClass) {
27132        //     superClass = superClass.replace('series.', '');
27133        //     var classType = parseClassType(superClass);
27134        //     Clazz = ChartView.getClass(classType.main, true);
27135        // }
27136        return Chart.extend(opts);
27137    }
27138
27139    /**
27140     * ZRender need a canvas context to do measureText.
27141     * But in node environment canvas may be created by node-canvas.
27142     * So we need to specify how to create a canvas instead of using document.createElement('canvas')
27143     *
27144     * Be careful of using it in the browser.
27145     *
27146     * @param {Function} creator
27147     * @example
27148     *     var Canvas = require('canvas');
27149     *     var echarts = require('echarts');
27150     *     echarts.setCanvasCreator(function () {
27151 *         // Small size is enough.
27152 *         return new Canvas(32, 32);
27153 *     });
27154     */
27155    function setCanvasCreator(creator) {
27156        $override('createCanvas', creator);
27157    }
27158
27159    /**
27160     * @param {string} mapName
27161     * @param {Object|string} geoJson
27162     * @param {Object} [specialAreas]
27163     *
27164     * @example
27165     *     $.get('USA.json', function (geoJson) {
27166 *         echarts.registerMap('USA', geoJson);
27167 *         // Or
27168 *         echarts.registerMap('USA', {
27169 *             geoJson: geoJson,
27170 *             specialAreas: {}
27171 *         })
27172 *     });
27173     */
27174    function registerMap(mapName, geoJson, specialAreas) {
27175        if (geoJson.geoJson && !geoJson.features) {
27176            specialAreas = geoJson.specialAreas;
27177            geoJson = geoJson.geoJson;
27178        }
27179        if (typeof geoJson === 'string') {
27180            geoJson = (typeof JSON !== 'undefined' && JSON.parse)
27181                ? JSON.parse(geoJson) : (new Function('return (' + geoJson + ');'))();
27182        }
27183        mapDataStores[mapName] = {
27184            geoJson: geoJson,
27185            specialAreas: specialAreas
27186        };
27187    }
27188
27189    /**
27190     * @param {string} mapName
27191     * @return {Object}
27192     */
27193    function getMap(mapName) {
27194        return mapDataStores[mapName];
27195    }
27196
27197    registerVisual(PRIORITY_VISUAL_GLOBAL, seriesColor);
27198    registerPreprocessor(backwardCompat);
27199    registerProcessor(PRIORITY_PROCESSOR_STATISTIC, dataStack);
27200    registerLoading('default', loadingDefault);
27201
27202// Default actions
27203
27204    registerAction({
27205        type: 'highlight',
27206        event: 'highlight',
27207        update: 'highlight'
27208    }, noop);
27209
27210    registerAction({
27211        type: 'downplay',
27212        event: 'downplay',
27213        update: 'downplay'
27214    }, noop);
27215
27216// Default theme
27217    registerTheme('light', lightTheme);
27218    registerTheme('dark', theme);
27219
27220// For backward compatibility, where the namespace `dataTool` will
27221// be mounted on `echarts` is the extension `dataTool` is imported.
27222    var dataTool = {};
27223
27224    /*
27225* Licensed to the Apache Software Foundation (ASF) under one
27226* or more contributor license agreements.  See the NOTICE file
27227* distributed with this work for additional information
27228* regarding copyright ownership.  The ASF licenses this file
27229* to you under the Apache License, Version 2.0 (the
27230* "License"); you may not use this file except in compliance
27231* with the License.  You may obtain a copy of the License at
27232*
27233*   http://www.apache.org/licenses/LICENSE-2.0
27234*
27235* Unless required by applicable law or agreed to in writing,
27236* software distributed under the License is distributed on an
27237* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
27238* KIND, either express or implied.  See the License for the
27239* specific language governing permissions and limitations
27240* under the License.
27241*/
27242
27243
27244    function defaultKeyGetter(item) {
27245        return item;
27246    }
27247
27248    /**
27249     * @param {Array} oldArr
27250     * @param {Array} newArr
27251     * @param {Function} oldKeyGetter
27252     * @param {Function} newKeyGetter
27253     * @param {Object} [context] Can be visited by this.context in callback.
27254     */
27255    function DataDiffer(oldArr, newArr, oldKeyGetter, newKeyGetter, context) {
27256        this._old = oldArr;
27257        this._new = newArr;
27258
27259        this._oldKeyGetter = oldKeyGetter || defaultKeyGetter;
27260        this._newKeyGetter = newKeyGetter || defaultKeyGetter;
27261
27262        this.context = context;
27263    }
27264
27265    DataDiffer.prototype = {
27266
27267        constructor: DataDiffer,
27268
27269        /**
27270         * Callback function when add a data
27271         */
27272        add: function (func) {
27273            this._add = func;
27274            return this;
27275        },
27276
27277        /**
27278         * Callback function when update a data
27279         */
27280        update: function (func) {
27281            this._update = func;
27282            return this;
27283        },
27284
27285        /**
27286         * Callback function when remove a data
27287         */
27288        remove: function (func) {
27289            this._remove = func;
27290            return this;
27291        },
27292
27293        execute: function () {
27294            var oldArr = this._old;
27295            var newArr = this._new;
27296
27297            var oldDataIndexMap = {};
27298            var newDataIndexMap = {};
27299            var oldDataKeyArr = [];
27300            var newDataKeyArr = [];
27301            var i;
27302
27303            initIndexMap(oldArr, oldDataIndexMap, oldDataKeyArr, '_oldKeyGetter', this);
27304            initIndexMap(newArr, newDataIndexMap, newDataKeyArr, '_newKeyGetter', this);
27305
27306            // Travel by inverted order to make sure order consistency
27307            // when duplicate keys exists (consider newDataIndex.pop() below).
27308            // For performance consideration, these code below do not look neat.
27309            for (i = 0; i < oldArr.length; i++) {
27310                var key = oldDataKeyArr[i];
27311                var idx = newDataIndexMap[key];
27312
27313                // idx can never be empty array here. see 'set null' logic below.
27314                if (idx != null) {
27315                    // Consider there is duplicate key (for example, use dataItem.name as key).
27316                    // We should make sure every item in newArr and oldArr can be visited.
27317                    var len = idx.length;
27318                    if (len) {
27319                        len === 1 && (newDataIndexMap[key] = null);
27320                        idx = idx.unshift();
27321                    }
27322                    else {
27323                        newDataIndexMap[key] = null;
27324                    }
27325                    this._update && this._update(idx, i);
27326                }
27327                else {
27328                    this._remove && this._remove(i);
27329                }
27330            }
27331
27332            for (var i = 0; i < newDataKeyArr.length; i++) {
27333                var key = newDataKeyArr[i];
27334                if (newDataIndexMap.hasOwnProperty(key)) {
27335                    var idx = newDataIndexMap[key];
27336                    if (idx == null) {
27337                        continue;
27338                    }
27339                    // idx can never be empty array here. see 'set null' logic above.
27340                    if (!idx.length) {
27341                        this._add && this._add(idx);
27342                    }
27343                    else {
27344                        for (var j = 0, len = idx.length; j < len; j++) {
27345                            this._add && this._add(idx[j]);
27346                        }
27347                    }
27348                }
27349            }
27350        }
27351    };
27352
27353    function initIndexMap(arr, map, keyArr, keyGetterName, dataDiffer) {
27354        for (var i = 0; i < arr.length; i++) {
27355            // Add prefix to avoid conflict with Object.prototype.
27356            var key = '_ec_' + dataDiffer[keyGetterName](arr[i], i);
27357            var existence = map[key];
27358            if (existence == null) {
27359                keyArr.push(key);
27360                map[key] = i;
27361            }
27362            else {
27363                if (!existence.length) {
27364                    map[key] = existence = [existence];
27365                }
27366                existence.push(i);
27367            }
27368        }
27369    }
27370
27371    /*
27372* Licensed to the Apache Software Foundation (ASF) under one
27373* or more contributor license agreements.  See the NOTICE file
27374* distributed with this work for additional information
27375* regarding copyright ownership.  The ASF licenses this file
27376* to you under the Apache License, Version 2.0 (the
27377* "License"); you may not use this file except in compliance
27378* with the License.  You may obtain a copy of the License at
27379*
27380*   http://www.apache.org/licenses/LICENSE-2.0
27381*
27382* Unless required by applicable law or agreed to in writing,
27383* software distributed under the License is distributed on an
27384* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
27385* KIND, either express or implied.  See the License for the
27386* specific language governing permissions and limitations
27387* under the License.
27388*/
27389
27390    var OTHER_DIMENSIONS = createHashMap([
27391        'tooltip', 'label', 'itemName', 'itemId', 'seriesName'
27392    ]);
27393
27394    function summarizeDimensions(data) {
27395        var summary = {};
27396        var encode = summary.encode = {};
27397        var notExtraCoordDimMap = createHashMap();
27398        var defaultedLabel = [];
27399        var defaultedTooltip = [];
27400
27401        each$1(data.dimensions, function (dimName) {
27402            var dimItem = data.getDimensionInfo(dimName);
27403
27404            var coordDim = dimItem.coordDim;
27405            if (coordDim) {
27406                if (__DEV__) {
27407                    assert$1(OTHER_DIMENSIONS.get(coordDim) == null);
27408                }
27409                var coordDimArr = encode[coordDim];
27410                if (!encode.hasOwnProperty(coordDim)) {
27411                    coordDimArr = encode[coordDim] = [];
27412                }
27413                coordDimArr[dimItem.coordDimIndex] = dimName;
27414
27415                if (!dimItem.isExtraCoord) {
27416                    notExtraCoordDimMap.set(coordDim, 1);
27417
27418                    // Use the last coord dim (and label friendly) as default label,
27419                    // because when dataset is used, it is hard to guess which dimension
27420                    // can be value dimension. If both show x, y on label is not look good,
27421                    // and conventionally y axis is focused more.
27422                    if (mayLabelDimType(dimItem.type)) {
27423                        defaultedLabel[0] = dimName;
27424                    }
27425                }
27426                if (dimItem.defaultTooltip) {
27427                    defaultedTooltip.push(dimName);
27428                }
27429            }
27430
27431            OTHER_DIMENSIONS.each(function (v, otherDim) {
27432                var otherDimArr = encode[otherDim];
27433                if (!encode.hasOwnProperty(otherDim)) {
27434                    otherDimArr = encode[otherDim] = [];
27435                }
27436
27437                var dimIndex = dimItem.otherDims[otherDim];
27438                if (dimIndex != null && dimIndex !== false) {
27439                    otherDimArr[dimIndex] = dimItem.name;
27440                }
27441            });
27442        });
27443
27444        var dataDimsOnCoord = [];
27445        var encodeFirstDimNotExtra = {};
27446
27447        notExtraCoordDimMap.each(function (v, coordDim) {
27448            var dimArr = encode[coordDim];
27449            // ??? FIXME extra coord should not be set in dataDimsOnCoord.
27450            // But should fix the case that radar axes: simplify the logic
27451            // of `completeDimension`, remove `extraPrefix`.
27452            encodeFirstDimNotExtra[coordDim] = dimArr[0];
27453            // Not necessary to remove duplicate, because a data
27454            // dim canot on more than one coordDim.
27455            dataDimsOnCoord = dataDimsOnCoord.concat(dimArr);
27456        });
27457
27458        summary.dataDimsOnCoord = dataDimsOnCoord;
27459        summary.encodeFirstDimNotExtra = encodeFirstDimNotExtra;
27460
27461        var encodeLabel = encode.label;
27462        // FIXME `encode.label` is not recommanded, because formatter can not be set
27463        // in this way. Use label.formatter instead. May be remove this approach someday.
27464        if (encodeLabel && encodeLabel.length) {
27465            defaultedLabel = encodeLabel.slice();
27466        }
27467
27468        var encodeTooltip = encode.tooltip;
27469        if (encodeTooltip && encodeTooltip.length) {
27470            defaultedTooltip = encodeTooltip.slice();
27471        }
27472        else if (!defaultedTooltip.length) {
27473            defaultedTooltip = defaultedLabel.slice();
27474        }
27475
27476        encode.defaultedLabel = defaultedLabel;
27477        encode.defaultedTooltip = defaultedTooltip;
27478
27479        return summary;
27480    }
27481
27482    function getDimensionTypeByAxis(axisType) {
27483        return axisType === 'category'
27484            ? 'ordinal'
27485            : axisType === 'time'
27486                ? 'time'
27487                : 'float';
27488    }
27489
27490    function mayLabelDimType(dimType) {
27491        // In most cases, ordinal and time do not suitable for label.
27492        // Ordinal info can be displayed on axis. Time is too long.
27493        return !(dimType === 'ordinal' || dimType === 'time');
27494    }
27495
27496// function findTheLastDimMayLabel(data) {
27497//     // Get last value dim
27498//     var dimensions = data.dimensions.slice();
27499//     var valueType;
27500//     var valueDim;
27501//     while (dimensions.length && (
27502//         valueDim = dimensions.pop(),
27503//         valueType = data.getDimensionInfo(valueDim).type,
27504//         valueType === 'ordinal' || valueType === 'time'
27505//     )) {} // jshint ignore:line
27506//     return valueDim;
27507// }
27508
27509    /*
27510* Licensed to the Apache Software Foundation (ASF) under one
27511* or more contributor license agreements.  See the NOTICE file
27512* distributed with this work for additional information
27513* regarding copyright ownership.  The ASF licenses this file
27514* to you under the Apache License, Version 2.0 (the
27515* "License"); you may not use this file except in compliance
27516* with the License.  You may obtain a copy of the License at
27517*
27518*   http://www.apache.org/licenses/LICENSE-2.0
27519*
27520* Unless required by applicable law or agreed to in writing,
27521* software distributed under the License is distributed on an
27522* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
27523* KIND, either express or implied.  See the License for the
27524* specific language governing permissions and limitations
27525* under the License.
27526*/
27527
27528    /**
27529     * List for data storage
27530     * @module echarts/data/List
27531     */
27532
27533    var isObject$4 = isObject$1;
27534
27535    var UNDEFINED = 'undefined';
27536
27537// Use prefix to avoid index to be the same as otherIdList[idx],
27538// which will cause weird udpate animation.
27539    var ID_PREFIX = 'e\0\0';
27540
27541    var dataCtors = {
27542        'float': typeof Float64Array === UNDEFINED
27543            ? Array : Float64Array,
27544        'int': typeof Int32Array === UNDEFINED
27545            ? Array : Int32Array,
27546        // Ordinal data type can be string or int
27547        'ordinal': Array,
27548        'number': Array,
27549        'time': Array
27550    };
27551
27552// Caution: MUST not use `new CtorUint32Array(arr, 0, len)`, because the Ctor of array is
27553// different from the Ctor of typed array.
27554    var CtorUint32Array = typeof Uint32Array === UNDEFINED ? Array : Uint32Array;
27555    var CtorUint16Array = typeof Uint16Array === UNDEFINED ? Array : Uint16Array;
27556
27557    function getIndicesCtor(list) {
27558        // The possible max value in this._indicies is always this._rawCount despite of filtering.
27559        return list._rawCount > 65535 ? CtorUint32Array : CtorUint16Array;
27560    }
27561
27562    function cloneChunk(originalChunk) {
27563        var Ctor = originalChunk.constructor;
27564        // Only shallow clone is enough when Array.
27565        return Ctor === Array ? originalChunk.slice() : new Ctor(originalChunk);
27566    }
27567
27568    var TRANSFERABLE_PROPERTIES = [
27569        'hasItemOption', '_nameList', '_idList', '_invertedIndicesMap',
27570        '_rawData', '_chunkSize', '_chunkCount', '_dimValueGetter',
27571        '_count', '_rawCount', '_nameDimIdx', '_idDimIdx'
27572    ];
27573    var CLONE_PROPERTIES = [
27574        '_extent', '_approximateExtent', '_rawExtent'
27575    ];
27576
27577    function transferProperties(target, source) {
27578        each$1(TRANSFERABLE_PROPERTIES.concat(source.__wrappedMethods || []), function (propName) {
27579            if (source.hasOwnProperty(propName)) {
27580                target[propName] = source[propName];
27581            }
27582        });
27583
27584        target.__wrappedMethods = source.__wrappedMethods;
27585
27586        each$1(CLONE_PROPERTIES, function (propName) {
27587            target[propName] = clone(source[propName]);
27588        });
27589
27590        target._calculationInfo = extend(source._calculationInfo);
27591    }
27592
27593
27594
27595
27596
27597    /**
27598     * @constructor
27599     * @alias module:echarts/data/List
27600     *
27601     * @param {Array.<string|Object>} dimensions
27602     *      For example, ['someDimName', {name: 'someDimName', type: 'someDimType'}, ...].
27603     *      Dimensions should be concrete names like x, y, z, lng, lat, angle, radius
27604     *      Spetial fields: {
27605 *          ordinalMeta: <module:echarts/data/OrdinalMeta>
27606 *          createInvertedIndices: <boolean>
27607 *      }
27608     * @param {module:echarts/model/Model} hostModel
27609     */
27610    var List = function (dimensions, hostModel) {
27611
27612        dimensions = dimensions || ['x', 'y'];
27613
27614        var dimensionInfos = {};
27615        var dimensionNames = [];
27616        var invertedIndicesMap = {};
27617
27618        for (var i = 0; i < dimensions.length; i++) {
27619            // Use the original dimensions[i], where other flag props may exists.
27620            var dimensionInfo = dimensions[i];
27621
27622            if (isString(dimensionInfo)) {
27623                dimensionInfo = {name: dimensionInfo};
27624            }
27625
27626            var dimensionName = dimensionInfo.name;
27627            dimensionInfo.type = dimensionInfo.type || 'float';
27628            if (!dimensionInfo.coordDim) {
27629                dimensionInfo.coordDim = dimensionName;
27630                dimensionInfo.coordDimIndex = 0;
27631            }
27632
27633            dimensionInfo.otherDims = dimensionInfo.otherDims || {};
27634            dimensionNames.push(dimensionName);
27635            dimensionInfos[dimensionName] = dimensionInfo;
27636
27637            dimensionInfo.index = i;
27638
27639            if (dimensionInfo.createInvertedIndices) {
27640                invertedIndicesMap[dimensionName] = [];
27641            }
27642        }
27643
27644        /**
27645         * @readOnly
27646         * @type {Array.<string>}
27647         */
27648        this.dimensions = dimensionNames;
27649
27650        /**
27651         * Infomation of each data dimension, like data type.
27652         * @type {Object}
27653         */
27654        this._dimensionInfos = dimensionInfos;
27655
27656        /**
27657         * @type {module:echarts/model/Model}
27658         */
27659        this.hostModel = hostModel;
27660
27661        /**
27662         * @type {module:echarts/model/Model}
27663         */
27664        this.dataType;
27665
27666        /**
27667         * Indices stores the indices of data subset after filtered.
27668         * This data subset will be used in chart.
27669         * @type {Array.<number>}
27670         * @readOnly
27671         */
27672        this._indices = null;
27673
27674        this._count = 0;
27675        this._rawCount = 0;
27676
27677        /**
27678         * Data storage
27679         * @type {Object.<key, Array.<TypedArray|Array>>}
27680         * @private
27681         */
27682        this._storage = {};
27683
27684        /**
27685         * @type {Array.<string>}
27686         */
27687        this._nameList = [];
27688        /**
27689         * @type {Array.<string>}
27690         */
27691        this._idList = [];
27692
27693        /**
27694         * Models of data option is stored sparse for optimizing memory cost
27695         * @type {Array.<module:echarts/model/Model>}
27696         * @private
27697         */
27698        this._optionModels = [];
27699
27700        /**
27701         * Global visual properties after visual coding
27702         * @type {Object}
27703         * @private
27704         */
27705        this._visual = {};
27706
27707        /**
27708         * Globel layout properties.
27709         * @type {Object}
27710         * @private
27711         */
27712        this._layout = {};
27713
27714        /**
27715         * Item visual properties after visual coding
27716         * @type {Array.<Object>}
27717         * @private
27718         */
27719        this._itemVisuals = [];
27720
27721        /**
27722         * Key: visual type, Value: boolean
27723         * @type {Object}
27724         * @readOnly
27725         */
27726        this.hasItemVisual = {};
27727
27728        /**
27729         * Item layout properties after layout
27730         * @type {Array.<Object>}
27731         * @private
27732         */
27733        this._itemLayouts = [];
27734
27735        /**
27736         * Graphic elemnents
27737         * @type {Array.<module:zrender/Element>}
27738         * @private
27739         */
27740        this._graphicEls = [];
27741
27742        /**
27743         * Max size of each chunk.
27744         * @type {number}
27745         * @private
27746         */
27747        this._chunkSize = 1e5;
27748
27749        /**
27750         * @type {number}
27751         * @private
27752         */
27753        this._chunkCount = 0;
27754
27755        /**
27756         * @type {Array.<Array|Object>}
27757         * @private
27758         */
27759        this._rawData;
27760
27761        /**
27762         * Raw extent will not be cloned, but only transfered.
27763         * It will not be calculated util needed.
27764         * key: dim,
27765         * value: {end: number, extent: Array.<number>}
27766         * @type {Object}
27767         * @private
27768         */
27769        this._rawExtent = {};
27770
27771        /**
27772         * @type {Object}
27773         * @private
27774         */
27775        this._extent = {};
27776
27777        /**
27778         * key: dim
27779         * value: extent
27780         * @type {Object}
27781         * @private
27782         */
27783        this._approximateExtent = {};
27784
27785        /**
27786         * Cache summary info for fast visit. See "dimensionHelper".
27787         * @type {Object}
27788         * @private
27789         */
27790        this._dimensionsSummary = summarizeDimensions(this);
27791
27792        /**
27793         * @type {Object.<Array|TypedArray>}
27794         * @private
27795         */
27796        this._invertedIndicesMap = invertedIndicesMap;
27797
27798        /**
27799         * @type {Object}
27800         * @private
27801         */
27802        this._calculationInfo = {};
27803    };
27804
27805    var listProto = List.prototype;
27806
27807    listProto.type = 'list';
27808
27809    /**
27810     * If each data item has it's own option
27811     * @type {boolean}
27812     */
27813    listProto.hasItemOption = true;
27814
27815    /**
27816     * Get dimension name
27817     * @param {string|number} dim
27818     *        Dimension can be concrete names like x, y, z, lng, lat, angle, radius
27819     *        Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radiu
27819s'
27820     * @return {string} Concrete dim name.
27821     */
27822    listProto.getDimension = function (dim) {
27823        if (!isNaN(dim)) {
27824            dim = this.dimensions[dim] || dim;
27825        }
27826        return dim;
27827    };
27828
27829    /**
27830     * Get type and calculation info of particular dimension
27831     * @param {string|number} dim
27832     *        Dimension can be concrete names like x, y, z, lng, lat, angle, radius
27833     *        Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius'
27834     */
27835    listProto.getDimensionInfo = function (dim) {
27836        // Do not clone, because there may be categories in dimInfo.
27837        return this._dimensionInfos[this.getDimension(dim)];
27838    };
27839
27840    /**
27841     * @return {Array.<string>} concrete dimension name list on coord.
27842     */
27843    listProto.getDimensionsOnCoord = function () {
27844        return this._dimensionsSummary.dataDimsOnCoord.slice();
27845    };
27846
27847    /**
27848     * @param {string} coordDim
27849     * @param {number} [idx] A coordDim may map to more than one data dim.
27850     *        If idx is `true`, return a array of all mapped dims.
27851     *        If idx is not specified, return the first dim not extra.
27852     * @return {string|Array.<string>} concrete data dim.
27853     *        If idx is number, and not found, return null/undefined.
27854     *        If idx is `true`, and not found, return empty array (always return array).
27855     */
27856    listProto.mapDimension = function (coordDim, idx) {
27857        var dimensionsSummary = this._dimensionsSummary;
27858
27859        if (idx == null) {
27860            return dimensionsSummary.encodeFirstDimNotExtra[coordDim];
27861        }
27862
27863        var dims = dimensionsSummary.encode[coordDim];
27864        return idx === true
27865            // always return array if idx is `true`
27866            ? (dims || []).slice()
27867            : (dims && dims[idx]);
27868    };
27869
27870    /**
27871     * Initialize from data
27872     * @param {Array.<Object|number|Array>} data source or data or data provider.
27873     * @param {Array.<string>} [nameLIst] The name of a datum is used on data diff and
27874     *        defualt label/tooltip.
27875     *        A name can be specified in encode.itemName,
27876     *        or dataItem.name (only for series option data),
27877     *        or provided in nameList from outside.
27878     * @param {Function} [dimValueGetter] (dataItem, dimName, dataIndex, dimIndex) => number
27879     */
27880    listProto.initData = function (data, nameList, dimValueGetter) {
27881
27882        var notProvider = Source.isInstance(data) || isArrayLike(data);
27883        if (notProvider) {
27884            data = new DefaultDataProvider(data, this.dimensions.length);
27885        }
27886
27887        if (__DEV__) {
27888            if (!notProvider && (typeof data.getItem != 'function' || typeof data.count != 'function')) {
27889                throw new Error('Inavlid data provider.');
27890            }
27891        }
27892
27893        this._rawData = data;
27894
27895        // Clear
27896        this._storage = {};
27897        this._indices = null;
27898
27899        this._nameList = nameList || [];
27900
27901        this._idList = [];
27902
27903        this._nameRepeatCount = {};
27904
27905        if (!dimValueGetter) {
27906            this.hasItemOption = false;
27907        }
27908
27909        /**
27910         * @readOnly
27911         */
27912        this.defaultDimValueGetter = defaultDimValueGetters[
27913            this._rawData.getSource().sourceFormat
27914            ];
27915
27916        // Default dim value getter
27917        this._dimValueGetter = dimValueGetter = dimValueGetter
27918            || this.defaultDimValueGetter;
27919
27920        // Reset raw extent.
27921        this._rawExtent = {};
27922
27923        this._initDataFromProvider(0, data.count());
27924
27925        // If data has no item option.
27926        if (data.pure) {
27927            this.hasItemOption = false;
27928        }
27929    };
27930
27931    listProto.getProvider = function () {
27932        return this._rawData;
27933    };
27934
27935    listProto.appendData = function (data) {
27936        if (__DEV__) {
27937            assert$1(!this._indices, 'appendData can only be called on raw data.');
27938        }
27939
27940        var rawData = this._rawData;
27941        var start = this.count();
27942        rawData.appendData(data);
27943        var end = rawData.count();
27944        if (!rawData.persistent) {
27945            end += start;
27946        }
27947        this._initDataFromProvider(start, end);
27948    };
27949
27950    listProto._initDataFromProvider = function (start, end) {
27951        // Optimize.
27952        if (start >= end) {
27953            return;
27954        }
27955
27956        var chunkSize = this._chunkSize;
27957        var rawData = this._rawData;
27958        var storage = this._storage;
27959        var dimensions = this.dimensions;
27960        var dimLen = dimensions.length;
27961        var dimensionInfoMap = this._dimensionInfos;
27962        var nameList = this._nameList;
27963        var idList = this._idList;
27964        var rawExtent = this._rawExtent;
27965        var nameRepeatCount = this._nameRepeatCount = {};
27966        var nameDimIdx;
27967
27968        var chunkCount = this._chunkCount;
27969        var lastChunkIndex = chunkCount - 1;
27970        for (var i = 0; i < dimLen; i++) {
27971            var dim = dimensions[i];
27972            if (!rawExtent[dim]) {
27973                rawExtent[dim] = getInitialExtent();
27974            }
27975
27976            var dimInfo = dimensionInfoMap[dim];
27977            if (dimInfo.otherDims.itemName === 0) {
27978                nameDimIdx = this._nameDimIdx = i;
27979            }
27980            if (dimInfo.otherDims.itemId === 0) {
27981                this._idDimIdx = i;
27982            }
27983            var DataCtor = dataCtors[dimInfo.type];
27984
27985            if (!storage[dim]) {
27986                storage[dim] = [];
27987            }
27988            var resizeChunkArray = storage[dim][lastChunkIndex];
27989            if (resizeChunkArray && resizeChunkArray.length < chunkSize) {
27990                var newStore = new DataCtor(Math.min(end - lastChunkIndex * chunkSize, chunkSize));
27991                // The cost of the copy is probably inconsiderable
27992                // within the initial chunkSize.
27993                for (var j = 0; j < resizeChunkArray.length; j++) {
27994                    newStore[j] = resizeChunkArray[j];
27995                }
27996                storage[dim][lastChunkIndex] = newStore;
27997            }
27998
27999            // Create new chunks.
28000            for (var k = chunkCount * chunkSize; k < end; k += chunkSize) {
28001                storage[dim].push(new DataCtor(Math.min(end - k, chunkSize)));
28002            }
28003            this._chunkCount = storage[dim].length;
28004        }
28005
28006        var dataItem = new Array(dimLen);
28007        for (var idx = start; idx < end; idx++) {
28008            // NOTICE: Try not to write things into dataItem
28009            dataItem = rawData.getItem(idx, dataItem);
28010            // Each data item is value
28011            // [1, 2]
28012            // 2
28013            // Bar chart, line chart which uses category axis
28014            // only gives the 'y' value. 'x' value is the indices of c
28014ategory
28015            // Use a tempValue to normalize the value to be a (x, y) value
28016            var chunkIndex = Math.floor(idx / chunkSize);
28017            var chunkOffset = idx % chunkSize;
28018
28019            // Store the data by dimensions
28020            for (var k = 0; k < dimLen; k++) {
28021                var dim = dimensions[k];
28022                var dimStorage = storage[dim][chunkIndex];
28023                // PENDING NULL is empty or zero
28024                var val = this._dimValueGetter(dataItem, dim, idx, k);
28025                dimStorage[chunkOffset] = val;
28026
28027                var dimRawExtent = rawExtent[dim];
28028                if (val < dimRawExtent[0]) {
28029                    dimRawExtent[0] = val;
28030                }
28031                if (val > dimRawExtent[1]) {
28032                    dimRawExtent[1] = val;
28033                }
28034            }
28035
28036            // ??? FIXME not check by pure but sourceFormat?
28037            // TODO refactor these logic.
28038            if (!rawData.pure) {
28039                var name = nameList[idx];
28040
28041                if (dataItem && name == null) {
28042                    // If dataItem is {name: ...}, it has highest priority.
28043                    // That is appropriate for many common cases.
28044                    if (dataItem.name != null) {
28045                        // There is no other place to persistent dataItem.name,
28046                        // so save it to nameList.
28047                        nameList[idx] = name = dataItem.name;
28048                    }
28049                    else if (nameDimIdx != null) {
28050                        var nameDim = dimensions[nameDimIdx];
28051                        var nameDimChunk = storage[nameDim][chunkIndex];
28052                        if (nameDimChunk) {
28053                            name = nameDimChunk[chunkOffset];
28054                            var ordinalMeta = dimensionInfoMap[nameDim].ordinalMeta;
28055                            if (ordinalMeta && ordinalMeta.categories.length) {
28056                                name = ordinalMeta.categories[name];
28057                            }
28058                        }
28059                    }
28060                }
28061
28062                // Try using the id in option
28063                // id or name is used on dynamical data, mapping old and new items.
28064                var id = dataItem == null ? null : dataItem.id;
28065
28066                if (id == null && name != null) {
28067                    // Use name as id and add counter to avoid same name
28068                    nameRepeatCount[name] = nameRepeatCount[name] || 0;
28069                    id = name;
28070                    if (nameRepeatCount[name] > 0) {
28071                        id += '__ec__' + nameRepeatCount[name];
28072                    }
28073                    nameRepeatCount[name]++;
28074                }
28075                id != null && (idList[idx] = id);
28076            }
28077        }
28078
28079        if (!rawData.persistent && rawData.clean) {
28080            // Clean unused data if data source is typed array.
28081            rawData.clean();
28082        }
28083
28084        this._rawCount = this._count = end;
28085
28086        // Reset data extent
28087        this._extent = {};
28088
28089        prepareInvertedIndex(this);
28090    };
28091
28092    function prepareInvertedIndex(list) {
28093        var invertedIndicesMap = list._invertedIndicesMap;
28094        each$1(invertedIndicesMap, function (invertedIndices, dim) {
28095            var dimInfo = list._dimensionInfos[dim];
28096
28097            // Currently, only dimensions that has ordinalMeta can create inverted indices.
28098            var ordinalMeta = dimInfo.ordinalMeta;
28099            if (ordinalMeta) {
28100                invertedIndices = invertedIndicesMap[dim] = new CtorUint32Array(
28101                    ordinalMeta.categories.length
28102                );
28103                // The default value of TypedArray is 0. To avoid miss
28104                // mapping to 0, we should set it as NaN.
28105                for (var i = 0; i < invertedIndices.length; i++) {
28106                    invertedIndices[i] = NaN;
28107                }
28108                for (var i = 0; i < list._count; i++) {
28109                    // Only support the case that all values are distinct.
28110                    invertedIndices[list.get(dim, i)] = i;
28111                }
28112            }
28113        });
28114    }
28115
28116    function getRawValueFromStore(list, dimIndex, rawIndex) {
28117        var val;
28118        if (dimIndex != null) {
28119            var chunkSize = list._chunkSize;
28120            var chunkIndex = Math.floor(rawIndex / chunkSize);
28121            var chunkOffset = rawIndex % chunkSize;
28122            var dim = list.dimensions[dimIndex];
28123            var chunk = list._storage[dim][chunkIndex];
28124            if (chunk) {
28125                val = chunk[chunkOffset];
28126                var ordinalMeta = list._dimensionInfos[dim].ordinalMeta;
28127                if (ordinalMeta && ordinalMeta.categories.length) {
28128                    val = ordinalMeta.categories[val];
28129                }
28130            }
28131        }
28132        return val;
28133    }
28134
28135    /**
28136     * @return {number}
28137     */
28138    listProto.count = function () {
28139        return this._count;
28140    };
28141
28142    listProto.getIndices = function () {
28143        var newIndices;
28144
28145        var indices = this._indices;
28146        if (indices) {
28147            var Ctor = indices.constructor;
28148            var thisCount = this._count;
28149            // `new Array(a, b, c)` is different from `new Uint32Array(a, b, c)`.
28150            if (Ctor === Array) {
28151                newIndices = new Ctor(thisCount);
28152                for (var i = 0; i < thisCount; i++) {
28153                    newIndices[i] = indices[i];
28154                }
28155            }
28156            else {
28157                newIndices = new Ctor(indices.buffer, 0, thisCount);
28158            }
28159        }
28160        else {
28161            var Ctor = getIndicesCtor(this);
28162            var newIndices = new Ctor(this.count());
28163            for (var i = 0; i < newIndices.length; i++) {
28164                newIndices[i] = i;
28165            }
28166        }
28167
28168        return newIndices;
28169    };
28170
28171    /**
28172     * Get value. Return NaN if idx is out of range.
28173     * @param {string} dim Dim must be concrete name.
28174     * @param {number} idx
28175     * @param {boolean} stack
28176     * @return {number}
28177     */
28178    listProto.get = function (dim, idx /*, stack */) {
28179        if (!(idx >= 0 && idx < this._count)) {
28180            return NaN;
28181        }
28182        var storage = this._storage;
28183        if (!storage[dim]) {
28184            // TODO Warn ?
28185            return NaN;
28186        }
28187
28188        idx = this.getRawIndex(idx);
28189
28190        var chunkIndex = Math.floor(idx / this._chunkSize);
28191        var chunkOffset = idx % this._chunkSize;
28192
28193        var chunkStore = storage[dim][chunkIndex];
28194        var value = chunkStore[chunkOffset];
28195        // FIXME ordinal data type is not stackable
28196        // if (stack) {
28197        //     var dimensionInfo = this._dimensionInfos[dim];
28198        //     if (dimensionInfo && dimensionInfo.stackable) {
28199        //         var stackedOn = this.stackedOn;
28200        //         while (stackedOn) {
28201        //             // Get no stacked data of stacked on
28202        //             var stackedValue = stackedOn.get(dim, idx);
28203        //             // Considering positive stack, negative stack and empty data
28204        //             if ((value >= 0 && stackedValue > 0)  // Positive stack
28205        //                 || (value <= 0 && stackedValue < 0) // Negative stack
28206        //             ) {
28207        //                 value += stackedValue;
28208        //             }
28209        //             stackedOn = stackedOn.stackedOn;
28210        //         }
28211        //     }
28212        // }
28213
28214        return value;
28215    };
28216
28217    /**
28218     * @param {string} dim concrete dim
28219     * @param {number} rawIndex
28220     * @return {number|string}
28221     */
28222    listProto.getByRawIndex = function (dim, rawIdx) {
28223        if (!(rawIdx >= 0 && rawIdx < this._rawCount)) {
28224            return NaN;
28225        }
28226        var dimStore = this._storage[dim];
28227        if (!dimStore) {
28228            // TODO Warn ?
28229            return NaN;
28230        }
28231
28232        var chunkIndex = Math.floor(rawIdx / this._chunkSize);
28233        var chunkOffset = rawIdx % this._chunkSize;
28234        var chunkStore = dimStore[chunkIndex];
28235        return chunkStore[chunkOffset];
28236    };
28237
28238    /**
28239     * FIXME Use `get` on chrome maybe slow(in filterSelf and selectRange).
28240     * Hack a much simpler _getFast
28241     * @private
28242     */
28243    listProto._getFast = function (dim, rawIdx) {
28244        var chunkIndex = Math.floor(rawIdx / this._chunkSize);
28245        var chunkOffset = rawIdx % this._chunkSize;
28246        var chunkStore = this._storage[dim][chunkIndex];
28247        return chunkStore[chunkOffset];
28248    };
28249
28250    /**
28251     * Get value for multi dimensions.
28252     * @param {Array.<string>} [dimensions] If ignored, using all dimensions.
28253     * @param {number} idx
28254     * @return {number}
28255     */
28256    listProto.getValues = function (dimensions, idx /*, stack */) {
28257        var values = [];
28258
28259        if (!isArray(dimensions)) {
28260            // stack = idx;
28261            idx = dimensions;
28262            dimensions = this.dimensions;
28263        }
28264
28265        for (var i = 0, len = dimensions.length; i < len; i++) {
28266            values.push(this.get(dimensions[i], idx /*, stack */));
28267        }
28268
28269        return values;
28270    };
28271
28272    /**
28273     * If value is NaN. Inlcuding '-'
28274     * Only check the coord dimensions.
28275     * @param {string} dim
28276     * @param {number} idx
28277     * @return {number}
28278     */
28279    listProto.hasValue = function (idx) {
28280        var dataDimsOnCoord = this._dimensionsSummary.dataDimsOnCoord;
28281        var dimensionInfos = this._dimensionInfos;
28282        for (var i = 0, len = dataDimsOnCoord.length; i < len; i++) {
28283            if (
28284                // Ordinal type can be string or number
28285            dimensionInfos[dataDimsOnCoord[i]].type !== 'ordinal'
28286            // FIXME check ordinal when using index?
28287            && isNaN(this.get(dataDimsOnCoord[i], idx))
28288            ) {
28289                return false;
28290            }
28291        }
28292        return true;
28293    };
28294
28295    /**
28296     * Get extent of data in one dimension
28297     * @param {string} dim
28298     * @param {boolean} stack
28299     */
28300    listProto.getDataExtent = function (dim /*, stack */) {
28301        // Make sure use concrete dim as cache name.
28302        dim = this.getDimension(dim);
28303        var dimData = this._storage[dim];
28304        var initialExtent = getInitialExtent();
28305
28306        // stack = !!((stack || false) && this.getCalculationInfo(dim));
28307
28308        if (!dimData) {
28309            return initialExtent;
28310        }
28311
28312        // Make more strict checkings to ensure hitting cache.
28313        var currEnd = this.count();
28314        // var cacheName = [dim, !!stack].join('_');
28315        // var cacheName = dim;
28316
28317        // Consider the most cases when using data zoom, `getDataExtent`
28318        // happened before filtering. We cache raw extent, which is not
28319        // necessary to be cleared and recalculated when restore data.
28320        var useRaw = !this._indices; // && !stack;
28321        var dimExtent;
28322
28323        if (useRaw) {
28324            return this._rawExtent[dim].slice();
28325        }
28326        dimExtent = this._extent[dim];
28327        if (dimExtent) {
28328            return dimExtent.slice();
28329        }
28330        dimExtent = initialExtent;
28331
28332        var min = dimExtent[0];
28333        var max = dimExtent[1];
28334
28335        for (var i = 0; i < currEnd; i++) {
28336            // var value = stack ? this.get(dim, i, true) : this._getFast(dim, this.getRawIndex(i));
28337            var value = this._getFast(dim, this.getRawIndex(i));
28338            value < min && (min = value);
28339            value > max && (max = value);
28340        }
28341
28342        dimExtent = [min, max];
28343
28344        this._extent[dim] = dimExtent;
28345
28346        return dimExtent;
28347    };
28348
28349    /**
28350     * Optimize for the scenario that data is filtered by a given extent.
28351     * Consider that if data amount is more than hundreds of thousand,
28352     * extent calculation will cost more than 10ms and the cache will
28353     * be erased because of the filtering.
28354     */
28355    listProto.getApproximateExtent = function (dim /*, stack */) {
28356        dim = this.getDimension(dim);
28357        return this._approximateExtent[dim] || this.getDataExtent(dim /*, stack */);
28358    };
28359
28360    listProto.setApproximateExtent = function (extent, dim /*, stack */) {
28361        dim = this.getDimension(dim);
28362        this._approximateExtent[dim] = extent.slice();
28363    };
28364
28365    /**
28366     * @param {string} key
28367     * @return {*}
28368     */
28369    listProto.getCalculationInfo = function (key) {
28370        return this._calculationInfo[key];
28371    };
28372
28373    /**
28374     * @param {string|Object} key or k-v object
28375     * @param {*} [value]
28376     */
28377    listProto.setCalculationInfo = function (key, value) {
28378        isObject$4(key)
28379            ? extend(this._calculationInfo, key)
28380            : (this._calculationInfo[key] = value);
28381    };
28382
28383    /**
28384     * Get sum of data in one dimension
28385     * @param {string} dim
28386     */
28387    listProto.getSum = function (dim /*, stack */) {
28388        var dimData = this._storage[dim];
28389        var sum = 0;
28390        if (dimData) {
28391            for (var i = 0, len = this.count(); i < len; i++) {
28392                var value = this.get(dim, i /*, stack */);
28393                if (!isNaN(value)) {
28394                    sum += value;
28395                }
28396            }
28397        }
28398        return sum;
28399    };
28400
28401    /**
28402     * Get median of data in one dimension
28403     * @param {string} dim
28404     */
28405    listProto.getMedian = function (dim /*, stack */) {
28406        var dimDataArray = [];
28407        // map all data of one dimension
28408        this.each(dim, function (val, idx) {
28409            if (!isNaN(val)) {
28410                dimDataArray.push(val);
28411            }
28412        });
28413
28414        // TODO
28415        // Use quick select?
28416
28417        // immutability & sort
28418        var sortedDimDataArray = [].concat(dimDataArray).sort(function(a, b) {
28419            return a - b;
28420        });
28421        var len = this.count();
28422        // calculate median
28423        return len === 0 ? 0 :
28424            len % 2 === 1 ? sortedDimDataArray[(len - 1) / 2] :
28425                (sortedDimDataArray[len / 2] + sortedDimDataArray[len / 2 - 1]) / 2;
28426    };
28427
28428// /**
28429//  * Retreive the index with given value
28430//  * @param {string} dim Concrete dimension.
28431//  * @param {number} value
28432//  * @return {number}
28433//  */
28434// Currently incorrect: should return dataIndex but not rawIndex.
28435// Do not fix it until this method is to be used somewhere.
28436// FIXME Precision of float value
28437// listProto.indexOf = function (dim, value) {
28438//     var storage = this._storage;
28439//     var dimData = storage[dim];
28440//     var chunkSize = this._chunkSize;
28441//     if (dimData) {
28442//         for (var i = 0, len = this.count(); i < len; i++) {
28443//             var chunkIndex = Math.floor(i / chunkSize);
28444//             var chunkOffset = i % chunkSize;
28445//             if (dimData[chunkIndex][chunkOffset] === value) {
28446//                 return i;
28447//             }
28448//         }
28449//     }
28450//     return -1;
28451// };
28452
28453    /**
28454     * Only support the dimension which inverted index created.
28455     * Do not support other cases until required.
28456     * @param {string} concrete dim
28457     * @param {number|string} value
28458     * @return {number} rawIndex
28459     */
28460    listProto.rawIndexOf = function (dim, value) {
28461        var invertedIndices = dim && this._invertedIndicesMap[dim];
28462        if (__DEV__) {
28463            if (!invertedIndices) {
28464                throw new Error('Do not supported yet');
28465            }
28466        }
28467        var rawIndex = invertedIndices[value];
28468        if (rawIndex == null || isNaN(rawIndex)) {
28469            return -1;
28470        }
28471        return rawIndex;
28472    };
28473
28474    /**
28475     * Retreive the index with given name
28476     * @param {number} idx
28477     * @param {number} name
28478     * @return {number}
28479     */
28480    listProto.indexOfName = function (name) {
28481        for (var i = 0, len = this.count(); i < len; i++) {
28482            if (this.getName(i) === name) {
28483                return i;
28484            }
28485        }
28486
28487        return -1;
28488    };
28489
28490    /**
28491     * Retreive the index with given raw data index
28492     * @param {number} idx
28493     * @param {number} name
28494     * @return {number}
28495     */
28496    listProto.indexOfRawIndex = function (rawIndex) {
28497        if (!this._indices) {
28498            return rawIndex;
28499        }
28500
28501        if (rawIndex >= this._rawCount || rawIndex < 0) {
28502            return -1;
28503        }
28504
28505        // Indices are ascending
28506        var indices = this._indices;
28507
28508        // If rawIndex === dataIndex
28509        var rawDataIndex = indices[rawIndex];
28510        if (rawDataIndex != null && rawDataIndex < this._count && rawDataIndex === rawIndex) {
28511            return rawIndex;
28512        }
28513
28514        var left = 0;
28515        var right = this._count - 1;
28516        while (left <= right) {
28517            var mid = (left + right) / 2 | 0;
28518            if (indices[mid] < rawIndex) {
28519                left = mid + 1;
28520            }
28521            else if (indices[mid] > rawIndex) {
28522                right = mid - 1;
28523            }
28524            else {
28525                return mid;
28526            }
28527        }
28528        return -1;
28529    };
28530
28531    /**
28532     * Retreive the index of nearest value
28533     * @param {string} dim
28534     * @param {number} value
28535     * @param {number} [maxDistance=Infinity]
28536     * @return {Array.<number>} Considere multiple points has the same value.
28537     */
28538    listProto.indicesOfNearest = function (dim, value, maxDistance) {
28539        var storage = this._storage;
28540        var dimData = storage[dim];
28541        var nearestIndices = [];
28542
28543        if (!dimData) {
28544            return nearestIndices;
28545        }
28546
28547        if (maxDistance == null) {
28548            maxDistance = Infinity;
28549        }
28550
28551        var minDist = Number.MAX_VALUE;
28552        var minDiff = -1;
28553        for (var i = 0, len = this.count(); i < len; i++) {
28554            var diff = value - this.get(dim, i /*, stack */);
28555            var dist = Math.abs(diff);
28556            if (diff <= maxDistance && dist <= minDist) {
28557                // For the case of two data are same on xAxis, which has sequence data.
28558                // Show the nearest index
28559                // https://github.com/ecomfe/echarts/issues/2869
28560                if (dist < minDist || (diff >= 0 && minDiff < 0)) {
28561                    minDist = dist;
28562                    minDiff = diff;
28563                    nearestIndices.length = 0;
28564                }
28565                nearestIndices.push(i);
28566            }
28567        }
28568        return nearestIndices;
28569    };
28570
28571    /**
28572     * Get raw data index
28573     * @param {number} idx
28574     * @return {number}
28575     */
28576    listProto.getRawIndex = getRawIndexWithoutIndices;
28577
28578    function getRawIndexWithoutIndices(idx) {
28579        return idx;
28580    }
28581
28582    function getRawIndexWithIndices(idx) {
28583        if (idx < this._count && idx >= 0) {
28584            return this._indices[idx];
28585        }
28586        return -1;
28587    }
28588
28589    /**
28590     * Get raw data item
28591     * @param {number} idx
28592     * @return {number}
28593     */
28594    listProto.getRawDataItem = function (idx) {
28595        if (!this._rawData.persistent) {
28596            var val = [];
28597            for (var i = 0; i < this.dimensions.length; i++) {
28598                var dim = this.dimensions[i];
28599                val.push(this.get(dim, idx));
28600            }
28601            return val;
28602        }
28603        else {
28604            return this._rawData.getItem(this.getRawIndex(idx));
28605        }
28606    };
28607
28608    /**
28609     * @param {number} idx
28610     * @param {boolean} [notDefaultIdx=false]
28611     * @return {string}
28612     */
28613    listProto.getName = function (idx) {
28614        var rawIndex = this.getRawIndex(idx);
28615        return this._nameList[rawIndex]
28616            || getRawValueFromStore(this, this._nameDimIdx, rawIndex)
28617            || '';
28618    };
28619
28620    /**
28621     * @param {number} idx
28622     * @param {boolean} [notDefaultIdx=false]
28623     * @return {string}
28624     */
28625    listProto.getId = function (idx) {
28626        return getId(this, this.getRawIndex(idx));
28627    };
28628
28629    function getId(list, rawIndex) {
28630        var id = list._idList[rawIndex];
28631        if (id == null) {
28632            id = getRawValueFromStore(list, list._idDimIdx, rawIndex);
28633        }
28634        if (id == null) {
28635            // FIXME Check the usage in graph, should not use prefix.
28636            id = ID_PREFIX + rawIndex;
28637        }
28638        return id;
28639    }
28640
28641    function normalizeDimensions(dimensions) {
28642        if (!isArray(dimensions)) {
28643            dimensions = [dimensions];
28644        }
28645        return dimensions;
28646    }
28647
28648    function validateDimensions(list, dims) {
28649        for (var i = 0; i < dims.length; i++) {
28650            // stroage may be empty when no data, so use
28651            // dimensionInfos to check.
28652            if (!list._dimensionInfos[dims[i]]) {
28653                console.error('Unkown dimension ' + dims[i]);
28654            }
28655        }
28656    }
28657
28658    /**
28659     * Data iteration
28660     * @param {string|Array.<string>}
28661     * @param {Function} cb
28662     * @param {*} [context=this]
28663     *
28664     * @example
28665     *  list.each('x', function (x, idx) {});
28666     *  list.each(['x', 'y'], function (x, y, idx) {});
28667     *  list.each(function (idx) {})
28668     */
28669    listProto.each = function (dims, cb, context, contextCompat) {
28670        'use strict';
28671
28672        if (!this._count) {
28673            return;
28674        }
28675
28676        if (typeof dims === 'function') {
28677            contextCompat = context;
28678            context = cb;
28679            cb = dims;
28680            dims = [];
28681        }
28682
28683        // contextCompat just for compat echarts3
28684        context = context || contextCompat || this;
28685
28686        dims = map(normalizeDimensions(dims), this.getDimension, this);
28687
28688        if (__DEV__) {
28689            validateDimensions(this, dims);
28690        }
28691
28692        var dimSize = dims.length;
28693
28694        for (var i = 0; i < this.count(); i++) {
28695            // Simple optimization
28696            switch (dimSize) {
28697                case 0:
28698                    cb.call(context, i);
28699                    break;
28700                case 1:
28701                    cb.call(context, this.get(dims[0], i), i);
28702                    break;
28703                case 2:
28704                    cb.call(context, this.get(dims[0], i), this.get(dims[1], i), i);
28705                    break;
28706                default:
28707                    var k = 0;
28708                    var value = [];
28709                    for (; k < dimSize; k++) {
28710                        value[k] = this.get(dims[k], i);
28711                    }
28712                    // Index
28713                    value[k] = i;
28714                    cb.apply(context, value);
28715            }
28716        }
28717    };
28718
28719    /**
28720     * Data filter
28721     * @param {string|Array.<string>}
28722     * @param {Function} cb
28723     * @param {*} [context=this]
28724     */
28725    listProto.filterSelf = function (dimensions, cb, context, contextCompat) {
28726        'use strict';
28727
28728        if (!this._count) {
28729            return;
28730        }
28731
28732        if (typeof dimensions === 'function') {
28733            contextCompat = context;
28734            context = cb;
28735            cb = dimensions;
28736            dimensions = [];
28737        }
28738
28739        // contextCompat just for compat echarts3
28740        context = context || contextCompat || this;
28741
28742        dimensions = map(
28743            normalizeDimensions(dimensions), this.getDimension, this
28744        );
28745
28746        if (__DEV__) {
28747            validateDimensions(this, dimensions);
28748        }
28749
28750
28751        var count = this.count();
28752        var Ctor = getIndicesCtor(this);
28753        var newIndices = new Ctor(count);
28754        var value = [];
28755        var dimSize = dimensions.length;
28756
28757        var offset = 0;
28758        var dim0 = dimensions[0];
28759
28760        for (var i = 0; i < count; i++) {
28761            var keep;
28762            var rawIdx = this.getRawIndex(i);
28763            // Simple optimization
28764            if (dimSize === 0) {
28765                keep = cb.call(context, i);
28766            }
28767            else if (dimSize === 1) {
28768                var val = this._getFast(dim0, rawIdx);
28769                keep = cb.call(context, val, i);
28770            }
28771            else {
28772                for (var k = 0; k < dimSize; k++) {
28773                    value[k] = this._getFast(dim0, rawIdx);
28774                }
28775                value[k] = i;
28776                keep = cb.apply(context, value);
28777            }
28778            if (keep) {
28779                newIndices[offset++] = rawIdx;
28780            }
28781        }
28782
28783        // Set indices after filtered.
28784        if (offset < count) {
28785            this._indices = newIndices;
28786        }
28787        this._count = offset;
28788        // Reset data extent
28789        this._extent = {};
28790
28791        this.getRawIndex = this._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices;
28792
28793        return this;
28794    };
28795
28796    /**
28797     * Select data in range. (For optimization of filter)
28798     * (Manually inline code, support 5 million data filtering in data zoom.)
28799     */
28800    listProto.selectRange = function (range) {
28801        'use strict';
28802
28803        if (!this._count) {
28804            return;
28805        }
28806
28807        var dimensions = [];
28808        for (var dim in range) {
28809            if (range.hasOwnProperty(dim)) {
28810                dimensions.push(dim);
28811            }
28812        }
28813
28814        if (__DEV__) {
28815            validateDimensions(this, dimensions);
28816        }
28817
28818        var dimSize = dimensions.length;
28819        if (!dimSize) {
28820            return;
28821        }
28822
28823        var originalCount = this.count();
28824        var Ctor = getIndicesCtor(this);
28825        var newIndices = new Ctor(originalCount);
28826
28827        var offset = 0;
28828        var dim0 = dimensions[0];
28829
28830        var min = range[dim0][0];
28831        var max = range[dim0][1];
28832
28833        var quickFinished = false;
28834        if (!this._indices) {
28835            // Extreme optimization for common case. About 2x faster in chrome.
28836            var idx = 0;
28837            if (di
28837mSize === 1) {
28838                var dimStorage = this._storage[dimensions[0]];
28839                for (var k = 0; k < this._chunkCount; k++) {
28840                    var chunkStorage = dimStorage[k];
28841                    var len = Math.min(this._count - k * this._chunkSize, this._chunkSize);
28842                    for (var i = 0; i < len; i++) {
28843                        var val = chunkStorage[i];
28844                        // NaN will not be filtered. Consider the case, in line chart, empty
28845                        // value indicates the line should be broken. But for the case like
28846                        // scatter plot, a data item with empty value will not be rendered,
28847                        // but the axis extent may be effected if some other dim of the data
28848                        // item has value. Fortunately it is not a significant negative effect.
28849                        if (
28850                            (val >= min && val <= max) || isNaN(val)
28851                        ) {
28852                            newIndices[offset++] = idx;
28853                        }
28854                        idx++;
28855                    }
28856                }
28857                quickFinished = true;
28858            }
28859            else if (dimSize === 2) {
28860                var dimStorage = this._storage[dim0];
28861                var dimStorage2 = this._storage[dimensions[1]];
28862                var min2 = range[dimensions[1]][0];
28863                var max2 = range[dimensions[1]][1];
28864                for (var k = 0; k < this._chunkCount; k++) {
28865                    var chunkStorage = dimStorage[k];
28866                    var chunkStorage2= dimStorage2[k];
28867                    var len = Math.min(this._count - k * this._chunkSize, this._chunkSize);
28868                    for (var i = 0; i < len; i++) {
28869                        var val = chunkStorage[i];
28870                        var val2 = chunkStorage2[i];
28871                        // Do not filter NaN, see comment above.
28872                        if ((
28873                                (val >= min && val <= max) || isNaN(val)
28874                            )
28875                            && (
28876                                (val2 >= min2 && val2 <= max2) || isNaN(val2)
28877                            )
28878                        ) {
28879                            newIndices[offset++] = idx;
28880                        }
28881                        idx++;
28882                    }
28883                }
28884                quickFinished = true;
28885            }
28886        }
28887        if (!quickFinished) {
28888            if (dimSize === 1) {
28889                for (var i = 0; i < originalCount; i++) {
28890                    var rawIndex = this.getRawIndex(i);
28891                    var val = this._getFast(dim0, rawIndex);
28892                    // Do not filter NaN, see comment above.
28893                    if (
28894                        (val >= min && val <= max) || isNaN(val)
28895                    ) {
28896                        newIndices[offset++] = rawIndex;
28897                    }
28898                }
28899            }
28900            else {
28901                for (var i = 0; i < originalCount; i++) {
28902                    var keep = true;
28903                    var rawIndex = this.getRawIndex(i);
28904                    for (var k = 0; k < dimSize; k++) {
28905                        var dimk = dimensions[k];
28906                        var val = this._getFast(dim, rawIndex);
28907                        // Do not filter NaN, see comment above.
28908                        if (val < range[dimk][0] || val > range[dimk][1]) {
28909                            keep = false;
28910                        }
28911                    }
28912                    if (keep) {
28913                        newIndices[offset++] = this.getRawIndex(i);
28914                    }
28915                }
28916            }
28917        }
28918
28919        // Set indices after filtered.
28920        if (offset < originalCount) {
28921            this._indices = newIndices;
28922        }
28923        this._count = offset;
28924        // Reset data extent
28925        this._extent = {};
28926
28927        this.getRawIndex = this._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices;
28928
28929        return this;
28930    };
28931
28932    /**
28933     * Data mapping to a plain array
28934     * @param {string|Array.<string>} [dimensions]
28935     * @param {Function} cb
28936     * @param {*} [context=this]
28937     * @return {Array}
28938     */
28939    listProto.mapArray = function (dimensions, cb, context, contextCompat) {
28940        'use strict';
28941
28942        if (typeof dimensions === 'function') {
28943            contextCompat = context;
28944            context = cb;
28945            cb = dimensions;
28946            dimensions = [];
28947        }
28948
28949        // contextCompat just for compat echarts3
28950        context = context || contextCompat || this;
28951
28952        var result = [];
28953        this.each(dimensions, function () {
28954            result.push(cb && cb.apply(this, arguments));
28955        }, context);
28956        return result;
28957    };
28958
28959// Data in excludeDimensions is copied, otherwise transfered.
28960    function cloneListForMapAndSample(original, excludeDimensions) {
28961        var allDimensions = original.dimensions;
28962        var list = new List(
28963            map(allDimensions, original.getDimensionInfo, original),
28964            original.hostModel
28965        );
28966        // FIXME If needs stackedOn, value may already been stacked
28967        transferProperties(list, original);
28968
28969        var storage = list._storage = {};
28970        var originalStorage = original._storage;
28971
28972        // Init storage
28973        for (var i = 0; i < allDimensions.length; i++) {
28974            var dim = allDimensions[i];
28975            if (originalStorage[dim]) {
28976                // Notice that we do not reset invertedIndicesMap here, becuase
28977                // there is no scenario of mapping or sampling ordinal dimension.
28978                if (indexOf(excludeDimensions, dim) >= 0) {
28979                    storage[dim] = cloneDimStore(originalStorage[dim]);
28980                    list._rawExtent[dim] = getInitialExtent();
28981                    list._extent[dim] = null;
28982                }
28983                else {
28984                    // Direct reference for other dimensions
28985                    storage[dim] = originalStorage[dim];
28986                }
28987            }
28988        }
28989        return list;
28990    }
28991
28992    function cloneDimStore(originalDimStore) {
28993        var newDimStore = new Array(originalDimStore.length);
28994        for (var j = 0; j < originalDimStore.length; j++) {
28995            newDimStore[j] = cloneChunk(originalDimStore[j]);
28996        }
28997        return newDimStore;
28998    }
28999
29000    function getInitialExtent() {
29001        return [Infinity, -Infinity];
29002    }
29003
29004    /**
29005     * Data mapping to a new List with given dimensions
29006     * @param {string|Array.<string>} dimensions
29007     * @param {Function} cb
29008     * @param {*} [context=this]
29009     * @return {Array}
29010     */
29011    listProto.map = function (dimensions, cb, context, contextCompat) {
29012        'use strict';
29013
29014        // contextCompat just for compat echarts3
29015        context = context || contextCompat || this;
29016
29017        dimensions = map(
29018            normalizeDimensions(dimensions), this.getDimension, this
29019        );
29020
29021        if (__DEV__) {
29022            validateDimensions(this, dimensions);
29023        }
29024
29025        var list = cloneListForMapAndSample(this, dimensions);
29026
29027        // Following properties are all immutable.
29028        // So we can reference to the same value
29029        list._indices = this._indices;
29030        list.getRawIndex = list._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices;
29031
29032        var storage = list._storage;
29033
29034        var tmpRetValue = [];
29035        var chunkSize = this._chunkSize;
29036        var dimSize = dimensions.length;
29037        var dataCount = this.count();
29038        var values = [];
29039        var rawExtent = list._rawExtent;
29040
29041        for (var dataIndex = 0; dataIndex < dataCount; dataIndex++) {
29042            for (var dimIndex = 0; dimIndex < dimSize; dimIndex++) {
29043                values[dimIndex] = this.get(dimensions[dimIndex], dataIndex /*, stack */);
29044            }
29045            values[dimSize] = dataIndex;
29046
29047            var retValue = cb && cb.apply(context, values);
29048            if (retValue != null) {
29049                // a number or string (in oridinal dimension)?
29050                if (typeof retValue !== 'object') {
29051                    tmpRetValue[0] = retValue;
29052                    retValue = tmpRetValue;
29053                }
29054
29055                var rawIndex = this.getRawIndex(dataIndex);
29056                var chunkIndex = Math.floor(rawIndex / chunkSize);
29057                var chunkOffset = rawIndex % chunkSize;
29058
29059                for (var i = 0; i < retValue.length; i++) {
29060                    var dim = dimensions[i];
29061                    var val = retValue[i];
29062                    var rawExtentOnDim = rawExtent[dim];
29063
29064                    var dimStore = storage[dim];
29065                    if (dimStore) {
29066                        dimStore[chunkIndex][chunkOffset] = val;
29067                    }
29068
29069                    if (val < rawExtentOnDim[0]) {
29070                        rawExtentOnDim[0] = val;
29071                    }
29072                    if (val > rawExtentOnDim[1]) {
29073                        rawExtentOnDim[1] = val;
29074                    }
29075                }
29076            }
29077        }
29078
29079        return list;
29080    };
29081
29082    /**
29083     * Large data down sampling on given dimension
29084     * @param {string} dimension
29085     * @param {number} rate
29086     * @param {Function} sampleValue
29087     * @param {Function} sampleIndex Sample index for name and id
29088     */
29089    listProto.downSample = function (dimension, rate, sampleValue, sampleIndex) {
29090        var list = cloneListForMapAndSample(this, [dimension]);
29091        var targetStorage = list._storage;
29092
29093        var frameValues = [];
29094        var frameSize = Math.floor(1 / rate);
29095
29096        var dimStore = targetStorage[dimension];
29097        var len = this.count();
29098        var chunkSize = this._chunkSize;
29099        var rawExtentOnDim = list._rawExtent[dimension];
29100
29101        var newIndices = new (getIndicesCtor(this))(len);
29102
29103        var offset = 0;
29104        for (var i = 0; i < len; i += frameSize) {
29105            // Last frame
29106            if (frameSize > len - i) {
29107                frameSize = len - i;
29108                frameValues.length = frameSize;
29109            }
29110            for (var k = 0; k < frameSize; k++) {
29111                var dataIdx = this.getRawIndex(i + k);
29112                var originalChunkIndex = Math.floor(dataIdx / chunkSize);
29113                var originalChunkOffset = dataIdx % chunkSize;
29114                frameValues[k] = dimStore[originalChunkIndex][originalChunkOffset];
29115            }
29116            var value = sampleValue(frameValues);
29117            var sampleFrameIdx = this.getRawIndex(
29118                Math.min(i + sampleIndex(frameValues, value) || 0, len - 1)
29119            );
29120            var sampleChunkIndex = Math.floor(sampleFrameIdx / chunkSize);
29121            var sampleChunkOffset = sampleFrameIdx % chunkSize;
29122            // Only write value on the filtered data
29123            dimStore[sampleChunkIndex][sampleChunkOffset] = value;
29124
29125            if (value < rawExtentOnDim[0]) {
29126                rawExtentOnDim[0] = value;
29127            }
29128            if (value > rawExtentOnDim[1]) {
29129                rawExtentOnDim[1] = value;
29130            }
29131
29132            newIndices[offset++] = sampleFrameIdx;
29133        }
29134
29135        list._count = offset;
29136        list._indices = newIndices;
29137
29138        list.getRawIndex = getRawIndexWithIndices;
29139
29140        return list;
29141    };
29142
29143    /**
29144     * Get model of one data item.
29145     *
29146     * @param {number} idx
29147     */
29148// FIXME Model proxy ?
29149    listProto.getItemModel = function (idx) {
29150        var hostModel = this.hostModel;
29151        return new Model(this.getRawDataItem(idx), hostModel, hostModel && hostModel.ecModel);
29152    };
29153
29154    /**
29155     * Create a data differ
29156     * @param {module:echarts/data/List} otherList
29157     * @return {module:echarts/data/DataDiffer}
29158     */
29159    listProto.diff = function (otherList) {
29160        var thisList = this;
29161
29162        return new DataDiffer(
29163            otherList ? otherList.getIndices() : [],
29164            this.getIndices(),
29165            function (idx) {
29166                return getId(otherList, idx);
29167            },
29168            function (idx) {
29169                return getId(thisList, idx);
29170            }
29171        );
29172    };
29173    /**
29174     * Get visual property.
29175     * @param {string} key
29176     */
29177    listProto.getVisual = function (key) {
29178        var visual = this._visual;
29179        return visual && visual[key];
29180    };
29181
29182    /**
29183     * Set visual property
29184     * @param {string|Object} key
29185     * @param {*} [value]
29186     *
29187     * @example
29188     *  setVisual('color', color);
29189     *  setVisual({
29190 *      'color': color
29191 *  });
29192     */
29193    listProto.setVisual = function (key, val) {
29194        if (isObject$4(key)) {
29195            for (var name in key) {
29196                if (key.hasOwnProperty(name)) {
29197                    this.setVisual(name, key[name]);
29198                }
29199            }
29200            return;
29201        }
29202        this._visual = this._visual || {};
29203        this._visual[key] = val;
29204    };
29205
29206    /**
29207     * Set layout property.
29208     * @param {string|Object} key
29209     * @param {*} [val]
29210     */
29211    listProto.setLayout = function (key, val) {
29212        if (isObject$4(key)) {
29213            for (var name in key) {
29214                if (key.hasOwnProperty(name)) {
29215                    this.setLayout(name, key[name]);
29216                }
29217            }
29218            return;
29219        }
29220        this._layout[key] = val;
29221    };
29222
29223    /**
29224     * Get layout property.
29225     * @param  {string} key.
29226     * @return {*}
29227     */
29228    listProto.getLayout = function (key) {
29229        return this._layout[key];
29230    };
29231
29232    /**
29233     * Get layout of single data item
29234     * @param {number} idx
29235     */
29236    listProto.getItemLayout = function (idx) {
29237        return this._itemLayouts[idx];
29238    };
29239
29240    /**
29241     * Set layout of single data item
29242     * @param {number} idx
29243     * @param {Object} layout
29244     * @param {boolean=} [merge=false]
29245     */
29246    listProto.setItemLayout = function (idx, layout, merge$$1) {
29247        this._itemLayouts[idx] = merge$$1
29248            ? extend(this._itemLayouts[idx] || {}, layout)
29249            : layout;
29250    };
29251
29252    /**
29253     * Clear all layout of single data item
29254     */
29255    listProto.clearItemLayouts = function () {
29256        this._itemLayouts.length = 0;
29257    };
29258
29259    /**
29260     * Get visual property of single data item
29261     * @param {number} idx
29262     * @param {string} key
29263     * @param {boolean} [ignoreParent=false]
29264     */
29265    listProto.getItemVisual = function (idx, key, ignoreParent) {
29266        var itemVisual = this._itemVisuals[idx];
29267        var val = itemVisual && itemVisual[key];
29268        if (val == null && !ignoreParent) {
29269            // Use global visual property
29270            return this.getVisual(key);
29271        }
29272        return val;
29273    };
29274
29275    /**
29276     * Set visual property of single data item
29277     *
29278     * @param {number} idx
29279     * @param {string|Object} key
29280     * @param {*} [value]
29281     *
29282     * @example
29283     *  setItemVisual(0, 'color', color);
29284     *  setItemVisual(0, {
29285 *      'color': color
29286 *  });
29287     */
29288    listProto.setItemVisual = function (idx, key, value) {
29289        var itemVisual = this._itemVisuals[idx] || {};
29290        var hasItemVisual = this.hasItemVisual;
29291        this._itemVisuals[idx] = itemVisual;
29292
29293        if (isObject$4(key)) {
29294            for (var name in key) {
29295                if (key.hasOwnProperty(name)) {
29296                    itemVisual[name] = key[name];
29297                    hasItemVisual[name] = true;
29298                }
29299            }
29300            return;
29301        }
29302        itemVisual[key] = value;
29303        hasItemVisual[key] = true;
29304    };
29305
29306    /**
29307     * Clear itemVisuals and list visual.
29308     */
29309    listProto.clearAllVisual = function () {
29310        this._visual = {};
29311        this._itemVisuals = [];
29312        this.hasItemVisual = {};
29313    };
29314
29315    var setItemDataAndSeriesIndex = function (child) {
29316        child.seriesIndex = this.seriesIndex;
29317        child.dataIndex = this.dataIndex;
29318        child.dataType = this.dataType;
29319    };
29320    /**
29321     * Set graphic element relative to data. It can be set as null
29322     * @param {number} idx
29323     * @param {module:zrender/Element} [el]
29324     */
29325    listProto.setItemGraphicEl = function (idx, el) {
29326        var hostModel = this.hostModel;
29327
29328        if (el) {
29329            // Add data index and series index for indexing the data by element
29330            // Useful in tooltip
29331            el.dataIndex = idx;
29332            el.dataType = this.dataType;
29333            el.seriesIndex = hostModel && hostModel.seriesIndex;
29334            if (el.type === 'group') {
29335                el.traverse(setItemDataAndSeriesIndex, el);
29336            }
29337        }
29338
29339        this._graphicEls[idx] = el;
29340    };
29341
29342    /**
29343     * @param {number} idx
29344     * @return {module:zrender/Element}
29345     */
29346    listProto.getItemGraphicEl = function (idx) {
29347        return this._graphicEls[idx];
29348    };
29349
29350    /**
29351     * @param {Function} cb
29352     * @param {*} context
29353     */
29354    listProto.eachItemGraphicEl = function (cb, context) {
29355        each$1(this._graphicEls, function (el, idx) {
29356            if (el) {
29357                cb && cb.call(context, el, idx);
29358            }
29359        });
29360    };
29361
29362    /**
29363     * Shallow clone a new list except visual and layout properties, and graph elements.
29364     * New list only change the indices.
29365     */
29366    listProto.cloneShallow = function (list) {
29367        if (!list) {
29368            var dimensionInfoList = map(this.dimensions, this.getDimensionInfo, this);
29369            list = new List(dimensionInfoList, this.hostModel);
29370        }
29371
29372        // FIXME
29373        list._storage = this._storage;
29374
29375        transferProperties(list, this);
29376
29377        // Clone will not change the data extent and indices
29378        if (this._indices) {
29379            var Ctor = this._indices.constructor;
29380            list._indices = new Ctor(this._indices);
29381        }
29382        else {
29383            list._indices = null;
29384        }
29385        list.getRawIndex = list._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices;
29386
29387        return list;
29388    };
29389
29390    /**
29391     * Wrap some method to add more feature
29392     * @param {string} methodName
29393     * @param {Function} injectFunction
29394     */
29395    listProto.wrapMethod = function (methodName, injectFunction) {
29396        var originalMethod = this[methodName];
29397        if (typeof originalMethod !== 'function') {
29398            return;
29399        }
29400        this.__wrappedMethods = this.__wrappedMethods || [];
29401        this.__wrappedMethods.push(methodName);
29402        this[methodName] = function () {
29403            var res = originalMethod.apply(this, arguments);
29404            return injectFunction.apply(this, [res].concat(slice(arguments)));
29405        };
29406    };
29407
29408// Methods that create a new list based on this list should be listed here.
29409// Notice that those method should `RETURN` the new list.
29410    listProto.TRANSFERABLE_METHODS = ['cloneShallow', 'downSample', 'map'];
29411// Methods that change indices of this list should be listed here.
29412    listProto.CHANGABLE_METHODS = ['filterSelf', 'selectRange'];
29413
29414    /*
29415* Licensed to the Apache Software Foundation (ASF) under one
29416* or more contributor license agreements.  See the NOTICE file
29417* distributed with this work for additional information
29418* regarding copyright ownership.  The ASF licenses this file
29419* to you under the Apache License, Version 2.0 (the
29420* "License");
29420 you may not use this file except in compliance
29421* with the License.  You may obtain a copy of the License at
29422*
29423*   http://www.apache.org/licenses/LICENSE-2.0
29424*
29425* Unless required by applicable law or agreed to in writing,
29426* software distributed under the License is distributed on an
29427* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
29428* KIND, either express or implied.  See the License for the
29429* specific language governing permissions and limitations
29430* under the License.
29431*/
29432
29433    /**
29434     * @deprecated
29435     * Use `echarts/data/helper/createDimensions` instead.
29436     */
29437
29438    /**
29439     * @see {module:echarts/test/ut/spec/data/completeDimensions}
29440     *
29441     * Complete the dimensions array, by user defined `dimension` and `encode`,
29442     * and guessing from the data structure.
29443     * If no 'value' dimension specified, the first no-named dimension will be
29444     * named as 'value'.
29445     *
29446     * @param {Array.<string>} sysDims Necessary dimensions, like ['x', 'y'], which
29447     *      provides not only dim template, but also default order.
29448     *      properties: 'name', 'type', 'displayName'.
29449     *      `name` of each item provides default coord name.
29450     *      [{dimsDef: [string|Object, ...]}, ...] dimsDef of sysDim item provides default dim name, and
29451     *                                    provide dims count that the sysDim required.
29452     *      [{ordinalMeta}] can be specified.
29453     * @param {module:echarts/data/Source|Array|Object} source or data (for compatibal with pervious)
29454     * @param {Object} [opt]
29455     * @param {Array.<Object|string>} [opt.dimsDef] option.series.dimensions User defined dimensions
29456     *      For example: ['asdf', {name, type}, ...].
29457     * @param {Object|HashMap} [opt.encodeDef] option.series.encode {x: 2, y: [3, 1], tooltip: [1, 2], label: 3}
29458     * @param {string} [opt.generateCoord] Generate coord dim with the given name.
29459     *                 If not specified, extra dim names will be:
29460     *                 'value', 'value0', 'value1', ...
29461     * @param {number} [opt.generateCoordCount] By default, the generated dim name is `generateCoord`.
29462     *                 If `generateCoordCount` specified, the generated dim names will be:
29463     *                 `generateCoord` + 0, `generateCoord` + 1, ...
29464     *                 can be Infinity, indicate that use all of the remain columns.
29465     * @param {number} [opt.dimCount] If not specified, guess by the first data item.
29466     * @param {number} [opt.encodeDefaulter] If not specified, auto find the next available data dim.
29467     * @return {Array.<Object>} [{
29468 *      name: string mandatory,
29469 *      displayName: string, the origin name in dimsDef, see source helper.
29470 *                 If displayName given, the tooltip will displayed vertically.
29471 *      coordDim: string mandatory,
29472 *      coordDimIndex: number mandatory,
29473 *      type: string optional,
29474 *      otherDims: { never null/undefined
29475 *          tooltip: number optional,
29476 *          label: number optional,
29477 *          itemName: number optional,
29478 *          seriesName: number optional,
29479 *      },
29480 *      isExtraCoord: boolean true if coord is generated
29481 *          (not specified in encode and not series specified)
29482 *      other props ...
29483 * }]
29484     */
29485    function completeDimensions(sysDims, source, opt) {
29486        if (!Source.isInstance(source)) {
29487            source = Source.seriesDataToSource(source);
29488        }
29489
29490        opt = opt || {};
29491        sysDims = (sysDims || []).slice();
29492        var dimsDef = (opt.dimsDef || []).slice();
29493        var encodeDef = createHashMap(opt.encodeDef);
29494        var dataDimNameMap = createHashMap();
29495        var coordDimNameMap = createHashMap();
29496        // var valueCandidate;
29497        var result = [];
29498
29499        var dimCount = getDimCount(source, sysDims, dimsDef, opt.dimCount);
29500
29501        // Apply user defined dims (`name` and `type`) and init result.
29502        for (var i = 0; i < dimCount; i++) {
29503            var dimDefItem = dimsDef[i] = extend(
29504                {}, isObject$1(dimsDef[i]) ? dimsDef[i] : {name: dimsDef[i]}
29505            );
29506            var userDimName = dimDefItem.name;
29507            var resultItem = result[i] = {otherDims: {}};
29508            // Name will be applied later for avoiding duplication.
29509            if (userDimName != null && dataDimNameMap.get(userDimName) == null) {
29510                // Only if `series.dimensions` is defined in option
29511                // displayName, will be set, and dimension will be diplayed vertically in
29512                // tooltip by default.
29513                resultItem.name = resultItem.displayName = userDimName;
29514                dataDimNameMap.set(userDimName, i);
29515            }
29516            dimDefItem.type != null && (resultItem.type = dimDefItem.type);
29517            dimDefItem.displayName != null && (resultItem.displayName = dimDefItem.displayName);
29518        }
29519
29520        // Set `coordDim` and `coordDimIndex` by `encodeDef` and normalize `encodeDef`.
29521        encodeDef.each(function (dataDims, coordDim) {
29522            dataDims = normalizeToArray(dataDims).slice();
29523            var validDataDims = encodeDef.set(coordDim, []);
29524            each$1(dataDims, function (resultDimIdx, idx) {
29525                // The input resultDimIdx can be dim name or index.
29526                isString(resultDimIdx) && (resultDimIdx = dataDimNameMap.get(resultDimIdx));
29527                if (resultDimIdx != null && resultDimIdx < dimCount) {
29528                    validDataDims[idx] = resultDimIdx;
29529                    applyDim(result[resultDimIdx], coordDim, idx);
29530                }
29531            });
29532        });
29533
29534        // Apply templetes and default order from `sysDims`.
29535        var availDimIdx = 0;
29536        each$1(sysDims, function (sysDimItem, sysDimIndex) {
29537            var coordDim;
29538            var sysDimItem;
29539            var sysDimItemDimsDef;
29540            var sysDimItemOtherDims;
29541            if (isString(sysDimItem)) {
29542                coordDim = sysDimItem;
29543                sysDimItem = {};
29544            }
29545            else {
29546                coordDim = sysDimItem.name;
29547                var ordinalMeta = sysDimItem.ordinalMeta;
29548                sysDimItem.ordinalMeta = null;
29549                sysDimItem = clone(sysDimItem);
29550                sysDimItem.ordinalMeta = ordinalMeta;
29551                // `coordDimIndex` should not be set directly.
29552                sysDimItemDimsDef = sysDimItem.dimsDef;
29553                sysDimItemOtherDims = sysDimItem.otherDims;
29554                sysDimItem.name = sysDimItem.coordDim = sysDimItem.coordDimIndex
29555                    = sysDimItem.dimsDef = sysDimItem.otherDims = null;
29556            }
29557
29558            var dataDims = normalizeToArray(encodeDef.get(coordDim));
29559            // dimensions provides default dim sequences.
29560            if (!dataDims.length) {
29561                for (var i = 0; i < (sysDimItemDimsDef && sysDimItemDimsDef.length || 1); i++) {
29562                    while (availDimIdx < result.length && result[availDimIdx].coordDim != null) {
29563                        availDimIdx++;
29564                    }
29565                    availDimIdx < result.length && dataDims.push(availDimIdx++);
29566                }
29567            }
29568
29569            // Apply templates.
29570            each$1(dataDims, function (resultDimIdx, coordDimIndex) {
29571                var resultItem = result[resultDimIdx];
29572                applyDim(defaults(resultItem, sysDimItem), coordDim, coordDimIndex);
29573                if (resultItem.name == null && sysDimItemDimsDef) {
29574                    var sysDimItemDimsDefItem = sysDimItemDimsDef[coordDimIndex];
29575                    !isObject$1(sysDimItemDimsDefItem) && (sysDimItemDimsDefItem = {name: sysDimItemDimsDefItem});
29576                    resultItem.name = resultItem.displayName = sysDimItemDimsDefItem.name;
29577                    resultItem.defaultTooltip = sysDimItemDimsDefItem.defaultTooltip;
29578                }
29579                // FIXME refactor, currently only used in case: {otherDims: {tooltip: false}}
29580                sysDimItemOtherDims && defaults(resultItem.otherDims, sysDimItemOtherDims);
29581            });
29582        });
29583
29584        function applyDim(resultItem, coordDim, coordDimIndex) {
29585            if (OTHER_DIMENSIONS.get(coordDim) != null) {
29586                resultItem.otherDims[coordDim] = coordDimIndex;
29587            }
29588            else {
29589                resultItem.coordDim = coordDim;
29590                resultItem.coordDimIndex = coordDimIndex;
29591                coordDimNameMap.set(coordDim, true);
29592            }
29593        }
29594
29595        // Make sure the first extra dim is 'value'.
29596        var generateCoord = opt.generateCoord;
29597        var generateCoordCount = opt.generateCoordCount;
29598        var fromZero = generateCoordCount != null;
29599        generateCoordCount = generateCoord ? (generateCoordCount || 1) : 0;
29600        var extra = generateCoord || 'value';
29601
29602        // Set dim `name` and other `coordDim` and other props.
29603        for (var resultDimIdx = 0; resultDimIdx < dimCount; resultDimIdx++) {
29604            var resultItem = result[resultDimIdx] = result[resultDimIdx] || {};
29605            var coordDim = resultItem.coordDim;
29606
29607            if (coordDim == null) {
29608                resultItem.coordDim = genName(
29609                    extra, coordDimNameMap, fromZero
29610                );
29611                resultItem.coordDimIndex = 0;
29612                if (!generateCoord || generateCoordCount <= 0) {
29613                    resultItem.isExtraCoord = true;
29614                }
29615                generateCoordCount--;
29616            }
29617
29618            resultItem.name == null && (resultItem.name = genName(
29619                resultItem.coordDim,
29620                dataDimNameMap
29621            ));
29622
29623            if (resultItem.type == null && guessOrdinal(source, resultDimIdx, resultItem.name)) {
29624                resultItem.type = 'ordinal';
29625            }
29626        }
29627
29628        return result;
29629    }
29630
29631// ??? TODO
29632// Originally detect dimCount by data[0]. Should we
29633// optimize it to only by sysDims and dimensions and encode.
29634// So only necessary dims will be initialized.
29635// But
29636// (1) custom series should be considered. where other dims
29637// may be visited.
29638// (2) sometimes user need to calcualte bubble size or use visualMap
29639// on other dimensions besides coordSys needed.
29640// So, dims that is not used by system, should be shared in storage?
29641    function getDimCount(source, sysDims, dimsDef, optDimCount) {
29642        // Note that the result dimCount should not small than columns count
29643        // of data, otherwise `dataDimNameMap` checking will be incorrect.
29644        var dimCount = Math.max(
29645            source.dimensionsDetectCount || 1,
29646            sysDims.length,
29647            dimsDef.length,
29648            optDimCount || 0
29649        );
29650        each$1(sysDims, function (sysDimItem) {
29651            var sysDimItemDimsDef = sysDimItem.dimsDef;
29652            sysDimItemDimsDef && (dimCount = Math.max(dimCount, sysDimItemDimsDef.length));
29653        });
29654        return dimCount;
29655    }
29656
29657    function genName(name, map$$1, fromZero) {
29658        if (fromZero || map$$1.get(name) != null) {
29659            var i = 0;
29660            while (map$$1.get(name + i) != null) {
29661                i++;
29662            }
29663            name += i;
29664        }
29665        map$$1.set(name, true);
29666        return name;
29667    }
29668
29669    /*
29670* Licensed to the Apache Software Foundation (ASF) under one
29671* or more contributor license agreements.  See the NOTICE file
29672* distributed with this work for additional information
29673* regarding copyright ownership.  The ASF licenses this file
29674* to you under the Apache License, Version 2.0 (the
29675* "License");
29675 you may not use this file except in compliance
29676* with the License.  You may obtain a copy of the License at
29677*
29678*   http://www.apache.org/licenses/LICENSE-2.0
29679*
29680* Unless required by applicable law or agreed to in writing,
29681* software distributed under the License is distributed on an
29682* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
29683* KIND, either express or implied.  See the License for the
29684* specific language governing permissions and limitations
29685* under the License.
29686*/
29687
29688    /**
29689     * Substitute `completeDimensions`.
29690     * `completeDimensions` is to be deprecated.
29691     */
29692    /**
29693     * @param {module:echarts/data/Source|module:echarts/data/List} source or data.
29694     * @param {Object|Array} [opt]
29695     * @param {Array.<string|Object>} [opt.coordDimensions=[]]
29696     * @param {number} [opt.dimensionsCount]
29697     * @param {string} [opt.generateCoord]
29698     * @param {string} [opt.generateCoordCount]
29699     * @param {Array.<string|Object>} [opt.dimensionsDefine=source.dimensionsDefine] Overwrite source define.
29700     * @param {Object|HashMap} [opt.encodeDefine=source.encodeDefine] Overwrite source define.
29701     * @return {Array.<Object>} dimensionsInfo
29702     */
29703    var createDimensions = function (source, opt) {
29704        opt = opt || {};
29705        return completeDimensions(opt.coordDimensions || [], source, {
29706            dimsDef: opt.dimensionsDefine || source.dimensionsDefine,
29707            encodeDef: opt.encodeDefine || source.encodeDefine,
29708            dimCount: opt.dimensionsCount,
29709            generateCoord: opt.generateCoord,
29710            generateCoordCount: opt.generateCoordCount
29711        });
29712    };
29713
29714    /*
29715* Licensed to the Apache Software Foundation (ASF) under one
29716* or more contributor license agreements.  See the NOTICE file
29717* distributed with this work for additional information
29718* regarding copyright ownership.  The ASF licenses this file
29719* to you under the Apache License, Version 2.0 (the
29720* "License"); you may not use this file except in compliance
29721* with the License.  You may obtain a copy of the License at
29722*
29723*   http://www.apache.org/licenses/LICENSE-2.0
29724*
29725* Unless required by applicable law or agreed to in writing,
29726* software distributed under the License is distributed on an
29727* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
29728* KIND, either express or implied.  See the License for the
29729* specific language governing permissions and limitations
29730* under the License.
29731*/
29732
29733    /**
29734     * Note that it is too complicated to support 3d stack by value
29735     * (have to create two-dimension inverted index), so in 3d case
29736     * we just support that stacked by index.
29737     *
29738     * @param {module:echarts/model/Series} seriesModel
29739     * @param {Array.<string|Object>} dimensionInfoList The same as the input of <module:echarts/data/List>.
29740     *        The input dimensionInfoList will be modified.
29741     * @param {Object} [opt]
29742     * @param {boolean} [opt.stackedCoordDimension=''] Specify a coord dimension if needed.
29743     * @param {boolean} [opt.byIndex=false]
29744     * @return {Object} calculationInfo
29745     * {
29746 *     stackedDimension: string
29747 *     stackedByDimension: string
29748 *     isStackedByIndex: boolean
29749 *     stackedOverDimension: string
29750 *     stackResultDimension: string
29751 * }
29752     */
29753    function enableDataStack(seriesModel, dimensionInfoList, opt) {
29754        opt = opt || {};
29755        var byIndex = opt.byIndex;
29756        var stackedCoordDimension = opt.stackedCoordDimension;
29757
29758        // Compatibal: when `stack` is set as '', do not stack.
29759        var mayStack = !!(seriesModel && seriesModel.get('stack'));
29760        var stackedByDimInfo;
29761        var stackedDimInfo;
29762        var stackResultDimension;
29763        var stackedOverDimension;
29764
29765        each$1(dimensionInfoList, function (dimensionInfo, index) {
29766            if (isString(dimensionInfo)) {
29767                dimensionInfoList[index] = dimensionInfo = {name: dimensionInfo};
29768            }
29769
29770            if (mayStack && !dimensionInfo.isExtraCoord) {
29771                // Find the first ordinal dimension as the stackedByDimInfo.
29772                if (!byIndex && !stackedByDimInfo && dimensionInfo.ordinalMeta) {
29773                    stackedByDimInfo = dimensionInfo;
29774                }
29775                // Find the first stackable dimension as the stackedDimInfo.
29776                if (!stackedDimInfo
29777                    && dimensionInfo.type !== 'ordinal'
29778                    && dimensionInfo.type !== 'time'
29779                    && (!stackedCoordDimension || stackedCoordDimension === dimensionInfo.coordDim)
29780                ) {
29781                    stackedDimInfo = dimensionInfo;
29782                }
29783            }
29784        });
29785
29786        if (stackedDimInfo && !byIndex && !stackedByDimInfo) {
29787            // Compatible with previous design, value axis (time axis) only stack by index.
29788            // It may make sense if the user provides elaborately constructed data.
29789            byIndex = true;
29790        }
29791
29792        // Add stack dimension, they can be both calculated by coordinate system in `unionExtent`.
29793        // That put stack logic in List is for using conveniently in echarts extensions, but it
29794        // might not be a good way.
29795        if (stackedDimInfo) {
29796            // Use a weird name that not duplicated with other names.
29797            stackResultDimension = '__\0ecstackresult';
29798            stackedOverDimension = '__\0ecstackedover';
29799
29800            // Create inverted index to fast query index by value.
29801            if (stackedByDimInfo) {
29802                stackedByDimInfo.createInvertedIndices = true;
29803            }
29804
29805            var stackedDimCoordDim = stackedDimInfo.coordDim;
29806            var stackedDimType = stackedDimInfo.type;
29807            var stackedDimCoordIndex = 0;
29808
29809            each$1(dimensionInfoList, function (dimensionInfo) {
29810                if (dimensionInfo.coordDim === stackedDimCoordDim) {
29811                    stackedDimCoordIndex++;
29812                }
29813            });
29814
29815            dimensionInfoList.push({
29816                name: stackResultDimension,
29817                coordDim: stackedDimCoordDim,
29818                coordDimIndex: stackedDimCoordIndex,
29819                type: stackedDimType,
29820                isExtraCoord: true,
29821                isCalculationCoord: true
29822            });
29823
29824            stackedDimCoordIndex++;
29825
29826            dimensionInfoList.push({
29827                name: stackedOverDimension,
29828                // This dimension contains stack base (generally, 0), so do not set it as
29829                // `stackedDimCoordDim` to avoid extent calculation, consider log scale.
29830                coordDim: stackedOverDimension,
29831                coordDimIndex: stackedDimCoordIndex,
29832                type: stackedDimType,
29833                isExtraCoord: true,
29834                isCalculationCoord: true
29835            });
29836        }
29837
29838        return {
29839            stackedDimension: stackedDimInfo && stackedDimInfo.name,
29840            stackedByDimension: stackedByDimInfo && stackedByDimInfo.name,
29841            isStackedByIndex: byIndex,
29842            stackedOverDimension: stackedOverDimension,
29843            stackResultDimension: stackResultDimension
29844        };
29845    }
29846
29847    /**
29848     * @param {module:echarts/data/List} data
29849     * @param {string} stackedDim
29850     */
29851    function isDimensionStacked(data, stackedDim /*, stackedByDim*/) {
29852        // Each single series only maps to one pair of axis. So we do not need to
29853        // check stackByDim, whatever stacked by a dimension or stacked by index.
29854        return !!stackedDim && stackedDim === data.getCalculationInfo('stackedDimension');
29855        // && (
29856        //     stackedByDim != null
29857        //         ? stackedByDim === data.getCalculationInfo('stackedByDimension')
29858        //         : data.getCalculationInfo('isStackedByIndex')
29859        // );
29860    }
29861
29862    /**
29863     * @param {module:echarts/data/List} data
29864     * @param {string} targetDim
29865     * @param {string} [stackedByDim] If not input this parameter, check whether
29866     *                                stacked by index.
29867     * @return {string} dimension
29868     */
29869    function getStackedDimension(data, targetDim) {
29870        return isDimensionStacked(data, targetDim)
29871            ? data.getCalculationInfo('stackResultDimension')
29872            : targetDim;
29873    }
29874
29875    /*
29876* Licensed to the Apache Software Foundation (ASF) under one
29877* or more contributor license agreements.  See the NOTICE file
29878* distributed with this work for additional information
29879* regarding copyright ownership.  The ASF licenses this file
29880* to you under the Apache License, Version 2.0 (the
29881* "License"); you may not use this file except in compliance
29882* with the License.  You may obtain a copy of the License at
29883*
29884*   http://www.apache.org/licenses/LICENSE-2.0
29885*
29886* Unless required by applicable law or agreed to in writing,
29887* software distributed under the License is distributed on an
29888* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
29889* KIND, either express or implied.  See the License for the
29890* specific language governing permissions and limitations
29891* under the License.
29892*/
29893
29894    /**
29895     * @param {module:echarts/data/Source|Array} source Or raw data.
29896     * @param {module:echarts/model/Series} seriesModel
29897     * @param {Object} [opt]
29898     * @param {string} [opt.generateCoord]
29899     */
29900    function createListFromArray(source, seriesModel, opt) {
29901        opt = opt || {};
29902
29903        if (!Source.isInstance(source)) {
29904            source = Source.seriesDataToSource(source);
29905        }
29906
29907        var coordSysName = seriesModel.get('coordinateSystem');
29908        var registeredCoordSys = CoordinateSystemManager.get(coordSysName);
29909
29910        var coordSysDefine = getCoordSysDefineBySeries(seriesModel);
29911
29912        var coordSysDimDefs;
29913
29914        if (coordSysDefine) {
29915            coordSysDimDefs = map(coordSysDefine.coordSysDims, function (dim) {
29916                var dimInfo = {name: dim};
29917                var axisModel = coordSysDefine.axisMap.get(dim);
29918                if (axisModel) {
29919                    var axisType = axisModel.get('type');
29920                    dimInfo.type = getDimensionTypeByAxis(axisType);
29921                    // dimInfo.stackable = isStackable(axisType);
29922                }
29923                return dimInfo;
29924            });
29925        }
29926
29927        if (!coordSysDimDefs) {
29928            // Get dimensions from registered coordinate system
29929            coordSysDimDefs = (registeredCoordSys && (
29930                registeredCoordSys.getDimensionsInfo
29931                    ? registeredCoordSys.getDimensionsInfo()
29932                    : registeredCoordSys.dimensions.slice()
29933            )) || ['x', 'y'];
29934        }
29935
29936        var dimInfoList = createDimensions(source, {
29937            coordDimensions: coordSysDimDefs,
29938            generateCoord: opt.generateCoord
29939        });
29940
29941        var firstCategoryDimIndex;
29942        var hasNameEncode;
29943        coordSysDefine && each$1(dimInfoList, function (dimInfo, dimIndex) {
29944            var coordDim = dimInfo.coordDim;
29945            var categoryAxisModel = coordSysDefine.categoryAxisMap.get(coordDim);
29946            if (categoryAxisModel) {
29947                if (firstCategoryDimIndex == null) {
29948                    firstCategoryDimIndex = dimIndex;
29949                }
29950                dimInfo.ordinalMeta = categoryAxisModel.getOrdinalMeta();
29951            }
29952            if (dimInfo.otherDims.itemName != null) {
29953                hasNameEncode = true;
29954            }
29955        });
29956        if (!hasNameEncode && firstCategoryDimIndex != null) {
29957            dimInfoList[firstCategoryDimIndex].otherDims.itemName = 0;
29958        }
29959
29960        var stackCalculationInfo = enableDataStack(seriesModel, dimInfoList);
29961
29962        var list = new List(dimInfoList, seriesModel);
29963
29964        list.setCalculationInfo(stackCalculationInfo);
29965
29966        var dimValueGetter = (firstCategoryDimIndex != null && isNeedCompleteOrdinalData(source))
29967            ? function (itemOpt, dimName, dataIndex, dimIndex) {
29968                // Use dataIndex as ordinal value in categoryAxis
29969                return dimIndex === firstCategoryDimIndex
29970                    ? dataIndex
29971                    : this.defaultDimValueGetter(itemOpt, dimName, dataIndex, dimIndex);
29972            }
29973            : null;
29974
29975        list.hasItemOption = false;
29976        list.initData(source, null, dimValueGetter);
29977
29978        return list;
29979    }
29980
29981    function isNeedCompleteOrdinalData(source) {
29982        if (source.sourceFormat === SOURCE_FORMAT_ORIGINAL) {
29983            var sampleItem = firstDataNotNull(source.data || []);
29984            return sampleItem != null
29985                && !isArray(getDataItemValue(sampleItem));
29986        }
29987    }
29988
29989    function firstDataNotNull(data) {
29990        var i = 0;
29991        while (i < data.length && data[i] == null) {
29992            i++;
29993        }
29994        return data[i];
29995    }
29996
29997    /*
29998* Licensed to the Apache Software Foundation (ASF) under one
29999* or more contributor license agreements.  See the NOTICE file
30000* distributed with this work for additional information
30001* regarding copyright ownership.  The ASF licenses this file
30002* to you under the Apache License, Version 2.0 (the
30003* "License"); you may not use this file except in compliance
30004* with the License.  You may obtain a copy of the License at
30005*
30006*   http://www.apache.org/licenses/LICENSE-2.0
30007*
30008* Unless required by applicable law or agreed to in writing,
30009* software distributed under the License is distributed on an
30010* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
30011* KIND, either express or implied.  See the License for the
30012* specific language governing permissions and limitations
30013* under the License.
30014*/
30015
30016    /**
30017     * // Scale class management
30018     * @module echarts/scale/Scale
30019     */
30020
30021    /**
30022     * @param {Object} [setting]
30023     */
30024    function Scale(setting) {
30025        this._setting = setting || {};
30026
30027        /**
30028         * Extent
30029         * @type {Array.<number>}
30030         * @protected
30031         */
30032        this._extent = [Infinity, -Infinity];
30033
30034        /**
30035         * Step is calculated in adjustExtent
30036         * @type {Array.<number>}
30037         * @protected
30038         */
30039        this._interval = 0;
30040
30041        this.init && this.init.apply(this, arguments);
30042    }
30043
30044    /**
30045     * Parse input val to valid inner number.
30046     * @param {*} val
30047     * @return {number}
30048     */
30049    Scale.prototype.parse = function (val) {
30050        // Notice: This would be a trap here, If the implementation
30051        // of this method depends on extent, and this method is used
30052        // before extent set (like in dataZoom), it would be wrong.
30053        // Nevertheless, parse does not depend on extent generally.
30054        return val;
30055    };
30056
30057    Scale.prototype.getSetting = function (name) {
30058        return this._setting[name];
30059    };
30060
30061    Scale.prototype.contain = function (val) {
30062        var extent = this._extent;
30063        return val >= extent[0] && val <= extent[1];
30064    };
30065
30066    /**
30067     * Normalize value to linear [0, 1], return 0.5 if extent span is 0
30068     * @param {number} val
30069     * @return {number}
30070     */
30071    Scale.prototype.normalize = function (val) {
30072        var extent = this._extent;
30073        if (extent[1] === extent[0]) {
30074            return 0.5;
30075        }
30076        return (val - extent[0]) / (extent[1] - extent[0]);
30077    };
30078
30079    /**
30080     * Scale normalized value
30081     * @param {number} val
30082     * @return {number}
30083     */
30084    Scale.prototype.scale = function (val) {
30085        var extent = this._extent;
30086        return val * (extent[1] - extent[0]) + extent[0];
30087    };
30088
30089    /**
30090     * Set extent from data
30091     * @param {Array.<number>} other
30092     */
30093    Scale.prototype.unionExtent = function (other) {
30094        var extent = this._extent;
30095        other[0] < extent[0] && (extent[0] = other[0]);
30096        other[1] > extent[1] && (extent[1] = other[1]);
30097        // not setExtent because in log axis it may transformed to power
30098        // this.setExtent(extent[0], extent[1]);
30099    };
30100
30101    /**
30102     * Set extent from data
30103     * @param {module:echarts/data/List} data
30104     * @param {string} dim
30105     */
30106    Scale.prototype.unionExtentFromData = function (data, dim) {
30107        this.unionExtent(data.getApproximateExtent(dim));
30108    };
30109
30110    /**
30111     * Get extent
30112     * @return {Array.<number>}
30113     */
30114    Scale.prototype.getExtent = function () {
30115        return this._extent.slice();
30116    };
30117
30118    /**
30119     * Set extent
30120     * @param {number} start
30121     * @param {number} end
30122     */
30123    Scale.prototype.setExtent = function (start, end) {
30124        var thisExtent = this._extent;
30125        if (!isNaN(start)) {
30126            thisExtent[0] = start;
30127        }
30128        if (!isNaN(end)) {
30129            thisExtent[1] = end;
30130        }
30131    };
30132
30133    /**
30134     * When axis extent depends on data and no data exists,
30135     * axis ticks should not be drawn, which is named 'blank'.
30136     */
30137    Scale.prototype.isBlank = function () {
30138        return this._isBlank;
30139    },
30140
30141        /**
30142         * When axis extent depends on data and no data exists,
30143         * axis ticks should not be drawn, which is named 'blank'.
30144         */
30145        Scale.prototype.setBlank = function (isBlank) {
30146            this._isBlank = isBlank;
30147        };
30148
30149    /**
30150     * @abstract
30151     * @param {*} tick
30152     * @return {string} label of the tick.
30153     */
30154    Scale.prototype.getLabel = null;
30155
30156
30157    enableClassExtend(Scale);
30158    enableClassManagement(Scale, {
30159        registerWhenExtend: true
30160    });
30161
30162    /*
30163* Licensed to the Apache Software Foundation (ASF) under one
30164* or more contributor license agreements.  See the NOTICE file
30165* distributed with this work for additional information
30166* regarding copyright ownership.  The ASF licenses this file
30167* to you under the Apache License, Version 2.0 (the
30168* "License"); you may not use this file except in compliance
30169* with the License.  You may obtain a copy of the License at
30170*
30171*   http://www.apache.org/licenses/LICENSE-2.0
30172*
30173* Unless required by applicable law or agreed to in writing,
30174* software distributed under the License is distributed on an
30175* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
30176* KIND, either express or implied.  See the License for the
30177* specific language governing permissions and limitations
30178* under the License.
30179*/
30180
30181    /**
30182     * @constructor
30183     * @param {Object} [opt]
30184     * @param {Object} [opt.categories=[]]
30185     * @param {Object} [opt.needCollect=false]
30186     * @param {Object} [opt.deduplication=false]
30187     */
30188    function OrdinalMeta(opt) {
30189
30190        /**
30191         * @readOnly
30192         * @type {Array.<string>}
30193         */
30194        this.categories = opt.categories || [];
30195
30196        /**
30197         * @private
30198         * @type {boolean}
30199         */
30200        this._needCollect = opt.needCollect;
30201
30202        /**
30203         * @private
30204         * @type {boolean}
30205         */
30206        this._deduplication = opt.deduplication;
30207
30208        /**
30209         * @private
30210         * @type {boolean}
30211         */
30212        this._map;
30213    }
30214
30215    /**
30216     * @param {module:echarts/model/Model} axisModel
30217     * @return {module:echarts/data/OrdinalMeta}
30218     */
30219    OrdinalMeta.createByAxisModel = function (axisModel) {
30220        var option = axisModel.option;
30221        var data = option.data;
30222        var categories = data && map(data, getName);
30223
30224        return new OrdinalMeta({
30225            categories: categories,
30226            needCollect: !categories,
30227            // deduplication is default in axis.
30228            deduplication: option.dedplication !== false
30229        });
30230    };
30231
30232    var proto$1 = OrdinalMeta.prototype;
30233
30234    /**
30235     * @param {string} category
30236     * @return {number} ordinal
30237     */
30238    proto$1.getOrdinal = function (category) {
30239        return getOrCreateMap(this).get(category);
30240    };
30241
30242    /**
30243     * @param {*} category
30244     * @return {number} The ordinal. If not found, return NaN.
30245     */
30246    proto$1.parseAndCollect = function (category) {
30247        var index;
30248        var needCollect = this._needCollect;
30249
30250        // The value of category dim can be the index of the given category set.
30251        // This feature is only supported when !needCollect, because we should
30252        // consider a common case: a value is 2017, which is a number but is
30253        // expected to be tread as a category. This case usually happen in dataset,
30254        // where it happent to be no need of the index feature.
30255        if (typeof category !== 'string' && !needCollect) {
30256            return category;
30257        }
30258
30259        // Optimize for the scenario:
30260        // category is ['2012-01-01', '2012-01-02', ...], where the input
30261        // data has been ensured not duplicate and is large data.
30262        // Notice, if a dataset dimension provide categroies, usually echarts
30263        // should remove duplication except user tell echarts dont do that
30264        // (set axis.deduplication = false), because echarts do not know whether
30265        // the values in the category dimension has duplication (consider the
30266        // parallel-aqi example)
30267        if (needCollect && !this._deduplication) {
30268            index = this.categories.length;
30269            this.categories[index] = category;
30270            return index;
30271        }
30272
30273        var map$$1 = getOrCreateMap(this);
30274        index = map$$1.get(category);
30275
30276        if (index == null) {
30277            if (needCollect) {
30278                index = this.categories.length;
30279                this.categories[index] = category;
30280                map$$1.set(category, index);
30281            }
30282            else {
30283                index = NaN;
30284            }
30285        }
30286
30287        return index;
30288    };
30289
30290// Consider big data, do not create map until needed.
30291    function getOrCreateMap(ordinalMeta) {
30292        return ordinalMeta._map || (
30293            ordinalMeta._map = createHashMap(ordinalMeta.categories)
30294        );
30295    }
30296
30297    function getName(obj) {
30298        if (isObject$1(obj) && obj.value != null) {
30299            return obj.value;
30300        }
30301        else {
30302            return obj + '';
30303        }
30304    }
30305
30306    /*
30307* Licensed to the Apache Software Foundation (ASF) under one
30308* or more contributor license agreements.  See the NOTICE file
30309* distributed with this work for additional information
30310* regarding copyright ownership.  The ASF licenses this file
30311* to you under the Apache License, Version 2.0 (the
30312* "License");
30312 you may not use this file except in compliance
30313* with the License.  You may obtain a copy of the License at
30314*
30315*   http://www.apache.org/licenses/LICENSE-2.0
30316*
30317* Unless required by applicable law or agreed to in writing,
30318* software distributed under the License is distributed on an
30319* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
30320* KIND, either express or implied.  See the License for the
30321* specific language governing permissions and limitations
30322* under the License.
30323*/
30324
30325    /**
30326     * Linear continuous scale
30327     * @module echarts/coord/scale/Ordinal
30328     *
30329     * http://en.wikipedia.org/wiki/Level_of_measurement
30330     */
30331
30332// FIXME only one data
30333
30334    var scaleProto = Scale.prototype;
30335
30336    var OrdinalScale = Scale.extend({
30337
30338        type: 'ordinal',
30339
30340        /**
30341         * @param {module:echarts/data/OrdianlMeta|Array.<string>} ordinalMeta
30342         */
30343        init: function (ordinalMeta, extent) {
30344            // Caution: Should not use instanceof, consider ec-extensions using
30345            // import approach to get OrdinalMeta class.
30346            if (!ordinalMeta || isArray(ordinalMeta)) {
30347                ordinalMeta = new OrdinalMeta({categories: ordinalMeta});
30348            }
30349            this._ordinalMeta = ordinalMeta;
30350            this._extent = extent || [0, ordinalMeta.categories.length - 1];
30351        },
30352
30353        parse: function (val) {
30354            return typeof val === 'string'
30355                ? this._ordinalMeta.getOrdinal(val)
30356                // val might be float.
30357                : Math.round(val);
30358        },
30359
30360        contain: function (rank) {
30361            rank = this.parse(rank);
30362            return scaleProto.contain.call(this, rank)
30363                && this._ordinalMeta.categories[rank] != null;
30364        },
30365
30366        /**
30367         * Normalize given rank or name to linear [0, 1]
30368         * @param {number|string} [val]
30369         * @return {number}
30370         */
30371        normalize: function (val) {
30372            return scaleProto.normalize.call(this, this.parse(val));
30373        },
30374
30375        scale: function (val) {
30376            return Math.round(scaleProto.scale.call(this, val));
30377        },
30378
30379        /**
30380         * @return {Array}
30381         */
30382        getTicks: function () {
30383            var ticks = [];
30384            var extent = this._extent;
30385            var rank = extent[0];
30386
30387            while (rank <= extent[1]) {
30388                ticks.push(rank);
30389                rank++;
30390            }
30391
30392            return ticks;
30393        },
30394
30395        /**
30396         * Get item on rank n
30397         * @param {number} n
30398         * @return {string}
30399         */
30400        getLabel: function (n) {
30401            if (!this.isBlank()) {
30402                // Note that if no data, ordinalMeta.categories is an empty array.
30403                return this._ordinalMeta.categories[n];
30404            }
30405        },
30406
30407        /**
30408         * @return {number}
30409         */
30410        count: function () {
30411            return this._extent[1] - this._extent[0] + 1;
30412        },
30413
30414        /**
30415         * @override
30416         */
30417        unionExtentFromData: function (data, dim) {
30418            this.unionExtent(data.getApproximateExtent(dim));
30419        },
30420
30421        getOrdinalMeta: function () {
30422            return this._ordinalMeta;
30423        },
30424
30425        niceTicks: noop,
30426        niceExtent: noop
30427    });
30428
30429    /**
30430     * @return {module:echarts/scale/Time}
30431     */
30432    OrdinalScale.create = function () {
30433        return new OrdinalScale();
30434    };
30435
30436    /*
30437* Licensed to the Apache Software Foundation (ASF) under one
30438* or more contributor license agreements.  See the NOTICE file
30439* distributed with this work for additional information
30440* regarding copyright ownership.  The ASF licenses this file
30441* to you under the Apache License, Version 2.0 (the
30442* "License"); you may not use this file except in compliance
30443* with the License.  You may obtain a copy of the License at
30444*
30445*   http://www.apache.org/licenses/LICENSE-2.0
30446*
30447* Unless required by applicable law or agreed to in writing,
30448* software distributed under the License is distributed on an
30449* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
30450* KIND, either express or implied.  See the License for the
30451* specific language governing permissions and limitations
30452* under the License.
30453*/
30454
30455    /**
30456     * For testable.
30457     */
30458
30459    var roundNumber$1 = round$1;
30460
30461    /**
30462     * @param {Array.<number>} extent Both extent[0] and extent[1] should be valid number.
30463     *                                Should be extent[0] < extent[1].
30464     * @param {number} splitNumber splitNumber should be >= 1.
30465     * @param {number} [minInterval]
30466     * @param {number} [maxInterval]
30467     * @return {Object} {interval, intervalPrecision, niceTickExtent}
30468     */
30469    function intervalScaleNiceTicks(extent, splitNumber, minInterval, maxInterval) {
30470        var result = {};
30471        var span = extent[1] - extent[0];
30472
30473        var interval = result.interval = nice(span / splitNumber, true);
30474        if (minInterval != null && interval < minInterval) {
30475            interval = result.interval = minInterval;
30476        }
30477        if (maxInterval != null && interval > maxInterval) {
30478            interval = result.interval = maxInterval;
30479        }
30480        // Tow more digital for tick.
30481        var precision = result.intervalPrecision = getIntervalPrecision(interval);
30482        // Niced extent inside original extent
30483        var niceTickExtent = result.niceTickExtent = [
30484            roundNumber$1(Math.ceil(extent[0] / interval) * interval, precision),
30485            roundNumber$1(Math.floor(extent[1] / interval) * interval, precision)
30486        ];
30487
30488        fixExtent(niceTickExtent, extent);
30489
30490        return result;
30491    }
30492
30493    /**
30494     * @param {number} interval
30495     * @return {number} interval precision
30496     */
30497    function getIntervalPrecision(interval) {
30498        // Tow more digital for tick.
30499        return getPrecisionSafe(interval) + 2;
30500    }
30501
30502    function clamp(niceTickExtent, idx, extent) {
30503        niceTickExtent[idx] = Math.max(Math.min(niceTickExtent[idx], extent[1]), extent[0]);
30504    }
30505
30506// In some cases (e.g., splitNumber is 1), niceTickExtent may be out of extent.
30507    function fixExtent(niceTickExtent, extent) {
30508        !isFinite(niceTickExtent[0]) && (niceTickExtent[0] = extent[0]);
30509        !isFinite(niceTickExtent[1]) && (niceTickExtent[1] = extent[1]);
30510        clamp(niceTickExtent, 0, extent);
30511        clamp(niceTickExtent, 1, extent);
30512        if (niceTickExtent[0] > niceTickExtent[1]) {
30513            niceTickExtent[0] = niceTickExtent[1];
30514        }
30515    }
30516
30517    function intervalScaleGetTicks(interval, extent, niceTickExtent, intervalPrecision) {
30518        var ticks = [];
30519
30520        // If interval is 0, return [];
30521        if (!interval) {
30522            return ticks;
30523        }
30524
30525        // Consider this case: using dataZoom toolbox, zoom and zoom.
30526        var safeLimit = 10000;
30527
30528        if (extent[0] < niceTickExtent[0]) {
30529            ticks.push(extent[0]);
30530        }
30531        var tick = niceTickExtent[0];
30532
30533        while (tick <= niceTickExtent[1]) {
30534            ticks.push(tick);
30535            // Avoid rounding error
30536            tick = roundNumber$1(tick + interval, intervalPrecision);
30537            if (tick === ticks[ticks.length - 1]) {
30538                // Consider out of safe float point, e.g.,
30539                // -3711126.9907707 + 2e-10 === -3711126.9907707
30540                break;
30541            }
30542            if (ticks.length > safeLimit) {
30543                return [];
30544            }
30545        }
30546        // Consider this case: the last item of ticks is smaller
30547        // than niceTickExtent[1] and niceTickExtent[1] === extent[1].
30548        if (extent[1] > (ticks.length ? ticks[ticks.length - 1] : niceTickExtent[1])) {
30549            ticks.push(extent[1]);
30550        }
30551
30552        return ticks;
30553    }
30554
30555    /*
30556* Licensed to the Apache Software Foundation (ASF) under one
30557* or more contributor license agreements.  See the NOTICE file
30558* distributed with this work for additional information
30559* regarding copyright ownership.  The ASF licenses this file
30560* to you under the Apache License, Version 2.0 (the
30561* "License"); you may not use this file except in compliance
30562* with the License.  You may obtain a copy of the License at
30563*
30564*   http://www.apache.org/licenses/LICENSE-2.0
30565*
30566* Unless required by applicable law or agreed to in writing,
30567* software distributed under the License is distributed on an
30568* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
30569* KIND, either express or implied.  See the License for the
30570* specific language governing permissions and limitations
30571* under the License.
30572*/
30573
30574    /**
30575     * Interval scale
30576     * @module echarts/scale/Interval
30577     */
30578
30579
30580    var roundNumber = round$1;
30581
30582    /**
30583     * @alias module:echarts/coord/scale/Interval
30584     * @constructor
30585     */
30586    var IntervalScale = Scale.extend({
30587
30588        type: 'interval',
30589
30590        _interval: 0,
30591
30592        _intervalPrecision: 2,
30593
30594        setExtent: function (start, end) {
30595            var thisExtent = this._extent;
30596            //start,end may be a Number like '25',so...
30597            if (!isNaN(start)) {
30598                thisExtent[0] = parseFloat(start);
30599            }
30600            if (!isNaN(end)) {
30601                thisExtent[1] = parseFloat(end);
30602            }
30603        },
30604
30605        unionExtent: function (other) {
30606            var extent = this._extent;
30607            other[0] < extent[0] && (extent[0] = other[0]);
30608            other[1] > extent[1] && (extent[1] = other[1]);
30609
30610            // unionExtent may called by it's sub classes
30611            IntervalScale.prototype.setExtent.call(this, extent[0], extent[1]);
30612        },
30613        /**
30614         * Get interval
30615         */
30616        getInterval: function () {
30617            return this._interval;
30618        },
30619
30620        /**
30621         * Set interval
30622         */
30623        setInterval: function (interval) {
30624            this._interval = interval;
30625            // Dropped auto calculated niceExtent and use user setted extent
30626            // We assume user wan't to set both interval, min, max to get a better result
30627            this._niceExtent = this._extent.slice();
30628
30629            this._intervalPrecision = getIntervalPrecision(interval);
30630        },
30631
30632        /**
30633         * @return {Array.<number>}
30634         */
30635        getTicks: function () {
30636            return intervalScaleGetTicks(
30637                this._interval, this._extent, this._niceExtent, this._intervalPrecision
30638            );
30639        },
30640
30641        /**
30642         * @param {number} data
30643         * @param {Object} [opt]
30644         * @param {number|string} [opt.precision] If 'auto', use nice presision.
30645         * @param {boolean} [opt.pad] returns 1.50 but not 1.5 if precision is 2.
30646         * @return {string}
30647         */
30648        getLabel: function (data, opt) {
30649            if (data == null) {
30650                return '';
30651            }
30652
30653            var precision = opt && opt.precision;
30654
30655            if (precision == null) {
30656                precision = getPrecisionSafe(data) || 0;
30657            }
30658            else if (precision === 'auto') {
30659                // Should be more precise then tick.
30660                precision = this._intervalPrecision;
30661            }
30662
30663            // (1) If `precision` is set, 12.005 should be display as '12.00500'.
30664            // (2) Use roundNumber (toFixed) to avoid scientific notation like '3.5e-7'.
30665            data = roundNumber(data, precision, true);
30666
30667            return addCommas(data);
30668        },
30669
30670        /**
30671         * Update interval and extent of intervals for nice ticks
30672         *
30673         * @param {number}
30673 [splitNumber = 5] Desired number of ticks
30674         * @param {number} [minInterval]
30675         * @param {number} [maxInterval]
30676         */
30677        niceTicks: function (splitNumber, minInterval, maxInterval) {
30678            splitNumber = splitNumber || 5;
30679            var extent = this._extent;
30680            var span = extent[1] - extent[0];
30681            if (!isFinite(span)) {
30682                return;
30683            }
30684            // User may set axis min 0 and data are all negative
30685            // FIXME If it needs to reverse ?
30686            if (span < 0) {
30687                span = -span;
30688                extent.reverse();
30689            }
30690
30691            var result = intervalScaleNiceTicks(
30692                extent, splitNumber, minInterval, maxInterval
30693            );
30694
30695            this._intervalPrecision = result.intervalPrecision;
30696            this._interval = result.interval;
30697            this._niceExtent = result.niceTickExtent;
30698        },
30699
30700        /**
30701         * Nice extent.
30702         * @param {Object} opt
30703         * @param {number} [opt.splitNumber = 5] Given approx tick number
30704         * @param {boolean} [opt.fixMin=false]
30705         * @param {boolean} [opt.fixMax=false]
30706         * @param {boolean} [opt.minInterval]
30707         * @param {boolean} [opt.maxInterval]
30708         */
30709        niceExtent: function (opt) {
30710            var extent = this._extent;
30711            // If extent start and end are same, expand them
30712            if (extent[0] === extent[1]) {
30713                if (extent[0] !== 0) {
30714                    // Expand extent
30715                    var expandSize = extent[0];
30716                    // In the fowllowing case
30717                    //      Axis has been fixed max 100
30718                    //      Plus data are all 100 and axis extent are [100, 100].
30719                    // Extend to the both side will cause expanded max is larger than fixed max.
30720                    // So only expand to the smaller side.
30721                    if (!opt.fixMax) {
30722                        extent[1] += expandSize / 2;
30723                        extent[0] -= expandSize / 2;
30724                    }
30725                    else {
30726                        extent[0] -= expandSize / 2;
30727                    }
30728                }
30729                else {
30730                    extent[1] = 1;
30731                }
30732            }
30733            var span = extent[1] - extent[0];
30734            // If there are no data and extent are [Infinity, -Infinity]
30735            if (!isFinite(span)) {
30736                extent[0] = 0;
30737                extent[1] = 1;
30738            }
30739
30740            this.niceTicks(opt.splitNumber, opt.minInterval, opt.maxInterval);
30741
30742            // var extent = this._extent;
30743            var interval = this._interval;
30744
30745            if (!opt.fixMin) {
30746                extent[0] = roundNumber(Math.floor(extent[0] / interval) * interval);
30747            }
30748            if (!opt.fixMax) {
30749                extent[1] = roundNumber(Math.ceil(extent[1] / interval) * interval);
30750            }
30751        }
30752    });
30753
30754    /**
30755     * @return {module:echarts/scale/Time}
30756     */
30757    IntervalScale.create = function () {
30758        return new IntervalScale();
30759    };
30760
30761    /*
30762* Licensed to the Apache Software Foundation (ASF) under one
30763* or more contributor license agreements.  See the NOTICE file
30764* distributed with this work for additional information
30765* regarding copyright ownership.  The ASF licenses this file
30766* to you under the Apache License, Version 2.0 (the
30767* "License"); you may not use this file except in compliance
30768* with the License.  You may obtain a copy of the License at
30769*
30770*   http://www.apache.org/licenses/LICENSE-2.0
30771*
30772* Unless required by applicable law or agreed to in writing,
30773* software distributed under the License is distributed on an
30774* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
30775* KIND, either express or implied.  See the License for the
30776* specific language governing permissions and limitations
30777* under the License.
30778*/
30779
30780    var STACK_PREFIX = '__ec_stack_';
30781    var LARGE_BAR_MIN_WIDTH = 0.5;
30782
30783    var LargeArr = typeof Float32Array !== 'undefined' ? Float32Array : Array;
30784
vendor: 8,296 bytes, lines 30785-30999
30785    function getSeriesStackId(seriesModel) {
30786        return seriesModel.get('stack') || STACK_PREFIX + seriesModel.seriesIndex;
30787    }
30788
30789    function getAxisKey(axis) {
30790        return axis.dim + axis.index;
30791    }
30792
30793    /**
30794     * @param {Object} opt
30795     * @param {module:echarts/coord/Axis} opt.axis Only support category axis currently.
30796     * @param {number} opt.count Positive interger.
30797     * @param {number} [opt.barWidth]
30798     * @param {number} [opt.barMaxWidth]
30799     * @param {number} [opt.barGap]
30800     * @param {number} [opt.barCategoryGap]
30801     * @return {Object} {width, offset, offsetCenter} If axis.type is not 'category', return undefined.
30802     */
30803    function getLayoutOnAxis(opt) {
30804        var params = [];
30805        var baseAxis = opt.axis;
30806        var axisKey = 'axis0';
30807
30808        if (baseAxis.type !== 'category') {
30809            return;
30810        }
30811        var bandWidth = baseAxis.getBandWidth();
30812
30813        for (var i = 0; i < opt.count || 0; i++) {
30814            params.push(defaults({
30815                bandWidth: bandWidth,
30816                axisKey: axisKey,
30817                stackId: STACK_PREFIX + i
30818            }, opt));
30819        }
30820        var widthAndOffsets = doCalBarWidthAndOffset(params);
30821
30822        var result = [];
30823        for (var i = 0; i < opt.count; i++) {
30824            var item = widthAndOffsets[axisKey][STACK_PREFIX + i];
30825            item.offsetCenter = item.offset + item.width / 2;
30826            result.push(item);
30827        }
30828
30829        return result;
30830    }
30831
30832    function prepareLayoutBarSeries(seriesType, ecModel) {
30833        var seriesModels = [];
30834        ecModel.eachSeriesByType(seriesType, function (seriesModel) {
30835            // Check series coordinate, do layout for cartesian2d only
30836            if (isOnCartesian(seriesModel) && !isInLargeMode(seriesModel)) {
30837                seriesModels.push(seriesModel);
30838            }
30839        });
30840        return seriesModels;
30841    }
30842
30843    function makeColumnLayout(barSeries) {
30844        var seriesInfoList = [];
30845        each$1(barSeries, function (seriesModel) {
30846            var data = seriesModel.getData();
30847            var cartesian = seriesModel.coordinateSystem;
30848            var baseAxis = cartesian.getBaseAxis();
30849            var axisExtent = baseAxis.getExtent();
30850            var bandWidth = baseAxis.type === 'category'
30851                ? baseAxis.getBandWidth()
30852                : (Math.abs(axisExtent[1] - axisExtent[0]) / data.count());
30853
30854            var barWidth = parsePercent$1(
30855                seriesModel.get('barWidth'), bandWidth
30856            );
30857            var barMaxWidth = parsePercent$1(
30858                seriesModel.get('barMaxWidth'), bandWidth
30859            );
30860            var barGap = seriesModel.get('barGap');
30861            var barCategoryGap = seriesModel.get('barCategoryGap');
30862
30863            seriesInfoList.push({
30864                bandWidth: bandWidth,
30865                barWidth: barWidth,
30866                barMaxWidth: barMaxWidth,
30867                barGap: barGap,
30868                barCategoryGap: barCategoryGap,
30869                axisKey: getAxisKey(baseAxis),
30870                stackId: getSeriesStackId(seriesModel)
30871            });
30872        });
30873
30874        return doCalBarWidthAndOffset(seriesInfoList);
30875    }
30876
30877    function doCalBarWidthAndOffset(seriesInfoList) {
30878        // Columns info on each category axis. Key is cartesian name
30879        var columnsMap = {};
30880
30881        each$1(seriesInfoList, function (seriesInfo, idx) {
30882            var axisKey = seriesInfo.axisKey;
30883            var bandWidth = seriesInfo.bandWidth;
30884            var columnsOnAxis = columnsMap[axisKey] || {
30885                bandWidth: bandWidth,
30886                remainedWidth: bandWidth,
30887                autoWidthCount: 0,
30888                categoryGap: '20%',
30889                gap: '30%',
30890                stacks: {}
30891            };
30892            var stacks = columnsOnAxis.stacks;
30893            columnsMap[axisKey] = columnsOnAxis;
30894
30895            var stackId = seriesInfo.stackId;
30896
30897            if (!stacks[stackId]) {
30898                columnsOnAxis.autoWidthCount++;
30899            }
30900            stacks[stackId] = stacks[stackId] || {
30901                width: 0,
30902                maxWidth: 0
30903            };
30904
30905            // Caution: In a single coordinate system, these barGrid attributes
30906            // will be shared by series. Consider that they have default values,
30907            // only the attributes set on the last series will work.
30908            // Do not change this fact unless there will be a break change.
30909
30910            // TODO
30911            var barWidth = seriesInfo.barWidth;
30912            if (barWidth && !stacks[stackId].width) {
30913                // See #6312, do not restrict width.
30914                stacks[stackId].width = barWidth;
30915                barWidth = Math.min(columnsOnAxis.remainedWidth, barWidth);
30916                columnsOnAxis.remainedWidth -= barWidth;
30917            }
30918
30919            var barMaxWidth = seriesInfo.barMaxWidth;
30920            barMaxWidth && (stacks[stackId].maxWidth = barMaxWidth);
30921            var barGap = seriesInfo.barGap;
30922            (barGap != null) && (columnsOnAxis.gap = barGap);
30923            var barCategoryGap = seriesInfo.barCategoryGap;
30924            (barCategoryGap != null) && (columnsOnAxis.categoryGap = barCategoryGap);
30925        });
30926
30927        var result = {};
30928
30929        each$1(columnsMap, function (columnsOnAxis, coordSysName) {
30930
30931            result[coordSysName] = {};
30932
30933            var stacks = columnsOnAxis.stacks;
30934            var bandWidth = columnsOnAxis.bandWidth;
30935            var categoryGap = parsePercent$1(columnsOnAxis.categoryGap, bandWidth);
30936            var barGapPercent = parsePercent$1(columnsOnAxis.gap, 1);
30937
30938            var remainedWidth = columnsOnAxis.remainedWidth;
30939            var autoWidthCount = columnsOnAxis.autoWidthCount;
30940            var autoWidth = (remainedWidth - categoryGap)
30941                / (autoWidthCount + (autoWidthCount - 1) * barGapPercent);
30942            autoWidth = Math.max(autoWidth, 0);
30943
30944            // Find if any auto calculated bar exceeded maxBarWidth
30945            each$1(stacks, function (column, stack) {
30946                var maxWidth = column.maxWidth;
30947                if (maxWidth && maxWidth < autoWidth) {
30948                    maxWidth = Math.min(maxWidth, remainedWidth);
30949                    if (column.width) {
30950                        maxWidth = Math.min(maxWidth, column.width);
30951                    }
30952                    remainedWidth -= maxWidth;
30953                    column.width = maxWidth;
30954                    autoWidthCount--;
30955                }
30956            });
30957
30958            // Recalculate width again
30959            autoWidth = (remainedWidth - categoryGap)
30960                / (autoWidthCount + (autoWidthCount - 1) * barGapPercent);
30961            autoWidth = Math.max(autoWidth, 0);
30962
30963            var widthSum = 0;
30964            var lastColumn;
30965            each$1(stacks, function (column, idx) {
30966                if (!column.width) {
30967                    column.width = autoWidth;
30968                }
30969                lastColumn = column;
30970                widthSum += column.width * (1 + barGapPercent);
30971            });
30972            if (lastColumn) {
30973                widthSum -= lastColumn.width * barGapPercent;
30974            }
30975
30976            var offset = -widthSum / 2;
30977            each$1(stacks, function (column, stackId) {
30978                result[coordSysName][stackId] = result[coordSysName][stackId] || {
30979                    offset: offset,
30980                    width: column.width
30981                };
30982
30983                offset += column.width * (1 + barGapPercent);
30984            });
30985        });
30986
30987        return result;
30988    }
30989
30990    /**
30991     * @param {Object} barWidthAndOffset The result of makeColumnLayout
30992     * @param {module:echarts/coord/Axis} axis
30993     * @param {module:echarts/model/Series} [seriesModel] If not provided, return all.
30994     * @return {Object} {stackId: {offset, width}} or {offset, width} if seriesModel provided.
30995     */
30996    function retrieveColumnLayout(barWidthAndOffset, axis, seriesModel) {
30997        if (barWidthAndOffset && axis) {
30998            var result = barWidthAndOffset[getAxisKey(axis)];
30999            if (result != null && seriesModel != null) {
31000                result = result[getSeriesStackId(seriesModel)];
31001            }
31002            return result;
31003        }
31004    }
31005
31006    /**
31007     * @param {string} seriesType
31008     * @param {module:echarts/model/Global} ecModel
31009     */
31010    function layout(seriesType, ecModel) {
31011
31012        var seriesModels = prepareLayoutBarSeries(seriesType, ecModel);
31013        var barWidthAndOffset = makeColumnLayout(seriesModels);
31014
31015        var lastStackCoords = {};
31016        each$1(seriesModels, function (seriesModel) {
31017
31018            var data = seriesModel.getData();
31019            var cartesian = seriesModel.coordinateSystem;
31020            var baseAxis = cartesian.getBaseAxis();
31021
31022            var stackId = getSeriesStackId(seriesModel);
31023            var columnLayoutInfo = barWidthAndOffset[getAxisKey(baseAxis)][stackId];
31024            var columnOffset = columnLayoutInfo.offset;
31025            var columnWidth = columnLayoutInfo.width;
31026            var valueAxis = cartesian.getOtherAxis(baseAxis);
31027
31028            var barMinHeight = seriesModel.get('barMinHeight') || 0;
31029
31030            lastStackCoords[stackId] = lastStackCoords[stackId] || [];
31031            data.setLayout({
31032                offset: columnOffset,
31033                size: columnWidth
31034            });
31035
31036            var valueDim = data.mapDimension(valueAxis.dim);
31037            var baseDim = data.mapDimension(baseAxis.dim);
31038            var stacked = isDimensionStacked(data, valueDim /*, baseDim*/);
31039            var isValueAxisH = valueAxis.isHorizontal();
31040
31041            var valueAxisStart = getValueAxisStart(baseAxis, valueAxis, stacked);
31042
31043            for (var idx = 0, len = data.count(); idx < len; idx++) {
31044                var value = data.get(valueDim, idx);
31045                var baseValue = data.get(baseDim, idx);
31046
31047                if (isNaN(value)) {
31048                    continue;
31049                }
31050
31051                var sign = value >= 0 ? 'p' : 'n';
31052                var baseCoord = valueAxisStart;
31053
31054                // Because of the barMinHeight, we can not use the value in
31055                // stackResultDimension directly.
31056                if (stacked) {
31057                    // Only ordinal axis can be stacked.
31058                    if (!lastStackCoords[stackId][baseValue]) {
31059                        lastStackCoords[stackId][baseValue] = {
31060                            p: valueAxisStart, // Positive stack
31061                            n: valueAxisStart  // Negative stack
31062                        };
31063                    }
31064                    // Should also consider #4243
31065                    baseCoord = lastStackCoords[stackId][baseValue][sign];
31066                }
31067
31068                var x;
31069                var y;
31070                var width;
31071                var height;
31072
31073                if (isValueAxisH) {
31074                    var coord = cartesian.dataToPoint([value, baseValue]);
31075                    x = baseCoord;
31076                    y = coord[1] + columnOffset;
31077                    width = coord[0] - valueAxisStart;
31078                    height = columnWidth;
31079
31080                    if (Math.abs(width) < barMinHeight) {
31081                        width = (width < 0 ? -1 : 1) * barMinHeight;
31082                    }
31083                    stacked && (lastStackCoords[stackId][baseValue][sign] += width);
31084                }
31085                else {
31086                    var coord = cartesian.dataToPoint([baseValue, value]);
31087                    x = coord[0] + columnOffset;
31088                    y = baseCoord;
31089                    width = columnWidth;
31090                    height = coord[1] - valueAxisStart;
31091
31092                    if (Math.abs(height) < barMinHeight) {
31093                        // Include zero to has a positive bar
31094                        height = (height <= 0 ? -1 : 1) * barMinHeight;
31095                    }
31096                    stacked && (lastStackCoords[stackId][baseValue][sign] += height);
31097                }
31098
31099                data.setItemLayout(idx, {
31100                    x: x,
31101                    y: y,
31102                    width: width,
31103                    height: height
31104                });
31105            }
31106
31107        }, this);
31108    }
31109
31110// TODO: Do not support stack in large mode yet.
31111    var largeLayout = {
31112
31113        seriesType: 'bar',
31114
31115        plan: createRenderPlanner(),
31116
31117        reset: function (seriesModel) {
31118            if (!isOnCartesian(seriesModel) || !isInLargeMode(seriesModel)) {
31119                return;
31120            }
31121
31122            var data = seriesModel.getData();
31123            var cartesian = seriesModel.coordinateSystem;
31124            var baseAxis = cartesian.getBaseAxis();
31125            var valueAxis = cartesian.getOtherAxis(baseAxis);
31126            var valueDim = data.mapDimension(valueAxis.dim);
31127            var baseDim = data.mapDimension(baseAxis.dim);
31128            var valueAxisHorizontal = valueAxis.isHorizontal();
31129            var valueDimIdx = valueAxisHorizontal ? 0 : 1;
31130
31131            var barWidth = retrieveColumnLayout(
31132                makeColumnLayout([seriesModel]), baseAxis, seriesModel
31133            ).width;
31134            if (!(barWidth > LARGE_BAR_MIN_WIDTH)) { // jshint ignore:line
31135                barWidth = LARGE_BAR_MIN_WIDTH;
31136            }
31137
31138            return {progress: progress};
31139
31140            function progress(params, data) {
31141                var largePoints = new LargeArr(params.count * 2);
31142                var dataIndex;
31143                var coord = [];
31144                var valuePair = [];
31145                var offset = 0;
31146
31147                while ((dataIndex = params.next()) != null) {
31148                    valuePair[valueDimIdx] = data.get(valueDim, dataIndex);
31149                    valuePair[1 - valueDimIdx] = data.get(baseDim, dataIndex);
31150
31151                    coord = cartesian.dataToPoint(valuePair, null, coord);
31152                    largePoints[offset++] = coord[0];
31153                    largePoints[offset++] = coord[1];
31154                }
31155
31156                data.setLayout({
31157                    largePoints: largePoints,
31158                    barWidth: barWidth,
31159                    valueAxisStart: getValueAxisStart(baseAxis, valueAxis, false),
31160                    valueAxisHorizontal: valueAxisHorizontal
31161                });
31162            }
31163        }
31164    };
31165
31166    function isOnCartesian(seriesModel) {
31167        return seriesModel.coordinateSystem && seriesModel.coordinateSystem.type === 'cartesian2d';
31168    }
31169
31170    function isInLargeMode(seriesModel) {
31171        return seriesModel.pipelineContext && seriesModel.pipelineContext.large;
31172    }
31173
31174    function getValueAxisStart(baseAxis, valueAxis, stacked) {
31175        return (
31176            indexOf(baseAxis.getAxesOnZeroOf(), valueAxis) >= 0
31177            || stacked
31178        )
31179            ? valueAxis.toGlobalCoord(valueAxis.dataToCoord(0))
31180            : valueAxis.getGlobalExtent()[0];
31181    }
31182
31183    /*
31184* Licensed to the Apache Software Foundation (ASF) under one
31185* or more contributor license agreements.  See the NOTICE file
31186* distributed with this work for additional information
31187* regarding copyright ownership.  The ASF licenses this file
31188* to you under the Apache License, Version 2.0 (the
31189* "License"); you may not use this file except in compliance
31190* with the License.  You may obtain a copy of the License at
31191*
31192*   http://www.apache.org/licenses/LICENSE-2.0
31193*
31194* Unless required by applicable law or agreed to in writing,
31195* software distributed under the License is distributed on an
31196* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
31197* KIND, either express or implied.  See the License for the
31198* specific language governing permissions and limitations
31199* under the License.
31200*/
31201
31202    /*
31203* The `scaleLevels` references to d3.js. The use of the source
31204* code of this file is also subject to the terms and consitions
31205* of its license (BSD-3Clause, see <echarts/src/licenses/LICENSE-d3>).
31206*/
31207
31208// [About UTC and local time zone]:
31209// In most cases, `number.parseDate` will treat input data string as local time
31210// (except time zone is specified in time string). And `format.formateTime` returns
31211// local time by default. option.useUTC is false by default. This design have
31212// concidered these common case:
31213// (1) Time that is persistent in server is in UTC, but it is needed to be diplayed
31214// in local time by default.
31215// (2) By default, the input data string (e.g., '2011-01-02') should be displayed
31216// as its original time, without any time difference.
31217
31218    var intervalScaleProto = IntervalScale.prototype;
31219
31220    var mathCeil = Math.ceil;
31221    var mathFloor = Math.floor;
31222    var ONE_SECOND = 1000;
31223    var ONE_MINUTE = ONE_SECOND * 60;
31224    var ONE_HOUR = ONE_MINUTE * 60;
31225    var ONE_DAY = ONE_HOUR * 24;
31226
31227// FIXME 公用?
31228    var bisect = function (a, x, lo, hi) {
31229        while (lo < hi) {
31230            var mid = lo + hi >>> 1;
31231            if (a[mid][1] < x) {
31232                lo = mid + 1;
31233            }
31234            else {
31235                hi  = mid;
31236            }
31237        }
31238        return lo;
31239    };
31240
31241    /**
31242     * @alias module:echarts/coord/scale/Time
31243     * @constructor
31244     */
31245    var TimeScale = IntervalScale.extend({
31246        type: 'time',
31247
31248        /**
31249         * @override
31250         */
31251        getLabel: function (val) {
31252            var stepLvl = this._stepLvl;
31253
31254            var date = new Date(val);
31255
31256            return formatTime(stepLvl[0], date, this.getSetting('useUTC'));
31257        },
31258
31259        /**
31260         * @override
31261         */
31262        niceExtent: function (opt) {
31263            var extent = this._extent;
31264            // If extent start and end are same, expand them
31265            if (extent[0] === extent[1]) {
31266                // Expand extent
31267                extent[0] -= ONE_DAY;
31268                extent[1] += ONE_DAY;
31269            }
31270            // If there are no data and extent are [Infinity, -Infinity]
31271            if (extent[1] === -Infinity && extent[0] === Infinity) {
31272                var d = new Date();
31273                extent[1] = +new Date(d.getFullYear(), d.getMonth(), d.getDate());
31274                extent[0] = extent[1] - ONE_DAY;
31275            }
31276
31277            this.niceTicks(opt.splitNumber, opt.minInterval, opt.maxInterval);
31278
31279            // var extent = this._extent;
31280            var interval = this._interval;
31281
31282            if (!opt.fixMin) {
31283                extent[0] = round$1(mathFloor(extent[0] / interval) * interval);
31284            }
31285            if (!opt.fixMax) {
31286                extent[1] = round$1(mathCeil(extent[1] / interval) * interval);
31287            }
31288        },
31289
31290        /**
31291         * @override
31292         */
31293        niceTicks: function (approxTickNum, minInterval, maxInterval) {
31294            approxTickNum = approxTickNum || 10;
31295
31296            var extent = this._extent;
31297            var span = extent[1] - extent[0];
31298            var approxInterval = span / approxTickNum;
31299
31300            if (minInterval != null && approxInterval < minInterval) {
31301                approxInterval = minInterval;
31302            }
31303            if (maxInterval != null && approxInterval > maxInterval) {
31304                approxInterval = maxInterval;
31305            }
31306
31307            var scaleLevelsLen = scaleLevels.length;
31308            var idx = bisect(scaleLevels, approxInterval, 0, scaleLevelsLen);
31309
31310            var level = scaleLevels[Math.min(idx, scaleLevelsLen - 1)];
31311            var interval = level[1];
31312            // Same with interval scale if span is much larger than 1 year
31313            if (level[0] === 'year') {
31314                var yearSpan = span / interval;
31315
31316                // From "Nice Numbers for Graph Labels" of Graphic Gems
31317                // var niceYearSpan = numberUtil.nice(yearSpan, false);
31318                var yearStep = nice(yearSpan / approxTickNum, true);
31319
31320                interval *= yearStep;
31321            }
31322
31323            var timezoneOffset = this.getSetting('useUTC')
31324                ? 0 : (new Date(+extent[0] || +extent[1])).getTimezoneOffset() * 60 * 1000;
31325            var niceExtent = [
31326                Math.round(mathCeil((extent[0] - timezoneOffset) / interval) * interval + timezoneOffset),
31327                Math.round(mathFloor((extent[1] - timezoneOffset) / interval) * interval + timezoneOffset)
31328            ];
31329
31330            fixExtent(niceExtent, extent);
31331
31332            this._stepLvl = level;
31333            // Interval will be used in getTicks
31334            this._interval = interval;
31335            this._niceExtent = niceExtent;
31336        },
31337
31338        parse: function (val) {
31339            // val might be float.
31340            return +parseDate(val);
31341        }
31342    });
31343
31344    each$1(['contain', 'normalize'], function (methodName) {
31345        TimeScale.prototype[methodName] = function (val) {
31346            return intervalScaleProto[methodName].call(this, this.parse(val));
31347        };
31348    });
31349
31350// Steps from d3, see the license statement at the top of this file.
31351    var scaleLevels = [
31352        // Format              interval
31353        ['hh:mm:ss', ONE_SECOND],          // 1s
31354        ['hh:mm:ss', ONE_SECOND * 5],      // 5s
31355        ['hh:mm:ss', ONE_SECOND * 10],     // 10s
31356        ['hh:mm:ss', ONE_SECOND * 15],     // 15s
31357        ['hh:mm:ss', ONE_SECOND * 30],     // 30s
31358        ['hh:mm\nMM-dd', ONE_MINUTE],      // 1m
31359        ['hh:mm\nMM-dd', ONE_MINUTE * 5],  // 5m
31360        ['hh:mm\nMM-dd', ONE_MINUTE * 10], // 10m
31361        ['hh:mm\nMM-dd', ONE_MINUTE * 15], // 15m
31362        ['hh:mm\nMM-dd', ONE_MINUTE * 30], // 30m
31363        ['hh:mm\nMM-dd', ONE_HOUR],        // 1h
31364        ['hh:mm\nMM-dd', ONE_HOUR * 2],    // 2h
31365        ['hh:mm\nMM-dd', ONE_HOUR * 6],    // 6h
31366        ['hh:mm\nMM-dd', ONE_HOUR * 12],   // 12h
31367        ['MM-dd\nyyyy', ONE_DAY],          // 1d
31368        ['MM-dd\nyyyy', ONE_DAY * 2],      // 2d
31369        ['MM-dd\nyyyy', ONE_DAY * 3],      // 3d
31370        ['MM-dd\nyyyy', ONE_DAY * 4],      // 4d
31371        ['MM-dd\nyyyy', ONE_DAY * 5],      // 5d
31372        ['MM-dd\nyyyy', ONE_DAY * 6],      // 6d
31373        ['week', ONE_DAY * 7],             // 7d
31374        ['MM-dd\nyyyy', ONE_DAY * 10],     // 10d
31375        ['week', ONE_DAY * 14],            // 2w
31376        ['week', ONE_DAY * 21],            // 3w
31377        ['month', ONE_DAY * 31],           // 1M
31378        ['week', ONE_DAY * 42],            // 6w
31379        ['month', ONE_DAY * 62],           // 2M
31380        ['week', ONE_DAY * 42],            // 10w
31381        ['quarter', ONE_DAY * 380 / 4],    // 3M
31382        ['month', ONE_DAY * 31 * 4],       // 4M
31383        ['month', ONE_DAY * 31 * 5],       // 5M
31384        ['half-year', ONE_DAY * 380 / 2],  // 6M
31385        ['month', ONE_DAY * 31 * 8],       // 8M
31386        ['month', ONE_DAY * 31 * 10],      // 10M
31387        ['year', ONE_DAY * 380]            // 1Y
31388    ];
31389
31390    /**
31391     * @param {module:echarts/model/Model}
31392     * @return {module:echarts/scale/Time}
31393     */
31394    TimeScale.create = function (model) {
31395        return new TimeScale({useUTC: model.ecModel.get('useUTC')});
31396    };
31397
31398    /*
31399* Licensed to the Apache Software Foundation (ASF) under one
31400* or more contributor license agreements.  See the NOTICE file
31401* distributed with this work for additional information
31402* regarding copyright ownership.  The ASF licenses this file
31403* to you under the Apache License, Version 2.0 (the
31404* "License"); you may not use this file except in compliance
31405* with the License.  You may obtain a copy of the License at
31406*
31407*   http://www.apache.org/licenses/LICENSE-2.0
31408*
31409* Unless required by applicable law or agreed to in writing,
31410* software distributed under the License is distributed on an
31411* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
31412* KIND, either express or implied.  See the License for the
31413* specific language governing permissions and limitations
31414* under the License.
31415*/
31416
31417    /**
31418     * Log scale
31419     * @module echarts/scale/Log
31420     */
31421
31422// Use some method of IntervalScale
31423    var scaleProto$1 = Scale.prototype;
31424    var intervalScaleProto$1 = IntervalScale.prototype;
31425
31426    var getPrecisionSafe$1 = getPrecisionSafe;
31427    var roundingErrorFix = round$1;
31428
31429    var mathFloor$1 = Math.floor;
31430    var mathCeil$1 = Math.ceil;
31431    var mathPow$1 = Math.pow;
31432
31433    var mathLog = Math.log;
31434
31435    var LogScale = Scale.extend({
31436
31437        type: 'log',
31438
31439        base: 10,
31440
31441        $constructor: function () {
31442            Scale.apply(this, arguments);
31443            this._originalScale = new IntervalScale();
31444        },
31445
31446        /**
31447         * @return {Array.<number>}
31448         */
31449        getTicks: function () {
31450            var originalScale = this._originalScale;
31451            var extent = this._extent;
31452            var originalExtent = originalScale.getExtent();
31453
31454            return map(intervalScaleProto$1.getTicks.call(this), function (val) {
31455                var powVal = round$1(mathPow$1(this.base, val));
31456
31457                // Fix #4158
31458                powVal = (val === extent[0] && originalScale.__fixMin)
31459                    ? fixRoundingError(powVal, originalExtent[0])
31460                    : powVal;
31461                powVal = (val === extent[1] && originalScale.__fixMax)
31462                    ? fixRoundingError(powVal, originalExtent[1])
31463                    : powVal;
31464
31465                return powVal;
31466            }, this);
31467        },
31468
31469        /**
31470         * @param {number} val
31471         * @return {string}
31472         */
31473        getLabel: intervalScaleProto$1.getLabel,
31474
31475        /**
31476         * @param  {number} val
31477         * @return {number}
31478         */
31479        scale: function (val) {
31480            val = scaleProto$1.scale.call(this, val);
31481            return mathPow$1(this.base, val);
31482        },
31483
31484        /**
31485         * @param {number} start
31486         * @param {number} end
31487         */
31488        setExtent: function (start, end) {
31489            var base = this.base;
31490            start = mathLog(start) / mathLog(base);
31491            end = mathLog(end) / mathLog(base);
31492            intervalScaleProto$1.setExtent.call(this, start, end);
31493        },
31494
31495        /**
31496         * @return {number} end
31497         */
31498        getExtent: function () {
31499            var base = this.base;
31500            var extent = scaleProto$1.getExtent.call(this);
31501            extent[0] = mathPow$1(base, extent[0]);
31502            extent[1] = mathPow$1(base, extent[1]);
31503
31504            // Fix #4158
31505            var originalScale = this._originalScale;
31506            var originalExtent = originalScale.getExtent();
31507            originalScale.__fixMin && (extent[0] = fixRoundingError(extent[0], originalExtent[0]));
31508            originalScale.__fixMax && (extent[1] = fixRoundingError(extent[1], originalExtent[1]));
31509
31510            return extent;
31511        },
31512
31513        /**
31514         * @param  {Array.<number>} extent
31515         */
31516        unionExtent: function (extent) {
31517            this._originalScale.unionExtent(extent);
31518
31519            var base = this.base;
31520            extent[0] = mathLog(extent[0]) / mathLog(base);
31521            extent[1] = mathLog(extent[1]) / mathLog(base);
31522            scaleProto$1.unionExtent.call(this, extent);
31523        },
31524
31525        /**
31526         * @override
31527         */
31528        unionExtentFromData: function (data, dim) {
31529            // TODO
31530            // filter value that <= 0
31531            this.unionExtent(data.getApproximateExtent(dim));
31532        },
31533
31534        /**
31535         * Update interval and extent of intervals for nice ticks
31536         * @param  {number} [approxTickNum = 10] Given approx tick number
31537         */
31538        niceTicks: function (approxTickNum) {
31539            approxTickNum = approxTickNum || 10;
31540            var extent = this._extent;
31541            var span = extent[1] - extent[0];
31542            if (span === Infinity || span <= 0) {
31543                return;
31544            }
31545
31546            var interval = quantity(span);
31547            var err = approxTickNum / span * interval;
31548
31549            // Filter ticks to get closer to the desired count.
31550            if (err <= 0.5) {
31551                interval *= 10;
31552            }
31553
31554            // Interval should be integer
31555            while (!isNaN(interval) && Math.abs(interval) < 1 && Math.abs(interval) > 0) {
31556                interval *= 10;
31557            }
31558
31559            var niceExtent = [
31560                round$1(mathCeil$1(extent[0] / interval) * interval),
31561                round$1(mathFloor$1(extent[1] / interval) * interval)
31562            ];
31563
31564            this._interval = interval;
31565            this._niceExtent = niceExtent;
31566        },
31567
31568        /**
31569         * Nice extent.
31570         * @override
31571         */
31572        niceExtent: function (opt) {
31573            intervalScaleProto$1.niceExtent.call(this, opt);
31574
31575            var originalScale = this._originalScale;
31576            originalScale.__fixMin = opt.fixMin;
31577            originalScale.__fixMax = opt.fixMax;
31578        }
31579
31580    });
31581
31582    each$1(['contain', 'normalize'], function (methodName) {
31583        LogScale.prototype[methodName] = function (val) {
31584            val = mathLog(val) / mathLog(this.base);
31585            return scaleProto$1[methodName].call(this, val);
31586        };
31587    });
31588
31589    LogScale.create = function () {
31590        return new LogScale();
31591    };
31592
31593    function fixRoundingError(val, originalVal) {
31594        return roundingErrorFix(val, getPrecisionSafe$1(originalVal));
31595    }
31596
31597    /*
31598* Licensed to the Apache Software Foundation (ASF) under one
31599* or more contributor license agreements.  See the NOTICE file
31600* distributed with this work for additional information
31601* regarding copyright ownership.  The ASF licenses this file
31602* to you under the Apache License, Version 2.0 (the
31603* "License"); you may not use this file except in compliance
31604* with the License.  You may obtain a copy of the License at
31605*
31606*   http://www.apache.org/licenses/LICENSE-2.0
31607*
31608* Unless required by applicable law or agreed to in writing,
31609* software distributed under the License is distributed on an
31610* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
31611* KIND, either express or implied.  See the License for the
31612* specific language governing permissions and limitations
31613* under the License.
31614*/
31615
31616    /**
31617     * Get axis scale extent before niced.
31618     * Item of returned array can only be number (including Infinity and NaN).
31619     */
31620    function getScaleExtent(scale, model) {
31621        var scaleType = scale.type;
31622
31623        var min = model.getMin();
31624        var max = model.getMax();
31625        var fixMin = min != null;
31626        var fixMax = max != null;
31627        var originalExtent = scale.getExtent();
31628
31629        var axisDataLen;
31630        var boundaryGap;
31631        var span;
31632        if (scaleType === 'ordinal') {
31633            axisDataLen = model.getCategories().length;
31634        }
31635        else {
31636            boundaryGap = model.get('boundaryGap');
31637            if (!isArray(boundaryGap)) {
31638                boundaryGap = [boundaryGap || 0, boundaryGap || 0];
31639            }
31640            if (typeof boundaryGap[0] === 'boolean') {
31641                if (__DEV__) {
31642                    console.warn('Boolean type for boundaryGap is only '
31643                        + 'allowed for ordinal axis. Please use string in '
31644                        + 'percentage instead, e.g., "20%". Currently, '
31645                        + 'boundaryGap is set to be 0.');
31646                }
31647                boundaryGap = [0, 0];
31648            }
31649            boundaryGap[0] = parsePercent$1(boundaryGap[0], 1);
31650            boundaryGap[1] = parsePercent$1(boundaryGap[1], 1);
31651            span = (originalExtent[1] - originalExtent[0])
31652                || Math.abs(originalExtent[0]);
31653        }
31654
31655        // Notice: When min/max is not set (that is, when there are null/undefined,
31656        // which is the most common case), these cases should be ensured:
31657        // (1) For 'ordinal', show all axis.data.
31658        // (2) For others:
31659        //      + `boundaryGap` is applied (if min/max set, boundaryGap is
31660        //      disabled).
31661        //      + If `needCrossZero`, min/max should be zero, otherwise, min/max should
31662        //      be the result that originalExtent enlarged by boundaryGap.
31663        // (3) If no data, it should be ensured that `scale.setBlank` is set.
31664
31665        // FIXME
31666        // (1) When min/max is 'dataMin' or 'dataMax', should boundaryGap be able to used?
31667        // (2) When `needCrossZero` and all data is positive/negative, should it be ensured
31668        // that the results processed by boundaryGap are positive/negative?
31669
31670        if (min == null) {
31671            min = scaleType === 'ordinal'
31672                ? (axisDataLen ? 0 : NaN)
31673                : originalExtent[0] - boundaryGap[0] * span;
31674        }
31675        if (max == null) {
31676            max = scaleType === 'ordinal'
31677                ? (axisDataLen ? axisDataLen - 1 : NaN)
31678                : originalExtent[1] + boundaryGap[1] * span;
31679        }
31680
31681        if (min === 'dataMin') {
31682            min = originalExtent[0];
31683        }
31684        else if (typeof min === 'function') {
31685            min = min({
31686                min: originalExtent[0],
31687                max: originalExtent[1]
31688            });
31689        }
31690
31691        if (max === 'dataMax') {
31692            max = originalExtent[1];
31693        }
31694        else if (typeof max === 'function') {
31695            max = max({
31696                min: originalExtent[0],
31697                max: originalExtent[1]
31698            });
31699        }
31700
31701        (min == null || !isFinite(min)) && (min = NaN);
31702        (max == null || !isFinite(max)) && (max = NaN);
31703
31704        scale.setBlank(
31705            eqNaN(min)
31706            || eqNaN(max)
31707            || (scaleType === 'ordinal' && !scale.getOrdinalMeta().categories.length)
31708        );
31709
31710        // Evaluate if axis needs cross zero
31711        if (model.getNeedCrossZero()) {
31712            // Axis is over zero and min is not set
31713            if (min > 0 && max > 0 && !fixMin) {
31714                min = 0;
31715            }
31716            // Axis is under zero and max is not set
31717            if (min < 0 && max < 0 && !fixMax) {
31718                max = 0;
31719            }
31720        }
31721
31722        // If bars are placed on a base axis of type time or interval account for axis boundary overflow and current axis
31723        // is base axis
31724        // FIXME
31725        // (1) Consider support value axis, where below zero and axis `onZero` should be handled properly.
31726        // (2) Refactor the logic with `barGrid`. Is it not need to `makeBarWidthAndOffsetInfo` twice with different extent?
31727        //     Should not depend on series type `bar`?
31728        // (3) Fix that might overlap when using dataZoom.
31729        // (4) Consider other chart types using `barGrid`?
31730        // See #6728, #4862, `test/bar-overflow-time-plot.html`
31731        var ecModel = model.ecModel;
31732        if (ecModel && (scaleType === 'time' /*|| scaleType === 'interval' */)) {
31733            var barSeriesModels = prepareLayoutBarSeries('bar', ecModel);
31734            var isBaseAxisAndHasBarSeries;
31735
31736            each$1(barSeriesModels, function (seriesModel) {
31737                isBaseAxisAndHasBarSeries |= seriesModel.getBaseAxis() === model.axis;
31738            });
31739
31740            if (isBaseAxisAndHasBarSeries) {
31741                // Calculate placement of bars on axis
31742                var barWidthAndOffset = makeColumnLayout(barSeriesModels);
31743
31744                // Adjust axis min and max to account for overflow
31745                var adjustedScale = adjustScaleForOverflow(min, max, model, barWidthAndOffset);
31746                min = adjustedScale.min;
31747                max = adjustedScale.max;
31748            }
31749        }
31750
31751        return [min, max];
31752    }
31753
31754    function adjustScaleForOverflow(min, max, model, barWidthAndOffset) {
31755
31756        // Get Axis Length
31757        var axisExtent = model.axis.getExtent();
31758        var axisLength = axisExtent[1] - axisExtent[0];
31759
31760        // Get bars on current base axis and calculate min and max overflow
31761        var barsOnCurrentAxis = retrieveColumnLayout(barWidthAndOffset, model.axis);
31762        if (barsOnCurrentAxis === undefined) {
31763            return {min: min, max: max};
31764        }
31765
31766        var minOverflow = Infinity;
31767        each$1(barsOnCurrentAxis, function (item) {
31768            minOverflow = Math.min(item.offset, minOverflow);
31769        });
31770        var maxOverflow = -Infinity;
31771        each$1(barsOnCurrentAxis, function (item) {
31772            maxOverflow = Math.max(item.offset + item.width, maxOverflow);
31773        });
31774        minOverflow = Math.abs(minOverflow);
31775        maxOverflow = Math.abs(maxOverflow);
31776        var totalOverFlow = minOverflow + maxOverflow;
31777
31778        // Calulate required buffer based on old range and overflow
31779        var oldRange = max - min;
31780        var oldRangePercentOfNew = (1 - (minOverflow + maxOverflow) / axisLength);
31781        var overflowBuffer = ((oldRange / oldRangePercentOfNew) - oldRange);
31782
31783        max += overflowBuffer * (maxOverflow / totalOverFlow);
31784        min -= overflowBuffer * (minOverflow / totalOverFlow);
31785
31786        return {min: min, max: max};
31787    }
31788
31789    function niceScaleExtent(scale, model) {
31790        var extent = getScaleExtent(scale, model);
31791        var fixMin = model.getMin() != null;
31792        var fixMax = model.getMax() != null;
31793        var splitNumber = model.get('splitNumber');
31794
31795        if (scale.type === 'log') {
31796            scale.base = model.get('logBase');
31797        }
31798
31799        var scaleType = scale.type;
31800        scale.setExtent(extent[0], extent[1]);
31801        scale.niceExtent({
31802            splitNumber: splitNumber,
31803            fixMin: fixMin,
31804            fixMax: fixMax,
31805            minInterval: (scaleType === 'interval' || scaleType === 'time')
31806                ? model.get('minInterval') : null,
31807            maxInterval: (scaleType === 'interval' || scaleType === 'time')
31808                ? model.get('maxInterval') : null
31809        });
31810
31811        // If some one specified the min, max. And the default calculated interval
31812        // is not good enough. He can specify the interval. It is often appeared
31813        // in angle axis with angle 0 - 360. Interval calculated in interval scale is hard
31814        // to be 60.
31815        // FIXME
31816        var interval = model.get('interval');
31817        if (interval != null) {
31818            scale.setInterval && scale.setInterval(interval);
31819        }
31820    }
31821
31822    /**
31823     * @param {module:echarts/model/Model} model
31824     * @param {string} [axisType] Default retrieve from model.type
31825     * @return {module:echarts/scale/*}
31826     */
31827    function createScaleByModel(model, axisType) {
31828        axisType = axisType || model.get('type');
31829        if (axisType) {
31830            switch (axisType) {
31831                // Buildin scale
31832                case 'category':
31833                    return new OrdinalScale(
31834                        model.getOrdinalMeta
31835                            ? model.getOrdinalMeta()
31836                            : model.getCategories(),
31837                        [Infinity, -Infinity]
31838                    );
31839                case 'value':
31840                    return new IntervalScale();
31841                // Extended scale, like time and log
31842                default:
31843                    return (Scale.getClass(axisType) || IntervalScale).create(model);
31844            }
31845        }
31846    }
31847
31848    /**
31849     * Check if the axis corss 0
31850     */
31851    function ifAxisCrossZero(axis) {
31852        var dataExtent = axis.scale.getExtent();
31853        var min = dataExtent[0];
31854        var max = dataExtent[1];
31855        return !((min > 0 && max > 0) || (min < 0 && max < 0));
31856    }
31857
31858    /**
31859     * @param {module:echarts/coord/Axis} axis
31860     * @return {Function} Label formatter function.
31861     *         param: {number} tickValue,
31862     *         param: {number} idx, the index in all ticks.
31863     *                         If category axis, this param is not requied.
31864     *         return: {string} label string.
31865     */
31866    function makeLabelFormatter(axis) {
31867        var labelFormatter = axis.getLabelModel().get('formatter');
31868        var categoryTickStart = axis.type === 'category' ? axis.scale.getExtent()[0] : null;
31869
31870        if (typeof labelFormatter === 'string') {
31871            labelFormatter = (function (tpl) {
31872                return function (val) {
31873                    return tpl.replace('{value}', val != null ? val : '');
31874                };
31875            })(labelFormatter);
31876            // Consider empty array
31877            return labelFormatter;
31878        }
31879        else if (typeof labelFormatter === 'function') {
31880            return function (tickValue, idx) {
31881                // The original intention of `idx` is "the index of the tick in all ticks".
31882                // But the previous implementation of category axis do not consider the
31883                // `axisLabel.interval`, which cause that, for example, the `interval` is
31884                // `1`, then the ticks "name5", "name7", "name9" are displayed, where the
31885                // corresponding `idx` are `0`, `2`, `4`, but not `0`, `1`, `2`. So we keep
31886                // the definition here for back compatibility.
31887                if (categoryTickStart != null) {
31888                    idx = tickValue - categoryTickStart;
31889                }
31890                return labelFormatter(getAxisRawValue(axis, tickValue), idx);
31891            };
31892        }
31893        else {
31894            return function (tick) {
31895                return axis.scale.getLabel(tick);
31896            };
31897        }
31898    }
31899
31900    function getAxisRawValue(axis, value) {
31901        // In category axis with data zoom, tick is not the original
31902        // index of axis.data. So tick should not be exposed to user
31903        // in category axis.
31904        return axis.type === 'category' ? axis.scale.getLabel(value) : value;
31905    }
31906
31907    /**
31908     * @param {module:echarts/coord/Axis} axis
31909     * @return {module:zrender/core/BoundingRect} Be null/undefined if no labels.
31910     */
31911    function estimateLabelUnionRect(axis) {
31912        var axisModel = axis.model;
31913        var scale = axis.scale;
31914
31915        if (!axisModel.get('axisLabel.show') || scale.isBlank()) {
31916            return;
31917        }
31918
31919        var isCategory = axis.type === 'category';
31920
31921        var realNumberScaleTicks;
31922        var tickCount;
31923        var categoryScaleExtent = scale.getExtent();
31924
31925        // Optimize for large category data, avoid call `getTicks()`.
31926        if (isCategory) {
31927            tickCount = scale.count();
31928        }
31929        else {
31930            realNumberScaleTicks = scale.getTicks();
31931            tickCount = realNumberScaleTicks.length;
31932        }
31933
31934        var axisLabelModel = axis.getLabelModel();
31935        var labelFormatter = makeLabelFormatter(axis);
31936
31937        var rect;
31938        var step = 1;
31939        // Simple optimization for large amount of labels
31940        if (tickCount > 40) {
31941            step = Math.ceil(tickCount / 40);
31942        }
31943        for (var i = 0; i < tickCount; i += step) {
31944            var tickValue = realNumberScaleTicks ? realNumberScaleTicks[i] : categoryScaleExtent[0] + i;
31945            var label = labelFormatter(tickValue);
31946            var unrotatedSingleRect = axisLabelModel.getTextRect(label);
31947            var singleRect = rotateTextRect(unrotatedSingleRect, axisLabelModel.get('rotate') || 0);
31948
31949            rect ? rect.union(singleRect) : (rect = singleRect);
31950        }
31951
31952        return rect;
31953    }
31954
31955    function rotateTextRect(textRect, rotate) {
31956        var rotateRadians = rotate * Math.PI / 180;
31957        var boundingBox = textRect.plain();
31958        var beforeWidth = boundingBox.width;
31959        var beforeHeight = boundingBox.height;
31960        var afterWidth = beforeWidth * Math.cos(rotateRadians) + beforeHeight * Math.sin(rotateRadians);
31961        var afterHeight = beforeWidth * Math.sin(rotateRadians) + beforeHeight * Math.cos(rotateRadians);
31962        var rotatedRect = new BoundingRect(boundingBox.x, boundingBox.y, afterWidth, afterHeight);
31963
31964        return rotatedRect;
31965    }
31966
31967    /*
31968* Licensed to the Apache Software Foundation (ASF) under one
31969* or more contributor license agreements.  See the NOTICE file
31970* distributed with this work for additional information
31971* regarding copyright ownership.  The ASF licenses this file
31972* to you under the Apache License, Version 2.0 (the
31973* "License"); you may not use this file except in compliance
31974* with the License.  You may obtain a copy of the License at
31975*
31976*   http://www.apache.org/licenses/LICENSE-2.0
31977*
31978* Unless required by applicable law or agreed to in writing,
31979* software distributed under the License is distributed on an
31980* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
31981* KIND, either express or implied.  See the License for the
31982* specific language governing permissions and limitations
31983* under the License.
31984*/
31985
31986    var axisModelCommonMixin = {
31987
31988        /**
31989         * @param {boolean} origin
31990         * @return {number|string} min value or 'dataMin' or null/undefined (means auto) or NaN
31991         */
31992        getMin: function (origin) {
31993            var option = this.option;
31994            var min = (!origin && option.rangeStart != null)
31995                ? option.rangeStart : option.min;
31996
31997            if (this.axis
31998                && min != null
31999                && min !== 'dataMin'
32000                && typeof min !== 'function'
32001                && !eqNaN(min)
32002            ) {
32003                min = this.axis.scale.parse(min);
32004            }
32005            return min;
32006        },
32007
32008        /**
32009         * @param {boolean} origin
32010         * @return {number|string} max value or 'dataMax' or null/undefined (means auto) or NaN
32011         */
32012        getMax: function (origin) {
32013            var option = this.option;
32014            var max = (!origin && option.rangeEnd != null)
32015                ? option.rangeEnd : option.max;
32016
32017            if (this.axis
32018                && max != null
32019                && max !== 'dataMax'
32020                && typeof max !== 'function'
32021                && !eqNaN(max)
32022            ) {
32023                max = this.axis.scale.parse(max);
32024            }
32025            return max;
32026        },
32027
32028        /**
32029         * @return {boolean}
32030         */
32031        getNeedCrossZero: function () {
32032            var option = this.option;
32033            return (option.rangeStart != null || option.rangeEnd != null)
32034                ? false : !option.scale;
32035        },
32036
32037        /**
32038         * Should be implemented by each axis model if necessary.
32039         * @return {module:echarts/model/Component} coordinate system model
32040         */
32041        getCoordSysModel: noop,
32042
32043        /**
32044         * @param {number} rangeStart Can only be finite number or null/undefined or NaN.
32045         * @param {number} rangeEnd Can only be finite number or null/undefined or NaN.
32046         */
32047        setRange: function (rangeStart, rangeEnd) {
32048            this.option.rangeStart = rangeStart;
32049            this.option.rangeEnd = rangeEnd;
32050        },
32051
32052        /**
32053         * Reset range
32054         */
32055        resetRange: function () {
32056            // rangeStart and rangeEnd is readonly.
32057            this.option.rangeStart = this.option.rangeEnd = null;
32058        }
32059    };
32060
32061    /*
32062* Licensed to the Apache Software Foundation (ASF) under one
32063* or more contributor license agreements.  See the NOTICE file
32064* distributed with this work for additional information
32065* regarding copyright ownership.  The ASF licenses this file
32066* to you under the Apache License, Version 2.0 (the
32067* "License"); you may not use this file except in compliance
32068* with the License.  You may obtain a copy of the License at
32069*
32070*   http://www.apache.org/licenses/LICENSE-2.0
32071*
32072* Unless required by applicable law or agreed to in writing,
32073* software distributed under the License is distributed on an
32074* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
32075* KIND, either express or implied.  See the License for the
32076* specific language governing permissions and limitations
32077* under the License.
32078*/
32079
32080// Symbol factory
32081
32082    /**
32083     * Triangle shape
32084     * @inner
32085     */
32086    var Triangle = extendShape({
32087        type: 'triangle',
32088        shape: {
32089            cx: 0,
32090            cy: 0,
32091            width: 0,
32092            height: 0
32093        },
32094        buildPath: function (path, shape) {
32095            var cx = shape.cx;
32096            var cy = shape.cy;
32097            var width = shape.width / 2;
32098            var height = shape.height / 2;
32099            path.moveTo(cx, cy - height);
32100            path.lineTo(cx + width, cy + height);
32101            path.lineTo(cx - width, cy + height);
vendor: 5,327 bytes, lines 32102-32314
32102            path.closePath();
32103        }
32104    });
32105
32106    /**
32107     * Diamond shape
32108     * @inner
32109     */
32110    var Diamond = extendShape({
32111        type: 'diamond',
32112        shape: {
32113            cx: 0,
32114            cy: 0,
32115            width: 0,
32116            height: 0
32117        },
32118        buildPath: function (path, shape) {
32119            var cx = shape.cx;
32120            var cy = shape.cy;
32121            var width = shape.width / 2;
32122            var height = shape.height / 2;
32123            path.moveTo(cx, cy - height);
32124            path.lineTo(cx + width, cy);
32125            path.lineTo(cx, cy + height);
32126            path.lineTo(cx - width, cy);
32127            path.closePath();
32128        }
32129    });
32130
32131    /**
32132     * Pin shape
32133     * @inner
32134     */
32135    var Pin = extendShape({
32136        type: 'pin',
32137        shape: {
32138            // x, y on the cusp
32139            x: 0,
32140            y: 0,
32141            width: 0,
32142            height: 0
32143        },
32144
32145        buildPath: function (path, shape) {
32146            var x = shape.x;
32147            var y = shape.y;
32148            var w = shape.width / 5 * 3;
32149            // Height must be larger than width
32150            var h = Math.max(w, shape.height);
32151            var r = w / 2;
32152
32153            // Dist on y with tangent point and circle center
32154            var dy = r * r / (h - r);
32155            var cy = y - h + r + dy;
32156            var angle = Math.asin(dy / r);
32157            // Dist on x with tangent point and circle center
32158            var dx = Math.cos(angle) * r;
32159
32160            var tanX = Math.sin(angle);
32161            var tanY = Math.cos(angle);
32162
32163            var cpLen = r * 0.6;
32164            var cpLen2 = r * 0.7;
32165
32166            path.moveTo(x - dx, cy + dy);
32167
32168            path.arc(
32169                x, cy, r,
32170                Math.PI - angle,
32171                Math.PI * 2 + angle
32172            );
32173            path.bezierCurveTo(
32174                x + dx - tanX * cpLen, cy + dy + tanY * cpLen,
32175                x, y - cpLen2,
32176                x, y
32177            );
32178            path.bezierCurveTo(
32179                x, y - cpLen2,
32180                x - dx + tanX * cpLen, cy + dy + tanY * cpLen,
32181                x - dx, cy + dy
32182            );
32183            path.closePath();
32184        }
32185    });
32186
32187    /**
32188     * Arrow shape
32189     * @inner
32190     */
32191    var Arrow = extendShape({
32192
32193        type: 'arrow',
32194
32195        shape: {
32196            x: 0,
32197            y: 0,
32198            width: 0,
32199            height: 0
32200        },
32201
32202        buildPath: function (ctx, shape) {
32203            var height = shape.height;
32204            var width = shape.width;
32205            var x = shape.x;
32206            var y = shape.y;
32207            var dx = width / 3 * 2;
32208            ctx.moveTo(x, y);
32209            ctx.lineTo(x + dx, y + height);
32210            ctx.lineTo(x, y + height / 4 * 3);
32211            ctx.lineTo(x - dx, y + height);
32212            ctx.lineTo(x, y);
32213            ctx.closePath();
32214        }
32215    });
32216
32217    /**
32218     * Map of path contructors
32219     * @type {Object.<string, module:zrender/graphic/Path>}
32220     */
32221    var symbolCtors = {
32222
32223        line: Line,
32224
32225        rect: Rect,
32226
32227        roundRect: Rect,
32228
32229        square: Rect,
32230
32231        circle: Circle,
32232
32233        diamond: Diamond,
32234
32235        pin: Pin,
32236
32237        arrow: Arrow,
32238
32239        triangle: Triangle
32240    };
32241
32242    var symbolShapeMakers = {
32243
32244        line: function (x, y, w, h, shape) {
32245            // FIXME
32246            shape.x1 = x;
32247            shape.y1 = y + h / 2;
32248            shape.x2 = x + w;
32249            shape.y2 = y + h / 2;
32250        },
32251
32252        rect: function (x, y, w, h, shape) {
32253            shape.x = x;
32254            shape.y = y;
32255            shape.width = w;
32256            shape.height = h;
32257        },
32258
32259        roundRect: function (x, y, w, h, shape) {
32260            shape.x = x;
32261            shape.y = y;
32262            shape.width = w;
32263            shape.height = h;
32264            shape.r = Math.min(w, h) / 4;
32265        },
32266
32267        square: function (x, y, w, h, shape) {
32268            var size = Math.min(w, h);
32269            shape.x = x;
32270            shape.y = y;
32271            shape.width = size;
32272            shape.height = size;
32273        },
32274
32275        circle: function (x, y, w, h, shape) {
32276            // Put circle in the center of square
32277            shape.cx = x + w / 2;
32278            shape.cy = y + h / 2;
32279            shape.r = Math.min(w, h) / 2;
32280        },
32281
32282        diamond: function (x, y, w, h, shape) {
32283            shape.cx = x + w / 2;
32284            shape.cy = y + h / 2;
32285            shape.width = w;
32286            shape.height = h;
32287        },
32288
32289        pin: function (x, y, w, h, shape) {
32290            shape.x = x + w / 2;
32291            shape.y = y + h / 2;
32292            shape.width = w;
32293            shape.height = h;
32294        },
32295
32296        arrow: function (x, y, w, h, shape) {
32297            shape.x = x + w / 2;
32298            shape.y = y + h / 2;
32299            shape.width = w;
32300            shape.height = h;
32301        },
32302
32303        triangle: function (x, y, w, h, shape) {
32304            shape.cx = x + w / 2;
32305            shape.cy = y + h / 2;
32306            shape.width = w;
32307            shape.height = h;
32308        }
32309    };
32310
32311    var symbolBuildProxies = {};
32312    each$1(symbolCtors, function (Ctor, name) {
32313        symbolBuildProxies[name] = new Ctor();
32314    });
32315
32316    var SymbolClz = extendShape({
32317
32318        type: 'symbol',
32319
32320        shape: {
32321            symbolType: '',
32322            x: 0,
32323            y: 0,
32324            width: 0,
32325            height: 0
32326        },
32327
32328        beforeBrush: function () {
32329            var style = this.style;
32330            var shape = this.shape;
32331            // FIXME
32332            if (shape.symbolType === 'pin' && style.textPosition === 'inside') {
32333                style.textPosition = ['50%', '40%'];
32334                style.textAlign = 'center';
32335                style.textVerticalAlign = 'middle';
32336            }
32337        },
32338
32339        buildPath: function (ctx, shape, inBundle) {
32340            var symbolType = shape.symbolType;
32341            var proxySymbol = symbolBuildProxies[symbolType];
32342            if (shape.symbolType !== 'none') {
32343                if (!proxySymbol) {
32344                    // Default rect
32345                    symbolType = 'rect';
32346                    proxySymbol = symbolBuildProxies[symbolType];
32347                }
32348                symbolShapeMakers[symbolType](
32349                    shape.x, shape.y, shape.width, shape.height, proxySymbol.shape
32350                );
32351                proxySymbol.buildPath(ctx, proxySymbol.shape, inBundle);
32352            }
32353        }
32354    });
32355
32356// Provide setColor helper method to avoid determine if set the fill or stroke outside
32357    function symbolPathSetColor(color, innerColor) {
32358        if (this.type !== 'image') {
32359            var symbolStyle = this.style;
32360            var symbolShape = this.shape;
32361            if (symbolShape && symbolShape.symbolType === 'line') {
32362                symbolStyle.stroke = color;
32363            }
32364            else if (this.__isEmptyBrush) {
32365                symbolStyle.stroke = color;
32366                symbolStyle.fill = innerColor || '#fff';
32367            }
32368            else {
32369                // FIXME 判断图形默认是填充还是描边,使用 onlyStroke ?
32370                symbolStyle.fill && (symbolStyle.fill = color);
32371                symbolStyle.stroke && (symbolStyle.stroke = color);
32372            }
32373            this.dirty(false);
32374        }
32375    }
32376
32377    /**
32378     * Create a symbol element with given symbol configuration: shape, x, y, width, height, color
32379     * @param {string} symbolType
32380     * @param {number} x
32381     * @param {number} y
32382     * @param {number} w
32383     * @param {number} h
32384     * @param {string} color
32385     * @param {boolean} [keepAspect=false] whether to keep the ratio of w/h,
32386     *                            for path and image only.
32387     */
32388    function createSymbol(symbolType, x, y, w, h, color, keepAspect) {
32389        // TODO Support image object, DynamicImage.
32390
32391        var isEmpty = symbolType.indexOf('empty') === 0;
32392        if (isEmpty) {
32393            symbolType = symbolType.substr(5, 1).toLowerCase() + symbolType.substr(6);
32394        }
32395        var symbolPath;
32396
32397        if (symbolType.indexOf('image://') === 0) {
32398            symbolPath = makeImage(
32399                symbolType.slice(8),
32400                new BoundingRect(x, y, w, h),
32401                keepAspect ? 'center' : 'cover'
32402            );
32403        }
32404        else if (symbolType.indexOf('path://') === 0) {
32405            symbolPath = makePath(
32406                symbolType.slice(7),
32407                {},
32408                new BoundingRect(x, y, w, h),
32409                keepAspect ? 'center' : 'cover'
32410            );
32411        }
32412        else {
32413            symbolPath = new SymbolClz({
32414                shape: {
32415                    symbolType: symbolType,
32416                    x: x,
32417                    y: y,
32418                    width: w,
32419                    height: h
32420                }
32421            });
32422        }
32423
32424        symbolPath.__isEmptyBrush = isEmpty;
32425
32426        symbolPath.setColor = symbolPathSetColor;
32427
32428        symbolPath.setColor(color);
32429
32430        return symbolPath;
32431    }
32432
32433    /*
32434* Licensed to the Apache Software Foundation (ASF) under one
32435* or more contributor license agreements.  See the NOTICE file
32436* distributed with this work for additional information
32437* regarding copyright ownership.  The ASF licenses this file
32438* to you under the Apache License, Version 2.0 (the
32439* "License");
32439 you may not use this file except in compliance
32440* with the License.  You may obtain a copy of the License at
32441*
32442*   http://www.apache.org/licenses/LICENSE-2.0
32443*
32444* Unless required by applicable law or agreed to in writing,
32445* software distributed under the License is distributed on an
32446* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
32447* KIND, either express or implied.  See the License for the
32448* specific language governing permissions and limitations
32449* under the License.
32450*/
32451
32452// import createGraphFromNodeEdge from './chart/helper/createGraphFromNodeEdge';
32453    /**
32454     * Create a muti dimension List structure from seriesModel.
32455     * @param  {module:echarts/model/Model} seriesModel
32456     * @return {module:echarts/data/List} list
32457     */
32458    function createList(seriesModel) {
32459        return createListFromArray(seriesModel.getSource(), seriesModel);
32460    }
32461
32462    var dataStack$1 = {
32463        isDimensionStacked: isDimensionStacked,
32464        enableDataStack: enableDataStack,
32465        getStackedDimension: getStackedDimension
32466    };
32467
32468    /**
32469     * Create scale
32470     * @param {Array.<number>} dataExtent
32471     * @param {Object|module:echarts/Model} option
32472     */
32473    function createScale(dataExtent, option) {
32474        var axisModel = option;
32475        if (!Model.isInstance(option)) {
32476            axisModel = new Model(option);
32477            mixin(axisModel, axisModelCommonMixin);
32478        }
32479
32480        var scale = createScaleByModel(axisModel);
32481        scale.setExtent(dataExtent[0], dataExtent[1]);
32482
32483        niceScaleExtent(scale, axisModel);
32484        return scale;
32485    }
32486
32487    /**
32488     * Mixin common methods to axis model,
32489     *
32490     * Inlcude methods
32491     * `getFormattedLabels() => Array.<string>`
32492     * `getCategories() => Array.<string>`
32493     * `getMin(origin: boolean) => number`
32494     * `getMax(origin: boolean) => number`
32495     * `getNeedCrossZero() => boolean`
32496     * `setRange(start: number, end: number)`
32497     * `resetRange()`
32498     */
32499    function mixinAxisModelCommonMethods(Model$$1) {
32500        mixin(Model$$1, axisModelCommonMixin);
32501    }
32502
32503    var helper = (Object.freeze || Object)({
32504        createList: createList,
32505        getLayoutRect: getLayoutRect,
32506        dataStack: dataStack$1,
32507        createScale: createScale,
32508        mixinAxisModelCommonMethods: mixinAxisModelCommonMethods,
32509        completeDimensions: completeDimensions,
32510        createDimensions: createDimensions,
32511        createSymbol: createSymbol
32512    });
32513
32514    var EPSILON$3 = 1e-8;
32515
32516    function isAroundEqual$1(a, b) {
32517        return Math.abs(a - b) < EPSILON$3;
32518    }
32519
32520    function contain$1(points, x, y) {
32521        var w = 0;
32522        var p = points[0];
32523
32524        if (!p) {
32525            return false;
32526        }
32527
32528        for (var i = 1; i < points.length; i++) {
32529            var p2 = points[i];
32530            w += windingLine(p[0], p[1], p2[0], p2[1], x, y);
32531            p = p2;
32532        }
32533
32534        // Close polygon
32535        var p0 = points[0];
32536        if (!isAroundEqual$1(p[0], p0[0]) || !isAroundEqual$1(p[1], p0[1])) {
32537            w += windingLine(p[0], p[1], p0[0], p0[1], x, y);
32538        }
32539
32540        return w !== 0;
32541    }
32542
32543    /*
32544* Licensed to the Apache Software Foundation (ASF) under one
32545* or more contributor license agreements.  See the NOTICE file
32546* distributed with this work for additional information
32547* regarding copyright ownership.  The ASF licenses this file
32548* to you under the Apache License, Version 2.0 (the
32549* "License"); you may not use this file except in compliance
32550* with the License.  You may obtain a copy of the License at
32551*
32552*   http://www.apache.org/licenses/LICENSE-2.0
32553*
32554* Unless required by applicable law or agreed to in writing,
32555* software distributed under the License is distributed on an
32556* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
32557* KIND, either express or implied.  See the License for the
32558* specific language governing permissions and limitations
32559* under the License.
32560*/
32561
32562    /**
32563     * @module echarts/coord/geo/Region
32564     */
32565
32566    /**
32567     * @param {string} name
32568     * @param {Array} geometries
32569     * @param {Array.<number>} cp
32570     */
32571    function Region(name, geometries, cp) {
32572
32573        /**
32574         * @type {string}
32575         * @readOnly
32576         */
32577        this.name = name;
32578
32579        /**
32580         * @type {Array.<Array>}
32581         * @readOnly
32582         */
32583        this.geometries = geometries;
32584
32585        if (!cp) {
32586            var rect = this.getBoundingRect();
32587            cp = [
32588                rect.x + rect.width / 2,
32589                rect.y + rect.height / 2
32590            ];
32591        }
32592        else {
32593            cp = [cp[0], cp[1]];
32594        }
32595        /**
32596         * @type {Array.<number>}
32597         */
32598        this.center = cp;
32599    }
32600
32601    Region.prototype = {
32602
32603        constructor: Region,
32604
32605        properties: null,
32606
32607        /**
32608         * @return {module:zrender/core/BoundingRect}
32609         */
32610        getBoundingRect: function () {
32611            var rect = this._rect;
32612            if (rect) {
32613                return rect;
32614            }
32615
32616            var MAX_NUMBER = Number.MAX_VALUE;
32617            var min$$1 = [MAX_NUMBER, MAX_NUMBER];
32618            var max$$1 = [-MAX_NUMBER, -MAX_NUMBER];
32619            var min2 = [];
32620            var max2 = [];
32621            var geometries = this.geometries;
32622            for (var i = 0; i < geometries.length; i++) {
32623                // Only support polygon
32624                if (geometries[i].type !== 'polygon') {
32625                    continue;
32626                }
32627                // Doesn't consider hole
32628                var exterior = geometries[i].exterior;
32629                fromPoints(exterior, min2, max2);
32630                min(min$$1, min$$1, min2);
32631                max(max$$1, max$$1, max2);
32632            }
32633            // No data
32634            if (i === 0) {
32635                min$$1[0] = min$$1[1] = max$$1[0] = max$$1[1] = 0;
32636            }
32637
32638            return (this._rect = new BoundingRect(
32639                min$$1[0], min$$1[1], max$$1[0] - min$$1[0], max$$1[1] - min$$1[1]
32640            ));
32641        },
32642
32643        /**
32644         * @param {<Array.<number>} coord
32645         * @return {boolean}
32646         */
32647        contain: function (coord) {
32648            var rect = this.getBoundingRect();
32649            var geometries = this.geometries;
32650            if (!rect.contain(coord[0], coord[1])) {
32651                return false;
32652            }
32653            loopGeo: for (var i = 0, len$$1 = geometries.length; i < len$$1; i++) {
32654                // Only support polygon.
32655                if (geometries[i].type !== 'polygon') {
32656                    continue;
32657                }
32658                var exterior = geometries[i].exterior;
32659                var interiors = geometries[i].interiors;
32660                if (contain$1(exterior, coord[0], coord[1])) {
32661                    // Not in the region if point is in the hole.
32662                    for (var k = 0; k < (interiors ? interiors.length : 0); k++) {
32663                        if (contain$1(interiors[k])) {
32664                            continue loopGeo;
32665                        }
32666                    }
32667                    return true;
32668                }
32669            }
32670            return false;
32671        },
32672
32673        transformTo: function (x, y, width, height) {
32674            var rect = this.getBoundingRect();
32675            var aspect = rect.width / rect.height;
32676            if (!width) {
32677                width = aspect * height;
32678            }
32679            else if (!height) {
32680                height = width / aspect ;
32681            }
32682            var target = new BoundingRect(x, y, width, height);
32683            var transform = rect.calculateTransform(target);
32684            var geometries = this.geometries;
32685            for (var i = 0; i < geometries.length; i++) {
32686                // Only support polygon.
32687                if (geometries[i].type !== 'polygon') {
32688                    continue;
32689                }
32690                var exterior = geometries[i].exterior;
32691                var interiors = geometries[i].interiors;
32692                for (var p = 0; p < exterior.length; p++) {
32693                    applyTransform(exterior[p], exterior[p], transform);
32694                }
32695                for (var h = 0; h < (interiors ? interiors.length : 0); h++) {
32696                    for (var p = 0; p < interiors[h].length; p++) {
32697                        applyTransform(interiors[h][p], interiors[h][p], transform);
32698                    }
32699                }
32700            }
32701            rect = this._rect;
32702            rect.copy(target);
32703            // Update center
32704            this.center = [
32705                rect.x + rect.width / 2,
32706                rect.y + rect.height / 2
32707            ];
32708        }
32709    };
32710
32711    /*
32712* Licensed to the Apache Software Foundation (ASF) under one
32713* or more contributor license agreements.  See the NOTICE file
32714* distributed with this work for additional information
32715* regarding copyright ownership.  The ASF licenses this file
32716* to you under the Apache License, Version 2.0 (the
32717* "License");
32717 you may not use this file except in compliance
32718* with the License.  You may obtain a copy of the License at
32719*
32720*   http://www.apache.org/licenses/LICENSE-2.0
32721*
32722* Unless required by applicable law or agreed to in writing,
32723* software distributed under the License is distributed on an
32724* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
32725* KIND, either express or implied.  See the License for the
32726* specific language governing permissions and limitations
32727* under the License.
32728*/
32729
32730    /**
32731     * Parse and decode geo json
32732     * @module echarts/coord/geo/parseGeoJson
32733     */
32734
32735    function decode(json) {
32736        if (!json.UTF8Encoding) {
32737            return json;
32738        }
32739        var encodeScale = json.UTF8Scale;
32740        if (encodeScale == null) {
32741            encodeScale = 1024;
32742        }
32743
32744        var features = json.features;
32745
32746        for (var f = 0; f < features.length; f++) {
32747            var feature = features[f];
32748            var geometry = feature.geometry;
32749            var coordinates = geometry.coordinates;
32750            var encodeOffsets = geometry.encodeOffsets;
32751
32752            for (var c = 0; c < coordinates.length; c++) {
32753                var coordinate = coordinates[c];
32754
32755                if (geometry.type === 'Polygon') {
32756                    coordinates[c] = decodePolygon(
32757                        coordinate,
32758                        encodeOffsets[c],
32759                        encodeScale
32760                    );
32761                }
32762                else if (geometry.type === 'MultiPolygon') {
32763                    for (var c2 = 0; c2 < coordinate.length; c2++) {
32764                        var polygon = coordinate[c2];
32765                        coordinate[c2] = decodePolygon(
32766                            polygon,
32767                            encodeOffsets[c][c2],
32768                            encodeScale
32769                        );
32770                    }
32771                }
32772            }
32773        }
32774        // Has been decoded
32775        json.UTF8Encoding = false;
32776        return json;
32777    }
32778
32779    function decodePolygon(coordinate, encodeOffsets, encodeScale) {
32780        var result = [];
32781        var prevX = encodeOffsets[0];
32782        var prevY = encodeOffsets[1];
32783
32784        for (var i = 0; i < coordinate.length; i += 2) {
32785            var x = coordinate.charCodeAt(i) - 64;
32786            var y = coordinate.charCodeAt(i + 1) - 64;
32787            // ZigZag decoding
32788            x = (x >> 1) ^ (-(x & 1));
32789            y = (y >> 1) ^ (-(y & 1));
32790            // Delta deocding
32791            x += prevX;
32792            y += prevY;
32793
32794            prevX = x;
32795            prevY = y;
32796            // Dequantize
32797            result.push([x / encodeScale, y / encodeScale]);
32798        }
32799
32800        return result;
32801    }
32802
32803    /**
32804     * @alias module:echarts/coord/geo/parseGeoJson
32805     * @param {Object} geoJson
32806     * @return {module:zrender/container/Group}
32807     */
32808    var parseGeoJson$1 = function (geoJson) {
32809
32810        decode(geoJson);
32811
32812        return map(filter(geoJson.features, function (featureObj) {
32813            // Output of mapshaper may have geometry null
32814            return featureObj.geometry
32815                && featureObj.properties
32816                && featureObj.geometry.coordinates.length > 0;
32817        }), function (featureObj) {
32818            var properties = featureObj.properties;
32819            var geo = featureObj.geometry;
32820
32821            var coordinates = geo.coordinates;
32822
32823            var geometries = [];
32824            if (geo.type === 'Polygon') {
32825                geometries.push({
32826                    type: 'polygon',
32827                    // According to the GeoJSON specification.
32828                    // First must be exterior, and the rest are all interior(holes).
32829                    exterior: coordinates[0],
32830                    interiors: coordinates.slice(1)
32831                });
32832            }
32833            if (geo.type === 'MultiPolygon') {
32834                each$1(coordinates, function (item) {
32835                    if (item[0]) {
32836                        geometries.push({
32837                            type: 'polygon',
32838                            exterior: item[0],
32839                            interiors: item.slice(1)
32840                        });
32841                    }
32842                });
32843            }
32844
32845            var region = new Region(
32846                properties.name,
32847                geometries,
32848                properties.cp
32849            );
32850            region.properties = properties;
32851            return region;
32852        });
32853    };
32854
32855    /*
32856* Licensed to the Apache Software Foundation (ASF) under one
32857* or more contributor license agreements.  See the NOTICE file
32858* distributed with this work for additional information
32859* regarding copyright ownership.  The ASF licenses this file
32860* to you under the Apache License, Version 2.0 (the
32861* "License");
32861 you may not use this file except in compliance
32862* with the License.  You may obtain a copy of the License at
32863*
32864*   http://www.apache.org/licenses/LICENSE-2.0
32865*
32866* Unless required by applicable law or agreed to in writing,
32867* software distributed under the License is distributed on an
32868* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
32869* KIND, either express or implied.  See the License for the
32870* specific language governing permissions and limitations
32871* under the License.
32872*/
32873
32874    var inner$6 = makeInner();
32875
32876    /**
32877     * @param {module:echats/coord/Axis} axis
32878     * @return {Object} {
32879 *     labels: [{
32880 *         formattedLabel: string,
32881 *         rawLabel: string,
32882 *         tickValue: number
32883 *     }, ...],
32884 *     labelCategoryInterval: number
32885 * }
32886     */
32887    function createAxisLabels(axis) {
32888        // Only ordinal scale support tick interval
32889        return axis.type === 'category'
32890            ? makeCategoryLabels(axis)
32891            : makeRealNumberLabels(axis);
32892    }
32893
32894    /**
32895     * @param {module:echats/coord/Axis} axis
32896     * @param {module:echarts/model/Model} tickModel For example, can be axisTick, splitLine, splitArea.
32897     * @return {Object} {
32898 *     ticks: Array.<number>
32899 *     tickCategoryInterval: number
32900 * }
32901     */
32902    function createAxisTicks(axis, tickModel) {
32903        // Only ordinal scale support tick interval
32904        return axis.type === 'category'
32905            ? makeCategoryTicks(axis, tickModel)
32906            : {ticks: axis.scale.getTicks()};
32907    }
32908
32909    function makeCategoryLabels(axis) {
32910        var labelModel = axis.getLabelModel();
32911        var result = makeCategoryLabelsActually(axis, labelModel);
32912
32913        return (!labelModel.get('show') || axis.scale.isBlank())
32914            ? {labels: [], labelCategoryInterval: result.labelCategoryInterval}
32915            : result;
32916    }
32917
32918    function makeCategoryLabelsActually(axis, labelModel) {
32919        var labelsCache = getListCache(axis, 'labels');
32920        var optionLabelInterval = getOptionCategoryInterval(labelModel);
32921        var result = listCacheGet(labelsCache, optionLabelInterval);
32922
32923        if (result) {
32924            return result;
32925        }
32926
32927        var labels;
32928        var numericLabelInterval;
32929
32930        if (isFunction$1(optionLabelInterval)) {
32931            labels = makeLabelsByCustomizedCategoryInterval(axis, optionLabelInterval);
32932        }
32933        else {
32934            numericLabelInterval = optionLabelInterval === 'auto'
32935                ? makeAutoCategoryInterval(axis) : optionLabelInterval;
32936            labels = makeLabelsByNumericCategoryInterval(axis, numericLabelInterval);
32937        }
32938
32939        // Cache to avoid calling interval function repeatly.
32940        return listCacheSet(labelsCache, optionLabelInterval, {
32941            labels: labels, labelCategoryInterval: numericLabelInterval
32942        });
32943    }
32944
32945    function makeCategoryTicks(axis, tickModel) {
32946        var ticksCache = getListCache(axis, 'ticks');
32947        var optionTickInterval = getOptionCategoryInterval(tickModel);
32948        var result = listCacheGet(ticksCache, optionTickInterval);
32949
32950        if (result) {
32951            return result;
32952        }
32953
32954        var ticks;
32955        var tickCategoryInterval;
32956
32957        // Optimize for the case that large category data and no label displayed,
32958        // we should not return all ticks.
32959        if (!tickModel.get('show') || axis.scale.isBlank()) {
32960            ticks = [];
32961        }
32962
32963        if (isFunction$1(optionTickInterval)) {
32964            ticks = makeLabelsByCustomizedCategoryInterval(axis, optionTickInterval, true);
32965        }
32966        // Always use label interval by default despite label show. Consider this
32967        // scenario, Use multiple grid with the xAxis sync, and only one xAxis shows
32968        // labels. `splitLine` and `axisTick` should be consistent in this case.
32969        else if (optionTickInterval === 'auto') {
32970            var labelsResult = makeCategoryLabelsActually(axis, axis.getLabelModel());
32971            tickCategoryInterval = labelsResult.labelCategoryInterval;
32972            ticks = map(labelsResult.labels, function (labelItem) {
32973                return labelItem.tickValue;
32974            });
32975        }
32976        else {
32977            tickCategoryInterval = optionTickInterval;
32978            ticks = makeLabelsByNumericCategoryInterval(axis, tickCategoryInterval, true);
32979        }
32980
32981        // Cache to avoid calling interval function repeatly.
32982        return listCacheSet(ticksCache, optionTickInterval, {
32983            ticks: ticks, tickCategoryInterval: tickCategoryInterval
32984        });
32985    }
32986
32987    function makeRealNumberLabels(axis) {
32988        var ticks = axis.scale.getTicks();
32989        var labelFormatter = makeLabelFormatter(axis);
32990        return {
32991            labels: map(ticks, function (tickValue, idx) {
32992                return {
32993                    formattedLabel: labelFormatter(tickValue, idx),
32994                    rawLabel: axis.scale.getLabel(tickValue),
32995                    tickValue: tickValue
32996                };
32997            })
32998        };
32999    }
33000
33001// Large category data calculation is performence sensitive, and ticks and label
33002// probably be fetched by multiple times. So we cache the result.
33003// axis is created each time during a ec process, so we do not need to clear cache.
33004    function getListCache(axis, prop) {
33005        // Because key can be funciton, and cache size always be small, we use array cache.
33006        return inner$6(axis)[prop] || (inner$6(axis)[prop] = []);
33007    }
33008
33009    function listCacheGet(cache, key) {
33010        for (var i = 0; i < cache.length; i++) {
33011            if (cache[i].key === key) {
33012                return cache[i].value;
33013            }
33014        }
33015    }
33016
33017    function listCacheSet(cache, key, value) {
33018        cache.push({key: key, value: value});
33019        return value;
33020    }
33021
33022    function makeAutoCategoryInterval(axis) {
33023        var result = inner$6(axis).autoInterval;
33024        return result != null
33025            ? result
33026            : (inner$6(axis).autoInterval = axis.calculateCategoryInterval());
33027    }
33028
33029    /**
33030     * Calculate interval for category axis ticks and labels.
33031     * To get precise result, at least one of `getRotate` and `isHorizontal`
33032     * should be implemented in axis.
33033     */
33034    function calculateCategoryInterval(axis) {
33035        var params = fetchAutoCategoryIntervalCalculationParams(axis);
33036        var labelFormatter = makeLabelFormatter(axis);
33037        var rotation = (params.axisRotate - params.labelRotate) / 180 * Math.PI;
33038
33039        var ordinalScale = axis.scale;
33040        var ordinalExtent = ordinalScale.getExtent();
33041        // Providing this method is for optimization:
33042        // avoid generating a long array by `getTicks`
33043        // in large category data case.
33044        var tickCount = ordinalScale.count();
33045
33046        if (ordinalExtent[1] - ordinalExtent[0] < 1) {
33047            return 0;
33048        }
33049
33050        var step = 1;
33051        // Simple optimization. Empirical value: tick count should less than 40.
33052        if (tickCount > 40) {
33053            step = Math.max(1, Math.floor(tickCount / 40));
33054        }
33055        var tickValue = ordinalExtent[0];
33056        var unitSpan = axis.dataToCoord(tickValue + 1) - axis.dataToCoord(tickValue);
33057        var unitW = Math.abs(unitSpan * Math.cos(rotation));
33058        var unitH = Math.abs(unitSpan * Math.sin(rotation));
33059
33060        var maxW = 0;
33061        var maxH = 0;
33062
33063        // Caution: Performance sensitive for large category data.
33064        // Consider dataZoom, we should make appropriate step to avoid O(n) loop.
33065        for (; tickValue <= ordinalExtent[1]; tickValue += step) {
33066            var width = 0;
33067            var height = 0;
33068
33069            // Polar is also calculated in assumptive linear layout here.
33070            // Not precise, do not consider align and vertical align
33071            // and each distance from axis line yet.
vendor: 1,261 bytes, lines 33072-33102
33072            var rect = getBoundingRect(
33073                labelFormatter(tickValue), params.font, 'center', 'top'
33074            );
33075            // Magic number
33076            width = rect.width * 1.3;
33077            height = rect.height * 1.3;
33078
33079            // Min size, void long loop.
33080            maxW = Math.max(maxW, width, 7);
33081            maxH = Math.max(maxH, height, 7);
33082        }
33083
33084        var dw = maxW / unitW;
33085        var dh = maxH / unitH;
33086        // 0/0 is NaN, 1/0 is Infinity.
33087        isNaN(dw) && (dw = Infinity);
33088        isNaN(dh) && (dh = Infinity);
33089        var interval = Math.max(0, Math.floor(Math.min(dw, dh)));
33090
33091        var cache = inner$6(axis.model);
33092        var lastAutoInterval = cache.lastAutoInterval;
33093        var lastTickCount = cache.lastTickCount;
33094
33095        // Use cache to keep interval stable while moving zoom window,
33096        // otherwise the calculated interval might jitter when the zoom
33097        // window size is close to the interval-changing size.
33098        if (lastAutoInterval != null
33099            && lastTickCount != null
33100            && Math.abs(lastAutoInterval - interval) <= 1
33101            && Math.abs(lastTickCount - tickCount) <= 1
33102            // Always choose the bigger one, otherwise the critical
33103            // point is not the same when zooming in or zooming out.
33104            && lastAutoInterval > interval
33105        ) {
33106            interval = lastAutoInterval;
33107        }
33108        // Only update cache if cache not used, otherwise the
33109        // changing of interval is too insensitive.
33110        else {
33111            cache.lastTickCount = tickCount;
33112            cache.lastAutoInterval = interval;
33113        }
33114
33115        return interval;
33116    }
33117
33118    function fetchAutoCategoryIntervalCalculationParams(axis) {
33119        var labelModel = axis.getLabelModel();
33120        return {
33121            axisRotate: axis.getRotate
33122                ? axis.getRotate()
33123                : (axis.isHorizontal && !axis.isHorizontal())
33124                    ? 90
33125                    : 0,
33126            labelRotate: labelModel.get('rotate') || 0,
33127            font: labelModel.getFont()
33128        };
33129    }
33130
33131    function makeLabelsByNumericCategoryInterval(axis, categoryInterval, onlyTick) {
33132        var labelFormatter = makeLabelFormatter(axis);
33133        var ordinalScale = axis.scale;
33134        var ordinalExtent = ordinalScale.getExtent();
33135        var labelModel = axis.getLabelModel();
33136        var result = [];
33137
33138        // TODO: axisType: ordinalTime, pick the tick from each month/day/year/...
33139
33140        var step = Math.max((categoryInterval || 0) + 1, 1);
33141        var startTick = ordinalExtent[0];
33142        var tickCount = ordinalScale.count();
33143
33144        // Calculate start tick based on zero if possible to keep label consistent
33145        // while zooming and moving while interval > 0. Otherwise the selection
33146        // of displayable ticks and symbols probably keep changing.
33147        // 3 is empirical value.
33148        if (startTick !== 0 && step > 1 && tickCount / step > 2) {
33149            startTick = Math.round(Math.ceil(startTick / step) * step);
33150        }
33151
33152        // (1) Only add min max label here but leave overlap checking
33153        // to render stage, which also ensure the returned list
33154        // suitable for splitLine and splitArea rendering.
33155        // (2) Scales except category always contain min max label so
33156        // do not need to perform this process.
33157        var showMinMax = {
33158            min: labelModel.get('showMinLabel'),
33159            max: labelModel.get('showMaxLabel')
33160        };
33161
33162        if (showMinMax.min && startTick !== ordinalExtent[0]) {
33163            addItem(ordinalExtent[0]);
33164        }
33165
33166        // Optimize: avoid generating large array by `ordinalScale.getTicks()`.
33167        var tickValue = startTick;
33168        for (; tickValue <= ordinalExtent[1]; tickValue += step) {
33169            addItem(tickValue);
33170        }
33171
33172        if (showMinMax.max && tickValue !== ordinalExtent[1]) {
33173            addItem(ordinalExtent[1]);
33174        }
33175
33176        function addItem(tVal) {
33177            result.push(onlyTick
33178                ? tVal
33179                : {
33180                    formattedLabel: labelFormatter(tVal),
33181                    rawLabel: ordinalScale.getLabel(tVal),
33182                    tickValue: tVal
33183                }
33184            );
33185        }
33186
33187        return result;
33188    }
33189
33190// When interval is function, the result `false` means ignore the tick.
33191// It is time consuming for large category data.
33192    function makeLabelsByCustomizedCategoryInterval(axis, categoryInterval, onlyTick) {
33193        var ordinalScale = axis.scale;
33194        var labelFormatter = makeLabelFormatter(axis);
33195        var result = [];
33196
33197        each$1(ordinalScale.getTicks(), function (tickValue) {
33198            var rawLabel = ordinalScale.getLabel(tickValue);
33199            if (categoryInterval(tickValue, rawLabel)) {
33200                result.push(onlyTick
33201                    ? tickValue
33202                    : {
33203                        formattedLabel: labelFormatter(tickValue),
33204                        rawLabel: rawLabel,
33205                        tickValue: tickValue
33206                    }
33207                );
33208            }
33209        });
33210
33211        return result;
33212    }
33213
33214// Can be null|'auto'|number|function
33215    function getOptionCategoryInterval(model) {
33216        var interval = model.get('interval');
33217        return interval == null ? 'auto' : interval;
33218    }
33219
33220    /*
33221* Licensed to the Apache Software Foundation (ASF) under one
33222* or more contributor license agreements.  See the NOTICE file
33223* distributed with this work for additional information
33224* regarding copyright ownership.  The ASF licenses this file
33225* to you under the Apache License, Version 2.0 (the
33226* "License");
33226 you may not use this file except in compliance
33227* with the License.  You may obtain a copy of the License at
33228*
33229*   http://www.apache.org/licenses/LICENSE-2.0
33230*
33231* Unless required by applicable law or agreed to in writing,
33232* software distributed under the License is distributed on an
33233* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
33234* KIND, either express or implied.  See the License for the
33235* specific language governing permissions and limitations
33236* under the License.
33237*/
33238
33239    var NORMALIZED_EXTENT = [0, 1];
33240
33241    /**
33242     * Base class of Axis.
33243     * @constructor
33244     */
33245    var Axis = function (dim, scale, extent) {
33246
33247        /**
33248         * Axis dimension. Such as 'x', 'y', 'z', 'angle', 'radius'.
33249         * @type {string}
33250         */
33251        this.dim = dim;
33252
33253        /**
33254         * Axis scale
33255         * @type {module:echarts/coord/scale/*}
33256         */
33257        this.scale = scale;
33258
33259        /**
33260         * @type {Array.<number>}
33261         * @private
33262         */
33263        this._extent = extent || [0, 0];
33264
33265        /**
33266         * @type {boolean}
33267         */
33268        this.inverse = false;
33269
33270        /**
33271         * Usually true when axis has a ordinal scale
33272         * @type {boolean}
33273         */
33274        this.onBand = false;
33275    };
33276
33277    Axis.prototype = {
33278
33279        constructor: Axis,
33280
33281        /**
33282         * If axis extent contain given coord
33283         * @param {number} coord
33284         * @return {boolean}
33285         */
33286        contain: function (coord) {
33287            var extent = this._extent;
33288            var min = Math.min(extent[0], extent[1]);
33289            var max = Math.max(extent[0], extent[1]);
33290            return coord >= min && coord <= max;
33291        },
33292
33293        /**
33294         * If axis extent contain given data
33295         * @param {number} data
33296         * @return {boolean}
33297         */
33298        containData: function (data) {
33299            return this.contain(this.dataToCoord(data));
33300        },
33301
33302        /**
33303         * Get coord extent.
33304         * @return {Array.<number>}
33305         */
33306        getExtent: function () {
33307            return this._extent.slice();
33308        },
33309
33310        /**
33311         * Get precision used for formatting
33312         * @param {Array.<number>} [dataExtent]
33313         * @return {number}
33314         */
33315        getPixelPrecision: function (dataExtent) {
33316            return getPixelPrecision(
33317                dataExtent || this.scale.getExtent(),
33318                this._extent
33319            );
33320        },
33321
33322        /**
33323         * Set coord extent
33324         * @param {number} start
33325         * @param {number} end
33326         */
33327        setExtent: function (start, end) {
33328            var extent = this._extent;
33329            extent[0] = start;
33330            extent[1] = end;
33331        },
33332
33333        /**
33334         * Convert data to coord. Data is the rank if it has an ordinal scale
33335         * @param {number} data
33336         * @param  {boolean} clamp
33337         * @return {number}
33338         */
33339        dataToCoord: function (data, clamp) {
33340            var extent = this._extent;
33341            var scale = this.scale;
33342            data = scale.normalize(data);
33343
33344            if (this.onBand && scale.type === 'ordinal') {
33345                extent = extent.slice();
33346                fixExtentWithBands(extent, scale.count());
33347            }
33348
33349            return linearMap(data, NORMALIZED_EXTENT, extent, clamp);
33350        },
33351
33352        /**
33353         * Convert coord to data. Data is the rank if it has an ordinal scale
33354         * @param {number} coord
33355         * @param  {boolean} clamp
33356         * @return {number}
33357         */
33358        coordToData: function (coord, clamp) {
33359            var extent = this._extent;
33360            var scale = this.scale;
33361
33362            if (this.onBand && scale.type === 'ordinal') {
33363                extent = extent.slice();
33364                fixExtentWithBands(extent, scale.count());
33365            }
33366
33367            var t = linearMap(coord, extent, NORMALIZED_EXTENT, clamp);
33368
33369            return this.scale.scale(t);
33370        },
33371
33372        /**
33373         * Convert pixel point to data in axis
33374         * @param {Array.<number>} point
33375         * @param  {boolean} clamp
33376         * @return {number} data
33377         */
33378        pointToData: function (point, clamp) {
33379            // Should be implemented in derived class if necessary.
33380        },
33381
33382        /**
33383         * Different from `zrUtil.map(axis.getTicks(), axis.dataToCoord, axis)`,
33384         * `axis.getTicksCoords` considers `onBand`, which is used by
33385         * `boundaryGap:true` of category axis and splitLine and splitArea.
33386         * @param {Object} [opt]
33387         * @param {number} [opt.tickModel=axis.model.getModel('axisTick')]
33388         * @param {boolean} [opt.clamp] If `true`, the first and the last
33389         *        tick must be at the axis end points. Otherwise, clip ticks
33390         *        that outside the axis extent.
33391         * @return {Array.<Object>} [{
33392     *     coord: ...,
33393     *     tickValue: ...
33394     * }, ...]
33395         */
33396        getTicksCoords: function (opt) {
33397            opt = opt || {};
33398
33399            var tickModel = opt.tickModel || this.getTickModel();
33400
33401            var result = createAxisTicks(this, tickModel);
33402            var ticks = result.ticks;
33403
33404            var ticksCoords = map(ticks, function (tickValue) {
33405                return {
33406                    coord: this.dataToCoord(tickValue),
33407                    tickValue: tickValue
33408                };
33409            }, this);
33410
33411            var alignWithLabel = tickModel.get('alignWithLabel');
33412            fixOnBandTicksCoords(
33413                this, ticksCoords, result.tickCategoryInterval, alignWithLabel, opt.clamp
33414            );
33415
33416            return ticksCoords;
33417        },
33418
33419        /**
33420         * @return {Array.<Object>} [{
33421     *     formattedLabel: string,
33422     *     rawLabel: axis.scale.getLabel(tickValue)
33423     *     tickValue: number
33424     * }, ...]
33425         */
33426        getViewLabels: function () {
33427            return createAxisLabels(this).labels;
33428        },
33429
33430        /**
33431         * @return {module:echarts/coord/model/Model}
33432         */
33433        getLabelModel: function () {
33434            return this.model.getModel('axisLabel');
33435        },
33436
33437        /**
33438         * Notice here we only get the default tick model. For splitLine
33439         * or splitArea, we should pass the splitLineModel or splitAreaModel
33440         * manually when calling `getTicksCoords`.
33441         * In GL, this method may be overrided to:
33442         * `axisModel.getModel('axisTick', grid3DModel.getModel('axisTick'));`
33443         * @return {module:echarts/coord/model/Model}
33444         */
33445        getTickModel: function () {
33446            return this.model.getModel('axisTick');
33447        },
33448
33449        /**
33450         * Get width of band
33451         * @return {number}
33452         */
33453        getBandWidth: function () {
33454            var axisExtent = this._extent;
33455            var dataExtent = this.scale.getExtent();
33456
33457            var len = dataExtent[1] - dataExtent[0] + (this.onBand ? 1 : 0);
33458            // Fix #2728, avoid NaN when only one data.
33459            len === 0 && (len = 1);
33460
33461            var size = Math.abs(axisExtent[1] - axisExtent[0]);
33462
33463            return Math.abs(size) / len;
33464        },
33465
33466        /**
33467         * @abstract
33468         * @return {boolean} Is horizontal
33469         */
33470        isHorizontal: null,
33471
33472        /**
33473         * @abstract
33474         * @return {number} Get axis rotate, b
33474y degree.
33475         */
33476        getRotate: null,
33477
33478        /**
33479         * Only be called in category axis.
33480         * Can be overrided, consider other axes like in 3D.
33481         * @return {number} Auto interval for cateogry axis tick and label
33482         */
33483        calculateCategoryInterval: function () {
33484            return calculateCategoryInterval(this);
33485        }
33486
33487    };
33488
33489    function fixExtentWithBands(extent, nTick) {
33490        var size = extent[1] - extent[0];
33491        var len = nTick;
33492        var margin = size / len / 2;
33493        extent[0] += margin;
33494        extent[1] -= margin;
33495    }
33496
33497// If axis has labels [1, 2, 3, 4]. Bands on the axis are
33498// |---1---|---2---|---3---|---4---|.
33499// So the displayed ticks and splitLine/splitArea should between
33500// each data item, otherwise cause misleading (e.g., split tow bars
33501// of a single data item when there are two bar series).
33502// Also consider if tickCategoryInterval > 0 and onBand, ticks and
33503// splitLine/spliteArea should layout appropriately corresponding
33504// to displayed labels. (So we should not use `getBandWidth` in this
33505// case).
33506    function fixOnBandTicksCoords(axis, ticksCoords, tickCategoryInterval, alignWithLabel, clamp) {
33507        var ticksLen = ticksCoords.length;
33508
33509        if (!axis.onBand || alignWithLabel || !ticksLen) {
33510            return;
33511        }
33512
33513        var axisExtent = axis.getExtent();
33514        var last;
33515        if (ticksLen === 1) {
33516            ticksCoords[0].coord = axisExtent[0];
33517            last = ticksCoords[1] = {coord: axisExtent[0]};
33518        }
33519        else {
33520            var shift = (ticksCoords[1].coord - ticksCoords[0].coord);
33521            each$1(ticksCoords, function (ticksItem) {
33522                ticksItem.coord -= shift / 2;
33523                var tickCategoryInterval = tickCategoryInterval || 0;
33524                // Avoid split a single data item when odd interval.
33525                if (tickCategoryInterval % 2 > 0) {
33526                    ticksItem.coord -= shift / ((tickCategoryInterval + 1) * 2);
33527                }
33528            });
33529            last = {coord: ticksCoords[ticksLen - 1].coord + shift};
33530            ticksCoords.push(last);
33531        }
33532
33533        var inverse = axisExtent[0] > axisExtent[1];
33534
33535        if (littleThan(ticksCoords[0].coord, axisExtent[0])) {
33536            clamp ? (ticksCoords[0].coord = axisExtent[0]) : ticksCoords.shift();
33537        }
33538        if (clamp && littleThan(axisExtent[0], ticksCoords[0].coord)) {
33539            ticksCoords.unshift({coord: axisExtent[0]});
33540        }
33541        if (littleThan(axisExtent[1], last.coord)) {
33542            clamp ? (last.coord = axisExtent[1]) : ticksCoords.pop();
33543        }
33544        if (clamp && littleThan(last.coord, axisExtent[1])) {
33545            ticksCoords.push({coord: axisExtent[1]});
33546        }
33547
33548        function littleThan(a, b) {
33549            return inverse ? a > b : a < b;
33550        }
33551    }
33552
33553    /*
33554* Licensed to the Apache Software Foundation (ASF) under one
33555* or more contributor license agreements.  See the NOTICE file
33556* distributed with this work for additional information
33557* regarding copyright ownership.  The ASF licenses this file
33558* to you under the Apache License, Version 2.0 (the
33559* "License"); you may not use this file except in compliance
33560* with the License.  You may obtain a copy of the License at
33561*
33562*   http://www.apache.org/licenses/LICENSE-2.0
33563*
33564* Unless required by applicable law or agreed to in writing,
33565* software distributed under the License is distributed on an
33566* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
33567* KIND, either express or implied.  See the License for the
33568* specific language governing permissions and limitations
33569* under the License.
33570*/
33571
33572    /**
33573     * Do not mount those modules on 'src/echarts' for better tree shaking.
33574     */
33575
33576    var parseGeoJson = parseGeoJson$1;
33577
33578    var ecUtil = {};
33579    each$1([
33580            'map', 'each', 'filter', 'indexOf', 'inherits', 'reduce', 'filter',
33581            'bind', 'curry', 'isArray', 'isString', 'isObject', 'isFunction',
33582            'extend', 'defaults', 'clone', 'merge'
33583        ],
33584        function (name) {
33585            ecUtil[name] = zrUtil[name];
33586        }
33587    );
33588
33589    /*
33590* Licensed to the Apache Software Foundation (ASF) under one
33591* or more contributor license agreements.  See the NOTICE file
33592* distributed with this work for additional information
33593* regarding copyright ownership.  The ASF licenses this file
33594* to you under the Apache License, Version 2.0 (the
33595* "License");
33595 you may not use this file except in compliance
33596* with the License.  You may obtain a copy of the License at
33597*
33598*   http://www.apache.org/licenses/LICENSE-2.0
33599*
33600* Unless required by applicable law or agreed to in writing,
33601* software distributed under the License is distributed on an
33602* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
33603* KIND, either express or implied.  See the License for the
33604* specific language governing permissions and limitations
33605* under the License.
33606*/
33607
33608    SeriesModel.extend({
33609
33610        type: 'series.line',
33611
33612        dependencies: ['grid', 'polar'],
33613
33614        getInitialData: function (option, ecModel) {
33615            if (__DEV__) {
33616                var coordSys = option.coordinateSystem;
33617                if (coordSys !== 'polar' && coordSys !== 'cartesian2d') {
33618                    throw new Error('Line not support coordinateSystem besides cartesian and polar');
33619                }
33620            }
33621            return createListFromArray(this.getSource(), this);
33622        },
33623
33624        defaultOption: {
33625            zlevel: 0,
33626            z: 2,
33627            coordinateSystem: 'cartesian2d',
33628            legendHoverLink: true,
33629
33630            hoverAnimation: true,
33631            // stack: null
33632            // xAxisIndex: 0,
33633            // yAxisIndex: 0,
33634
33635            // polarIndex: 0,
33636
33637            // If clip the overflow value
33638            clipOverflow: true,
33639            // cursor: null,
33640
33641            label: {
33642                position: 'top'
33643            },
33644            // itemStyle: {
33645            // },
33646
33647            lineStyle: {
33648                width: 2,
33649                type: 'solid'
33650            },
33651            // areaStyle: {
33652            // origin of areaStyle. Valid values:
33653            // `'auto'/null/undefined`: from axisLine to data
33654            // `'start'`: from min to data
33655            // `'end'`: from data to max
33656            // origin: 'auto'
33657            // },
33658            // false, 'start', 'end', 'middle'
33659            step: false,
33660
33661            // Disabled if step is true
33662            smooth: false,
33663            smoothMonotone: null,
33664            symbol: 'emptyCircle',
33665            symbolSize: 4,
33666            symbolRotate: null,
33667
33668            showSymbol: true,
33669            // `false`: follow the label interval strategy.
33670            // `true`: show all symbols.
33671            // `'auto'`: If possible, show all symbols, otherwise
33672            //           follow the label interval strategy.
33673            showAllSymbol: 'auto',
33674
33675            // Whether to connect break point.
33676            connectNulls: false,
33677
33678            // Sampling for large data. Can be: 'average', 'max', 'min', 'sum'.
33679            sampling: 'none',
33680
33681            animationEasing: 'linear',
33682
33683            // Disable progressive
33684            progressive: 0,
33685            hoverLayerThreshold: Infinity
33686        }
33687    });
33688
33689    /*
33690* Licensed to the Apache Software Foundation (ASF) under one
33691* or more contributor license agreements.  See the NOTICE file
33692* distributed with this work for additional information
33693* regarding copyright ownership.  The ASF licenses this file
33694* to you under the Apache License, Version 2.0 (the
33695* "License"); you may not use this file except in compliance
33696* with the License.  You may obtain a copy of the License at
33697*
33698*   http://www.apache.org/licenses/LICENSE-2.0
33699*
33700* Unless required by applicable law or agreed to in writing,
33701* software distributed under the License is distributed on an
33702* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
33703* KIND, either express or implied.  See the License for the
33704* specific language governing permissions and limitations
33705* under the License.
33706*/
33707
33708    /**
33709     * @param {module:echarts/data/List} data
33710     * @param {number} dataIndex
33711     * @return {string} label string. Not null/undefined
33712     */
33713    function getDefaultLabel(data, dataIndex) {
33714        var labelDims = data.mapDimension('defaultedLabel', true);
33715        var len = labelDims.length;
33716
33717        // Simple optimization (in lots of cases, label dims length is 1)
33718        if (len === 1) {
33719            return retrieveRawValue(data, dataIndex, labelDims[0]);
33720        }
33721        else if (len) {
33722            var vals = [];
33723            for (var i = 0; i < labelDims.length; i++) {
33724                var val = retrieveRawValue(data, dataIndex, labelDims[i]);
33725                vals.push(val);
33726            }
33727            return vals.join(' ');
33728        }
33729    }
33730
33731    /*
33732* Licensed to the Apache Software Foundation (ASF) under one
33733* or more contributor license agreements.  See the NOTICE file
33734* distributed with this work for additional information
33735* regarding copyright ownership.  The ASF licenses this file
33736* to you under the Apache License, Version 2.0 (the
33737* "License");
33737 you may not use this file except in compliance
33738* with the License.  You may obtain a copy of the License at
33739*
33740*   http://www.apache.org/licenses/LICENSE-2.0
33741*
33742* Unless required by applicable law or agreed to in writing,
33743* software distributed under the License is distributed on an
33744* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
33745* KIND, either express or implied.  See the License for the
33746* specific language governing permissions and limitations
33747* under the License.
33748*/
33749
33750    /**
33751     * @module echarts/chart/helper/Symbol
33752     */
33753
33754    /**
33755     * @constructor
33756     * @alias {module:echarts/chart/helper/Symbol}
33757     * @param {module:echarts/data/List} data
33758     * @param {number} idx
33759     * @extends {module:zrender/graphic/Group}
33760     */
33761    function SymbolClz$1(data, idx, seriesScope) {
33762        Group.call(this);
33763        this.updateData(data, idx, seriesScope);
33764    }
33765
33766    var symbolProto = SymbolClz$1.prototype;
33767
33768    /**
33769     * @public
33770     * @static
33771     * @param {module:echarts/data/List} data
33772     * @param {number} dataIndex
33773     * @return {Array.<number>} [width, height]
33774     */
33775    var getSymbolSize = SymbolClz$1.getSymbolSize = function (data, idx) {
33776        var symbolSize = data.getItemVisual(idx, 'symbolSize');
33777        return symbolSize instanceof Array
33778            ? symbolSize.slice()
33779            : [+symbolSize, +symbolSize];
33780    };
33781
33782    function getScale(symbolSize) {
33783        return [symbolSize[0] / 2, symbolSize[1] / 2];
33784    }
33785
33786    function driftSymbol(dx, dy) {
33787        this.parent.drift(dx, dy);
33788    }
33789
33790    symbolProto._createSymbol = function (
33791        symbolType,
33792        data,
33793        idx,
33794        symbolSize,
33795        keepAspect
33796    ) {
33797        // Remove paths created before
33798        this.removeAll();
33799
33800        var color = data.getItemVisual(idx, 'color');
33801
33802        // var symbolPath = createSymbol(
33803        //     symbolType, -0.5, -0.5, 1, 1, color
33804        // );
33805        // If width/height are set too small (e.g., set to 1) on ios10
33806        // and macOS Sierra, a circle stroke become a rect, no matter what
33807        // the scale is set. So we set width/height as 2. See #4150.
33808        var symbolPath = createSymbol(
33809            symbolType, -1, -1, 2, 2, color, keepAspect
33810        );
33811
33812        symbolPath.attr({
33813            z2: 100,
33814            culling: true,
33815            scale: getScale(symbolSize)
33816        });
33817        // Rewrite drift method
33818        symbolPath.drift = driftSymbol;
33819
33820        this._symbolType = symbolType;
33821
33822        this.add(symbolPath);
33823    };
33824
33825    /**
33826     * Stop animation
33827     * @param {boolean} toLastFrame
33828     */
33829    symbolProto.stopSymbolAnimation = function (toLastFrame) {
33830        this.childAt(0).stopAnimation(toLastFrame);
33831    };
33832
33833    /**
33834     * FIXME:
33835     * Caution: This method breaks the encapsulation of this module,
33836     * but it indeed brings convenience. So do not use the method
33837     * unless you detailedly know all the implements of `Symbol`,
33838     * especially animation.
33839     *
33840     * Get symbol path element.
33841     */
33842    symbolProto.getSymbolPath = function () {
33843        return this.childAt(0);
33844    };
33845
33846    /**
33847     * Get scale(aka, current symbol size).
33848     * Including the change caused by animation
33849     */
33850    symbolProto.getScale = function () {
33851        return this.childAt(0).scale;
33852    };
33853
33854    /**
33855     * Highlight symbol
33856     */
33857    symbolProto.highlight = function () {
33858        this.childAt(0).trigger('emphasis');
33859    };
33860
33861    /**
33862     * Downplay symbol
33863     */
33864    symbolProto.downplay = function () {
33865        this.childAt(0).trigger('normal');
33866    };
33867
33868    /**
33869     * @param {number} zlevel
33870     * @param {number} z
33871     */
33872    symbolProto.setZ = function (zlevel, z) {
33873        var symbolPath = this.childAt(0);
33874        symbolPath.zlevel = zlevel;
33875        symbolPath.z = z;
33876    };
33877
33878    symbolProto.setDraggable = function (draggable) {
33879        var symbolPath = this.childAt(0);
33880        symbolPath.draggable = draggable;
33881        symbolPath.cursor = draggable ? 'move' : 'pointer';
33882    };
33883
33884    /**
33885     * Update symbol properties
33886     * @param {module:echarts/data/List} data
33887     * @param {number} idx
33888     * @param {Object} [seriesScope]
33889     * @param {Object} [seriesScope.itemStyle]
33890     * @param {Object} [seriesScope.hoverItemStyle]
33891     * @param {Object} [seriesScope.symbolRotate]
33892     * @param {Object} [seriesScope.symbolOffset]
33893     * @param {module:echarts/model/Model} [seriesScope.labelModel]
33894     * @param {module:echarts/model/Model} [seriesScope.hoverLabelModel]
33895     * @param {boolean} [seriesScope.hoverAnimation]
33896     * @param {Object} [seriesScope.cursorStyle]
33897     * @param {module:echarts/model/Model} [seriesScope.itemModel]
33898     * @param {string} [seriesScope.symbolInnerColor]
33899     * @param {Object} [seriesScope.fadeIn=false]
33900     */
33901    symbolProto.updateData = function (data, idx, seriesScope) {
33902        this.silent = false;
33903
33904        var symbolType = data.getItemVisual(idx, 'symbol') || 'circle';
33905        var seriesModel = data.hostModel;
33906        var symbolSize = getSymbolSize(data, idx);
33907        var isInit = symbolType !== this._symbolType;
33908
33909        if (isInit) {
33910            var keepAspect = data.getItemVisual(idx, 'symbolKeepAspect');
33911            this._createSymbol(symbolType, data, idx, symbolSize, keepAspect);
33912        }
33913        else {
33914            var symbolPath = this.childAt(0);
33915            symbolPath.silent = false;
33916            updateProps(symbolPath, {
33917                scale: getScale(symbolSize)
33918            }, seriesModel, idx);
33919        }
33920
33921        this._updateCommon(data, idx, symbolSize, seriesScope);
33922
33923        if (isInit) {
33924            var symbolPath = this.childAt(0);
33925            var fadeIn = seriesScope && seriesScope.fadeIn;
33926
33927            var target = {scale: symbolPath.scale.slice()};
33928            fadeIn && (target.style = {opacity: symbolPath.style.opacity});
33929
33930            symbolPath.scale = [0, 0];
33931            fadeIn && (symbolPath.style.opacity = 0);
33932
33933            initProps(symbolPath, target, seriesModel, idx);
33934        }
33935
33936        this._seriesModel = seriesModel;
33937    };
33938
33939// Update common properties
33940    var normalStyleAccessPath = ['itemStyle'];
33941    var emphasisStyleAccessPath = ['emphasis', 'itemStyle'];
33942    var normalLabelAccessPath = ['label'];
33943    var emphasisLabelAccessPath = ['emphasis', 'label'];
33944
33945    /**
33946     * @param {module:echarts/data/List} data
33947     * @param {number} idx
33948     * @param {Array.<number>} symbolSize
33949     * @param {Object} [seriesScope]
33950     */
33951    symbolProto._updateCommon = function (data, idx, symbolSize, seriesScope) {
33952        var symbolPath = this.childAt(0);
33953        var seriesModel = data.hostModel;
33954        var color = data.getItemVisual(idx, 'color');
33955
33956        // Reset style
33957        if (symbolPath.type !== 'image') {
33958            symbolPath.useStyle({
33959                strokeNoScale: true
33960            });
33961        }
33962
33963        var itemStyle = seriesScope && seriesScope.itemStyle;
33964        var hoverItemStyle = seriesScope && seriesScope.hoverItemStyle;
33965        var symbolRotate = seriesScope && seriesScope.symbolRotate;
33966        var symbolOffset = seriesScope && seriesScope.symbolOffset;
33967        var labelModel = seriesScope && seriesScope.labelModel;
33968        var hoverLabelModel = seriesScope && seriesScope.hoverLabelModel;
33969        var hoverAnimation = seriesScope && seriesScope.hoverAnimation;
33970        var cursorStyle = seriesScope && seriesScope.cursorStyle;
33971
33972        if (!seriesScope || data.hasItemOption) {
33973            var itemModel = (seriesScope && seriesScope.itemModel)
33974                ? seriesScope.itemModel : data.getItemModel(idx);
33975
33976            // Color must be excluded.
33977            // Because symbol provide setColor individually to set fill and stroke
33978            itemStyle = itemModel.getModel(normalStyleAccessPath).getItemStyle(['color']);
33979            hoverItemStyle = itemModel.getModel(emphasisStyleAccessPath).getItemStyle();
33980
33981            symbolRotate = itemModel.getShallow('symbolRotate');
33982            symbolOffset = itemModel.getShallow('symbolOffset');
33983
33984            labelModel = itemModel.getModel(normalLabelAccessPath);
33985            hoverLabelModel = itemModel.getModel(emphasisLabelAccessPath);
33986            hoverAnimation = itemModel.getShallow('hoverAnimation');
33987            cursorStyle = itemModel.getShallow('cursor');
33988        }
33989        else {
33990            hoverItemStyle = extend({}, hoverItemStyle);
33991        }
33992
33993        var elStyle = symbolPath.style;
33994
33995        symbolPath.attr('rotation', (symbolRotate || 0) * Math.PI / 180 || 0);
33996
33997        if (symbolOffset) {
33998            symbolPath.attr('position', [
33999                parsePercent$1(symbolOffset[0], symbolSize[0]),
34000                parsePercent$1(symbolOffset[1], symbolSize[1])
34001            ]);
34002        }
34003
34004        cursorStyle && symbolPath.attr('cursor', cursorStyle);
34005
34006        // PENDING setColor before setStyle!!!
34007        symbolPath.setColor(color, seriesScope && seriesScope.symbolInnerColor);
34008
34009        symbolPath.setStyle(itemStyle);
34010
34011        var opacity = data.getItemVisual(idx, 'opacity');
34012        if (opacity != null) {
34013            elStyle.opacity = opacity;
34014        }
34015
34016        var liftZ = data.getItemVisual(idx, 'liftZ');
34017        var z2Origin = symbolPath.__z2Origin;
34018        if (liftZ != null) {
34019            if (z2Origin == null) {
34020                symbolPath.__z2Origin = symbolPath.z2;
34021                symbolPath.z2 += liftZ;
34022            }
34023        }
34024        else if (z2Origin != null) {
34025            symbolPath.z2 = z2Origin;
34026            symbolPath.__z2Origin = null;
34027        }
34028
34029        var useNameLabel = seriesScope && seriesScope.useNameLabel;
34030
34031        setLabelStyle(
34032            elStyle, hoverItemStyle, labelModel, hoverLabelModel,
34033            {
34034                labelFetcher: seriesModel,
34035                labelDataIndex: idx,
34036                defaultText: getLabelDefaultText,
34037                isRectText: true,
34038                autoColor: color
34039            }
34040        );
34041
34042        // Do not execute util needed.
34043        function getLabelDefaultText(idx, opt) {
34044            return useNameLabel ? data.getName(idx) : getDefaultLabel(data, idx);
34045        }
34046
34047        symbolPath.off('mouseover')
34048            .off('mouseout')
34049            .off('emphasis')
34050            .off('normal');
34051
34052        symbolPath.hoverStyle = hoverItemStyle;
34053
34054        // FIXME
34055        // Do not use symbol.trigger('emphasis'), but use symbol.highlight() instead.
34056        setHoverStyle(symbolPath);
34057
34058        var scale = getScale(symbolSize);
34059
34060        if (hoverAnimation && seriesModel.isAnimationEnabled()) {
34061            var onEmphasis = function() {
34062                // Do not support this hover animation util some scenario required.
34063                // Animation can only be supported in hover layer when using `el.incremetal`.
34064                if (this.incremental) {
34065                    return;
34066                }
34067                var ratio = scale[1] / scale[0];
34068                this.animateTo({
34069                    scale: [
34070                        Math.max(scale[0] * 1.1, scale[0] + 3),
34071                        Math.max(scale[1] * 1.1, scale[1] + 3 * ratio)
34072                    ]
34073                }, 400, 'elasticOut');
34074            };
34075            var onNormal = function() {
34076                if (this.incremental) {
34077                    return;
34078                }
34079                this.animateTo({
34080                    scale: scale
34081                }, 400, 'elasticOut');
34082            };
34083            symbolPath.on('mouseover', onEmphasis)
34084                .on('mouseout', onNormal)
34085                .on('emphasis', onEmphasis)
34086                .on('normal', onNormal);
34087        }
34088    };
34089
34090    /**
34091     * @param {Function} cb
34092     * @param {Object} [opt]
34093     * @param {Object} [opt.keepLabel=true]
34094     */
34095    symbolProto.fadeOut = function (cb, opt) {
34096        var symbolPath = this.childAt(0);
34097        // Avoid mistaken hover when fading out
34098        this.silent = symbolPath.silent = true;
34099        // Not show text when animating
34100        !(opt && opt.keepLabel) && (symbolPath.style.text = null);
34101
34102        updateProps(
34103            symbolPath,
34104            {
34105                style: {opacity: 0},
34106                scale: [0, 0]
34107            },
34108            this._seriesModel,
34109            this.dataIndex,
34110            cb
34111        );
34112    };
34113
34114    inherits(SymbolClz$1, Group);
34115
34116    /*
34117* Licensed to the Apache Software Foundation (ASF) under one
34118* or more contributor license agreements.  See the NOTICE file
34119* distributed with this work for additional information
34120* regarding copyright ownership.  The ASF licenses this file
34121* to you under the Apache License, Version 2.0 (the
34122* "License"); you may not use this file except in compliance
34123* with the License.  You may obtain a copy of the License at
34124*
34125*   http://www.apache.org/licenses/LICENSE-2.0
34126*
34127* Unless required by applicable law or agreed to in writing,
34128* software distributed under the License is distributed on an
34129* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
34130* KIND, either express or implied.  See the License for the
34131* specific language governing permissions and limitations
34132* under the License.
34133*/
34134
34135    /**
34136     * @module echarts/chart/helper/SymbolDraw
34137     */
34138
34139    /**
34140     * @constructor
34141     * @alias module:echarts/chart/helper/SymbolDraw
34142     * @param {module:zrender/graphic/Group} [symbolCtor]
34143     */
34144    function SymbolDraw(symbolCtor) {
34145        this.group = new Group();
34146
34147        this._symbolCtor = symbolCtor || SymbolClz$1;
34148    }
34149
34150    var symbolDrawProto = SymbolDraw.prototype;
34151
34152    function symbolNeedsDraw(data, point, idx, opt) {
34153        return point && !isNaN(point[0]) && !isNaN(point[1])
34154            && !(opt.isIgnore && opt.isIgnore(idx))
34155            // We do not set clipShape on group, because it will cut part of
34156            // the symbol element shape. We use the same clip shape here as
34157            // the line clip.
34158            && !(opt.clipShape && !opt.clipShape.contain(point[0], point[1]))
34159            && data.getItemVisual(idx, 'symbol') !== 'none';
34160    }
34161
34162    /**
34163     * Update symbols draw by new data
34164     * @param {module:echarts/data/List} data
34165     * @param {Object} [opt] Or isIgnore
34166     * @param {Function} [opt.isIgnore]
34167     * @param {Object} [opt.clipShape]
34168     */
34169    symbolDrawProto.updateData = function (data, opt) {
34170        opt = normalizeUpdateOpt(opt);
34171
34172        var group = this.group;
34173        var seriesModel = data.hostModel;
34174        var oldData = this._data;
34175        var SymbolCtor = this._symbolCtor;
34176
34177        var seriesScope = makeSeriesScope(data);
34178
34179        // There is no oldLineData only when first rendering or switching from
34180        // stream mode to normal mode, where previous elements should be removed.
34181        if (!oldData) {
34182            group.removeAll();
34183        }
34184
34185        data.diff(oldData)
34186            .add(function (newIdx) {
34187                var point = data.getItemLayout(newIdx);
34188                if (symbolNeedsDraw(data, point, newIdx, opt)) {
34189                    var symbolEl = new SymbolCtor(data, newIdx, seriesScope);
34190                    symbolEl.attr('position', point);
34191                    data.setItemGraphicEl(newIdx, symbolEl);
34192                    group.add(symbolEl);
34193                }
34194            })
34195            .update(function (newIdx, oldIdx) {
34196                var symbolEl = oldData.getItemGraphicEl(oldIdx);
34197                var point = data.getItemLayout(newIdx);
34198                if (!symbolNeedsDraw(data, point, newIdx, opt)) {
34199                    group.remove(symbolEl);
34200                    return;
34201                }
34202                if (!symbolEl) {
34203                    symbolEl = new SymbolCtor(data, newIdx);
34204                    symbolEl.attr('position', point);
34205                }
34206                else {
34207                    symbolEl.updateData(data, newIdx, seriesScope);
34208                    updateProps(symbolEl, {
34209                        position: point
34210                    }, seriesModel);
34211                }
34212
34213                // Add back
34214                group.add(symbolEl);
34215
34216                data.setItemGraphicEl(newIdx, symbolEl);
34217            })
34218            .remove(function (oldIdx) {
34219                var el = oldData.getItemGraphicEl(oldIdx);
34220                el && el.fadeOut(function () {
34221                    group.remove(el);
34222                });
34223            })
34224            .execute();
34225
34226        this._data = data;
34227    };
34228
34229    symbolDrawProto.isPersistent = function () {
34230        return true;
34231    };
34232
34233    symbolDrawProto.updateLayout = function () {
34234        var data = this._data;
34235        if (data) {
34236            // Not use animation
34237            data.eachItemGraphicEl(function (el, idx) {
34238                var point = data.getItemLayout(idx);
34239                el.attr('position', point);
34240            });
34241        }
34242    };
34243
34244    symbolDrawProto.incrementalPrepareUpdate = function (data) {
34245        this._seriesScope = makeSeriesScope(data);
34246        this._data = null;
34247        this.group.removeAll();
34248    };
34249
34250    /**
34251     * Update symbols draw by new data
34252     * @param {module:echarts/data/List} data
34253     * @param {Object} [opt] Or isIgnore
34254     * @param {Function} [opt.isIgnore]
34255     * @param {Object} [opt.clipShape]
34256     */
34257    symbolDrawProto.incrementalUpdate = function (taskParams, data, opt) {
34258        opt = normalizeUpdateOpt(opt);
34259
34260        function updateIncrementalAndHover(el) {
34261            if (!el.isGroup) {
34262                el.incremental = el.useHoverLayer = true;
34263            }
34264        }
34265        for (var idx = taskParams.start; idx < taskParams.end; idx++) {
34266            var point = data.getItemLayout(idx);
34267            if (symbolNeedsDraw(data, point, idx, opt)) {
34268                var el = new this._symbolCtor(data, idx, this._seriesScope);
34269                el.traverse(updateIncrementalAndHover);
34270                el.attr('position', point);
34271                this.group.add(el);
34272                data.setItemGraphicEl(idx, el);
34273            }
34274        }
34275    };
34276
34277    function normalizeUpdateOpt(opt) {
34278        if (opt != null && !isObject$1(opt)) {
34279            opt = {isIgnore: opt};
34280        }
34281        return opt || {};
34282    }
34283
34284    symbolDrawProto.remove = function (enableAnimation) {
34285        var group = this.group;
34286        var data = this._data;
34287        // Incremental model do not have this._data.
34288        if (data && enableAnimation) {
34289            data.eachItemGraphicEl(function (el) {
34290                el.fadeOut(function () {
34291                    group.remove(el);
34292                });
34293            });
34294        }
34295        else {
34296            group.removeAll();
34297        }
34298    };
34299
34300    function makeSeriesScope(data) {
34301        var seriesModel = data.hostModel;
34302        return {
34303            itemStyle: seriesModel.getModel('itemStyle').getItemStyle(['color']),
34304            hoverItemStyle: seriesModel.getModel('emphasis.itemStyle').getItemStyle(),
34305            symbolRotate: seriesModel.get('symbolRotate'),
34306            symbolOffset: seriesModel.get('symbolOffset'),
34307            hoverAnimation: seriesModel.get('hoverAnimation'),
34308            labelModel: seriesModel.getModel('label'),
34309            hoverLabelModel: seriesModel.getModel('emphasis.label'),
34310            cursorStyle: seriesModel.get('cursor')
34311        };
34312    }
34313
34314    /*
34315* Licensed to the Apache Software Foundation (ASF) under one
34316* or more contributor license agreements.  See the NOTICE file
34317* distributed with this work for additional information
34318* regarding copyright ownership.  The ASF licenses this file
34319* to you under the Apache License, Version 2.0 (the
34320* "License");
34320 you may not use this file except in compliance
34321* with the License.  You may obtain a copy of the License at
34322*
34323*   http://www.apache.org/licenses/LICENSE-2.0
34324*
34325* Unless required by applicable law or agreed to in writing,
34326* software distributed under the License is distributed on an
34327* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
34328* KIND, either express or implied.  See the License for the
34329* specific language governing permissions and limitations
34330* under the License.
34331*/
34332
34333    /**
34334     * @param {Object} coordSys
34335     * @param {module:echarts/data/List} data
34336     * @param {string} valueOrigin lineSeries.option.areaStyle.origin
34337     */
34338    function prepareDataCoordInfo(coordSys, data, valueOrigin) {
34339        var baseAxis = coordSys.getBaseAxis();
34340        var valueAxis = coordSys.getOtherAxis(baseAxis);
34341        var valueStart = getValueStart(valueAxis, valueOrigin);
34342
34343        var baseAxisDim = baseAxis.dim;
34344        var valueAxisDim = valueAxis.dim;
34345        var valueDim = data.mapDimension(valueAxisDim);
34346        var baseDim = data.mapDimension(baseAxisDim);
34347        var baseDataOffset = valueAxisDim === 'x' || valueAxisDim === 'radius' ? 1 : 0;
34348
34349        var dims = map(coordSys.dimensions, function (coordDim) {
34350            return data.mapDimension(coordDim);
34351        });
34352
34353        var stacked;
34354        var stackResultDim = data.getCalculationInfo('stackResultDimension');
34355        if (stacked |= isDimensionStacked(data, dims[0] /*, dims[1]*/)) { // jshint ignore:line
34356            dims[0] = stackResultDim;
34357        }
34358        if (stacked |= isDimensionStacked(data, dims[1] /*, dims[0]*/)) { // jshint ignore:line
34359            dims[1] = stackResultDim;
34360        }
34361
34362        return {
34363            dataDimsForPoint: dims,
34364            valueStart: valueStart,
34365            valueAxisDim: valueAxisDim,
34366            baseAxisDim: baseAxisDim,
34367            stacked: !!stacked,
34368            valueDim: valueDim,
34369            baseDim: baseDim,
34370            baseDataOffset: baseDataOffset,
34371            stackedOverDimension: data.getCalculationInfo('stackedOverDimension')
34372        };
34373    }
34374
34375    function getValueStart(valueAxis, valueOrigin) {
34376        var valueStart = 0;
34377        var extent = valueAxis.scale.getExtent();
34378
34379        if (valueOrigin === 'start') {
34380            valueStart = extent[0];
34381        }
34382        else if (valueOrigin === 'end') {
34383            valueStart = extent[1];
34384        }
34385        // auto
34386        else {
34387            // Both positive
34388            if (extent[0] > 0) {
34389                valueStart = extent[0];
34390            }
34391            // Both negative
34392            else if (extent[1] < 0) {
34393                valueStart = extent[1];
34394            }
34395            // If is one positive, and one negative, onZero shall be true
34396        }
34397
34398        return valueStart;
34399    }
34400
34401    function getStackedOnPoint(dataCoordInfo, coordSys, data, idx) {
34402        var value = NaN;
34403        if (dataCoordInfo.stacked) {
34404            value = data.get(data.getCalculationInfo('stackedOverDimension'), idx);
34405        }
34406        if (isNaN(value)) {
34407            value = dataCoordInfo.valueStart;
34408        }
34409
34410        var baseDataOffset = dataCoordInfo.baseDataOffset;
34411        var stackedData = [];
34412        stackedData[baseDataOffset] = data.get(dataCoordInfo.baseDim, idx);
34413        stackedData[1 - baseDataOffset] = value;
34414
34415        return coordSys.dataToPoint(stackedData);
34416    }
34417
34418    /*
34419* Licensed to the Apache Software Foundation (ASF) under one
34420* or more contributor license agreements.  See the NOTICE file
34421* distributed with this work for additional information
34422* regarding copyright ownership.  The ASF licenses this file
34423* to you under the Apache License, Version 2.0 (the
34424* "License"); you may not use this file except in compliance
34425* with the License.  You may obtain a copy of the License at
34426*
34427*   http://www.apache.org/licenses/LICENSE-2.0
34428*
34429* Unless required by applicable law or agreed to in writing,
34430* software distributed under the License is distributed on an
34431* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
34432* KIND, either express or implied.  See the License for the
34433* specific language governing permissions and limitations
34434* under the License.
34435*/
34436
34437// var arrayDiff = require('zrender/src/core/arrayDiff');
34438// 'zrender/src/core/arrayDiff' has been used before, but it did
34439// not do well in performance when roam with fixed dataZoom window.
34440
34441// function convertToIntId(newIdList, oldIdList) {
34442//     // Generate int id instead of string id.
34443//     // Compare string maybe slow in score function of arrDiff
34444
34445//     // Assume id in idList are all unique
34446//     var idIndicesMap = {};
34447//     var idx = 0;
34448//     for (var i = 0; i < newIdList.length; i++) {
34449//         idIndicesMap[newIdList[i]] = idx;
34450//         newIdList[i] = idx++;
34451//     }
34452//     for (var i = 0; i < oldIdList.length; i++) {
34453//         var oldId = oldIdList[i];
34454//         // Same with newIdList
34455//         if (idIndicesMap[oldId]) {
34456//             oldIdList[i] = idIndicesMap[oldId];
34457//         }
34458//         else {
34459//             oldIdList[i] = idx++;
34460//         }
34461//     }
34462// }
34463
34464    function diffData(oldData, newData) {
34465        var diffResult = [];
34466
34467        newData.diff(oldData)
34468            .add(function (idx) {
34469                diffResult.push({cmd: '+', idx: idx});
34470            })
34471            .update(function (newIdx, oldIdx) {
34472                diffResult.push({cmd: '=', idx: oldIdx, idx1: newIdx});
34473            })
34474            .remove(function (idx) {
34475                diffResult.push({cmd: '-', idx: idx});
34476            })
34477            .execute();
34478
34479        return diffResult;
34480    }
34481
34482    var lineAnimationDiff = function (
34483        oldData, newData,
34484        oldStackedOnPoints, newStackedOnPoints,
34485        oldCoordSys, newCoordSys,
34486        oldValueOrigin, newValueOrigin
34487    ) {
34488        var diff = diffData(oldData, newData);
34489
34490        // var newIdList = newData.mapArray(newData.getId);
34491        // var oldIdList = oldData.mapArray(oldData.getId);
34492
34493        // convertToIntId(newIdList, oldIdList);
34494
34495        // // FIXME One data ?
34496        // diff = arrayDiff(oldIdList, newIdList);
34497
34498        var currPoints = [];
34499        var nextPoints = [];
34500        // Points for stacking base line
34501        var currStackedPoints = [];
34502        var nextStackedPoints = [];
34503
34504        var status = [];
34505        var sortedIndices = [];
34506        var rawIndices = [];
34507
34508        var newDataOldCoordInfo = prepareDataCoordInfo(oldCoordSys, newData, oldValueOrigin);
34509        var oldDataNewCoordInfo = prepareDataCoordInfo(newCoordSys, oldData, newValueOrigin);
34510
34511        for (var i = 0; i < diff.length; i++) {
34512            var diffItem = diff[i];
34513            var pointAdded = true;
34514
34515            // FIXME, animation is not so perfect when dataZoom window moves fast
34516            // Which is in case remvoing or add more than one data in the tail or head
34517            switch (diffItem.cmd) {
34518                case '=':
34519                    var currentPt = oldData.getItemLayout(diffItem.idx);
34520                    var nextPt = newData.getItemLayout(diffItem.idx1);
34521                    // If previous data is NaN, use next point directly
34522                    if (isNaN(currentPt[0]) || isNaN(currentPt[1])) {
34523                        currentPt = nextPt.slice();
34524                    }
34525                    currPoints.push(currentPt);
34526                    nextPoints.push(nextPt);
34527
34528                    currStackedPoints.push(oldStackedOnPoints[diffItem.idx]);
34529                    nextStackedPoints.push(newStackedOnPoints[diffItem.idx1]);
34530
34531                    rawIndices.push(newData.getRawIndex(diffItem.idx1));
34532                    break;
34533                case '+':
34534                    var idx = diffItem.idx;
34535                    currPoints.push(
34536                        oldCoordSys.dataToPoint([
34537                            newData.get(newDataOldCoordInfo.dataDimsForPoint[0], idx),
34538                            newData.get(newDataOldCoordInfo.dataDimsForPoint[1], idx)
34539                        ])
34540                    );
34541
34542                    nextPoints.push(newData.getItemLayout(idx).slice());
34543
34544                    currStackedPoints.push(
34545                        getStackedOnPoint(newDataOldCoordInfo, oldCoordSys, newData, idx)
34546                    );
34547                    nextStackedPoints.push(newStackedOnPoints[idx]);
34548
34549                    rawIndices.push(newData.getRawIndex(idx));
34550                    break;
34551                case '-':
34552                    var idx = diffItem.idx;
34553                    var rawIndex = oldData.getRawIndex(idx);
34554                    // Data is replaced. In the case of dynamic data queue
34555                    // FIXME FIXME FIXME
34556                    if (rawIndex !== idx) {
34557                        currPoints.push(oldData.getItemLayout(idx));
34558                        nextPoints.push(newCoordSys.dataToPoint([
34559                            oldData.get(oldDataNewCoordInfo.dataDimsForPoint[0], idx),
34560                            oldData.get(oldDataNewCoordInfo.dataDimsForPoint[1], idx)
34561                        ]));
34562
34563                        currStackedPoints.push(oldStackedOnPoints[idx]);
34564                        nextStackedPoints.push(
34565                            getStackedOnPoint(oldDataNewCoordInfo, newCoordSys, oldData, idx)
34566                        );
34567
34568                        rawIndices.push(rawIndex);
34569                    }
34570                    else {
34571                        pointAdded = false;
34572                    }
34573            }
34574
34575            // Original indices
34576            if (pointAdded) {
34577                status.push(diffItem);
34578                sortedIndices.push(sortedIndices.length);
34579            }
34580        }
34581
34582        // Diff result may be crossed if all items are changed
34583        // Sort by data index
34584        sortedIndices.sort(function (a, b) {
34585            return rawIndices[a] - rawIndices[b];
34586        });
34587
34588        var sortedCurrPoints = [];
34589        var sortedNextPoints = [];
34590
34591        var sortedCurrStackedPoints = [];
34592        var sortedNextStackedPoints = [];
34593
34594        var sortedStatus = [];
34595        for (var i = 0; i < sortedIndices.length; i++) {
34596            var idx = sortedIndices[i];
34597            sortedCurrPoints[i] = currPoints[idx];
34598            sortedNextPoints[i] = nextPoints[idx];
34599
34600            sortedCurrStackedPoints[i] = currStackedPoints[idx];
34601            sortedNextStackedPoints[i] = nextStackedPoints[idx];
34602
34603            sortedStatus[i] = status[idx];
34604        }
34605
34606        return {
34607            current: sortedCurrPoints,
34608            next: sortedNextPoints,
34609
34610            stackedOnCurrent: sortedCurrStackedPoints,
34611            stackedOnNext: sortedNextStackedPoints,
34612
34613            status: sortedStatus
34614        };
34615    };
34616
34617    /*
34618* Licensed to the Apache Software Foundation (ASF) under one
34619* or more contributor license agreements.  See the NOTICE file
34620* distributed with this work for additional information
34621* regarding copyright ownership.  The ASF licenses this file
34622* to you under the Apache License, Version 2.0 (the
34623* "License");
34623 you may not use this file except in compliance
34624* with the License.  You may obtain a copy of the License at
34625*
34626*   http://www.apache.org/licenses/LICENSE-2.0
34627*
34628* Unless required by applicable law or agreed to in writing,
34629* software distributed under the License is distributed on an
34630* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
34631* KIND, either express or implied.  See the License for the
34632* specific language governing permissions and limitations
34633* under the License.
34634*/
34635
34636// Poly path support NaN point
34637
34638    var vec2Min = min;
34639    var vec2Max = max;
34640
34641    var scaleAndAdd$1 = scaleAndAdd;
34642    var v2Copy = copy;
34643
34644// Temporary variable
34645    var v = [];
34646    var cp0 = [];
34647    var cp1 = [];
34648
34649    function isPointNull(p) {
34650        return isNaN(p[0]) || isNaN(p[1]);
34651    }
34652
34653    function drawSegment(
34654        ctx, points, start, segLen, allLen,
34655        dir, smoothMin, smoothMax, smooth, smoothMonotone, connectNulls
34656    ) {
34657        // if (smoothMonotone == null) {
34658        //     if (isMono(points, 'x')) {
34659        //         return drawMono(ctx, points, start, segLen, allLen,
34660        //             dir, smoothMin, smoothMax, smooth, 'x', connectNulls);
34661        //     }
34662        //     else if (isMono(points, 'y')) {
34663        //         return drawMono(ctx, points, start, segLen, allLen,
34664        //             dir, smoothMin, smoothMax, smooth, 'y', connectNulls);
34665        //     }
34666        //     else {
34667        //         return drawNonMono.apply(this, arguments);
34668        //     }
34669        // }
34670        // else if (smoothMonotone !== 'none' && isMono(points, smoothMonotone)) {
34671        //     return drawMono.apply(this, arguments);
34672        // }
34673        // else {
34674        //     return drawNonMono.apply(this, arguments);
34675        // }
34676        if (smoothMonotone === 'none' || !smoothMonotone) {
34677            return drawNonMono.apply(this, arguments);
34678        }
34679        else {
34680            return drawMono.apply(this, arguments);
34681        }
34682    }
34683
34684    /**
34685     * Check if points is in monotone.
34686     *
34687     * @param {number[][]} points         Array of points which is in [x, y] form
34688     * @param {string}     smoothMonotone 'x', 'y', or 'none', stating for which
34689     *                                    dimension that is checking.
34690     *                                    If is 'none', `drawNonMono` should be
34691     *                                    called.
34692     *                                    If is undefined, either being monotone
34693     *                                    in 'x' or 'y' will call `drawMono`.
34694     */
34695// function isMono(points, smoothMonotone) {
34696//     if (points.length <= 1) {
34697//         return true;
34698//     }
34699
34700//     var dim = smoothMonotone === 'x' ? 0 : 1;
34701//     var last = points[0][dim];
34702//     var lastDiff = 0;
34703//     for (var i = 1; i < points.length; ++i) {
34704//         var diff = points[i][dim] - last;
34705//         if (!isNaN(diff) && !isNaN(lastDiff)
34706//             && diff !== 0 && lastDiff !== 0
34707//             && ((diff >= 0) !== (lastDiff >= 0))
34708//         ) {
34709//             return false;
34710//         }
34711//         if (!isNaN(diff) && diff !== 0) {
34712//             lastDiff = diff;
34713//             last = points[i][dim];
34714//         }
34715//     }
34716//     return true;
34717// }
34718
34719    /**
34720     * Draw smoothed line in monotone, in which only vertical or horizontal bezier
34721     * control points will be used. This should be used when points are monotone
34722     * either in x or y dimension.
34723     */
34724    function drawMono(
34725        ctx, points, start, segLen, allLen,
34726        dir, smoothMin, smoothMax, smooth, smoothMonotone, connectNulls
34727    ) {
34728        var prevIdx = 0;
34729        var idx = start;
34730        for (var k = 0; k < segLen; k++) {
34731            var p = points[idx];
34732            if (idx >= allLen || idx < 0) {
34733                break;
34734            }
34735            if (isPointNull(p)) {
34736                if (connectNulls) {
34737                    idx += dir;
34738                    continue;
34739                }
34740                break;
34741            }
34742
34743            if (idx === start) {
34744                ctx[dir > 0 ? 'moveTo' : 'lineTo'](p[0], p[1]);
34745            }
34746            else {
34747                if (smooth > 0) {
34748                    var prevP = points[prevIdx];
34749                    var dim = smoothMonotone === 'y' ? 1 : 0;
34750
34751                    // Length of control point to p, either in x or y, but not both
34752                    var ctrlLen = (p[dim] - prevP[dim]) * smooth;
34753
34754                    v2Copy(cp0, prevP);
34755                    cp0[dim] = prevP[dim] + ctrlLen;
34756
34757                    v2Copy(cp1, p);
34758                    cp1[dim] = p[dim] - ctrlLen;
34759
34760                    ctx.bezierCurveTo(
34761                        cp0[0], cp0[1],
34762                        cp1[0], cp1[1],
34763                        p[0], p[1]
34764                    );
34765                }
34766                else {
34767                    ctx.lineTo(p[0], p[1]);
34768                }
34769            }
34770
34771            prevIdx = idx;
34772            idx += dir;
34773        }
34774
34775        return k;
34776    }
34777
34778    /**
34779     * Draw smoothed line in non-monotone, in may cause undesired curve in extreme
34780     * situations. This should be used when points are non-monotone neither in x or
34781     * y dimension.
34782     */
34783    function drawNonMono(
34784        ctx, points, start, segLen, allLen,
34785        dir, smoothMin, smoothMax, smooth, smoothMonotone, connectNulls
34786    ) {
34787        var prevIdx = 0;
34788        var idx = start;
34789        for (var k = 0; k < segLen; k++) {
34790            var p = points[idx];
34791            if (idx >= allLen || idx < 0) {
34792                break;
34793            }
34794            if (isPointNull(p)) {
34795                if (connectNulls) {
34796                    idx += dir;
34797                    continue;
34798                }
34799                break;
34800            }
34801
34802            if (idx === start) {
34803                ctx[dir > 0 ? 'moveTo' : 'lineTo'](p[0], p[1]);
34804                v2Copy(cp0, p);
34805            }
34806            else {
34807                if (smooth > 0) {
34808                    var nextIdx = idx + dir;
34809                    var nextP = points[nextIdx];
34810                    if (connectNulls) {
34811                        // Find next point not null
34812                        while (nextP && isPointNull(points[nextIdx])) {
34813                            nextIdx += dir;
34814                            nextP = points[nextIdx];
34815                        }
34816                    }
34817
34818                    var ratioNextSeg = 0.5;
34819                    var prevP = points[prevIdx];
34820                    var nextP = points[nextIdx];
34821                    // Last point
34822                    if (!nextP || isPointNull(nextP)) {
34823                        v2Copy(cp1, p);
34824                    }
34825                    else {
34826                        // If next data is null in not connect case
34827                        if (isPointNull(nextP) && !connectNulls) {
34828                            nextP = p;
34829                        }
34830
34831                        sub(v, nextP, prevP);
34832
34833                        var lenPrevSeg;
34834                        var lenNextSeg;
34835                        if (smoothMonotone === 'x' || smoothMonotone === 'y') {
34836                            var dim = smoothMonotone === 'x' ? 0 : 1;
34837                            lenPrevSeg = Math.abs(p[dim] - prevP[dim]);
34838                            lenNextSeg = Math.abs(p[dim] - nextP[dim]);
34839                        }
34840                        else {
34841                            lenPrevSeg = dist(p, prevP);
34842                            lenNextSeg = dist(p, nextP);
34843                        }
34844
34845                        // Use ratio of seg length
34846                        ratioNextSeg = lenNextSeg / (lenNextSeg + lenPrevSeg);
34847
34848                        scaleAndAdd$1(cp1, p, v, -smooth * (1 - ratioNextSeg));
34849                    }
34850                    // Smooth constraint
34851                    vec2Min(cp0, cp0, smoothMax);
34852                    vec2Max(cp0, cp0, smoothMin);
34853                    vec2Min(cp1, cp1, smoothMax);
34854                    vec2Max(cp1, cp1, smoothMin);
34855
34856                    ctx.bezierCurveTo(
34857                        cp0[0], cp0[1],
34858                        cp1[0], cp1[1],
34859                        p[0], p[1]
34860                    );
34861                    // cp0 of next segment
34862                    scaleAndAdd$1(cp0, p, v, smooth * ratioNextSeg);
34863                }
34864                else {
34865                    ctx.lineTo(p[0], p[1]);
34866                }
34867            }
34868
34869            prevIdx = idx;
34870            idx += dir;
34871        }
34872
34873        return k;
34874    }
34875
34876    function getBoundingBox(points, smoothConstraint) {
34877        var ptMin = [Infinity, Infinity];
34878        var ptMax = [-Infinity, -Infinity];
34879        if (smoothConstraint) {
34880            for (var i = 0; i < points.length; i++) {
34881                var pt = points[i];
34882                if (pt[0] < ptMin[0]) { ptMin[0] = pt[0]; }
34883                if (pt[1] < ptMin[1]) { ptMin[1] = pt[1]; }
34884                if (pt[0] > ptMax[0]) { ptMax[0] = pt[0]; }
34885                if (pt[1] > ptMax[1]) { ptMax[1] = pt[1]; }
34886            }
34887        }
34888        return {
34889            min: smoothConstraint ? ptMin : ptMax,
34890            max: smoothConstraint ? ptMax : ptMin
34891        };
34892    }
34893
34894    var Polyline$1 = Path.extend({
34895
34896        type: 'ec-polyline',
34897
34898        shape: {
34899            points: [],
34900
34901            smooth: 0,
34902
34903            smoothConstraint: true,
34904
34905            smoothMonotone: null,
34906
34907            connectNulls: false
34908        },
34909
34910        style: {
34911            fill: null,
34912
34913            stroke: '#000'
34914        },
34915
34916        brush: fixClipWithShadow(Path.prototype.brush),
34917
34918        buildPath: function (ctx, shape) {
34919            var points = shape.points;
34920
34921            var i = 0;
34922            var len$$1 = points.length;
34923
34924            var result = getBoundingBox(points, shape.smoothConstraint);
34925
34926            if (shape.connectNulls) {
34927                // Must remove first and last null values avoid draw error in polygon
34928                for (; len$$1 > 0; len$$1--) {
34929                    if (!isPointNull(points[len$$1 - 1])) {
34930                        break;
34931                    }
34932                }
34933                for (; i < len$$1; i++) {
34934                    if (!isPointNull(points[i])) {
34935                        break;
34936                    }
34937                }
34938            }
34939            while (i < len$$1) {
34940                i += drawSegment(
34941                    ctx, points, i, len$$1, len$$1,
34942                    1, result.min, result.max, shape.smooth,
34943                    shape.smoothMonotone, shape.connectNulls
34944                ) + 1;
34945            }
34946        }
34947    });
34948
34949    var Polygon$1 = Path.extend({
34950
34951        type: 'ec-polygon',
34952
34953        shape: {
34954            points: [],
34955
34956            // Offset between stacked base points and points
34957            stackedOnPoints: [],
34958
34959            smooth: 0,
34960
34961            stackedOnSmooth: 0,
34962
34963            smoothConstraint: true,
34964
34965            smoothMonotone: null,
34966
34967            connectNulls: false
34968        },
34969
34970        brush: fixClipWithShadow(Path.prototype.brush),
34971
34972        buildPath: function (ctx, shape) {
34973            var points = shape.points;
34974            var stackedOnPoints = shape.stackedOnPoints;
34975
34976            var i = 0;
34977            var len$$1 = points.length;
34978            var smoothMonotone = shape.smoothMonotone;
34979            var bbox = getBoundingBox(points, shape.smoothConstraint);
34980            var stackedOnBBox = getBoundingBox(stackedOnPoints, shape.smoothConstraint);
34981
34982            if (shape.connectNulls) {
34983                // Must remove first and last null values avoid draw error in polygon
34984                for (; len$$1 > 0; len$$1--) {
34985                    if (!isPointNull(points[len$$1 - 1])) {
34986                        break;
34987                    }
34988                }
34989                for (; i < len$$1; i++) {
34990                    if (!isPointNull(points[i])) {
34991                        break;
34992                    }
34993                }
34994            }
34995            while (i < len$$1) {
34996                var k = drawSegment(
34997                    ctx, points, i, len$$1, len$$1,
34998                    1, bbox.min, bbox.max, shape.smooth,
34999                    smoothMonotone, shape.connectNulls
35000                );
35001                drawSegment(
35002                    ctx, stackedOnPoints, i + k - 1, k, len$$1,
35003                    -1, stackedOnBBox.min, stackedOnBBox.max, shape.stackedOnSmooth,
35004                    smoothMonotone, shape.connectNulls
35005                );
35006                i += k + 1;
35007
35008                ctx.closePath();
35009            }
35010        }
35011    });
35012
35013    /*
35014* Licensed to the Apache Software Foundation (ASF) under one
35015* or more contributor license agreements.  See the NOTICE file
35016* distributed with this work for additional information
35017* regarding copyright ownership.  The ASF licenses this file
35018* to you under the Apache License, Version 2.0 (the
35019* "License");
35019 you may not use this file except in compliance
35020* with the License.  You may obtain a copy of the License at
35021*
35022*   http://www.apache.org/licenses/LICENSE-2.0
35023*
35024* Unless required by applicable law or agreed to in writing,
35025* software distributed under the License is distributed on an
35026* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
35027* KIND, either express or implied.  See the License for the
35028* specific language governing permissions and limitations
35029* under the License.
35030*/
35031
35032// FIXME step not support polar
35033
35034    function isPointsSame(points1, points2) {
35035        if (points1.length !== points2.length) {
35036            return;
35037        }
35038        for (var i = 0; i < points1.length; i++) {
35039            var p1 = points1[i];
35040            var p2 = points2[i];
35041            if (p1[0] !== p2[0] || p1[1] !== p2[1]) {
35042                return;
35043            }
35044        }
35045        return true;
35046    }
35047
35048    function getSmooth(smooth) {
35049        return typeof (smooth) === 'number' ? smooth : (smooth ? 0.5 : 0);
35050    }
35051
35052    function getAxisExtentWithGap(axis) {
35053        var extent = axis.getGlobalExtent();
35054        if (axis.onBand) {
35055            // Remove extra 1px to avoid line miter in clipped edge
35056            var halfBandWidth = axis.getBandWidth() / 2 - 1;
35057            var dir = extent[1] > extent[0] ? 1 : -1;
35058            extent[0] += dir * halfBandWidth;
35059            extent[1] -= dir * halfBandWidth;
35060        }
35061        return extent;
35062    }
35063
35064    /**
35065     * @param {module:echarts/coord/cartesian/Cartesian2D|module:echarts/coord/polar/Polar} coordSys
35066     * @param {module:echarts/data/List} data
35067     * @param {Object} dataCoordInfo
35068     * @param {Array.<Array.<number>>} points
35069     */
35070    function getStackedOnPoints(coordSys, data, dataCoordInfo) {
35071        if (!dataCoordInfo.valueDim) {
35072            return [];
35073        }
35074
35075        var points = [];
35076        for (var idx = 0, len = data.count(); idx < len; idx++) {
35077            points.push(getStackedOnPoint(dataCoordInfo, coordSys, data, idx));
35078        }
35079
35080        return points;
35081    }
35082
35083    function createGridClipShape(cartesian, hasAnimation, forSymbol, seriesModel) {
35084        var xExtent = getAxisExtentWithGap(cartesian.getAxis('x'));
35085        var yExtent = getAxisExtentWithGap(cartesian.getAxis('y'));
35086        var isHorizontal = cartesian.getBaseAxis().isHorizontal();
35087
35088        var x = Math.min(xExtent[0], xExtent[1]);
35089        var y = Math.min(yExtent[0], yExtent[1]);
35090        var width = Math.max(xExtent[0], xExtent[1]) - x;
35091        var height = Math.max(yExtent[0], yExtent[1]) - y;
35092
35093        // Avoid float number rounding error for symbol on the edge of axis extent.
35094        // See #7913 and `test/dataZoom-clip.html`.
35095        if (forSymbol) {
35096            x -= 0.5;
35097            width += 0.5;
35098            y -= 0.5;
35099            height += 0.5;
35100        }
35101        else {
35102            var lineWidth = seriesModel.get('lineStyle.width') || 2;
35103            // Expand clip shape to avoid clipping when line value exceeds axis
35104            var expandSize = seriesModel.get('clipOverflow') ? lineWidth / 2 : Math.max(width, height);
35105            if (isHorizontal) {
35106                y -= expandSize;
35107                height += expandSize * 2;
35108            }
35109            else {
35110                x -= expandSize;
35111                width += expandSize * 2;
35112            }
35113        }
35114
35115        var clipPath = new Rect({
35116            shape: {
35117                x: x,
35118                y: y,
35119                width: width,
35120                height: height
35121            }
35122        });
35123
35124        if (hasAnimation) {
35125            clipPath.shape[isHorizontal ? 'width' : 'height'] = 0;
35126            initProps(clipPath, {
35127                shape: {
35128                    width: width,
35129                    height: height
35130                }
35131            }, seriesModel);
35132        }
35133
35134        return clipPath;
35135    }
35136
35137    function createPolarClipShape(polar, hasAnimation, forSymbol, seriesModel) {
35138        var angleAxis = polar.getAngleAxis();
35139        var radiusAxis = polar.getRadiusAxis();
35140
35141        var radiusExtent = radiusAxis.getExtent().slice();
35142        radiusExtent[0] > radiusExtent[1] && radiusExtent.reverse();
35143        var angleExtent = angleAxis.getExtent();
35144
35145        var RADIAN = Math.PI / 180;
35146
35147        // Avoid float number rounding error for symbol on the edge of axis extent.
35148        if (forSymbol) {
35149            radiusExtent[0] -= 0.5;
35150            radiusExtent[1] += 0.5;
35151        }
35152
35153        var clipPath = new Sector({
35154            shape: {
35155                cx: round$1(polar.cx, 1),
35156                cy: round$1(polar.cy, 1),
35157                r0: round$1(radiusExtent[0], 1),
35158                r: round$1(radiusExtent[1], 1),
35159                startAngle: -angleExtent[0] * RADIAN,
35160                endAngle: -angleExtent[1] * RADIAN,
35161                clockwise: angleAxis.inverse
35162            }
35163        });
35164
35165        if (hasAnimation) {
35166            clipPath.shape.endAngle = -angleExtent[0] * RADIAN;
35167            initProps(clipPath, {
35168                shape: {
35169                    endAngle: -angleExtent[1] * RADIAN
35170                }
35171            }, seriesModel);
35172        }
35173
35174        return clipPath;
35175    }
35176
35177    function createClipShape(coordSys, hasAnimation, forSymbol, seriesModel) {
35178        return coordSys.type === 'polar'
35179            ? createPolarClipShape(coordSys, hasAnimation, forSymbol, seriesModel)
35180            : createGridClipShape(coordSys, hasAnimation, forSymbol, seriesModel);
35181    }
35182
35183    function turnPointsIntoStep(points, coordSys, stepTurnAt) {
35184        var baseAxis = coordSys.getBaseAxis();
35185        var baseIndex = baseAxis.dim === 'x' || baseAxis.dim === 'radius' ? 0 : 1;
35186
35187        var stepPoints = [];
35188        for (var i = 0; i < points.length - 1; i++) {
35189            var nextPt = points[i + 1];
35190            var pt = points[i];
35191            stepPoints.push(pt);
35192
35193            var stepPt = [];
35194            switch (stepTurnAt) {
35195                case 'end':
35196                    stepPt[baseIndex] = nextPt[baseIndex];
35197                    stepPt[1 - baseIndex] = pt[1 - baseIndex];
35198                    // default is start
35199                    stepPoints.push(stepPt);
35200                    break;
35201                case 'middle':
35202                    // default is start
35203                    var middle = (pt[baseIndex] + nextPt[baseIndex]) / 2;
35204                    var stepPt2 = [];
35205                    stepPt[baseIndex] = stepPt2[baseIndex] = middle;
35206                    stepPt[1 - baseIndex] = pt[1 - baseIndex];
35207                    stepPt2[1 - baseIndex] = nextPt[1 - baseIndex];
35208                    stepPoints.push(stepPt);
35209                    stepPoints.push(stepPt2);
35210                    break;
35211                default:
35212                    stepPt[baseIndex] = pt[baseIndex];
35213                    stepPt[1 - baseIndex] = nextPt[1 - baseIndex];
35214                    // default is start
35215                    stepPoints.push(stepPt);
35216            }
35217        }
35218        // Last points
35219        points[i] && stepPoints.push(points[i]);
35220        return stepPoints;
35221    }
35222
35223    function getVisualGradient(data, coordSys) {
35224        var visualMetaList = data.getVisual('visualMeta');
35225        if (!visualMetaList || !visualMetaList.length || !data.count()) {
35226            // When data.count() is 0, gradient range can not be calculated.
35227            return;
35228        }
35229
35230        if (coordSys.type !== 'cartesian2d') {
35231            if (__DEV__) {
35232                console.warn('Visual map on line style is only supported on cartesian2d.');
35233            }
35234            return;
35235        }
35236
35237        var coordDim;
35238        var visualMeta;
35239
35240        for (var i = visualMetaList.length - 1; i >= 0; i--) {
35241            var dimIndex = visualMetaList[i].dimension;
35242            var dimName = data.dimensions[dimIndex];
35243            var dimInfo = data.getDimensionInfo(dimName);
35244            coordDim = dimInfo && dimInfo.coordDim;
35245            // Can only be x or y
35246            if (coordDim === 'x' || coordDim === 'y') {
35247                visualMeta = visualMetaList[i];
35248                break;
35249            }
35250        }
35251
35252        if (!visualMeta) {
35253            if (__DEV__) {
35254                console.warn('Visual map on line style only support x or y dimension.');
35255            }
35256            return;
35257        }
35258
35259        // If the area to be rendered is bigger than area defined by LinearGradient,
35260        // the canvas spec prescribes that the color of the first stop and the last
35261        // stop should be used. But if two stops are added at offset 0, in effect
35262        // browsers use the color of the second stop to render area outside
35263        // LinearGradient. So we can only infinitesimally extend area defined in
35264        // LinearGradient to render `outerColors`.
35265
35266        var axis = coordSys.getAxis(coordDim);
35267
35268        // dataToCoor mapping may not be linear, but must be monotonic.
35269        var colorStops = map(visualMeta.stops, function (stop) {
35270            return {
35271                coord: axis.toGlobalCoord(axis.dataToCoord(stop.value)),
35272                color: stop.color
35273            };
35274        });
35275        var stopLen = colorStops.length;
35276        var outerColors = visualMeta.outerColors.slice();
35277
35278        if (stopLen && colorStops[0].coord > colorStops[stopLen - 1].coord) {
35279            colorStops.reverse();
35280            outerColors.reverse();
35281        }
35282
35283        var tinyExtent = 10; // Arbitrary value: 10px
35284        var minCoord = colorStops[0].coord - tinyExtent;
35285        var maxCoord = colorStops[stopLen - 1].coord + tinyExtent;
35286        var coordSpan = maxCoord - minCoord;
35287
35288        if (coordSpan < 1e-3) {
35289            return 'transparent';
35290        }
35291
35292        each$1(colorStops, function (stop) {
35293            stop.offset = (stop.coord - minCoord) / coordSpan;
35294        });
35295        colorStops.push({
35296            offset: stopLen ? colorStops[stopLen - 1].offset : 0.5,
35297            color: outerColors[1] || 'transparent'
35298        });
35299        colorStops.unshift({ // notice colorStops.length have been changed.
35300            offset: stopLen ? colorStops[0].offset : 0.5,
35301            color: outerColors[0] || 'transparent'
35302        });
35303
35304        // zrUtil.each(colorStops, function (colorStop) {
35305        //     // Make sure each offset has rounded px to avoid not sharp edge
35306        //     colorStop.offset = (Math.round(colorStop.offset * (end - start) + start) - start) / (end - start);
35307        // });
35308
35309        var gradient = new LinearGradient(0, 0, 0, 0, colorStops, true);
35310        gradient[coordDim] = minCoord;
35311        gradient[coordDim + '2'] = maxCoord;
35312
35313        return gradient;
35314    }
35315
35316    function getIsIgnoreFunc(seriesModel, data, coordSys) {
35317        var showAllSymbol = seriesModel.get('showAllSymbol');
35318        var isAuto = showAllSymbol === 'auto';
35319
35320        if (showAllSymbol && !isAuto) {
35321            return;
35322        }
35323
35324        var categoryAxis = coordSys.getAxesByScale('ordinal')[0];
35325        if (!categoryAxis) {
35326            return;
35327        }
35328
35329        // Note that category label interval strategy might bring some weird effect
35330        // in some scenario: users may wonder why some of the symbols are not
35331        // displayed. So we show all symbols as possible as we can.
35332        if (isAuto
35333            // Simplify the logic, do not determine label overlap here.
35334            && canShowAllSymbolForCategory(categoryAxis, data)
35335        ) {
35336            return;
35337        }
35338
35339        // Otherwise follow the label interval strategy on category axis.
35340        var categoryDataDim = data.mapDimension(categoryAxis.dim);
35341        var labelMap = {};
35342
35343        each$1(categoryAxis.getViewLabels(), function (labelItem) {
35344            labelMap[labelItem.tickValue] = 1;
35345        });
35346
35347        return function (dataIndex) {
35348            return !labelMap.hasOwnProperty(data.get(categoryDataDim, dataIndex));
35349        };
35350    }
35351
35352    function canShowAllSymbolForCategory(categoryAxis, data) {
35353        // In mose cases, line is monotonous on category axis, and the label size
35354        // is close with each other. So we check the symbol size and some of the
35355        // label size alone with the category axis to estimate whether all symbol
35356        // can be shown without overlap.
35357        var axisExtent = categoryAxis.getExtent();
35358        var availSize = Math.abs(axisExtent[1] - axisExtent[0]) / categoryAxis.scale.count();
35359        isNaN(availSize) && (availSize = 0); // 0/0 is NaN.
35360
35361        // Sampling some points, max 5.
35362        var dataLen = data.count();
35363        var step = Math.max(1, Math.round(dataLen / 5));
35364        for (var dataIndex = 0; dataIndex < dataLen; dataIndex += step) {
35365            if (SymbolClz$1.getSymbolSize(
35366                    data, dataIndex
35367                    // Only for cartesian, where `isHorizontal` exists.
35368                )[categoryAxis.isHorizontal() ? 1 : 0]
35369                // Empirical number
35370                * 1.5 > availSize
35371            ) {
35372                return false;
35373            }
35374        }
35375
35376        return true;
35377    }
35378
35379    Chart.extend({
35380
35381        type: 'line',
35382
35383        init: function () {
35384            var lineGroup = new Group();
35385
35386            var symbolDraw = new SymbolDraw();
35387            this.group.add(symbolDraw.group);
35388
35389            this._symbolDraw = symbolDraw;
35390            this._lineGroup = lineGroup;
35391        },
35392
35393        render: function (seriesModel, ecModel, api) {
35394            var coordSys = seriesModel.coordinateSystem;
35395            var group = this.group;
35396            var data = seriesModel.getData();
35397            var lineStyleModel = seriesModel.getModel('lineStyle');
35398            var areaStyleModel = seriesModel.getModel('areaStyle');
35399
35400            var points = data.mapArray(data.getItemLayout);
35401
35402            var isCoordSysPolar = coordSys.type === 'polar';
35403            var prevCoordSys = this._coordSys;
35404
35405            var symbolDraw = this._symbolDraw;
35406            var polyline = this._polyline;
35407            var polygon = this._polygon;
35408
35409            var lineGroup = this._lineGroup;
35410
35411            var hasAnimation = seriesModel.get('animation');
35412
35413            var isAreaChart = !areaStyleModel.isEmpty();
35414
35415            var valueOrigin = areaStyleModel.get('origin');
35416            var dataCoordInfo = prepareDataCoordInfo(coordSys, data, valueOrigin);
35417
35418            var stackedOnPoints = getStackedOnPoints(coordSys, data, dataCoordInfo);
35419
35420            var showSymbol = seriesModel.get('showSymbol');
35421
35422            var isIgnoreFunc = showSymbol && !isCoordSysPolar
35423                && getIsIgnoreFunc(seriesModel, data, coordSys);
35424
35425            // Remove temporary symbols
35426            var oldData = this._data;
35427            oldData && oldData.eachItemGraphicEl(function (el, idx) {
35428                if (el.__temp) {
35429                    group.remove(el);
35430                    oldData.setItemGraphicEl(idx, null);
35431                }
35432            });
35433
35434            // Remove previous created symbols if showSymbol changed to false
35435            if (!showSymbol) {
35436                symbolDraw.remove();
35437            }
35438
35439            group.add(lineGroup);
35440
35441            // FIXME step not support polar
35442            var step = !isCoordSysPolar && seriesModel.get('step');
35443            // Initialization animation or coordinate system changed
35444            if (
35445                !(polyline && prevCoordSys.type === coordSys.type && step === this._step)
35446            ) {
35447                showSymbol && symbolDraw.updateData(data, {
35448                    isIgnore: isIgnoreFunc,
35449                    clipShape: createClipShape(coordSys, false, true, seriesModel)
35450                });
35451
35452                if (step) {
35453                    // TODO If stacked series is not step
35454                    points = turnPointsIntoStep(points, coordSys, step);
35455                    stackedOnPoints = turnPointsIntoStep(stackedOnPoints, coordSys, step);
35456                }
35457
35458                polyline = this._newPolyline(points, coordSys, hasAnimation);
35459                if (isAreaChart) {
35460                    polygon = this._newPolygon(
35461                        points, stackedOnPoints,
35462                        coordSys, hasAnimation
35463                    );
35464                }
35465                lineGroup.setClipPath(createClipShape(coordSys, true, false, seriesModel));
35466            }
35467            else {
35468                if (isAreaChart && !polygon) {
35469                    // If areaStyle is added
35470                    polygon = this._newPolygon(
35471                        points, stackedOnPoints,
35472                        coordSys, hasAnimation
35473                    );
35474                }
35475                else if (polygon && !isAreaChart) {
35476                    // If areaStyle is removed
35477                    lineGroup.remove(polygon);
35478                    polygon = this._polygon = null;
35479                }
35480
35481                // Update clipPath
35482                lineGroup.setClipPath(createClipShape(coordSys, false, false, seriesModel));
35483
35484                // Always update, or it is wrong in the case turning on legend
35485                // because points are not changed
35486                showSymbol && symbolDraw.updateData(data, {
35487                    isIgnore: isIgnoreFunc,
35488                    clipShape: createClipShape(coordSys, false, true, seriesModel)
35489                });
35490
35491                // Stop symbol animation and sync with line points
35492                // FIXME performance?
35493                data.eachItemGraphicEl(function (el) {
35494                    el.stopAnimation(true);
35495                });
35496
35497                // In the case data zoom triggerred refreshing frequently
35498                // Data may not change if line has a category axis. So it should animate nothing
35499                if (!isPointsSame(this._stackedOnPoints, stackedOnPoints)
35500                    || !isPointsSame(this._points, points)
35501                ) {
35502                    if (hasAnimation) {
35503                        this._updateAnimation(
35504                            data, stackedOnPoints, coordSys, api, step, valueOrigin
35505                        );
35506                    }
35507                    else {
35508                        // Not do it in update with animation
35509                        if (step) {
35510                            // TODO If stacked series is not step
35511                            points = turnPointsIntoStep(points, coordSys, step);
35512                            stackedOnPoints = turnPointsIntoStep(stackedOnPoints, coordSys, step);
35513                        }
35514
35515                        polyline.setShape({
35516                            points: points
35517                        });
35518                        polygon && polygon.setShape({
35519                            points: points,
35520                            stackedOnPoints: stackedOnPoints
35521                        });
35522                    }
35523                }
35524            }
35525
35526            var visualColor = getVisualGradient(data, coordSys) || data.getVisual('color');
35527
35528            polyline.useStyle(defaults(
35529                // Use color in lineStyle first
35530                lineStyleModel.getLineStyle(),
35531                {
35532                    fill: 'none',
35533                    stroke: visualColor,
35534                    lineJoin: 'bevel'
35535                }
35536            ));
35537
35538            var smooth = seriesModel.get('smooth');
35539            smooth = getSmooth(seriesModel.get('smooth'));
35540            polyline.setShape({
35541                smooth: smooth,
35542                smoothMonotone: seriesModel.get('smoothMonotone'),
35543                connectNulls: seriesModel.get('connectNulls')
35544            });
35545
35546            if (polygon) {
35547                var stackedOnSeries = data.getCalculationInfo('stackedOnSeries');
35548                var stackedOnSmooth = 0;
35549
35550                polygon.useStyle(defaults(
35551                    areaStyleModel.getAreaStyle(),
35552                    {
35553                        fill: visualColor,
35554                        opacity: 0.7,
35555                        lineJoin: 'bevel'
35556                    }
35557                ));
35558
35559                if (stackedOnSeries) {
35560                    stackedOnSmooth = getSmooth(stackedOnSeries.get('smooth'));
35561                }
35562
35563                polygon.setShape({
35564                    smooth: smooth,
35565                    stackedOnSmooth: stackedOnSmooth,
35566                    smoothMonotone: seriesModel.get('smoothMonotone'),
35567                    connectNulls: seriesModel.get('connectNulls')
35568                });
35569            }
35570
35571            this._data = data;
35572            // Save the coordinate system for transition animation when data changed
35573            this._coordSys = coordSys;
35574            this._stackedOnPoints = stackedOnPoints;
35575            this._points = points;
35576            this._step = step;
35577            this._valueOrigin = valueOrigin;
35578        },
35579
35580        dispose: function () {},
35581
35582        highlight: function (seriesModel, ecModel, api, payload) {
35583            var data = seriesModel.getData();
35584            var dataIndex = queryDataIndex(data, payload);
35585
35586            if (!(dataIndex instanceof Array) && dataIndex != null && dataIndex >= 0) {
35587                var symbol = data.getItemGraphicEl(dataIndex);
35588                if (!symbol) {
35589                    // Create a temporary symbol if it is not exists
35590                    var pt = data.getItemLayout(dataIndex);
35591                    if (!pt) {
35592                        // Null data
35593                        return;
35594                    }
35595                    symbol = new SymbolClz$1(data, dataIndex);
35596                    symbol.position = pt;
35597                    symbol.setZ(
35598                        seriesModel.get('zlevel'),
35599                        seriesModel.get('z')
35600                    );
35601                    symbol.ignore = isNaN(pt[0]) || isNaN(pt[1]);
35602                    symbol.__temp = true;
35603                    data.setItemGraphicEl(dataIndex, symbol);
35604
35605                    // Stop scale animation
35606                    symbol.stopSymbolAnimation(true);
35607
35608                    this.group.add(symbol);
35609                }
35610                symbol.highlight();
35611            }
35612            else {
35613                // Highlight whole series
35614                Chart.prototype.highlight.call(
35615                    this, seriesModel, ecModel, api, payload
35616                );
35617            }
35618        },
35619
35620        downplay: function (seriesModel, ecModel, api, payload) {
35621            var data = seriesModel.getData();
35622            var dataIndex = queryDataIndex(data, payload);
35623            if (dataIndex != null && dataIndex >= 0) {
35624                var symbol = data.getItemGraphicEl(dataIndex);
35625                if (symbol) {
35626                    if (symbol.__temp) {
35627                        data.setItemGraphicEl(dataIndex, null);
35628                        this.group.remove(symbol);
35629                    }
35630                    else {
35631                        symbol.downplay();
35632                    }
35633                }
35634            }
35635            else {
35636                // FIXME
35637                // can not downplay completely.
35638                // Downplay whole series
35639                Chart.prototype.downplay.call(
35640                    this, seriesModel, ecModel, api, payload
35641                );
35642            }
35643        },
35644
35645        /**
35646         * @param {module:zrender/container/Group} group
35647         * @param {Array.<Array.<number>>} points
35648         * @private
35649         */
35650        _newPolyline: function (points) {
35651            var polyline = this._polyline;
35652            // Remove previous created polyline
35653            if (polyline) {
35654                this._lineGroup.remove(polyline);
35655            }
35656
35657            polyline = new Polyline$1({
35658                shape: {
35659                    points: points
35660                },
35661                silent: true,
35662                z2: 10
35663            });
35664
35665            this._lineGroup.add(polyline);
35666
35667            this._polyline = polyline;
35668
35669            return polyline;
35670        },
35671
35672        /**
35673         * @param {module:zrender/container/Group} group
35674         * @param {Array.<Array.<number>>} stackedOnPoints
35675         * @param {Array.<Array.<number>>} points
35676         * @private
35677         */
35678        _newPolygon: function (points, stackedOnPoints) {
35679            var polygon = this._polygon;
35680            // Remove previous created polygon
35681            if (polygon) {
35682                this._lineGroup.remove(polygon);
35683            }
35684
35685            polygon = new Polygon$1({
35686                shape: {
35687                    points: points,
35688                    stackedOnPoints: stackedOnPoints
35689                },
35690                silent: true
35691            });
35692
35693            this._lineGroup.add(polygon);
35694
35695            this._polygon = polygon;
35696            return polygon;
35697        },
35698
35699        /**
35700         * @private
35701         */
35702        // FIXME Two value axis
35703        _updateAnimation: function (data, stackedOnPoints, coordSys, api, step, valueOrigin) {
35704            var polyline = this._polyline;
35705            var polygon = this._polygon;
35706            var seriesModel = data.hostModel;
35707
35708            var diff = lineAnimationDiff(
35709                this._data, data,
35710                this._stackedOnPoints, stackedOnPoints,
35711                this._coordSys, coordSys,
35712                this._valueOrigin, valueOrigin
35713            );
35714
35715            var current = diff.current;
35716            var stackedOnCurrent = diff.stackedOnCurrent;
35717            var next = diff.next;
35718            var stackedOnNext = diff.stackedOnNext;
35719            if (step) {
35720                // TODO If stacked series is not step
35721                current = turnPointsIntoStep(diff.current, coordSys, step);
35722                stackedOnCurrent = turnPointsIntoStep(diff.stackedOnCurrent, coordSys, step);
35723                next = turnPointsIntoStep(diff.next, coordSys, step);
35724                stackedOnNext = turnPointsIntoStep(diff.stackedOnNext, coordSys, step);
35725            }
35726            // `diff.current` is subset of `current` (which should be ensured by
35727            // turnPointsIntoStep), so points in `__points` can be updated when
35728            // points in `current` are update during animation.
35729            polyline.shape.__points = diff.current;
35730            polyline.shape.points = current;
35731
35732            updateProps(polyline, {
35733                shape: {
35734                    points: next
35735                }
35736            }, seriesModel);
35737
35738            if (polygon) {
35739                polygon.setShape({
35740                    points: current,
35741                    stackedOnPoints: stackedOnCurrent
35742                });
35743                updateProps(polygon, {
35744                    shape: {
35745                        points: next,
35746                        stackedOnPoints: stackedOnNext
35747                    }
35748                }, seriesModel);
35749            }
35750
35751            var updatedDataInfo = [];
35752            var diffStatus = diff.status;
35753
35754            for (var i = 0; i < diffStatus.length; i++) {
35755                var cmd = diffStatus[i].cmd;
35756                if (cmd === '=') {
35757                    var el = data.getItemGraphicEl(diffStatus[i].idx1);
35758                    if (el) {
35759                        updatedDataInfo.push({
35760                            el: el,
35761                            ptIdx: i    // Index of points
35762                        });
35763                    }
35764                }
35765            }
35766
35767            if (polyline.animators && polyline.animators.length) {
35768                polyline.animators[0].during(function () {
35769                    for (var i = 0; i < updatedDataInfo.length; i++) {
35770                        var el = updatedDataInfo[i].el;
35771                        el.attr('position', polyline.shape.__points[updatedDataInfo[i].ptIdx]);
35772                    }
35773                });
35774            }
35775        },
35776
35777        remove: function (ecModel) {
35778            var group = this.group;
35779            var oldData = this._data;
35780            this._lineGroup.removeAll();
35781            this._symbolDraw.remove(true);
35782            // Remove temporary created elements when highlighting
35783            oldData && oldData.eachItemGraphicEl(function (el, idx) {
35784                if (el.__temp) {
35785                    group.remove(el);
35786                    oldData.setItemGraphicEl(idx, null);
35787                }
35788            });
35789
35790            this._polyline =
35791                this._polygon =
35792                    this._coordSys =
35793                        this._points =
35794                            this._stackedOnPoints =
35795                                this._data = null;
35796        }
35797    });
35798
35799    /*
35800* Licensed to the Apache Software Foundation (ASF) under one
35801* or more contributor license agreements.  See the NOTICE file
35802* distributed with this work for additional information
35803* regarding copyright ownership.  The ASF licenses this file
35804* to you under the Apache License, Version 2.0 (the
35805* "License"); you may not use this file except in compliance
35806* with the License.  You may obtain a copy of the License at
35807*
35808*   http://www.apache.org/licenses/LICENSE-2.0
35809*
35810* Unless required by applicable law or agreed to in writing,
35811* software distributed under the License is distributed on an
35812* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
35813* KIND, either express or implied.  See the License for the
35814* specific language governing permissions and limitations
35815* under the License.
35816*/
35817
35818
35819    var visualSymbol = function (seriesType, defaultSymbolType, legendSymbol) {
35820        // Encoding visual for all series include which is filtered for legend drawing
35821        return {
35822            seriesType: seriesType,
35823
35824            // For legend.
35825            performRawSeries: true,
35826
35827            reset: function (seriesModel, ecModel, api) {
35828                var data = seriesModel.getData();
35829
35830                var symbolType = seriesModel.get('symbol') || defaultSymbolType;
35831                var symbolSize = seriesModel.get('symbolSize');
35832                var keepAspect = seriesModel.get('symbolKeepAspect');
35833
35834                data.setVisual({
35835                    legendSymbol: legendSymbol || symbolType,
35836                    symbol: symbolType,
35837                    symbolSize: symbolSize,
35838                    symbolKeepAspect: keepAspect
35839                });
35840
35841                // Only visible series has each data be visual encoded
35842                if (ecModel.isSeriesFiltered(seriesModel)) {
35843                    return;
35844                }
35845
35846                var hasCallback = typeof symbolSize === 'function';
35847
35848                function dataEach(data, idx) {
35849                    if (typeof symbolSize === 'function') {
35850                        var rawValue = seriesModel.getRawValue(idx);
35851                        // FIXME
35852                        var params = seriesModel.getDataParams(idx);
35853                        data.setItemVisual(idx, 'symbolSize', symbolSize(rawValue, params));
35854                    }
35855
35856                    if (data.hasItemOption) {
35857                        var itemModel = data.getItemModel(idx);
35858                        var itemSymbolType = itemModel.getShallow('symbol', true);
35859                        var itemSymbolSize = itemModel.getShallow('symbolSize',
35860                            true);
35861                        var itemSymbolKeepAspect =
35862                            itemModel.getShallow('symbolKeepAspect',true);
35863
35864                        // If has item symbol
35865                        if (itemSymbolType != null) {
35866                            data.setItemVisual(idx, 'symbol', itemSymbolType);
35867                        }
35868                        if (itemSymbolSize != null) {
35869                            // PENDING Transform symbolSize ?
35870                            data.setItemVisual(idx, 'symbolSize', itemSymbolSize);
35871                        }
35872                        if (itemSymbolKeepAspect != null) {
35873                            data.setItemVisual(idx, 'symbolKeepAspect',
35874                                itemSymbolKeepAspect);
35875                        }
35876                    }
35877                }
35878
35879                return { dataEach: (data.hasItemOption || hasCallback) ? dataEach : null };
35880            }
35881        };
35882    };
35883
35884    /*
35885* Licensed to the Apache Software Foundation (ASF) under one
35886* or more contributor license agreements.  See the NOTICE file
35887* distributed with this work for additional information
35888* regarding copyright ownership.  The ASF licenses this file
35889* to you under the Apache License, Version 2.0 (the
35890* "License");
35890 you may not use this file except in compliance
35891* with the License.  You may obtain a copy of the License at
35892*
35893*   http://www.apache.org/licenses/LICENSE-2.0
35894*
35895* Unless required by applicable law or agreed to in writing,
35896* software distributed under the License is distributed on an
35897* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
35898* KIND, either express or implied.  See the License for the
35899* specific language governing permissions and limitations
35900* under the License.
35901*/
35902
35903    var pointsLayout = function (seriesType) {
35904        return {
35905            seriesType: seriesType,
35906
35907            plan: createRenderPlanner(),
35908
35909            reset: function (seriesModel) {
35910                var data = seriesModel.getData();
35911                var coordSys = seriesModel.coordinateSystem;
35912                var pipelineContext = seriesModel.pipelineContext;
35913                var isLargeRender = pipelineContext.large;
35914
35915                if (!coordSys) {
35916                    return;
35917                }
35918
35919                var dims = map(coordSys.dimensions, function (dim) {
35920                    return data.mapDimension(dim);
35921                }).slice(0, 2);
35922                var dimLen = dims.length;
35923
35924                var stackResultDim = data.getCalculationInfo('stackResultDimension');
35925                if (isDimensionStacked(data, dims[0] /*, dims[1]*/)) {
35926                    dims[0] = stackResultDim;
35927                }
35928                if (isDimensionStacked(data, dims[1] /*, dims[0]*/)) {
35929                    dims[1] = stackResultDim;
35930                }
35931
35932                function progress(params, data) {
35933                    var segCount = params.end - params.start;
35934                    var points = isLargeRender && new Float32Array(segCount * dimLen);
35935
35936                    for (var i = params.start, offset = 0, tmpIn = [], tmpOut = []; i < params.end; i++) {
35937                        var point;
35938
35939                        if (dimLen === 1) {
35940                            var x = data.get(dims[0], i);
35941                            point = !isNaN(x) && coordSys.dataToPoint(x, null, tmpOut);
35942                        }
35943                        else {
35944                            var x = tmpIn[0] = data.get(dims[0], i);
35945                            var y = tmpIn[1] = data.get(dims[1], i);
35946                            // Also {Array.<number>}, not undefined to avoid if...else... statement
35947                            point = !isNaN(x) && !isNaN(y) && coordSys.dataToPoint(tmpIn, null, tmpOut);
35948                        }
35949
35950                        if (isLargeRender) {
35951                            points[offset++] = point ? point[0] : NaN;
35952                            points[offset++] = point ? point[1] : NaN;
35953                        }
35954                        else {
35955                            data.setItemLayout(i, (point && point.slice()) || [NaN, NaN]);
35956                        }
35957                    }
35958
35959                    isLargeRender && data.setLayout('symbolPoints', points);
35960                }
35961
35962                return dimLen && {progress: progress};
35963            }
35964        };
35965    };
35966
35967    /*
35968* Licensed to the Apache Software Foundation (ASF) under one
35969* or more contributor license agreements.  See the NOTICE file
35970* distributed with this work for additional information
35971* regarding copyright ownership.  The ASF licenses this file
35972* to you under the Apache License, Version 2.0 (the
35973* "License"); you may not use this file except in compliance
35974* with the License.  You may obtain a copy of the License at
35975*
35976*   http://www.apache.org/licenses/LICENSE-2.0
35977*
35978* Unless required by applicable law or agreed to in writing,
35979* software distributed under the License is distributed on an
35980* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
35981* KIND, either express or implied.  See the License for the
35982* specific language governing permissions and limitations
35983* under the License.
35984*/
35985
35986
35987    var samplers = {
35988        average: function (frame) {
35989            var sum = 0;
35990            var count = 0;
35991            for (var i = 0; i < frame.length; i++) {
35992                if (!isNaN(frame[i])) {
35993                    sum += frame[i];
35994                    count++;
35995                }
35996            }
35997            // Return NaN if count is 0
35998            return count === 0 ? NaN : sum / count;
35999        },
36000        sum: function (frame) {
36001            var sum = 0;
36002            for (var i = 0; i < frame.length; i++) {
36003                // Ignore NaN
36004                sum += frame[i] || 0;
36005            }
36006            return sum;
36007        },
36008        max: function (frame) {
36009            var max = -Infinity;
36010            for (var i = 0; i < frame.length; i++) {
36011                frame[i] > max && (max = frame[i]);
36012            }
36013            // NaN will cause illegal axis extent.
36014            return isFinite(max) ? max : NaN;
36015        },
36016        min: function (frame) {
36017            var min = Infinity;
36018            for (var i = 0; i < frame.length; i++) {
36019                frame[i] < min && (min = frame[i]);
36020            }
36021            // NaN will cause illegal axis extent.
36022            return isFinite(min) ? min : NaN;
36023        },
36024        // TODO
36025        // Median
36026        nearest: function (frame) {
36027            return frame[0];
36028        }
36029    };
36030
36031    var indexSampler = function (frame, value) {
36032        return Math.round(frame.length / 2);
36033    };
36034
36035    var dataSample = function (seriesType) {
36036        return {
36037
36038            seriesType: seriesType,
36039
36040            modifyOutputEnd: true,
36041
36042            reset: function (seriesModel, ecModel, api) {
36043                var data = seriesModel.getData();
36044                var sampling = seriesModel.get('sampling');
36045                var coordSys = seriesModel.coordinateSystem;
36046                // Only cartesian2d support down sampling
36047                if (coordSys.type === 'cartesian2d' && sampling) {
36048                    var baseAxis = coordSys.getBaseAxis();
36049                    var valueAxis = coordSys.getOtherAxis(baseAxis);
36050                    var extent = baseAxis.getExtent();
36051                    // Coordinste system has been resized
36052                    var size = extent[1] - extent[0];
36053                    var rate = Math.round(data.count() / size);
36054                    if (rate > 1) {
36055                        var sampler;
36056                        if (typeof sampling === 'string') {
36057                            sampler = samplers[sampling];
36058                        }
36059                        else if (typeof sampling === 'function') {
36060                            sampler = sampling;
36061                        }
36062                        if (sampler) {
36063                            // Only support sample the first dim mapped from value axis.
36064                            seriesModel.setData(data.downSample(
36065                                data.mapDimension(valueAxis.dim), 1 / rate, sampler, indexSampler
36066                            ));
36067                        }
36068                    }
36069                }
36070            }
36071        };
36072    };
36073
36074    /*
36075* Licensed to the Apache Software Foundation (ASF) under one
36076* or more contributor license agreements.  See the NOTICE file
36077* distributed with this work for additional information
36078* regarding copyright ownership.  The ASF licenses this file
36079* to you under the Apache License, Version 2.0 (the
36080* "License"); you may not use this file except in compliance
36081* with the License.  You may obtain a copy of the License at
36082*
36083*   http://www.apache.org/licenses/LICENSE-2.0
36084*
36085* Unless required by applicable law or agreed to in writing,
36086* software distributed under the License is distributed on an
36087* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
36088* KIND, either express or implied.  See the License for the
36089* specific language governing permissions and limitations
36090* under the License.
36091*/
36092
36093    /**
36094     * Cartesian coordinate system
36095     * @module  echarts/coord/Cartesian
36096     *
36097     */
36098
36099    function dimAxisMapper(dim) {
36100        return this._axes[dim];
36101    }
36102
36103    /**
36104     * @alias module:echarts/coord/Cartesian
36105     * @constructor
36106     */
36107    var Cartesian = function (name) {
36108        this._axes = {};
36109
36110        this._dimList = [];
36111
36112        /**
36113         * @type {string}
36114         */
36115        this.name = name || '';
36116    };
36117
36118    Cartesian.prototype = {
36119
36120        constructor: Cartesian,
36121
36122        type: 'cartesian',
36123
36124        /**
36125         * Get axis
36126         * @param  {number|string} dim
36127         * @return {module:echarts/coord/Cartesian~Axis}
36128         */
36129        getAxis: function (dim) {
36130            return this._axes[dim];
36131        },
36132
36133        /**
36134         * Get axes list
36135         * @return {Array.<module:echarts/coord/Cartesian~Axis>}
36136         */
36137        getAxes: function () {
36138            return map(this._dimList, dimAxisMapper, this);
36139        },
36140
36141        /**
36142         * Get axes list by given scale type
36143         */
36144        getAxesByScale: function (scaleType) {
36145            scaleType = scaleType.toLowerCase();
36146            return filter(
36147                this.getAxes(),
36148                function (axis) {
36149                    return axis.scale.type === scaleType;
36150                }
36151            );
36152        },
36153
36154        /**
36155         * Add axis
36156         * @param {module:echarts/coord/Cartesian.Axis}
36157         */
36158        addAxis: function (axis) {
36159            var dim = axis.dim;
36160
36161            this._axes[dim] = axis;
36162
36163            this._dimList.push(dim);
36164        },
36165
36166        /**
36167         * Convert data to coord in nd space
36168         * @param {Array.<number>|Object.<string, number>} val
36169         * @return {Array.<number>|Object.<string, number>}
36170         */
36171        dataToCoord: function (val) {
36172            return this._dataCoordConvert(val, 'dataToCoord');
36173        },
36174
36175        /**
36176         * Convert coord in nd space to data
36177         * @param  {Array.<number>|Object.<string, number>} val
36178         * @return {Array.<number>|Object.<string, number>}
36179         */
36180        coordToData: function (val) {
36181            return this._dataCoordConvert(val, 'coordToData');
36182        },
36183
36184        _dataCoordConvert: function (input, method) {
36185            var dimList = this._dimList;
36186
36187            var output = input instanceof Array ? [] : {};
36188
36189            for (var i = 0; i < dimList.length; i++) {
36190                var dim = dimList[i];
36191                var axis = this._axes[dim];
36192
36193                output[dim] = axis[method](input[dim]);
36194            }
36195
36196            return output;
36197        }
36198    };
36199
36200    /*
36201* Licensed to the Apache Software Foundation (ASF) under one
36202* or more contributor license agreements.  See the NOTICE file
36203* distributed with this work for additional information
36204* regarding copyright ownership.  The ASF licenses this file
36205* to you under the Apache License, Version 2.0 (the
36206* "License"); you may not use this file except in compliance
36207* with the License.  You may obtain a copy of the License at
36208*
36209*   http://www.apache.org/licenses/LICENSE-2.0
36210*
36211* Unless required by applicable law or agreed to in writing,
36212* software distributed under the License is distributed on an
36213* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
36214* KIND, either express or implied.  See the License for the
36215* specific language governing permissions and limitations
36216* under the License.
36217*/
36218
36219
36220    function Cartesian2D(name) {
36221
36222        Cartesian.call(this, name);
36223    }
36224
36225    Cartesian2D.prototype = {
36226
36227        constructor: Cartesian2D,
36228
36229        type: 'cartesian2d',
36230
36231        /**
36232         * @type {Array.<string>}
36233         * @readOnly
36234         */
36235        dimensions: ['x', 'y'],
36236
36237        /**
36238         * Base axis will be used on stacking.
36239         *
36240         * @return {module:echarts/coord/cartesian/Axis2D}
36241         */
36242        getBaseAxis: function () {
36243            return this.getAxesByScale('ordinal')[0]
36244                || this.getAxesByScale('time')[0]
36245                || this.getAxis('x');
36246        },
36247
36248        /**
36249         * If contain point
36250         * @param {Array.<number>} point
36251         * @return {boolean}
36252         */
36253        containPoint: function (point) {
36254            var axisX = this.getAxis('x');
36255            var axisY = this.getAxis('y');
36256            return axisX.contain(axisX.toLocalCoord(point[0]))
36257                && axisY.contain(axisY.toLocalCoord(point[1]));
36258        },
36259
36260        /**
36261         * If contain data
36262         * @param {Array.<number>} data
36263         * @return {boolean}
36264         */
36265        containData: function (data) {
36266            return this.getAxis('x').containData(data[0])
36267                && this.getAxis('y').containData(data[1]);
36268        },
36269
36270        /**
36271         * @param {Array.<number>} data
36272         * @param {Array.<number>} out
36273         * @return {Array.<number>}
36274         */
36275        dataToPoint: function (data, reserved, out) {
36276            var xAxis = this.getAxis('x');
36277            var yAxis = this.getAxis('y');
36278            out = out || [];
36279            out[0] = xAxis.toGlobalCoord(xAxis.dataToCoord(data[0]));
36280            out[1] = yAxis.toGlobalCoord(yAxis.dataToCoord(data[1]));
36281            return out;
36282        },
36283
36284        /**
36285         * @param {Array.<number>} data
36286         * @param {Array.<number>} out
36287         * @return {Array.<number>}
36288         */
36289        clampData: function (data, out) {
36290            var xScale = this.getAxis('x').scale;
36291            var yScale = this.getAxis('y').scale;
36292            var xAxisExtent = xScale.getExtent();
36293            var yAxisExtent = yScale.getExtent();
36294            var x = xScale.parse(data[0]);
36295            var y = yScale.parse(data[1]);
36296            out = out || [];
36297            out[0] = Math.min(
36298                Math.max(Math.min(xAxisExtent[0], xAxisExtent[1]), x),
36299                Math.max(xAxisExtent[0], xAxisExtent[1])
36300            );
36301            out[1] = Math.min(
36302                Math.max(Math.min(yAxisExtent[0], yAxisExtent[1]), y),
36303                Math.max(yAxisExtent[0], yAxisExtent[1])
36304            );
36305
36306            return out;
36307        },
36308
36309        /**
36310         * @param {Array.<number>} point
36311         * @param {Array.<number>} out
36312         * @return {Array.<number>}
36313         */
36314        pointToData: function (point, out) {
36315            var xAxis = this.getAxis('x');
36316            var yAxis = this.getAxis('y');
36317            out = out || [];
36318            out[0] = xAxis.coordToData(xAxis.toLocalCoord(point[0]));
36319            out[1] = yAxis.coordToData(yAxis.toLocalCoord(point[1]));
36320            return out;
36321        },
36322
36323        /**
36324         * Get other axis
36325         * @param {module:echarts/coord/cartesian/Axis2D} axis
36326         */
36327        getOtherAxis: function (axis) {
36328            return this.getAxis(axis.dim === 'x' ? 'y' : 'x');
36329        }
36330
36331    };
36332
36333    inherits(Cartesian2D, Cartesian);
36334
36335    /*
36336* Licensed to the Apache Software Foundation (ASF) under one
36337* or more contributor license agreements.  See the NOTICE file
36338* distributed with this work for additional information
36339* regarding copyright ownership.  The ASF licenses this file
36340* to you under the Apache License, Version 2.0 (the
36341* "License");
36341 you may not use this file except in compliance
36342* with the License.  You may obtain a copy of the License at
36343*
36344*   http://www.apache.org/licenses/LICENSE-2.0
36345*
36346* Unless required by applicable law or agreed to in writing,
36347* software distributed under the License is distributed on an
36348* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
36349* KIND, either express or implied.  See the License for the
36350* specific language governing permissions and limitations
36351* under the License.
36352*/
36353
36354    /**
36355     * Extend axis 2d
36356     * @constructor module:echarts/coord/cartesian/Axis2D
36357     * @extends {module:echarts/coord/cartesian/Axis}
36358     * @param {string} dim
36359     * @param {*} scale
36360     * @param {Array.<number>} coordExtent
36361     * @param {string} axisType
36362     * @param {string} position
36363     */
36364    var Axis2D = function (dim, scale, coordExtent, axisType, position) {
36365        Axis.call(this, dim, scale, coordExtent);
36366        /**
36367         * Axis type
36368         *  - 'category'
36369         *  - 'value'
36370         *  - 'time'
36371         *  - 'log'
36372         * @type {string}
36373         */
36374        this.type = axisType || 'value';
36375
36376        /**
36377         * Axis position
36378         *  - 'top'
36379         *  - 'bottom'
36380         *  - 'left'
36381         *  - 'right'
36382         */
36383        this.position = position || 'bottom';
36384    };
36385
36386    Axis2D.prototype = {
36387
36388        constructor: Axis2D,
36389
36390        /**
36391         * Index of axis, can be used as key
36392         */
36393        index: 0,
36394
36395        /**
36396         * Implemented in <module:echarts/coord/cartesian/Grid>.
36397         * @return {Array.<module:echarts/coord/cartesian/Axis2D>}
36398         *         If not on zero of other axis, return null/undefined.
36399         *         If no axes, return an empty array.
36400         */
36401        getAxesOnZeroOf: null,
36402
36403        /**
36404         * Axis model
36405         * @param {module:echarts/coord/cartesian/AxisModel}
36406         */
36407        model: null,
36408
36409        isHorizontal: function () {
36410            var position = this.position;
36411            return position === 'top' || position === 'bottom';
36412        },
36413
36414        /**
36415         * Each item cooresponds to this.getExtent(), which
36416         * means globalExtent[0] may greater than globalExtent[1],
36417         * unless `asc` is input.
36418         *
36419         * @param {boolean} [asc]
36420         * @return {Array.<number>}
36421         */
36422        getGlobalExtent: function (asc) {
36423            var ret = this.getExtent();
36424            ret[0] = this.toGlobalCoord(ret[0]);
36425            ret[1] = this.toGlobalCoord(ret[1]);
36426            asc && ret[0] > ret[1] && ret.reverse();
36427            return ret;
36428        },
36429
36430        getOtherAxis: function () {
36431            this.grid.getOtherAxis();
36432        },
36433
36434        /**
36435         * @override
36436         */
36437        pointToData: function (point, clamp) {
36438            return this.coordToData(this.toLocalCoord(point[this.dim === 'x' ? 0 : 1]), clamp);
36439        },
36440
36441        /**
36442         * Transform global coord to local coord,
36443         * i.e. var localCoord = axis.toLocalCoord(80);
36444         * designate by module:echarts/coord/cartesian/Grid.
36445         * @type {Function}
36446         */
36447        toLocalCoord: null,
36448
36449        /**
36450         * Transform global coord to local coord,
36451         * i.e. var globalCoord = axis.toLocalCoord(40);
36452         * designate by module:echarts/coord/cartesian/Grid.
36453         * @type {Function}
36454         */
36455        toGlobalCoord: null
36456
36457    };
36458
36459    inherits(Axis2D, Axis);
36460
36461    /*
36462* Licensed to the Apache Software Foundation (ASF) under one
36463* or more contributor license agreements.  See the NOTICE file
36464* distributed with this work for additional information
36465* regarding copyright ownership.  The ASF licenses this file
36466* to you under the Apache License, Version 2.0 (the
36467* "License"); you may not use this file except in compliance
36468* with the License.  You may obtain a copy of the License at
36469*
36470*   http://www.apache.org/licenses/LICENSE-2.0
36471*
36472* Unless required by applicable law or agreed to in writing,
36473* software distributed under the License is distributed on an
36474* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
36475* KIND, either express or implied.  See the License for the
36476* specific language governing permissions and limitations
36477* under the License.
36478*/
36479
36480    var defaultOption = {
36481        show: true,
36482        zlevel: 0,
36483        z: 0,
36484        // Inverse the axis.
36485        inverse: false,
36486
36487        // Axis name displayed.
36488        name: '',
36489        // 'start' | 'middle' | 'end'
36490        nameLocation: 'end',
36491        // By degree. By defualt auto rotate by nameLocation.
36492        nameRotate: null,
36493        nameTruncate: {
36494            maxWidth: null,
36495            ellipsis: '...',
36496            placeholder: '.'
36497        },
36498        // Use global text style by default.
36499        nameTextStyle: {},
36500        // The gap between axisName and axisLine.
36501        nameGap: 15,
36502
36503        // Default `false` to support tooltip.
36504        silent: false,
36505        // Default `false` to avoid legacy user event listener fail.
36506        triggerEvent: false,
36507
36508        tooltip: {
36509            show: false
36510        },
36511
36512        axisPointer: {},
36513
36514        axisLine: {
36515            show: true,
36516            onZero: true,
36517            onZeroAxisIndex: null,
36518            lineStyle: {
36519                color: '#333',
36520                width: 1,
36521                type: 'solid'
36522            },
36523            // The arrow at both ends the the axis.
36524            symbol: ['none', 'none'],
36525            symbolSize: [10, 15]
36526        },
36527        axisTick: {
36528            show: true,
36529            // Whether axisTick is inside the grid or outside the grid.
36530            inside: false,
36531            // The length of axisTick.
36532            length: 5,
36533            lineStyle: {
36534                width: 1
36535            }
36536        },
36537        axisLabel: {
36538            show: true,
36539            // Whether axisLabel is inside the grid or outside the grid.
36540            inside: false,
36541            rotate: 0,
36542            // true | false | null/undefined (auto)
36543            showMinLabel: null,
36544            // true | false | null/undefined (auto)
36545            showMaxLabel: null,
36546            margin: 8,
36547            // formatter: null,
36548            fontSize: 12
36549        },
36550        splitLine: {
36551            show: true,
36552            lineStyle: {
36553                color: ['#ccc'],
36554                width: 1,
36555                type: 'solid'
36556            }
36557        },
36558        splitArea: {
36559            show: false,
36560            areaStyle: {
36561                color: ['rgba(250,250,250,0.3)','rgba(200,200,200,0.3)']
36562            }
36563        }
36564    };
36565
36566    var axisDefault = {};
36567
36568    axisDefault.categoryAxis = merge({
36569        // The gap at both ends of the axis. For categoryAxis, boolean.
36570        boundaryGap: true,
36571        // Set false to faster category collection.
36572        // Only usefull in the case like: category is
36573        // ['2012-01-01', '2012-01-02', ...], where the input
36574        // data has been ensured not duplicate and is large data.
36575        // null means "auto":
36576        // if axis.data provided, do not deduplication,
36577        // else do deduplication.
36578        deduplication: null,
36579        // splitArea: {
36580        // show: false
36581        // },
36582        splitLine: {
36583            show: false
36584        },
36585        axisTick: {
36586            // If tick is align with label when boundaryGap is true
36587            alignWithLabel: false,
36588            interval: 'auto'
36589        },
36590        axisLabel: {
36591            interval: 'auto'
36592        }
36593    }, defaultOption);
36594
36595    axisDefault.valueAxis = merge({
36596        // The gap at both ends of the axis. For value axis, [GAP, GAP], where
36597        // `GAP` can be an absolute pixel number (like `35`), or percent (like `'30%'`)
36598        boundaryGap: [0, 0],
36599
36600        // TODO
36601        // min/max: [30, datamin, 60] or [20, datamin] or [datamin, 60]
36602
36603        // Min value of the axis. can be:
36604        // + a number
36605        // + 'dataMin': use the min value in data.
36606        // + null/undefined: auto decide min value (consider pretty look and boundaryGap).
36607        // min: null,
36608
36609        // Max value of the axis. can be:
36610        // + a number
36611        // + 'dataMax': use the max value in data.
36612        // + null/undefined: auto decide max value (consider pretty look and boundaryGap).
36613        // max: null,
36614
36615        // Readonly prop, specifies start value of the range when using data zoom.
36616        // rangeStart: null
36617
36618        // Readonly prop, specifies end value of the range when using data zoom.
36619        // rangeEnd: null
36620
36621        // Optional value can be:
36622        // + `false`: always include value 0.
36623        // + `true`: the extent do not consider value 0.
36624        // scale: false,
36625
36626        // AxisTick and axisLabel and splitLine are caculated based on splitNumber.
36627        splitNumber: 5
36628
36629        // Interval specifies the span of the ticks is mandatorily.
36630        // interval: null
36631
36632        // Specify min interval when auto calculate tick interval.
36633        // minInterval: null
36634
36635        // Specify max interval when auto calculate tick interval.
36636        // maxInterval: null
36637
36638    }, defaultOption);
36639
36640    axisDefault.timeAxis = defaults({
36641        scale: true,
36642        min: 'dataMin',
36643        max: 'dataMax'
36644    }, axisDefault.valueAxis);
36645
36646    axisDefault.logAxis = defaults({
36647        scale: true,
36648        logBase: 10
36649    }, axisDefault.valueAxis);
36650
36651    /*
36652* Licensed to the Apache Software Foundation (ASF) under one
36653* or more contributor license agreements.  See the NOTICE file
36654* distributed with this work for additional information
36655* regarding copyright ownership.  The ASF licenses this file
36656* to you under the Apache License, Version 2.0 (the
36657* "License"); you may not use this file except in compliance
36658* with the License.  You may obtain a copy of the License at
36659*
36660*   http://www.apache.org/licenses/LICENSE-2.0
36661*
36662* Unless required by applicable law or agreed to in writing,
36663* software distributed under the License is distributed on an
36664* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
36665* KIND, either express or implied.  See the License for the
36666* specific language governing permissions and limitations
36667* under the License.
36668*/
36669
36670// FIXME axisType is fixed ?
36671    var AXIS_TYPES = ['value', 'category', 'time', 'log'];
36672
36673    /**
36674     * Generate sub axis model class
36675     * @param {string} axisName 'x' 'y' 'radius' 'angle' 'parallel'
36676     * @param {module:echarts/model/Component} BaseAxisModelClass
36677     * @param {Function} axisTypeDefaulter
36678     * @param {Object} [extraDefaultOption]
36679     */
36680    var axisModelCreator = function (axisName, BaseAxisModelClass, axisTypeDefaulter, extraDefaultOption) {
36681
36682        each$1(AXIS_TYPES, function (axisType) {
36683
36684            BaseAxisModelClass.extend({
36685
36686                /**
36687                 * @readOnly
36688                 */
36689                type: axisName + 'Axis.' + axisType,
36690
36691                mergeDefaultAndTheme: function (option, ecModel) {
36692                    var layoutMode = this.layoutMode;
36693                    var inputPositionParams = layoutMode
36694                        ? getLayoutParams(option) : {};
36695
36696                    var themeModel = ecModel.getTheme();
36697                    merge(option, themeModel.get(axisType + 'Axis'));
36698                    merge(option, this.getDefaultOption());
36699
36700                    option.type = axisTypeDefaulter(axisName, option);
36701
36702                    if (layoutMode) {
36703                        mergeLayoutParam(option, inputPositionParams, layoutMode);
36704                    }
36705                },
36706
36707                /**
36708                 * @override
36709                 */
36710                optionUpdated: function () {
36711                    var thisOption = this.option;
36712                    if (thisOption.type === 'category') {
36713                        this.__ordinalMeta = OrdinalMeta.createByAxisModel(this);
36714                    }
36715                },
36716
36717                /**
36718                 * Should not be called before all of 'getInitailData' finished.
36719                 * Because categories are collected during initializing data.
36720                 */
36721                getCategories: function (rawData) {
36722                    var option = this.option;
36723                    // FIXME
36724                    // warning if called before all of 'getInitailData' finished.
36725                    if (option.type === 'category') {
36726                        if (rawData) {
36727                            return option.data;
36728                        }
36729                        return this.__ordinalMeta.categories;
36730                    }
36731                },
36732
36733                getOrdinalMeta: function () {
36734                    return this.__ordinalMeta;
36735                },
36736
36737                defaultOption: mergeAll(
36738                    [
36739                        {},
36740                        axisDefault[axisType + 'Axis'],
36741                        extraDefaultOption
36742                    ],
36743                    true
36744                )
36745            });
36746        });
36747
36748        ComponentModel.registerSubTypeDefaulter(
36749            axisName + 'Axis',
36750            curry(axisTypeDefaulter, axisName)
36751        );
36752    };
36753
36754    /*
36755* Licensed to the Apache Software Foundation (ASF) under one
36756* or more contributor license agreements.  See the NOTICE file
36757* distributed with this work for additional information
36758* regarding copyright ownership.  The ASF licenses this file
36759* to you under the Apache License, Version 2.0 (the
36760* "License");
36760 you may not use this file except in compliance
36761* with the License.  You may obtain a copy of the License at
36762*
36763*   http://www.apache.org/licenses/LICENSE-2.0
36764*
36765* Unless required by applicable law or agreed to in writing,
36766* software distributed under the License is distributed on an
36767* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
36768* KIND, either express or implied.  See the License for the
36769* specific language governing permissions and limitations
36770* under the License.
36771*/
36772
36773    var AxisModel = ComponentModel.extend({
36774
36775        type: 'cartesian2dAxis',
36776
36777        /**
36778         * @type {module:echarts/coord/cartesian/Axis2D}
36779         */
36780        axis: null,
36781
36782        /**
36783         * @override
36784         */
36785        init: function () {
36786            AxisModel.superApply(this, 'init', arguments);
36787            this.resetRange();
36788        },
36789
36790        /**
36791         * @override
36792         */
36793        mergeOption: function () {
36794            AxisModel.superApply(this, 'mergeOption', arguments);
36795            this.resetRange();
36796        },
36797
36798        /**
36799         * @override
36800         */
36801        restoreData: function () {
36802            AxisModel.superApply(this, 'restoreData', arguments);
36803            this.resetRange();
36804        },
36805
36806        /**
36807         * @override
36808         * @return {module:echarts/model/Component}
36809         */
36810        getCoordSysModel: function () {
36811            return this.ecModel.queryComponents({
36812                mainType: 'grid',
36813                index: this.option.gridIndex,
36814                id: this.option.gridId
36815            })[0];
36816        }
36817
36818    });
36819
36820    function getAxisType(axisDim, option) {
36821        // Default axis with data is category axis
36822        return option.type || (option.data ? 'category' : 'value');
36823    }
36824
36825    merge(AxisModel.prototype, axisModelCommonMixin);
36826
36827    var extraOption = {
36828        // gridIndex: 0,
36829        // gridId: '',
36830
36831        // Offset is for multiple axis on the same position
36832        offset: 0
36833    };
36834
36835    axisModelCreator('x', AxisModel, getAxisType, extraOption);
36836    axisModelCreator('y', AxisModel, getAxisType, extraOption);
36837
36838    /*
36839* Licensed to the Apache Software Foundation (ASF) under one
36840* or more contributor license agreements.  See the NOTICE file
36841* distributed with this work for additional information
36842* regarding copyright ownership.  The ASF licenses this file
36843* to you under the Apache License, Version 2.0 (the
36844* "License"); you may not use this file except in compliance
36845* with the License.  You may obtain a copy of the License at
36846*
36847*   http://www.apache.org/licenses/LICENSE-2.0
36848*
36849* Unless required by applicable law or agreed to in writing,
36850* software distributed under the License is distributed on an
36851* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
36852* KIND, either express or implied.  See the License for the
36853* specific language governing permissions and limitations
36854* under the License.
36855*/
36856
36857// Grid æ˜¯åœ¨æœ‰ç›´è§’åæ ‡ç³»çš„æ—¶å€™å¿…é¡»è¦å­˜åœ¨çš„
36858// 所以这里也要被 Cartesian2D 依赖
36859
36860    ComponentModel.extend({
36861
36862        type: 'grid',
36863
36864        dependencies: ['xAxis', 'yAxis'],
36865
36866        layoutMode: 'box',
36867
36868        /**
36869         * @type {module:echarts/coord/cartesian/Grid}
36870         */
36871        coordinateSystem: null,
36872
36873        defaultOption: {
36874            show: false,
36875            zlevel: 0,
36876            z: 0,
36877            left: '10%',
36878            top: 60,
36879            right: '10%',
36880            bottom: 60,
36881            // If grid size contain label
36882            containLabel: false,
36883            // width: {totalWidth} - left - right,
36884            // height: {totalHeight} - top - bottom,
36885            backgroundColor: 'rgba(0,0,0,0)',
36886            borderWidth: 1,
36887            borderColor: '#ccc'
36888        }
36889    });
36890
36891    /*
36892* Licensed to the Apache Software Foundation (ASF) under one
36893* or more contributor license agreements.  See the NOTICE file
36894* distributed with this work for additional information
36895* regarding copyright ownership.  The ASF licenses this file
36896* to you under the Apache License, Version 2.0 (the
36897* "License");
36897 you may not use this file except in compliance
36898* with the License.  You may obtain a copy of the License at
36899*
36900*   http://www.apache.org/licenses/LICENSE-2.0
36901*
36902* Unless required by applicable law or agreed to in writing,
36903* software distributed under the License is distributed on an
36904* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
36905* KIND, either express or implied.  See the License for the
36906* specific language governing permissions and limitations
36907* under the License.
36908*/
36909
36910    /**
36911     * Grid is a region which contains at most 4 cartesian systems
36912     *
36913     * TODO Default cartesian
36914     */
36915
36916// Depends on GridModel, AxisModel, which performs preprocess.
36917    /**
36918     * Check if the axis is used in the specified grid
36919     * @inner
36920     */
36921    function isAxisUsedInTheGrid(axisModel, gridModel, ecModel) {
36922        return axisModel.getCoordSysModel() === gridModel;
36923    }
36924
36925    function Grid(gridModel, ecModel, api) {
36926        /**
36927         * @type {Object.<string, module:echarts/coord/cartesian/Cartesian2D>}
36928         * @private
36929         */
36930        this._coordsMap = {};
36931
36932        /**
36933         * @type {Array.<module:echarts/coord/cartesian/Cartesian>}
36934         * @private
36935         */
36936        this._coordsList = [];
36937
36938        /**
36939         * @type {Object.<string, module:echarts/coord/cartesian/Axis2D>}
36940         * @private
36941         */
36942        this._axesMap = {};
36943
36944        /**
36945         * @type {Array.<module:echarts/coord/cartesian/Axis2D>}
36946         * @private
36947         */
36948        this._axesList = [];
36949
36950        this._initCartesian(gridModel, ecModel, api);
36951
36952        this.model = gridModel;
36953    }
36954
36955    var gridProto = Grid.prototype;
36956
36957    gridProto.type = 'grid';
36958
36959    gridProto.axisPointerEnabled = true;
36960
36961    gridProto.getRect = function () {
36962        return this._rect;
36963    };
36964
36965    gridProto.update = function (ecModel, api) {
36966
36967        var axesMap = this._axesMap;
36968
36969        this._updateScale(ecModel, this.model);
36970
36971        each$1(axesMap.x, function (xAxis) {
36972            niceScaleExtent(xAxis.scale, xAxis.model);
36973        });
36974        each$1(axesMap.y, function (yAxis) {
36975            niceScaleExtent(yAxis.scale, yAxis.model);
36976        });
36977        each$1(axesMap.x, function (xAxis) {
36978            fixAxisOnZero(axesMap, 'y', xAxis);
36979        });
36980        each$1(axesMap.y, function (yAxis) {
36981            fixAxisOnZero(axesMap, 'x', yAxis);
36982        });
36983
36984        // Resize again if containLabel is enabled
36985        // FIXME It may cause getting wrong grid size in data processing stage
36986        this.resize(this.model, api);
36987    };
36988
36989    function fixAxisOnZero(axesMap, otherAxisDim, axis) {
36990
36991        axis.getAxesOnZeroOf = function () {
36992            // TODO: onZero of multiple axes.
36993            return otherAxis ? [otherAxis] : [];
36994        };
36995
36996        // onZero can not be enabled in these two situations:
36997        // 1. When any other axis is a category axis.
36998        // 2. When no axis is cross 0 point.
36999        var otherAxes = axesMap[otherAxisDim];
37000
37001        var otherAxis;
37002        var axisModel = axis.model;
37003        var onZero = axisModel.get('axisLine.onZero');
37004        var onZeroAxisIndex = axisModel.get('axisLine.onZeroAxisIndex');
37005
37006        if (!onZero) {
37007            return;
37008        }
37009
37010        // If target axis is specified.
37011        if (onZeroAxisIndex != null) {
37012            if (canOnZeroToAxis(otherAxes[onZeroAxisIndex])) {
37013                otherAxis = otherAxes[onZeroAxisIndex];
37014            }
37015            return;
37016        }
37017
37018        // Find the first available other axis.
37019        for (var idx in otherAxes) {
37020            if (otherAxes.hasOwnProperty(idx) && canOnZeroToAxis(otherAxes[idx])) {
37021                otherAxis = otherAxes[idx];
37022                break;
37023            }
37024        }
37025    }
37026
37027    function canOnZeroToAxis(axis) {
37028        return axis && axis.type !== 'category' && axis.type !== 'time' && ifAxisCrossZero(axis);
37029    }
37030
37031    /**
37032     * Resize the grid
37033     * @param {module:echarts/coord/cartesian/GridModel} gridModel
37034     * @param {module:echarts/ExtensionAPI} api
37035     */
37036    gridProto.resize = function (gridModel, api, ignoreContainLabel) {
37037
37038        var gridRect = getLayoutRect(
37039            gridModel.getBoxLayoutParams(), {
37040                width: api.getWidth(),
37041                height: api.getHeight()
37042            });
37043
37044        this._rect = gridRect;
37045
37046        var axesList = this._axesList;
37047
37048        adjustAxes();
37049
37050        // Minus label size
37051        if (!ignoreContainLabel && gridModel.get('containLabel')) {
37052            each$1(axesList, function (axis) {
37053                if (!axis.model.get('axisLabel.inside')) {
37054                    var labelUnionRect = estimateLabelUnionRect(axis);
37055                    if (labelUnionRect) {
37056                        var dim = axis.isHorizontal() ? 'height' : 'width';
37057                        var margin = axis.model.get('axisLabel.margin');
37058                        gridRect[dim] -= labelUnionRect[dim] + margin;
37059                        if (axis.position === 'top') {
37060                            gridRect.y += labelUnionRect.height + margin;
37061                        }
37062                        else if (axis.position === 'left')  {
37063                            gridRect.x += labelUnionRect.width + margin;
37064                        }
37065                    }
37066                }
37067            });
37068
37069            adjustAxes();
37070        }
37071
37072        function adjustAxes() {
37073            each$1(axesList, function (axis) {
37074                var isHorizontal = axis.isHorizontal();
37075                var extent = isHorizontal ? [0, gridRect.width] : [0, gridRect.height];
37076                var idx = axis.inverse ? 1 : 0;
37077                axis.setExtent(extent[idx], extent[1 - idx]);
37078                updateAxisTransform(axis, isHorizontal ? gridRect.x : gridRect.y);
37079            });
37080        }
37081    };
37082
37083    /**
37084     * @param {string} axisType
37085     * @param {number} [axisIndex]
37086     */
37087    gridProto.getAxis = function (axisType, axisIndex) {
37088        var axesMapOnDim = this._axesMap[axisType];
37089        if (axesMapOnDim != null) {
37090            if (axisIndex == null) {
37091                // Find first axis
37092                for (var name in axesMapOnDim) {
37093                    if (axesMapOnDim.hasOwnProperty(name)) {
37094                        return axesMapOnDim[name];
37095                    }
37096                }
37097            }
37098            return axesMapOnDim[axisIndex];
37099        }
37100    };
37101
37102    /**
37103     * @return {Array.<module:echarts/coord/Axis>}
37104     */
37105    gridProto.getAxes = function () {
37106        return this._axesList.slice();
37107    };
37108
37109    /**
37110     * Usage:
37111     *      grid.getCartesian(xAxisIndex, yAxisIndex);
37112     *      grid.getCartesian(xAxisIndex);
37113     *      grid.getCartesian(null, yAxisIndex);
37114     *      grid.getCartesian({xAxisIndex: ..., yAxisIndex: ...});
37115     *
37116     * @param {number|Object} [xAxisIndex]
37117     * @param {number} [yAxisIndex]
37118     */
37119    gridProto.getCartesian = function (xAxisIndex, yAxisIndex) {
37120        if (xAxisIndex != null && yAxisIndex != null) {
37121            var key = 'x' + xAxisIndex + 'y' + yAxisIndex;
37122            return this._coordsMap[key];
37123        }
37124
37125        if (isObject$1(xAxisIndex)) {
37126            yAxisIndex = xAxisIndex.yAxisIndex;
37127            xAxisIndex = xAxisIndex.xAxisIndex;
37128        }
37129        // When only xAxisIndex or yAxisIndex given, find its first cartesian.
37130        for (var i = 0, coordList = this._coordsList; i < coordList.length; i++) {
37131            if (coordList[i].getAxis('x').index === xAxisIndex
37132                || coordList[i].getAxis('y').index === yAxisIndex
37133            ) {
37134                return coordList[i];
37135            }
37136        }
37137    };
37138
37139    gridProto.getCartesians = function () {
37140        return this._coordsList.slice();
37141    };
37142
37143    /**
37144     * @implements
37145     * see {module:echarts/CoodinateSystem}
37146     */
37147    gridProto.convertToPixel = function (ecModel, finder, value) {
37148        var target = this._findConvertTarget(ecModel, finder);
37149
37150        return target.cartesian
37151            ? target.cartesian.dataToPoint(value)
37152            : target.axis
37153                ? target.axis.toGlobalCoord(target.axis.dataToCoord(value))
37154                : null;
37155    };
37156
37157    /**
37158     * @implements
37159     * see {module:echarts/CoodinateSystem}
37160     */
37161    gridProto.convertFromPixel = function (ecModel, finder, value) {
37162        var target = this._findConvertTarget(ecModel, finder);
37163
37164        return target.cartesian
37165            ? target.cartesian.pointToData(value)
37166            : target.axis
37167                ? target.axis.coordToData(target.axis.toLocalCoord(value))
37168                : null;
37169    };
37170
37171    /**
37172     * @inner
37173     */
37174    gridProto._findConvertTarget = function (ecModel, finder) {
37175        var seriesModel = finder.seriesModel;
37176        var xAxisModel = finder.xAxisModel
37177            || (seriesModel && seriesModel.getReferringComponents('xAxis')[0]);
37178        var yAxisModel = finder.yAxisModel
37179            || (seriesModel && seriesModel.getReferringComponents('yAxis')[0]);
37180        var gridModel = finder.gridModel;
37181        var coordsList = this._coordsList;
37182        var cartesian;
37183        var axis;
37184
37185        if (seriesModel) {
37186            cartesian = seriesModel.coordinateSystem;
37187            indexOf(coordsList, cartesian) < 0 && (cartesian = null);
37188        }
37189        else if (xAxisModel && yAxisModel) {
37190            cartesian = this.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex);
37191        }
37192        else if (xAxisModel) {
37193            axis = this.getAxis('x', xAxisModel.componentIndex);
37194        }
37195        else if (yAxisModel) {
37196            axis = this.getAxis('y', yAxisModel.componentIndex);
37197        }
37198        // Lowest priority.
37199        else if (gridModel) {
37200            var grid = gridModel.coordinateSystem;
37201            if (grid === this) {
37202                cartesian = this._coordsList[0];
37203            }
37204        }
37205
37206        return {cartesian: cartesian, axis: axis};
37207    };
37208
37209    /**
37210     * @implements
37211     * see {module:echarts/CoodinateSystem}
37212     */
37213    gridProto.containPoint = function (point) {
37214        var coord = this._coordsList[0];
37215        if (coord) {
37216            return coord.containPoint(point);
37217        }
37218    };
37219
37220    /**
37221     * Initialize cartesian coordinate systems
37222     * @private
37223     */
37224    gridProto._initCartesian = function (gridModel, ecModel, api) {
37225        var axisPositionUsed = {
37226            left: false,
37227            right: false,
37228            top: false,
37229            bottom: false
37230        };
37231
37232        var axesMap = {
37233            x: {},
37234            y: {}
37235        };
37236        var axesCount = {
37237            x: 0,
37238            y: 0
37239        };
37240
37241        /// Create axis
37242        ecModel.eachComponent('xAxis', createAxisCreator('x'), this);
37243        ecModel.eachComponent('yAxis', createAxisCreator('y'), this);
37244
37245        if (!axesCount.x || !axesCount.y) {
37246            // Roll back when there no either x or y axis
37247            this._axesMap = {};
37248            this._axesList = [];
37249            return;
37250        }
37251
37252        this._axesMap = axesMap;
37253
37254        /// Create cartesian2d
37255        each$1(axesMap.x, function (xAxis, xAxisIndex) {
37256            each$1(axesMap.y, function (yAxis, yAxisIndex) {
37257                var key = 'x' + xAxisIndex + 'y' + yAxisIndex;
37258                var cartesian = new Cartesian2D(key);
37259
37260                cartesian.grid = this;
37261                cartesian.model = gridModel;
37262
37263                this._coordsMap[key] = cartesian;
37264                this._coordsList.push(cartesian);
37265
37266                cartesian.addAxis(xAxis);
37267                cartesian.addAxis(yAxis);
37268            }, this);
37269        }, this);
37270
37271        function createAxisCreator(axisType) {
37272            return function (axisModel, idx) {
37273                if (!isAxisUsedInTheGrid(axisModel, gridModel, ecModel)) {
37274                    return;
37275                }
37276
37277                var axisPosition = axisModel.get('position');
37278                if (axisType === 'x') {
37279                    // Fix position
37280                    if (axisPosition !== 'top' && axisPosition !== 'bottom') {
37281                        // Default bottom of X
37282                        axisPosition = 'bottom';
37283                        if (axisPositionUsed[axisPosition]) {
37284                            axisPosition = axisPosition === 'top' ? 'bottom' : 'top';
37285                        }
37286                    }
37287                }
37288                else {
37289                    // Fix position
37290                    if (axisPosition !== 'left' && axisPosition !== 'right') {
37291                        // Default left of Y
37292                        axisPosition = 'left';
37293                        if (axisPositionUsed[axisPosition]) {
37294                            axisPosition = axisPosition === 'left' ? 'right' : 'left';
37295                        }
37296                    }
37297                }
37298                axisPositionUsed[axisPosition] = true;
37299
37300                var axis = new Axis2D(
37301                    axisType, createScaleByModel(axisModel),
37302                    [0, 0],
37303                    axisModel.get('type'),
37304                    axisPosition
37305                );
37306
37307                var isCategory = axis.type === 'category';
37308                axis.onBand = isCategory && axisModel.get('boundaryGap');
37309                axis.inverse = axisModel.get('inverse');
37310
37311                // Inject axis into axisModel
37312                axisModel.axis = axis;
37313
37314                // Inject axisModel into axis
37315                axis.model = axisModel;
37316
37317                // Inject grid info axis
37318                axis.grid = this;
37319
37320                // Index of axis, can be used as key
37321                axis.index = idx;
37322
37323                this._axesList.push(axis);
37324
37325                axesMap[axisType][idx] = axis;
37326                axesCount[axisType]++;
37327            };
37328        }
37329    };
37330
37331    /**
37332     * Update cartesian properties from series
37333     * @param  {module:echarts/model/Option} option
37334     * @private
37335     */
37336    gridProto._updateScale = function (ecModel, gridModel) {
37337        // Reset scale
37338        each$1(this._axesList, function (axis) {
37339            axis.scale.setExtent(Infinity, -Infinity);
37340        });
37341        ecModel.eachSeries(function (seriesModel) {
37342            if (isCartesian2D(seriesModel)) {
37343                var axesModels = findAxesModels(seriesModel, ecModel);
37344                var xAxisModel = axesModels[0];
37345                var yAxisModel = axesModels[1];
37346
37347                if (!isAxisUsedInTheGrid(xAxisModel, gridModel, ecModel)
37348                    || !isAxisUsedInTheGrid(yAxisModel, gridModel, ecModel)
37349                ) {
37350                    return;
37351                }
37352
37353                var cartesian = this.getCartesian(
37354                    xAxisModel.componentIndex, yAxisModel.componentIndex
37355                );
37356                var data = seriesModel.getData();
37357                var xAxis = cartesian.getAxis('x');
37358                var yAxis = cartesian.getAxis('y');
37359
37360                if (data.type === 'list') {
37361                    unionExtent(data, xAxis, seriesModel);
37362                    unionExtent(data, yAxis, seriesModel);
37363                }
37364            }
37365        }, this);
37366
37367        function unionExtent(data, axis, seriesModel) {
37368            each$1(data.mapDimension(axis.dim, true), function (dim) {
37369                axis.scale.unionExtentFromData(
37370                    // For example, the extent of the orginal dimension
37371                    // is [0.1, 0.5], the extent of the `stackResultDimension`
37372                    // is [7, 9], the final extent should not include [0.1, 0.5].
37373                    data, getStackedDimension(data, dim)
37374                );
37375            });
37376        }
37377    };
37378
37379    /**
37380     * @param {string} [dim] 'x' or 'y' or 'auto' or null/undefined
37381     * @return {Object} {baseAxes: [], otherAxes: []}
37382     */
37383    gridProto.getTooltipAxes = function (dim) {
37384        var baseAxes = [];
37385        var otherAxes = [];
37386
37387        each$1(this.getCartesians(), function (cartesian) {
37388            var baseAxis = (dim != null && dim !== 'auto')
37389                ? cartesian.getAxis(dim) : cartesian.getBaseAxis();
37390            var otherAxis = cartesian.getOtherAxis(baseAxis);
37391            indexOf(baseAxes, baseAxis) < 0 && baseAxes.push(baseAxis);
37392            indexOf(otherAxes, otherAxis) < 0 && otherAxes.push(otherAxis);
37393        });
37394
37395        return {baseAxes: baseAxes, otherAxes: otherAxes};
37396    };
37397
37398    /**
37399     * @inner
37400     */
37401    function updateAxisTransform(axis, coordBase) {
37402        var axisExtent = axis.getExtent();
37403        var axisExtentSum = axisExtent[0] + axisExtent[1];
37404
37405        // Fast transform
37406        axis.toGlobalCoord = axis.dim === 'x'
37407            ? function (coord) {
37408                return coord + coordBase;
37409            }
37410            : function (coord) {
37411                return axisExtentSum - coord + coordBase;
37412            };
37413        axis.toLocalCoord = axis.dim === 'x'
37414            ? function (coord) {
37415                return coord - coordBase;
37416            }
37417            : function (coord) {
37418                return axisExtentSum - coord + coordBase;
37419            };
37420    }
37421
37422    var axesTypes = ['xAxis', 'yAxis'];
37423    /**
37424     * @inner
37425     */
37426    function findAxesModels(seriesModel, ecModel) {
37427        return map(axesTypes, function (axisType) {
37428            var axisModel = seriesModel.getReferringComponents(axisType)[0];
37429
37430            if (__DEV__) {
37431                if (!axisModel) {
37432                    throw new Error(axisType + ' "' + retrieve(
37433                        seriesModel.get(axisType + 'Index'),
37434                        seriesModel.get(axisType + 'Id'),
37435                        0
37436                    ) + '" not found');
37437                }
37438            }
37439            return axisModel;
37440        });
37441    }
37442
37443    /**
37444     * @inner
37445     */
37446    function isCartesian2D(seriesModel) {
37447        return seriesModel.get('coordinateSystem') === 'cartesian2d';
37448    }
37449
37450    Grid.create = function (ecModel, api) {
37451        var grids = [];
37452        ecModel.eachComponent('grid', function (gridModel, idx) {
37453            var grid = new Grid(gridModel, ecModel, api);
37454            grid.name = 'grid_' + idx;
37455            // dataSampling requires axis extent, so resize
37456            // should be performed in create stage.
37457            grid.resize(gridModel, api, true);
37458
37459            gridModel.coordinateSystem = grid;
37460
37461            grids.push(grid);
37462        });
37463
37464        // Inject the coordinateSystems into seriesModel
37465        ecModel.eachSeries(function (seriesModel) {
37466            if (!isCartesian2D(seriesModel)) {
37467                return;
37468            }
37469
37470            var axesModels = findAxesModels(seriesModel, ecModel);
37471            var xAxisModel = axesModels[0];
37472            var yAxisModel = axesModels[1];
37473
37474            var gridModel = xAxisModel.getCoordSysModel();
37475
37476            if (__DEV__) {
37477                if (!gridModel) {
37478                    throw new Error(
37479                        'Grid "' + retrieve(
37480                        xAxisModel.get('gridIndex'),
37481                        xAxisModel.get('gridId'),
37482                        0
37483                        ) + '" not found'
37484                    );
37485                }
37486                if (xAxisModel.getCoordSysModel() !== yAxisModel.getCoordSysModel()) {
37487                    throw new Error('xAxis and yAxis must use the same grid');
37488                }
37489            }
37490
37491            var grid = gridModel.coordinateSystem;
37492
37493            seriesModel.coordinateSystem = grid.getCartesian(
37494                xAxisModel.componentIndex, yAxisModel.componentIndex
37495            );
37496        });
37497
37498        return grids;
37499    };
37500
37501// For deciding which dimensions to use when creating list data
37502    Grid.dimensions = Grid.prototype.dimensions = Cartesian2D.prototype.dimensions;
37503
37504    CoordinateSystemManager.register('cartesian2d', Grid);
37505
37506    /*
37507* Licensed to the Apache Software Foundation (ASF) under one
37508* or more contributor license agreements.  See the NOTICE file
37509* distributed with this work for additional information
37510* regarding copyright ownership.  The ASF licenses this file
37511* to you under the Apache License, Version 2.0 (the
37512* "License"); you may not use this file except in compliance
37513* with the License.  You may obtain a copy of the License at
37514*
37515*   http://www.apache.org/licenses/LICENSE-2.0
37516*
37517* Unless required by applicable law or agreed to in writing,
37518* software distributed under the License is distributed on an
37519* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
37520* KIND, either express or implied.  See the License for the
37521* specific language governing permissions and limitations
37522* under the License.
37523*/
37524
37525    var PI$2 = Math.PI;
37526
37527    function makeAxisEventDataBase(axisModel) {
37528        var eventData = {
37529            componentType: axisModel.mainType
37530        };
37531        eventData[axisModel.mainType + 'Index'] = axisModel.componentIndex;
37532        return eventData;
37533    }
37534
37535    /**
37536     * A final axis is translated and rotated from a "standard axis".
37537     * So opt.position and opt.rotation is required.
37538     *
37539     * A standard axis is and axis from [0, 0] to [0, axisExtent[1]],
37540     * for example: (0, 0) ------------> (0, 50)
37541     *
37542     * nameDirection or tickDirection or labelDirection is 1 means tick
37543     * or label is below the standard axis, whereas is -1 means above
37544     * the standard axis. labelOffset means offset between label and axis,
37545     * which is useful when 'onZero', where axisLabel is in the grid and
37546     * label in outside grid.
37547     *
37548     * Tips: like always,
37549     * positive rotation represents anticlockwise, and negative rotation
37550     * represents clockwise.
37551     * The direction of position coordinate is the same as the direction
37552     * of screen coordinate.
37553     *
37554     * Do not need to consider axis 'inverse', which is auto processed by
37555     * axis extent.
37556     *
37557     * @param {module:zrender/container/Group} group
37558     * @param {Object} axisModel
37559     * @param {Object} opt Standard axis parameters.
37560     * @param {Array.<number>} opt.position [x, y]
37561     * @param {number} opt.rotation by radian
37562     * @param {number} [opt.nameDirection=1] 1 or -1 Used when nameLocation is 'middle' or 'center'.
37563     * @param {number} [opt.tickDirection=1] 1 or -1
37564     * @param {number} [opt.labelDirection=1] 1 or -1
37565     * @param {number} [opt.labelOffset=0] Usefull when onZero.
37566     * @param {string} [opt.axisLabelShow] default get from axisModel.
37567     * @param {string} [opt.axisName] default get from axisModel.
37568     * @param {number} [opt.axisNameAvailableWidth]
37569     * @param {number} [opt.labelRotate] by degree, default get from axisModel.
37570     * @param {number} [opt.strokeContainThreshold] Default label interval when label
37571     * @param {number} [opt.nameTruncateMaxWidth]
37572     */
37573    var AxisBuilder = function (axisModel, opt) {
37574
37575        /**
37576         * @readOnly
37577         */
37578        this.opt = opt;
37579
37580        /**
37581         * @readOnly
37582         */
37583        this.axisModel = axisModel;
37584
37585        // Default value
37586        defaults(
37587            opt,
37588            {
37589                labelOffset: 0,
37590                nameDirection: 1,
37591                tickDirection: 1,
37592                labelDirection: 1,
37593                silent: true
37594            }
37595        );
37596
37597        /**
37598         * @readOnly
37599         */
37600        this.group = new Group();
37601
37602        // FIXME Not use a seperate text group?
37603        var dumbGroup = new Group({
37604            position: opt.position.slice(),
37605            rotation: opt.rotation
37606        });
37607
37608        // this.group.add(dumbGroup);
37609        // this._dumbGroup = dumbGroup;
37610
37611        dumbGroup.updateTransform();
37612        this._transform = dumbGroup.transform;
37613
37614        this._dumbGroup = dumbGroup;
37615    };
37616
37617    AxisBuilder.prototype = {
37618
37619        constructor: AxisBuilder,
37620
37621        hasBuilder: function (name) {
37622            return !!builders[name];
37623        },
37624
37625        add: function (name) {
37626            builders[name].call(this);
37627        },
37628
37629        getGroup: function () {
37630            return this.group;
37631        }
37632
37633    };
37634
37635    var builders = {
37636
37637        /**
37638         * @private
37639         */
37640        axisLine: function () {
37641            var opt = this.opt;
37642            var axisModel = this.axisModel;
37643
37644            if (!axisModel.get('axisLine.show')) {
37645                return;
37646            }
37647
37648            var extent = this.axisModel.axis.getExtent();
37649
37650            var matrix = this._transform;
37651            var pt1 = [extent[0], 0];
37652            var pt2 = [extent[1], 0];
37653            if (matrix) {
37654                applyTransform(pt1, pt1, matrix);
37655                applyTransform(pt2, pt2, matrix);
37656            }
37657
37658            var lineStyle = extend(
37659                {
37660                    lineCap: 'round'
37661                },
37662                axisModel.getModel('axisLine.lineStyle').getLineStyle()
37663            );
37664
37665            this.group.add(new Line(subPixelOptimizeLine({
37666                // Id for animation
37667                anid: 'line',
37668
37669                shape: {
37670                    x1: pt1[0],
37671                    y1: pt1[1],
37672                    x2: pt2[0],
37673                    y2: pt2[1]
37674                },
37675                style: lineStyle,
37676                strokeContainThreshold: opt.strokeContainThreshold || 5,
37677                silent: true,
37678                z2: 1
37679            })));
37680
37681            var arrows = axisModel.get('axisLine.symbol');
37682            var arrowSize = axisModel.get('axisLine.symbolSize');
37683
37684            var arrowOffset = axisModel.get('axisLine.symbolOffset') || 0;
37685            if (typeof arrowOffset === 'number') {
37686                arrowOffset = [arrowOffset, arrowOffset];
37687            }
37688
37689            if (arrows != null) {
37690                if (typeof arrows === 'string') {
37691                    // Use the same arrow for start and end point
37692                    arrows = [arrows, arrows];
37693                }
37694                if (typeof arrowSize === 'string'
37695                    || typeof arrowSize === 'number'
37696                ) {
37697                    // Use the same size for width and height
37698                    arrowSize = [arrowSize, arrowSize];
37699                }
37700
37701                var symbolWidth = arrowSize[0];
37702                var symbolHeight = arrowSize[1];
37703
37704                each$1([{
37705                    rotate: opt.rotation + Math.PI / 2,
37706                    offset: arrowOffset[0],
37707                    r: 0
37708                }, {
37709                    rotate: opt.rotation - Math.PI / 2,
37710                    offset: arrowOffset[1],
37711                    r: Math.sqrt((pt1[0] - pt2[0]) * (pt1[0] - pt2[0])
37712                        + (pt1[1] - pt2[1]) * (pt1[1] - pt2[1]))
37713                }], function (point, index) {
37714                    if (arrows[index] !== 'none' && arrows[index] != null) {
37715                        var symbol = createSymbol(
37716                            arrows[index],
37717                            -symbolWidth / 2,
37718                            -symbolHeight / 2,
37719                            symbolWidth,
37720                            symbolHeight,
37721                            lineStyle.stroke,
37722                            true
37723                        );
37724
37725                        // Calculate arrow position with offset
37726                        var r = point.r + point.offset;
37727                        var pos = [
37728                            pt1[0] + r * Math.cos(opt.rotation),
37729                            pt1[1] - r * Math.sin(opt.rotation)
37730                        ];
37731
37732                        symbol.attr({
37733                            rotation: point.rotate,
37734                            position: pos,
37735                            silent: true
37736                        });
37737                        this.group.add(symbol);
37738                    }
37739                }, this);
37740            }
37741        },
37742
37743        /**
37744         * @private
37745         */
37746        axisTickLabel: function () {
37747            var axisModel = this.axisModel;
37748            var opt = this.opt;
37749
37750            var tickEls = buildAxisTick(this, axisModel, opt);
37751            var labelEls = buildAxisLabel(this, axisModel, opt);
37752
37753            fixMinMaxLabelShow(axisModel, labelEls, tickEls);
37754        },
37755
37756        /**
37757         * @private
37758         */
37759        axisName: function () {
37760            var opt = this.opt;
37761            var axisModel = this.axisModel;
37762            var name = retrieve(opt.axisName, axisModel.get('name'));
37763
37764            if (!name) {
37765                return;
37766            }
37767
37768            var nameLocation = axisModel.get('nameLocation');
37769            var nameDirection = opt.nameDirection;
37770            var textStyleModel = axisModel.getModel('nameTextStyle');
37771            var gap = axisModel.get('nameGap') || 0;
37772
37773            var extent = this.axisModel.axis.getExtent();
37774            var gapSignal = extent[0] > extent[1] ? -1 : 1;
37775            var pos = [
37776                nameLocation === 'start'
37777                    ? extent[0] - gapSignal * gap
37778                    : nameLocation === 'end'
37779                    ? extent[1] + gapSignal * gap
37780                    : (extent[0] + extent[1]) / 2, // 'middle'
37781                // Reuse labelOffset.
37782                isNameLocationCenter(nameLocation) ? opt.labelOffset + nameDirection * gap : 0
37783            ];
37784
37785            var labelLayout;
37786
37787            var nameRotation = axisModel.get('nameRotate');
37788            if (nameRotation != null) {
37789                nameRotation = nameRotation * PI$2 / 180; // To radian.
37790            }
37791
37792            var axisNameAvailableWidth;
37793
37794            if (isNameLocationCenter(nameLocation)) {
37795                labelLayout = innerTextLayout(
37796                    opt.rotation,
37797                    nameRotation != null ? nameRotation : opt.rotation, // Adapt to axis.
37798                    nameDirection
37799                );
37800            }
37801            else {
37802                labelLayout = endTextLayout(
37803                    opt, nameLocation, nameRotation || 0, extent
37804                );
37805
37806                axisNameAvailableWidth = opt.axisNameAvailableWidth;
37807                if (axisNameAvailableWidth != null) {
37808                    axisNameAvailableWidth = Math.abs(
37809                        axisNameAvailableWidth / Math.sin(labelLayout.rotation)
37810                    );
37811                    !isFinite(axisNameAvailableWidth) && (axisNameAvailableWidth = null);
37812                }
37813            }
37814
37815            var textFont = textStyleModel.getFont();
37816
37817            var truncateOpt = axisModel.get('nameTruncate', true) || {};
37818            var ellipsis = truncateOpt.ellipsis;
37819            var maxWidth = retrieve(
37820                opt.nameTruncateMaxWidth, truncateOpt.maxWidth, axisNameAvailableWidth
37821            );
37822            // FIXME
37823            // truncate rich text? (consider performance)
37824            var truncatedText = (ellipsis != null && maxWidth != null)
37825                ? truncateText$1(
37826                    name, maxWidth, textFont, ellipsis,
37827                    {minChar: 2, placeholder: truncateOpt.placeholder}
37828                )
37829                : name;
37830
37831            var tooltipOpt = axisModel.get('tooltip', true);
37832
37833            var mainType = axisModel.mainType;
37834            var formatterParams = {
37835                componentType: mainType,
37836                name: name,
37837                $vars: ['name']
37838            };
37839            formatterParams[mainType + 'Index'] = axisModel.componentIndex;
37840
37841            var textEl = new Text({
37842                // Id for animation
37843                anid: 'name',
37844
37845                __fullText: name,
37846                __truncatedText: truncatedText,
37847
37848                position: pos,
37849                rotation: labelLayout.rotation,
37850                silent: isSilent(axisModel),
37851                z2: 1,
37852                tooltip: (tooltipOpt && tooltipOpt.show)
37853                    ? extend({
37854                        content: name,
37855                        formatter: function () {
37856                            return name;
37857                        },
37858                        formatterParams: formatterParams
37859                    }, tooltipOpt)
37860                    : null
37861            });
37862
37863            setTextStyle(textEl.style, textStyleModel, {
37864                text: truncatedText,
37865                textFont: textFont,
37866                textFill: textStyleModel.getTextColor()
37867                || axisModel.get('axisLine.lineStyle.color'),
37868                textAlign: labelLayout.textAlign,
37869                textVerticalAlign: labelLayout.textVerticalAlign
37870            });
37871
37872            if (axisModel.get('triggerEvent')) {
37873                textEl.eventData = makeAxisEventDataBase(axisModel);
37874                textEl.eventData.targetType = 'axisName';
37875                textEl.eventData.name = name;
37876            }
37877
37878            // FIXME
37879            this._dumbGroup.add(textEl);
37880            textEl.updateTransform();
37881
37882            this.group.add(textEl);
37883
37884            textEl.decomposeTransform();
37885        }
37886
37887    };
37888
37889    /**
37890     * @public
37891     * @static
37892     * @param {Object} opt
37893     * @param {number} axisRotation in radian
37894     * @param {number} textRotation in radian
37895     * @param {number} direction
37896     * @return {Object} {
37897 *  rotation, // according to axis
37898 *  textAlign,
37899 *  textVerticalAlign
37900 * }
37901     */
37902    var innerTextLayout = AxisBuilder.innerTextLayout = function (axisRotation, textRotation, direction) {
37903        var rotationDiff = remRadian(textRotation - axisRotation);
37904        var textAlign;
37905        var textVerticalAlign;
37906
37907        if (isRadianAroundZero(rotationDiff)) { // Label is parallel with axis line.
37908            textVerticalAlign = direction > 0 ? 'top' : 'bottom';
37909            textAlign = 'center';
37910        }
37911        else if (isRadianAroundZero(rotationDiff - PI$2)) { // Label is inverse parallel with axis line.
37912            textVerticalAlign = direction > 0 ? 'bottom' : 'top';
37913            textAlign = 'center';
37914        }
37915        else {
37916            textVerticalAlign = 'middle';
37917
37918            if (rotationDiff > 0 && rotationDiff < PI$2) {
37919                textAlign = direction > 0 ? 'right' : 'left';
37920            }
37921            else {
37922                textAlign = direction > 0 ? 'left' : 'right';
37923            }
37924        }
37925
37926        return {
37927            rotation: rotationDiff,
37928            textAlign: textAlign,
37929            textVerticalAlign: textVerticalAlign
37930        };
37931    };
37932
37933    function endTextLayout(opt, textPosition, textRotate, extent) {
37934        var rotationDiff = remRadian(textRotate - opt.rotation);
37935        var textAlign;
37936        var textVerticalAlign;
37937        var inverse = extent[0] > extent[1];
37938        var onLeft = (textPosition === 'start' && !inverse)
37939            || (textPosition !== 'start' && inverse);
37940
37941        if (isRadianAroundZero(rotationDiff - PI$2 / 2)) {
37942            textVerticalAlign = onLeft ? 'bottom' : 'top';
37943            textAlign = 'center';
37944        }
37945        else if (isRadianAroundZero(rotationDiff - PI$2 * 1.5)) {
37946            textVerticalAlign = onLeft ? 'top' : 'bottom';
37947            textAlign = 'center';
37948        }
37949        else {
37950            textVerticalAlign = 'middle';
37951            if (rotationDiff < PI$2 * 1.5 && rotationDiff > PI$2 / 2) {
37952                textAlign = onLeft ? 'left' : 'right';
37953            }
37954            else {
37955                textAlign = onLeft ? 'right' : 'left';
37956            }
37957        }
37958
37959        return {
37960            rotation: rotationDiff,
37961            textAlign: textAlign,
37962            textVerticalAlign: textVerticalAlign
37963        };
37964    }
37965
37966    function isSilent(axisModel) {
37967        var tooltipOpt = axisModel.get('tooltip');
37968        return axisModel.get('silent')
37969            // Consider mouse cursor, add these restrictions.
37970            || !(
37971                axisModel.get('triggerEvent') || (tooltipOpt && tooltipOpt.show)
37972            );
37973    }
37974
37975    function fixMinMaxLabelShow(axisModel, labelEls, tickEls) {
37976        // If min or max are user set, we need to check
37977        // If the tick on min(max) are overlap on their neighbour tick
37978        // If they are overlapped, we need to hide the min(max) tick label
37979        var showMinLabel = axisModel.get('axisLabel.showMinLabel');
37980        var showMaxLabel = axisModel.get('axisLabel.showMaxLabel');
37981
37982        // FIXME
37983        // Have not consider onBand yet, where tick els is more than label els.
37984
37985        labelEls = labelEls || [];
37986        tickEls = tickEls || [];
37987
37988        var firstLabel = labelEls[0];
37989        var nextLabel = labelEls[1];
37990        var lastLabel = labelEls[labelEls.length - 1];
37991        var prevLabel = labelEls[labelEls.length - 2];
37992
37993        var firstTick = tickEls[0];
37994        var nextTick = tickEls[1];
37995        var lastTick = tickEls[tickEls.length - 1];
37996        var prevTick = tickEls[tickEls.length - 2];
37997
37998        if (showMinLabel === false) {
37999            ignoreEl(firstLabel);
38000            ignoreEl(firstTick);
38001        }
38002        else if (isTwoLabelOverlapped(firstLabel, nextLabel)) {
38003            if (showMinLabel) {
38004                ignoreEl(nextLabel);
38005                ignoreEl(nextTick);
38006            }
38007            else {
38008                ignoreEl(firstLabel);
38009                ignoreEl(firstTick);
38010            }
38011        }
38012
38013        if (showMaxLabel === false) {
38014            ignoreEl(lastLabel);
38015            ignoreEl(lastTick);
38016        }
38017        else if (isTwoLabelOverlapped(prevLabel, lastLabel)) {
38018            if (showMaxLabel) {
38019                ignoreEl(prevLabel);
38020                ignoreEl(prevTick);
38021            }
38022            else {
38023                ignoreEl(lastLabel);
38024                ignoreEl(lastTick);
38025            }
38026        }
38027    }
38028
38029    function ignoreEl(el) {
38030        el && (el.ignore = true);
38031    }
38032
38033    function isTwoLabelOverlapped(current, next, labelLayout) {
38034        // current and next has the same rotation.
38035        var firstRect = current && current.getBoundingRect().clone();
38036        var nextRect = next && next.getBoundingRect().clone();
38037
38038        if (!firstRect || !nextRect) {
38039            return;
38040        }
38041
38042        // When checking intersect of two rotated labels, we use mRotationBack
38043        // to avoid that boundingRect is enlarge when using `boundingRect.applyTransform`.
38044        var mRotationBack = identity([]);
38045        rotate(mRotationBack, mRotationBack, -current.rotation);
38046
38047        firstRect.applyTransform(mul$1([], mRotationBack, current.getLocalTransform()));
38048        nextRect.applyTransform(mul$1([], mRotationBack, next.getLocalTransform()));
38049
38050        return firstRect.intersect(nextRect);
38051    }
38052
38053    function isNameLocationCenter(nameLocation) {
38054        return nameLocation === 'middle' || nameLocation === 'center';
38055    }
38056
38057    function buildAxisTick(axisBuilder, axisModel, opt) {
38058        var axis = axisModel.axis;
38059
38060        if (!axisModel.get('axisTick.show') || axis.scale.isBlank()) {
38061            return;
38062        }
38063
38064        var tickModel = axisModel.getModel('axisTick');
38065
38066        var lineStyleModel = tickModel.getModel('lineStyle');
38067        var tickLen = tickModel.get('length');
38068
38069        var ticksCoords = axis.getTicksCoords();
38070
38071        var pt1 = [];
38072        var pt2 = [];
38073        var matrix = axisBuilder._transform;
38074
38075        var tickEls = [];
38076
38077        for (var i = 0; i < ticksCoords.length; i++) {
38078            var tickCoord = ticksCoords[i].coord;
38079
38080            pt1[0] = tickCoord;
38081            pt1[1] = 0;
38082            pt2[0] = tickCoord;
38083            pt2[1] = opt.tickDirection * tickLen;
38084
38085            if (matrix) {
38086                applyTransform(pt1, pt1, matrix);
38087                applyTransform(pt2, pt2, matrix);
38088            }
38089            // Tick line, Not use group transform to have better line draw
38090            var tickEl = new Line(subPixelOptimizeLine({
38091                // Id for animation
38092                anid: 'tick_' + ticksCoords[i].tickValue,
38093
38094                shape: {
38095                    x1: pt1[0],
38096                    y1: pt1[1],
38097                    x2: pt2[0],
38098                    y2: pt2[1]
38099                },
38100                style: defaults(
38101                    lineStyleModel.getLineStyle(),
38102                    {
38103                        stroke: axisModel.get('axisLine.lineStyle.color')
38104                    }
38105                ),
38106                z2: 2,
38107                silent: true
38108            }));
38109            axisBuilder.group.add(tickEl);
38110            tickEls.push(tickEl);
38111        }
38112
38113        return tickEls;
38114    }
38115
38116    function buildAxisLabel(axisBuilder, axisModel, opt) {
38117        var axis = axisModel.axis;
38118        var show = retrieve(opt.axisLabelShow, axisModel.get('axisLabel.show'));
38119
38120        if (!show || axis.scale.isBlank()) {
38121            return;
38122        }
38123
38124        var labelModel = axisModel.getModel('axisLabel');
38125        var labelMargin = labelModel.get('margin');
38126        var labels = axis.getViewLabels();
38127
38128        // Special label rotate.
38129        var labelRotation = (
38130            retrieve(opt.labelRotate, labelModel.get('rotate')) || 0
38131        ) * PI$2 / 180;
38132
38133        var labelLayout = innerTextLayout(opt.rotation, labelRotation, opt.labelDirection);
38134        var rawCategoryData = axisModel.getCategories(true);
38135
38136        var labelEls = [];
38137        var silent = isSilent(axisModel);
38138        var triggerEvent = axisModel.get('triggerEvent');
38139
38140        each$1(labels, function (labelItem, index) {
38141            var tickValue = labelItem.tickValue;
38142            var formattedLabel = labelItem.formattedLabel;
38143            var rawLabel = labelItem.rawLabel;
38144
38145            var itemLabelModel = labelModel;
38146            if (rawCategoryData && rawCategoryData[tickValue] && rawCategoryData[tickValue].textStyle) {
38147                itemLabelModel = new Model(
38148                    rawCategoryData[tickValue].textStyle, labelModel, axisModel.ecModel
38149                );
38150            }
38151
38152            var textColor = itemLabelModel.getTextColor()
38153                || axisModel.get('axisLine.lineStyle.color');
38154
38155            var tickCoord = axis.dataToCoord(tickValue);
38156            var pos = [
38157                tickCoord,
38158                opt.labelOffset + opt.labelDirection * labelMargin
38159            ];
38160
38161            var textEl = new Text({
38162                // Id for animation
38163                anid: 'label_' + tickValue,
38164                position: pos,
38165                rotation: labelLayout.rotation,
38166                silent: silent,
38167                z2: 10
38168            });
38169
38170            setTextStyle(textEl.style, itemLabelModel, {
38171                text: formattedLabel,
38172                textAlign: itemLabelModel.getShallow('align', true)
38173                || labelLayout.textAlign,
38174                textVerticalAlign: itemLabelModel.getShallow('verticalAlign', true)
38175                || itemLabelModel.getShallow('baseline', true)
38176                || labelLayout.textVerticalAlign,
38177                textFill: typeof textColor === 'function'
38178                    ? textColor(
38179                        // (1) In category axis with data zoom, tick is not the original
38180                        // index of axis.data. So tick should not be exposed to user
38181                        // in category axis.
38182                        // (2) Compatible with previous version, which always use formatted label as
38183                        // input. But in interval scale the formatted label is like '223,445', which
38184                        // maked user repalce ','. So we modify it to return original val but remain
38185                        // it as 'string' to avoid error in replacing.
38186                        axis.type === 'category'
38187                            ? rawLabel
38188                            : axis.type === 'value'
38189                            ? tickValue + ''
38190                            : tickValue,
38191                        index
38192                    )
38193                    : textColor
38194            });
38195
38196            // Pack data for mouse event
38197            if (triggerEvent) {
38198                textEl.eventData = makeAxisEventDataBase(axisModel);
38199                textEl.eventData.targetType = 'axisLabel';
38200                textEl.eventData.value = rawLabel;
38201            }
38202
38203            // FIXME
38204            axisBuilder._dumbGroup.add(textEl);
38205            textEl.updateTransform();
38206
38207            labelEls.push(textEl);
38208            axisBuilder.group.add(textEl);
38209
38210            textEl.decomposeTransform();
38211
38212        });
38213
38214        return labelEls;
38215    }
38216
38217    /*
38218* Licensed to the Apache Software Foundation (ASF) under one
38219* or more contributor license agreements.  See the NOTICE file
38220* distributed with this work for additional information
38221* regarding copyright ownership.  The ASF licenses this file
38222* to you under the Apache License, Version 2.0 (the
38223* "License");
38223 you may not use this file except in compliance
38224* with the License.  You may obtain a copy of the License at
38225*
38226*   http://www.apache.org/licenses/LICENSE-2.0
38227*
38228* Unless required by applicable law or agreed to in writing,
38229* software distributed under the License is distributed on an
38230* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
38231* KIND, either express or implied.  See the License for the
38232* specific language governing permissions and limitations
38233* under the License.
38234*/
38235
38236    var each$6 = each$1;
38237    var curry$1 = curry;
38238
38239// Build axisPointerModel, mergin tooltip.axisPointer model for each axis.
38240// allAxesInfo should be updated when setOption performed.
38241    function collect(ecModel, api) {
38242        var result = {
38243            /**
38244             * key: makeKey(axis.model)
38245             * value: {
38246         *      axis,
38247         *      coordSys,
38248         *      axisPointerModel,
38249         *      triggerTooltip,
38250         *      involveSeries,
38251         *      snap,
38252         *      seriesModels,
38253         *      seriesDataCount
38254         * }
38255             */
38256            axesInfo: {},
38257            seriesInvolved: false,
38258            /**
38259             * key: makeKey(coordSys.model)
38260             * value: Object: key makeKey(axis.model), value: axisInfo
38261             */
38262            coordSysAxesInfo: {},
38263            coordSysMap: {}
38264        };
38265
38266        collectAxesInfo(result, ecModel, api);
38267
38268        // Check seriesInvolved for performance, in case too many series in some chart.
38269        result.seriesInvolved && collectSeriesInfo(result, ecModel);
38270
38271        return result;
38272    }
38273
38274    function collectAxesInfo(result, ecModel, api) {
38275        var globalTooltipModel = ecModel.getComponent('tooltip');
38276        var globalAxisPointerModel = ecModel.getComponent('axisPointer');
38277        // links can only be set on global.
38278        var linksOption = globalAxisPointerModel.get('link', true) || [];
38279        var linkGroups = [];
38280
38281        // Collect axes info.
38282        each$6(api.getCoordinateSystems(), function (coordSys) {
38283            // Some coordinate system do not support axes, like geo.
38284            if (!coordSys.axisPointerEnabled) {
38285                return;
38286            }
38287
38288            var coordSysKey = makeKey(coordSys.model);
38289            var axesInfoInCoordSys = result.coordSysAxesInfo[coordSysKey] = {};
38290            result.coordSysMap[coordSysKey] = coordSys;
38291
38292            // Set tooltip (like 'cross') is a convienent way to show axisPointer
38293            // for user. So we enable seting tooltip on coordSys model.
38294            var coordSysModel = coordSys.model;
38295            var baseTooltipModel = coordSysModel.getModel('tooltip', globalTooltipModel);
38296
38297            each$6(coordSys.getAxes(), curry$1(saveTooltipAxisInfo, false, null));
38298
38299            // If axis tooltip used, choose tooltip axis for each coordSys.
38300            // Notice this case: coordSys is `grid` but not `cartesian2D` here.
38301            if (coordSys.getTooltipAxes
38302                && globalTooltipModel
38303                // If tooltip.showContent is set as false, tooltip will not
38304                // show but axisPointer will show as normal.
38305                && baseTooltipModel.get('show')
38306            ) {
38307                // Compatible with previous logic. But series.tooltip.trigger: 'axis'
38308                // or series.data[n].tooltip.trigger: 'axis' are not support any more.
38309                var triggerAxis = baseTooltipModel.get('trigger') === 'axis';
38310                var cross = baseTooltipModel.get('axisPointer.type') === 'cross';
38311                var tooltipAxes = coordSys.getTooltipAxes(baseTooltipModel.get('axisPointer.axis'));
38312                if (triggerAxis || cross) {
38313                    each$6(tooltipAxes.baseAxes, curry$1(
38314                        saveTooltipAxisInfo, cross ? 'cross' : true, triggerAxis
38315                    ));
38316                }
38317                if (cross) {
38318                    each$6(tooltipAxes.otherAxes, curry$1(saveTooltipAxisInfo, 'cross', false));
38319                }
38320            }
38321
38322            // fromTooltip: true | false | 'cross'
38323            // triggerTooltip: true | false | null
38324            function saveTooltipAxisInfo(fromTooltip, triggerTooltip, axis) {
38325                var axisPointerModel = axis.model.getModel('axisPointer', globalAxisPointerModel);
38326
38327                var axisPointerShow = axisPointerModel.get('show');
38328                if (!axisPointerShow || (
38329                        axisPointerShow === 'auto'
38330                        && !fromTooltip
38331                        && !isHandleTrigger(axisPointerModel)
38332                    )) {
38333                    return;
38334                }
38335
38336                if (triggerTooltip == null) {
38337                    triggerTooltip = axisPointerModel.get('triggerTooltip');
38338                }
38339
38340                axisPointerModel = fromTooltip
38341                    ? makeAxisPointerModel(
38342                        axis, baseTooltipModel, globalAxisPointerModel, ecModel,
38343                        fromTooltip, triggerTooltip
38344                    )
38345                    : axisPointerModel;
38346
38347                var snap = axisPointerModel.get('snap');
38348                var key = makeKey(axis.model);
38349                var involveSeries = triggerTooltip || snap || axis.type === 'category';
38350
38351                // If result.axesInfo[key] exist, override it (tooltip has higher priority).
38352                var axisInfo = result.axesInfo[key] = {
38353                    key: key,
38354                    axis: axis,
38355                    coordSys: coordSys,
38356                    axisPointerModel: axisPointerModel,
38357                    triggerTooltip: triggerTooltip,
38358                    involveSeries: involveSeries,
38359                    snap: snap,
38360                    useHandle: isHandleTrigger(axisPointerModel),
38361                    seriesModels: []
38362                };
38363                axesInfoInCoordSys[key] = axisInfo;
38364                result.seriesInvolved |= involveSeries;
38365
38366                var groupIndex = getLinkGroupIndex(linksOption, axis);
vendor: 3,646 bytes, lines 38367-38445
38367                if (groupIndex != null) {
38368                    var linkGroup = linkGroups[groupIndex] || (linkGroups[groupIndex] = {axesInfo: {}});
38369                    linkGroup.axesInfo[key] = axisInfo;
38370                    linkGroup.mapper = linksOption[groupIndex].mapper;
38371                    axisInfo.linkGroup = linkGroup;
38372                }
38373            }
38374        });
38375    }
38376
38377    function makeAxisPointerModel(
38378        axis, baseTooltipModel, globalAxisPointerModel, ecModel, fromTooltip, triggerTooltip
38379    ) {
38380        var tooltipAxisPointerModel = baseTooltipModel.getModel('axisPointer');
38381        var volatileOption = {};
38382
38383        each$6(
38384            [
38385                'type', 'snap', 'lineStyle', 'shadowStyle', 'label',
38386                'animation', 'animationDurationUpdate', 'animationEasingUpdate', 'z'
38387            ],
38388            function (field) {
38389                volatileOption[field] = clone(tooltipAxisPointerModel.get(field));
38390            }
38391        );
38392
38393        // category axis do not auto snap, otherwise some tick that do not
38394        // has value can not be hovered. value/time/log axis default snap if
38395        // triggered from tooltip and trigger tooltip.
38396        volatileOption.snap = axis.type !== 'category' && !!triggerTooltip;
38397
38398        // Compatibel with previous behavior, tooltip axis do not show label by default.
38399        // Only these properties can be overrided from tooltip to axisPointer.
38400        if (tooltipAxisPointerModel.get('type') === 'cross') {
38401            volatileOption.type = 'line';
38402        }
38403        var labelOption = volatileOption.label || (volatileOption.label = {});
38404        // Follow the convention, do not show label when triggered by tooltip by default.
38405        labelOption.show == null && (labelOption.show = false);
38406
38407        if (fromTooltip === 'cross') {
38408            // When 'cross', both axes show labels.
38409            var tooltipAxisPointerLabelShow = tooltipAxisPointerModel.get('label.show');
38410            labelOption.show = tooltipAxisPointerLabelShow != null ? tooltipAxisPointerLabelShow : true;
38411            // If triggerTooltip, this is a base axis, which should better not use cross style
38412            // (cross style is dashed by default)
38413            if (!triggerTooltip) {
38414                var crossStyle = volatileOption.lineStyle = tooltipAxisPointerModel.get('crossStyle');
38415                crossStyle && defaults(labelOption, crossStyle.textStyle);
38416            }
38417        }
38418
38419        return axis.model.getModel(
38420            'axisPointer',
38421            new Model(volatileOption, globalAxisPointerModel, ecModel)
38422        );
38423    }
38424
38425    function collectSeriesInfo(result, ecModel) {
38426        // Prepare data for axis trigger
38427        ecModel.eachSeries(function (seriesModel) {
38428
38429            // Notice this case: this coordSys is `cartesian2D` but not `grid`.
38430            var coordSys = seriesModel.coordinateSystem;
38431            var seriesTooltipTrigger = seriesModel.get('tooltip.trigger', true);
38432            var seriesTooltipShow = seriesModel.get('tooltip.show', true);
38433            if (!coordSys
38434                || seriesTooltipTrigger === 'none'
38435                || seriesTooltipTrigger === false
38436                || seriesTooltipTrigger === 'item'
38437                || seriesTooltipShow === false
38438                || seriesModel.get('axisPointer.show', true) === false
38439            ) {
38440                return;
38441            }
38442
38443            each$6(result.coordSysAxesInfo[makeKey(coordSys.model)], function (axisInfo) {
38444                var axis = axisInfo.axis;
38445                if (coordSys.getAxis(axis.dim) === axis) {
38446                    axisInfo.seriesModels.push(seriesModel);
38447                    axisInfo.seriesDataCount == null && (axisInfo.seriesDataCount = 0);
38448                    axisInfo.seriesDataCount += seriesModel.getData().count();
38449                }
38450            });
38451
38452        }, this);
38453    }
38454
38455    /**
38456     * For example:
38457     * {
38458 *     axisPointer: {
38459 *         links: [{
38460 *             xAxisIndex: [2, 4],
38461 *             yAxisIndex: 'all'
38462 *         }, {
38463 *             xAxisId: ['a5', 'a7'],
38464 *             xAxisName: 'xxx'
38465 *         }]
38466 *     }
38467 * }
38468     */
38469    function getLinkGroupIndex(linksOption, axis) {
38470        var axisModel = axis.model;
38471        var dim = axis.dim;
38472        for (var i = 0; i < linksOption.length; i++) {
38473            var linkOption = linksOption[i] || {};
38474            if (checkPropInLink(linkOption[dim + 'AxisId'], axisModel.id)
38475                || checkPropInLink(linkOption[dim + 'AxisIndex'], axisModel.componentIndex)
38476                || checkPropInLink(linkOption[dim + 'AxisName'], axisModel.name)
38477            ) {
38478                return i;
38479            }
38480        }
38481    }
38482
38483    function checkPropInLink(linkPropValue, axisPropValue) {
38484        return linkPropValue === 'all'
38485            || (isArray(linkPropValue) && indexOf(linkPropValue, axisPropValue) >= 0)
38486            || linkPropValue === axisPropValue;
38487    }
38488
38489    function fixValue(axisModel) {
38490        var axisInfo = getAxisInfo(axisModel);
38491        if (!axisInfo) {
38492            return;
38493        }
38494
38495        var axisPointerModel = axisInfo.axisPointerModel;
38496        var scale = axisInfo.axis.scale;
38497        var option = axisPointerModel.option;
38498        var status = axisPointerModel.get('status');
38499        var value = axisPointerModel.get('value');
38500
38501        // Parse init value for category and time axis.
38502        if (value != null) {
38503            value = scale.parse(value);
38504        }
38505
38506        var useHandle = isHandleTrigger(axisPointerModel);
38507        // If `handle` used, `axisPointer` will always be displayed, so value
38508        // and status should be initialized.
38509        if (status == null) {
38510            option.status = useHandle ? 'show' : 'hide';
38511        }
38512
38513        var extent = scale.getExtent().slice();
38514        extent[0] > extent[1] && extent.reverse();
38515
38516        if (// Pick a value on axis when initializing.
38517        value == null
38518        // If both `handle` and `dataZoom` are used, value may be out of axis extent,
38519        // where we should re-pick a value to keep `handle` displaying normally.
38520        || value > extent[1]
38521        ) {
38522            // Make handle displayed on the end of the axis when init, which looks better.
38523            value = extent[1];
38524        }
38525        if (value < extent[0]) {
38526            value = extent[0];
38527        }
38528
38529        option.value = value;
38530
38531        if (useHandle) {
38532            option.status = axisInfo.axis.scale.isBlank() ? 'hide' : 'show';
38533        }
38534    }
38535
38536    function getAxisInfo(axisModel) {
38537        var coordSysAxesInfo = (axisModel.ecModel.getComponent('axisPointer') || {}).coordSysAxesInfo;
38538        return coordSysAxesInfo && coordSysAxesInfo.axesInfo[makeKey(axisModel)];
38539    }
38540
38541    function getAxisPointerModel(axisModel) {
38542        var axisInfo = getAxisInfo(axisModel);
38543        return axisInfo && axisInfo.axisPointerModel;
38544    }
38545
38546    function isHandleTrigger(axisPointerModel) {
38547        return !!axisPointerModel.get('handle.show');
38548    }
38549
38550    /**
38551     * @param {module:echarts/model/Model} model
38552     * @return {string} unique key
38553     */
38554    function makeKey(model) {
38555        return model.type + '||' + model.id;
38556    }
38557
38558    /*
38559* Licensed to the Apache Software Foundation (ASF) under one
38560* or more contributor license agreements.  See the NOTICE file
38561* distributed with this work for additional information
38562* regarding copyright ownership.  The ASF licenses this file
38563* to you under the Apache License, Version 2.0 (the
38564* "License"); you may not use this file except in compliance
38565* with the License.  You may obtain a copy of the License at
38566*
38567*   http://www.apache.org/licenses/LICENSE-2.0
38568*
38569* Unless required by applicable law or agreed to in writing,
38570* software distributed under the License is distributed on an
38571* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
38572* KIND, either express or implied.  See the License for the
38573* specific language governing permissions and limitations
38574* under the License.
38575*/
38576
38577    /**
38578     * Base class of AxisView.
38579     */
38580    var AxisView = extendComponentView({
38581
38582        type: 'axis',
38583
38584        /**
38585         * @private
38586         */
38587        _axisPointer: null,
38588
38589        /**
38590         * @protected
38591         * @type {string}
38592         */
38593        axisPointerClass: null,
38594
38595        /**
38596         * @override
38597         */
38598        render: function (axisModel, ecModel, api, payload) {
38599            // FIXME
38600            // This process should proformed after coordinate systems updated
38601            // (axis scale updated), and should be performed each time update.
38602            // So put it here temporarily, although it is not appropriate to
38603            // put a model-writing procedure in `view`.
38604            this.axisPointerClass && fixValue(axisModel);
38605
38606            AxisView.superApply(this, 'render', arguments);
38607
38608            updateAxisPointer(this, axisModel, ecModel, api, payload, true);
38609        },
38610
38611        /**
38612         * Action handler.
38613         * @public
38614         * @param {module:echarts/coord/cartesian/AxisModel} axisModel
38615         * @param {module:echarts/model/Global} ecModel
38616         * @param {module:echarts/ExtensionAPI} api
38617         * @param {Object} payload
38618         */
38619        updateAxisPointer: function (axisModel, ecModel, api, payload, force) {
38620            updateAxisPointer(this, axisModel, ecModel, api, payload, false);
38621        },
38622
38623        /**
38624         * @override
38625         */
38626        remove: function (ecModel, api) {
38627            var axisPointer = this._axisPointer;
38628            axisPointer && axisPointer.remove(api);
38629            AxisView.superApply(this, 'remove', arguments);
38630        },
38631
38632        /**
38633         * @override
38634         */
38635        dispose: function (ecModel, api) {
38636            disposeAxisPointer(this, api);
38637            AxisView.superApply(this, 'dispose', arguments);
38638        }
38639
38640    });
38641
38642    function updateAxisPointer(axisView, axisModel, ecModel, api, payload, forceRender) {
38643        var Clazz = AxisView.getAxisPointerClass(axisView.axisPointerClass);
38644        if (!Clazz) {
38645            return;
38646        }
38647        var axisPointerModel = getAxisPointerModel(axisModel);
38648        axisPointerModel
38649            ? (axisView._axisPointer || (axisView._axisPointer = new Clazz()))
38650                .render(axisModel, axisPointerModel, api, forceRender)
38651            : disposeAxisPointer(axisView, api);
38652    }
38653
38654    function disposeAxisPointer(axisView, ecModel, api) {
38655        var axisPointer = axisView._axisPointer;
38656        axisPointer && axisPointer.dispose(ecModel, api);
38657        axisView._axisPointer = null;
38658    }
38659
38660    var axisPointerClazz = [];
38661
38662    AxisView.registerAxisPointerClass = function (type, clazz) {
38663        if (__DEV__) {
38664            if (axisPointerClazz[type]) {
38665                throw new Error('axisPointer ' + type + ' exists');
38666            }
38667        }
38668        axisPointerClazz[type] = clazz;
38669    };
38670
38671    AxisView.getAxisPointerClass = function (type) {
38672        return type && axisPointerClazz[type];
38673    };
38674
38675    /*
38676* Licensed to the Apache Software Foundation (ASF) under one
38677* or more contributor license agreements.  See the NOTICE file
38678* distributed with this work for additional information
38679* regarding copyright ownership.  The ASF licenses this file
38680* to you under the Apache License, Version 2.0 (the
38681* "License"); you may not use this file except in compliance
38682* with the License.  You may obtain a copy of the License at
38683*
38684*   http://www.apache.org/licenses/LICENSE-2.0
38685*
38686* Unless required by applicable law or agreed to in writing,
38687* software distributed under the License is distributed on an
38688* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
38689* KIND, either express or implied.  See the License for the
38690* specific language governing permissions and limitations
38691* under the License.
38692*/
38693
38694    /**
38695     * Can only be called after coordinate system creation stage.
38696     * (Can be called before coordinate system update stage).
38697     *
38698     * @param {Object} opt {labelInside}
38699     * @return {Object} {
38700 *  position, rotation, labelDirection, labelOffset,
38701 *  tickDirection, labelRotate, z2
38702 * }
38703     */
38704    function layout$1(gridModel, axisModel, opt) {
38705        opt = opt || {};
38706        var grid = gridModel.coordinateSystem;
38707        var axis = axisModel.axis;
38708        var layout = {};
38709        var otherAxisOnZeroOf = axis.getAxesOnZeroOf()[0];
38710
38711        var rawAxisPosition = axis.position;
38712        var axisPosition = otherAxisOnZeroOf ? 'onZero' : rawAxisPosition;
38713        var axisDim = axis.dim;
38714
38715        var rect = grid.getRect();
38716        var rectBound = [rect.x, rect.x + rect.width, rect.y, rect.y + rect.height];
38717        var idx = {left: 0, right: 1, top: 0, bottom: 1, onZero: 2};
38718        var axisOffset = axisModel.get('offset') || 0;
38719
38720        var posBound = axisDim === 'x'
38721            ? [rectBound[2] - axisOffset, rectBound[3] + axisOffset]
38722            : [rectBound[0] - axisOffset, rectBound[1] + axisOffset];
38723
38724        if (otherAxisOnZeroOf) {
38725            var onZeroCoord = otherAxisOnZeroOf.toGlobalCoord(otherAxisOnZeroOf.dataToCoord(0));
38726            posBound[idx['onZero']] = Math.max(Math.min(onZeroCoord, posBound[1]), posBound[0]);
38727        }
38728
38729        // Axis position
38730        layout.position = [
38731            axisDim === 'y' ? posBound[idx[axisPosition]] : rectBound[0],
38732            axisDim === 'x' ? posBound[idx[axisPosition]] : rectBound[3]
38733        ];
38734
38735        // Axis rotation
38736        layout.rotation = Math.PI / 2 * (axisDim === 'x' ? 0 : 1);
38737
38738        // Tick and label direction, x y is axisDim
38739        var dirMap = {top: -1, bottom: 1, left: -1, right: 1};
38740
38741        layout.labelDirection = layout.tickDirection = layout.nameDirection = dirMap[rawAxisPosition];
38742        layout.labelOffset = otherAxisOnZeroOf ? posBound[idx[rawAxisPosition]] - posBound[idx['onZero']] : 0;
38743
38744        if (axisModel.get('axisTick.inside')) {
38745            layout.tickDirection = -layout.tickDirection;
38746        }
38747        if (retrieve(opt.labelInside, axisModel.get('axisLabel.inside'))) {
38748            layout.labelDirection = -layout.labelDirection;
38749        }
38750
38751        // Special label rotation
38752        var labelRotate = axisModel.get('axisLabel.rotate');
38753        layout.labelRotate = axisPosition === 'top' ? -labelRotate : labelRotate;
38754
38755        // Over splitLine and splitArea
38756        layout.z2 = 1;
38757
38758        return layout;
38759    }
38760
38761    /*
38762* Licensed to the Apache Software Foundation (ASF) under one
38763* or more contributor license agreements.  See the NOTICE file
38764* distributed with this work for additional information
38765* regarding copyright ownership.  The ASF licenses this file
38766* to you under the Apache License, Version 2.0 (the
38767* "License");
38767 you may not use this file except in compliance
38768* with the License.  You may obtain a copy of the License at
38769*
38770*   http://www.apache.org/licenses/LICENSE-2.0
38771*
38772* Unless required by applicable law or agreed to in writing,
38773* software distributed under the License is distributed on an
38774* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
38775* KIND, either express or implied.  See the License for the
38776* specific language governing permissions and limitations
38777* under the License.
38778*/
38779
38780    var axisBuilderAttrs = [
38781        'axisLine', 'axisTickLabel', 'axisName'
38782    ];
38783    var selfBuilderAttrs = [
38784        'splitArea', 'splitLine'
38785    ];
38786
38787// function getAlignWithLabel(model, axisModel) {
38788//     var alignWithLabel = model.get('alignWithLabel');
38789//     if (alignWithLabel === 'auto') {
38790//         alignWithLabel = axisModel.get('axisTick.alignWithLabel');
38791//     }
38792//     return alignWithLabel;
38793// }
38794
38795    var CartesianAxisView = AxisView.extend({
38796
38797        type: 'cartesianAxis',
38798
38799        axisPointerClass: 'CartesianAxisPointer',
38800
38801        /**
38802         * @override
38803         */
38804        render: function (axisModel, ecModel, api, payload) {
38805
38806            this.group.removeAll();
38807
38808            var oldAxisGroup = this._axisGroup;
38809            this._axisGroup = new Group();
38810
38811            this.group.add(this._axisGroup);
38812
38813            if (!axisModel.get('show')) {
38814                return;
38815            }
38816
38817            var gridModel = axisModel.getCoordSysModel();
38818
38819            var layout = layout$1(gridModel, axisModel);
38820
38821            var axisBuilder = new AxisBuilder(axisModel, layout);
38822
38823            each$1(axisBuilderAttrs, axisBuilder.add, axisBuilder);
38824
38825            this._axisGroup.add(axisBuilder.getGroup());
38826
38827            each$1(selfBuilderAttrs, function (name) {
38828                if (axisModel.get(name + '.show')) {
38829                    this['_' + name](axisModel, gridModel);
38830                }
38831            }, this);
38832
38833            groupTransition(oldAxisGroup, this._axisGroup, axisModel);
38834
38835            CartesianAxisView.superCall(this, 'render', axisModel, ecModel, api, payload);
38836        },
38837
38838        remove: function () {
38839            this._splitAreaColors = null;
38840        },
38841
38842        /**
38843         * @param {module:echarts/coord/cartesian/AxisModel} axisModel
38844         * @param {module:echarts/coord/cartesian/GridModel} gridModel
38845         * @private
38846         */
38847        _splitLine: function (axisModel, gridModel) {
38848            var axis = axisModel.axis;
38849
38850            if (axis.scale.isBlank()) {
38851                return;
38852            }
38853
38854            var splitLineModel = axisModel.getModel('splitLine');
38855            var lineStyleModel = splitLineModel.getModel('lineStyle');
38856            var lineColors = lineStyleModel.get('color');
38857
38858            lineColors = isArray(lineColors) ? lineColors : [lineColors];
38859
38860            var gridRect = gridModel.coordinateSystem.getRect();
38861            var isHorizontal = axis.isHorizontal();
38862
38863            var lineCount = 0;
38864
38865            var ticksCoords = axis.getTicksCoords({
38866                tickModel: splitLineModel
38867            });
38868
38869            var p1 = [];
38870            var p2 = [];
38871
38872            // Simple optimization
38873            // Batching the lines if color are the same
38874            var lineStyle = lineStyleModel.getLineStyle();
38875            for (var i = 0; i < ticksCoords.length; i++) {
38876                var tickCoord = axis.toGlobalCoord(ticksCoords[i].coord);
38877
38878                if (isHorizontal) {
38879                    p1[0] = tickCoord;
38880                    p1[1] = gridRect.y;
38881                    p2[0] = tickCoord;
38882                    p2[1] = gridRect.y + gridRect.height;
38883                }
38884                else {
38885                    p1[0] = gridRect.x;
38886                    p1[1] = tickCoord;
38887                    p2[0] = gridRect.x + gridRect.width;
38888                    p2[1] = tickCoord;
38889                }
38890
38891                var colorIndex = (lineCount++) % lineColors.length;
38892                var tickValue = ticksCoords[i].tickValue;
38893                this._axisGroup.add(new Line(subPixelOptimizeLine({
38894                    anid: tickValue != null ? 'line_' + ticksCoords[i].tickValue : null,
38895                    shape: {
38896                        x1: p1[0],
38897                        y1: p1[1],
38898                        x2: p2[0],
38899                        y2: p2[1]
38900                    },
38901                    style: defaults({
38902                        stroke: lineColors[colorIndex]
38903                    }, lineStyle),
38904                    silent: true
38905                })));
38906            }
38907        },
38908
38909        /**
38910         * @param {module:echarts/coord/cartesian/AxisModel} axisModel
38911         * @param {module:echarts/coord/cartesian/GridModel} gridModel
38912         * @private
38913         */
38914        _splitArea: function (axisModel, gridModel) {
38915            var axis = axisModel.axis;
38916
38917            if (axis.scale.isBlank()) {
38918                return;
38919            }
38920
38921            var splitAreaModel = axisModel.getModel('splitArea');
38922            var areaStyleModel = splitAreaModel.getModel('areaStyle');
38923            var areaColors = areaStyleModel.get('color');
38924
38925            var gridRect = gridModel.coordinateSystem.getRect();
38926
38927            var ticksCoords = axis.getTicksCoords({
38928                tickModel: splitAreaModel,
38929                clamp: true
38930            });
38931
38932            if (!ticksCoords.length) {
38933                return;
38934            }
38935
38936            // For Making appropriate splitArea animation, the color and anid
38937            // should be corresponding to previous one if possible.
38938            var areaColorsLen = areaColors.length;
38939            var lastSplitAreaColors = this._splitAreaColors;
38940            var newSplitAreaColors = createHashMap();
38941            var colorIndex = 0;
38942            if (lastSplitAreaColors) {
38943                for (var i = 0; i < ticksCoords.length; i++) {
38944                    var cIndex = lastSplitAreaColors.get(ticksCoords[i].tickValue);
38945                    if (cIndex != null) {
38946                        colorIndex = (cIndex + (areaColorsLen - 1) * i) % areaColorsLen;
38947                        break;
38948                    }
38949                }
38950            }
38951
38952            var prev = axis.toGlobalCoord(ticksCoords[0].coord);
38953
38954            var areaStyle = areaStyleModel.getAreaStyle();
38955            areaColors = isArray(areaColors) ? areaColors : [areaColors];
38956
38957            for (var i = 1; i < ticksCoords.length; i++) {
38958                var tickCoord = axis.toGlobalCoord(ticksCoords[i].coord);
38959
38960                var x;
38961                var y;
38962                var width;
38963                var height;
38964                if (axis.isHorizontal()) {
38965                    x = prev;
38966                    y = gridRect.y;
38967                    width = tickCoord - x;
38968                    height = gridRect.height;
38969                    prev = x + width;
38970                }
38971                else {
38972                    x = gridRect.x;
38973                    y = prev;
38974                    width = gridRect.width;
38975                    height = tickCoord - y;
38976                    prev = y + height;
38977                }
38978
38979                var tickValue = ticksCoords[i - 1].tickValue;
38980                tickValue != null && newSplitAreaColors.set(tickValue, colorIndex);
38981
38982                this._axisGroup.add(new Rect({
38983                    anid: tickValue != null ? 'area_' + tickValue : null,
38984                    shape: {
38985                        x: x,
38986                        y: y,
38987                        width: width,
38988                        height: height
38989                    },
38990                    style: defaults({
38991                        fill: areaColors[colorIndex]
38992                    }, areaStyle),
38993                    silent: true
38994                }));
38995
38996                colorIndex = (colorIndex + 1) % areaColorsLen;
38997            }
38998
38999            this._splitAreaColors = newSplitAreaColors;
39000        }
39001    });
39002
39003    CartesianAxisView.extend({
39004        type: 'xAxis'
39005    });
39006    CartesianAxisView.extend({
39007        type: 'yAxis'
39008    });
39009
39010    /*
39011* Licensed to the Apache Software Foundation (ASF) under one
39012* or more contributor license agreements.  See the NOTICE file
39013* distributed with this work for additional information
39014* regarding copyright ownership.  The ASF licenses this file
39015* to you under the Apache License, Version 2.0 (the
39016* "License"); you may not use this file except in compliance
39017* with the License.  You may obtain a copy of the License at
39018*
39019*   http://www.apache.org/licenses/LICENSE-2.0
39020*
39021* Unless required by applicable law or agreed to in writing,
39022* software distributed under the License is distributed on an
39023* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
39024* KIND, either express or implied.  See the License for the
39025* specific language governing permissions and limitations
39026* under the License.
39027*/
39028
39029    /*
39030* Licensed to the Apache Software Foundation (ASF) under one
39031* or more contributor license agreements.  See the NOTICE file
39032* distributed with this work for additional information
39033* regarding copyright ownership.  The ASF licenses this file
39034* to you under the Apache License, Version 2.0 (the
39035* "License"); you may not use this file except in compliance
39036* with the License.  You may obtain a copy of the License at
39037*
39038*   http://www.apache.org/licenses/LICENSE-2.0
39039*
39040* Unless required by applicable law or agreed to in writing,
39041* software distributed under the License is distributed on an
39042* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
39043* KIND, either express or implied.  See the License for the
39044* specific language governing permissions and limitations
39045* under the License.
39046*/
39047
39048// Grid view
39049    extendComponentView({
39050
39051        type: 'grid',
39052
39053        render: function (gridModel, ecModel) {
39054            this.group.removeAll();
39055            if (gridModel.get('show')) {
39056                this.group.add(new Rect({
39057                    shape: gridModel.coordinateSystem.getRect(),
39058                    style: defaults({
39059                        fill: gridModel.get('backgroundColor')
39060                    }, gridModel.getItemStyle()),
39061                    silent: true,
39062                    z2: -1
39063                }));
39064            }
39065        }
39066
39067    });
39068
39069    registerPreprocessor(function (option) {
39070        // Only create grid when need
39071        if (option.xAxis && option.yAxis && !option.grid) {
39072            option.grid = {};
39073        }
39074    });
39075
39076    /*
39077* Licensed to the Apache Software Foundation (ASF) under one
39078* or more contributor license agreements.  See the NOTICE file
39079* distributed with this work for additional information
39080* regarding copyright ownership.  The ASF licenses this file
39081* to you under the Apache License, Version 2.0 (the
39082* "License"); you may not use this file except in compliance
39083* with the License.  You may obtain a copy of the License at
39084*
39085*   http://www.apache.org/licenses/LICENSE-2.0
39086*
39087* Unless required by applicable law or agreed to in writing,
39088* software distributed under the License is distributed on an
39089* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
39090* KIND, either express or implied.  See the License for the
39091* specific language governing permissions and limitations
39092* under the License.
39093*/
39094
39095// In case developer forget to include grid component
39096    registerVisual(visualSymbol('line', 'circle', 'line'));
39097    registerLayout(pointsLayout('line'));
39098
39099// Down sample after filter
39100    registerProcessor(
39101        PRIORITY.PROCESSOR.STATISTIC,
39102        dataSample('line')
39103    );
39104
39105    /*
39106* Licensed to the Apache Software Foundation (ASF) under one
39107* or more contributor license agreements.  See the NOTICE file
39108* distributed with this work for additional information
39109* regarding copyright ownership.  The ASF licenses this file
39110* to you under the Apache License, Version 2.0 (the
39111* "License"); you may not use this file except in compliance
39112* with the License.  You may obtain a copy of the License at
39113*
39114*   http://www.apache.org/licenses/LICENSE-2.0
39115*
39116* Unless required by applicable law or agreed to in writing,
39117* software distributed under the License is distributed on an
39118* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
39119* KIND, either express or implied.  See the License for the
39120* specific language governing permissions and limitations
39121* under the License.
39122*/
39123
39124    var BaseBarSeries = SeriesModel.extend({
39125
39126        type: 'series.__base_bar__',
39127
39128        getInitialData: function (option, ecModel) {
39129            return createListFromArray(this.getSource(), this);
39130        },
39131
39132        getMarkerPosition: function (value) {
39133            var coordSys = this.coordinateSystem;
39134            if (coordSys) {
39135                // PENDING if clamp ?
39136                var pt = coordSys.dataToPoint(coordSys.clampData(value));
39137                var data = this.getData();
39138                var offset = data.getLayout('offset');
39139                var size = data.getLayout('size');
39140                var offsetIndex = coordSys.getBaseAxis().isHorizontal() ? 0 : 1;
39141                pt[offsetIndex] += offset + size / 2;
39142                return pt;
39143            }
39144            return [NaN, NaN];
39145        },
39146
39147        defaultOption: {
39148            zlevel: 0,                  // 一级层å
39149            z: 2,                       // 二级层å
39150            coordinateSystem: 'cartesian2d',
39151            legendHoverLink: true,
39152            // stack: null
39153
39154            // Cartesian coordinate system
39155            // xAxisIndex: 0,
39156            // yAxisIndex: 0,
39157
39158            // 最小高度改为0
39159            barMinHeight: 0,
39160            // 最小角度为0ï¼Œä»…å¯¹æžåæ ‡ç³»ä¸‹çš„æŸ±çŠ¶å›¾æœ‰æ•ˆ
39161            barMinAngle: 0,
39162            // cursor: null,
39163
39164            large: false,
39165            largeThreshold: 400,
39166            progressive: 3e3,
39167            progressiveChunkMode: 'mod',
39168
39169            // barMaxWidth: null,
39170            // 默认自适应
39171            // barWidth: null,
39172            // 柱间距离,默认为柱形宽度的30%,可设固定值
39173            // barGap: '30%',
39174            // 类目间柱形距离,默认为类目间距的20%,可设固定值
39175            // barCategoryGap: '20%',
39176            // label: {
39177            //      show: false
39178            // },
39179            itemStyle: {},
39180            emphasis: {}
39181        }
39182    });
39183
39184    /*
39185* Licensed to the Apache Software Foundation (ASF) under one
39186* or more contributor license agreements.  See the NOTICE file
39187* distributed with this work for additional information
39188* regarding copyright ownership.  The ASF licenses this file
39189* to you under the Apache License, Version 2.0 (the
39190* "License"); you may not use this file except in compliance
39191* with the License.  You may obtain a copy of the License at
39192*
39193*   http://www.apache.org/licenses/LICENSE-2.0
39194*
39195* Unless required by applicable law or agreed to in writing,
39196* software distributed under the License is distributed on an
39197* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
39198* KIND, either express or implied.  See the License for the
39199* specific language governing permissions and limitations
39200* under the License.
39201*/
39202
39203    BaseBarSeries.extend({
39204
39205        type: 'series.bar',
39206
39207        dependencies: ['grid', 'polar'],
39208
39209        brushSelector: 'rect',
39210
39211        /**
39212         * @override
39213         */
39214        getProgressive: function () {
39215            // Do not support progressive in normal mode.
39216            return this.get('large')
39217                ? this.get('progressive')
39218                : false;
39219        },
39220
39221        /**
39222         * @override
39223         */
39224        getProgressiveThreshold: function () {
39225            // Do not support progressive in normal mode.
39226            var progressiveThreshold = this.get('progressiveThreshold');
39227            var largeThreshold = this.get('largeThreshold');
39228            if (largeThreshold > progressiveThreshold) {
39229                progressiveThreshold = largeThreshold;
39230            }
39231            return progressiveThreshold;
39232        }
39233
39234    });
39235
39236    /*
39237* Licensed to the Apache Software Foundation (ASF) under one
39238* or more contributor license agreements.  See the NOTICE file
39239* distributed with this work for additional information
39240* regarding copyright ownership.  The ASF licenses this file
39241* to you under the Apache License, Version 2.0 (the
39242* "License"); you may not use this file except in compliance
39243* with the License.  You may obtain a copy of the License at
39244*
39245*   http://www.apache.org/licenses/LICENSE-2.0
39246*
39247* Unless required by applicable law or agreed to in writing,
39248* software distributed under the License is distributed on an
39249* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
39250* KIND, either express or implied.  See the License for the
39251* specific language governing permissions and limitations
39252* under the License.
39253*/
39254
39255    function setLabel(
39256        normalStyle, hoverStyle, itemModel, color, seriesModel, dataIndex, labelPositionOutside
39257    ) {
39258        var labelModel = itemModel.getModel('label');
39259        var hoverLabelModel = itemModel.getModel('emphasis.label');
39260
39261        setLabelStyle(
39262            normalStyle, hoverStyle, labelModel, hoverLabelModel,
39263            {
39264                labelFetcher: seriesModel,
39265                labelDataIndex: dataIndex,
39266                defaultText: getDefaultLabel(seriesModel.getData(), dataIndex),
39267                isRectText: true,
39268                autoColor: color
39269            }
39270        );
39271
39272        fixPosition(normalStyle);
39273        fixPosition(hoverStyle);
39274    }
39275
39276    function fixPosition(style, labelPositionOutside) {
39277        if (style.textPosition === 'outside') {
39278            style.textPosition = labelPositionOutside;
39279        }
39280    }
39281
39282    /*
39283* Licensed to the Apache Software Foundation (ASF) under one
39284* or more contributor license agreements.  See the NOTICE file
39285* distributed with this work for additional information
39286* regarding copyright ownership.  The ASF licenses this file
39287* to you under the Apache License, Version 2.0 (the
39288* "License");
39288 you may not use this file except in compliance
39289* with the License.  You may obtain a copy of the License at
39290*
39291*   http://www.apache.org/licenses/LICENSE-2.0
39292*
39293* Unless required by applicable law or agreed to in writing,
39294* software distributed under the License is distributed on an
39295* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
39296* KIND, either express or implied.  See the License for the
39297* specific language governing permissions and limitations
39298* under the License.
39299*/
39300
39301    var getBarItemStyle = makeStyleMapper(
39302        [
39303            ['fill', 'color'],
39304            ['stroke', 'borderColor'],
39305            ['lineWidth', 'borderWidth'],
39306            // Compatitable with 2
39307            ['stroke', 'barBorderColor'],
39308            ['lineWidth', 'barBorderWidth'],
39309            ['opacity'],
39310            ['shadowBlur'],
39311            ['shadowOffsetX'],
39312            ['shadowOffsetY'],
39313            ['shadowColor']
39314        ]
39315    );
39316
39317    var barItemStyle = {
39318        getBarItemStyle: function (excludes) {
39319            var style = getBarItemStyle(this, excludes);
39320            if (this.getBorderLineDash) {
39321                var lineDash = this.getBorderLineDash();
39322                lineDash && (style.lineDash = lineDash);
39323            }
39324            return style;
39325        }
39326    };
39327
39328    /*
39329* Licensed to the Apache Software Foundation (ASF) under one
39330* or more contributor license agreements.  See the NOTICE file
39331* distributed with this work for additional information
39332* regarding copyright ownership.  The ASF licenses this file
39333* to you under the Apache License, Version 2.0 (the
39334* "License"); you may not use this file except in compliance
39335* with the License.  You may obtain a copy of the License at
39336*
39337*   http://www.apache.org/licenses/LICENSE-2.0
39338*
39339* Unless required by applicable law or agreed to in writing,
39340* software distributed under the License is distributed on an
39341* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
39342* KIND, either express or implied.  See the License for the
39343* specific language governing permissions and limitations
39344* under the License.
39345*/
39346
39347    var BAR_BORDER_WIDTH_QUERY = ['itemStyle', 'barBorderWidth'];
39348
39349// FIXME
39350// Just for compatible with ec2.
39351    extend(Model.prototype, barItemStyle);
39352
39353    extendChartView({
39354
39355        type: 'bar',
39356
39357        render: function (seriesModel, ecModel, api) {
39358            this._updateDrawMode(seriesModel);
39359
39360            var coordinateSystemType = seriesModel.get('coordinateSystem');
39361
39362            if (coordinateSystemType === 'cartesian2d'
39363                || coordinateSystemType === 'polar'
39364            ) {
39365                this._isLargeDraw
39366                    ? this._renderLarge(seriesModel, ecModel, api)
39367                    : this._renderNormal(seriesModel, ecModel, api);
39368            }
39369            else if (__DEV__) {
39370                console.warn('Only cartesian2d and polar supported for bar.');
39371            }
39372
39373            return this.group;
39374        },
39375
39376        incrementalPrepareRender: function (seriesModel, ecModel, api) {
39377            this._clear();
39378            this._updateDrawMode(seriesModel);
39379        },
39380
39381        incrementalRender: function (params, seriesModel, ecModel, api) {
39382            // Do not support progressive in normal mode.
39383            this._incrementalRenderLarge(params, seriesModel);
39384        },
39385
39386        _updateDrawMode: function (seriesModel) {
39387            var isLargeDraw = seriesModel.pipelineContext.large;
39388            if (this._isLargeDraw == null || isLargeDraw ^ this._isLargeDraw) {
39389                this._isLargeDraw = isLargeDraw;
39390                this._clear();
39391            }
39392        },
39393
39394        _renderNormal: function (seriesModel, ecModel, api) {
39395            var group = this.group;
39396            var data = seriesModel.getData();
39397            var oldData = this._data;
39398
39399            var coord = seriesModel.coordinateSystem;
39400            var baseAxis = coord.getBaseAxis();
39401            var isHorizontalOrRadial;
39402
39403            if (coord.type === 'cartesian2d') {
39404                isHorizontalOrRadial = baseAxis.isHorizontal();
39405            }
39406            else if (coord.type === 'polar') {
39407                isHorizontalOrRadial = baseAxis.dim === 'angle';
39408            }
39409
39410            var animationModel = seriesModel.isAnimationEnabled() ? seriesModel : null;
39411
39412            data.diff(oldData)
39413                .add(function (dataIndex) {
39414                    if (!data.hasValue(dataIndex)) {
39415                        return;
39416                    }
39417
39418                    var itemModel = data.getItemModel(dataIndex);
39419                    var layout = getLayout[coord.type](data, dataIndex, itemModel);
39420                    var el = elementCreator[coord.type](
39421                        data, dataIndex, itemModel, layout, isHorizontalOrRadial, animationModel
39422                    );
39423                    data.setItemGraphicEl(dataIndex, el);
39424                    group.add(el);
39425
39426                    updateStyle(
39427                        el, data, dataIndex, itemModel, layout,
39428                        seriesModel, isHorizontalOrRadial, coord.type === 'polar'
39429                    );
39430                })
39431                .update(function (newIndex, oldIndex) {
39432                    var el = oldData.getItemGraphicEl(oldIndex);
39433
39434                    if (!data.hasValue(newIndex)) {
39435                        group.remove(el);
39436                        return;
39437                    }
39438
39439                    var itemModel = data.getItemModel(newIndex);
39440                    var layout = getLayout[coord.type](data, newIndex, itemModel);
39441
39442                    if (el) {
39443                        updateProps(el, {shape: layout}, animationModel, newIndex);
39444                    }
39445                    else {
39446                        el = elementCreator[coord.type](
39447                            data, newIndex, itemModel, layout, isHorizontalOrRadial, animationModel, true
39448                        );
39449                    }
39450
39451                    data.setItemGraphicEl(newIndex, el);
39452                    // Add back
39453                    group.add(el);
39454
39455                    updateStyle(
39456                        el, data, newIndex, itemModel, layout,
39457                        seriesModel, isHorizontalOrRadial, coord.type === 'polar'
39458                    );
39459                })
39460                .remove(function (dataIndex) {
39461                    var el = oldData.getItemGraphicEl(dataIndex);
39462                    if (coord.type === 'cartesian2d') {
39463                        el && removeRect(dataIndex, animationModel, el);
39464                    }
39465                    else {
39466                        el && removeSector(dataIndex, animationModel, el);
39467                    }
39468                })
39469                .execute();
39470
39471            this._data = data;
39472        },
39473
39474        _renderLarge: function (seriesModel, ecModel, api) {
39475            this._clear();
39476            createLarge(seriesModel, this.group);
39477        },
39478
39479        _incrementalRenderLarge: function (params, seriesModel) {
39480            createLarge(seriesModel, this.group, true);
39481        },
39482
39483        dispose: noop,
39484
39485        remove: function (ecModel) {
39486            this._clear(ecModel);
39487        },
39488
39489        _clear: function (ecModel) {
39490            var group = this.group;
39491            var data = this._data;
39492            if (ecModel && ecModel.get('animation') && data && !this._isLargeDraw) {
39493                data.eachItemGraphicEl(function (el) {
39494                    if (el.type === 'sector') {
39495                        removeSector(el.dataIndex, ecModel, el);
39496                    }
39497                    else {
39498                        removeRect(el.dataIndex, ecModel, el);
39499                    }
39500                });
39501            }
39502            else {
39503                group.removeAll();
39504            }
39505            this._data = null;
39506        }
39507
39508    });
39509
39510    var elementCreator = {
39511
39512        cartesian2d: function (
39513            data, dataIndex, itemModel, layout, isHorizontal,
39514            animationModel, isUpdate
39515        ) {
39516            var rect = new Rect({shape: extend({}, layout)});
39517
39518            // Animation
39519            if (animationModel) {
39520                var rectShape = rect.shape;
39521                var animateProperty = isHorizontal ? 'height' : 'width';
39522                var animateTarget = {};
39523                rectShape[animateProperty] = 0;
39524                animateTarget[animateProperty] = layout[animateProperty];
39525                graphic[isUpdate ? 'updateProps' : 'initProps'](rect, {
39526                    shape: animateTarget
39527                }, animationModel, dataIndex);
39528            }
39529
39530            return rect;
39531        },
39532
39533        polar: function (
39534            data, dataIndex, itemModel, layout, isRadial,
39535            animationModel, isUpdate
39536        ) {
39537            // Keep the same logic with bar in catesion: use end value to control
39538            // direction. Notice that if clockwise is true (by default), the sector
39539            // will always draw clockwisely, no matter whether endAngle is greater
39540            // or less than startAngle.
39541            var clockwise = layout.startAngle < layout.endAngle;
39542            var sector = new Sector({
39543                shape: defaults({clockwise: clockwise}, layout)
39544            });
39545
39546            // Animation
39547            if (animationModel) {
39548                var sectorShape = sector.shape;
39549                var animateProperty = isRadial ? 'r' : 'endAngle';
39550                var animateTarget = {};
39551                sectorShape[animateProperty] = isRadial ? 0 : layout.startAngle;
39552                animateTarget[animateProperty] = layout[animateProperty];
39553                graphic[isUpdate ? 'updateProps' : 'initProps'](sector, {
39554                    shape: animateTarget
39555                }, animationModel, dataIndex);
39556            }
39557
39558            return sector;
39559        }
39560    };
39561
39562    function removeRect(dataIndex, animationModel, el) {
39563        // Not show text when animating
39564        el.style.text = null;
39565        updateProps(el, {
39566            shape: {
39567                width: 0
39568            }
39569        }, animationModel, dataIndex, function () {
39570            el.parent && el.parent.remove(el);
39571        });
39572    }
39573
39574    function removeSector(dataIndex, animationModel, el) {
39575        // Not show text when animating
39576        el.style.text = null;
39577        updateProps(el, {
39578            shape: {
39579                r: el.shape.r0
39580            }
39581        }, animationModel, dataIndex, function () {
39582            el.parent && el.parent.remove(el);
39583        });
39584    }
39585
39586    var getLayout = {
39587        cartesian2d: function (data, dataIndex, itemModel) {
39588            var layout = data.getItemLayout(dataIndex);
39589            var fixedLineWidth = getLineWidth(itemModel, layout);
39590
39591            // fix layout with lineWidth
39592            var signX = layout.width > 0 ? 1 : -1;
39593            var signY = layout.height > 0 ? 1 : -1;
39594            return {
39595                x: layout.x + signX * fixedLineWidth / 2,
39596                y: layout.y + signY * fixedLineWidth / 2,
39597                width: layout.width - signX * fixedLineWidth,
39598                height: layout.height - signY * fixedLineWidth
39599            };
39600        },
39601
39602        polar: function (data, dataIndex, itemModel) {
39603            var layout = data.getItemLayout(dataIndex);
39604            return {
39605                cx: layout.cx,
39606                cy: layout.cy,
39607                r0: layout.r0,
39608                r: layout.r,
39609                startAngle: layout.startAngle,
39610                endAngle: layout.endAngle
39611            };
39612        }
39613    };
39614
39615    function updateStyle(
39616        el, data, dataIndex, itemModel, layout, seriesModel, isHorizontal, isPolar
39617    ) {
39618        var color = data.getItemVisual(dataIndex, 'color');
39619        var opacity = data.getItemVisual(dataIndex, 'opacity');
39620        var itemStyleModel = itemModel.getModel('itemStyle');
39621        var hoverStyle = itemModel.getModel('emphasis.itemStyle').getBarItemStyle();
39622
39623        if (!isPolar) {
39624            el.setShape('r', itemStyleModel.get('barBorderRadius') || 0);
39625        }
39626
39627        el.useStyle(defaults(
39628            {
39629                fill: color,
39630                opacity: opacity
39631            },
39632            itemStyleModel.getBarItemStyle()
39633        ));
39634
39635        var cursorStyle = itemModel.getShallow('cursor');
39636        cursorStyle && el.attr('cursor', cursorStyle);
39637
39638        var labelPositionOutside = isHorizontal
39639            ? (layout.height > 0 ? 'bottom' : 'top')
39640            : (layout.width > 0 ? 'left' : 'right');
39641
39642        if (!isPolar) {
39643            setLabel(
39644                el.style, hoverStyle, itemModel, color,
39645                seriesModel, dataIndex, labelPositionOutside
39646            );
39647        }
39648
39649        setHoverStyle(el, hoverStyle);
39650    }
39651
39652// In case width or height are too small.
39653    function getLineWidth(itemModel, rawLayout) {
39654        var lineWidth = itemModel.get(BAR_BORDER_WIDTH_QUERY) || 0;
39655        return Math.min(lineWidth, Math.abs(rawLayout.width), Math.abs(rawLayout.height));
39656    }
39657
39658
39659    var LargePath = Path.extend({
39660
39661        type: 'largeBar',
39662
39663        shape: {points: []},
39664
39665        buildPath: function (ctx, shape) {
39666            // Drawing lines is more efficient than drawing
39667            // a whole line or drawing rects.
39668            var points = shape.points;
39669            var startPoint = this.__startPoint;
39670            var valueIdx = this.__valueIdx;
39671
39672            for (var i = 0; i < points.length; i += 2) {
39673                startPoint[this.__valueIdx] = points[i + valueIdx];
39674                ctx.moveTo(startPoint[0], startPoint[1]);
39675                ctx.lineTo(points[i], points[i + 1]);
39676            }
39677        }
39678    });
39679
39680    function createLarge(seriesModel, group, incremental) {
39681        // TODO support polar
39682        var data = seriesModel.getData();
39683        var startPoint = [];
39684        var valueIdx = data.getLayout('valueAxisHorizontal') ? 1 : 0;
39685        startPoint[1 - valueIdx] = data.getLayout('valueAxisStart');
39686
39687        var el = new LargePath({
39688            shape: {points: data.getLayout('largePoints')},
39689            incremental: !!incremental,
39690            __startPoint: startPoint,
39691            __valueIdx: valueIdx
39692        });
39693        group.add(el);
39694        setLargeStyle(el, seriesModel, data);
39695    }
39696
39697    function setLargeStyle(el, seriesModel, data) {
39698        var borderColor = data.getVisual('borderColor') || data.getVisual('color');
39699        var itemStyle = seriesModel.getModel('itemStyle').getItemStyle(['color', 'borderColor']);
39700
39701        el.useStyle(itemStyle);
39702        el.style.fill = null;
39703        el.style.stroke = borderColor;
39704        el.style.lineWidth = data.getLayout('barWidth');
39705    }
39706
39707    /*
39708* Licensed to the Apache Software Foundation (ASF) under one
39709* or more contributor license agreements.  See the NOTICE file
39710* distributed with this work for additional information
39711* regarding copyright ownership.  The ASF licenses this file
39712* to you under the Apache License, Version 2.0 (the
39713* "License");
39713 you may not use this file except in compliance
39714* with the License.  You may obtain a copy of the License at
39715*
39716*   http://www.apache.org/licenses/LICENSE-2.0
39717*
39718* Unless required by applicable law or agreed to in writing,
39719* software distributed under the License is distributed on an
39720* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
39721* KIND, either express or implied.  See the License for the
39722* specific language governing permissions and limitations
39723* under the License.
39724*/
39725
39726// In case developer forget to include grid component
39727    registerLayout(curry(layout, 'bar'));
39728// Should after normal bar layout, otherwise it is blocked by normal bar layout.
39729    registerLayout(largeLayout);
39730
39731    registerVisual({
39732        seriesType: 'bar',
39733        reset: function (seriesModel) {
39734            // Visual coding for legend
39735            seriesModel.getData().setVisual('legendSymbol', 'roundRect');
39736        }
39737    });
39738
39739    /*
39740* Licensed to the Apache Software Foundation (ASF) under one
39741* or more contributor license agreements.  See the NOTICE file
39742* distributed with this work for additional information
39743* regarding copyright ownership.  The ASF licenses this file
39744* to you under the Apache License, Version 2.0 (the
39745* "License"); you may not use this file except in compliance
39746* with the License.  You may obtain a copy of the License at
39747*
39748*   http://www.apache.org/licenses/LICENSE-2.0
39749*
39750* Unless required by applicable law or agreed to in writing,
39751* software distributed under the License is distributed on an
39752* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
39753* KIND, either express or implied.  See the License for the
39754* specific language governing permissions and limitations
39755* under the License.
39756*/
39757
39758
39759    /**
39760     * [Usage]:
39761     * (1)
39762     * createListSimply(seriesModel, ['value']);
39763     * (2)
39764     * createListSimply(seriesModel, {
39765 *     coordDimensions: ['value'],
39766 *     dimensionsCount: 5
39767 * });
39768     *
39769     * @param {module:echarts/model/Series} seriesModel
39770     * @param {Object|Array.<string|Object>} opt opt or coordDimensions
39771     *        The options in opt, see `echarts/data/helper/createDimensions`
39772     * @param {Array.<string>} [nameList]
39773     * @return {module:echarts/data/List}
39774     */
39775    var createListSimply = function (seriesModel, opt, nameList) {
39776        opt = isArray(opt) && {coordDimensions: opt} || extend({}, opt);
39777
39778        var source = seriesModel.getSource();
39779
39780        var dimensionsInfo = createDimensions(source, opt);
39781
39782        var list = new List(dimensionsInfo, seriesModel);
39783        list.initData(source, nameList);
39784
39785        return list;
39786    };
39787
39788    /*
39789* Licensed to the Apache Software Foundation (ASF) under one
39790* or more contributor license agreements.  See the NOTICE file
39791* distributed with this work for additional information
39792* regarding copyright ownership.  The ASF licenses this file
39793* to you under the Apache License, Version 2.0 (the
39794* "License"); you may not use this file except in compliance
39795* with the License.  You may obtain a copy of the License at
39796*
39797*   http://www.apache.org/licenses/LICENSE-2.0
39798*
39799* Unless required by applicable law or agreed to in writing,
39800* software distributed under the License is distributed on an
39801* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
39802* KIND, either express or implied.  See the License for the
39803* specific language governing permissions and limitations
39804* under the License.
39805*/
39806
39807    /**
39808     * Data selectable mixin for chart series.
39809     * To eanble data select, option of series must have `selectedMode`.
39810     * And each data item will use `selected` to toggle itself selected status
39811     */
39812
39813    var selectableMixin = {
39814
39815        /**
39816         * @param {Array.<Object>} targetList [{name, value, selected}, ...]
39817         *        If targetList is an array, it should like [{name: ..., value: ...}, ...].
39818         *        If targetList is a "List", it must have coordDim: 'value' dimension and name.
39819         */
39820        updateSelectedMap: function (targetList) {
39821            this._targetList = isArray(targetList) ? targetList.slice() : [];
39822
39823            this._selectTargetMap = reduce(targetList || [], function (targetMap, target) {
39824                targetMap.set(target.name, target);
39825                return targetMap;
39826            }, createHashMap());
39827        },
39828
39829        /**
39830         * Either name or id should be passed as input here.
39831         * If both of them are defined, id is used.
39832         *
39833         * @param {string|undefined} name name of data
39834         * @param {number|undefined} id dataIndex of data
39835         */
39836        // PENGING If selectedMode is null ?
39837        select: function (name, id) {
39838            var target = id != null
39839                ? this._targetList[id]
39840                : this._selectTargetMap.get(name);
39841            var selectedMode = this.get('selectedMode');
39842            if (selectedMode === 'single') {
39843                this._selectTargetMap.each(function (target) {
39844                    target.selected = false;
39845                });
39846            }
39847            target && (target.selected = true);
39848        },
39849
39850        /**
39851         * Either name or id should be passed as input here.
39852         * If both of them are defined, id is used.
39853         *
39854         * @param {string|undefined} name name of data
39855         * @param {number|undefined} id dataIndex of data
39856         */
39857        unSelect: function (name, id) {
39858            var target = id != null
39859                ? this._targetList[id]
39860                : this._selectTargetMap.get(name);
39861            // var selectedMode = this.get('selectedMode');
39862            // selectedMode !== 'single' && target && (target.selected = false);
39863            target && (target.selected = false);
39864        },
39865
39866        /**
39867         * Either name or id should be passed as input here.
39868         * If both of them are defined, id is used.
39869         *
39870         * @param {string|undefined} name name of data
39871         * @param {number|undefined} id dataIndex of data
39872         */
39873        toggleSelected: function (name, id) {
39874            var target = id != null
39875                ? this._targetList[id]
39876                : this._selectTargetMap.get(name);
39877            if (target != null) {
39878                this[target.selected ? 'unSelect' : 'select'](name, id);
39879                return target.selected;
39880            }
39881        },
39882
39883        /**
39884         * Either name or id should be passed as input here.
39885         * If both of them are defined, id is used.
39886         *
39887         * @param {string|undefined} name name of data
39888         * @param {number|undefined} id dataIndex of data
39889         */
39890        isSelected: function (name, id) {
39891            var target = id != null
39892                ? this._targetList[id]
39893                : this._selectTargetMap.get(name);
39894            return target && target.selected;
39895        }
39896    };
39897
39898    /*
39899* Licensed to the Apache Software Foundation (ASF) under one
39900* or more contributor license agreements.  See the NOTICE file
39901* distributed with this work for additional information
39902* regarding copyright ownership.  The ASF licenses this file
39903* to you under the Apache License, Version 2.0 (the
39904* "License"); you may not use this file except in compliance
39905* with the License.  You may obtain a copy of the License at
39906*
39907*   http://www.apache.org/licenses/LICENSE-2.0
39908*
39909* Unless required by applicable law or agreed to in writing,
39910* software distributed under the License is distributed on an
39911* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
39912* KIND, either express or implied.  See the License for the
39913* specific language governing permissions and limitations
39914* under the License.
39915*/
39916
39917    var PieSeries = extendSeriesModel({
39918
39919        type: 'series.pie',
39920
39921        // Overwrite
39922        init: function (option) {
39923            PieSeries.superApply(this, 'init', arguments);
39924
39925            // Enable legend selection for each data item
39926            // Use a function instead of direct access because data reference may changed
39927            this.legendDataProvider = function () {
39928                return this.getRawData();
39929            };
39930
39931            this.updateSelectedMap(this._createSelectableList());
39932
39933            this._defaultLabelLine(option);
39934        },
39935
39936        // Overwrite
39937        mergeOption: function (newOption) {
39938            PieSeries.superCall(this, 'mergeOption', newOption);
39939
39940            this.updateSelectedMap(this._createSelectableList());
39941        },
39942
39943        getInitialData: function (option, ecModel) {
39944            return createListSimply(this, ['value']);
39945        },
39946
39947        _createSelectableList: function () {
39948            var data = this.getRawData();
39949            var valueDim = data.mapDimension('value');
39950            var targetList = [];
39951            for (var i = 0, len = data.count(); i < len; i++) {
39952                targetList.push({
39953                    name: data.getName(i),
39954                    value: data.get(valueDim, i),
39955                    selected: retrieveRawAttr(data, i, 'selected')
39956                });
39957            }
39958            return targetList;
39959        },
39960
39961        // Overwrite
39962        getDataParams: function (dataIndex) {
39963            var data = this.getData();
39964            var params = PieSeries.superCall(this, 'getDataParams', dataIndex);
39965            // FIXME toFixed?
39966
39967            var valueList = [];
39968            data.each(data.mapDimension('value'), function (value) {
39969                valueList.push(value);
39970            });
39971
39972            params.percent = getPercentWithPrecision(
39973                valueList,
39974                dataIndex,
39975                data.hostModel.get('percentPrecision')
39976            );
39977
39978            params.$vars.push('percent');
39979            return params;
39980        },
39981
39982        _defaultLabelLine: function (option) {
39983            // Extend labelLine emphasis
39984            defaultEmphasis(option, 'labelLine', ['show']);
39985
39986            var labelLineNormalOpt = option.labelLine;
39987            var labelLineEmphasisOpt = option.emphasis.labelLine;
39988            // Not show label line if `label.normal.show = false`
39989            labelLineNormalOpt.show = labelLineNormalOpt.show
39990                && option.label.show;
39991            labelLineEmphasisOpt.show = labelLineEmphasisOpt.show
39992                && option.emphasis.label.show;
39993        },
39994
39995        defaultOption: {
39996            zlevel: 0,
39997            z: 2,
39998            legendHoverLink: true,
39999
40000            hoverAnimation: true,
40001            // 默认全局居中
40002            center: ['50%', '50%'],
40003            radius: [0, '75%'],
40004            // 默认顺时针
40005            clockwise: true,
40006            startAngle: 90,
40007            // 最小角度改为0
40008            minAngle: 0,
40009            // 选中时扇区偏移量
40010            selectedOffset: 10,
40011            // 高亮扇区偏移量
40012            hoverOffset: 10,
40013
40014            // If use strategy to avoid label overlapping
40015            avoidLabelOverlap: true,
40016            // 选择模式,默认关闭,可选single,multiple
40017            // selectedMode: false,
40018            // å—ä¸æ ¼å°”çŽ«ç‘°å›¾æ¨¡å¼ï¼Œ'radius'(半径) | 'area'(面积)
40019            // roseType: null,
40020
40021            percentPrecision: 2,
40022
40023            // If still show when all data zero.
40024            stillShowZeroSum: true,
40025
40026            // cursor: null,
40027
40028            label: {
40029                // If rotate around circle
40030                rotate: false,
40031                show: true,
40032                // 'outer', 'inside', 'center'
40033                position: 'outer'
40034                // formatter: æ ‡ç­¾æ–‡æœ¬æ ¼å¼å™¨ï¼ŒåŒTooltip.formatter,不支持异步回调
40035                // é»˜è®¤ä½¿ç”¨å…¨å±€æ–‡æœ¬æ ·å¼ï¼Œè¯¦è§TEXTSTYLE
40036                // distance: 当position为inner时有效,为label位置到圆心的距离与圆半径(环状图为内外半径和)的比例系数
40037            },
40038            // Enabled when label.normal.position is 'outer'
40039            labelLine: {
40040                show: true,
40041                // 引导线两段中的第一段长度
40042                length: 15,
40043                // 引导线两段中的第二段长度
40044                length2: 15,
40045                smooth: false,
40046                lineStyle: {
40047                    // color: 各异,
40048                    width: 1,
40049                    type: 'solid'
40050                }
40051            },
40052            itemStyle: {
40053                borderWidth: 1
40054            },
40055
40056            // Animation type canbe expansion, scale
40057            animationType: 'expansion',
40058
40059            animationEasing: 'cubicOut'
40060        }
40061    });
40062
40063    mixin(PieSeries, selectableMixin);
40064
40065    /*
40066* Licensed to the Apache Software Foundation (ASF) under one
40067* or more contributor license agreements.  See the NOTICE file
40068* distributed with this work for additional information
40069* regarding copyright ownership.  The ASF licenses this file
40070* to you under the Apache License, Version 2.0 (the
40071* "License"); you may not use this file except in compliance
40072* with the License.  You may obtain a copy of the License at
40073*
40074*   http://www.apache.org/licenses/LICENSE-2.0
40075*
40076* Unless required by applicable law or agreed to in writing,
40077* software distributed under the License is distributed on an
40078* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
40079* KIND, either express or implied.  See the License for the
40080* specific language governing permissions and limitations
40081* under the License.
40082*/
40083
40084    /**
40085     * @param {module:echarts/model/Series} seriesModel
40086     * @param {boolean} hasAnimation
40087     * @inner
40088     */
40089    function updateDataSelected(uid, seriesModel, hasAnimation, api) {
40090        var data = seriesModel.getData();
40091        var dataIndex = this.dataIndex;
40092        var name = data.getName(dataIndex);
40093        var selectedOffset = seriesModel.get('selectedOffset');
40094
40095        api.dispatchAction({
40096            type: 'pieToggleSelect',
40097            from: uid,
40098            name: name,
40099            seriesId: seriesModel.id
40100        });
40101
40102        data.each(function (idx) {
40103            toggleItemSelected(
40104                data.getItemGraphicEl(idx),
40105                data.getItemLayout(idx),
40106                seriesModel.isSelected(data.getName(idx)),
40107                selectedOffset,
40108                hasAnimation
40109            );
40110        });
40111    }
40112
40113    /**
40114     * @param {module:zrender/graphic/Sector} el
40115     * @param {Object} layout
40116     * @param {boolean} isSelected
40117     * @param {number} selectedOffset
40118     * @param {boolean} hasAnimation
40119     * @inner
40120     */
40121    function toggleItemSelected(el, layout, isSelected, selectedOffset, hasAnimation) {
40122        var midAngle = (layout.startAngle + layout.endAngle) / 2;
40123
40124        var dx = Math.cos(midAngle);
40125        var dy = Math.sin(midAngle);
40126
40127        var offset = isSelected ? selectedOffset : 0;
40128        var position = [dx * offset, dy * offset];
40129
40130        hasAnimation
40131            // animateTo will stop revious animation like update transition
40132            ? el.animate()
40133                .when(200, {
40134                    position: position
40135                })
40136                .start('bounceOut')
40137            : el.attr('position', position);
40138    }
40139
40140    /**
40141     * Piece of pie including Sector, Label, LabelLine
40142     * @constructor
40143     * @extends {module:zrender/graphic/Group}
40144     */
40145    function PiePiece(data, idx) {
40146
40147        Group.call(this);
40148
40149        var sector = new Sector({
40150            z2: 2
40151        });
40152        var polyline = new Polyline();
40153        var text = new Text();
40154        this.add(sector);
40155        this.add(polyline);
40156        this.add(text);
40157
40158        this.updateData(data, idx, true);
40159
40160        // Hover to change label and labelLine
40161        function onEmphasis() {
40162            polyline.ignore = polyline.hoverIgnore;
40163            text.ignore = text.hoverIgnore;
40164        }
40165        function onNormal() {
40166            polyline.ignore = polyline.normalIgnore;
40167            text.ignore = text.normalIgnore;
40168        }
40169        this.on('emphasis', onEmphasis)
40170            .on('normal', onNormal)
40171            .on('mouseover', onEmphasis)
40172            .on('mouseout', onNormal);
40173    }
40174
40175    var piePieceProto = PiePiece.prototype;
40176
40177    piePieceProto.updateData = function (data, idx, firstCreate) {
40178
40179        var sector = this.childAt(0);
40180
40181        var seriesModel = data.hostModel;
40182        var itemModel = data.getItemModel(idx);
40183        var layout = data.getItemLayout(idx);
40184        var sectorShape = extend({}, layout);
40185        sectorShape.label = null;
40186
40187        if (firstCreate) {
40188            sector.setShape(sectorShape);
40189
40190            var animationType = seriesModel.getShallow('animationType');
40191            if (animationType === 'scale') {
40192                sector.shape.r = layout.r0;
40193                initProps(sector, {
40194                    shape: {
40195                        r: layout.r
40196                    }
40197                }, seriesModel, idx);
40198            }
40199            // Expansion
40200            else {
40201                sector.shape.endAngle = layout.startAngle;
40202                updateProps(sector, {
40203                    shape: {
40204                        endAngle: layout.endAngle
40205                    }
40206                }, seriesModel, idx);
40207            }
40208
40209        }
40210        else {
40211            updateProps(sector, {
40212                shape: sectorShape
40213            }, seriesModel, idx);
40214        }
40215
40216        // Update common style
40217        var visualColor = data.getItemVisual(idx, 'color');
40218
40219        sector.useStyle(
40220            defaults(
40221                {
40222                    lineJoin: 'bevel',
40223                    fill: visualColor
40224                },
40225                itemModel.getModel('itemStyle').getItemStyle()
40226            )
40227        );
40228        sector.hoverStyle = itemModel.getModel('emphasis.itemStyle').getItemStyle();
40229
40230        var cursorStyle = itemModel.getShallow('cursor');
40231        cursorStyle && sector.attr('cursor', cursorStyle);
40232
40233        // Toggle selected
40234        toggleItemSelected(
40235            this,
40236            data.getItemLayout(idx),
40237            seriesModel.isSelected(null, idx),
40238            seriesModel.get('selectedOffset'),
40239            seriesModel.get('animation')
40240        );
40241
40242        function onEmphasis() {
40243            // Sector may has animation of updating data. Force to move to the last frame
40244            // Or it may stopped on the wrong shape
40245            sector.stopAnimation(true);
40246            sector.animateTo({
40247                shape: {
40248                    r: layout.r + seriesModel.get('hoverOffset')
40249                }
40250            }, 300, 'elasticOut');
40251        }
40252        function onNormal() {
40253            sector.stopAnimation(true);
40254            sector.animateTo({
40255                shape: {
40256                    r: layout.r
40257                }
40258            }, 300, 'elasticOut');
40259        }
40260        sector.off('mouseover').off('mouseout').off('emphasis').off('normal');
40261        if (itemModel.get('hoverAnimation') && seriesModel.isAnimationEnabled()) {
40262            sector
40263                .on('mouseover', onEmphasis)
40264                .on('mouseout', onNormal)
40265                .on('emphasis', onEmphasis)
40266                .on('normal', onNormal);
40267        }
40268
40269        this._updateLabel(data, idx);
40270
40271        setHoverStyle(this);
40272    };
40273
40274    piePieceProto._updateLabel = function (data, idx) {
40275
40276        var labelLine = this.childAt(1);
40277        var labelText = this.childAt(2);
40278
40279        var seriesModel = data.hostModel;
40280        var itemModel = data.getItemModel(idx);
40281        var layout = data.getItemLayout(idx);
40282        var labelLayout = layout.label;
40283        var visualColor = data.getItemVisual(idx, 'color');
40284
40285        updateProps(labelLine, {
40286            shape: {
40287                points: labelLayout.linePoints || [
40288                    [labelLayout.x, labelLayout.y], [labelLayout.x, labelLayout.y], [labelLayout.x, labelLayout.y]
40289                ]
40290            }
40291        }, seriesModel, idx);
40292
40293        updateProps(labelText, {
40294            style: {
40295                x: labelLayout.x,
40296                y: labelLayout.y
40297            }
40298        }, seriesModel, idx);
40299        labelText.attr({
40300            rotation: labelLayout.rotation,
40301            origin: [labelLayout.x, labelLayout.y],
40302            z2: 10
40303        });
40304
40305        var labelModel = itemModel.getModel('label');
40306        var labelHoverModel = itemModel.getModel('emphasis.label');
40307        var labelLineModel = itemModel.getModel('labelLine');
40308        var labelLineHoverModel = itemModel.getModel('emphasis.labelLine');
40309        var visualColor = data.getItemVisual(idx, 'color');
40310
40311        setLabelStyle(
40312            labelText.style, labelText.hoverStyle = {}, labelModel, labelHoverModel,
40313            {
40314                labelFetcher: data.hostModel,
40315                labelDataIndex: idx,
40316                defaultText: data.getName(idx),
40317                autoColor: visualColor,
40318                useInsideStyle: !!labelLayout.inside
40319            },
40320            {
40321                textAlign: labelLayout.textAlign,
40322                textVerticalAlign: labelLayout.verticalAlign,
40323                opacity: data.getItemVisual(idx, 'opacity')
40324            }
40325        );
40326
40327        labelText.ignore = labelText.normalIgnore = !labelModel.get('show');
40328        labelText.hoverIgnore = !labelHoverModel.get('show');
40329
40330        labelLine.ignore = labelLine.normalIgnore = !labelLineModel.get('show');
40331        labelLine.hoverIgnore = !labelLineHoverModel.get('show');
40332
40333        // Default use item visual color
40334        labelLine.setStyle({
40335            stroke: visualColor,
40336            opacity: data.getItemVisual(idx, 'opacity')
40337        });
40338        labelLine.setStyle(labelLineModel.getModel('lineStyle').getLineStyle());
40339
40340        labelLine.hoverStyle = labelLineHoverModel.getModel('lineStyle').getLineStyle();
40341
40342        var smooth = labelLineModel.get('smooth');
40343        if (smooth && smooth === true) {
40344            smooth = 0.4;
40345        }
40346        labelLine.setShape({
40347            smooth: smooth
40348        });
40349    };
40350
40351    inherits(PiePiece, Group);
40352
40353
40354// Pie view
40355    var PieView = Chart.extend({
40356
40357        type: 'pie',
40358
40359        init: function () {
40360            var sectorGroup = new Group();
40361            this._sectorGroup = sectorGroup;
40362        },
40363
40364        render: function (seriesModel, ecModel, api, payload) {
40365            if (payload && (payload.from === this.uid)) {
40366                return;
40367            }
40368
40369            var data = seriesModel.getData();
40370            var oldData = this._data;
40371            var group = this.group;
40372
40373            var hasAnimation = ecModel.get('animation');
40374            var isFirstRender = !oldData;
40375            var animationType = seriesModel.get('animationType');
40376
40377            var onSectorClick = curry(
40378                updateDataSelected, this.uid, seriesModel, hasAnimation, api
40379            );
40380
40381            var selectedMode = seriesModel.get('selectedMode');
40382
40383            data.diff(oldData)
40384                .add(function (idx) {
40385                    var piePiece = new PiePiece(data, idx);
40386                    // Default expansion animation
40387                    if (isFirstRender && animationType !== 'scale') {
40388                        piePiece.eachChild(function (child) {
40389                            child.stopAnimation(true);
40390                        });
40391                    }
40392
40393                    selectedMode && piePiece.on('click', onSectorClick);
40394
40395                    data.setItemGraphicEl(idx, piePiece);
40396
40397                    group.add(piePiece);
40398                })
40399                .update(function (newIdx, oldIdx) {
40400                    var piePiece = oldData.getItemGraphicEl(oldIdx);
40401
40402                    piePiece.updateData(data, newIdx);
40403
40404                    piePiece.off('click');
40405                    selectedMode && piePiece.on('click', onSectorClick);
40406                    group.add(piePiece);
40407                    data.setItemGraphicEl(newIdx, piePiece);
40408                })
40409                .remove(function (idx) {
40410                    var piePiece = oldData.getItemGraphicEl(idx);
40411                    group.remove(piePiece);
40412                })
40413                .execute();
40414
40415            if (
40416                hasAnimation && isFirstRender && data.count() > 0
40417                // Default expansion animation
40418                && animationType !== 'scale'
40419            ) {
40420                var shape = data.getItemLayout(0);
40421                var r = Math.max(api.getWidth(), api.getHeight()) / 2;
40422
40423                var removeClipPath = bind(group.removeClipPath, group);
40424                group.setClipPath(this._createClipPath(
40425                    shape.cx, shape.cy, r, shape.startAngle, shape.clockwise, removeClipPath, seriesModel
40426                ));
40427            }
40428
40429            this._data = data;
40430        },
40431
40432        dispose: function () {},
40433
40434        _createClipPath: function (
40435            cx, cy, r, startAngle, clockwise, cb, seriesModel
40436        ) {
40437            var clipPath = new Sector({
40438                shape: {
40439                    cx: cx,
40440                    cy: cy,
40441                    r0: 0,
40442                    r: r,
40443                    startAngle: startAngle,
40444                    endAngle: startAngle,
40445                    clockwise: clockwise
40446                }
40447            });
40448
40449            initProps(clipPath, {
40450                shape: {
40451                    endAngle: startAngle + (clockwise ? 1 : -1) * Math.PI * 2
40452                }
40453            }, seriesModel, cb);
40454
40455            return clipPath;
40456        },
40457
40458        /**
40459         * @implement
40460         */
40461        containPoint: function (point, seriesModel) {
40462            var data = seriesModel.getData();
40463            var itemLayout = data.getItemLayout(0);
40464            if (itemLayout) {
40465                var dx = point[0] - itemLayout.cx;
40466                var dy = point[1] - itemLayout.cy;
40467                var radius = Math.sqrt(dx * dx + dy * dy);
40468                return radius <= itemLayout.r && radius >= itemLayout.r0;
40469            }
40470        }
40471
40472    });
40473
40474    /*
40475* Licensed to the Apache Software Foundation (ASF) under one
40476* or more contributor license agreements.  See the NOTICE file
40477* distributed with this work for additional information
40478* regarding copyright ownership.  The ASF licenses this file
40479* to you under the Apache License, Version 2.0 (the
40480* "License"); you may not use this file except in compliance
40481* with the License.  You may obtain a copy of the License at
40482*
40483*   http://www.apache.org/licenses/LICENSE-2.0
40484*
40485* Unless required by applicable law or agreed to in writing,
40486* software distributed under the License is distributed on an
40487* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
40488* KIND, either express or implied.  See the License for the
40489* specific language governing permissions and limitations
40490* under the License.
40491*/
40492
40493    var createDataSelectAction = function (seriesType, actionInfos) {
40494        each$1(actionInfos, function (actionInfo) {
40495            actionInfo.update = 'updateView';
40496            /**
40497             * @payload
40498             * @property {string} seriesName
40499             * @property {string} name
40500             */
40501            registerAction(actionInfo, function (payload, ecModel) {
40502                var selected = {};
40503                ecModel.eachComponent(
40504                    {mainType: 'series', subType: seriesType, query: payload},
40505                    function (seriesModel) {
40506                        if (seriesModel[actionInfo.method]) {
40507                            seriesModel[actionInfo.method](
40508                                payload.name,
40509                                payload.dataIndex
40510                            );
40511                        }
40512                        var data = seriesModel.getData();
40513                        // Create selected map
40514                        data.each(function (idx) {
40515                            var name = data.getName(idx);
40516                            selected[name] = seriesModel.isSelected(name)
40517                                || false;
40518                        });
40519                    }
40520                );
40521                return {
40522                    name: payload.name,
40523                    selected: selected
40524                };
40525            });
40526        });
40527    };
40528
40529    /*
40530* Licensed to the Apache Software Foundation (ASF) under one
40531* or more contributor license agreements.  See the NOTICE file
40532* distributed with this work for additional information
40533* regarding copyright ownership.  The ASF licenses this file
40534* to you under the Apache License, Version 2.0 (the
40535* "License"); you may not use this file except in compliance
40536* with the License.  You may obtain a copy of the License at
40537*
40538*   http://www.apache.org/licenses/LICENSE-2.0
40539*
40540* Unless required by applicable law or agreed to in writing,
40541* software distributed under the License is distributed on an
40542* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
40543* KIND, either express or implied.  See the License for the
40544* specific language governing permissions and limitations
40545* under the License.
40546*/
40547
40548// Pick color from palette for each data item.
40549// Applicable for charts that require applying color palette
40550// in data level (like pie, funnel, chord).
40551    var dataColor = function (seriesType) {
40552        return {
40553            getTargetSeries: function (ecModel) {
40554                // Pie and funnel may use diferrent scope
40555                var paletteScope = {};
40556                var seiresModelMap = createHashMap();
40557
40558                ecModel.eachSeriesByType(seriesType, function (seriesModel) {
40559                    seriesModel.__paletteScope = paletteScope;
40560                    seiresModelMap.set(seriesModel.uid, seriesModel);
40561                });
40562
40563                return seiresModelMap;
40564            },
40565            reset: function (seriesModel, ecModel) {
40566                var dataAll = seriesModel.getRawData();
40567                var idxMap = {};
40568                var data = seriesModel.getData();
40569
40570                data.each(function (idx) {
40571                    var rawIdx = data.getRawIndex(idx);
40572                    idxMap[rawIdx] = idx;
40573                });
40574
40575                dataAll.each(function (rawIdx) {
40576                    var filteredIdx = idxMap[rawIdx];
40577
40578                    // If series.itemStyle.normal.color is a function. itemVisual may be encoded
40579                    var singleDataColor = filteredIdx != null
40580                        && data.getItemVisual(filteredIdx, 'color', true);
40581
40582                    if (!singleDataColor) {
40583                        // FIXME Performance
40584                        var itemModel = dataAll.getItemModel(rawIdx);
40585
40586                        var color = itemModel.get('itemStyle.color')
40587                            || seriesModel.getColorFromPalette(
40588                                dataAll.getName(rawIdx) || (rawIdx + ''), seriesModel.__paletteScope,
40589                                dataAll.count()
40590                            );
40591                        // Legend may use the visual info in data before processed
40592                        dataAll.setItemVisual(rawIdx, 'color', color);
40593
40594                        // Data is not filtered
40595                        if (filteredIdx != null) {
40596                            data.setItemVisual(filteredIdx, 'color', color);
40597                        }
40598                    }
40599                    else {
40600                        // Set data all color for legend
40601                        dataAll.setItemVisual(rawIdx, 'color', singleDataColor);
40602                    }
40603                });
40604            }
40605        };
40606    };
40607
40608    /*
40609* Licensed to the Apache Software Foundation (ASF) under one
40610* or more contributor license agreements.  See the NOTICE file
40611* distributed with this work for additional information
40612* regarding copyright ownership.  The ASF licenses this file
40613* to you under the Apache License, Version 2.0 (the
40614* "License");
40614 you may not use this file except in compliance
40615* with the License.  You may obtain a copy of the License at
40616*
40617*   http://www.apache.org/licenses/LICENSE-2.0
40618*
40619* Unless required by applicable law or agreed to in writing,
40620* software distributed under the License is distributed on an
40621* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
40622* KIND, either express or implied.  See the License for the
40623* specific language governing permissions and limitations
40624* under the License.
40625*/
40626
40627// FIXME emphasis label position is not same with normal label position
40628
40629    function adjustSingleSide(list, cx, cy, r, dir, viewWidth, viewHeight) {
40630        list.sort(function (a, b) {
40631            return a.y - b.y;
40632        });
40633
40634        // 压
40635        function shiftDown(start, end, delta, dir) {
40636            for (var j = start; j < end; j++) {
40637                list[j].y += delta;
40638                if (j > start
40639                    && j + 1 < end
40640                    && list[j + 1].y > list[j].y + list[j].height
40641                ) {
40642                    shiftUp(j, delta / 2);
40643                    return;
40644                }
40645            }
40646
40647            shiftUp(end - 1, delta / 2);
40648        }
40649
40650        // å¼¹
40651        function shiftUp(end, delta) {
40652            for (var j = end; j >= 0; j--) {
40653                list[j].y -= delta;
40654                if (j > 0
40655                    && list[j].y > list[j - 1].y + list[j - 1].height
40656                ) {
40657                    break;
40658                }
40659            }
40660        }
40661
40662        function changeX(list, isDownList, cx, cy, r, dir) {
40663            var lastDeltaX = dir > 0
40664                ? isDownList                // 右侧
40665                    ? Number.MAX_VALUE      // 下
40666                    : 0                     // 上
40667                : isDownList                // 左侧
40668                    ? Number.MAX_VALUE      // 下
40669                    : 0;                    // 上
40670
40671            for (var i = 0, l = list.length; i < l; i++) {
40672                // Not change x for center label
40673                if (list[i].position === 'center') {
40674                    continue;
40675                }
40676                var deltaY = Math.abs(list[i].y - cy);
40677                var length = list[i].len;
40678                var length2 = list[i].len2;
40679                var deltaX = (deltaY < r + length)
40680                    ? Math.sqrt(
40681                        (r + length + length2) * (r + length + length2)
40682                        - deltaY * deltaY
40683                    )
40684                    : Math.abs(list[i].x - cx);
40685                if (isDownList && deltaX >= lastDeltaX) {
40686                    // 右下,左下
40687                    deltaX = lastDeltaX - 10;
40688                }
40689                if (!isDownList && deltaX <= lastDeltaX) {
40690                    // 右上,左上
40691                    deltaX = lastDeltaX + 10;
40692                }
40693
40694                list[i].x = cx + deltaX * dir;
40695                lastDeltaX = deltaX;
40696            }
40697        }
40698
40699        var lastY = 0;
40700        var delta;
40701        var len = list.length;
40702        var upList = [];
40703        var downList = [];
40704        for (var i = 0; i < len; i++) {
40705            delta = list[i].y - lastY;
40706            if (delta < 0) {
40707                shiftDown(i, len, -delta, dir);
40708            }
40709            lastY = list[i].y + list[i].height;
40710        }
40711        if (viewHeight - lastY < 0) {
40712            shiftUp(len - 1, lastY - viewHeight);
40713        }
40714        for (var i = 0; i < len; i++) {
40715            if (list[i].y >= cy) {
40716                downList.push(list[i]);
40717            }
40718            else {
40719                upList.push(list[i]);
40720            }
40721        }
40722        changeX(upList, false, cx, cy, r, dir);
40723        changeX(downList, true, cx, cy, r, dir);
40724    }
40725
40726    function avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight) {
40727        var leftList = [];
40728        var rightList = [];
40729        for (var i = 0; i < labelLayoutList.length; i++) {
40730            if (labelLayoutList[i].x < cx) {
40731                leftList.push(labelLayoutList[i]);
40732            }
40733            else {
40734                rightList.push(labelLayoutList[i]);
40735            }
40736        }
40737
40738        adjustSingleSide(rightList, cx, cy, r, 1, viewWidth, viewHeight);
40739        adjustSingleSide(leftList, cx, cy, r, -1, viewWidth, viewHeight);
40740
40741        for (var i = 0; i < labelLayoutList.length; i++) {
40742            var linePoints = labelLayoutList[i].linePoints;
40743            if (linePoints) {
40744                var dist = linePoints[1][0] - linePoints[2][0];
40745                if (labelLayoutList[i].x < cx) {
40746                    linePoints[2][0] = labelLayoutList[i].x + 3;
40747                }
40748                else {
40749                    linePoints[2][0] = labelLayoutList[i].x - 3;
40750                }
40751                linePoints[1][1] = linePoints[2][1] = labelLayoutList[i].y;
40752                linePoints[1][0] = linePoints[2][0] + dist;
40753            }
40754        }
40755    }
40756
40757    var labelLayout = function (seriesModel, r, viewWidth, viewHeight) {
40758        var data = seriesModel.getData();
40759        var labelLayoutList = [];
40760        var cx;
40761        var cy;
40762        var hasLabelRotate = false;
40763
40764        data.each(function (idx) {
40765            var layout = data.getItemLayout(idx);
40766
40767            var itemModel = data.getItemModel(idx);
40768            var labelModel = itemModel.getModel('label');
40769            // Use position in normal or emphasis
40770            var labelPosition = labelModel.get('position') || itemModel.get('emphasis.label.position');
40771
40772            var labelLineModel = itemModel.getModel('labelLine');
40773            var labelLineLen = labelLineModel.get('length');
40774            var labelLineLen2 = labelLineModel.get('length2');
40775
40776            var midAngle = (layout.startAngle + layout.endAngle) / 2;
40777            var dx = Math.cos(midAngle);
40778            var dy = Math.sin(midAngle);
40779
40780            var textX;
40781            var textY;
40782            var linePoints;
40783            var textAlign;
40784
40785            cx = layout.cx;
40786            cy = layout.cy;
40787
40788            var isLabelInside = labelPosition === 'inside' || labelPosition === 'inner';
40789            if (labelPosition === 'center') {
40790                textX = layout.cx;
40791                textY = layout.cy;
40792                textAlign = 'center';
40793            }
40794            else {
40795                var x1 = (isLabelInside ? (layout.r + layout.r0) / 2 * dx : layout.r * dx) + cx;
40796                var y1 = (isLabelInside ? (layout.r + layout.r0) / 2 * dy : layout.r * dy) + cy;
40797
40798                textX = x1 + dx * 3;
40799                textY = y1 + dy * 3;
40800
40801                if (!isLabelInside) {
40802                    // For roseType
40803                    var x2 = x1 + dx * (labelLineLen + r - layout.r);
40804                    var y2 = y1 + dy * (labelLineLen + r - layout.r);
40805                    var x3 = x2 + ((dx < 0 ? -1 : 1) * labelLineLen2);
40806                    var y3 = y2;
40807
40808                    textX = x3 + (dx < 0 ? -5 : 5);
40809                    textY = y3;
40810                    linePoints = [[x1, y1], [x2, y2], [x3, y3]];
40811                }
40812
40813                textAlign = isLabelInside ? 'center' : (dx > 0 ? 'left' : 'right');
40814            }
40815            var font = labelModel.getFont();
40816
40817            var labelRotate = labelModel.get('rotate')
40818                ? (dx < 0 ? -midAngle + Math.PI : -midAngle) : 0;
40819            var text = seriesModel.getFormattedLabel(idx, 'normal')
40820                || data.getName(idx);
40821            var textRect = getBoundingRect(
40822                text, font, textAlign, 'top'
40823            );
40824            hasLabelRotate = !!labelRotate;
40825            layout.label = {
40826                x: textX,
40827                y: textY,
40828                position: labelPosition,
40829                height: textRect.height,
40830                len: labelLineLen,
40831                len2: labelLineLen2,
40832                linePoints: linePoints,
40833                textAlign: textAlign,
40834                verticalAlign: 'middle',
40835                rotation: labelRotate,
40836                inside: isLabelInside
40837            };
40838
40839            // Not layout the inside label
40840            if (!isLabelInside) {
40841                labelLayoutList.push(layout.label);
40842            }
40843        });
40844        if (!hasLabelRotate && seriesModel.get('avoidLabelOverlap')) {
40845            avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight);
40846        }
40847    };
40848
40849    /*
40850* Licensed to the Apache Software Foundation (ASF) under one
40851* or more contributor license agreements.  See the NOTICE file
40852* distributed with this work for additional information
40853* regarding copyright ownership.  The ASF licenses this file
40854* to you under the Apache License, Version 2.0 (the
40855* "License"); you may not use this file except in compliance
40856* with the License.  You may obtain a copy of the License at
40857*
40858*   http://www.apache.org/licenses/LICENSE-2.0
40859*
40860* Unless required by applicable law or agreed to in writing,
40861* software distributed under the License is distributed on an
40862* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
40863* KIND, either express or implied.  See the License for the
40864* specific language governing permissions and limitations
40865* under the License.
40866*/
40867
40868
40869    var PI2$4 = Math.PI * 2;
40870    var RADIAN = Math.PI / 180;
40871
40872    var pieLayout = function (seriesType, ecModel, api, payload) {
40873        ecModel.eachSeriesByType(seriesType, function (seriesModel) {
40874            var data = seriesModel.getData();
40875            var valueDim = data.mapDimension('value');
40876
40877            var center = seriesModel.get('center');
40878            var radius = seriesModel.get('radius');
40879
40880            if (!isArray(radius)) {
40881                radius = [0, radius];
40882            }
40883            if (!isArray(center)) {
40884                center = [center, center];
40885            }
40886
40887            var width = api.getWidth();
40888            var height = api.getHeight();
40889            var size = Math.min(width, height);
40890            var cx = parsePercent$1(center[0], width);
40891            var cy = parsePercent$1(center[1], height);
40892            var r0 = parsePercent$1(radius[0], size / 2);
40893            var r = parsePercent$1(radius[1], size / 2);
40894
40895            var startAngle = -seriesModel.get('startAngle') * RADIAN;
40896
40897            var minAngle = seriesModel.get('minAngle') * RADIAN;
40898
40899            var validDataCount = 0;
40900            data.each(valueDim, function (value) {
40901                !isNaN(value) && validDataCount++;
40902            });
40903
40904            var sum = data.getSum(valueDim);
40905            // Sum may be 0
40906            var unitRadian = Math.PI / (sum || validDataCount) * 2;
40907
40908            var clockwise = seriesModel.get('clockwise');
40909
40910            var roseType = seriesModel.get('roseType');
40911            var stillShowZeroSum = seriesModel.get('stillShowZeroSum');
40912
40913            // [0...max]
40914            var extent = data.getDataExtent(valueDim);
40915            extent[0] = 0;
40916
40917            // In the case some sector angle is smaller than minAngle
40918            var restAngle = PI2$4;
40919            var valueSumLargerThanMinAngle = 0;
40920
40921            var currentAngle = startAngle;
40922            var dir = clockwise ? 1 : -1;
40923
40924            data.each(valueDim, function (value, idx) {
40925                var angle;
40926                if (isNaN(value)) {
40927                    data.setItemLayout(idx, {
40928                        angle: NaN,
40929                        startAngle: NaN,
40930                        endAngle: NaN,
40931                        clockwise: clockwise,
40932                        cx: cx,
40933                        cy: cy,
40934                        r0: r0,
40935                        r: roseType
40936                            ? NaN
40937                            : r
40938                    });
40939                    return;
40940                }
40941
40942                // FIXME 兼容 2.0 但是 roseType 是 area çš„æ—¶å€™æ‰æ˜¯è¿™æ ·ï¼Ÿ
40943                if (roseType !== 'area') {
40944                    angle = (sum === 0 && stillShowZeroSum)
40945                        ? unitRadian : (value * unitRadian);
40946                }
40947                else {
40948                    angle = PI2$4 / validDataCount;
40949                }
40950
40951                if (angle < minAngle) {
40952                    angle = minAngle;
40953                    restAngle -= minAngle;
40954                }
40955                else {
40956                    valueSumLargerThanMinAngle += value;
40957                }
40958
40959                var endAngle = currentAngle + dir * angle;
40960                data.setItemLayout(idx, {
40961                    angle: angle,
40962                    startAngle: currentAngle,
40963                    endAngle: endAngle,
40964                    clockwise: clockwise,
40965                    cx: cx,
40966                    cy: cy,
40967                    r0: r0,
40968                    r: roseType
40969                        ? linearMap(value, extent, [r0, r])
40970                        : r
40971                });
40972
40973                currentAngle = endAngle;
40974            });
40975
40976            // Some sector is constrained by minAngle
40977            // Rest sectors needs recalculate angle
40978            if (restAngle < PI2$4 && validDataCount) {
40979                // Average the angle if rest angle is not enough after all angles is
40980                // Constrained by minAngle
40981                if (restAngle <= 1e-3) {
40982                    var angle = PI2$4 / validDataCount;
40983                    data.each(valueDim, function (value, idx) {
40984                        if (!isNaN(value)) {
40985                            var layout = data.getItemLayout(idx);
40986                            layout.angle = angle;
40987                            layout.startAngle = startAngle + dir * idx * angle;
40988                            layout.endAngle = startAngle + dir * (idx + 1) * angle;
40989                        }
40990                    });
40991                }
40992                else {
40993                    unitRadian = restAngle / valueSumLargerThanMinAngle;
40994                    currentAngle = startAngle;
40995                    data.each(valueDim, function (value, idx) {
40996                        if (!isNaN(value)) {
40997                            var layout = data.getItemLayout(idx);
40998                            var angle = layout.angle === minAngle
40999                                ? minAngle : value * unitRadian;
41000                            layout.startAngle = currentAngle;
41001                            layout.endAngle = currentAngle + dir * angle;
41002                            currentAngle += dir * angle;
41003                        }
41004                    });
41005                }
41006            }
41007
41008            labelLayout(seriesModel, r, width, height);
41009        });
41010    };
41011
41012    /*
41013* Licensed to the Apache Software Foundation (ASF) under one
41014* or more contributor license agreements.  See the NOTICE file
41015* distributed with this work for additional information
41016* regarding copyright ownership.  The ASF licenses this file
41017* to you under the Apache License, Version 2.0 (the
41018* "License");
41018 you may not use this file except in compliance
41019* with the License.  You may obtain a copy of the License at
41020*
41021*   http://www.apache.org/licenses/LICENSE-2.0
41022*
41023* Unless required by applicable law or agreed to in writing,
41024* software distributed under the License is distributed on an
41025* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
41026* KIND, either express or implied.  See the License for the
41027* specific language governing permissions and limitations
41028* under the License.
41029*/
41030
41031    var dataFilter = function (seriesType) {
41032        return {
41033            seriesType: seriesType,
41034            reset: function (seriesModel, ecModel) {
41035                var legendModels = ecModel.findComponents({
41036                    mainType: 'legend'
41037                });
41038                if (!legendModels || !legendModels.length) {
41039                    return;
41040                }
41041                var data = seriesModel.getData();
41042                data.filterSelf(function (idx) {
41043                    var name = data.getName(idx);
41044                    // If in any legend component the status is not selected.
41045                    for (var i = 0; i < legendModels.length; i++) {
41046                        if (!legendModels[i].isSelected(name)) {
41047                            return false;
41048                        }
41049                    }
41050                    return true;
41051                });
41052            }
41053        };
41054    };
41055
41056    /*
41057* Licensed to the Apache Software Foundation (ASF) under one
41058* or more contributor license agreements.  See the NOTICE file
41059* distributed with this work for additional information
41060* regarding copyright ownership.  The ASF licenses this file
41061* to you under the Apache License, Version 2.0 (the
41062* "License"); you may not use this file except in compliance
41063* with the License.  You may obtain a copy of the License at
41064*
41065*   http://www.apache.org/licenses/LICENSE-2.0
41066*
41067* Unless required by applicable law or agreed to in writing,
41068* software distributed under the License is distributed on an
41069* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
41070* KIND, either express or implied.  See the License for the
41071* specific language governing permissions and limitations
41072* under the License.
41073*/
41074
41075    createDataSelectAction('pie', [{
41076        type: 'pieToggleSelect',
41077        event: 'pieselectchanged',
41078        method: 'toggleSelected'
41079    }, {
41080        type: 'pieSelect',
41081        event: 'pieselected',
41082        method: 'select'
41083    }, {
41084        type: 'pieUnSelect',
41085        event: 'pieunselected',
41086        method: 'unSelect'
41087    }]);
41088
41089    registerVisual(dataColor('pie'));
41090    registerLayout(curry(pieLayout, 'pie'));
41091    registerProcessor(dataFilter('pie'));
41092
41093    /*
41094* Licensed to the Apache Software Foundation (ASF) under one
41095* or more contributor license agreements.  See the NOTICE file
41096* distributed with this work for additional information
41097* regarding copyright ownership.  The ASF licenses this file
41098* to you under the Apache License, Version 2.0 (the
41099* "License"); you may not use this file except in compliance
41100* with the License.  You may obtain a copy of the License at
41101*
41102*   http://www.apache.org/licenses/LICENSE-2.0
41103*
41104* Unless required by applicable law or agreed to in writing,
41105* software distributed under the License is distributed on an
41106* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
41107* KIND, either express or implied.  See the License for the
41108* specific language governing permissions and limitations
41109* under the License.
41110*/
41111
41112    SeriesModel.extend({
41113
41114        type: 'series.scatter',
41115
41116        dependencies: ['grid', 'polar', 'geo', 'singleAxis', 'calendar'],
41117
41118        getInitialData: function (option, ecModel) {
41119            return createListFromArray(this.getSource(), this);
41120        },
41121
41122        brushSelector: 'point',
41123
41124        getProgressive: function () {
41125            var progressive = this.option.progressive;
41126            if (progressive == null) {
41127                // PENDING
41128                return this.option.large ? 5e3 : this.get('progressive');
41129            }
41130            return progressive;
41131        },
41132
41133        getProgressiveThreshold: function () {
41134            var progressiveThreshold = this.option.progressiveThreshold;
41135            if (progressiveThreshold == null) {
41136                // PENDING
41137                return this.option.large ? 1e4 : this.get('progressiveThreshold');
41138            }
41139            return progressiveThreshold;
41140        },
41141
41142        defaultOption: {
41143            coordinateSystem: 'cartesian2d',
41144            zlevel: 0,
41145            z: 2,
41146            legendHoverLink: true,
41147
41148            hoverAnimation: true,
41149            // Cartesian coordinate system
41150            // xAxisIndex: 0,
41151            // yAxisIndex: 0,
41152
41153            // Polar coordinate system
41154            // polarIndex: 0,
41155
41156            // Geo coordinate system
41157            // geoIndex: 0,
41158
41159            // symbol: null,        // 图形类型
41160            symbolSize: 10,          // 图形大小,半宽(半径)参数,当图形为方向或菱形则总宽度为symbolSize * 2
41161            // symbolRotate: null,  // 图形旋转控制
41162
41163            large: false,
41164            // Available when large is true
41165            largeThreshold: 2000,
41166            // cursor: null,
41167
41168            // label: {
41169            // show: false
41170            // distance: 5,
41171            // formatter: æ ‡ç­¾æ–‡æœ¬æ ¼å¼å™¨ï¼ŒåŒTooltip.formatter,不支持异步回调
41172            // position: 默认自适应,水平布局为'top',垂直布局为'right',可选为
41173            //           'inside'|'left'|'right'|'top'|'bottom'
41174            // é»˜è®¤ä½¿ç”¨å…¨å±€æ–‡æœ¬æ ·å¼ï¼Œè¯¦è§TEXTSTYLE
41175            // },
41176            itemStyle: {
41177                opacity: 0.8
41178                // color: 各异
41179            }
41180
41181            // progressive: null
41182        }
41183
41184    });
41185
41186    /*
41187* Licensed to the Apache Software Foundation (ASF) under one
41188* or more contributor license agreements.  See the NOTICE file
41189* distributed with this work for additional information
41190* regarding copyright ownership.  The ASF licenses this file
41191* to you under the Apache License, Version 2.0 (the
41192* "License"); you may not use this file except in compliance
41193* with the License.  You may obtain a copy of the License at
41194*
41195*   http://www.apache.org/licenses/LICENSE-2.0
41196*
41197* Unless required by applicable law or agreed to in writing,
41198* software distributed under the License is distributed on an
41199* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
41200* KIND, either express or implied.  See the License for the
41201* specific language governing permissions and limitations
41202* under the License.
41203*/
41204
41205// TODO Batch by color
41206
41207    var BOOST_SIZE_THRESHOLD = 4;
41208
41209    var LargeSymbolPath = extendShape({
41210
41211        shape: {
41212            points: null
41213        },
41214
41215        symbolProxy: null,
41216
41217        buildPath: function (path, shape) {
41218            var points = shape.points;
41219            var size = shape.size;
41220
41221            var symbolProxy = this.symbolProxy;
41222            var symbolProxyShape = symbolProxy.shape;
41223            var ctx = path.getContext ? path.getContext() : path;
41224            var canBoost = ctx && size[0] < BOOST_SIZE_THRESHOLD;
41225
41226            // Do draw in afterBrush.
41227            if (canBoost) {
41228                return;
41229            }
41230
41231            for (var i = 0; i < points.length;) {
41232                var x = points[i++];
41233                var y = points[i++];
41234
41235                if (isNaN(x) || isNaN(y)) {
41236                    continue;
41237                }
41238
41239                symbolProxyShape.x = x - size[0] / 2;
41240                symbolProxyShape.y = y - size[1] / 2;
41241                symbolProxyShape.width = size[0];
41242                symbolProxyShape.height = size[1];
41243
41244                symbolProxy.buildPath(path, symbolProxyShape, true);
41245            }
41246        },
41247
41248        afterBrush: function (ctx) {
41249            var shape = this.shape;
41250            var points = shape.points;
41251            var size = shape.size;
41252            var canBoost = size[0] < BOOST_SIZE_THRESHOLD;
41253
41254            if (!canBoost) {
41255                return;
41256            }
41257
41258            this.setTransform(ctx);
41259            // PENDING If style or other canvas status changed?
41260            for (var i = 0; i < points.length;) {
41261                var x = points[i++];
41262                var y = points[i++];
41263                if (isNaN(x) || isNaN(y)) {
41264                    continue;
41265                }
41266                // fillRect is faster than building a rect path and draw.
41267                // And it support light globalCompositeOperation.
41268                ctx.fillRect(
41269                    x - size[0] / 2, y - size[1] / 2,
41270                    size[0], size[1]
41271                );
41272            }
41273
41274            this.restoreTransform(ctx);
41275        },
41276
41277        findDataIndex: function (x, y) {
41278            // TODO ???
41279            // Consider transform
41280
41281            var shape = this.shape;
41282            var points = shape.points;
41283            var size = shape.size;
41284
41285            var w = Math.max(size[0], 4);
41286            var h = Math.max(size[1], 4);
41287
41288            // Not consider transform
41289            // Treat each element as a rect
41290            // top down traverse
41291            for (var idx = points.length / 2 - 1; idx >= 0; idx--) {
41292                var i = idx * 2;
41293                var x0 = points[i] - w / 2;
41294                var y0 = points[i + 1] - h / 2;
41295                if (x >= x0 && y >= y0 && x <= x0 + w && y <= y0 + h) {
41296                    return idx;
41297                }
41298            }
41299
41300            return -1;
41301        }
41302    });
41303
41304    function LargeSymbolDraw() {
41305        this.group = new Group();
41306    }
41307
41308    var largeSymbolProto = LargeSymbolDraw.prototype;
41309
41310    largeSymbolProto.isPersistent = function () {
41311        return !this._incremental;
41312    };
41313
41314    /**
41315     * Update symbols draw by new data
41316     * @param {module:echarts/data/List} data
41317     */
41318    largeSymbolProto.updateData = function (data) {
41319        this.group.removeAll();
41320        var symbolEl = new LargeSymbolPath({
41321            rectHover: true,
41322            cursor: 'default'
41323        });
41324
41325        symbolEl.setShape({
41326            points: data.getLayout('symbolPoints')
41327        });
41328        this._setCommon(symbolEl, data);
41329        this.group.add(symbolEl);
41330
41331        this._incremental = null;
41332    };
41333
41334    largeSymbolProto.updateLayout = function (data) {
41335        if (this._incremental) {
41336            return;
41337        }
41338
41339        var points = data.getLayout('symbolPoints');
41340        this.group.eachChild(function (child) {
41341            if (child.startIndex != null) {
41342                var len = (child.endIndex - child.startIndex) * 2;
41343                var byteOffset = child.startIndex * 4 * 2;
41344                points = new Float32Array(points.buffer, byteOffset, len);
41345            }
41346            child.setShape('points', points);
41347        });
41348    };
41349
41350    largeSymbolProto.incrementalPrepareUpdate = function (data) {
41351        this.group.removeAll();
41352
41353        this._clearIncremental();
41354        // Only use incremental displayables when data amount is larger than 2 million.
41355        // PENDING Incremental data?
41356        if (data.count() > 2e6) {
41357            if (!this._incremental) {
41358                this._incremental = new IncrementalDisplayble({
41359                    silent: true
41360                });
41361            }
41362            this.group.add(this._incremental);
41363        }
41364        else {
41365            this._incremental = null;
41366        }
41367    };
41368
41369    largeSymbolProto.incrementalUpdate = function (taskParams, data) {
41370        var symbolEl;
41371        if (this._incremental) {
41372            symbolEl = new LargeSymbolPath();
41373            this._incremental.addDisplayable(symbolEl, true);
41374        }
41375        else {
41376            symbolEl = new LargeSymbolPath({
41377                rectHover: true,
41378                cursor: 'default',
41379                startIndex: taskParams.start,
41380                endIndex: taskParams.end
41381            });
41382            symbolEl.incremental = true;
41383            this.group.add(symbolEl);
41384        }
41385
41386        symbolEl.setShape({
41387            points: data.getLayout('symbolPoints')
41388        });
41389        this._setCommon(symbolEl, data, !!this._incremental);
41390    };
41391
41392    largeSymbolProto._setCommon = function (symbolEl, data, isIncremental) {
41393        var hostModel = data.hostModel;
41394
41395        // TODO
41396        // if (data.hasItemVisual.symbolSize) {
41397        //     // TODO typed array?
41398        //     symbolEl.setShape('sizes', data.mapArray(
41399        //         function (idx) {
41400        //             var size = data.getItemVisual(idx, 'symbolSize');
41401        //             return (size instanceof Array) ? size : [size, size];
41402        //         }
41403        //     ));
41404        // }
41405        // else {
41406        var size = data.getVisual('symbolSize');
41407        symbolEl.setShape('size', (size instanceof Array) ? size : [size, size]);
41408        // }
41409
41410        // Create symbolProxy to build path for each data
41411        symbolEl.symbolProxy = createSymbol(
41412            data.getVisual('symbol'), 0, 0, 0, 0
41413        );
41414        // Use symbolProxy setColor method
41415        symbolEl.setColor = symbolEl.symbolProxy.setColor;
41416
41417        var extrudeShadow = symbolEl.shape.size[0] < BOOST_SIZE_THRESHOLD;
41418        symbolEl.useStyle(
41419            // Draw shadow when doing fillRect is extremely slow.
41420            hostModel.getModel('itemStyle').getItemStyle(extrudeShadow ? ['color', 'shadowBlur', 'shadowColor'] : ['color'])
41421        );
41422
41423        var visualColor = data.getVisual('color');
41424        if (visualColor) {
41425            symbolEl.setColor(visualColor);
41426        }
41427
41428        if (!isIncremental) {
41429            // Enable tooltip
41430            // PENDING May have performance issue when path is extremely large
41431            symbolEl.seriesIndex = hostModel.seriesIndex;
41432            symbolEl.on('mousemove', function (e) {
41433                symbolEl.dataIndex = null;
41434                var dataIndex = symbolEl.findDataIndex(e.offsetX, e.offsetY);
41435                if (dataIndex >= 0) {
41436                    // Provide dataIndex for tooltip
41437                    symbolEl.dataIndex = dataIndex + (symbolEl.startIndex || 0);
41438                }
41439            });
41440        }
41441    };
41442
41443    largeSymbolProto.remove = function () {
41444        this._clearIncremental();
41445        this._incremental = null;
41446        this.group.removeAll();
41447    };
41448
41449    largeSymbolProto._clearIncremental = function () {
41450        var incremental = this._incremental;
41451        if (incremental) {
41452            incremental.clearDisplaybles();
41453        }
41454    };
41455
41456    /*
41457* Licensed to the Apache Software Foundation (ASF) under one
41458* or more contributor license agreements.  See the NOTICE file
41459* distributed with this work for additional information
41460* regarding copyright ownership.  The ASF licenses this file
41461* to you under the Apache License, Version 2.0 (the
41462* "License");
41462 you may not use this file except in compliance
41463* with the License.  You may obtain a copy of the License at
41464*
41465*   http://www.apache.org/licenses/LICENSE-2.0
41466*
41467* Unless required by applicable law or agreed to in writing,
41468* software distributed under the License is distributed on an
41469* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
41470* KIND, either express or implied.  See the License for the
41471* specific language governing permissions and limitations
41472* under the License.
41473*/
41474
41475    extendChartView({
41476
41477        type: 'scatter',
41478
41479        render: function (seriesModel, ecModel, api) {
41480            var data = seriesModel.getData();
41481
41482            var symbolDraw = this._updateSymbolDraw(data, seriesModel);
41483            symbolDraw.updateData(data);
41484
41485            this._finished = true;
41486        },
41487
41488        incrementalPrepareRender: function (seriesModel, ecModel, api) {
41489            var data = seriesModel.getData();
41490            var symbolDraw = this._updateSymbolDraw(data, seriesModel);
41491
41492            symbolDraw.incrementalPrepareUpdate(data);
41493
41494            this._finished = false;
41495        },
41496
41497        incrementalRender: function (taskParams, seriesModel, ecModel) {
41498            this._symbolDraw.incrementalUpdate(taskParams, seriesModel.getData());
41499
41500            this._finished = taskParams.end === seriesModel.getData().count();
41501        },
41502
41503        updateTransform: function (seriesModel, ecModel, api) {
41504            var data = seriesModel.getData();
41505            // Must mark group dirty and make sure the incremental layer will be cleared
41506            // PENDING
41507            this.group.dirty();
41508
41509            if (!this._finished || data.count() > 1e4 || !this._symbolDraw.isPersistent()) {
41510                return {
41511                    update: true
41512                };
41513            }
41514            else {
41515                var res = pointsLayout().reset(seriesModel);
41516                if (res.progress) {
41517                    res.progress({ start: 0, end: data.count() }, data);
41518                }
41519
41520                this._symbolDraw.updateLayout(data);
41521            }
41522        },
41523
41524        _updateSymbolDraw: function (data, seriesModel) {
41525            var symbolDraw = this._symbolDraw;
41526            var pipelineContext = seriesModel.pipelineContext;
41527            var isLargeDraw = pipelineContext.large;
41528
41529            if (!symbolDraw || isLargeDraw !== this._isLargeDraw) {
41530                symbolDraw && symbolDraw.remove();
41531                symbolDraw = this._symbolDraw = isLargeDraw
41532                    ? new LargeSymbolDraw()
41533                    : new SymbolDraw();
41534                this._isLargeDraw = isLargeDraw;
41535                this.group.removeAll();
41536            }
41537
41538            this.group.add(symbolDraw.group);
41539
41540            return symbolDraw;
41541        },
41542
41543        remove: function (ecModel, api) {
41544            this._symbolDraw && this._symbolDraw.remove(true);
41545            this._symbolDraw = null;
41546        },
41547
41548        dispose: function () {}
41549    });
41550
41551    /*
41552* Licensed to the Apache Software Foundation (ASF) under one
41553* or more contributor license agreements.  See the NOTICE file
41554* distributed with this work for additional information
41555* regarding copyright ownership.  The ASF licenses this file
41556* to you under the Apache License, Version 2.0 (the
41557* "License"); you may not use this file except in compliance
41558* with the License.  You may obtain a copy of the License at
41559*
41560*   http://www.apache.org/licenses/LICENSE-2.0
41561*
41562* Unless required by applicable law or agreed to in writing,
41563* software distributed under the License is distributed on an
41564* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
41565* KIND, either express or implied.  See the License for the
41566* specific language governing permissions and limitations
41567* under the License.
41568*/
41569
41570// import * as zrUtil from 'zrender/src/core/util';
41571
41572// In case developer forget to include grid component
41573    registerVisual(visualSymbol('scatter', 'circle'));
41574    registerLayout(pointsLayout('scatter'));
41575
41576// echarts.registerProcessor(function (ecModel, api) {
41577//     ecModel.eachSeriesByType('scatter', function (seriesModel) {
41578//         var data = seriesModel.getData();
41579//         var coordSys = seriesModel.coordinateSystem;
41580//         if (coordSys.type !== 'geo') {
41581//             return;
41582//         }
41583//         var startPt = coordSys.pointToData([0, 0]);
41584//         var endPt = coordSys.pointToData([api.getWidth(), api.getHeight()]);
41585
41586//         var dims = zrUtil.map(coordSys.dimensions, function (dim) {
41587//             return data.mapDimension(dim);
41588//         });
41589//         var range = {};
41590//         range[dims[0]] = [Math.min(startPt[0], endPt[0]), Math.max(startPt[0], endPt[0])];
41591//         range[dims[1]] = [Math.min(startPt[1], endPt[1]), Math.max(startPt[1], endPt[1])];
41592
41593//         data.selectRange(range);
41594//     });
41595// });
41596
41597    /*
41598* Licensed to the Apache Software Foundation (ASF) under one
41599* or more contributor license agreements.  See the NOTICE file
41600* distributed with this work for additional information
41601* regarding copyright ownership.  The ASF licenses this file
41602* to you under the Apache License, Version 2.0 (the
41603* "License"); you may not use this file except in compliance
41604* with the License.  You may obtain a copy of the License at
41605*
41606*   http://www.apache.org/licenses/LICENSE-2.0
41607*
41608* Unless required by applicable law or agreed to in writing,
41609* software distributed under the License is distributed on an
41610* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
41611* KIND, either express or implied.  See the License for the
41612* specific language governing permissions and limitations
41613* under the License.
41614*/
41615
41616    function IndicatorAxis(dim, scale, radiusExtent) {
41617        Axis.call(this, dim, scale, radiusExtent);
41618
41619        /**
41620         * Axis type
41621         *  - 'category'
41622         *  - 'value'
41623         *  - 'time'
41624         *  - 'log'
41625         * @type {string}
41626         */
41627        this.type = 'value';
41628
41629        this.angle = 0;
41630
41631        /**
41632         * Indicator name
41633         * @type {string}
41634         */
41635        this.name = '';
41636        /**
41637         * @type {module:echarts/model/Model}
41638         */
41639        this.model;
41640    }
41641
41642    inherits(IndicatorAxis, Axis);
41643
41644    /*
41645* Licensed to the Apache Software Foundation (ASF) under one
41646* or more contributor license agreements.  See the NOTICE file
41647* distributed with this work for additional information
41648* regarding copyright ownership.  The ASF licenses this file
41649* to you under the Apache License, Version 2.0 (the
41650* "License"); you may not use this file except in compliance
41651* with the License.  You may obtain a copy of the License at
41652*
41653*   http://www.apache.org/licenses/LICENSE-2.0
41654*
41655* Unless required by applicable law or agreed to in writing,
41656* software distributed under the License is distributed on an
41657* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
41658* KIND, either express or implied.  See the License for the
41659* specific language governing permissions and limitations
41660* under the License.
41661*/
41662
41663// TODO clockwise
41664
41665    function Radar(radarModel, ecModel, api) {
41666
41667        this._model = radarModel;
41668        /**
41669         * Radar dimensions
41670         * @type {Array.<string>}
41671         */
41672        this.dimensions = [];
41673
41674        this._indicatorAxes = map(radarModel.getIndicatorModels(), function (indicatorModel, idx) {
41675            var dim = 'indicator_' + idx;
41676            var indicatorAxis = new IndicatorAxis(dim, new IntervalScale());
41677            indicatorAxis.name = indicatorModel.get('name');
41678            // Inject model and axis
41679            indicatorAxis.model = indicatorModel;
41680            indicatorModel.axis = indicatorAxis;
41681            this.dimensions.push(dim);
41682            return indicatorAxis;
41683        }, this);
41684
41685        this.resize(radarModel, api);
41686
41687        /**
41688         * @type {number}
41689         * @readOnly
41690         */
41691        this.cx;
41692        /**
41693         * @type {number}
41694         * @readOnly
41695         */
41696        this.cy;
41697        /**
41698         * @type {number}
41699         * @readOnly
41700         */
41701        this.r;
41702        /**
41703         * @type {number}
41704         * @readOnly
41705         */
41706        this.startAngle;
41707    }
41708
41709    Radar.prototype.getIndicatorAxes = function () {
41710        return this._indicatorAxes;
41711    };
41712
41713    Radar.prototype.dataToPoint = function (value, indicatorIndex) {
41714        var indicatorAxis = this._indicatorAxes[indicatorIndex];
41715
41716        return this.coordToPoint(indicatorAxis.dataToCoord(value), indicatorIndex);
41717    };
41718
41719    Radar.prototype.coordToPoint = function (coord, indicatorIndex) {
41720        var indicatorAxis = this._indicatorAxes[indicatorIndex];
41721        var angle = indicatorAxis.angle;
41722        var x = this.cx + coord * Math.cos(angle);
41723        var y = this.cy - coord * Math.sin(angle);
41724        return [x, y];
41725    };
41726
41727    Radar.prototype.pointToData = function (pt) {
41728        var dx = pt[0] - this.cx;
41729        var dy = pt[1] - this.cy;
41730        var radius = Math.sqrt(dx * dx + dy * dy);
41731        dx /= radius;
41732        dy /= radius;
41733
41734        var radian = Math.atan2(-dy, dx);
41735
41736        // Find the closest angle
41737        // FIXME index can calculated directly
41738        var minRadianDiff = Infinity;
41739        var closestAxis;
41740        var closestAxisIdx = -1;
41741        for (var i = 0; i < this._indicatorAxes.length; i++) {
41742            var indicatorAxis = this._indicatorAxes[i];
41743            var diff = Math.abs(radian - indicatorAxis.angle);
41744            if (diff < minRadianDiff) {
41745                closestAxis = indicatorAxis;
41746                closestAxisIdx = i;
41747                minRadianDiff = diff;
41748            }
41749        }
41750
41751        return [closestAxisIdx, +(closestAxis && closestAxis.coodToData(radius))];
41752    };
41753
41754    Radar.prototype.resize = function (radarModel, api) {
41755        var center = radarModel.get('center');
41756        var viewWidth = api.getWidth();
41757        var viewHeight = api.getHeight();
41758        var viewSize = Math.min(viewWidth, viewHeight) / 2;
41759        this.cx = parsePercent$1(center[0], viewWidth);
41760        this.cy = parsePercent$1(center[1], viewHeight);
41761
41762        this.startAngle = radarModel.get('startAngle') * Math.PI / 180;
41763
41764        this.r = parsePercent$1(radarModel.get('radius'), viewSize);
41765
41766        each$1(this._indicatorAxes, function (indicatorAxis, idx) {
41767            indicatorAxis.setExtent(0, this.r);
41768            var angle = (this.startAngle + idx * Math.PI * 2 / this._indicatorAxes.length);
41769            // Normalize to [-PI, PI]
41770            angle = Math.atan2(Math.sin(angle), Math.cos(angle));
41771            indicatorAxis.angle = angle;
41772        }, this);
41773    };
41774
41775    Radar.prototype.update = function (ecModel, api) {
41776        var indicatorAxes = this._indicatorAxes;
41777        var radarModel = this._model;
41778        each$1(indicatorAxes, function (indicatorAxis) {
41779            indicatorAxis.scale.setExtent(Infinity, -Infinity);
41780        });
41781        ecModel.eachSeriesByType('radar', function (radarSeries, idx) {
41782            if (radarSeries.get('coordinateSystem') !== 'radar'
41783                || ecModel.getComponent('radar', radarSeries.get('radarIndex')) !== radarModel
41784            ) {
41785                return;
41786            }
41787            var data = radarSeries.getData();
41788            each$1(indicatorAxes, function (indicatorAxis) {
41789                indicatorAxis.scale.unionExtentFromData(data, data.mapDimension(indicatorAxis.dim));
41790            });
41791        }, this);
41792
41793        var splitNumber = radarModel.get('splitNumber');
41794
41795        function increaseInterval(interval) {
41796            var exp10 = Math.pow(10, Math.floor(Math.log(interval) / Math.LN10));
41797            // Increase interval
41798            var f = interval / exp10;
41799            if (f === 2) {
41800                f = 5;
41801            }
41802            else { // f is 2 or 5
41803                f *= 2;
41804            }
41805            return f * exp10;
41806        }
41807        // Force all the axis fixing the maxSplitNumber.
41808        each$1(indicatorAxes, function (indicatorAxis, idx) {
41809            var rawExtent = getScaleExtent(indicatorAxis.scale, indicatorAxis.model);
41810            niceScaleExtent(indicatorAxis.scale, indicatorAxis.model);
41811
41812            var axisModel = indicatorAxis.model;
41813            var scale = indicatorAxis.scale;
41814            var fixedMin = axisModel.getMin();
41815            var fixedMax = axisModel.getMax();
41816            var interval = scale.getInterval();
41817
41818            if (fixedMin != null && fixedMax != null) {
41819                // User set min, max, divide to get new interval
41820                scale.setExtent(+fixedMin, +fixedMax);
41821                scale.setInterval(
41822                    (fixedMax - fixedMin) / splitNumber
41823                );
41824            }
41825            else if (fixedMin != null) {
41826                var max;
41827                // User set min, expand extent on the other side
41828                do {
41829                    max = fixedMin + interval * splitNumber;
41830                    scale.setExtent(+fixedMin, max);
41831                    // Interval must been set after extent
41832                    // FIXME
41833                    scale.setInterval(interval);
41834
41835                    interval = increaseInterval(interval);
41836                } while (max < rawExtent[1] && isFinite(max) && isFinite(rawExtent[1]));
41837            }
41838            else if (fixedMax != null) {
41839                var min;
41840                // User set min, expand extent on the other side
41841                do {
41842                    min = fixedMax - interval * splitNumber;
41843                    scale.setExtent(min, +fixedMax);
41844                    scale.setInterval(interval);
41845                    interval = increaseInterval(interval);
41846                } while (min > rawExtent[0] && isFinite(min) && isFinite(rawExtent[0]));
41847            }
41848            else {
41849                var nicedSplitNumber = scale.getTicks().length - 1;
41850                if (nicedSplitNumber > splitNumber) {
41851                    interval = increaseInterval(interval);
41852                }
41853                // PENDING
41854                var center = Math.round((rawExtent[0] + rawExtent[1]) / 2 / interval) * interval;
41855                var halfSplitNumber = Math.round(splitNumber / 2);
41856                scale.setExtent(
41857                    round$1(center - halfSplitNumber * interval),
41858                    round$1(center + (splitNumber - halfSplitNumber) * interval)
41859                );
41860                scale.setInterval(interval);
41861            }
41862        });
41863    };
41864
41865    /**
41866     * Radar dimensions is based on the data
41867     * @type {Array}
41868     */
41869    Radar.dimensions = [];
41870
41871    Radar.create = function (ecModel, api) {
41872        var radarList = [];
41873        ecModel.eachComponent('radar', function (radarModel) {
41874            var radar = new Radar(radarModel, ecModel, api);
41875            radarList.push(radar);
41876            radarModel.coordinateSystem = radar;
41877        });
41878        ecModel.eachSeriesByType('radar', function (radarSeries) {
41879            if (radarSeries.get('coordinateSystem') === 'radar') {
41880                // Inject coordinate system
41881                radarSeries.coordinateSystem = radarList[radarSeries.get('radarIndex') || 0];
41882            }
41883        });
41884        return radarList;
41885    };
41886
41887    CoordinateSystemManager.register('radar', Radar);
41888
41889    /*
41890* Licensed to the Apache Software Foundation (ASF) under one
41891* or more contributor license agreements.  See the NOTICE file
41892* distributed with this work for additional information
41893* regarding copyright ownership.  The ASF licenses this file
41894* to you under the Apache License, Version 2.0 (the
41895* "License");
41895 you may not use this file except in compliance
41896* with the License.  You may obtain a copy of the License at
41897*
41898*   http://www.apache.org/licenses/LICENSE-2.0
41899*
41900* Unless required by applicable law or agreed to in writing,
41901* software distributed under the License is distributed on an
41902* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
41903* KIND, either express or implied.  See the License for the
41904* specific language governing permissions and limitations
41905* under the License.
41906*/
41907
41908    var valueAxisDefault = axisDefault.valueAxis;
41909
41910    function defaultsShow(opt, show) {
41911        return defaults({
41912            show: show
41913        }, opt);
41914    }
41915
41916    var RadarModel = extendComponentModel({
41917
41918        type: 'radar',
41919
41920        optionUpdated: function () {
41921            var boundaryGap = this.get('boundaryGap');
41922            var splitNumber = this.get('splitNumber');
41923            var scale = this.get('scale');
41924            var axisLine = this.get('axisLine');
41925            var axisTick = this.get('axisTick');
41926            var axisLabel = this.get('axisLabel');
41927            var nameTextStyle = this.get('name');
41928            var showName = this.get('name.show');
41929            var nameFormatter = this.get('name.formatter');
41930            var nameGap = this.get('nameGap');
41931            var triggerEvent = this.get('triggerEvent');
41932
41933            var indicatorModels = map(this.get('indicator') || [], function (indicatorOpt) {
41934                // PENDING
41935                if (indicatorOpt.max != null && indicatorOpt.max > 0 && !indicatorOpt.min) {
41936                    indicatorOpt.min = 0;
41937                }
41938                else if (indicatorOpt.min != null && indicatorOpt.min < 0 && !indicatorOpt.max) {
41939                    indicatorOpt.max = 0;
41940                }
41941                var iNameTextStyle = nameTextStyle;
41942                if(indicatorOpt.color != null) {
41943                    iNameTextStyle = defaults({color: indicatorOpt.color}, nameTextStyle);
41944                }
41945                // Use same configuration
41946                indicatorOpt = merge(clone(indicatorOpt), {
41947                    boundaryGap: boundaryGap,
41948                    splitNumber: splitNumber,
41949                    scale: scale,
41950                    axisLine: axisLine,
41951                    axisTick: axisTick,
41952                    axisLabel: axisLabel,
41953                    // Competitable with 2 and use text
41954                    name: indicatorOpt.text,
41955                    nameLocation: 'end',
41956                    nameGap: nameGap,
41957                    // min: 0,
41958                    nameTextStyle: iNameTextStyle,
41959                    triggerEvent: triggerEvent
41960                }, false);
41961                if (!showName) {
41962                    indicatorOpt.name = '';
41963                }
41964                if (typeof nameFormatter === 'string') {
41965                    var indName = indicatorOpt.name;
41966                    indicatorOpt.name = nameFormatter.replace('{value}', indName != null ? indName : '');
41967                }
41968                else if (typeof nameFormatter === 'function') {
41969                    indicatorOpt.name = nameFormatter(
41970                        indicatorOpt.name, indicatorOpt
41971                    );
41972                }
41973                var model = extend(
41974                    new Model(indicatorOpt, null, this.ecModel),
41975                    axisModelCommonMixin
41976                );
41977
41978                // For triggerEvent.
41979                model.mainType = 'radar';
41980                model.componentIndex = this.componentIndex;
41981
41982                return model;
41983            }, this);
41984
41985            this.getIndicatorModels = function () {
41986                return indicatorModels;
41987            };
41988        },
41989
41990        defaultOption: {
41991
41992            zlevel: 0,
41993
41994            z: 0,
41995
41996            center: ['50%', '50%'],
41997
41998            radius: '75%',
41999
42000            startAngle: 90,
42001
42002            name: {
42003                show: true
42004                // formatter: null
42005                // textStyle: {}
42006            },
42007
42008            boundaryGap: [0, 0],
42009
42010            splitNumber: 5,
42011
42012            nameGap: 15,
42013
42014            scale: false,
42015
42016            // Polygon or circle
42017            shape: 'polygon',
42018
42019            axisLine: merge(
42020                {
42021                    lineStyle: {
42022                        color: '#bbb'
42023                    }
42024                },
42025                valueAxisDefault.axisLine
42026            ),
42027            axisLabel: defaultsShow(valueAxisDefault.axisLabel, false),
42028            axisTick: defaultsShow(valueAxisDefault.axisTick, false),
42029            splitLine: defaultsShow(valueAxisDefault.splitLine, true),
42030            splitArea: defaultsShow(valueAxisDefault.splitArea, true),
42031
42032            // {text, min, max}
42033            indicator: []
42034        }
42035    });
42036
42037    /*
42038* Licensed to the Apache Software Foundation (ASF) under one
42039* or more contributor license agreements.  See the NOTICE file
42040* distributed with this work for additional information
42041* regarding copyright ownership.  The ASF licenses this file
42042* to you under the Apache License, Version 2.0 (the
42043* "License"); you may not use this file except in compliance
42044* with the License.  You may obtain a copy of the License at
42045*
42046*   http://www.apache.org/licenses/LICENSE-2.0
42047*
42048* Unless required by applicable law or agreed to in writing,
42049* software distributed under the License is distributed on an
42050* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
42051* KIND, either express or implied.  See the License for the
42052* specific language governing permissions and limitations
42053* under the License.
42054*/
42055
42056    var axisBuilderAttrs$1 = [
42057        'axisLine', 'axisTickLabel', 'axisName'
42058    ];
42059
42060    extendComponentView({
42061
42062        type: 'radar',
42063
42064        render: function (radarModel, ecModel, api) {
42065            var group = this.group;
42066            group.removeAll();
42067
42068            this._buildAxes(radarModel);
42069            this._buildSplitLineAndArea(radarModel);
42070        },
42071
42072        _buildAxes: function (radarModel) {
42073            var radar = radarModel.coordinateSystem;
42074            var indicatorAxes = radar.getIndicatorAxes();
42075            var axisBuilders = map(indicatorAxes, function (indicatorAxis) {
42076                var axisBuilder = new AxisBuilder(indicatorAxis.model, {
42077                    position: [radar.cx, radar.cy],
42078                    rotation: indicatorAxis.angle,
42079                    labelDirection: -1,
42080                    tickDirection: -1,
42081                    nameDirection: 1
42082                });
42083                return axisBuilder;
42084            });
42085
42086            each$1(axisBuilders, function (axisBuilder) {
42087                each$1(axisBuilderAttrs$1, axisBuilder.add, axisBuilder);
42088                this.group.add(axisBuilder.getGroup());
42089            }, this);
42090        },
42091
42092        _buildSplitLineAndArea: function (radarModel) {
42093            var radar = radarModel.coordinateSystem;
42094            var indicatorAxes = radar.getIndicatorAxes();
42095            if (!indicatorAxes.length) {
42096                return;
42097            }
42098            var shape = radarModel.get('shape');
42099            var splitLineModel = radarModel.getModel('splitLine');
42100            var splitAreaModel = radarModel.getModel('splitArea');
42101            var lineStyleModel = splitLineModel.getModel('lineStyle');
42102            var areaStyleModel = splitAreaModel.getModel('areaStyle');
42103
42104            var showSplitLine = splitLineModel.get('show');
42105            var showSplitArea = splitAreaModel.get('show');
42106            var splitLineColors = lineStyleModel.get('color');
42107            var splitAreaColors = areaStyleModel.get('color');
42108
42109            splitLineColors = isArray(splitLineColors) ? splitLineColors : [splitLineColors];
42110            splitAreaColors = isArray(splitAreaColors) ? splitAreaColors : [splitAreaColors];
42111
42112            var splitLines = [];
42113            var splitAreas = [];
42114
42115            function getColorIndex(areaOrLine, areaOrLineColorList, idx) {
42116                var colorIndex = idx % areaOrLineColorList.length;
42117                areaOrLine[colorIndex] = areaOrLine[colorIndex] || [];
42118                return colorIndex;
42119            }
42120
42121            if (shape === 'circle') {
42122                var ticksRadius = indicatorAxes[0].getTicksCoords();
42123                var cx = radar.cx;
42124                var cy = radar.cy;
42125                for (var i = 0; i < ticksRadius.length; i++) {
42126                    if (showSplitLine) {
42127                        var colorIndex = getColorIndex(splitLines, splitLineColors, i);
42128                        splitLines[colorIndex].push(new Circle({
42129                            shape: {
42130                                cx: cx,
42131                                cy: cy,
42132                                r: ticksRadius[i].coord
42133                            }
42134                        }));
42135                    }
42136                    if (showSplitArea && i < ticksRadius.length - 1) {
42137                        var colorIndex = getColorIndex(splitAreas, splitAreaColors, i);
42138                        splitAreas[colorIndex].push(new Ring({
42139                            shape: {
42140                                cx: cx,
42141                                cy: cy,
42142                                r0: ticksRadius[i].coord,
42143                                r: ticksRadius[i + 1].coord
42144                            }
42145                        }));
42146                    }
42147                }
42148            }
42149            // Polyyon
42150            else {
42151                var realSplitNumber;
42152                var axesTicksPoints = map(indicatorAxes, function (indicatorAxis, idx) {
42153                    var ticksCoords = indicatorAxis.getTicksCoords();
42154                    realSplitNumber = realSplitNumber == null
42155                        ? ticksCoords.length - 1
42156                        : Math.min(ticksCoords.length - 1, realSplitNumber);
42157                    return map(ticksCoords, function (tickCoord) {
42158                        return radar.coordToPoint(tickCoord.coord, idx);
42159                    });
42160                });
42161
42162                var prevPoints = [];
42163                for (var i = 0; i <= realSplitNumber; i++) {
42164                    var points = [];
42165                    for (var j = 0; j < indicatorAxes.length; j++) {
42166                        points.push(axesTicksPoints[j][i]);
42167                    }
42168                    // Close
42169                    if (points[0]) {
42170                        points.push(points[0].slice());
42171                    }
42172                    else {
42173                        if (__DEV__) {
42174                            console.error('Can\'t draw value axis ' + i);
42175                        }
42176                    }
42177
42178                    if (showSplitLine) {
42179                        var colorIndex = getColorIndex(splitLines, splitLineColors, i);
42180                        splitLines[colorIndex].push(new Polyline({
42181                            shape: {
42182                                points: points
42183                            }
42184                        }));
42185                    }
42186                    if (showSplitArea && prevPoints) {
42187                        var colorIndex = getColorIndex(splitAreas, splitAreaColors, i - 1);
42188                        splitAreas[colorIndex].push(new Polygon({
42189                            shape: {
42190                                points: points.concat(prevPoints)
42191                            }
42192                        }));
42193                    }
42194                    prevPoints = points.slice().reverse();
42195                }
42196            }
42197
42198            var lineStyle = lineStyleModel.getLineStyle();
42199            var areaStyle = areaStyleModel.getAreaStyle();
42200            // Add splitArea before splitLine
42201            each$1(splitAreas, function (splitAreas, idx) {
42202                this.group.add(mergePath(
42203                    splitAreas, {
42204                        style: defaults({
42205                            stroke: 'none',
42206                            fill: splitAreaColors[idx % splitAreaColors.length]
42207                        }, areaStyle),
42208                        silent: true
42209                    }
42210                ));
42211            }, this);
42212
42213            each$1(splitLines, function (splitLines, idx) {
42214                this.group.add(mergePath(
42215                    splitLines, {
42216                        style: defaults({
42217                            fill: 'none',
42218                            stroke: splitLineColors[idx % splitLineColors.length]
42219                        }, lineStyle),
42220                        silent: true
42221                    }
42222                ));
42223            }, this);
42224
42225        }
42226    });
42227
42228    /*
42229* Licensed to the Apache Software Foundation (ASF) under one
42230* or more contributor license agreements.  See the NOTICE file
42231* distributed with this work for additional information
42232* regarding copyright ownership.  The ASF licenses this file
42233* to you under the Apache License, Version 2.0 (the
42234* "License"); you may not use this file except in compliance
42235* with the License.  You may obtain a copy of the License at
42236*
42237*   http://www.apache.org/licenses/LICENSE-2.0
42238*
42239* Unless required by applicable law or agreed to in writing,
42240* software distributed under the License is distributed on an
42241* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
42242* KIND, either express or implied.  See the License for the
42243* specific language governing permissions and limitations
42244* under the License.
42245*/
42246
42247    /*
42248* Licensed to the Apache Software Foundation (ASF) under one
42249* or more contributor license agreements.  See the NOTICE file
42250* distributed with this work for additional information
42251* regarding copyright ownership.  The ASF licenses this file
42252* to you under the Apache License, Version 2.0 (the
42253* "License"); you may not use this file except in compliance
42254* with the License.  You may obtain a copy of the License at
42255*
42256*   http://www.apache.org/licenses/LICENSE-2.0
42257*
42258* Unless required by applicable law or agreed to in writing,
42259* software distributed under the License is distributed on an
42260* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
42261* KIND, either express or implied.  See the License for the
42262* specific language governing permissions and limitations
42263* under the License.
42264*/
42265
42266    var RadarSeries = SeriesModel.extend({
42267
42268        type: 'series.radar',
42269
42270        dependencies: ['radar'],
42271
42272
42273        // Overwrite
42274        init: function (option) {
42275            RadarSeries.superApply(this, 'init', arguments);
42276
42277            // Enable legend selection for each data item
42278            // Use a function instead of direct access because data reference may changed
42279            this.legendDataProvider = function () {
42280                return this.getRawData();
42281            };
42282        },
42283
42284        getInitialData: function (option, ecModel) {
42285            return createListSimply(this, {
42286                generateCoord: 'indicator_',
42287                generateCoordCount: Infinity
42288            });
42289        },
42290
42291        formatTooltip: function (dataIndex) {
42292            var data = this.getData();
42293            var coordSys = this.coordinateSystem;
42294            var indicatorAxes = coordSys.getIndicatorAxes();
42295            var name = this.getData().getName(dataIndex);
42296            return encodeHTML(name === '' ? this.name : name) + '<br/>'
42297                + map(indicatorAxes, function (axis, idx) {
42298                    var val = data.get(data.mapDimension(axis.dim), dataIndex);
42299                    return encodeHTML(axis.name + ' : ' + val);
42300                }).join('<br />');
42301        },
42302
42303        defaultOption: {
42304            zlevel: 0,
42305            z: 2,
42306            coordinateSystem: 'radar',
42307            legendHoverLink: true,
42308            radarIndex: 0,
42309            lineStyle: {
42310                width: 2,
42311                type: 'solid'
42312            },
42313            label: {
42314                position: 'top'
42315            },
42316            // areaStyle: {
42317            // },
42318            // itemStyle: {}
42319            symbol: 'emptyCircle',
42320            symbolSize: 4
42321            // symbolRotate: null
42322        }
42323    });
42324
42325    /*
42326* Licensed to the Apache Software Foundation (ASF) under one
42327* or more contributor license agreements.  See the NOTICE file
42328* distributed with this work for additional information
42329* regarding copyright ownership.  The ASF licenses this file
42330* to you under the Apache License, Version 2.0 (the
42331* "License"); you may not use this file except in compliance
42332* with the License.  You may obtain a copy of the License at
42333*
42334*   http://www.apache.org/licenses/LICENSE-2.0
42335*
42336* Unless required by applicable law or agreed to in writing,
42337* software distributed under the License is distributed on an
42338* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
42339* KIND, either express or implied.  See the License for the
42340* specific language governing permissions and limitations
42341* under the License.
42342*/
42343
42344    function normalizeSymbolSize(symbolSize) {
42345        if (!isArray(symbolSize)) {
42346            symbolSize = [+symbolSize, +symbolSize];
42347        }
42348        return symbolSize;
42349    }
42350
42351    extendChartView({
42352
42353        type: 'radar',
42354
42355        render: function (seriesModel, ecModel, api) {
42356            var polar = seriesModel.coordinateSystem;
42357            var group = this.group;
42358
42359            var data = seriesModel.getData();
42360            var oldData = this._data;
42361
42362            function createSymbol$$1(data, idx) {
42363                var symbolType = data.getItemVisual(idx, 'symbol') || 'circle';
42364                var color = data.getItemVisual(idx, 'color');
42365                if (symbolType === 'none') {
42366                    return;
42367                }
42368                var symbolSize = normalizeSymbolSize(
42369                    data.getItemVisual(idx, 'symbolSize')
42370                );
42371                var symbolPath = createSymbol(
42372                    symbolType, -1, -1, 2, 2, color
42373                );
42374                symbolPath.attr({
42375                    style: {
42376                        strokeNoScale: true
42377                    },
42378                    z2: 100,
42379                    scale: [symbolSize[0] / 2, symbolSize[1] / 2]
42380                });
42381                return symbolPath;
42382            }
42383
42384            function updateSymbols(oldPoints, newPoints, symbolGroup, data, idx, isInit) {
42385                // Simply rerender all
42386                symbolGroup.removeAll();
42387                for (var i = 0; i < newPoints.length - 1; i++) {
42388                    var symbolPath = createSymbol$$1(data, idx);
42389                    if (symbolPath) {
42390                        symbolPath.__dimIdx = i;
42391                        if (oldPoints[i]) {
42392                            symbolPath.attr('position', oldPoints[i]);
42393                            graphic[isInit ? 'initProps' : 'updateProps'](
42394                                symbolPath, {
42395                                    position: newPoints[i]
42396                                }, seriesModel, idx
42397                            );
42398                        }
42399                        else {
42400                            symbolPath.attr('position', newPoints[i]);
42401                        }
42402                        symbolGroup.add(symbolPath);
42403                    }
42404                }
42405            }
42406
42407            function getInitialPoints(points) {
42408                return map(points, function (pt) {
42409                    return [polar.cx, polar.cy];
42410                });
42411            }
42412            data.diff(oldData)
42413                .add(function (idx) {
42414                    var points = data.getItemLayout(idx);
42415                    if (!points) {
42416                        return;
42417                    }
42418                    var polygon = new Polygon();
42419                    var polyline = new Polyline();
42420                    var target = {
42421                        shape: {
42422                            points: points
42423                        }
42424                    };
42425                    polygon.shape.points = getInitialPoints(points);
42426                    polyline.shape.points = getInitialPoints(points);
42427                    initProps(polygon, target, seriesModel, idx);
42428                    initProps(polyline, target, seriesModel, idx);
42429
42430                    var itemGroup = new Group();
42431                    var symbolGroup = new Group();
42432                    itemGroup.add(polyline);
42433                    itemGroup.add(polygon);
42434                    itemGroup.add(symbolGroup);
42435
42436                    updateSymbols(
42437                        polyline.shape.points, points, symbolGroup, data, idx, true
42438                    );
42439
42440                    data.setItemGraphicEl(idx, itemGroup);
42441                })
42442                .update(function (newIdx, oldIdx) {
42443                    var itemGroup = oldData.getItemGraphicEl(oldIdx);
42444                    var polyline = itemGroup.childAt(0);
42445                    var polygon = itemGroup.childAt(1);
42446                    var symbolGroup = itemGroup.childAt(2);
42447                    var target = {
42448                        shape: {
42449                            points: data.getItemLayout(newIdx)
42450                        }
42451                    };
42452                    if (!target.shape.points) {
42453                        return;
42454                    }
42455                    updateSymbols(
42456                        polyline.shape.points, target.shape.points, symbolGroup, data, newIdx, false
42457                    );
42458
42459                    updateProps(polyline, target, seriesModel);
42460                    updateProps(polygon, target, seriesModel);
42461
42462                    data.setItemGraphicEl(newIdx, itemGroup);
42463                })
42464                .remove(function (idx) {
42465                    group.remove(oldData.getItemGraphicEl(idx));
42466                })
42467                .execute();
42468
42469            data.eachItemGraphicEl(function (itemGroup, idx) {
42470                var itemModel = data.getItemModel(idx);
42471                var polyline = itemGroup.childAt(0);
42472                var polygon = itemGroup.childAt(1);
42473                var symbolGroup = itemGroup.childAt(2);
42474                var color = data.getItemVisual(idx, 'color');
42475
42476                group.add(itemGroup);
42477
42478                polyline.useStyle(
42479                    defaults(
42480                        itemModel.getModel('lineStyle').getLineStyle(),
42481                        {
42482                            fill: 'none',
42483                            stroke: color
42484                        }
42485                    )
42486                );
42487                polyline.hoverStyle = itemModel.getModel('emphasis.lineStyle').getLineStyle();
42488
42489                var areaStyleModel = itemModel.getModel('areaStyle');
42490                var hoverAreaStyleModel = itemModel.getModel('emphasis.areaStyle');
42491                var polygonIgnore = areaStyleModel.isEmpty() && areaStyleModel.parentModel.isEmpty();
42492                var hoverPolygonIgnore = hoverAreaStyleModel.isEmpty() && hoverAreaStyleModel.parentModel.isEmpty();
42493
42494                hoverPolygonIgnore = hoverPolygonIgnore && polygonIgnore;
42495                polygon.ignore = polygonIgnore;
42496
42497                polygon.useStyle(
42498                    defaults(
42499                        areaStyleModel.getAreaStyle(),
42500                        {
42501                            fill: color,
42502                            opacity: 0.7
42503                        }
42504                    )
42505                );
42506                polygon.hoverStyle = hoverAreaStyleModel.getAreaStyle();
42507
42508                var itemStyle = itemModel.getModel('itemStyle').getItemStyle(['color']);
42509                var itemHoverStyle = itemModel.getModel('emphasis.itemStyle').getItemStyle();
42510                var labelModel = itemModel.getModel('label');
42511                var labelHoverModel = itemModel.getModel('emphasis.label');
42512                symbolGroup.eachChild(function (symbolPath) {
42513                    symbolPath.setStyle(itemStyle);
42514                    symbolPath.hoverStyle = clone(itemHoverStyle);
42515
42516                    setLabelStyle(
42517                        symbolPath.style, symbolPath.hoverStyle, labelModel, labelHoverModel,
42518                        {
42519                            labelFetcher: data.hostModel,
42520                            labelDataIndex: idx,
42521                            labelDimIndex: symbolPath.__dimIdx,
42522                            defaultText: data.get(data.dimensions[symbolPath.__dimIdx], idx),
42523                            autoColor: color,
42524                            isRectText: true
42525                        }
42526                    );
42527                });
42528
42529                function onEmphasis() {
42530                    polygon.attr('ignore', hoverPolygonIgnore);
42531                }
42532
42533                function onNormal() {
42534                    polygon.attr('ignore', polygonIgnore);
42535                }
42536
42537                itemGroup.off('mouseover').off('mouseout').off('normal').off('emphasis');
42538                itemGroup.on('emphasis', onEmphasis)
42539                    .on('mouseover', onEmphasis)
42540                    .on('normal', onNormal)
42541                    .on('mouseout', onNormal);
42542
42543                setHoverStyle(itemGroup);
42544            });
42545
42546            this._data = data;
42547        },
42548
42549        remove: function () {
42550            this.group.removeAll();
42551            this._data = null;
42552        },
42553
42554        dispose: function () {}
42555    });
42556
42557    /*
42558* Licensed to the Apache Software Foundation (ASF) under one
42559* or more contributor license agreements.  See the NOTICE file
42560* distributed with this work for additional information
42561* regarding copyright ownership.  The ASF licenses this file
42562* to you under the Apache License, Version 2.0 (the
42563* "License"); you may not use this file except in compliance
42564* with the License.  You may obtain a copy of the License at
42565*
42566*   http://www.apache.org/licenses/LICENSE-2.0
42567*
42568* Unless required by applicable law or agreed to in writing,
42569* software distributed under the License is distributed on an
42570* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
42571* KIND, either express or implied.  See the License for the
42572* specific language governing permissions and limitations
42573* under the License.
42574*/
42575
42576
42577    var radarLayout = function (ecModel) {
42578        ecModel.eachSeriesByType('radar', function (seriesModel) {
42579            var data = seriesModel.getData();
42580            var points = [];
42581            var coordSys = seriesModel.coordinateSystem;
42582            if (!coordSys) {
42583                return;
42584            }
42585
42586            function pointsConverter(val, idx) {
42587                points[idx] = points[idx] || [];
42588                points[idx][i] = coordSys.dataToPoint(val, i);
42589            }
42590            var axes = coordSys.getIndicatorAxes();
42591            for (var i = 0; i < axes.length; i++) {
42592                data.each(data.mapDimension(axes[i].dim), pointsConverter);
42593            }
42594
42595            data.each(function (idx) {
42596                // Close polygon
42597                points[idx][0] && points[idx].push(points[idx][0].slice());
42598                data.setItemLayout(idx, points[idx]);
42599            });
42600        });
42601    };
42602
42603    /*
42604* Licensed to the Apache Software Foundation (ASF) under one
42605* or more contributor license agreements.  See the NOTICE file
42606* distributed with this work for additional information
42607* regarding copyright ownership.  The ASF licenses this file
42608* to you under the Apache License, Version 2.0 (the
42609* "License"); you may not use this file except in compliance
42610* with the License.  You may obtain a copy of the License at
42611*
42612*   http://www.apache.org/licenses/LICENSE-2.0
42613*
42614* Unless required by applicable law or agreed to in writing,
42615* software distributed under the License is distributed on an
42616* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
42617* KIND, either express or implied.  See the License for the
42618* specific language governing permissions and limitations
42619* under the License.
42620*/
42621
42622// Backward compat for radar chart in 2
42623    var backwardCompat$1 = function (option) {
42624        var polarOptArr = option.polar;
42625        if (polarOptArr) {
42626            if (!isArray(polarOptArr)) {
42627                polarOptArr = [polarOptArr];
42628            }
42629            var polarNotRadar = [];
42630            each$1(polarOptArr, function (polarOpt, idx) {
42631                if (polarOpt.indicator) {
42632                    if (polarOpt.type && !polarOpt.shape) {
42633                        polarOpt.shape = polarOpt.type;
42634                    }
42635                    option.radar = option.radar || [];
42636                    if (!isArray(option.radar)) {
42637                        option.radar = [option.radar];
42638                    }
42639                    option.radar.push(polarOpt);
42640                }
42641                else {
42642                    polarNotRadar.push(polarOpt);
42643                }
42644            });
42645            option.polar = polarNotRadar;
42646        }
42647        each$1(option.series, function (seriesOpt) {
42648            if (seriesOpt && seriesOpt.type === 'radar' && seriesOpt.polarIndex) {
42649                seriesOpt.radarIndex = seriesOpt.polarIndex;
42650            }
42651        });
42652    };
42653
42654    /*
42655* Licensed to the Apache Software Foundation (ASF) under one
42656* or more contributor license agreements.  See the NOTICE file
42657* distributed with this work for additional information
42658* regarding copyright ownership.  The ASF licenses this file
42659* to you under the Apache License, Version 2.0 (the
42660* "License");
42660 you may not use this file except in compliance
42661* with the License.  You may obtain a copy of the License at
42662*
42663*   http://www.apache.org/licenses/LICENSE-2.0
42664*
42665* Unless required by applicable law or agreed to in writing,
42666* software distributed under the License is distributed on an
42667* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
42668* KIND, either express or implied.  See the License for the
42669* specific language governing permissions and limitations
42670* under the License.
42671*/
42672
42673
42674// Must use radar component
42675    registerVisual(dataColor('radar'));
42676    registerVisual(visualSymbol('radar', 'circle'));
42677    registerLayout(radarLayout);
42678    registerProcessor(dataFilter('radar'));
42679    registerPreprocessor(backwardCompat$1);
42680
42681    /*
42682* Licensed to the Apache Software Foundation (ASF) under one
42683* or more contributor license agreements.  See the NOTICE file
42684* distributed with this work for additional information
42685* regarding copyright ownership.  The ASF licenses this file
42686* to you under the Apache License, Version 2.0 (the
42687* "License"); you may not use this file except in compliance
42688* with the License.  You may obtain a copy of the License at
42689*
42690*   http://www.apache.org/licenses/LICENSE-2.0
42691*
42692* Unless required by applicable law or agreed to in writing,
42693* software distributed under the License is distributed on an
42694* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
42695* KIND, either express or implied.  See the License for the
42696* specific language governing permissions and limitations
42697* under the License.
42698*/
42699
42700    /**
42701     * Simple view coordinate system
42702     * Mapping given x, y to transformd view x, y
42703     */
42704
42705    var v2ApplyTransform$1 = applyTransform;
42706
42707// Dummy transform node
42708    function TransformDummy() {
42709        Transformable.call(this);
42710    }
42711    mixin(TransformDummy, Transformable);
42712
42713    function View(name) {
42714        /**
42715         * @type {string}
42716         */
42717        this.name = name;
42718
42719        /**
42720         * @type {Object}
42721         */
42722        this.zoomLimit;
42723
42724        Transformable.call(this);
42725
42726        this._roamTransformable = new TransformDummy();
42727
42728        this._rawTransformable = new TransformDummy();
42729
42730        this._center;
42731        this._zoom;
42732    }
42733
42734    View.prototype = {
42735
42736        constructor: View,
42737
42738        type: 'view',
42739
42740        /**
42741         * @param {Array.<string>}
42742         * @readOnly
42743         */
42744        dimensions: ['x', 'y'],
42745
42746        /**
42747         * Set bounding rect
42748         * @param {number} x
42749         * @param {number} y
42750         * @param {number} width
42751         * @param {number} height
42752         */
42753
42754        // PENDING to getRect
42755        setBoundingRect: function (x, y, width, height) {
42756            this._rect = new BoundingRect(x, y, width, height);
42757            return this._rect;
42758        },
42759
42760        /**
42761         * @return {module:zrender/core/BoundingRect}
42762         */
42763        // PENDING to getRect
42764        getBoundingRect: function () {
42765            return this._rect;
42766        },
42767
42768        /**
42769         * @param {number} x
42770         * @param {number} y
42771         * @param {number} width
42772         * @param {number} height
42773         */
42774        setViewRect: function (x, y, width, height) {
42775            this.transformTo(x, y, width, height);
42776            this._viewRect = new BoundingRect(x, y, width, height);
42777        },
42778
42779        /**
42780         * Transformed to particular position and size
42781         * @param {number} x
42782         * @param {number} y
42783         * @param {number} width
42784         * @param {number} height
42785         */
42786        transformTo: function (x, y, width, height) {
42787            var rect = this.getBoundingRect();
42788            var rawTransform = this._rawTransformable;
42789
42790            rawTransform.transform = rect.calculateTransform(
42791                new BoundingRect(x, y, width, height)
42792            );
42793
42794            rawTransform.decomposeTransform();
42795
42796            this._updateTransform();
42797        },
42798
42799        /**
42800         * Set center of view
42801         * @param {Array.<number>} [centerCoord]
42802         */
42803        setCenter: function (centerCoord) {
42804            if (!centerCoord) {
42805                return;
42806            }
42807            this._center = centerCoord;
42808
42809            this._updateCenterAndZoom();
42810        },
42811
42812        /**
42813         * @param {number} zoom
42814         */
42815        setZoom: function (zoom) {
42816            zoom = zoom || 1;
42817
42818            var zoomLimit = this.zoomLimit;
42819            if (zoomLimit) {
42820                if (zoomLimit.max != null) {
42821                    zoom = Math.min(zoomLimit.max, zoom);
42822                }
42823                if (zoomLimit.min != null) {
42824                    zoom = Math.max(zoomLimit.min, zoom);
42825                }
42826            }
42827            this._zoom = zoom;
42828
42829            this._updateCenterAndZoom();
42830        },
42831
42832        /**
42833         * Get default center without roam
42834         */
42835        getDefaultCenter: function () {
42836            // Rect before any transform
42837            var rawRect = this.getBoundingRect();
42838            var cx = rawRect.x + rawRect.width / 2;
42839            var cy = rawRect.y + rawRect.height / 2;
42840
42841            return [cx, cy];
42842        },
42843
42844        getCenter: function () {
42845            return this._center || this.getDefaultCenter();
42846        },
42847
42848        getZoom: function () {
42849            return this._zoom || 1;
42850        },
42851
42852        /**
42853         * @return {Array.<number}
42854         */
42855        getRoamTransform: function () {
42856            return this._roamTransformable.getLocalTransform();
42857        },
42858
42859        /**
42860         * Remove roam
42861         */
42862
42863        _updateCenterAndZoom: function () {
42864            // Must update after view transform updated
42865            var rawTransformMatrix = this._rawTransformable.getLocalTransform();
42866            var roamTransform = this._roamTransformable;
42867            var defaultCenter = this.getDefaultCenter();
42868            var center = this.getCenter();
42869            var zoom = this.getZoom();
42870
42871            center = applyTransform([], center, rawTransformMatrix);
42872            defaultCenter = applyTransform([], defaultCenter, rawTransformMatrix);
42873
42874            roamTransform.origin = center;
42875            roamTransform.position = [
42876                defaultCenter[0] - center[0],
42877                defaultCenter[1] - center[1]
42878            ];
42879            roamTransform.scale = [zoom, zoom];
42880
42881            this._updateTransform();
42882        },
42883
42884        /**
42885         * Update transform from roam and mapLocation
42886         * @private
42887         */
42888        _updateTransform: function () {
42889            var roamTransformable = this._roamTransformable;
42890            var rawTransformable = this._rawTransformable;
42891
42892            rawTransformable.parent = roamTransformable;
42893            roamTransformable.updateTransform();
42894            rawTransformable.updateTransform();
42895
42896            copy$1(this.transform || (this.transform = []), rawTransformable.transform || create$1());
42897
42898            this._rawTransform = rawTransformable.getLocalTransform();
42899
42900            this.invTransform = this.invTransform || [];
42901            invert(this.invTransform, this.transform);
42902
42903            this.decomposeTransform();
42904        },
42905
42906        /**
42907         * @return {module:zrender/core/BoundingRect}
42908         */
42909        getViewRect: function () {
42910            return this._viewRect;
42911        },
42912
42913        /**
42914         * Get view rect after roam transform
42915         * @return {module:zrender/core/BoundingRect}
42916         */
42917        getViewRectAfterRoam: function () {
42918            var rect = this.getBoundingRect().clone();
42919            rect.applyTransform(this.transform);
42920            return rect;
42921        },
42922
42923        /**
42924         * Convert a single (lon, lat) data item to (x, y) point.
42925         * @param {Array.<number>} data
42926         * @param {boolean} noRoam
42927         * @param {Array.<number>} [out]
42928         * @return {Array.<number>}
42929         */
42930        dataToPoint: function (data, noRoam, out) {
42931            var transform = noRoam ? this._rawTransform : this.transform;
42932            out = out || [];
42933            return transform
42934                ? v2ApplyTransform$1(out, data, transform)
42935                : copy(out, data);
42936        },
42937
42938        /**
42939         * Convert a (x, y) point to (lon, lat) data
42940         * @param {Array.<number>} point
42941         * @return {Array.<number>}
42942         */
42943        pointToData: function (point) {
42944            var invTransform = this.invTransform;
42945            return invTransform
42946                ? v2ApplyTransform$1([], point, invTransform)
42947                : [point[0], point[1]];
42948        },
42949
42950        /**
42951         * @implements
42952         * see {module:echarts/CoodinateSystem}
42953         */
42954        convertToPixel: curry(doConvert$1, 'dataToPoint'),
42955
42956        /**
42957         * @implements
42958         * see {module:echarts/CoodinateSystem}
42959         */
42960        convertFromPixel: curry(doConvert$1, 'pointToData'),
42961
42962        /**
42963         * @implements
42964         * see {module:echarts/CoodinateSystem}
42965         */
42966        containPoint: function (point) {
42967            return this.getViewRectAfterRoam().contain(point[0], point[1]);
42968        }
42969
42970        /**
42971         * @return {number}
42972         */
42973        // getScalarScale: function () {
42974        //     // Use determinant square root of transform to mutiply scalar
42975        //     var m = this.transform;
42976        //     var det = Math.sqrt(Math.abs(m[0] * m[3] - m[2] * m[1]));
42977        //     return det;
42978        // }
42979    };
42980
42981    mixin(View, Transformable);
42982
42983    function doConvert$1(methodName, ecModel, finder, value) {
42984        var seriesModel = finder.seriesModel;
42985        var coordSys = seriesModel ? seriesModel.coordinateSystem : null; // e.g., graph.
42986        return coordSys === this ? coordSys[methodName](value) : null;
42987    }
42988
42989    /*
42990* Licensed to the Apache Software Foundation (ASF) under one
42991* or more contributor license agreements.  See the NOTICE file
42992* distributed with this work for additional information
42993* regarding copyright ownership.  The ASF licenses this file
42994* to you under the Apache License, Version 2.0 (the
42995* "License");
42995 you may not use this file except in compliance
42996* with the License.  You may obtain a copy of the License at
42997*
42998*   http://www.apache.org/licenses/LICENSE-2.0
42999*
43000* Unless required by applicable law or agreed to in writing,
43001* software distributed under the License is distributed on an
43002* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
43003* KIND, either express or implied.  See the License for the
43004* specific language governing permissions and limitations
43005* under the License.
43006*/
43007
43008// Fix for 南海诸岛
43009
43010    var geoCoord = [126, 25];
43011
43012    var points$1 = [
43013        [[0,3.5],[7,11.2],[15,11.9],[30,7],[42,0.7],[52,0.7],
43014            [56,7.7],[59,0.7],[64,0.7],[64,0],[5,0],[0,3.5]],
43015        [[13,16.1],[19,14.7],[16,21.7],[11,23.1],[13,16.1]],
43016        [[12,32.2],[14,38.5],[15,38.5],[13,32.2],[12,32.2]],
43017        [[16,47.6],[12,53.2],[13,53.2],[18,47.6],[16,47.6]],
43018        [[6,64.4],[8,70],[9,70],[8,64.4],[6,64.4]],
43019        [[23,82.6],[29,79.8],[30,79.8],[25,82.6],[23,82.6]],
43020        [[37,70.7],[43,62.3],[44,62.3],[39,70.7],[37,70.7]],
43021        [[48,51.1],[51,45.5],[53,45.5],[50,51.1],[48,51.1]],
43022        [[51,35],[51,28.7],[53,28.7],[53,35],[51,35]],
43023        [[52,22.4],[55,17.5],[56,17.5],[53,22.4],[52,22.4]],
43024        [[58,12.6],[62,7],[63,7],[60,12.6],[58,12.6]],
43025        [[0,3.5],[0,93.1],[64,93.1],[64,0],[63,0],[63,92.4],
43026            [1,92.4],[1,3.5],[0,3.5]]
43027    ];
43028
43029    for (var i$1 = 0; i$1 < points$1.length; i$1++) {
43030        for (var k = 0; k < points$1[i$1].length; k++) {
43031            points$1[i$1][k][0] /= 10.5;
43032            points$1[i$1][k][1] /= -10.5 / 0.75;
43033
43034            points$1[i$1][k][0] += geoCoord[0];
43035            points$1[i$1][k][1] += geoCoord[1];
43036        }
43037    }
43038
43039    var fixNanhai = function (geo) {
43040        if (geo.map === 'china') {
43041            geo.regions.push(new Region(
43042                '南海诸岛',
43043                map(points$1, function (exterior) {
43044                    return {
43045                        type: 'polygon',
43046                        exterior: exterior
43047                    };
43048                }), geoCoord
43049            ));
43050        }
43051    };
43052
43053    /*
43054* Licensed to the Apache Software Foundation (ASF) under one
43055* or more contributor license agreements.  See the NOTICE file
43056* distributed with this work for additional information
43057* regarding copyright ownership.  The ASF licenses this file
43058* to you under the Apache License, Version 2.0 (the
43059* "License"); you may not use this file except in compliance
43060* with the License.  You may obtain a copy of the License at
43061*
43062*   http://www.apache.org/licenses/LICENSE-2.0
43063*
43064* Unless required by applicable law or agreed to in writing,
43065* software distributed under the License is distributed on an
43066* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
43067* KIND, either express or implied.  See the License for the
43068* specific language governing permissions and limitations
43069* under the License.
43070*/
43071
43072    var coordsOffsetMap = {
43073        '南海诸岛' : [32, 80],
43074        // 全国
43075        '广东': [0, -10],
43076        '香港': [10, 5],
43077        '澳门': [-10, 10],
43078        //'北京': [-10, 0],
43079        '天津': [5, 5]
43080    };
43081
43082    var fixTextCoord = function (geo) {
43083        each$1(geo.regions, function (region) {
43084            var coordFix = coordsOffsetMap[region.name];
43085            if (coordFix) {
43086                var cp = region.center;
43087                cp[0] += coordFix[0] / 10.5;
43088                cp[1] += -coordFix[1] / (10.5 / 0.75);
43089            }
43090        });
43091    };
43092
43093    /*
43094* Licensed to the Apache Software Foundation (ASF) under one
43095* or more contributor license agreements.  See the NOTICE file
43096* distributed with this work for additional information
43097* regarding copyright ownership.  The ASF licenses this file
43098* to you under the Apache License, Version 2.0 (the
43099* "License"); you may not use this file except in compliance
43100* with the License.  You may obtain a copy of the License at
43101*
43102*   http://www.apache.org/licenses/LICENSE-2.0
43103*
43104* Unless required by applicable law or agreed to in writing,
43105* software distributed under the License is distributed on an
43106* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
43107* KIND, either express or implied.  See the License for the
43108* specific language governing permissions and limitations
43109* under the License.
43110*/
43111
43112    var geoCoordMap = {
43113        'Russia': [100, 60],
43114        'United States': [-99, 38],
43115        'United States of America': [-99, 38]
43116    };
43117
43118    var fixGeoCoord = function (geo) {
43119        each$1(geo.regions, function (region) {
43120            var geoCoord = geoCoordMap[region.name];
43121            if (geoCoord) {
43122                var cp = region.center;
43123                cp[0] = geoCoord[0];
43124                cp[1] = geoCoord[1];
43125            }
43126        });
43127    };
43128
43129    /*
43130* Licensed to the Apache Software Foundation (ASF) under one
43131* or more contributor license agreements.  See the NOTICE file
43132* distributed with this work for additional information
43133* regarding copyright ownership.  The ASF licenses this file
43134* to you under the Apache License, Version 2.0 (the
43135* "License");
43135 you may not use this file except in compliance
43136* with the License.  You may obtain a copy of the License at
43137*
43138*   http://www.apache.org/licenses/LICENSE-2.0
43139*
43140* Unless required by applicable law or agreed to in writing,
43141* software distributed under the License is distributed on an
43142* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
43143* KIND, either express or implied.  See the License for the
43144* specific language governing permissions and limitations
43145* under the License.
43146*/
43147
43148// Fix for 钓鱼岛
43149
43150// var Region = require('../Region');
43151// var zrUtil = require('zrender/src/core/util');
43152
43153// var geoCoord = [126, 25];
43154
43155    var points$2 = [
43156        [
43157            [123.45165252685547, 25.73527164402261],
43158            [123.49731445312499, 25.73527164402261],
43159            [123.49731445312499, 25.750734064600884],
43160            [123.45165252685547, 25.750734064600884],
43161            [123.45165252685547, 25.73527164402261]
43162        ]
43163    ];
43164
43165    var fixDiaoyuIsland = function (geo) {
43166        if (geo.map === 'china') {
43167            for (var i = 0, len = geo.regions.length; i < len; ++i) {
43168                if (geo.regions[i].name === '台湾') {
43169                    geo.regions[i].geometries.push({
43170                        type: 'polygon',
43171                        exterior: points$2[0]
43172                    });
43173                }
43174            }
43175        }
43176    };
43177
43178    /*
43179* Licensed to the Apache Software Foundation (ASF) under one
43180* or more contributor license agreements.  See the NOTICE file
43181* distributed with this work for additional information
43182* regarding copyright ownership.  The ASF licenses this file
43183* to you under the Apache License, Version 2.0 (the
43184* "License"); you may not use this file except in compliance
43185* with the License.  You may obtain a copy of the License at
43186*
43187*   http://www.apache.org/licenses/LICENSE-2.0
43188*
43189* Unless required by applicable law or agreed to in writing,
43190* software distributed under the License is distributed on an
43191* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
43192* KIND, either express or implied.  See the License for the
43193* specific language governing permissions and limitations
43194* under the License.
43195*/
43196
43197// Geo fix functions
43198    var geoFixFuncs = [
43199        fixNanhai,
43200        fixTextCoord,
43201        fixGeoCoord,
43202        fixDiaoyuIsland
43203    ];
43204
43205    /**
43206     * [Geo description]
43207     * @param {string} name Geo name
43208     * @param {string} map Map type
43209     * @param {Object} geoJson
43210     * @param {Object} [specialAreas]
43211     *        Specify the positioned areas by left, top, width, height
43212     * @param {Object.<string, string>} [nameMap]
43213     *        Specify name alias
43214     */
43215    function Geo(name, map$$1, geoJson, specialAreas, nameMap) {
43216
43217        View.call(this, name);
43218
43219        /**
43220         * Map type
43221         * @type {string}
43222         */
43223        this.map = map$$1;
43224
43225        this._nameCoordMap = createHashMap();
43226
43227        this.loadGeoJson(geoJson, specialAreas, nameMap);
43228    }
43229
43230    Geo.prototype = {
43231
43232        constructor: Geo,
43233
43234        type: 'geo',
43235
43236        /**
43237         * @param {Array.<string>}
43238         * @readOnly
43239         */
43240        dimensions: ['lng', 'lat'],
43241
43242        /**
43243         * If contain given lng,lat coord
43244         * @param {Array.<number>}
43245         * @readOnly
43246         */
43247        containCoord: function (coord) {
43248            var regions = this.regions;
43249            for (var i = 0; i < regions.length; i++) {
43250                if (regions[i].contain(coord)) {
43251                    return true;
43252                }
43253            }
43254            return false;
43255        },
43256        /**
43257         * @param {Object} geoJson
43258         * @param {Object} [specialAreas]
43259         *        Specify the positioned areas by left, top, width, height
43260         * @param {Object.<string, string>} [nameMap]
43261         *        Specify name alias
43262         */
43263        loadGeoJson: function (geoJson, specialAreas, nameMap) {
43264            // https://jsperf.com/try-catch-performance-overhead
43265            try {
43266                this.regions = geoJson ? parseGeoJson$1(geoJson) : [];
43267            }
43268            catch (e) {
43269                throw 'Invalid geoJson format\n' + e.message;
43270            }
43271            specialAreas = specialAreas || {};
43272            nameMap = nameMap || {};
43273            var regions = this.regions;
43274            var regionsMap = createHashMap();
43275            for (var i = 0; i < regions.length; i++) {
43276                var regionName = regions[i].name;
43277                // Try use the alias in nameMap
43278                regionName = nameMap.hasOwnProperty(regionName) ? nameMap[regionName] : regionName;
43279                regions[i].name = regionName;
43280
43281                regionsMap.set(regionName, regions[i]);
43282                // Add geoJson
43283                this.addGeoCoord(regionName, regions[i].center);
43284
43285                // Some area like Alaska in USA map needs to be tansformed
43286                // to look better
43287                var specialArea = specialAreas[regionName];
43288                if (specialArea) {
43289                    regions[i].transformTo(
43290                        specialArea.left, specialArea.top, specialArea.width, specialArea.height
43291                    );
43292                }
43293            }
43294
43295            this._regionsMap = regionsMap;
43296
43297            this._rect = null;
43298
43299            each$1(geoFixFuncs, function (fixFunc) {
43300                fixFunc(this);
43301            }, this);
43302        },
43303
43304        // Overwrite
43305        transformTo: function (x, y, width, height) {
43306            var rect = this.getBoundingRect();
43307
43308            rect = rect.clone();
43309            // Longitute is inverted
43310            rect.y = -rect.y - rect.height;
43311
43312            var rawTransformable = this._rawTransformable;
43313
43314            rawTransformable.transform = rect.calculateTransform(
43315                new BoundingRect(x, y, width, height)
43316            );
43317
43318            rawTransformable.decomposeTransform();
43319
43320            var scale = rawTransformable.scale;
43321            scale[1] = -scale[1];
43322
43323            rawTransformable.updateTransform();
43324
43325            this._updateTransform();
43326        },
43327
43328        /**
43329         * @param {string} name
43330         * @return {module:echarts/coord/geo/Region}
43331         */
43332        getRegion: function (name) {
43333            return this._regionsMap.get(name);
43334        },
43335
43336        getRegionByCoord: function (coord) {
43337            var regions = this.regions;
43338            for (var i = 0; i < regions.length; i++) {
43339                if (regions[i].contain(coord)) {
43340                    return regions[i];
43341                }
43342            }
43343        },
43344
43345        /**
43346         * Add geoCoord for indexing by name
43347         * @param {string} name
43348         * @param {Array.<number>} geoCoord
43349         */
43350        addGeoCoord: function (name, geoCoord) {
43351            this._nameCoordMap.set(name, geoCoord);
43352        },
43353
43354        /**
43355         * Get geoCoord by name
43356         * @param {string} name
43357         * @return {Array.<number>}
43358         */
43359        getGeoCoord: function (name) {
43360            return this._nameCoordMap.get(name);
43361        },
43362
43363        // Overwrite
43364        getBoundingRect: function () {
43365            if (this._rect) {
43366                return this._rect;
43367            }
43368            var rect;
43369
43370            var regions = this.regions;
43371            for (var i = 0; i < regions.length; i++) {
43372                var regionRect = regions[i].getBoundingRect();
43373                rect = rect || regionRect.clone();
43374                rect.union(regionRect);
43375            }
43376            // FIXME Always return new ?
43377            return (this._rect = rect || new BoundingRect(0, 0, 0, 0));
43378        },
43379
43380        /**
43381         * @param {string|Array.<number>} data
43382         * @param {boolean} noRoam
43383         * @param {Array.<number>} [out]
43384         * @return {Array.<number>}
43385         */
43386        dataToPoint: function (data, noRoam, out) {
43387            if (typeof data === 'string') {
43388                // Map area name to geoCoord
43389                data = this.getGeoCoord(data);
43390            }
43391            if (data) {
43392                return View.prototype.dataToPoint.call(this, data, noRoam, out);
43393            }
43394        },
43395
43396        /**
43397         * @inheritDoc
43398         */
43399        convertToPixel: curry(doConvert, 'dataToPoint'),
43400
43401        /**
43402         * @inheritDoc
43403         */
43404        convertFromPixel: curry(doConvert, 'pointToData')
43405
43406    };
43407
43408    mixin(Geo, View);
43409
43410    function doConvert(methodName, ecModel, finder, value) {
43411        var geoModel = finder.geoModel;
43412        var seriesModel = finder.seriesModel;
43413
43414        var coordSys = geoModel
43415            ? geoModel.coordinateSystem
43416            : seriesModel
43417                ? (
43418                    seriesModel.coordinateSystem // For map.
43419                    || (seriesModel.getReferringComponents('geo')[0] || {}).coordinateSystem
43420                )
43421                : null;
43422
43423        return coordSys === this ? coordSys[methodName](value) : null;
43424    }
43425
43426    /*
43427* Licensed to the Apache Software Foundation (ASF) under one
43428* or more contributor license agreements.  See the NOTICE file
43429* distributed with this work for additional information
43430* regarding copyright ownership.  The ASF licenses this file
43431* to you under the Apache License, Version 2.0 (the
43432* "License");
43432 you may not use this file except in compliance
43433* with the License.  You may obtain a copy of the License at
43434*
43435*   http://www.apache.org/licenses/LICENSE-2.0
43436*
43437* Unless required by applicable law or agreed to in writing,
43438* software distributed under the License is distributed on an
43439* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
43440* KIND, either express or implied.  See the License for the
43441* specific language governing permissions and limitations
43442* under the License.
43443*/
43444
43445    /**
43446     * Resize method bound to the geo
43447     * @param {module:echarts/coord/geo/GeoModel|module:echarts/chart/map/MapModel} geoModel
43448     * @param {module:echarts/ExtensionAPI} api
43449     */
43450    function resizeGeo(geoModel, api) {
43451
43452        var boundingCoords = geoModel.get('boundingCoords');
43453        if (boundingCoords != null) {
43454            var leftTop = boundingCoords[0];
43455            var rightBottom = boundingCoords[1];
43456            if (isNaN(leftTop[0]) || isNaN(leftTop[1]) || isNaN(rightBottom[0]) || isNaN(rightBottom[1])) {
43457                if (__DEV__) {
43458                    console.error('Invalid boundingCoords');
43459                }
43460            }
43461            else {
43462                this.setBoundingRect(leftTop[0], leftTop[1], rightBottom[0] - leftTop[0], rightBottom[1] - leftTop[1]);
43463            }
43464        }
43465
43466        var rect = this.getBoundingRect();
43467
43468        var boxLayoutOption;
43469
43470        var center = geoModel.get('layoutCenter');
43471        var size = geoModel.get('layoutSize');
43472
43473        var viewWidth = api.getWidth();
43474        var viewHeight = api.getHeight();
43475
43476        var aspectScale = geoModel.get('aspectScale') || 0.75;
43477        var aspect = rect.width / rect.height * aspectScale;
43478
43479        var useCenterAndSize = false;
43480
43481        if (center && size) {
43482            center = [
43483                parsePercent$1(center[0], viewWidth),
43484                parsePercent$1(center[1], viewHeight)
43485            ];
43486            size = parsePercent$1(size, Math.min(viewWidth, viewHeight));
43487
43488            if (!isNaN(center[0]) && !isNaN(center[1]) && !isNaN(size)) {
43489                useCenterAndSize = true;
43490            }
43491            else {
43492                if (__DEV__) {
43493                    console.warn('Given layoutCenter or layoutSize data are invalid. Use left/top/width/height instead.');
43494                }
43495            }
43496        }
43497
43498        var viewRect;
43499        if (useCenterAndSize) {
43500            var viewRect = {};
43501            if (aspect > 1) {
43502                // Width is same with size
43503                viewRect.width = size;
43504                viewRect.height = size / aspect;
43505            }
43506            else {
43507                viewRect.height = size;
43508                viewRect.width = size * aspect;
43509            }
43510            viewRect.y = center[1] - viewRect.height / 2;
43511            viewRect.x = center[0] - viewRect.width / 2;
43512        }
43513        else {
43514            // Use left/top/width/height
43515            boxLayoutOption = geoModel.getBoxLayoutParams();
43516
43517            // 0.75 rate
43518            boxLayoutOption.aspect = aspect;
43519
43520            viewRect = getLayoutRect(boxLayoutOption, {
43521                width: viewWidth,
43522                height: viewHeight
43523            });
43524        }
43525
43526        this.setViewRect(viewRect.x, viewRect.y, viewRect.width, viewRect.height);
43527
43528        this.setCenter(geoModel.get('center'));
43529        this.setZoom(geoModel.get('zoom'));
43530    }
43531
43532    /**
43533     * @param {module:echarts/coord/Geo} geo
43534     * @param {module:echarts/model/Model} model
43535     * @inner
43536     */
43537    function setGeoCoords(geo, model) {
43538        each$1(model.get('geoCoord'), function (geoCoord, name) {
43539            geo.addGeoCoord(name, geoCoord);
43540        });
43541    }
43542
43543    if (__DEV__) {
43544        var mapNotExistsError = function (name) {
43545            console.error('Map ' + name + ' not exists. You can download map file on http://echarts.baidu.com/download-map.html');
43546        };
43547    }
43548
43549    var geoCreator = {
43550
43551        // For deciding which dimensions to use when creating list data
43552        dimensions: Geo.prototype.dimensions,
43553
43554        create: function (ecModel, api) {
43555            var geoList = [];
43556
43557            // FIXME Create each time may be slow
43558            ecModel.eachComponent('geo', function (geoModel, idx) {
43559                var name = geoModel.get('map');
43560                var mapData = getMap(name);
43561                if (__DEV__) {
43562                    if (!mapData) {
43563                        mapNotExistsError(name);
43564                    }
43565                }
43566                var geo = new Geo(
43567                    name + idx, name,
43568                    mapData && mapData.geoJson, mapData && mapData.specialAreas,
43569                    geoModel.get('nameMap')
43570                );
43571                geo.zoomLimit = geoModel.get('scaleLimit');
43572                geoList.push(geo);
43573
43574                setGeoCoords(geo, geoModel);
43575
43576                geoModel.coordinateSystem = geo;
43577                geo.model = geoModel;
43578
43579                // Inject resize method
43580                geo.resize = resizeGeo;
43581
43582                geo.resize(geoModel, api);
43583            });
43584
43585            ecModel.eachSeries(function (seriesModel) {
43586                var coordSys = seriesModel.get('coordinateSystem');
43587                if (coordSys === 'geo') {
43588                    var geoIndex = seriesModel.get('geoIndex') || 0;
43589                    seriesModel.coordinateSystem = geoList[geoIndex];
43590                }
43591            });
43592
43593            // If has map series
43594            var mapModelGroupBySeries = {};
43595
43596            ecModel.eachSeriesByType('map', function (seriesModel) {
43597                if (!seriesModel.getHostGeoModel()) {
43598                    var mapType = seriesModel.getMapType();
43599                    mapModelGroupBySeries[mapType] = mapModelGroupBySeries[mapType] || [];
43600                    mapModelGroupBySeries[mapType].push(seriesModel);
43601                }
43602            });
43603
43604            each$1(mapModelGroupBySeries, function (mapSeries, mapType) {
43605                var mapData = getMap(mapType);
43606                if (__DEV__) {
43607                    if (!mapData) {
43608                        mapNotExistsError(mapSeries[0].get('map'));
43609                    }
43610                }
43611
43612                var nameMapList = map(mapSeries, function (singleMapSeries) {
43613                    return singleMapSeries.get('nameMap');
43614                });
43615                var geo = new Geo(
43616                    mapType, mapType,
43617                    mapData && mapData.geoJson, mapData && mapData.specialAreas,
43618                    mergeAll(nameMapList)
43619                );
43620                geo.zoomLimit = retrieve.apply(null, map(mapSeries, function (singleMapSeries) {
43621                    return singleMapSeries.get('scaleLimit');
43622                }));
43623                geoList.push(geo);
43624
43625                // Inject resize method
43626                geo.resize = resizeGeo;
43627
43628                geo.resize(mapSeries[0], api);
43629
43630                each$1(mapSeries, function (singleMapSeries) {
43631                    singleMapSeries.coordinateSystem = geo;
43632
43633                    setGeoCoords(geo, singleMapSeries);
43634                });
43635            });
43636
43637            return geoList;
43638        },
43639
43640        /**
43641         * Fill given regions array
43642         * @param  {Array.<Object>} originRegionArr
43643         * @param  {string} mapName
43644         * @param  {Object} [nameMap]
43645         * @return {Array}
43646         */
43647        getFilledRegions: function (originRegionArr, mapName, nameMap) {
43648            // Not use the original
43649            var regionsArr = (originRegionArr || []).slice();
43650            nameMap = nameMap || {};
43651
43652            var map$$1 = getMap(mapName);
43653            var geoJson = map$$1 && map$$1.geoJson;
43654            if (!geoJson) {
43655                if (__DEV__) {
43656                    mapNotExistsError(mapName);
43657                }
43658                return originRegionArr;
43659            }
43660
43661            var dataNameMap = createHashMap();
43662            var features = geoJson.features;
43663            for (var i = 0; i < regionsArr.length; i++) {
43664                dataNameMap.set(regionsArr[i].name, regionsArr[i]);
43665            }
43666
43667            for (var i = 0; i < features.length; i++) {
43668                var name = features[i].properties.name;
43669                if (!dataNameMap.get(name)) {
43670                    if (nameMap.hasOwnProperty(name)) {
43671                        name = nameMap[name];
43672                    }
43673                    regionsArr.push({
43674                        name: name
43675                    });
43676                }
43677            }
43678            return regionsArr;
43679        }
43680    };
43681
43682    registerCoordinateSystem('geo', geoCreator);
43683
43684    /*
43685* Licensed to the Apache Software Foundation (ASF) under one
43686* or more contributor license agreements.  See the NOTICE file
43687* distributed with this work for additional information
43688* regarding copyright ownership.  The ASF licenses this file
43689* to you under the Apache License, Version 2.0 (the
43690* "License"); you may not use this file except in compliance
43691* with the License.  You may obtain a copy of the License at
43692*
43693*   http://www.apache.org/licenses/LICENSE-2.0
43694*
43695* Unless required by applicable law or agreed to in writing,
43696* software distributed under the License is distributed on an
43697* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
43698* KIND, either express or implied.  See the License for the
43699* specific language governing permissions and limitations
43700* under the License.
43701*/
43702
43703    var MapSeries = SeriesModel.extend({
43704
43705        type: 'series.map',
43706
43707        dependencies: ['geo'],
43708
43709        layoutMode: 'box',
43710
43711        /**
43712         * Only first map series of same mapType will drawMap
43713         * @type {boolean}
43714         */
43715        needsDrawMap: false,
43716
43717        /**
43718         * Group of all map series with same mapType
43719         * @type {boolean}
43720         */
43721        seriesGroup: [],
43722
43723        init: function (option) {
43724
43725            // this._fillOption(option, this.getMapType());
43726            // this.option = option;
43727
43728            MapSeries.superApply(this, 'init', arguments);
43729
43730            this.updateSelectedMap(this._createSelectableList());
43731        },
43732
43733        getInitialData: function (option) {
43734            return createListSimply(this, ['value']);
43735        },
43736
43737        mergeOption: function (newOption) {
43738            // this._fillOption(newOption, this.getMapType());
43739
43740            MapSeries.superApply(this, 'mergeOption', arguments);
43741
43742            this.updateSelectedMap(this._createSelectableList());
43743        },
43744
43745        _createSelectableList: function () {
43746            var data = this.getRawData();
43747            var valueDim = data.mapDimension('value');
43748            var targetList = [];
43749            for (var i = 0, len = data.count(); i < len; i++) {
43750                targetList.push({
43751                    name: data.getName(i),
43752                    value: data.get(valueDim, i),
43753                    selected: retrieveRawAttr(data, i, 'selected')
43754                });
43755            }
43756
43757            targetList = geoCreator.getFilledRegions(targetList, this.getMapType(), this.option.nameMap);
43758
43759            return targetList;
43760        },
43761
43762        /**
43763         * If no host geo model, return null, which means using a
43764         * inner exclusive geo model.
43765         */
43766        getHostGeoModel: function () {
43767            var geoIndex = this.option.geoIndex;
43768            return geoIndex != null
43769                ? this.dependentModels.geo[geoIndex]
43770                : null;
43771        },
43772
43773        getMapType: function () {
43774            return (this.getHostGeoModel() || this).option.map;
43775        },
43776
43777        _fillOption: function (option, mapName) {
43778            // Shallow clone
43779            // option = zrUtil.extend({}, option);
43780
43781            // option.data = geoCreator.getFilledRegions(option.data, mapName, option.nameMap);
43782
43783            // return option;
43784        },
43785
43786        getRawValue: function (dataIndex) {
43787            // Use value stored in data instead because it is calculated from multiple series
43788            // FIXME Provide all value of multiple series ?
43789            var data = this.getData();
43790            return data.get(data.mapDimension('value'), dataIndex);
43791        },
43792
43793        /**
43794         * Get model of region
43795         * @param  {string} name
43796         * @return {module:echarts/model/Model}
43797         */
43798        getRegionModel: function (regionName) {
43799            var data = this.getData();
43800            return data.getItemModel(data.indexOfName(regionName));
43801        },
43802
43803        /**
43804         * Map tooltip formatter
43805         *
43806         * @param {number} dataIndex
43807         */
43808        formatTooltip: function (dataIndex) {
43809            // FIXME orignalData and data is a bit confusing
43810            var data = this.getData();
43811            var formattedValue = addCommas(this.getRawValue(dataIndex));
43812            var name = data.getName(dataIndex);
43813
43814            var seriesGroup = this.seriesGroup;
43815            var seriesNames = [];
43816            for (var i = 0; i < seriesGroup.length; i++) {
43817                var otherIndex = seriesGroup[i].originalData.indexOfName(name);
43818                var valueDim = data.mapDimension('value');
43819                if (!isNaN(seriesGroup[i].originalData.get(valueDim, otherIndex))) {
43820                    seriesNames.push(
43821                        encodeHTML(seriesGroup[i].name)
43822                    );
43823                }
43824            }
43825
43826            return seriesNames.join(', ') + '<br />'
43827                + encodeHTML(name + ' : ' + formattedValue);
43828        },
43829
43830        /**
43831         * @implement
43832         */
43833        getTooltipPosition: function (dataIndex) {
43834            if (dataIndex != null) {
43835                var name = this.getData().getName(dataIndex);
43836                var geo = this.coordinateSystem;
43837                var region = geo.getRegion(name);
43838
43839                return region && geo.dataToPoint(region.center);
43840            }
43841        },
43842
43843        setZoom: function (zoom) {
43844            this.option.zoom = zoom;
43845        },
43846
43847        setCenter: function (center) {
43848            this.option.center = center;
43849        },
43850
43851        defaultOption: {
43852            // 一级层å
43853            zlevel: 0,
43854            // 二级层å
43855            z: 2,
43856
43857            coordinateSystem: 'geo',
43858
43859            // map should be explicitly specified since ec3.
43860            map: '',
43861
43862            // If `geoIndex` is not specified, a exclusive geo will be
43863            // created. Otherwise use the specified geo component, and
43864            // `map` and `mapType` are ignored.
43865            // geoIndex: 0,
43866
43867            // 'center' | 'left' | 'right' | 'x%' | {number}
43868            left: 'center',
43869            // 'center' | 'top' | 'bottom' | 'x%' | {number}
43870            top: 'center',
43871            // right
43872            // bottom
43873            // width:
43874            // height
43875
43876            // Aspect is width / height. Inited to be geoJson bbox aspect
43877            // This parameter is used for scale this aspect
43878            aspectScale: 0.75,
43879
43880            ///// Layout with center and size
43881            // If you wan't to put map in a fixed size box with right aspect ratio
43882            // This two properties may more conveninet
43883            // layoutCenter: [50%, 50%]
43884            // layoutSize: 100
43885
43886
43887            // 数值合并方式,默èÂ
43887®Â¤Ã¥Å  和,可选为:
43888            // 'sum' | 'average' | 'max' | 'min'
43889            // mapValueCalculation: 'sum',
43890            // 地图数值计算结果小数精度
43891            // mapValuePrecision: 0,
43892
43893
43894            // æ˜¾ç¤ºå›¾ä¾‹é¢œè‰²æ ‡è¯†ï¼ˆç³»åˆ—æ ‡è¯†çš„å°åœ†ç‚¹ï¼‰ï¼Œå›¾ä¾‹å¼€å¯æ—¶æœ‰æ•ˆ
43895            showLegendSymbol: true,
43896            // 选择模式,默认关闭,可选single,multiple
43897            // selectedMode: false,
43898            dataRangeHoverLink: true,
43899            // 是否开启缩放及漫游模式
43900            // roam: false,
43901
43902            // Define left-top, right-bottom coords to control view
43903            // For example, [ [180, 90], [-180, -90] ],
43904            // higher priority than center and zoom
43905            boundingCoords: null,
43906
43907            // Default on center of map
43908            center: null,
43909
43910            zoom: 1,
43911
43912            scaleLimit: null,
43913
43914            label: {
43915                show: false,
43916                color: '#000'
43917            },
43918            // scaleLimit: null,
43919            itemStyle: {
43920                borderWidth: 0.5,
43921                borderColor: '#444',
43922                areaColor: '#eee'
43923            },
43924
43925            emphasis: {
43926                label: {
43927                    show: true,
43928                    color: 'rgb(100,0,0)'
43929                },
43930                itemStyle: {
43931                    areaColor: 'rgba(255,215,0,0.8)'
43932                }
43933            }
43934        }
43935
43936    });
43937
43938    mixin(MapSeries, selectableMixin);
43939
43940    /*
43941* Licensed to the Apache Software Foundation (ASF) under one
43942* or more contributor license agreements.  See the NOTICE file
43943* distributed with this work for additional information
43944* regarding copyright ownership.  The ASF licenses this file
43945* to you under the Apache License, Version 2.0 (the
43946* "License"); you may not use this file except in compliance
43947* with the License.  You may obtain a copy of the License at
43948*
43949*   http://www.apache.org/licenses/LICENSE-2.0
43950*
43951* Unless required by applicable law or agreed to in writing,
43952* software distributed under the License is distributed on an
43953* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
43954* KIND, either express or implied.  See the License for the
43955* specific language governing permissions and limitations
43956* under the License.
43957*/
43958
43959    var ATTR = '\0_ec_interaction_mutex';
43960
43961    function take(zr, resourceKey, userKey) {
43962        var store = getStore(zr);
43963        store[resourceKey] = userKey;
43964    }
43965
43966    function release(zr, resourceKey, userKey) {
43967        var store = getStore(zr);
43968        var uKey = store[resourceKey];
43969
43970        if (uKey === userKey) {
43971            store[resourceKey] = null;
43972        }
43973    }
43974
43975    function isTaken(zr, resourceKey) {
43976        return !!getStore(zr)[resourceKey];
43977    }
43978
43979    function getStore(zr) {
43980        return zr[ATTR] || (zr[ATTR] = {});
43981    }
43982
43983    /**
43984     * payload: {
43985 *     type: 'takeGlobalCursor',
43986 *     key: 'dataZoomSelect', or 'brush', or ...,
43987 *         If no userKey, release global cursor.
43988 * }
43989     */
43990    registerAction(
43991        {type: 'takeGlobalCursor', event: 'globalCursorTaken', update: 'update'},
43992        function () {}
43993    );
43994
43995    /*
43996* Licensed to the Apache Software Foundation (ASF) under one
43997* or more contributor license agreements.  See the NOTICE file
43998* distributed with this work for additional information
43999* regarding copyright ownership.  The ASF licenses this file
44000* to you under the Apache License, Version 2.0 (the
44001* "License"); you may not use this file except in compliance
44002* with the License.  You may obtain a copy of the License at
44003*
44004*   http://www.apache.org/licenses/LICENSE-2.0
44005*
44006* Unless required by applicable law or agreed to in writing,
44007* software distributed under the License is distributed on an
44008* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
44009* KIND, either express or implied.  See the License for the
44010* specific language governing permissions and limitations
44011* under the License.
44012*/
44013
44014    /**
44015     * @alias module:echarts/component/helper/RoamController
44016     * @constructor
44017     * @mixin {module:zrender/mixin/Eventful}
44018     *
44019     * @param {module:zrender/zrender~ZRender} zr
44020     */
44021    function RoamController(zr) {
44022
44023        /**
44024         * @type {Function}
44025         */
44026        this.pointerChecker;
44027
44028        /**
44029         * @type {module:zrender}
44030         */
44031        this._zr = zr;
44032
44033        /**
44034         * @type {Object}
44035         */
44036        this._opt = {};
44037
44038        // Avoid two roamController bind the same handler
44039        var bind$$1 = bind;
44040        var mousedownHandler = bind$$1(mousedown, this);
44041        var mousemoveHandler = bind$$1(mousemove, this);
44042        var mouseupHandler = bind$$1(mouseup, this);
44043        var mousewheelHandler = bind$$1(mousewheel, this);
44044        var pinchHandler = bind$$1(pinch, this);
44045
44046        Eventful.call(this);
44047
44048        /**
44049         * @param {Function} pointerChecker
44050         *                   input: x, y
44051         *                   output: boolean
44052         */
44053        this.setPointerChecker = function (pointerChecker) {
44054            this.pointerChecker = pointerChecker;
44055        };
44056
44057        /**
44058         * Notice: only enable needed types. For example, if 'zoom'
44059         * is not needed, 'zoom' should not be enabled, otherwise
44060         * default mousewheel behaviour (scroll page) will be disabled.
44061         *
44062         * @param  {boolean|string} [controlType=true] Specify the control type,
44063         *                          which can be null/undefined or true/false
44064         *                          or 'pan/move' or 'zoom'/'scale'
44065         * @param {Object} [opt]
44066         * @param {Object} [opt.zoomOnMouseWheel=true]
44067         * @param {Object} [opt.moveOnMouseMove=true]
44068         * @param {Object} [opt.preventDefaultMouseMove=true] When pan.
44069         */
44070        this.enable = function (controlType, opt) {
44071
44072            // Disable previous first
44073            this.disable();
44074
44075            this._opt = defaults(clone(opt) || {}, {
44076                zoomOnMouseWheel: true,
44077                moveOnMouseMove: true,
44078                preventDefaultMouseMove: true
44079            });
44080
44081            if (controlType == null) {
44082                controlType = true;
44083            }
44084
44085            if (controlType === true || (controlType === 'move' || controlType === 'pan')) {
44086                zr.on('mousedown', mousedownHandler);
44087                zr.on('mousemove', mousemoveHandler);
44088                zr.on('mouseup', mouseupHandler);
44089            }
44090            if (controlType === true || (controlType === 'scale' || controlType === 'zoom')) {
44091                zr.on('mousewheel', mousewheelHandler);
44092                zr.on('pinch', pinchHandler);
44093            }
44094        };
44095
44096        this.disable = function () {
44097            zr.off('mousedown', mousedownHandler);
44098            zr.off('mousemove', mousemoveHandler);
44099            zr.off('mouseup', mouseupHandler);
44100            zr.off('mousewheel', mousewheelHandler);
44101            zr.off('pinch', pinchHandler);
44102        };
44103
44104        this.dispose = this.disable;
44105
44106        this.isDragging = function () {
44107            return this._dragging;
44108        };
44109
44110        this.isPinching = function () {
44111            return this._pinching;
44112        };
44113    }
44114
44115    mixin(RoamController, Eventful);
44116
44117
44118    function mousedown(e) {
44119        if (notLeftMouse(e)
44120            || (e.target && e.target.draggable)
44121        ) {
44122            return;
44123        }
44124
44125        var x = e.offsetX;
44126        var y = e.offsetY;
44127
44128        // Only check on mosedown, but not mousemove.
44129        // Mouse can be out of target when mouse moving.
44130        if (this.pointerChecker && this.pointerChecker(e, x, y)) {
44131            this._x = x;
44132            this._y = y;
44133            this._dragging = true;
44134        }
44135    }
44136
44137    function mousemove(e) {
44138        if (notLeftMouse(e)
44139            || !checkKeyBinding(this, 'moveOnMouseMove', e)
44140            || !this._dragging
44141            || e.gestureEvent === 'pinch'
44142            || isTaken(this._zr, 'globalPan')
44143        ) {
44144            return;
44145        }
44146
44147        var x = e.offsetX;
44148        var y = e.offsetY;
44149
44150        var oldX = this._x;
44151        var oldY = this._y;
44152
44153        var dx = x - oldX;
44154        var dy = y - oldY;
44155
44156        this._x = x;
44157        this._y = y;
44158
44159        this._opt.preventDefaultMouseMove && stop(e.event);
44160
44161        this.trigger('pan', dx, dy, oldX, oldY, x, y);
44162    }
44163
44164    function mouseup(e) {
44165        if (!notLeftMouse(e)) {
44166            this._dragging = false;
44167        }
44168    }
44169
44170    function mousewheel(e) {
44171        // wheelDelta maybe -0 in chrome mac.
44172        if (!checkKeyBinding(this, 'zoomOnMouseWheel', e) || e.wheelDelta === 0) {
44173            return;
44174        }
44175
44176        // Convenience:
44177        // Mac and VM Windows on Mac: scroll up: zoom out.
44178        // Windows: scroll up: zoom in.
44179        var zoomDelta = e.wheelDelta > 0 ? 1.1 : 1 / 1.1;
44180        zoom.call(this, e, zoomDelta, e.offsetX, e.offsetY);
44181    }
44182
44183    function pinch(e) {
44184        if (isTaken(this._zr, 'globalPan')) {
44185            return;
44186        }
44187        var zoomDelta = e.pinchScale > 1 ? 1.1 : 1 / 1.1;
44188        zoom.call(this, e, zoomDelta, e.pinchX, e.pinchY);
44189    }
44190
44191    function zoom(e, zoomDelta, zoomX, zoomY) {
44192        if (this.pointerChecker && this.pointerChecker(e, zoomX, zoomY)) {
44193            // When mouse is out of roamController rect,
44194            // default befavoius should not be be disabled, otherwise
44195            // page sliding is disabled, contrary to expectation.
44196            stop(e.event);
44197
44198            this.trigger('zoom', zoomDelta, zoomX, zoomY);
44199        }
44200    }
44201
44202    function checkKeyBinding(roamController, prop, e) {
44203        var setting = roamController._opt[prop];
44204        return setting
44205            && (!isString(setting) || e.event[setting + 'Key']);
44206    }
44207
44208    /*
44209* Licensed to the Apache Software Foundation (ASF) under one
44210* or more contributor license agreements.  See the NOTICE file
44211* distributed with this work for additional information
44212* regarding copyright ownership.  The ASF licenses this file
44213* to you under the Apache License, Version 2.0 (the
44214* "License"); you may not use this file except in compliance
44215* with the License.  You may obtain a copy of the License at
44216*
44217*   http://www.apache.org/licenses/LICENSE-2.0
44218*
44219* Unless required by applicable law or agreed to in writing,
44220* software distributed under the License is distributed on an
44221* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
44222* KIND, either express or implied.  See the License for the
44223* specific language governing permissions and limitations
44224* under the License.
44225*/
44226
44227
44228    /**
44229     * For geo and graph.
44230     *
44231     * @param {Object} controllerHost
44232     * @param {module:zrender/Element} controllerHost.target
44233     */
44234    function updateViewOnPan(controllerHost, dx, dy) {
44235        var target = controllerHost.target;
44236        var pos = target.position;
44237        pos[0] += dx;
44238        pos[1] += dy;
44239        target.dirty();
44240    }
44241
44242    /**
44243     * For geo and graph.
44244     *
44245     * @param {Object} controllerHost
44246     * @param {module:zrender/Element} controllerHost.target
44247     * @param {number} controllerHost.zoom
44248     * @param {number} controllerHost.zoomLimit like: {min: 1, max: 2}
44249     */
44250    function updateViewOnZoom(controllerHost, zoomDelta, zoomX, zoomY) {
44251        var target = controllerHost.target;
44252        var zoomLimit = controllerHost.zoomLimit;
44253        var pos = target.position;
44254        var scale = target.scale;
44255
44256        var newZoom = controllerHost.zoom = controllerHost.zoom || 1;
44257        newZoom *= zoomDelta;
44258        if (zoomLimit) {
44259            var zoomMin = zoomLimit.min || 0;
44260            var zoomMax = zoomLimit.max || Infinity;
44261            newZoom = Math.max(
44262                Math.min(zoomMax, newZoom),
44263                zoomMin
44264            );
44265        }
44266        var zoomScale = newZoom / controllerHost.zoom;
44267        controllerHost.zoom = newZoom;
44268        // Keep the mouse center when scaling
44269        pos[0] -= (zoomX - pos[0]) * (zoomScale - 1);
44270        pos[1] -= (zoomY - pos[1]) * (zoomScale - 1);
44271        scale[0] *= zoomScale;
44272        scale[1] *= zoomScale;
44273
44274        target.dirty();
44275    }
44276
44277    /*
44278* Licensed to the Apache Software Foundation (ASF) under one
44279* or more contributor license agreements.  See the NOTICE file
44280* distributed with this work for additional information
44281* regarding copyright ownership.  The ASF licenses this file
44282* to you under the Apache License, Version 2.0 (the
44283* "License"); you may not use this file except in compliance
44284* with the License.  You may obtain a copy of the License at
44285*
44286*   http://www.apache.org/licenses/LICENSE-2.0
44287*
44288* Unless required by applicable law or agreed to in writing,
44289* software distributed under the License is distributed on an
44290* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
44291* KIND, either express or implied.  See the License for the
44292* specific language governing permissions and limitations
44293* under the License.
44294*/
44295
44296
44297    var IRRELEVANT_EXCLUDES = {'axisPointer': 1, 'tooltip': 1, 'brush': 1};
44298
44299    /**
44300     * Avoid that: mouse click on a elements that is over geo or graph,
44301     * but roam is triggered.
44302     */
44303    function onIrrelevantElement(e, api, targetCoordSysModel) {
44304        var model = api.getComponentByElement(e.topTarget);
44305        // If model is axisModel, it works only if it is injected with coordinateSystem.
44306        var coordSys = model && model.coordinateSystem;
44307        return model
44308            && model !== targetCoordSysModel
44309            && !IRRELEVANT_EXCLUDES[model.mainType]
44310            && (coordSys && coordSys.model !== targetCoordSysModel);
44311    }
44312
44313    /*
44314* Licensed to the Apache Software Foundation (ASF) under one
44315* or more contributor license agreements.  See the NOTICE file
44316* distributed with this work for additional information
44317* regarding copyright ownership.  The ASF licenses this file
44318* to you under the Apache License, Version 2.0 (the
44319* "License");
44319 you may not use this file except in compliance
44320* with the License.  You may obtain a copy of the License at
44321*
44322*   http://www.apache.org/licenses/LICENSE-2.0
44323*
44324* Unless required by applicable law or agreed to in writing,
44325* software distributed under the License is distributed on an
44326* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
44327* KIND, either express or implied.  See the License for the
44328* specific language governing permissions and limitations
44329* under the License.
44330*/
44331
44332    function getFixedItemStyle(model, scale) {
44333        var itemStyle = model.getItemStyle();
44334        var areaColor = model.get('areaColor');
44335
44336        // If user want the color not to be changed when hover,
44337        // they should both set areaColor and color to be null.
44338        if (areaColor != null) {
44339            itemStyle.fill = areaColor;
44340        }
44341
44342        return itemStyle;
44343    }
44344
44345    function updateMapSelectHandler(mapDraw, mapOrGeoModel, group, api, fromView) {
44346        group.off('click');
44347        group.off('mousedown');
44348
44349        if (mapOrGeoModel.get('selectedMode')) {
44350
44351            group.on('mousedown', function () {
44352                mapDraw._mouseDownFlag = true;
44353            });
44354
44355            group.on('click', function (e) {
44356                if (!mapDraw._mouseDownFlag) {
44357                    return;
44358                }
44359                mapDraw._mouseDownFlag = false;
44360
44361                var el = e.target;
44362                while (!el.__regions) {
44363                    el = el.parent;
44364                }
44365                if (!el) {
44366                    return;
44367                }
44368
44369                var action = {
44370                    type: (mapOrGeoModel.mainType === 'geo' ? 'geo' : 'map') + 'ToggleSelect',
44371                    batch: map(el.__regions, function (region) {
44372                        return {
44373                            name: region.name,
44374                            from: fromView.uid
44375                        };
44376                    })
44377                };
44378                action[mapOrGeoModel.mainType + 'Id'] = mapOrGeoModel.id;
44379
44380                api.dispatchAction(action);
44381
44382                updateMapSelected(mapOrGeoModel, group);
44383            });
44384        }
44385    }
44386
44387    function updateMapSelected(mapOrGeoModel, group) {
44388        // FIXME
44389        group.eachChild(function (otherRegionEl) {
44390            each$1(otherRegionEl.__regions, function (region) {
44391                otherRegionEl.trigger(mapOrGeoModel.isSelected(region.name) ? 'emphasis' : 'normal');
44392            });
44393        });
44394    }
44395
44396    /**
44397     * @alias module:echarts/component/helper/MapDraw
44398     * @param {module:echarts/ExtensionAPI} api
44399     * @param {boolean} updateGroup
44400     */
44401    function MapDraw(api, updateGroup) {
44402
44403        var group = new Group();
44404
44405        /**
44406         * @type {module:echarts/component/helper/RoamController}
44407         * @private
44408         */
44409        this._controller = new RoamController(api.getZr());
44410
44411        /**
44412         * @type {Object} {target, zoom, zoomLimit}
44413         * @private
44414         */
44415        this._controllerHost = {target: updateGroup ? group : null};
44416
44417        /**
44418         * @type {module:zrender/container/Group}
44419         * @readOnly
44420         */
44421        this.group = group;
44422
44423        /**
44424         * @type {boolean}
44425         * @private
44426         */
44427        this._updateGroup = updateGroup;
44428
44429        /**
44430         * This flag is used to make sure that only one among
44431         * `pan`, `zoom`, `click` can occurs, otherwise 'selected'
44432         * action may be triggered when `pan`, which is unexpected.
44433         * @type {booelan}
44434         */
44435        this._mouseDownFlag;
44436    }
44437
44438    MapDraw.prototype = {
44439
44440        constructor: MapDraw,
44441
44442        draw: function (mapOrGeoModel, ecModel, api, fromView, payload) {
44443
44444            var isGeo = mapOrGeoModel.mainType === 'geo';
44445
44446            // Map series has data. GEO model that controlled by map series
44447            // will be assigned with map data. Other GEO model has no data.
44448            var data = mapOrGeoModel.getData && mapOrGeoModel.getData();
44449            isGeo && ecModel.eachComponent({mainType: 'series', subType: 'map'}, function (mapSeries) {
44450                if (!data && mapSeries.getHostGeoModel() === mapOrGeoModel) {
44451                    data = mapSeries.getData();
44452                }
44453            });
44454
44455            var geo = mapOrGeoModel.coordinateSystem;
44456
44457            var group = this.group;
44458
44459            var scale = geo.scale;
44460            var groupNewProp = {
44461                position: geo.position,
44462                scale: scale
44463            };
44464
44465            // No animation when first draw or in action
44466            if (!group.childAt(0) || payload) {
44467                group.attr(groupNewProp);
44468            }
44469            else {
44470                updateProps(group, groupNewProp, mapOrGeoModel);
44471            }
44472
44473            group.removeAll();
44474
44475            var itemStyleAccessPath = ['itemStyle'];
44476            var hoverItemStyleAccessPath = ['emphasis', 'itemStyle'];
44477            var labelAccessPath = ['label'];
44478            var hoverLabelAccessPath = ['emphasis', 'label'];
44479            var nameMap = createHashMap();
44480
44481            each$1(geo.regions, function (region) {
44482
44483                // Consider in GeoJson properties.name may be duplicated, for example,
44484                // there is multiple region named "United Kindom" or "France" (so many
44485                // colonies). And it is not appropriate to merge them in geo, which
44486                // will make them share the same label and bring trouble in label
44487                // location calculation.
44488                var regionGroup = nameMap.get(region.name)
44489                    || nameMap.set(region.name, new Group());
44490
44491                var compoundPath = new CompoundPath({
44492                    shape: {
44493                        paths: []
44494                    }
44495                });
44496                regionGroup.add(compoundPath);
44497
44498                var regionModel = mapOrGeoModel.getRegionModel(region.name) || mapOrGeoModel;
44499
44500                var itemStyleModel = regionModel.getModel(itemStyleAccessPath);
44501                var hoverItemStyleModel = regionModel.getModel(hoverItemStyleAccessPath);
44502                var itemStyle = getFixedItemStyle(itemStyleModel, scale);
44503                var hoverItemStyle = getFixedItemStyle(hoverItemStyleModel, scale);
44504
44505                var labelModel = regionModel.getModel(labelAccessPath);
44506                var hoverLabelModel = regionModel.getModel(hoverLabelAccessPath);
44507
44508                var dataIdx;
44509                // Use the itemStyle in data if has data
44510                if (data) {
44511                    dataIdx = data.indexOfName(region.name);
44512                    // Only visual color of each item will be used. It can be encoded by dataRange
44513                    // But visual color of series is used in symbol drawing
44514                    //
44515                    // Visual color for each series is for the symbol draw
44516                    var visualColor = data.getItemVisual(dataIdx, 'color', true);
44517                    if (visualColor) {
44518                        itemStyle.fill = visualColor;
44519                    }
44520                }
44521
44522                each$1(region.geometries, function (geometry) {
44523                    if (geometry.type !== 'polygon') {
44524                        return;
44525                    }
44526                    compoundPath.shape.paths.push(new Polygon({
44527                        shape: {
44528                            points: geometry.exterior
44529                        }
44530                    }));
44531
44532                    for (var i = 0; i < (geometry.interiors ? geometry.interiors.length : 0); i++) {
44533                        compoundPath.shape.paths.push(new Polygon({
44534                            shape: {
44535                                points: geometry.interiors[i]
44536                            }
44537                        }));
44538                    }
44539                });
44540
44541                compoundPath.setStyle(itemStyle);
44542                compoundPath.style.strokeNoScale = true;
44543                compoundPath.culling = true;
44544                // Label
44545                var showLabel = labelModel.get('show');
44546                var hoverShowLabel = hoverLabelModel.get('show');
44547
44548                var isDataNaN = data && isNaN(data.get(data.mapDimension('value'), dataIdx));
44549                var itemLayout = data && data.getItemLayout(dataIdx);
44550                // In the following cases label will be drawn
44551                // 1. In map series and data value is NaN
44552                // 2. In geo component
44553                // 4. Region has no series legendSymbol, which will be add a showLabel flag in mapSymbol
44553Layout
44554                if (
44555                    (isGeo || isDataNaN && (showLabel || hoverShowLabel))
44556                    || (itemLayout && itemLayout.showLabel)
44557                ) {
44558                    var query = !isGeo ? dataIdx : region.name;
44559                    var labelFetcher;
44560
44561                    // Consider dataIdx not found.
44562                    if (!data || dataIdx >= 0) {
44563                        labelFetcher = mapOrGeoModel;
44564                    }
44565
44566                    var textEl = new Text({
44567                        position: region.center.slice(),
44568                        scale: [1 / scale[0], 1 / scale[1]],
44569                        z2: 10,
44570                        silent: true
44571                    });
44572
44573                    setLabelStyle(
44574                        textEl.style, textEl.hoverStyle = {}, labelModel, hoverLabelModel,
44575                        {
44576                            labelFetcher: labelFetcher,
44577                            labelDataIndex: query,
44578                            defaultText: region.name,
44579                            useInsideStyle: false
44580                        },
44581                        {
44582                            textAlign: 'center',
44583                            textVerticalAlign: 'middle'
44584                        }
44585                    );
44586
44587                    regionGroup.add(textEl);
44588                }
44589
44590                // setItemGraphicEl, setHoverStyle after all polygons and labels
44591                // are added to the rigionGroup
44592                if (data) {
44593                    data.setItemGraphicEl(dataIdx, regionGroup);
44594                }
44595                else {
44596                    var regionModel = mapOrGeoModel.getRegionModel(region.name);
44597                    // Package custom mouse event for geo component
44598                    compoundPath.eventData = {
44599                        componentType: 'geo',
44600                        geoIndex: mapOrGeoModel.componentIndex,
44601                        name: region.name,
44602                        region: (regionModel && regionModel.option) || {}
44603                    };
44604                }
44605
44606                var groupRegions = regionGroup.__regions || (regionGroup.__regions = []);
44607                groupRegions.push(region);
44608
44609                setHoverStyle(
44610                    regionGroup,
44611                    hoverItemStyle,
44612                    {hoverSilentOnTouch: !!mapOrGeoModel.get('selectedMode')}
44613                );
44614
44615                group.add(regionGroup);
44616            });
44617
44618            this._updateController(mapOrGeoModel, ecModel, api);
44619
44620            updateMapSelectHandler(this, mapOrGeoModel, group, api, fromView);
44621
44622            updateMapSelected(mapOrGeoModel, group);
44623        },
44624
44625        remove: function () {
44626            this.group.removeAll();
44627            this._controller.dispose();
44628            this._controllerHost = {};
44629        },
44630
44631        _updateController: function (mapOrGeoModel, ecModel, api) {
44632            var geo = mapOrGeoModel.coordinateSystem;
44633            var controller = this._controller;
44634            var controllerHost = this._controllerHost;
44635
44636            controllerHost.zoomLimit = mapOrGeoModel.get('scaleLimit');
44637            controllerHost.zoom = geo.getZoom();
44638
44639            // roamType is will be set default true if it is null
44640            controller.enable(mapOrGeoModel.get('roam') || false);
44641            var mainType = mapOrGeoModel.mainType;
44642
44643            function makeActionBase() {
44644                var action = {
44645                    type: 'geoRoam',
44646                    componentType: mainType
44647                };
44648                action[mainType + 'Id'] = mapOrGeoModel.id;
44649                return action;
44650            }
44651
44652            controller.off('pan').on('pan', function (dx, dy) {
44653                this._mouseDownFlag = false;
44654
44655                updateViewOnPan(controllerHost, dx, dy);
44656
44657                api.dispatchAction(extend(makeActionBase(), {
44658                    dx: dx,
44659                    dy: dy
44660                }));
44661            }, this);
44662
44663            controller.off('zoom').on('zoom', function (zoom, mouseX, mouseY) {
44664                this._mouseDownFlag = false;
44665
44666                updateViewOnZoom(controllerHost, zoom, mouseX, mouseY);
44667
44668                api.dispatchAction(extend(makeActionBase(), {
44669                    zoom: zoom,
44670                    originX: mouseX,
44671                    originY: mouseY
44672                }));
44673
44674                if (this._updateGroup) {
44675                    var group = this.group;
44676                    var scale = group.scale;
44677                    group.traverse(function (el) {
44678                        if (el.type === 'text') {
44679                            el.attr('scale', [1 / scale[0], 1 / scale[1]]);
44680                        }
44681                    });
44682                }
44683            }, this);
44684
44685            controller.setPointerChecker(function (e, x, y) {
44686                return geo.getViewRectAfterRoam().contain(x, y)
44687                    && !onIrrelevantElement(e, api, mapOrGeoModel);
44688            });
44689        }
44690    };
44691
44692    /*
44693* Licensed to the Apache Software Foundation (ASF) under one
44694* or more contributor license agreements.  See the NOTICE file
44695* distributed with this work for additional information
44696* regarding copyright ownership.  The ASF licenses this file
44697* to you under the Apache License, Version 2.0 (the
44698* "License"); you may not use this file except in compliance
44699* with the License.  You may obtain a copy of the License at
44700*
44701*   http://www.apache.org/licenses/LICENSE-2.0
44702*
44703* Unless required by applicable law or agreed to in writing,
44704* software distributed under the License is distributed on an
44705* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
44706* KIND, either express or implied.  See the License for the
44707* specific language governing permissions and limitations
44708* under the License.
44709*/
44710
44711    extendChartView({
44712
44713        type: 'map',
44714
44715        render: function (mapModel, ecModel, api, payload) {
44716            // Not render if it is an toggleSelect action from self
44717            if (payload && payload.type === 'mapToggleSelect'
44718                && payload.from === this.uid
44719            ) {
44720                return;
44721            }
44722
44723            var group = this.group;
44724            group.removeAll();
44725
44726            if (mapModel.getHostGeoModel()) {
44727                return;
44728            }
44729
44730            // Not update map if it is an roam action from self
44731            if (!(payload && payload.type === 'geoRoam'
44732                    && payload.componentType === 'series'
44733                    && payload.seriesId === mapModel.id
44734                )
44735            ) {
44736                if (mapModel.needsDrawMap) {
44737                    var mapDraw = this._mapDraw || new MapDraw(api, true);
44738                    group.add(mapDraw.group);
44739
44740                    mapDraw.draw(mapModel, ecModel, api, this, payload);
44741
44742                    this._mapDraw = mapDraw;
44743                }
44744                else {
44745                    // Remove drawed map
44746                    this._mapDraw && this._mapDraw.remove();
44747                    this._mapDraw = null;
44748                }
44749            }
44750            else {
44751                var mapDraw = this._mapDraw;
44752                mapDraw && group.add(mapDraw.group);
44753            }
44754
44755            mapModel.get('showLegendSymbol') && ecModel.getComponent('legend')
44756            && this._renderSymbols(mapModel, ecModel, api);
44757        },
44758
44759        remove: function () {
44760            this._mapDraw && this._mapDraw.remove();
44761            this._mapDraw = null;
44762            this.group.removeAll();
44763        },
44764
44765        dispose: function () {
44766            this._mapDraw && this._mapDraw.remove();
44767            this._mapDraw = null;
44768        },
44769
44770        _renderSymbols: function (mapModel, ecModel, api) {
44771            var originalData = mapModel.originalData;
44772            var group = this.group;
44773
44774            originalData.each(originalData.mapDimension('value'), function (value, idx) {
44775                if (isNaN(value)) {
44776                    return;
44777                }
44778
44779                var layout = originalData.getItemLayout(idx);
44780
44781                if (!layout || !layout.point) {
44782                    // Not exists in map
44783                    return;
44784                }
44785
44786                var point = layout.point;
44787                var offset = layout.offset;
44788
44789                var circle = new Circle({
44790                    style: {
44791                        // Because the special of map draw.
44792                        // Which needs statistic of multiple series and draw on one map.
44793                        // And each series also need a symbol with legend color
44794                        //
44795                        // Layout and visual are put one the different data
44796                        fill: mapModel.getData().getVisual('color')
44797                    },
44798                    shape: {
44799                        cx: point[0] + offset * 9,
44800                        cy: point[1],
44801                        r: 3
44802                    },
44803                    silent: true,
44804                    // Do not overlap the first series, on which labels are displayed.
44805                    z2: !offset ? 10 : 8
44806                });
44807
44808                // First data on the same region
44809                if (!offset) {
44810                    var fullData = mapModel.mainSeries.getData();
44811                    var name = originalData.getName(idx);
44812
44813                    var fullIndex = fullData.indexOfName(name);
44814
44815                    var itemModel = originalData.getItemModel(idx);
44816                    var labelModel = itemModel.getModel('label');
44817                    var hoverLabelModel = itemModel.getModel('emphasis.label');
44818
44819                    var polygonGroups = fullData.getItemGraphicEl(fullIndex);
44820
44821                    var normalText = retrieve2(
44822                        mapModel.getFormattedLabel(idx, 'normal'),
44823                        name
44824                    );
44825                    var emphasisText = retrieve2(
44826                        mapModel.getFormattedLabel(idx, 'emphasis'),
44827                        normalText
44828                    );
44829
44830                    var onEmphasis = function () {
44831                        var hoverStyle = setTextStyle({}, hoverLabelModel, {
44832                            text: hoverLabelModel.get('show') ? emphasisText : null
44833                        }, {isRectText: true, useInsideStyle: false}, true);
44834                        circle.style.extendFrom(hoverStyle);
44835                        // Make label upper than others if overlaps.
44836                        circle.__mapOriginalZ2 = circle.z2;
44837                        circle.z2 += 1;
44838                    };
44839
44840                    var onNormal = function () {
44841                        setTextStyle(circle.style, labelModel, {
44842                            text: labelModel.get('show') ? normalText : null,
44843                            textPosition: labelModel.getShallow('position') || 'bottom'
44844                        }, {isRectText: true, useInsideStyle: false});
44845
44846                        if (circle.__mapOriginalZ2 != null) {
44847                            circle.z2 = circle.__mapOriginalZ2;
44848                            circle.__mapOriginalZ2 = null;
44849                        }
44850                    };
44851
44852                    polygonGroups.on('mouseover', onEmphasis)
44853                        .on('mouseout', onNormal)
44854                        .on('emphasis', onEmphasis)
44855                        .on('normal', onNormal);
44856
44857                    onNormal();
44858                }
44859
44860                group.add(circle);
44861            });
44862        }
44863    });
44864
44865    /*
44866* Licensed to the Apache Software Foundation (ASF) under one
44867* or more contributor license agreements.  See the NOTICE file
44868* distributed with this work for additional information
44869* regarding copyright ownership.  The ASF licenses this file
44870* to you under the Apache License, Version 2.0 (the
44871* "License"); you may not use this file except in compliance
44872* with the License.  You may obtain a copy of the License at
44873*
44874*   http://www.apache.org/licenses/LICENSE-2.0
44875*
44876* Unless required by applicable law or agreed to in writing,
44877* software distributed under the License is distributed on an
44878* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
44879* KIND, either express or implied.  See the License for the
44880* specific language governing permissions and limitations
44881* under the License.
44882*/
44883
44884    /**
44885     * @param {module:echarts/coord/View} view
44886     * @param {Object} payload
44887     * @param {Object} [zoomLimit]
44888     */
44889    function updateCenterAndZoom(
44890        view, payload, zoomLimit
44891    ) {
44892        var previousZoom = view.getZoom();
44893        var center = view.getCenter();
44894        var zoom = payload.zoom;
44895
44896        var point = view.dataToPoint(center);
44897
44898        if (payload.dx != null && payload.dy != null) {
44899            point[0] -= payload.dx;
44900            point[1] -= payload.dy;
44901
44902            var center = view.pointToData(point);
44903            view.setCenter(center);
44904        }
44905        if (zoom != null) {
44906            if (zoomLimit) {
44907                var zoomMin = zoomLimit.min || 0;
44908                var zoomMax = zoomLimit.max || Infinity;
44909                zoom = Math.max(
44910                    Math.min(previousZoom * zoom, zoomMax),
44911                    zoomMin
44912                ) / previousZoom;
44913            }
44914
44915            // Zoom on given point(originX, originY)
44916            view.scale[0] *= zoom;
44917            view.scale[1] *= zoom;
44918            var position = view.position;
44919            var fixX = (payload.originX - position[0]) * (zoom - 1);
44920            var fixY = (payload.originY - position[1]) * (zoom - 1);
44921
44922            position[0] -= fixX;
44923            position[1] -= fixY;
44924
44925            view.updateTransform();
44926            // Get the new center
44927            var center = view.pointToData(point);
44928            view.setCenter(center);
44929            view.setZoom(zoom * previousZoom);
44930        }
44931
44932        return {
44933            center: view.getCenter(),
44934            zoom: view.getZoom()
44935        };
44936    }
44937
44938    /*
44939* Licensed to the Apache Software Foundation (ASF) under one
44940* or more contributor license agreements.  See the NOTICE file
44941* distributed with this work for additional information
44942* regarding copyright ownership.  The ASF licenses this file
44943* to you under the Apache License, Version 2.0 (the
44944* "License");
44944 you may not use this file except in compliance
44945* with the License.  You may obtain a copy of the License at
44946*
44947*   http://www.apache.org/licenses/LICENSE-2.0
44948*
44949* Unless required by applicable law or agreed to in writing,
44950* software distributed under the License is distributed on an
44951* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
44952* KIND, either express or implied.  See the License for the
44953* specific language governing permissions and limitations
44954* under the License.
44955*/
44956
44957    /**
44958     * @payload
44959     * @property {string} [componentType=series]
44960     * @property {number} [dx]
44961     * @property {number} [dy]
44962     * @property {number} [zoom]
44963     * @property {number} [originX]
44964     * @property {number} [originY]
44965     */
44966    registerAction({
44967        type: 'geoRoam',
44968        event: 'geoRoam',
44969        update: 'updateTransform'
44970    }, function (payload, ecModel) {
44971        var componentType = payload.componentType || 'series';
44972
44973        ecModel.eachComponent(
44974            { mainType: componentType, query: payload },
44975            function (componentModel) {
44976                var geo = componentModel.coordinateSystem;
44977                if (geo.type !== 'geo') {
44978                    return;
44979                }
44980
44981                var res = updateCenterAndZoom(
44982                    geo, payload, componentModel.get('scaleLimit')
44983                );
44984
44985                componentModel.setCenter
44986                && componentModel.setCenter(res.center);
44987
44988                componentModel.setZoom
44989                && componentModel.setZoom(res.zoom);
44990
44991                // All map series with same `map` use the same geo coordinate system
44992                // So the center and zoom must be in sync. Include the series not selected by legend
44993                if (componentType === 'series') {
44994                    each$1(componentModel.seriesGroup, function (seriesModel) {
44995                        seriesModel.setCenter(res.center);
44996                        seriesModel.setZoom(res.zoom);
44997                    });
44998                }
44999            }
45000        );
45001    });
45002
45003    /*
45004* Licensed to the Apache Software Foundation (ASF) under one
45005* or more contributor license agreements.  See the NOTICE file
45006* distributed with this work for additional information
45007* regarding copyright ownership.  The ASF licenses this file
45008* to you under the Apache License, Version 2.0 (the
45009* "License"); you may not use this file except in compliance
45010* with the License.  You may obtain a copy of the License at
45011*
45012*   http://www.apache.org/licenses/LICENSE-2.0
45013*
45014* Unless required by applicable law or agreed to in writing,
45015* software distributed under the License is distributed on an
45016* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
45017* KIND, either express or implied.  See the License for the
45018* specific language governing permissions and limitations
45019* under the License.
45020*/
45021
45022    var mapSymbolLayout = function (ecModel) {
45023
45024        var processedMapType = {};
45025
45026        ecModel.eachSeriesByType('map', function (mapSeries) {
45027            var mapType = mapSeries.getMapType();
45028            if (mapSeries.getHostGeoModel() || processedMapType[mapType]) {
45029                return;
45030            }
45031
45032            var mapSymbolOffsets = {};
45033
45034            each$1(mapSeries.seriesGroup, function (subMapSeries) {
45035                var geo = subMapSeries.coordinateSystem;
45036                var data = subMapSeries.originalData;
45037                if (subMapSeries.get('showLegendSymbol') && ecModel.getComponent('legend')) {
45038                    data.each(data.mapDimension('value'), function (value, idx) {
45039                        var name = data.getName(idx);
45040                        var region = geo.getRegion(name);
45041
45042                        // If input series.data is [11, 22, '-'/null/undefined, 44],
45043                        // it will be filled with NaN: [11, 22, NaN, 44] and NaN will
45044                        // not be drawn. So here must validate if value is NaN.
45045                        if (!region || isNaN(value)) {
45046                            return;
45047                        }
45048
45049                        var offset = mapSymbolOffsets[name] || 0;
45050
45051                        var point = geo.dataToPoint(region.center);
45052
45053                        mapSymbolOffsets[name] = offset + 1;
45054
45055                        data.setItemLayout(idx, {
45056                            point: point,
45057                            offset: offset
45058                        });
45059                    });
45060                }
45061            });
45062
45063            // Show label of those region not has legendSymbol(which is offset 0)
45064            var data = mapSeries.getData();
45065            data.each(function (idx) {
45066                var name = data.getName(idx);
45067                var layout = data.getItemLayout(idx) || {};
45068                layout.showLabel = !mapSymbolOffsets[name];
45069                data.setItemLayout(idx, layout);
45070            });
45071
45072            processedMapType[mapType] = true;
45073        });
45074    };
45075
45076    /*
45077* Licensed to the Apache Software Foundation (ASF) under one
45078* or more contributor license agreements.  See the NOTICE file
45079* distributed with this work for additional information
45080* regarding copyright ownership.  The ASF licenses this file
45081* to you under the Apache License, Version 2.0 (the
45082* "License"); you may not use this file except in compliance
45083* with the License.  You may obtain a copy of the License at
45084*
45085*   http://www.apache.org/licenses/LICENSE-2.0
45086*
45087* Unless required by applicable law or agreed to in writing,
45088* software distributed under the License is distributed on an
45089* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
45090* KIND, either express or implied.  See the License for the
45091* specific language governing permissions and limitations
45092* under the License.
45093*/
45094
45095
45096    var mapVisual = function (ecModel) {
45097        ecModel.eachSeriesByType('map', function (seriesModel) {
45098            var colorList = seriesModel.get('color');
45099            var itemStyleModel = seriesModel.getModel('itemStyle');
45100
45101            var areaColor = itemStyleModel.get('areaColor');
45102            var color = itemStyleModel.get('color')
45103                || colorList[seriesModel.seriesIndex % colorList.length];
45104
45105            seriesModel.getData().setVisual({
45106                'areaColor': areaColor,
45107                'color': color
45108            });
45109        });
45110    };
45111
45112    /*
45113* Licensed to the Apache Software Foundation (ASF) under one
45114* or more contributor license agreements.  See the NOTICE file
45115* distributed with this work for additional information
45116* regarding copyright ownership.  The ASF licenses this file
45117* to you under the Apache License, Version 2.0 (the
45118* "License"); you may not use this file except in compliance
45119* with the License.  You may obtain a copy of the License at
45120*
45121*   http://www.apache.org/licenses/LICENSE-2.0
45122*
45123* Unless required by applicable law or agreed to in writing,
45124* software distributed under the License is distributed on an
45125* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
45126* KIND, either express or implied.  See the License for the
45127* specific language governing permissions and limitations
45128* under the License.
45129*/
45130
45131// FIXME 公用?
45132    /**
45133     * @param {Array.<module:echarts/data/List>} datas
45134     * @param {string} statisticType 'average' 'sum'
45135     * @inner
45136     */
45137    function dataStatistics(datas, statisticType) {
45138        var dataNameMap = {};
45139
45140        each$1(datas, function (data) {
45141            data.each(data.mapDimension('value'), function (value, idx) {
45142                // Add prefix to avoid conflict with Object.prototype.
45143                var mapKey = 'ec-' + data.getName(idx);
45144                dataNameMap[mapKey] = dataNameMap[mapKey] || [];
45145                if (!isNaN(value)) {
45146                    dataNameMap[mapKey].push(value);
45147                }
45148            });
45149        });
45150
45151        return datas[0].map(datas[0].mapDimension('value'), function (value, idx) {
45152            var mapKey = 'ec-' + datas[0].getName(idx);
45153            var sum = 0;
45154            var min = Infinity;
45155            var max = -Infinity;
45156            var len = dataNameMap[mapKey].length;
45157            for (var i = 0; i < len; i++) {
45158                min = Math.min(min, dataNameMap[mapKey][i]);
45159                max = Math.max(max, dataNameMap[mapKey][i]);
45160                sum += dataNameMap[mapKey][i];
45161            }
45162            var result;
45163            if (statisticType === 'min') {
45164                result = min;
45165            }
45166            else if (statisticType === 'max') {
45167                result = max;
45168            }
45169            else if (statisticType === 'average') {
45170                result = sum / len;
45171            }
45172            else {
45173                result = sum;
45174            }
45175            return len === 0 ? NaN : result;
45176        });
45177    }
45178
45179    var mapDataStatistic = function (ecModel) {
45180        var seriesGroups = {};
45181        ecModel.eachSeriesByType('map', function (seriesModel) {
45182            var hostGeoModel = seriesModel.getHostGeoModel();
45183            var key = hostGeoModel ? 'o' + hostGeoModel.id : 'i' + seriesModel.getMapType();
45184            (seriesGroups[key] = seriesGroups[key] || []).push(seriesM
45184odel);
45185        });
45186
45187        each$1(seriesGroups, function (seriesList, key) {
45188            var data = dataStatistics(
45189                map(seriesList, function (seriesModel) {
45190                    return seriesModel.getData();
45191                }),
45192                seriesList[0].get('mapValueCalculation')
45193            );
45194
45195            for (var i = 0; i < seriesList.length; i++) {
45196                seriesList[i].originalData = seriesList[i].getData();
45197            }
45198
45199            // FIXME Put where?
45200            for (var i = 0; i < seriesList.length; i++) {
45201                seriesList[i].seriesGroup = seriesList;
45202                seriesList[i].needsDrawMap = i === 0 && !seriesList[i].getHostGeoModel();
45203
45204                seriesList[i].setData(data.cloneShallow());
45205                seriesList[i].mainSeries = seriesList[0];
45206            }
45207        });
45208    };
45209
45210    /*
45211* Licensed to the Apache Software Foundation (ASF) under one
45212* or more contributor license agreements.  See the NOTICE file
45213* distributed with this work for additional information
45214* regarding copyright ownership.  The ASF licenses this file
45215* to you under the Apache License, Version 2.0 (the
45216* "License"); you may not use this file except in compliance
45217* with the License.  You may obtain a copy of the License at
45218*
45219*   http://www.apache.org/licenses/LICENSE-2.0
45220*
45221* Unless required by applicable law or agreed to in writing,
45222* software distributed under the License is distributed on an
45223* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
45224* KIND, either express or implied.  See the License for the
45225* specific language governing permissions and limitations
45226* under the License.
45227*/
45228
45229    var backwardCompat$2 = function (option) {
45230        // Save geoCoord
45231        var mapSeries = [];
45232        each$1(option.series, function (seriesOpt) {
45233            if (seriesOpt && seriesOpt.type === 'map') {
45234                mapSeries.push(seriesOpt);
45235                seriesOpt.map = seriesOpt.map || seriesOpt.mapType;
45236                // Put x, y, width, height, x2, y2 in the top level
45237                defaults(seriesOpt, seriesOpt.mapLocation);
45238            }
45239        });
45240    };
45241
45242    /*
45243* Licensed to the Apache Software Foundation (ASF) under one
45244* or more contributor license agreements.  See the NOTICE file
45245* distributed with this work for additional information
45246* regarding copyright ownership.  The ASF licenses this file
45247* to you under the Apache License, Version 2.0 (the
45248* "License"); you may not use this file except in compliance
45249* with the License.  You may obtain a copy of the License at
45250*
45251*   http://www.apache.org/licenses/LICENSE-2.0
45252*
45253* Unless required by applicable law or agreed to in writing,
45254* software distributed under the License is distributed on an
45255* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
45256* KIND, either express or implied.  See the License for the
45257* specific language governing permissions and limitations
45258* under the License.
45259*/
45260
45261    registerLayout(mapSymbolLayout);
45262    registerVisual(mapVisual);
45263    registerProcessor(PRIORITY.PROCESSOR.STATISTIC, mapDataStatistic);
45264    registerPreprocessor(backwardCompat$2);
45265
45266    createDataSelectAction('map', [{
45267        type: 'mapToggleSelect',
45268        event: 'mapselectchanged',
45269        method: 'toggleSelected'
45270    }, {
45271        type: 'mapSelect',
45272        event: 'mapselected',
45273        method: 'select'
45274    }, {
45275        type: 'mapUnSelect',
45276        event: 'mapunselected',
45277        method: 'unSelect'
45278    }]);
45279
45280    /*
45281* Licensed to the Apache Software Foundation (ASF) under one
45282* or more contributor license agreements.  See the NOTICE file
45283* distributed with this work for additional information
45284* regarding copyright ownership.  The ASF licenses this file
45285* to you under the Apache License, Version 2.0 (the
45286* "License"); you may not use this file except in compliance
45287* with the License.  You may obtain a copy of the License at
45288*
45289*   http://www.apache.org/licenses/LICENSE-2.0
45290*
45291* Unless required by applicable law or agreed to in writing,
45292* software distributed under the License is distributed on an
45293* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
45294* KIND, either express or implied.  See the License for the
45295* specific language governing permissions and limitations
45296* under the License.
45297*/
45298
45299    /**
45300     * Link lists and struct (graph or tree)
45301     */
45302
45303    var each$7 = each$1;
45304
45305    var DATAS = '\0__link_datas';
45306    var MAIN_DATA = '\0__link_mainData';
45307
45308// Caution:
45309// In most case, either list or its shallow clones (see list.cloneShallow)
45310// is active in echarts process. So considering heap memory consumption,
45311// we do not clone tree or graph, but share them among list and its shallow clones.
45312// But in some rare case, we have to keep old list (like do animation in chart). So
45313// please take care that both the old list and the new list share the same tree/graph.
45314
45315    /**
45316     * @param {Object} opt
45317     * @param {module:echarts/data/List} opt.mainData
45318     * @param {Object} [opt.struct] For example, instance of Graph or Tree.
45319     * @param {string} [opt.structAttr] designation: list[structAttr] = struct;
45320     * @param {Object} [opt.datas] {dataType: data},
45321     *                 like: {node: nodeList, edge: edgeList}.
45322     *                 Should contain mainData.
45323     * @param {Object} [opt.datasAttr] {dataType: attr},
45324     *                 designation: struct[datasAttr[dataType]] = list;
45325     */
45326    function linkList(opt) {
45327        var mainData = opt.mainData;
45328        var datas = opt.datas;
45329
45330        if (!datas) {
45331            datas = {main: mainData};
45332            opt.datasAttr = {main: 'data'};
45333        }
45334        opt.datas = opt.mainData = null;
45335
45336        linkAll(mainData, datas, opt);
45337
45338        // Porxy data original methods.
45339        each$7(datas, function (data) {
45340            each$7(mainData.TRANSFERABLE_METHODS, function (methodName) {
45341                data.wrapMethod(methodName, curry(transferInjection, opt));
45342            });
45343
45344        });
45345
45346        // Beyond transfer, additional features should be added to `cloneShallow`.
45347        mainData.wrapMethod('cloneShallow', curry(cloneShallowInjection, opt));
45348
45349        // Only mainData trigger change, because struct.update may trigger
45350        // another changable methods, which may bring about dead lock.
45351        each$7(mainData.CHANGABLE_METHODS, function (methodName) {
45352            mainData.wrapMethod(methodName, curry(changeInjection, opt));
45353        });
45354
45355        // Make sure datas contains mainData.
45356        assert$1(datas[mainData.dataType] === mainData);
45357    }
45358
45359    function transferInjection(opt, res) {
45360        if (isMainData(this)) {
45361            // Transfer datas to new main data.
45362            var datas = extend({}, this[DATAS]);
45363            datas[this.dataType] = res;
45364            linkAll(res, datas, opt);
45365        }
45366        else {
45367            // Modify the reference in main data to point newData.
45368            linkSingle(res, this.dataType, this[MAIN_DATA], opt);
45369        }
45370        return res;
45371    }
45372
45373    function changeInjection(opt, res) {
45374        opt.struct && opt.struct.update(this);
45375        return res;
45376    }
45377
45378    function cloneShallowInjection(opt, res) {
45379        // cloneShallow, which brings about some fragilities, may be inappropriate
45380        // to be exposed as an API. So for implementation simplicity we can make
45381        // the restriction that cloneShallow of not-mainData should not be invoked
45382        // outside, but only be invoked here.
45383        each$7(res[DATAS], function (data, dataType) {
45384            data !== res && linkSingle(data.cloneShallow(), dataType, res, opt);
45385        });
45386        return res;
45387    }
45388
45389    /**
45390     * Supplement method to List.
45391     *
45392     * @public
45393     * @param {string} [dataType] If not specified, return mainData.
45394     * @return {module:echarts/data/List}
45395     */
45396    function getLinkedData(dataType) {
45397        var mainData = this[MAIN_DATA];
45398        return (dataType == null || mainData == null)
45399            ? mainData
45400            : mainData[DATAS][dataType];
45401    }
45402
45403    function isMainData(data) {
45404        return data[MAIN_DATA] === data;
45405    }
45406
45407    function linkAll(mainData, datas, opt) {
45408        mainData[DATAS] = {};
45409        each$7(datas, function (data, dataType) {
45410            linkSingle(data, dataType, mainData, opt);
45411        });
45412    }
45413
45414    function linkSingle(data, dataType, mainData, opt) {
45415        mainData[DATAS][dataType] = data;
45416        data[MAIN_DATA] = mainData;
45417        data.dataType = dataType;
45418
45419        if (opt.struct) {
45420            data[opt.structAttr] = opt.struct;
45421            opt.struct[opt.datasAttr[dataType]] = data;
45422        }
45423
45424        // Supplement method.
45425        data.getLinkedData = getLinkedData;
45426    }
45427
45428    /*
45429* Licensed to the Apache Software Foundation (ASF) under one
45430* or more contributor license agreements.  See the NOTICE file
45431* distributed with this work for additional information
45432* regarding copyright ownership.  The ASF licenses this file
45433* to you under the Apache License, Version 2.0 (the
45434* "License"); you may not use this file except in compliance
45435* with the License.  You may obtain a copy of the License at
45436*
45437*   http://www.apache.org/licenses/LICENSE-2.0
45438*
45439* Unless required by applicable law or agreed to in writing,
45440* software distributed under the License is distributed on an
45441* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
45442* KIND, either express or implied.  See the License for the
45443* specific language governing permissions and limitations
45444* under the License.
45445*/
45446
45447    /**
45448     * Tree data structure
45449     *
45450     * @module echarts/data/Tree
45451     */
45452
45453    /**
45454     * @constructor module:echarts/data/Tree~TreeNode
45455     * @param {string} name
45456     * @param {module:echarts/data/Tree} hostTree
45457     */
45458    var TreeNode = function (name, hostTree) {
45459        /**
45460         * @type {string}
45461         */
45462        this.name = name || '';
45463
45464        /**
45465         * Depth of node
45466         *
45467         * @type {number}
45468         * @readOnly
45469         */
45470        this.depth = 0;
45471
45472        /**
45473         * Height of the subtree rooted at this node.
45474         * @type {number}
45475         * @readOnly
45476         */
45477        this.height = 0;
45478
45479        /**
45480         * @type {module:echarts/data/Tree~TreeNode}
45481         * @readOnly
45482         */
45483        this.parentNode = null;
45484
45485        /**
45486         * Reference to list item.
45487         * Do not persistent dataIndex outside,
45488         * besause it may be changed by list.
45489         * If dataIndex -1,
45490         * this node is logical deleted (filtered) in list.
45491         *
45492         * @type {Object}
45493         * @readOnly
45494         */
45495        this.dataIndex = -1;
45496
45497        /**
45498         * @type {Array.<module:echarts/data/Tree~TreeNode>}
45499         * @readOnly
45500         */
45501        this.children = [];
45502
45503        /**
45504         * @type {Array.<module:echarts/data/Tree~TreeNode>}
45505         * @pubilc
45506         */
45507        this.viewChildren = [];
45508
45509        /**
45510         * @type {moduel:echarts/data/Tree}
45511         * @readOnly
45512         */
45513        this.hostTree = hostTree;
45514    };
45515
45516    TreeNode.prototype = {
45517
45518        constructor: TreeNode,
45519
45520        /**
45521         * The node is removed.
45522         * @return {boolean} is removed.
45523         */
45524        isRemoved: function () {
45525            return this.dataIndex < 0;
45526        },
45527
45528        /**
45529         * Travel this subtree (include this node).
45530         * Usage:
45531         *    node.eachNode(function () { ... }); // preorder
45532         *    node.eachNode('preorder', function () { ... }); // preorder
45533         *    node.eachNode('postorder', function () { ... }); // postorder
45534         *    node.eachNode(
45535         *        {order: 'postorder', attr: 'viewChildren'},
45536         *        function () { ... }
45537         *    ); // postorder
45538         *
45539         * @param {(Object|string)} options If string, means order.
45540         * @param {string=} options.order 'preorder' or 'postorder'
45541         * @param {string=} options.attr 'children' or 'viewChildren'
45542         * @param {Function} cb If in preorder and return false,
45543         *                      its subtree will not be visited.
45544         * @param {Object} [context]
45545         */
45546        eachNode: function (options, cb, context) {
45547            if (typeof options === 'function') {
45548                context = cb;
45549                cb = options;
45550                options = null;
45551            }
45552
45553            options = options || {};
45554            if (isString(options)) {
45555                options = {order: options};
45556            }
45557
45558            var order = options.order || 'preorder';
45559            var children = this[options.attr || 'children'];
45560
45561            var suppressVisitSub;
45562            order === 'preorder' && (suppressVisitSub = cb.call(context, this));
45563
45564            for (var i = 0; !suppressVisitSub && i < children.length; i++) {
45565                children[i].eachNode(options, cb, context);
45566            }
45567
45568            order === 'postorder' && cb.call(context, this);
45569        },
45570
45571        /**
45572         * Update depth and height of this subtree.
45573         *
45574         * @param  {number} depth
45575         */
45576        updateDepthAndHeight: function (depth) {
45577            var height = 0;
45578            this.depth = depth;
45579            for (var i = 0; i < this.children.length; i++) {
45580                var child = this.children[i];
45581                child.updateDepthAndHeight(depth + 1);
45582                if (child.height > height) {
45583                    height = child.height;
45584                }
45585            }
45586            this.height = height + 1;
45587        },
45588
45589        /**
45590         * @param  {string} id
45591         * @return {module:echarts/data/Tree~TreeNode}
45592         */
45593        getNodeById: function (id) {
45594            if (this.getId() === id) {
45595                return this;
45596            }
45597            for (var i = 0, children = this.children, len = children.length; i < len; i++) {
45598                var res = children[i].getNodeById(id);
45599                if (res) {
45600                    return res;
45601                }
45602            }
45603        },
45604
45605        /**
45606         * @param {module:echarts/data/Tree~TreeNode} node
45607         * @return {boolean}
45608         */
45609        contains: function (node) {
45610            if (node === this) {
45611                return true;
45612            }
45613            for (var i = 0, children = this.children, len = children.length; i < len; i++) {
45614                var res = children[i].contains(node);
45615                if (res) {
45616                    return res;
45617                }
45618            }
45619        },
45620
45621        /**
45622         * @param {boolean} includeSelf Default false.
45623         * @return {Array.<module:echarts/data/Tree~TreeNode>} order: [root, child, grandchild, ...]
45624         */
45625        getAncestors: function (includeSelf) {
45626            var ancestors = [];
45627            var node = includeSelf ? this : this.parentNode;
45628            while (node) {
45629                ancestors.push(node);
45630                node = node.parentNode;
45631            }
45632            ancestors.reverse();
45633            return ancestors;
45634        },
45635
45636        /**
45637         * @param {string|Array=} [dimension='value'] Default 'value'. can be 0, 1, 2, 3
45638         * @return {number} Value.
45639         */
45640        getValue: function (dimension) {
45641            var data = this.hostTree.data;
45642            return data.get(data.getDimension(dimension || 'value'), this.dataIndex);
45643        },
45644
45645        /**
45646         * @param {Object} layout
45647         * @param {boolean=} [merge=false]
45648         */
45649        setLayout: function (layout, merge$$1) {
45650            this.dataIndex >= 0
45651            && this.hostTree.data.setItemLayout(this.dataIndex, layout, merge$$1);
45652        },
45653
45654        /**
45655         * @return {Object} layout
45656         */
45657        getLayout: function () {
45658            return this.hostTree.data.getItemLayout(this.dataIndex);
45659        },
45660
45661        /**
45662         * @param {string} [path]
45663         * @return {module:echarts/model/Model}
45664         */
45665        getModel: function (path) {
45666            if (this.dataIndex < 0) {
45667                return;
45668            }
45669            var hostTree = this.hostTree;
45670            var itemModel = hostTree.data.getItemModel(this.dataIndex);
45671            var levelModel = this.getLevelModel();
45672            var leavesModel;
45673            if (!levelModel && (this.children.length === 0 || (this.children.length !== 0 && this.isExpand === false))) {
45674                leavesModel = this.getLeavesModel();
45675            }
45676            return itemModel.getModel(path, (levelModel || leavesModel || hostTree.hostModel).getModel(path));
45677        },
45678
45679        /**
45680         * @return {module:echarts/model/Model}
45681         */
45682        getLevelModel: function () {
45683            return (this.hostTree.levelModels || [])[this.depth];
45684        },
45685
45686        /**
45687         * @return {module:echarts/model/Model}
45688         */
45689        getLeavesModel: function () {
45690            return this.hostTree.leavesModel;
45691        },
45692
45693        /**
45694         * @example
45695         *  setItemVisual('color', color);
45696         *  setItemVisual({
45697     *      'color': color
45698     *  });
45699         */
45700        setVisual: function (key, value) {
45701            this.dataIndex >= 0
45702            && this.hostTree.data.setItemVisual(this.dataIndex, key, value);
45703        },
45704
45705        /**
45706         * Get item visual
45707         */
45708        getVisual: function (key, ignoreParent) {
45709            return this.hostTree.data.getItemVisual(this.dataIndex, key, ignoreParent);
45710        },
45711
45712        /**
45713         * @public
45714         * @return {number}
45715         */
45716        getRawIndex: function () {
45717            return this.hostTree.data.getRawIndex(this.dataIndex);
45718        },
45719
45720        /**
45721         * @public
45722         * @return {string}
45723         */
45724        getId: function () {
45725            return this.hostTree.data.getId(this.dataIndex);
45726        },
45727
45728        /**
45729         * if this is an ancestor of another node
45730         *
45731         * @public
45732         * @param {TreeNode} node another node
45733         * @return {boolean} if is ancestor
45734         */
45735        isAncestorOf: function (node) {
45736            var parent = node.parentNode;
45737            while (parent) {
45738                if (parent === this) {
45739                    return true;
45740                }
45741                parent = parent.parentNode;
45742            }
45743            return false;
45744        },
45745
45746        /**
45747         * if this is an descendant of another node
45748         *
45749         * @public
45750         * @param {TreeNode} node another node
45751         * @return {boolean} if is descendant
45752         */
45753        isDescendantOf: function (node) {
45754            return node !== this && node.isAncestorOf(this);
45755        }
45756    };
45757
45758    /**
45759     * @constructor
45760     * @alias module:echarts/data/Tree
45761     * @param {module:echarts/model/Model} hostModel
45762     * @param {Array.<Object>} levelOptions
45763     * @param {Object} leavesOption
45764     */
45765    function Tree(hostModel, levelOptions, leavesOption) {
45766        /**
45767         * @type {module:echarts/data/Tree~TreeNode}
45768         * @readOnly
45769         */
45770        this.root;
45771
45772        /**
45773         * @type {module:echarts/data/List}
45774         * @readOnly
45775         */
45776        this.data;
45777
45778        /**
45779         * Index of each item is the same as the raw index of coresponding list item.
45780         * @private
45781         * @type {Array.<module:echarts/data/Tree~TreeNode}
45782         */
45783        this._nodes = [];
45784
45785        /**
45786         * @private
45787         * @readOnly
45788         * @type {module:echarts/model/Model}
45789         */
45790        this.hostModel = hostModel;
45791
45792        /**
45793         * @private
45794         * @readOnly
45795         * @type {Array.<module:echarts/model/Model}
45796         */
45797        this.levelModels = map(levelOptions || [], function (levelDefine) {
45798            return new Model(levelDefine, hostModel, hostModel.ecModel);
45799        });
45800
45801        this.leavesModel = new Model(leavesOption || {}, hostModel, hostModel.ecModel);
45802    }
45803
45804    Tree.prototype = {
45805
45806        constructor: Tree,
45807
45808        type: 'tree',
45809
45810        /**
45811         * Travel this subtree (include this node).
45812         * Usage:
45813         *    node.eachNode(function () { ... }); // preorder
45814         *    node.eachNode('preorder', function () { ... }); // preorder
45815         *    node.eachNode('postorder', function () { ... }); // postorder
45816         *    node.eachNode(
45817         *        {order: 'postorder', attr: 'viewChildren'},
45818         *        function () { ... }
45819         *    ); // postorder
45820         *
45821         * @param {(Object|string)} options If string, means order.
45822         * @param {string=} options.order 'preorder' or 'postorder'
45823         * @param {string=} options.attr 'children' or 'viewChildren'
45824         * @param {Function} cb
45825         * @param {Object}   [context]
45826         */
45827        eachNode: function(options, cb, context) {
45828            this.root.eachNode(options, cb, context);
45829        },
45830
45831        /**
45832         * @param {number} dataIndex
45833         * @return {module:echarts/data/Tree~TreeNode}
45834         */
45835        getNodeByDataIndex: function (dataIndex) {
45836            var rawIndex = this.data.getRawIndex(dataIndex);
45837            return this._nodes[rawIndex];
45838        },
45839
45840        /**
45841         * @param {string} name
45842         * @return {module:echarts/data/Tree~TreeNode}
45843         */
45844        getNodeByName: function (name) {
45845            return this.root.getNodeByName(name);
45846        },
45847
45848        /**
45849         * Update item available by list,
45850         * when list has been performed options like 'filterSelf' or 'map'.
45851         */
45852        update: function () {
45853            var data = this.data;
45854            var nodes = this._nodes;
45855
45856            for (var i = 0, len = nodes.length; i < len; i++) {
45857                nodes[i].dataIndex = -1;
45858            }
45859
45860            for (var i = 0, len = data.count(); i < len; i++) {
45861                nodes[data.getRawIndex(i)].dataIndex = i;
45862            }
45863        },
45864
45865        /**
45866         * Clear all layouts
45867         */
45868        clearLayouts: function () {
45869            this.data.clearItemLayouts();
45870        }
45871    };
45872
45873    /**
45874     * data node format:
45875     * {
45876 *     name: ...
45877 *     value: ...
45878 *     children: [
45879 *         {
45880 *             name: ...
45881 *             value: ...
45882 *             children: ...
45883 *         },
45884 *         ...
45885 *     ]
45886 * }
45887     *
45888     * @static
45889     * @param {Object} dataRoot Root node.
45890     * @param {module:echarts/model/Model} hostModel
45891     * @param {Object} treeOptions
45892     * @param {Array.<Object>} treeOptions.levels
45893     * @param {Array.<Object>} treeOptions.leaves
45894     * @return module:echarts/data/Tree
45895     */
45896    Tree.createTree = function (dataRoot, hostModel, treeOptions) {
45897
45898        var tree = new Tree(hostModel, treeOptions.levels, treeOptions.leaves);
45899        var listData = [];
45900        var dimMax = 1;
45901
45902        buildHierarchy(dataRoot);
45903
45904        function buildHierarchy(dataNode, parentNode) {
45905            var value = dataNode.value;
45906            dimMax = Math.max(dimMax, isArray(value) ? value.length : 1);
45907
45908            listData.push(dataNode);
45909
45910            var node = new TreeNode(dataNode.name, tree);
45911            parentNode
45912                ? addChild(node, parentNode)
45913                : (tree.root = node);
45914
45915            tree._nodes.push(node);
45916
45917            var children = dataNode.children;
45918            if (children) {
45919                for (var i = 0; i < children.length; i++) {
45920                    buildHierarchy(children[i], node);
45921                }
45922            }
45923        }
45924
45925        tree.root.updateDepthAndHeight(0);
45926
45927        var dimensionsInfo = createDimensions(listData, {
45928            coordDimensions: ['value'],
45929            dimensionsCount: dimMax
45930        });
45931
45932        var list = new List(dimensionsInfo, hostModel);
45933        list.initData(listData);
45934
45935        linkList({
45936            mainData: list,
45937            struct: tree,
45938            structAttr: 'tree'
45939        });
45940
45941        tree.update();
45942
45943        return tree;
45944    };
45945
45946    /**
45947     * It is needed to consider the mess of 'list', 'hostModel' when creating a TreeNote,
45948     * so this function is not ready and not necessary to be public.
45949     *
45950     * @param {(module:echarts/data/Tree~TreeNode|Object)} child
45951     */
45952    function addChild(child, node) {
45953        var children = node.children;
45954        if (child.parentNode === node) {
45955            return;
45956        }
45957
45958        children.push(child);
45959        child.parentNode = node;
45960    }
45961
45962    /*
45963* Licensed to the Apache Software Foundation (ASF) under one
45964* or more contributor license agreements.  See the NOTICE file
45965* distributed with this work for additional information
45966* regarding copyright ownership.  The ASF licenses this file
45967* to you under the Apache License, Version 2.0 (the
45968* "License"); you may not use this file except in compliance
45969* with the License.  You may obtain a copy of the License at
45970*
45971*   http://www.apache.org/licenses/LICENSE-2.0
45972*
45973* Unless required by applicable law or agreed to in writing,
45974* software distributed under the License is distributed on an
45975* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
45976* KIND, either express or implied.  See the License for the
45977* specific language governing permissions and limitations
45978* under the License.
45979*/
45980
45981    /**
45982     * @file Create data struct and define tree view's series model
45983     */
45984
45985    SeriesModel.extend({
45986
45987        type: 'series.tree',
45988
45989        layoutInfo: null,
45990
45991        // can support the position parameters 'left', 'top','right','bottom', 'width',
45992        // 'height' in the setOption() with 'merge' mode normal.
45993        layoutMode: 'box',
45994
45995        /**
45996         * Init a tree data structure from data in option series
45997         * @param  {Object} option  the object used to config echarts view
45998         * @return {module:echarts/data/List} storage initial data
45999         */
46000        getInitialData: function (option) {
46001
46002            //create an virtual root
46003            var root = {name: option.name, children: option.data};
46004
46005            var leaves = option.leaves || {};
46006
46007            var treeOption = {};
46008
46009            treeOption.leaves = leaves;
46010
46011            var tree = Tree.createTree(root, this, treeOption);
46012
46013            var treeDepth = 0;
46014
46015            tree.eachNode('preorder', function (node) {
46016                if (node.depth > treeDepth) {
46017                    treeDepth = node.depth;
46018                }
46019            });
46020
46021            var expandAndCollapse = option.expandAndCollapse;
46022            var expandTreeDepth = (expandAndCollapse && option.initialTreeDepth >= 0)
46023                ? option.initialTreeDepth : treeDepth;
46024
46025            tree.root.eachNode('preorder', function (node) {
46026                var item = node.hostTree.data.getRawDataItem(node.dataIndex);
46027                // add item.collapsed != null, because users can collapse node original in the series.data.
46028                node.isExpand = (item && item.collapsed != null)
46029                    ? !item.collapsed
46030                    : node.depth <= expandTreeDepth;
46031            });
46032
46033            return tree.data;
46034        },
46035
46036        /**
46037         * Make the configuration 'orient' backward compatibly, with 'horizontal = LR', 'vertical = TB'.
46038         * @returns {string} orient
46039         */
46040        getOrient: function () {
46041            var orient = this.get('orient');
46042            if (orient === 'horizontal') {
46043                orient = 'LR';
46044            }
46045            else if (orient === 'vertical') {
46046                orient = 'TB';
46047            }
46048            return orient;
46049        },
46050
46051        /**
46052         * @override
46053         * @param {number} dataIndex
46054         */
46055        formatTooltip: function (dataIndex) {
46056            var tree = this.getData().tree;
46057            var realRoot = tree.root.children[0];
46058            var node = tree.getNodeByDataIndex(dataIndex);
46059            var value = node.getValue();
46060            var name = node.name;
46061            while (node && (node !== realRoot)) {
46062                name = node.parentNode.name + '.' + name;
46063                node = node.parentNode;
46064            }
46065            return encodeHTML(name + (
46066                (isNaN(value) || value == null) ? '' : ' : ' + value
46067            ));
46068        },
46069
46070        defaultOption: {
46071            zlevel: 0,
46072            z: 2,
46073
46074            // the position of the whole view
46075            left: '12%',
46076            top: '12%',
46077            right: '12%',
46078            bottom: '12%',
46079
46080            // the layout of the tree, two value can be selected, 'orthogonal' or 'radial'
46081            layout: 'orthogonal',
46082
46083            // The orient of orthoginal layout, can be setted to 'LR', 'TB', 'RL', 'BT'.
46084            // and the backward compatibility configuration 'horizontal = LR', 'vertical = TB'.
46085            orient: 'LR',
46086
46087            symbol: 'emptyCircle',
46088
46089            symbolSize: 7,
46090
46091            expandAndCollapse: true,
46092
46093            initialTreeDepth: 2,
46094
46095            lineStyle: {
46096                color: '#ccc',
46097                width: 1.5,
46098                curveness: 0.5
46099            },
46100
46101            itemStyle: {
46102                color: 'lightsteelblue',
46103                borderColor: '#c23531',
46104                borderWidth: 1.5
46105            },
46106
46107            label: {
46108                show: true,
46109                color: '#555'
46110            },
46111
46112            leaves: {
46113                label: {
46114                    show: true
46115                }
46116            },
46117
46118            animationEasing: 'linear',
46119
46120            animationDuration: 700,
46121
46122            animationDurationUpdate: 1000
46123        }
46124    });
vendor: 4,095 bytes, lines 46125-46231
46125
46126    /*
46127* Licensed to the Apache Software Foundation (ASF) under one
46128* or more contributor license agreements.  See the NOTICE file
46129* distributed with this work for additional information
46130* regarding copyright ownership.  The ASF licenses this file
46131* to you under the Apache License, Version 2.0 (the
46132* "License"); you may not use this file except in compliance
46133* with the License.  You may obtain a copy of the License at
46134*
46135*   http://www.apache.org/licenses/LICENSE-2.0
46136*
46137* Unless required by applicable law or agreed to in writing,
46138* software distributed under the License is distributed on an
46139* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
46140* KIND, either express or implied.  See the License for the
46141* specific language governing permissions and limitations
46142* under the License.
46143*/
46144
46145    /*
46146* The tree layout implementation references to d3.js
46147* (https://github.com/d3/d3-hierarchy). The use of the source
46148* code of this file is also subject to the terms and consitions
46149* of its license (BSD-3Clause, see <echarts/src/licenses/LICENSE-d3>).
46150*/
46151
46152    /**
46153     * @file The layout algorithm of node-link tree diagrams. Here we using Reingold-Tilford algorithm to drawing
46154     *       the tree.
46155     * @see https://github.com/d3/d3-hierarchy
46156     */
46157
46158    /**
46159     * Initialize all computational message for following algorithm
46160     * @param  {module:echarts/data/Tree~TreeNode} root   The virtual root of the tree
46161     */
46162    function init$2(root) {
46163        root.hierNode = {
46164            defaultAncestor: null,
46165            ancestor: root,
46166            prelim: 0,
46167            modifier: 0,
46168            change: 0,
46169            shift: 0,
46170            i: 0,
46171            thread: null
46172        };
46173
46174        var nodes = [root];
46175        var node;
46176        var children;
46177
46178        while (node = nodes.pop()) { // jshint ignore:line
46179            children = node.children;
46180            if (node.isExpand && children.length) {
46181                var n = children.length;
46182                for (var i = n - 1; i >= 0; i--) {
46183                    var child = children[i];
46184                    child.hierNode = {
46185                        defaultAncestor: null,
46186                        ancestor: child,
46187                        prelim: 0,
46188                        modifier: 0,
46189                        change: 0,
46190                        shift: 0,
46191                        i: i,
46192                        thread: null
46193                    };
46194                    nodes.push(child);
46195                }
46196            }
46197        }
46198    }
46199
46200    /**
46201     * Computes a preliminary x coordinate for node. Before that, this function is
46202     * applied recursively to the children of node, as well as the function
46203     * apportion(). After spacing out the children by calling executeShifts(), the
46204     * node is placed to the midpoint of its outermost children.
46205     * @param  {module:echarts/data/Tree~TreeNode} node
46206     * @param {Function} separation
46207     */
46208    function firstWalk(node, separation) {
46209        var children = node.isExpand ? node.children : [];
46210        var siblings = node.parentNode.children;
46211        var subtreeW = node.hierNode.i ? siblings[node.hierNode.i -1] : null;
46212        if (children.length) {
46213            executeShifts(node);
46214            var midPoint = (children[0].hierNode.prelim + children[children.length - 1].hierNode.prelim) / 2;
46215            if (subtreeW) {
46216                node.hierNode.prelim = subtreeW.hierNode.prelim + separation(node, subtreeW);
46217                node.hierNode.modifier = node.hierNode.prelim - midPoint;
46218            }
46219            else {
46220                node.hierNode.prelim = midPoint;
46221            }
46222        }
46223        else if (subtreeW) {
46224            node.hierNode.prelim = subtreeW.hierNode.prelim + separation(node, subtreeW);
46225        }
46226        node.parentNode.hierNode.defaultAncestor = apportion(node, subtreeW, node.parentNode.hierNode.defaultAncestor || siblings[0], separation);
46227    }
46228
46229
46230    /**
46231     * Computes all real x-coordinates by summing up the modifiers recursively.
46232     * @param  {module:echarts/data/Tree~TreeNode} node
46233     */
46234    function secondWalk(node) {
46235        var nodeX = node.hierNode.prelim + node.parentNode.hierNode.modifier;
46236        node.setLayout({x: nodeX}, true);
46237        node.hierNode.modifier += node.parentNode.hierNode.modifier;
46238    }
46239
46240
46241    function separation(cb) {
46242        return arguments.length ? cb : defaultSeparation;
46243    }
46244
46245    /**
46246     * Transform the common coordinate to radial coordinate
46247     * @param  {number} x
46248     * @param  {number} y
46249     * @return {Object}
46250     */
46251    function radialCoordinate(x, y) {
46252        var radialCoor = {};
46253        x -= Math.PI / 2;
46254        radialCoor.x = y * Math.cos(x);
46255        radialCoor.y = y * Math.sin(x);
46256        return radialCoor;
46257    }
46258
46259    /**
46260     * Get the layout position of the whole view
46261     * @param {module:echarts/model/Series} seriesModel  the model object of sankey series
46262     * @param {module:echarts/ExtensionAPI} api  provide the API list that the developer can call
46263     * @return {module:zrender/core/BoundingRect}  size of rect to draw the sankey view
46264     */
46265    function getViewRect(seriesModel, api) {
46266        return getLayoutRect(
46267            seriesModel.getBoxLayoutParams(), {
46268                width: api.getWidth(),
46269                height: api.getHeight()
46270            }
46271        );
46272    }
46273
46274    /**
46275     * All other shifts, applied to the smaller subtrees between w- and w+, are
46276     * performed by this function.
46277     * @param  {module:echarts/data/Tree~TreeNode} node
46278     */
46279    function executeShifts(node) {
46280        var children = node.children;
46281        var n = children.length;
46282        var shift = 0;
46283        var change = 0;
46284        while (--n >= 0) {
46285            var child = children[n];
46286            child.hierNode.prelim += shift;
46287            child.hierNode.modifier += shift;
46288            change += child.hierNode.change;
46289            shift += child.hierNode.shift + change;
46290        }
46291    }
46292
46293    /**
46294     * The core of the algorithm. Here, a new subtree is combined with the
46295     * previous subtrees. Threads are used to traverse the inside and outside
46296     * contours of the left and right subtree up to the highest common level.
46297     * Whenever two nodes of the inside contours conflict, we compute the left
46298     * one of the greatest uncommon ancestors using the function nextAncestor()
46299     * and call moveSubtree() to shift the subtree and prepare the shifts of
46300     * smaller subtrees. Finally, we add a new thread (if necessary).
46301     * @param  {module:echarts/data/Tree~TreeNode} subtreeV
46302     * @param  {module:echarts/data/Tree~TreeNode} subtreeW
46303     * @param  {module:echarts/data/Tree~TreeNode} ancestor
46304     * @param  {Function} separation
46305     * @return {module:echarts/data/Tree~TreeNode}
46306     */
46307    function apportion(subtreeV, subtreeW, ancestor, separation) {
46308
46309        if (subtreeW) {
46310            var nodeOutRight = subtreeV;
46311            var nodeInRight = subtreeV;
46312            var nodeOutLeft = nodeInRight.parentNode.children[0];
46313            var nodeInLeft = subtreeW;
46314
46315            var sumOutRight = nodeOutRight.hierNode.modifier;
46316            var sumInRight = nodeInRight.hierNode.modifier;
46317            var sumOutLeft = nodeOutLeft.hierNode.modifier;
46318            var sumInLeft = nodeInLeft.hierNode.modifier;
46319
46320            while (nodeInLeft = nextRight(nodeInLeft), nodeInRight = nextLeft(nodeInRight), nodeInLeft && nodeInRight) {
46321                nodeOutRight = nextRight(nodeOutRight);
46322                nodeOutLeft = nextLeft(nodeOutLeft);
46323                nodeOutRight.hierNode.ancestor = subtreeV;
46324                var shift = nodeInLeft.hierNode.prelim + sumInLeft - nodeInRight.hierNode.prelim
46325                    - sumInRight + separation(nodeInLeft, nodeInRight);
46326                if (shift > 0) {
46327                    moveSubtree(nextAncestor(nodeInLeft, subtreeV, ancestor), subtreeV, shift);
46328                    sumInRight += shift;
46329                    sumOutRight += shift;
46330                }
46331                sumInLeft += nodeInLeft.hierNode.modifier;
46332                sumInRight += nodeInRight.hierNode.modifier;
46333                sumOutRight += nodeOutRight.hierNode.modifier;
46334                sumOutLeft += nodeOutLeft.hierNode.modifier;
46335            }
46336            if (nodeInLeft && !nextRight(nodeOutRight)) {
46337                nodeOutRight.hierNode.thread = nodeInLeft;
46338                nodeOutRight.hierNode.modifier += sumInLeft - sumOutRight;
46339
46340            }
46341            if (nodeInRight && !nextLeft(nodeOutLeft)) {
46342                nodeOutLeft.hierNode.thread = nodeInRight;
46343                nodeOutLeft.hierNode.modifier += sumInRight - sumOutLeft;
46344                ancestor = subtreeV;
46345            }
46346        }
46347        return ancestor;
46348    }
46349
46350    /**
46351     * This function is used to traverse the right contour of a subtree.
46352     * It returns the rightmost child of node or the thread of node. The function
46353     * returns null if and only if node is on the highest depth of its subtree.
46354     * @param  {module:echarts/data/Tree~TreeNode} node
46355     * @return {module:echarts/data/Tree~TreeNode}
46356     */
46357    function nextRight(node) {
46358        var children = node.children;
46359        return children.length && node.isExpand ? children[children.length - 1] : node.hierNode.thread;
46360    }
46361
46362    /**
46363     * This function is used to traverse the left contour of a subtree (or a subforest).
46364     * It returns the leftmost child of node or the thread of node. The function
46365     * returns null if and only if node is on the highest depth of its subtree.
46366     * @param  {module:echarts/data/Tree~TreeNode} node
46367     * @return {module:echarts/data/Tree~TreeNode}
46368     */
46369    function nextLeft(node) {
46370        var children = node.children;
46371        return children.length && node.isExpand ? children[0] : node.hierNode.thread;
46372    }
46373
46374    /**
46375     * If nodeInLeft’s ancestor is a sibling of node, returns nodeInLeft’s ancestor.
46376     * Otherwise, returns the specified ancestor.
46377     * @param  {module:echarts/data/Tree~TreeNode} nodeInLeft
46378     * @param  {module:echarts/data/Tree~TreeNode} node
46379     * @param  {module:echarts/data/Tree~TreeNode} ancestor
46380     * @return {module:echarts/data/Tree~TreeNode}
46381     */
46382    function nextAncestor(nodeInLeft, node, ancestor) {
46383        return nodeInLeft.hierNode.ancestor.parentNode === node.parentNode
46384            ? nodeInLeft.hierNode.ancestor : ancestor;
46385    }
46386
46387    /**
46388     * Shifts the current subtree rooted at wr. This is done by increasing prelim(w+) and modifier(w+) by shift.
46389     * @param  {module:echarts/data/Tree~TreeNode} wl
46390     * @param  {module:echarts/data/Tree~TreeNode} wr
46391     * @param  {number} shift [description]
46392     */
46393    function moveSubtree(wl, wr,shift) {
46394        var change = shift / (wr.hierNode.i - wl.hierNode.i);
46395        wr.hierNode.change -= change;
46396        wr.hierNode.shift += shift;
46397        wr.hierNode.modifier += shift;
46398        wr.hierNode.prelim += shift;
46399        wl.hierNode.change += change;
46400    }
46401
46402    function defaultSeparation(node1, node2) {
46403        return node1.parentNode === node2.parentNode ? 1 : 2;
46404    }
46405
46406    /*
46407* Licensed to the Apache Software Foundation (ASF) under one
46408* or more contributor license agreements.  See the NOTICE file
46409* distributed with this work for additional information
46410* regarding copyright ownership.  The ASF licenses this file
46411* to you under the Apache License, Version 2.0 (the
46412* "License"); you may not use this file except in compliance
46413* with the License.  You may obtain a copy of the License at
46414*
46415*   http://www.apache.org/licenses/LICENSE-2.0
46416*
46417* Unless required by applicable law or agreed to in writing,
46418* software distributed under the License is distributed on an
46419* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
46420* KIND, either express or implied.  See the License for the
46421* specific language governing permissions and limitations
46422* under the License.
46423*/
46424
46425    /**
46426     * @file  This file used to draw tree view
46427     */
46428
46429    extendChartView({
46430
46431        type: 'tree',
46432
46433        /**
46434         * Init the chart
46435         * @override
46436         * @param  {module:echarts/model/Global} ecModel
46437         * @param  {module:echarts/ExtensionAPI} api
46438         */
46439        init: function (ecModel, api) {
46440
46441            /**
46442             * @private
46443             * @type {module:echarts/data/Tree}
46444             */
46445            this._oldTree;
46446
46447            /**
46448             * @private
46449             * @type {module:zrender/container/Group}
46450             */
46451            this._mainGroup = new Group();
46452
46453            this.group.add(this._mainGroup);
46454        },
46455
46456        render: function (seriesModel, ecModel, api, payload) {
46457
46458            var data = seriesModel.getData();
46459
46460            var layoutInfo = seriesModel.layoutInfo;
46461
46462            var group = this._mainGroup;
46463
46464            var layout = seriesModel.get('layout');
46465
46466            if (layout === 'radial') {
46467                group.attr('position', [layoutInfo.x + layoutInfo.width / 2, layoutInfo.y + layoutInfo.height / 2]);
46468            }
46469            else {
46470                group.attr('position', [layoutInfo.x, layoutInfo.y]);
46471            }
46472
46473            var oldData = this._data;
46474
46475            var seriesScope = {
46476                expandAndCollapse: seriesModel.get('expandAndCollapse'),
46477                layout: layout,
46478                orient: seriesModel.getOrient(),
46479                curvature: seriesModel.get('lineStyle.curveness'),
46480                symbolRotate: seriesModel.get('symbolRotate'),
46481                symbolOffset: seriesModel.get('symbolOffset'),
46482                hoverAnimation: seriesModel.get('hoverAnimation'),
46483                useNameLabel: true,
46484                fadeIn: true
46485            };
46486
46487            data.diff(oldData)
46488                .add(function (newIdx) {
46489                    if (symbolNeedsDraw$1(data, newIdx)) {
46490                        // create node and edge
46491                        updateNode(data, newIdx, null, group, seriesModel, seriesScope);
46492                    }
46493                })
46494                .update(function (newIdx, oldIdx) {
46495                    var symbolEl = oldData.getItemGraphicEl(oldIdx);
46496                    if (!symbolNeedsDraw$1(data, newIdx)) {
46497                        symbolEl && removeNode(oldData, oldIdx, symbolEl, group, seriesModel, seriesScope);
46498                        return;
46499                    }
46500                    // update  node and edge
46501                    updateNode(data, newIdx, symbolEl, group, seriesModel, seriesScope);
46502                })
46503                .remove(function (oldIdx) {
46504                    var symbolEl = oldData.getItemGraphicEl(oldIdx);
46505                    // When remove a collapsed node of subtree, since the collapsed
46506                    // node haven't been initialized with a symbol element,
46507                    // you can't found it's symbol element through index.
46508                    // so if we want to remove the symbol element we should insure
46509                    // that the symbol element is not null.
46510                    if (symbolEl) {
46511                        removeNode(oldData, oldIdx, symbolEl, group, seriesModel, seriesScope);
46512                    }
46513                })
46514                .execute();
46515
46516            if (seriesScope.expandAndCollapse === true) {
46517                data.eachItemGraphicEl(function (el, dataIndex) {
46518                    el.off('click').on('click', function () {
46519                        api.dispatchAction({
46520                            type: 'treeExpandAndCollapse',
46521                            seriesId: seriesModel.id,
46522                            dataIndex: dataIndex
46523                        });
46524                    });
46525                });
46526            }
46527
46528            this._data = data;
46529        },
46530
46531        dispose: function () {},
46532
46533        remove: function () {
46534            this._mainGroup.removeAll();
46535            this._data = null;
46536        }
46537
46538    });
46539
46540    function symbolNeedsDraw$1(data, dataIndex) {
46541        var layout = data.getItemLayout(dataIndex);
46542
46543        return layout
46544            && !isNaN(layout.x) && !isNaN(layout.y)
46545            && data.getItemVisual(dataIndex, 'symbol') !== 'none';
46546    }
46547
46548    function getTreeNodeStyle(node, itemModel, seriesScope) {
46549        seriesScope.itemModel = itemModel;
46550        seriesScope.itemStyle = itemModel.getModel('itemStyle').getItemStyle();
46551        seriesScope.hoverItemStyle = itemModel.getModel('emphasis.itemStyle').getItemStyle();
46552        seriesScope.lineStyle = itemModel.getModel('lineStyle').getLineStyle();
46553        seriesScope.labelModel = itemModel.getModel('label');
46554        seriesScope.hoverLabelModel = itemModel.getModel('emphasis.label');
46555
46556        if (node.isExpand === false && node.children.length !== 0) {
46557            seriesScope.symbolInnerColor = seriesScope.itemStyle.fill;
46558        }
46559        else {
46560            seriesScope.symbolInnerColor = '#fff';
46561        }
46562
46563        return seriesScope;
46564    }
46565
46566    function updateNode(data, dataIndex, symbolEl, group, seriesModel, seriesScope) {
46567        var isInit = !symbolEl;
46568        var node = data.tree.getNodeByDataIndex(dataIndex);
46569        var itemModel = node.getModel();
46570        var seriesScope = getTreeNodeStyle(node, itemModel, seriesScope);
46571        var virtualRoot = data.tree.root;
46572
46573        var source = node.parentNode === virtualRoot ? node : node.parentNode || node;
46574        var sourceSymbolEl = data.getItemGraphicEl(source.dataIndex);
46575        var sourceLayout = source.getLayout();
46576        var sourceOldLayout = sourceSymbolEl
46577            ? {
46578                x: sourceSymbolEl.position[0],
46579                y: sourceSymbolEl.position[1],
46580                rawX: sourceSymbolEl.__radialOldRawX,
46581                rawY: sourceSymbolEl.__radialOldRawY
46582            }
46583            : sourceLayout;
46584        var targetLayout = node.getLayout();
46585
46586        if (isInit) {
46587            symbolEl = new SymbolClz$1(data, dataIndex, seriesScope);
46588            symbolEl.attr('position', [sourceOldLayout.x, sourceOldLayout.y]);
46589        }
46590        else {
46591            symbolEl.updateData(data, dataIndex, seriesScope);
46592        }
46593
46594        symbolEl.__radialOldRawX = symbolEl.__radialRawX;
46595        symbolEl.__radialOldRawY = symbolEl.__radialRawY;
46596        symbolEl.__radialRawX = targetLayout.rawX;
46597        symbolEl.__radialRawY = targetLayout.rawY;
46598
46599        group.add(symbolEl);
46600        data.setItemGraphicEl(dataIndex, symbolEl);
46601        updateProps(symbolEl, {
46602            position: [targetLayout.x, targetLayout.y]
46603        }, seriesModel);
46604
46605        var symbolPath = symbolEl.getSymbolPath();
46606
46607        if (seriesScope.layout === 'radial') {
46608            var realRoot = virtualRoot.children[0];
46609            var rootLayout = realRoot.getLayout();
46610            var length = realRoot.children.length;
46611            var rad;
46612            var isLeft;
46613
46614            if (targetLayout.x === rootLayout.x && node.isExpand === true) {
46615                var center = {};
46616                center.x = (realRoot.children[0].getLayout().x + realRoot.children[length - 1].getLayout().x) / 2;
46617                center.y = (realRoot.children[0].getLayout().y + realRoot.children[length - 1].getLayout().y) / 2;
46618                rad = Math.atan2(center.y - rootLayout.y, center.x - rootLayout.x);
46619                if (rad < 0) {
46620                    rad = Math.PI * 2 + rad;
46621                }
46622                isLeft = center.x < rootLayout.x;
46623                if (isLeft) {
46624                    rad = rad - Math.PI;
46625                }
46626            }
46627            else {
46628                rad = Math.atan2(targetLayout.y - rootLayout.y, targetLayout.x - rootLayout.x);
46629                if (rad < 0) {
46630                    rad = Math.PI * 2 + rad;
46631                }
46632                if (node.children.length === 0 || (node.children.length !== 0 && node.isExpand === false)) {
46633                    isLeft = targetLayout.x < rootLayout.x;
46634                    if (isLeft) {
46635                        rad = rad - Math.PI;
46636                    }
46637                }
46638                else {
46639                    isLeft = targetLayout.x > rootLayout.x;
46640                    if (!isLeft) {
46641                        rad = rad - Math.PI;
46642                    }
46643                }
46644            }
46645
46646            var textPosition = isLeft ? 'left' : 'right';
46647            symbolPath.setStyle({
46648                textPosition: textPosition,
46649                textRotation: -rad,
46650                textOrigin: 'center',
46651                verticalAlign: 'middle'
46652            });
46653        }
46654
46655        if (node.parentNode && node.parentNode !== virtualRoot) {
46656            var edge = symbolEl.__edge;
46657            if (!edge) {
46658                edge = symbolEl.__edge = new BezierCurve({
46659                    shape: getEdgeShape(seriesScope, sourceOldLayout, sourceOldLayout),
46660                    style: defaults({opacity: 0}, seriesScope.lineStyle)
46661                });
46662            }
46663
46664            updateProps(edge, {
46665                shape: getEdgeShape(seriesScope, sourceLayout, targetLayout),
46666                style: {opacity: 1}
46667            }, seriesModel);
46668
46669            group.add(edge);
46670        }
46671    }
46672
46673    function removeNode(data, dataIndex, symbolEl, group, seriesModel, seriesScope) {
46674        var node = data.tree.getNodeByDataIndex(dataIndex);
46675        var virtualRoot = data.tree.root;
46676        var itemModel = node.getModel();
46677        var seriesScope = getTreeNodeStyle(node, itemModel, seriesScope);
46678
46679        var source = node.parentNode === virtualRoot ? node : node.parentNode || node;
46680        var sourceLayout;
46681        while (sourceLayout = source.getLayout(), sourceLayout == null) {
46682            source = source.parentNode === virtualRoot ? source : source.parentNode || source;
46683        }
46684
46685        updateProps(symbolEl, {
46686            position: [sourceLayout.x + 1, sourceLayout.y + 1]
46687        }, seriesModel, function () {
46688            group.remove(symbolEl);
46689            data.setItemGraphicEl(dataIndex, null);
46690        });
46691
46692        symbolEl.fadeOut(null, {keepLabel: true});
46693
46694        var edge = symbolEl.__edge;
46695        if (edge) {
46696            updateProps(edge, {
46697                shape: getEdgeShape(seriesScope, sourceLayout, sourceLayout),
46698                style: {
46699                    opacity: 0
46700                }
46701            }, seriesModel, function () {
46702                group.remove(edge);
46703            });
46704        }
46705    }
46706
46707    function getEdgeShape(seriesScope, sourceLayout, targetLayout) {
46708        var cpx1;
46709        var cpy1;
46710        var cpx2;
46711        var cpy2;
46712        var orient = seriesScope.orient;
46713
46714        if (seriesScope.layout === 'radial') {
46715            var x1 = sourceLayout.rawX;
46716            var y1 = sourceLayout.rawY;
46717            var x2 = targetLayout.rawX;
46718            var y2 = targetLayout.rawY;
46719
46720            var radialCoor1 = radialCoordinate(x1, y1);
46721            var radialCoor2 = radialCoordinate(x1, y1 + (y2 - y1) * seriesScope.curvature);
46722            var radialCoor3 = radialCoordinate(x2, y2 + (y1 - y2) * seriesScope.curvature);
46723            var radialCoor4 = radialCoordinate(x2, y2);
46724
46725            return {
46726                x1: radialCoor1.x,
46727                y1: radialCoor1.y,
46728                x2: radialCoor4.x,
46729                y2: radialCoor4.y,
46730                cpx1: radialCoor2.x,
46731                cpy1: radialCoor2.y,
46732                cpx2: radialCoor3.x,
46733                cpy2: radialCoor3.y
46734            };
46735        }
46736        else {
46737            var x1 = sourceLayout.x;
46738            var y1 = sourceLayout.y;
46739            var x2 = targetLayout.x;
46740            var y2 = targetLayout.y;
46741
46742            if (orient === 'LR' || orient === 'RL') {
46743                cpx1 = x1 + (x2 - x1) * seriesScope.curvature;
46744                cpy1 = y1;
46745                cpx2 = x2 + (x1 - x2) * seriesScope.curvature;
46746                cpy2 = y2;
46747            }
46748            if (orient === 'TB' || orient === 'BT') {
46749                cpx1 = x1;
46750                cpy1 = y1 + (y2 - y1) * seriesScope.curvature;
46751                cpx2 = x2;
46752                cpy2 = y2 + (y1 - y2) * seriesScope.curvature;
46753            }
46754            return {
46755                x1: x1,
46756                y1: y1,
46757                x2: x2,
46758                y2: y2,
46759                cpx1: cpx1,
46760                cpy1: cpy1,
46761                cpx2: cpx2,
46762                cpy2: cpy2
46763            };
46764        }
46765    }
46766
46767    /*
46768* Licensed to the Apache Software Foundation (ASF) under one
46769* or more contributor license agreements.  See the NOTICE file
46770* distributed with this work for additional information
46771* regarding copyright ownership.  The ASF licenses this file
46772* to you under the Apache License, Version 2.0 (the
46773* "License"); you may not use this file except in compliance
46774* with the License.  You may obtain a copy of the License at
46775*
46776*   http://www.apache.org/licenses/LICENSE-2.0
46777*
46778* Unless required by applicable law or agreed to in writing,
46779* software distributed under the License is distributed on an
46780* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
46781* KIND, either express or implied.  See the License for the
46782* specific language governing permissions and limitations
46783* under the License.
46784*/
46785
46786    registerAction({
46787        type: 'treeExpandAndCollapse',
46788        event: 'treeExpandAndCollapse',
46789        update: 'update'
46790    }, function (payload, ecModel) {
vendor: 4,692 bytes, lines 46791-46919
46791        ecModel.eachComponent({mainType: 'series', subType: 'tree', query: payload}, function (seriesModel) {
46792            var dataIndex = payload.dataIndex;
46793            var tree = seriesModel.getData().tree;
46794            var node = tree.getNodeByDataIndex(dataIndex);
46795            node.isExpand = !node.isExpand;
46796
46797        });
46798    });
46799
46800    /*
46801* Licensed to the Apache Software Foundation (ASF) under one
46802* or more contributor license agreements.  See the NOTICE file
46803* distributed with this work for additional information
46804* regarding copyright ownership.  The ASF licenses this file
46805* to you under the Apache License, Version 2.0 (the
46806* "License"); you may not use this file except in compliance
46807* with the License.  You may obtain a copy of the License at
46808*
46809*   http://www.apache.org/licenses/LICENSE-2.0
46810*
46811* Unless required by applicable law or agreed to in writing,
46812* software distributed under the License is distributed on an
46813* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
46814* KIND, either express or implied.  See the License for the
46815* specific language governing permissions and limitations
46816* under the License.
46817*/
46818
46819
46820    /**
46821     * Traverse the tree from bottom to top and do something
46822     * @param  {module:echarts/data/Tree~TreeNode} root  The real root of the tree
46823     * @param  {Function} callback
46824     */
46825    function eachAfter (root, callback, separation) {
46826        var nodes = [root];
46827        var next = [];
46828        var node;
46829
46830        while (node = nodes.pop()) { // jshint ignore:line
46831            next.push(node);
46832            if (node.isExpand) {
46833                var children = node.children;
46834                if (children.length) {
46835                    for (var i = 0; i < children.length; i++) {
46836                        nodes.push(children[i]);
46837                    }
46838                }
46839            }
46840        }
46841
46842        while (node = next.pop()) { // jshint ignore:line
46843            callback(node, separation);
46844        }
46845    }
46846
46847    /**
46848     * Traverse the tree from top to bottom and do something
46849     * @param  {module:echarts/data/Tree~TreeNode} root  The real root of the tree
46850     * @param  {Function} callback
46851     */
46852    function eachBefore (root, callback) {
46853        var nodes = [root];
46854        var node;
46855        while (node = nodes.pop()) { // jshint ignore:line
46856            callback(node);
46857            if (node.isExpand) {
46858                var children = node.children;
46859                if (children.length) {
46860                    for (var i = children.length - 1; i >= 0; i--) {
46861                        nodes.push(children[i]);
46862                    }
46863                }
46864            }
46865        }
46866    }
46867
46868    /*
46869* Licensed to the Apache Software Foundation (ASF) under one
46870* or more contributor license agreements.  See the NOTICE file
46871* distributed with this work for additional information
46872* regarding copyright ownership.  The ASF licenses this file
46873* to you under the Apache License, Version 2.0 (the
46874* "License"); you may not use this file except in compliance
46875* with the License.  You may obtain a copy of the License at
46876*
46877*   http://www.apache.org/licenses/LICENSE-2.0
46878*
46879* Unless required by applicable law or agreed to in writing,
46880* software distributed under the License is distributed on an
46881* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
46882* KIND, either express or implied.  See the License for the
46883* specific language governing permissions and limitations
46884* under the License.
46885*/
46886
46887    var treeLayout = function (ecModel, api) {
46888        ecModel.eachSeriesByType('tree', function (seriesModel) {
46889            commonLayout(seriesModel, api);
46890        });
46891    };
46892
46893    function commonLayout(seriesModel, api) {
46894        var layoutInfo = getViewRect(seriesModel, api);
46895        seriesModel.layoutInfo = layoutInfo;
46896        var layout = seriesModel.get('layout');
46897        var width = 0;
46898        var height = 0;
46899        var separation$$1 = null;
46900
46901        if (layout === 'radial') {
46902            width = 2 * Math.PI;
46903            height = Math.min(layoutInfo.height, layoutInfo.width) / 2;
46904            separation$$1 = separation(function (node1, node2) {
46905                return (node1.parentNode === node2.parentNode ? 1 : 2) / node1.depth;
46906            });
46907        }
46908        else {
46909            width = layoutInfo.width;
46910            height = layoutInfo.height;
46911            separation$$1 = separation();
46912        }
46913
46914        var virtualRoot = seriesModel.getData().tree.root;
46915        var realRoot = virtualRoot.children[0];
46916
46917        if (realRoot) {
46918            init$2(virtualRoot);
46919            eachAfter(realRoot, firstWalk, separation$$1);
vendor: 6,400 bytes, lines 46920-47080
46920            virtualRoot.hierNode.modifier = - realRoot.hierNode.prelim;
46921            eachBefore(realRoot, secondWalk);
46922
46923            var left = realRoot;
46924            var right = realRoot;
46925            var bottom = realRoot;
46926            eachBefore(realRoot, function (node) {
46927                var x = node.getLayout().x;
46928                if (x < left.getLayout().x) {
46929                    left = node;
46930                }
46931                if (x > right.getLayout().x) {
46932                    right = node;
46933                }
46934                if (node.depth > bottom.depth) {
46935                    bottom = node;
46936                }
46937            });
46938
46939            var delta = left === right ? 1 : separation$$1(left, right) / 2;
46940            var tx = delta - left.getLayout().x;
46941            var kx = 0;
46942            var ky = 0;
46943            var coorX = 0;
46944            var coorY = 0;
46945            if (layout === 'radial') {
46946                kx = width / (right.getLayout().x + delta + tx);
46947                // here we use (node.depth - 1), bucause the real root's depth is 1
46948                ky = height / ((bottom.depth - 1) || 1);
46949                eachBefore(realRoot, function (node) {
46950                    coorX = (node.getLayout().x + tx) * kx;
46951                    coorY = (node.depth - 1) * ky;
46952                    var finalCoor = radialCoordinate(coorX, coorY);
46953                    node.setLayout({x: finalCoor.x, y: finalCoor.y, rawX: coorX, rawY: coorY}, true);
46954                });
46955            }
46956            else {
46957                var orient = seriesModel.getOrient();
46958                if (orient === 'RL' || orient === 'LR') {
46959                    ky = height / (right.getLayout().x + delta + tx);
46960                    kx = width / ((bottom.depth - 1) || 1);
46961                    eachBefore(realRoot, function (node) {
46962                        coorY = (node.getLayout().x + tx) * ky;
46963                        coorX = orient === 'LR'
46964                            ? (node.depth - 1) * kx
46965                            : width - (node.depth - 1) * kx;
46966                        node.setLayout({x: coorX, y: coorY}, true);
46967                    });
46968                }
46969                else if (orient === 'TB' || orient === 'BT') {
46970                    kx = width / (right.getLayout().x + delta + tx);
46971                    ky = height / ((bottom.depth - 1) || 1);
46972                    eachBefore(realRoot, function (node) {
46973                        coorX = (node.getLayout().x + tx) * kx;
46974                        coorY = orient === 'TB'
46975                            ? (node.depth - 1) * ky
46976                            : height - (node.depth - 1) * ky;
46977                        node.setLayout({x: coorX, y: coorY}, true);
46978                    });
46979                }
46980            }
46981        }
46982    }
46983
46984    /*
46985* Licensed to the Apache Software Foundation (ASF) under one
46986* or more contributor license agreements.  See the NOTICE file
46987* distributed with this work for additional information
46988* regarding copyright ownership.  The ASF licenses this file
46989* to you under the Apache License, Version 2.0 (the
46990* "License"); you may not use this file except in compliance
46991* with the License.  You may obtain a copy of the License at
46992*
46993*   http://www.apache.org/licenses/LICENSE-2.0
46994*
46995* Unless required by applicable law or agreed to in writing,
46996* software distributed under the License is distributed on an
46997* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
46998* KIND, either express or implied.  See the License for the
46999* specific language governing permissions and limitations
47000* under the License.
47001*/
47002
47003    registerVisual(visualSymbol('tree', 'circle'));
47004    registerLayout(treeLayout);
47005
47006    /*
47007* Licensed to the Apache Software Foundation (ASF) under one
47008* or more contributor license agreements.  See the NOTICE file
47009* distributed with this work for additional information
47010* regarding copyright ownership.  The ASF licenses this file
47011* to you under the Apache License, Version 2.0 (the
47012* "License"); you may not use this file except in compliance
47013* with the License.  You may obtain a copy of the License at
47014*
47015*   http://www.apache.org/licenses/LICENSE-2.0
47016*
47017* Unless required by applicable law or agreed to in writing,
47018* software distributed under the License is distributed on an
47019* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
47020* KIND, either express or implied.  See the License for the
47021* specific language governing permissions and limitations
47022* under the License.
47023*/
47024
47025    function retrieveTargetInfo(payload, validPayloadTypes, seriesModel) {
47026        if (payload && indexOf(validPayloadTypes, payload.type) >= 0) {
47027            var root = seriesModel.getData().tree.root;
47028            var targetNode = payload.targetNode;
47029            if (targetNode && root.contains(targetNode)) {
47030                return {node: targetNode};
47031            }
47032
47033            var targetNodeId = payload.targetNodeId;
47034            if (targetNodeId != null && (targetNode = root.getNodeById(targetNodeId))) {
47035                return {node: targetNode};
47036            }
47037        }
47038    }
47039
47040// Not includes the given node at the last item.
47041    function getPathToRoot(node) {
47042        var path = [];
47043        while (node) {
47044            node = node.parentNode;
47045            node && path.push(node);
47046        }
47047        return path.reverse();
47048    }
47049
47050    function aboveViewRoot(viewRoot, node) {
47051        var viewPath = getPathToRoot(viewRoot);
47052        return indexOf(viewPath, node) >= 0;
47053    }
47054
47055// From root to the input node (the input node will be included).
47056    function wrapTreePathInfo(node, seriesModel) {
47057        var treePathInfo = [];
47058
47059        while (node) {
47060            var nodeDataIndex = node.dataIndex;
47061            treePathInfo.push({
47062                name: node.name,
47063                dataIndex: nodeDataIndex,
47064                value: seriesModel.getRawValue(nodeDataIndex)
47065            });
47066            node = node.parentNode;
47067        }
47068
47069        treePathInfo.reverse();
47070
47071        return treePathInfo;
47072    }
47073
47074    /*
47075* Licensed to the Apache Software Foundation (ASF) under one
47076* or more contributor license agreements.  See the NOTICE file
47077* distributed with this work for additional information
47078* regarding copyright ownership.  The ASF licenses this file
47079* to you under the Apache License, Version 2.0 (the
47080* "License");
47080 you may not use this file except in compliance
47081* with the License.  You may obtain a copy of the License at
47082*
47083*   http://www.apache.org/licenses/LICENSE-2.0
47084*
47085* Unless required by applicable law or agreed to in writing,
47086* software distributed under the License is distributed on an
47087* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
47088* KIND, either express or implied.  See the License for the
47089* specific language governing permissions and limitations
47090* under the License.
47091*/
47092
47093    SeriesModel.extend({
47094
47095        type: 'series.treemap',
47096
47097        layoutMode: 'box',
47098
47099        dependencies: ['grid', 'polar'],
47100
47101        /**
47102         * @type {module:echarts/data/Tree~Node}
47103         */
47104        _viewRoot: null,
47105
47106        defaultOption: {
47107            // Disable progressive rendering
47108            progressive: 0,
47109            hoverLayerThreshold: Infinity,
47110            // center: ['50%', '50%'],          // not supported in ec3.
47111            // size: ['80%', '80%'],            // deprecated, compatible with ec2.
47112            left: 'center',
47113            top: 'middle',
47114            right: null,
47115            bottom: null,
47116            width: '80%',
47117            height: '80%',
47118            sort: true,                         // Can be null or false or true
47119                                                // (order by desc default, asc not supported yet (strange effect))
47120            clipWindow: 'origin',               // Size of clipped window when zooming. 'origin' or 'fullscreen'
47121            squareRatio: 0.5 * (1 + Math.sqrt(5)), // golden ratio
47122            leafDepth: null,                    // Nodes on depth from root are regarded as leaves.
47123                                                // Count from zero (zero represents only view root).
47124            drillDownIcon: 'â–¶',                 // Use html character temporarily because it is complicated
47125            // to align specialized icon. ▷▶❒❐▼✚
47126
47127            zoomToNodeRatio: 0.32 * 0.32,       // Be effective when using zoomToNode. Specify the proportion of the
47128                                                // target node area in the view area.
47129            roam: true,                         // true, false, 'scale' or 'zoom', 'move'.
47130            nodeClick: 'zoomToNode',            // Leaf node click behaviour: 'zoomToNode', 'link', false.
47131                                                // If leafDepth is set and clicking a node which has children but
47132                                                // be on left depth, the behaviour would be changing root. Otherwise
47133                                                // use behavious defined above.
47134            animation: true,
47135            animationDurationUpdate: 900,
47136            animationEasing: 'quinticInOut',
47137            breadcrumb: {
47138                show: true,
47139                height: 22,
47140                left: 'center',
47141                top: 'bottom',
47142                // right
47143                // bottom
47144                emptyItemWidth: 25,             // Width of empty node.
47145                itemStyle: {
47146                    color: 'rgba(0,0,0,0.7)', //'#5793f3',
47147                    borderColor: 'rgba(255,255,255,0.7)',
47148                    borderWidth: 1,
47149                    shadowColor: 'rgba(150,150,150,1)',
47150                    shadowBlur: 3,
47151                    shadowOffsetX: 0,
47152                    shadowOffsetY: 0,
47153                    textStyle: {
47154                        color: '#fff'
47155                    }
47156                },
47157                emphasis: {
47158                    textStyle: {}
47159                }
47160            },
47161            label: {
47162                show: true,
47163                // Do not use textDistance, for ellipsis rect just the same as treemap node rect.
47164                distance: 0,
47165                padding: 5,
47166                position: 'inside', // Can be [5, '5%'] or position stirng like 'insideTopLeft', ...
47167                // formatter: null,
47168                color: '#fff',
47169                ellipsis: true
47170                // align
47171                // verticalAlign
47172            },
47173            upperLabel: {                   // Label when node is parent.
47174                show: false,
47175                position: [0, '50%'],
47176                height: 20,
47177                // formatter: null,
47178                color: '#fff',
47179                ellipsis: true,
47180                // align: null,
47181                verticalAlign: 'middle'
47182            },
47183            itemStyle: {
47184                color: null,            // Can be 'none' if not necessary.
47185                colorAlpha: null,       // Can be 'none' if not necessary.
47186                colorSaturation: null,  // Can be 'none' if not necessary.
47187                borderWidth: 0,
47188                gapWidth: 0,
47189                borderColor: '#fff',
47190                borderColorSaturation: null // If specified, borderColor will be ineffective, and the
47191                                            // border color is evaluated by color of current node and
47192                                            // borderColorSaturation.
47193            },
47194            emphasis: {
47195                upperLabel: {
47196                    show: true,
47197                    position: [0, '50%'],
47198                    color: '#fff',
47199                    ellipsis: true,
47200                    verticalAlign: 'middle'
47201                }
47202            },
47203
47204            visualDimension: 0,                 // Can be 0, 1, 2, 3.
47205            visualMin: null,
47206            visualMax: null,
47207
47208            color: [],                  // + treemapSeries.color should not be modified. Please only modified
47209                                        // level[n].color (if necessary).
47210                                        // + Specify color list of each level. level[0].color would be global
47211                                        // color list if not specified. (see method `setDefault`).
47212                                        // + But set as a empty array to forbid fetch color from global palette
47213                                        // when using nodeModel.get('color'), otherwise nodes on deep level
47214                                        // will always has color palette set and are not able to inherit color
47215                                        // from parent node.
47216                                        // + TreemapSeries.color can not be set as 'none', otherwise effect
47217                                        // legend color fetching (see seriesColor.js).
47218            colorAlpha: null,           // Array. Specify color alpha range of each level, like [0.2, 0.8]
47219            colorSaturation: null,      // Array. Specify color saturation of each level, like [0.2, 0.5]
47220            colorMappingBy: 'index',    // 'value' or 'index' or 'id'.
47221            visibleMin: 10,             // If area less than this threshold (unit: pixel^2), node will not
47222                                        // be rendered. Only works when sort is 'asc' or 'desc'.
47223            childrenVisibleMin: null,   // If area of a node less than this threshold (unit: pixel^2),
47224                                        // grandchildren will not show.
47225                                        // Why grandchildren? If not grandchildren but children,
47226                                        // some siblings show children and s
47226ome not,
47227                                        // the appearance may be mess and not consistent,
47228            levels: []                  // Each item: {
47229                                        //     visibleMin, itemStyle, visualDimension, label
47230                                        // }
47231            // data: {
47232            //      value: [],
47233            //      children: [],
47234            //      link: 'http://xxx.xxx.xxx',
47235            //      target: 'blank' or 'self'
47236            // }
47237        },
47238
47239        /**
47240         * @override
47241         */
47242        getInitialData: function (option, ecModel) {
47243            // Create a virtual root.
47244            var root = {name: option.name, children: option.data};
47245
47246            completeTreeValue(root);
47247
47248            var levels = option.levels || [];
47249
47250            levels = option.levels = setDefault(levels, ecModel);
47251
47252            var treeOption = {};
47253
47254            treeOption.levels = levels;
47255
47256            // Make sure always a new tree is created when setOption,
47257            // in TreemapView, we check whether oldTree === newTree
47258            // to choose mappings approach among old shapes and new shapes.
47259            return Tree.createTree(root, this, treeOption).data;
47260        },
47261
47262        optionUpdated: function () {
47263            this.resetViewRoot();
47264        },
47265
47266        /**
47267         * @override
47268         * @param {number} dataIndex
47269         * @param {boolean} [mutipleSeries=false]
47270         */
47271        formatTooltip: function (dataIndex) {
47272            var data = this.getData();
47273            var value = this.getRawValue(dataIndex);
47274            var formattedValue = isArray(value)
47275                ? addCommas(value[0]) : addCommas(value);
47276            var name = data.getName(dataIndex);
47277
47278            return encodeHTML(name + ': ' + formattedValue);
47279        },
47280
47281        /**
47282         * Add tree path to tooltip param
47283         *
47284         * @override
47285         * @param {number} dataIndex
47286         * @return {Object}
47287         */
47288        getDataParams: function (dataIndex) {
47289            var params = SeriesModel.prototype.getDataParams.apply(this, arguments);
47290
47291            var node = this.getData().tree.getNodeByDataIndex(dataIndex);
47292            params.treePathInfo = wrapTreePathInfo(node, this);
47293
47294            return params;
47295        },
47296
47297        /**
47298         * @public
47299         * @param {Object} layoutInfo {
47300     *                                x: containerGroup x
47301     *                                y: containerGroup y
47302     *                                width: containerGroup width
47303     *                                height: containerGroup height
47304     *                            }
47305         */
47306        setLayoutInfo: function (layoutInfo) {
47307            /**
47308             * @readOnly
47309             * @type {Object}
47310             */
47311            this.layoutInfo = this.layoutInfo || {};
47312            extend(this.layoutInfo, layoutInfo);
47313        },
47314
47315        /**
47316         * @param  {string} id
47317         * @return {number} index
47318         */
47319        mapIdToIndex: function (id) {
47320            // A feature is implemented:
47321            // index is monotone increasing with the sequence of
47322            // input id at the first time.
47323            // This feature can make sure that each data item and its
47324            // mapped color have the same index between data list and
47325            // color list at the beginning, which is useful for user
47326            // to adjust data-color mapping.
47327
47328            /**
47329             * @private
47330             * @type {Object}
47331             */
47332            var idIndexMap = this._idIndexMap;
47333
47334            if (!idIndexMap) {
47335                idIndexMap = this._idIndexMap = createHashMap();
47336                /**
47337                 * @private
47338                 * @type {number}
47339                 */
47340                this._idIndexMapCount = 0;
47341            }
47342
47343            var index = idIndexMap.get(id);
47344            if (index == null) {
47345                idIndexMap.set(id, index = this._idIndexMapCount++);
47346            }
47347
47348            return index;
47349        },
47350
47351        getViewRoot: function () {
47352            return this._viewRoot;
47353        },
47354
47355        /**
47356         * @param {module:echarts/data/Tree~Node} [viewRoot]
47357         */
47358        resetViewRoot: function (viewRoot) {
47359            viewRoot
47360                ? (this._viewRoot = viewRoot)
47361                : (viewRoot = this._viewRoot);
47362
47363            var root = this.getRawData().tree.root;
47364
47365            if (!viewRoot
47366                || (viewRoot !== root && !root.contains(viewRoot))
47367            ) {
47368                this._viewRoot = root;
47369            }
47370        }
47371    });
47372
47373    /**
47374     * @param {Object} dataNode
47375     */
47376    function completeTreeValue(dataNode) {
47377        // Postorder travel tree.
47378        // If value of none-leaf node is not set,
47379        // calculate it by suming up the value of all children.
47380        var sum = 0;
47381
47382        each$1(dataNode.children, function (child) {
47383
47384            completeTreeValue(child);
47385
47386            var childValue = child.value;
47387            isArray(childValue) && (childValue = childValue[0]);
47388
47389            sum += childValue;
47390        });
47391
47392        var thisValue = dataNode.value;
47393        if (isArray(thisValue)) {
47394            thisValue = thisValue[0];
47395        }
47396
47397        if (thisValue == null || isNaN(thisValue)) {
47398            thisValue = sum;
47399        }
47400        // Value should not less than 0.
47401        if (thisValue < 0) {
47402            thisValue = 0;
47403        }
47404
47405        isArray(dataNode.value)
47406            ? (dataNode.value[0] = thisValue)
47407            : (dataNode.value = thisValue);
47408    }
47409
47410    /**
47411     * set default to level configuration
47412     */
47413    function setDefault(levels, ecModel) {
47414        var globalColorList = ecModel.get('color');
47415
47416        if (!globalColorList) {
47417            return;
47418        }
47419
47420        levels = levels || [];
47421        var hasColorDefine;
47422        each$1(levels, function (levelDefine) {
47423            var model = new Model(levelDefine);
47424            var modelColor = model.get('color');
47425
47426            if (model.get('itemStyle.color')
47427                || (modelColor && modelColor !== 'none')
47428            ) {
47429                hasColorDefine = true;
47430            }
47431        });
47432
47433        if (!hasColorDefine) {
47434            var level0 = levels[0] || (levels[0] = {});
47435            level0.color = globalColorList.slice();
47436        }
47437
47438        return levels;
47439    }
47440
47441    /*
47442* Licensed to the Apache Software Foundation (ASF) under one
47443* or more contributor license agreements.  See the NOTICE file
47444* distributed with this work for additional information
47445* regarding copyright ownership.  The ASF licenses this file
47446* to you under the Apache License, Version 2.0 (the
47447* "License"); you may not use this file except in compliance
47448* with the License.  You may obtain a copy of the License at
47449*
47450*   http://www.apache.org/licenses/LICENSE-2.0
47451*
47452* Unless required by applicable law or agreed to in writing,
47453* software distributed under the License is distributed on an
47454* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
47455* KIND, either express or implied.  See the License for the
47456* specific language governing permissions and limitations
47457* under the License.
47458*/
47459
47460    var TEXT_PADDING = 8;
47461    var ITEM_GAP = 8;
47462    var ARRAY_LENGTH = 5;
47463
47464    function Breadcrumb(containerGroup) {
47465        /**
47466         * @private
47467         * @type {module:zrender/container/Group}
47468         */
47469        this.group = new Group();
47470
47471        containerGroup.add(this.group);
47472    }
47473
47474    Breadcrumb.prototype = {
47475
47476        constructor: Breadcrumb,
47477
47478        render: function (seriesModel, api, targetNode, onSelect) {
47479            var model = seriesModel.getModel('breadcrumb');
47480            var thisGroup = this.group;
47481
47482            thisGroup.removeAll();
47483
47484            if (!model.get('show') || !targetNode) {
47485                return;
47486            }
47487
47488            var normalStyleModel = model.getModel('itemStyle');
47489            // var emphasisStyleModel = model.getModel('emphasis.itemStyle');
47490            var textStyleModel = normalStyleModel.getModel('textStyle');
47491
47492            var layoutParam = {
47493                pos: {
47494                    left: model.get('left'),
47495                    right: model.get('right'),
47496                    top: model.get('top'),
47497                    bottom: model.get('bottom')
47498                },
47499                box: {
47500                    width: api.getWidth(),
47501                    height: api.getHeight()
47502                },
47503                emptyItemWidth: model.get('emptyItemWidth'),
47504                totalWidth: 0,
47505                renderList: []
47506            };
47507
47508            this._prepare(targetNode, layoutParam, textStyleModel);
47509            this._renderContent(seriesModel, layoutParam, normalStyleModel, textStyleModel, onSelect);
47510
47511            positionElement(thisGroup, layoutParam.pos, layoutParam.box);
47512        },
47513
47514        /**
47515         * Prepare render list and total width
47516         * @private
47517         */
47518        _prepare: function (targetNode, layoutParam, textStyleModel) {
47519            for (var node = targetNode; node; node = node.parentNode) {
47520                var text = node.getModel().get('name');
47521                var textRect = textStyleModel.getTextRect(text);
47522                var itemWidth = Math.max(
47523                    textRect.width + TEXT_PADDING * 2,
47524                    layoutParam.emptyItemWidth
47525                );
47526                layoutParam.totalWidth += itemWidth + ITEM_GAP;
47527                layoutParam.renderList.push({node: node, text: text, width: itemWidth});
47528            }
47529        },
47530
47531        /**
47532         * @private
47533         */
47534        _renderContent: function (
47535            seriesModel, layoutParam, normalStyleModel, textStyleModel, onSelect
47536        ) {
47537            // Start rendering.
47538            var lastX = 0;
47539            var emptyItemWidth = layoutParam.emptyItemWidth;
47540            var height = seriesModel.get('breadcrumb.height');
47541            var availableSize = getAvailableSize(layoutParam.pos, layoutParam.box);
47542            var totalWidth = layoutParam.totalWidth;
47543            var renderList = layoutParam.renderList;
47544
47545            for (var i = renderList.length - 1; i >= 0; i--) {
47546                var item = renderList[i];
47547                var itemNode = item.node;
47548                var itemWidth = item.width;
47549                var text = item.text;
47550
47551                // Hdie text and shorten width if necessary.
47552                if (totalWidth > availableSize.width) {
47553                    totalWidth -= itemWidth - emptyItemWidth;
47554                    itemWidth = emptyItemWidth;
47555                    text = null;
47556                }
47557
47558                var el = new Polygon({
47559                    shape: {
47560                        points: makeItemPoints(
47561                            lastX, 0, itemWidth, height,
47562                            i === renderList.length - 1, i === 0
47563                        )
47564                    },
47565                    style: defaults(
47566                        normalStyleModel.getItemStyle(),
47567                        {
47568                            lineJoin: 'bevel',
47569                            text: text,
47570                            textFill: textStyleModel.getTextColor(),
47571                            textFont: textStyleModel.getFont()
47572                        }
47573                    ),
47574                    z: 10,
47575                    onclick: curry(onSelect, itemNode)
47576                });
47577                this.group.add(el);
47578
47579                packEventData(el, seriesModel, itemNode);
47580
47581                lastX += itemWidth + ITEM_GAP;
47582            }
47583        },
47584
47585        /**
47586         * @override
47587         */
47588        remove: function () {
47589            this.group.removeAll();
47590        }
47591    };
47592
47593    function makeItemPoints(x, y, itemWidth, itemHeight, head, tail) {
47594        var points = [
47595            [head ? x : x - ARRAY_LENGTH, y],
47596            [x + itemWidth, y],
47597            [x + itemWidth, y + itemHeight],
47598            [head ? x : x - ARRAY_LENGTH, y + itemHeight]
47599        ];
47600        !tail && points.splice(2, 0, [x + itemWidth + ARRAY_LENGTH, y + itemHeight / 2]);
47601        !head && points.push([x, y + itemHeight / 2]);
47602        return points;
47603    }
47604
47605// Package custom mouse event.
47606    function packEventData(el, seriesModel, itemNode) {
47607        el.eventData = {
47608            componentType: 'series',
47609            componentSubType: 'treemap',
47610            seriesIndex: seriesModel.componentIndex,
47611            seriesName: seriesModel.name,
47612            seriesType: 'treemap',
47613            selfType: 'breadcrumb', // Distinguish with click event on treemap node.
47614            nodeData: {
47615                dataIndex: itemNode && itemNode.dataIndex,
47616                name: itemNode && itemNode.name
47617            },
47618            treePathInfo: itemNode && wrapTreePathInfo(itemNode, seriesModel)
47619        };
47620    }
47621
47622    /*
47623* Licensed to the Apache Software Foundation (ASF) under one
47624* or more contributor license agreements.  See the NOTICE file
47625* distributed with this work for additional information
47626* regarding copyright ownership.  The ASF licenses this file
47627* to you under the Apache License, Version 2.0 (the
47628* "License");
47628 you may not use this file except in compliance
47629* with the License.  You may obtain a copy of the License at
47630*
47631*   http://www.apache.org/licenses/LICENSE-2.0
47632*
47633* Unless required by applicable law or agreed to in writing,
47634* software distributed under the License is distributed on an
47635* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
47636* KIND, either express or implied.  See the License for the
47637* specific language governing permissions and limitations
47638* under the License.
47639*/
47640
47641    /**
47642     * @param {number} [time=500] Time in ms
47643     * @param {string} [easing='linear']
47644     * @param {number} [delay=0]
47645     * @param {Function} [callback]
47646     *
47647     * @example
47648     *  // Animate position
47649     *  animation
47650     *      .createWrap()
47651     *      .add(el1, {position: [10, 10]})
47652     *      .add(el2, {shape: {width: 500}, style: {fill: 'red'}}, 400)
47653     *      .done(function () { // done })
47654     *      .start('cubicOut');
47655     */
47656    function createWrap() {
47657
47658        var storage = [];
47659        var elExistsMap = {};
47660        var doneCallback;
47661
47662        return {
47663
47664            /**
47665             * Caution: a el can only be added once, otherwise 'done'
47666             * might not be called. This method checks this (by el.id),
47667             * suppresses adding and returns false when existing el found.
47668             *
47669             * @param {modele:zrender/Element} el
47670             * @param {Object} target
47671             * @param {number} [time=500]
47672             * @param {number} [delay=0]
47673             * @param {string} [easing='linear']
47674             * @return {boolean} Whether adding succeeded.
47675             *
47676             * @example
47677             *     add(el, target, time, delay, easing);
47678             *     add(el, target, time, easing);
47679             *     add(el, target, time);
47680             *     add(el, target);
47681             */
47682            add: function (el, target, time, delay, easing) {
47683                if (isString(delay)) {
47684                    easing = delay;
47685                    delay = 0;
47686                }
47687
47688                if (elExistsMap[el.id]) {
47689                    return false;
47690                }
47691                elExistsMap[el.id] = 1;
47692
47693                storage.push(
47694                    {el: el, target: target, time: time, delay: delay, easing: easing}
47695                );
47696
47697                return true;
47698            },
47699
47700            /**
47701             * Only execute when animation finished. Will not execute when any
47702             * of 'stop' or 'stopAnimation' called.
47703             *
47704             * @param {Function} callback
47705             */
47706            done: function (callback) {
47707                doneCallback = callback;
47708                return this;
47709            },
47710
47711            /**
47712             * Will stop exist animation firstly.
47713             */
47714            start: function () {
47715                var count = storage.length;
47716
47717                for (var i = 0, len = storage.length; i < len; i++) {
47718                    var item = storage[i];
47719                    item.el.animateTo(item.target, item.time, item.delay, item.easing, done);
47720                }
47721
47722                return this;
47723
47724                function done() {
47725                    count--;
47726                    if (!count) {
47727                        storage.length = 0;
47728                        elExistsMap = {};
47729                        doneCallback && doneCallback();
47730                    }
47731                }
47732            }
47733        };
47734    }
47735
47736    /*
47737* Licensed to the Apache Software Foundation (ASF) under one
47738* or more contributor license agreements.  See the NOTICE file
47739* distributed with this work for additional information
47740* regarding copyright ownership.  The ASF licenses this file
47741* to you under the Apache License, Version 2.0 (the
47742* "License"); you may not use this file except in compliance
47743* with the License.  You may obtain a copy of the License at
47744*
47745*   http://www.apache.org/licenses/LICENSE-2.0
47746*
47747* Unless required by applicable law or agreed to in writing,
47748* software distributed under the License is distributed on an
47749* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
47750* KIND, either express or implied.  See the License for the
47751* specific language governing permissions and limitations
47752* under the License.
47753*/
47754
47755    var bind$1 = bind;
47756    var Group$2 = Group;
47757    var Rect$1 = Rect;
47758    var each$8 = each$1;
47759
47760    var DRAG_THRESHOLD = 3;
47761    var PATH_LABEL_NOAMAL = ['label'];
47762    var PATH_LABEL_EMPHASIS = ['emphasis', 'label'];
47763    var PATH_UPPERLABEL_NORMAL = ['upperLabel'];
47764    var PATH_UPPERLABEL_EMPHASIS = ['emphasis', 'upperLabel'];
47765    var Z_BASE = 10; // Should bigger than every z.
47766    var Z_BG = 1;
47767    var Z_CONTENT = 2;
47768
47769    var getItemStyleEmphasis = makeStyleMapper([
47770        ['fill', 'color'],
47771        // `borderColor` and `borderWidth` has been occupied,
47772        // so use `stroke` to indicate the stroke of the rect.
47773        ['stroke', 'strokeColor'],
47774        ['lineWidth', 'strokeWidth'],
47775        ['shadowBlur'],
47776        ['shadowOffsetX'],
47777        ['shadowOffsetY'],
47778        ['shadowColor']
47779    ]);
47780    var getItemStyleNormal = function (model) {
47781        // Normal style props should include emphasis style props.
47782        var itemStyle = getItemStyleEmphasis(model);
47783        // Clear styles set by emphasis.
47784        itemStyle.stroke = itemStyle.fill = itemStyle.lineWidth = null;
47785        return itemStyle;
47786    };
47787
47788    extendChartView({
47789
47790        type: 'treemap',
47791
47792        /**
47793         * @override
47794         */
47795        init: function (o, api) {
47796
47797            /**
47798             * @private
47799             * @type {module:zrender/container/Group}
47800             */
47801            this._containerGroup;
47802
47803            /**
47804             * @private
47805             * @type {Object.<string, Array.<module:zrender/container/Group>>}
47806             */
47807            this._storage = createStorage();
47808
47809            /**
47810             * @private
47811             * @type {module:echarts/data/Tree}
47812             */
47813            this._oldTree;
47814
47815            /**
47816             * @private
47817             * @type {module:echarts/chart/treemap/Breadcrumb}
47818             */
47819            this._breadcrumb;
47820
47821            /**
47822             * @private
47823             * @type {module:echarts/component/helper/RoamController}
47824             */
47825            this._controller;
47826
47827            /**
47828             * 'ready', 'animating'
47829             * @private
47830             */
47831            this._state = 'ready';
47832        },
47833
47834        /**
47835         * @override
47836         */
47837        render: function (seriesModel, ecModel, api, payload) {
47838
47839            var models = ecModel.findComponents({
47840                mainType: 'series', subType: 'treemap', query: payload
47841            });
47842            if (indexOf(models, seriesModel) < 0) {
47843                return;
47844            }
47845
47846            this.seriesModel = seriesModel;
47847            this.api = api;
47848            this.ecModel = ecModel;
47849
47850            var types = ['treemapZoomToNode', 'treemapRootToNode'];
47851            var targetInfo = retrieveTargetInfo(payload, types, seriesModel);
47852            var payloadType = payload && payload.type;
47853            var layoutInfo = seriesModel.layoutInfo;
47854            var isInit = !this._oldTree;
47855            var thisStorage = this._storage;
47856
47857            // Mark new root when action is treemapRootToNode.
47858            var reRoot = (payloadType === 'treemapRootToNode' && targetInfo && thisStorage)
47859                ? {
47860                    rootNodeGroup: thisStorage.nodeGroup[targetInfo.node.getRawIndex()],
47861                    direction: payload.direction
47862                }
47863                : null;
47864
47865            var containerGroup = this._giveContainerGroup(layoutInfo);
47866
47867            var renderResult = this._doRender(containerGroup, seriesModel, reRoot);
47868            (
47869                !isInit && (
47870                    !payloadType
47871                    || payloadType === 'treemapZoomToNode'
47872                    || payloadType === 'treemapRootToNode'
47873                )
47874            )
47875                ? this._doAnimation(containerGroup, renderResult, seriesModel, reRoot)
47876                : renderResult.renderFinally();
47877
47878            this._resetController(api);
47879
47880            this._renderBreadcrumb(seriesModel, api, targetInfo);
47881        },
47882
47883        /**
47884         * @private
47885         */
47886        _giveContainerGroup: function (layoutInfo) {
47887            var containerGroup = this._containerGroup;
47888            if (!containerGroup) {
47889                // FIXME
47890                // åŠ ä¸€å±‚containerGroup是为了clip,但是现在clip功能并没有实现。
47891                containerGroup = this._containerGroup = new Group$2();
47892                this._initEvents(containerGroup);
47893                this.group.add(containerGroup);
47894            }
47895            containerGroup.attr('position', [layoutInfo.x, layoutInfo.y]);
47896
47897            return containerGroup;
47898        },
47899
47900        /**
47901         * @private
47902         */
47903        _doRender: function (containerGroup, seriesModel, reRoot) {
47904            var thisTree = seriesModel.getData().tree;
47905            var oldTree = this._oldTree;
47906
47907            // Clear last shape records.
47908            var lastsForAnimation = createStorage();
47909            var thisStorage = createStorage();
47910            var oldStorage = this._storage;
47911            var willInvisibleEls = [];
47912            var doRenderNode = curry(
47913                renderNode, seriesModel,
47914                thisStorage, oldStorage, reRoot,
47915                lastsForAnimation, willInvisibleEls
47916            );
47917
47918            // Notice: when thisTree and oldTree are the same tree (see list.cloneShallow),
47919            // the oldTree is actually losted, so we can not find all of the old graphic
47920            // elements from tree. So we use this stragegy: make element storage, move
47921            // from old storage to new storage, clear old storage.
47922
47923            dualTravel(
47924                thisTree.root ? [thisTree.root] : [],
47925                (oldTree && oldTree.root) ? [oldTree.root] : [],
47926                containerGroup,
47927                thisTree === oldTree || !oldTree,
47928                0
47929            );
47930
47931            // Process all removing.
47932            var willDeleteEls = clearStorage(oldStorage);
47933
47934            this._oldTree = thisTree;
47935            this._storage = thisStorage;
47936
47937            return {
47938                lastsForAnimation: lastsForAnimation,
47939                willDeleteEls: willDeleteEls,
47940                renderFinally: renderFinally
47941            };
47942
47943            function dualTravel(thisViewChildren, oldViewChildren, parentGroup, sameTree, depth) {
47944                // When 'render' is triggered by action,
47945                // 'this' and 'old' may be the same tree,
47946                // we use rawIndex in that case.
47947                if (sameTree) {
47948                    oldViewChildren = thisViewChildren;
47949                    each$8(thisViewChildren, function (child, index) {
47950                        !child.isRemoved() && processNode(index, index);
47951                    });
47952                }
47953                // Diff hierarchically (diff only in each subtree, but not whole).
47954                // because, consistency of view is important.
47955                else {
47956                    (new DataDiffer(oldViewChildren, thisViewChildren, getKey, getKey))
47957                        .add(processNode)
47958                        .update(processNode)
47959                        .remove(curry(processNode, null))
47960                        .execute();
47961                }
47962
47963                function getKey(node) {
47964                    // Identify by name or raw index.
47965                    return node.getId();
47966                }
47967
47968                function processNode(newIndex, oldIndex) {
47969                    var thisNode = newIndex != null ? thisViewChildren[newIndex] : null;
47970                    var oldNode = oldIndex != null ? oldViewChildren[oldIndex] : null;
47971
47972                    var group = doRenderNode(thisNode, oldNode, parentGroup, depth);
47973
47974                    group && dualTravel(
47975                        thisNode && thisNode.viewChildren || [],
47976                        oldNode && oldNode.viewChildren || [],
47977                        group,
47978                        sameTree,
47979                        depth + 1
47980                    );
47981                }
47982            }
47983
47984            function clearStorage(storage) {
47985                var willDeleteEls = createStorage();
47986                storage && each$8(storage, function (store, storageName) {
47987                    var delEls = willDeleteEls[storageName];
47988                    each$8(store, function (el) {
47989                        el && (delEls.push(el), el.__tmWillDelete = 1);
47990                    });
47991                });
47992                return willDeleteEls;
47993            }
47994
47995            function renderFinally() {
47996                each$8(willDeleteEls, function (els) {
47997                    each$8(els, function (el) {
47998                        el.parent && el.parent.remove(el);
47999                    });
48000                });
48001                each$8(willInvisibleEls, function (el) {
48002                    el.invisible = true;
48003                    // Setting invisible is for optimizing, so no need to set dirty,
48004                    // just mark as invisible.
48005                    el.dirty();
48006                });
48007            }
48008        },
48009
48010        /**
48011         * @private
48012         */
48013        _doAnimation: function (containerGroup, renderResult, seriesModel, reRoot) {
48014            if (!seriesModel.get('animation')) {
48015                return;
48016            }
48017
48018            var duration = seriesModel.get('animationDurationUpdate');
48019            var easing = seriesModel.get('animationEasing');
48020            var animationWrap = createWrap();
48021
48022            // Make delete animations.
48023            each$8(renderResult.willDeleteEls, function (store, storageName) {
48024                each$8(store, function (el, rawIndex) {
48025                    if (el.invisible) {
48026                        return;
48027                    }
48028
48029                    var parent = el.parent; // Always has parent, and parent is nodeGroup.
48030                    var target;
48031
48032                    if (reRoot && reRoot.direction === 'drillDown') {
48033                        target = parent === reRoot.rootNodeGroup
48034                            // This is the content element of view root.
48035                            // Only `content` will enter this branch, because
48036                            // `background` and `nodeGroup` will not be deleted.
48037                            ? {
48038                                shape: {
48039                                    x: 0,
48040                                    y: 0,
48041                                    width: parent.__tmNodeWidth,
48042                                    height: parent.__tmNodeHeight
48043                                },
48044                                style: {
48045                                    opacity: 0
48046                                }
48047                            }
48048                            // Others.
48049                            : {style: {opacity: 0}};
48050                    }
48051                    else {
48052                        var targetX = 0;
48053                        var targetY = 0;
48054
48055                        if (!parent.__tmWillDelete) {
48056                            // Let node animate to right-bottom corner, cooperating with fadeout,
48057                            // which is appropriate for user understanding.
48058                            // Divided by 2 for reRoot rolling up effect.
48059                            targetX = parent.__tmNodeWidth / 2;
48060                            targetY = parent.__tmNodeHeight / 2;
48061                        }
48062
48063                        target = storageName === 'nodeGroup'
48064                            ? {position: [targetX, targetY], style: {opacity: 0}}
48065                            : {
48066                                shape: {x: targetX, y: targetY, width: 0, height: 0},
48067                                style: {opacity: 0}
48068                            };
48069                    }
48070
48071                    target && animationWrap.add(el, target, duration, easing);
48072                });
48073            });
48074
48075            // Make other animations
48076            each$8(this._storage, function (store, storageName) {
48077                each$8(store, function (el, rawIndex) {
48078                    var last = renderResult.lastsForAnimation[storageName][rawIndex];
48079                    var target = {};
48080
48081                    if (!last) {
48082                        return;
48083                    }
48084
48085                    if (storageName === 'nodeGroup') {
48086                        if (last.old) {
48087                            target.position = el.position.slice();
48088                            el.attr('position', last.old);
48089                        }
48090                    }
48091                    else {
48092                        if (last.old) {
48093                            target.shape = extend({}, el.shape);
48094                            el.setShape(last.old);
48095                        }
48096
48097                        if (last.fadein) {
48098                            el.setStyle('opacity', 0);
48099                            target.style = {opacity: 1};
48100                        }
48101                        // When animation is stopped for succedent animation starting,
48102                        // el.style.opacity might not be 1
48103                        else if (el.style.opacity !== 1) {
48104                            target.style = {opacity: 1};
48105                        }
48106                    }
48107
48108                    animationWrap.add(el, target, duration, easing);
48109                });
48110            }, this);
48111
48112            this._state = 'animating';
48113
48114            animationWrap
48115                .done(bind$1(function () {
48116                    this._state = 'ready';
48117                    renderResult.renderFinally();
48118                }, this))
48119                .start();
48120        },
48121
48122        /**
48123         * @private
48124         */
48125        _resetController: function (api) {
48126            var controller = this._controller;
48127
48128            // Init controller.
48129            if (!controller) {
48130                controller = this._controller = new RoamController(api.getZr());
48131                controller.enable(this.seriesModel.get('roam'));
48132                controller.on('pan', bind$1(this._onPan, this));
48133                controller.on('zoom', bind$1(this._onZoom, this));
48134            }
48135
48136            var rect = new BoundingRect(0, 0, api.getWidth(), api.getHeight());
48137            controller.setPointerChecker(function (e, x, y) {
48138                return rect.contain(x, y);
48139            });
48140        },
48141
48142        /**
48143         * @private
48144         */
48145        _clearController: function () {
48146            var controller = this._controller;
48147            if (controller) {
48148                controller.dispose();
48149                controller = null;
48150            }
48151        },
48152
48153        /**
48154         * @private
48155         */
48156        _onPan: function (dx, dy) {
48157            if (this._state !== 'animating'
48158                && (Math.abs(dx) > DRAG_THRESHOLD || Math.abs(dy) > DRAG_THRESHOLD)
48159            ) {
48160                // These param must not be cached.
48161                var root = this.seriesModel.getData().tree.root;
48162
48163                if (!root) {
48164                    return;
48165                }
48166
48167                var rootLayout = root.getLayout();
48168
48169                if (!rootLayout) {
48170                    return;
48171                }
48172
48173                this.api.dispatchAction({
48174                    type: 'treemapMove',
48175                    from: this.uid,
48176                    seriesId: this.seriesModel.id,
48177                    rootRect: {
48178                        x: rootLayout.x + dx, y: rootLayout.y + dy,
48179                        width: rootLayout.width, height: rootLayout.height
48180                    }
48181                });
48182            }
48183        },
48184
48185        /**
48186         * @private
48187         */
48188        _onZoom: function (scale, mouseX, mouseY) {
48189            if (this._state !== 'animating') {
48190                // These param must not be cached.
48191                var root = this.seriesModel.getData().tree.root;
48192
48193                if (!root) {
48194                    return;
48195                }
48196
48197                var rootLayout = root.getLayout();
48198
48199                if (!rootLayout) {
48200                    return;
48201                }
48202
48203                var rect = new BoundingRect(
48204                    rootLayout.x, rootLayout.y, rootLayout.width, rootLayout.height
48205                );
48206                var layoutInfo = this.seriesModel.layoutInfo;
48207
48208                // Transform mouse coord from global to containerGroup.
48209                mouseX -= layoutInfo.x;
48210                mouseY -= layoutInfo.y;
48211
48212                // Scale root bounding rect.
48213                var m = create$1();
48214                translate(m, m, [-mouseX, -mouseY]);
48215                scale$1(m, m, [scale, scale]);
48216                translate(m, m, [mouseX, mouseY]);
48217
48218                rect.applyTransform(m);
48219
48220                this.api.dispatchAction({
48221                    type: 'treemapRender',
48222                    from: this.uid,
48223                    seriesId: this.seriesModel.id,
48224                    rootRect: {
48225                        x: rect.x, y: rect.y,
48226                        width: rect.width, height: rect.height
48227                    }
48228                });
48229            }
48230        },
48231
48232        /**
48233         * @private
48234         */
48235        _initEvents: function (containerGroup) {
48236            containerGroup.on('click', function (e) {
48237                if (this._state !== 'ready') {
48238                    return;
48239                }
48240
48241                var nodeClick = this.seriesModel.get('nodeClick', true);
48242
48243                if (!nodeClick) {
48244                    return;
48245                }
48246
48247                var targetInfo = this.findTarget(e.offsetX, e.offsetY);
48248
48249                if (!targetInfo) {
48250                    return;
48251                }
48252
48253                var node = targetInfo.node;
48254                if (node.getLayout().isLeafRoot) {
48255                    this._rootToNode(targetInfo);
48256                }
48257                else {
48258                    if (nodeClick === 'zoomToNode') {
48259                        this._zoomToNode(targetInfo);
48260                    }
48261                    else if (nodeClick === 'link') {
48262                        var itemModel = node.hostTree.data.getItemModel(node.dataIndex);
48263                        var link = itemModel.get('link', true);
48264                        var linkTarget = itemModel.get('target', true) || 'blank';
48265                        link && window.open(link, linkTarget);
48266                    }
48267                }
48268
48269            }, this);
48270        },
48271
48272        /**
48273         * @private
48274         */
48275        _renderBreadcrumb: function (seriesModel, api, targetInfo) {
48276            if (!targetInfo) {
48277                targetInfo = seriesModel.get('leafDepth', true) != null
48278                    ? {node: seriesModel.getViewRoot()}
48279                    // FIXME
48280                    // better way?
48281                    // Find breadcrumb tail on center of containerGroup.
48282                    : this.findTarget(api.getWidth() / 2, api.getHeight() / 2);
48283
48284                if (!targetInfo) {
48285                    targetInfo = {node: seriesModel.getData().tree.root};
48286                }
48287            }
48288
48289            (this._breadcrumb || (this._breadcrumb = new Breadcrumb(this.group)))
48290                .render(seriesModel, api, targetInfo.node, bind$1(onSelect, this));
48291
48292            function onSelect(node) {
48293                if (this._state !== 'animating') {
48294                    aboveViewRoot(seriesModel.getViewRoot(), node)
48295                        ? this._rootToNode({node: node})
48296                        : this._zoomToNode({node: node});
48297                }
48298            }
48299        },
48300
48301        /**
48302         * @override
48303         */
48304        remove: function () {
48305            this._clearController();
48306            this._containerGroup && this._containerGroup.removeAll();
48307            this._storage = createStorage();
48308            this._state = 'ready';
48309            this._breadcrumb && this._breadcrumb.remove();
48310        },
48311
48312        dispose: function () {
48313            this._clearController();
48314        },
48315
48316        /**
48317         * @private
48318         */
48319        _zoomToNode: function (targetInfo) {
48320            this.api.dispatchAction({
48321                type: 'treemapZoomToNode',
48322                from: this.uid,
48323                seriesId: this.seriesModel.id,
48324                targetNode: targetInfo.node
48325            });
48326        },
48327
48328        /**
48329         * @private
48330         */
48331        _rootToNode: function (targetInfo) {
48332            this.api.dispatchAction({
48333                type: 'treemapRootToNode',
48334                from: this.uid,
48335                seriesId: this.seriesModel.id,
48336                targetNode: targetInfo.node
48337            });
48338        },
48339
48340        /**
48341         * @public
48342         * @param {number} x Global coord x.
48343         * @param {number} y Global coord y.
48344         * @return {Object} info If not found, return undefined;
48345         * @return {number} info.node Target node.
48346         * @return {number} info.offsetX x refer to target node.
48347         * @return {number} info.offsetY y refer to target node.
48348         */
48349        findTarget: function (x, y) {
48350            var targetInfo;
48351            var viewRoot = this.seriesModel.getViewRoot();
48352
48353            viewRoot.eachNode({attr: 'viewChildren', order: 'preorder'}, function (node) {
48354                var bgEl = this._storage.background[node.getRawIndex()];
48355                // If invisible, there might be no element.
48356                if (bgEl) {
48357                    var point = bgEl.transformCoordToLocal(x, y);
48358                    var shape = bgEl.shape;
48359
48360                    // For performance consideration, dont use 'getBoundingRect'.
48361                    if (shape.x <= point[0]
48362                        && point[0] <= shape.x + shape.width
48363                        && shape.y <= point[1]
48364                        && point[1] <= shape.y + shape.height
48365                    ) {
48366                        targetInfo = {node: node, offsetX: point[0], offsetY: point[1]};
48367                    }
48368                    else {
48369                        return false; // Suppress visit subtree.
48370                    }
48371                }
48372            }, this);
48373
48374            return targetInfo;
48375        }
48376
48377    });
48378
48379    /**
48380     * @inner
48381     */
48382    function createStorage() {
48383        return {nodeGroup: [], background: [], content: []};
48384    }
48385
48386    /**
48387     * @inner
48388     * @return Return undefined means do not travel further.
48389     */
48390    function renderNode(
48391        seriesModel, thisStorage, oldStorage, reRoot,
48392        lastsForAnimation, willInvisibleEls,
48393        thisNode, oldNode, parentGroup, depth
48394    ) {
48395        // Whether under viewRoot.
48396        if (!thisNode) {
48397            // Deleting nodes will be performed finally. This method just find
48398            // element from old storage, or create new element, set them to new
48399            // storage, and set styles.
48400            return;
48401        }
48402
48403        // -------------------------------------------------------------------
48404        // Start of closure variables available in "Procedures in renderNode".
48405
48406        var thisLayout = thisNode.getLayout();
48407
48408        if (!thisLayout || !thisLayout.isInView) {
48409            return;
48410        }
48411
48412        var thisWidth = thisLayout.width;
48413        var thisHeight = thisLayout.height;
48414        var borderWidth = thisLayout.borderWidth;
48415        var thisInvisible = thisLayout.invisible;
48416
48417        var thisRawIndex = thisNode.getRawIndex();
48418        var oldRawIndex = oldNode && oldNode.getRawIndex();
48419
48420        var thisViewChildren = thisNode.viewChildren;
48421        var upperHeight = thisLayout.upperHeight;
48422        var isParent = thisViewChildren && thisViewChildren.length;
48423        var itemStyleNormalModel = thisNode.getModel('itemStyle');
48424        var itemStyleEmphasisModel = thisNode.getModel('emphasis.itemStyle');
48425
48426        // End of closure ariables available in "Procedures in renderNode".
48427        // -----------------------------------------------------------------
48428
48429        // Node group
48430        var group = giveGraphic('nodeGroup', Group$2);
48431
48432        if (!group) {
48433            return;
48434        }
48435
48436        parentGroup.add(group);
48437        // x,y are not set when el is above view root.
48438        group.attr('position', [thisLayout.x || 0, thisLayout.y || 0]);
48439        group.__tmNodeWidth = thisWidth;
48440        group.__tmNodeHeight = thisHeight;
48441
48442        if (thisLayout.isAboveViewRoot) {
48443            return group;
48444        }
48445
48446        // Background
48447        var bg = giveGraphic('background', Rect$1, depth, Z_BG);
48448        bg && renderBackground(group, bg, isParent && thisLayout.upperHeight);
48449
48450        // No children, render content.
48451        if (!isParent) {
48452            var content = giveGraphic('content', Rect$1, depth, Z_CONTENT);
48453            content && renderContent(group, content);
48454        }
48455
48456        return group;
48457
48458        // ----------------------------
48459        // | Procedures in renderNode |
48460        // ----------------------------
48461
48462        function renderBackground(group, bg, useUpperLabel) {
48463            // For tooltip.
48464            bg.dataIndex = thisNode.dataIndex;
48465            bg.seriesIndex = seriesModel.seriesIndex;
48466
48467            bg.setShape({x: 0, y: 0, width: thisWidth, height: thisHeight});
48468            var visualBorderColor = thisNode.getVisual('borderColor', true);
48469            var emphasisBorderColor = itemStyleEmphasisModel.get('borderColor');
48470
48471            updateStyle(bg, function () {
48472                var normalStyle = getItemStyleNormal(itemStyleNormalModel);
48473                normalStyle.fill = visualBorderColor;
48474                var emphasisStyle = getItemStyleEmphasis(itemStyleEmphasisModel);
48475                emphasisStyle.fill = emphasisBorderColor;
48476
48477                if (useUpperLabel) {
48478                    var upperLabelWidth = thisWidth - 2 * borderWidth;
48479
48480                    prepareText(
48481                        normalStyle, emphasisStyle, visualBorderColor, upperLabelWidth, upperHeight,
48482                        {x: borderWidth, y: 0, width: upperLabelWidth, height: upperHeight}
48483                    );
48484                }
48485                // For old bg.
48486                else {
48487                    normalStyle.text = emphasisStyle.text = null;
48488                }
48489
48490                bg.setStyle(normalStyle);
48491                setHoverStyle(bg, emphasisStyle);
48492            });
48493
48494            group.add(bg);
48495        }
48496
48497        function renderContent(group, content) {
48498            // For tooltip.
48499            content.dataIndex = thisNode.dataIndex;
48500            content.seriesIndex = seriesModel.seriesIndex;
48501
48502            var contentWidth = Math.max(thisWidth - 2 * borderWidth, 0);
48503            var contentHeight = Math.max(thisHeight - 2 * borderWidth, 0);
48504
48505            content.culling = true;
48506            content.setShape({
48507                x: borderWidth,
48508                y: borderWidth,
48509                width: contentWidth,
48510                height: contentHeight
48511            });
48512
48513            var visualColor = thisNode.getVisual('color', true);
48514            updateStyle(content, function () {
48515                var normalStyle = getItemStyleNormal(itemStyleNormalModel);
48516                normalStyle.fill = visualColor;
48517                var emphasisStyle = getItemStyleEmphasis(itemStyleEmphasisModel);
48518
48519                prepareText(normalStyle, emphasisStyle, visualColor, contentWidth, contentHeight);
48520
48521                content.setStyle(normalStyle);
48522                setHoverStyle(content, emphasisStyle);
48523            });
48524
48525            group.add(content);
48526        }
48527
48528        function updateStyle(element, cb) {
48529            if (!thisInvisible) {
48530                // If invisible, do not set visual, otherwise the element will
48531                // change immediately before animation. We think it is OK to
48532                // remain its origin color when moving out of the view window.
48533                cb();
48534
48535                if (!element.__tmWillVisible) {
48536                    element.invisible = false;
48537                }
48538            }
48539            else {
48540                // Delay invisible setting utill animation finished,
48541                // avoid element vanish suddenly before animation.
48542                !element.invisible && willInvisibleEls.push(element);
48543            }
48544        }
48545
48546        function prepareText(normalStyle, emphasisStyle, visualColor, width, height, upperLabelRect) {
48547            var nodeModel = thisNode.getModel();
48548            var text = retrieve(
48549                seriesModel.getFormattedLabel(
48550                    thisNode.dataIndex, 'normal', null, null, upperLabelRect ? 'upperLabel' : 'label'
48551                ),
48552                nodeModel.get('name')
48553            );
48554            if (!upperLabelRect && thisLayout.isLeafRoot) {
48555                var iconChar = seriesModel.get('drillDownIcon', true);
48556                text = iconChar ? iconChar + ' ' + text : text;
48557            }
48558
48559            var normalLabelModel = nodeModel.getModel(
48560                upperLabelRect ? PATH_UPPERLABEL_NORMAL : PATH_LABEL_NOAMAL
48561            );
48562            var emphasisLabelModel = nodeModel.getModel(
48563                upperLabelRect ? PATH_UPPERLABEL_EMPHASIS : PATH_LABEL_EMPHASIS
48564            );
48565
48566            var isShow = normalLabelModel.getShallow('show');
48567
48568            setLabelStyle(
48569                normalStyle, emphasisStyle, normalLabelModel, emphasisLabelModel,
48570                {
48571                    defaultText: isShow ? text : null,
48572                    autoColor: visualColor,
48573                    isRectText: true
48574                }
48575            );
48576
48577            upperLabelRect && (normalStyle.textRect = clone(upperLabelRect));
48578
48579            normalStyle.truncate = (isShow && normalLabelModel.get('ellipsis'))
48580                ? {
48581                    outerWidth: width,
48582                    outerHeight: height,
48583                    minChar: 2
48584                }
48585                : null;
48586        }
48587
48588        function giveGraphic(storageName, Ctor, depth, z) {
48589            var element = oldRawIndex != null && oldStorage[storageName][oldRawIndex];
48590            var lasts = lastsForAnimation[storageName];
48591
48592            if (element) {
48593                // Remove from oldStorage
48594                oldStorage[storageName][oldRawIndex] = null;
48595                prepareAnimationWhenHasOld(lasts, element, storageName);
48596            }
48597            // If invisible and no old element, do not create new element (for optimizing).
48598            else if (!thisInvisible) {
48599                element = new Ctor({z: calculateZ(depth, z)});
48600                element.__tmDepth = depth;
48601                element.__tmStorageName = storageName;
48602                prepareAnimationWhenNoOld(lasts, element, storageName);
48603            }
48604
48605            // Set to thisStorage
48606            return (thisStorage[storageName][thisRawIndex] = element);
48607        }
48608
48609        function prepareAnimationWhenHasOld(lasts, element, storageName) {
48610            var lastCfg = lasts[thisRawIndex] = {};
48611            lastCfg.old = storageName === 'nodeGroup'
48612                ? element.position.slice()
48613                : extend({}, element.shape);
48614        }
48615
48616        // If a element is new, we need to find the animation start point carefully,
48617        // otherwise it will looks strange when 'zoomToNode'.
48618        function prepareAnimationWhenNoOld(lasts, element, storageName) {
48619            var lastCfg = lasts[thisRawIndex] = {};
48620            var parentNode = thisNode.parentNode;
48621
48622            if (parentNode && (!reRoot || reRoot.direction === 'drillDown')) {
48623                var parentOldX = 0;
48624                var parentOldY = 0;
48625
48626                // New nodes appear from right-bottom corner in 'zoomToNode' animation.
48627                // For convenience, get old bounding rect from background.
48628                var parentOldBg = lastsForAnimation.background[parentNode.getRawIndex()];
48629                if (!reRoot && parentOldBg && parentOldBg.old) {
48630                    parentOldX = parentOldBg.old.width;
48631                    parentOldY = parentOldBg.old.height;
48632                }
48633
48634                // When no parent old shape found, its parent is new too,
48635                // so we can just use {x:0, y:0}.
48636                lastCfg.old = storageName === 'nodeGroup'
48637                    ? [0, parentOldY]
48638                    : {x: parentOldX, y: parentOldY, width: 0, height: 0};
48639            }
48640
48641            // Fade in, user can be aware that these nodes are new.
48642            lastCfg.fadein = storageName !== 'nodeGroup';
48643        }
48644    }
48645
48646// We can not set all backgroud with the same z, Because the behaviour of
48647// drill down and roll up differ background creation sequence from tree
48648// hierarchy sequence, which cause that lowser background element overlap
48649// upper ones. So we calculate z based on depth.
48650// Moreover, we try to shrink down z interval to [0, 1] to avoid that
48651// treemap with large z overlaps other components.
48652    function calculateZ(depth, zInLevel) {
48653        var zb = depth * Z_BASE + zInLevel;
48654        return (zb - 1) / zb;
48655    }
48656
48657    /*
48658* Licensed to the Apache Software Foundation (ASF) under one
48659* or more contributor license agreements.  See the NOTICE file
48660* distributed with this work for additional information
48661* regarding copyright ownership.  The ASF licenses this file
48662* to you under the Apache License, Version 2.0 (the
48663* "License");
48663 you may not use this file except in compliance
48664* with the License.  You may obtain a copy of the License at
48665*
48666*   http://www.apache.org/licenses/LICENSE-2.0
48667*
48668* Unless required by applicable law or agreed to in writing,
48669* software distributed under the License is distributed on an
48670* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
48671* KIND, either express or implied.  See the License for the
48672* specific language governing permissions and limitations
48673* under the License.
48674*/
48675
48676    /**
48677     * @file Treemap action
48678     */
48679
48680    var noop$1 = function () {};
48681
48682    var actionTypes = [
48683        'treemapZoomToNode',
48684        'treemapRender',
48685        'treemapMove'
48686    ];
48687
48688    for (var i$2 = 0; i$2 < actionTypes.length; i$2++) {
48689        registerAction({type: actionTypes[i$2], update: 'updateView'}, noop$1);
48690    }
48691
48692    registerAction(
48693        {type: 'treemapRootToNode', update: 'updateView'},
48694        function (payload, ecModel) {
48695
48696            ecModel.eachComponent(
48697                {mainType: 'series', subType: 'treemap', query: payload},
48698                handleRootToNode
48699            );
48700
48701            function handleRootToNode(model, index) {
48702                var types = ['treemapZoomToNode', 'treemapRootToNode'];
48703                var targetInfo = retrieveTargetInfo(payload, types, model);
48704
48705                if (targetInfo) {
48706                    var originViewRoot = model.getViewRoot();
48707                    if (originViewRoot) {
48708                        payload.direction = aboveViewRoot(originViewRoot, targetInfo.node)
48709                            ? 'rollUp' : 'drillDown';
48710                    }
48711                    model.resetViewRoot(targetInfo.node);
48712                }
48713            }
48714        }
48715    );
48716
48717    /*
48718* Licensed to the Apache Software Foundation (ASF) under one
48719* or more contributor license agreements.  See the NOTICE file
48720* distributed with this work for additional information
48721* regarding copyright ownership.  The ASF licenses this file
48722* to you under the Apache License, Version 2.0 (the
48723* "License"); you may not use this file except in compliance
48724* with the License.  You may obtain a copy of the License at
48725*
48726*   http://www.apache.org/licenses/LICENSE-2.0
48727*
48728* Unless required by applicable law or agreed to in writing,
48729* software distributed under the License is distributed on an
48730* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
48731* KIND, either express or implied.  See the License for the
48732* specific language governing permissions and limitations
48733* under the License.
48734*/
48735
48736    var each$9 = each$1;
48737    var isObject$5 = isObject$1;
48738
48739    var CATEGORY_DEFAULT_VISUAL_INDEX = -1;
48740
48741    /**
48742     * @param {Object} option
48743     * @param {string} [option.type] See visualHandlers.
48744     * @param {string} [option.mappingMethod] 'linear' or 'piecewise' or 'category' or 'fixed'
48745     * @param {Array.<number>=} [option.dataExtent] [minExtent, maxExtent],
48746     *                                              required when mappingMethod is 'linear'
48747     * @param {Array.<Object>=} [option.pieceList] [
48748     *                                             {value: someValue},
48749     *                                             {interval: [min1, max1], visual: {...}},
48750     *                                             {interval: [min2, max2]}
48751     *                                             ],
48752     *                                            required when mappingMethod is 'piecewise'.
48753     *                                            Visual for only each piece can be specified.
48754     * @param {Array.<string|Object>=} [option.categories] ['cate1', 'cate2']
48755     *                                            required when mappingMethod is 'category'.
48756     *                                            If no option.categories, categories is set
48757     *                                            as [0, 1, 2, ...].
48758     * @param {boolean} [option.loop=false] Whether loop mapping when mappingMethod is 'category'.
48759     * @param {(Array|Object|*)} [option.visual]  Visual data.
48760     *                                            when mappingMethod is 'category',
48761     *                                            visual data can be array or object
48762     *                                            (like: {cate1: '#222', none: '#fff'})
48763     *                                            or primary types (which represents
48764     *                                            defualt category visual), otherwise visual
48765     *                                            can be array or primary (which will be
48766     *                                            normalized to array).
48767     *
48768     */
48769    var VisualMapping = function (option) {
48770        var mappingMethod = option.mappingMethod;
48771        var visualType = option.type;
48772
48773        /**
48774         * @readOnly
48775         * @type {Object}
48776         */
48777        var thisOption = this.option = clone(option);
48778
48779        /**
48780         * @readOnly
48781         * @type {string}
48782         */
48783        this.type = visualType;
48784
48785        /**
48786         * @readOnly
48787         * @type {string}
48788         */
48789        this.mappingMethod = mappingMethod;
48790
48791        /**
48792         * @private
48793         * @type {Function}
48794         */
48795        this._normalizeData = normalizers[mappingMethod];
48796
48797        var visualHandler = visualHandlers[visualType];
48798
48799        /**
48800         * @public
48801         * @type {Function}
48802         */
48803        this.applyVisual = visualHandler.applyVisual;
48804
48805        /**
48806         * @public
48807         * @type {Function}
48808         */
48809        this.getColorMapper = visualHandler.getColorMapper;
48810
48811        /**
48812         * @private
48813         * @type {Function}
48814         */
48815        this._doMap = visualHandler._doMap[mappingMethod];
48816
48817        if (mappingMethod === 'piecewise') {
48818            normalizeVisualRange(thisOption);
48819            preprocessForPiecewise(thisOption);
48820        }
48821        else if (mappingMethod === 'category') {
48822            thisOption.categories
48823                ? preprocessForSpecifiedCategory(thisOption)
48824                // categories is ordinal when thisOption.categories not specified,
48825                // which need no more preprocess except normalize visual.
48826                : normalizeVisualRange(thisOption, true);
48827        }
48828        else { // mappingMethod === 'linear' or 'fixed'
48829            assert$1(mappingMethod !== 'linear' || thisOption.dataExtent);
48830            normalizeVisualRange(thisOption);
48831        }
48832    };
48833
48834    VisualMapping.prototype = {
48835
48836        constructor: VisualMapping,
48837
48838        mapValueToVisual: function (value) {
48839            var normalized = this._normalizeData(value);
48840            return this._doMap(normalized, value);
48841        },
48842
48843        getNormalizer: function () {
48844            return bind(this._normalizeData, this);
48845        }
48846    };
48847
48848    var visualHandlers = VisualMapping.visualHandlers = {
48849
48850        color: {
48851
48852            applyVisual: makeApplyVisual('color'),
48853
48854            /**
48855             * Create a mapper function
48856             * @return {Function}
48857             */
48858            getColorMapper: function () {
48859                var thisOption = this.option;
48860
48861                return bind(
48862                    thisOption.mappingMethod === 'category'
48863                        ? function (value, isNormalized) {
48864                            !isNormalized && (value = this._normalizeData(value));
48865                            return doMapCategory.call(this, value);
48866                        }
48867                        : function (value, isNormalized, out) {
48868                            // If output rgb array
48869                            // which will be much faster and useful in pixel manipulation
48870                            var returnRGBArray = !!out;
48871                            !isNormalized && (value = this._normalizeData(value));
48872                            out = fastLerp(value, thisOption.parsedVisual, out);
48873                            return returnRGBArray ? out : stringify(out, 'rgba');
48874                        },
48875                    this
48876                );
48877            },
48878
48879            _doMap: {
48880                linear: function (normalized) {
48881                    return stringify(
48882                        fastLerp(normalized, this.option.parsedVisual),
48883                        'rgba'
48884                    );
48885                },
48886                category: doMapCategory,
48887                piecewise: function (normalized, value) {
48888                    var result = getSpecifiedVisual.call(this, value);
48889                    if (result == null) {
48890                        result = stringify(
48891                            fastLerp(normalized, this.option.parsedVisual),
48892                            'rgba'
48893                        );
48894                    }
48895                    return result;
48896                },
48897                fixed: doMapFixed
48898            }
48899        },
48900
48901        colorHue: makePartialColorVisualHandler(function (color, value) {
48902            return modifyHSL(color, value);
48903        }),
48904
48905        colorSaturation: makePartialColorVisualHandler(function (color, value) {
48906            return modifyHSL(color, null, value);
48907        }),
48908
48909        colorLightness: makePartialColorVisualHandler(function (color, value) {
48910            return modifyHSL(color, null, null, value);
48911        }),
48912
48913        colorAlpha: makePartialColorVisualHandler(function (color, value) {
48914            return modifyAlpha(color, value);
48915        }),
48916
48917        opacity: {
48918            applyVisual: makeApplyVisual('opacity'),
48919            _doMap: makeDoMap([0, 1])
48920        },
48921
48922        liftZ: {
48923            applyVisual: makeApplyVisual('liftZ'),
48924            _doMap: {
48925                linear: doMapFixed,
48926                category: doMapFixed,
48927                piecewise: doMapFixed,
48928                fixed: doMapFixed
48929            }
48930        },
48931
48932        symbol: {
48933            applyVisual: function (value, getter, setter) {
48934                var symbolCfg = this.mapValueToVisual(value);
48935                if (isString(symbolCfg)) {
48936                    setter('symbol', symbolCfg);
48937                }
48938                else if (isObject$5(symbolCfg)) {
48939                    for (var name in symbolCfg) {
48940                        if (symbolCfg.hasOwnProperty(name)) {
48941                            setter(name, symbolCfg[name]);
48942                        }
48943                    }
48944                }
48945            },
48946            _doMap: {
48947                linear: doMapToArray,
48948                category: doMapCategory,
48949                piecewise: function (normalized, value) {
48950                    var result = getSpecifiedVisual.call(this, value);
48951                    if (result == null) {
48952                        result = doMapToArray.call(this, normalized);
48953                    }
48954                    return result;
48955                },
vendor: 4,455 bytes, lines 48956-49093
48956                fixed: doMapFixed
48957            }
48958        },
48959
48960        symbolSize: {
48961            applyVisual: makeApplyVisual('symbolSize'),
48962            _doMap: makeDoMap([0, 1])
48963        }
48964    };
48965
48966
48967    function preprocessForPiecewise(thisOption) {
48968        var pieceList = thisOption.pieceList;
48969        thisOption.hasSpecialVisual = false;
48970
48971        each$1(pieceList, function (piece, index) {
48972            piece.originIndex = index;
48973            // piece.visual is "result visual value" but not
48974            // a visual range, so it does not need to be normalized.
48975            if (piece.visual != null) {
48976                thisOption.hasSpecialVisual = true;
48977            }
48978        });
48979    }
48980
48981    function preprocessForSpecifiedCategory(thisOption) {
48982        // Hash categories.
48983        var categories = thisOption.categories;
48984        var visual = thisOption.visual;
48985
48986        var categoryMap = thisOption.categoryMap = {};
48987        each$9(categories, function (cate, index) {
48988            categoryMap[cate] = index;
48989        });
48990
48991        // Process visual map input.
48992        if (!isArray(visual)) {
48993            var visualArr = [];
48994
48995            if (isObject$1(visual)) {
48996                each$9(visual, function (v, cate) {
48997                    var index = categoryMap[cate];
48998                    visualArr[index != null ? index : CATEGORY_DEFAULT_VISUAL_INDEX] = v;
48999                });
49000            }
49001            else { // Is primary type, represents default visual.
49002                visualArr[CATEGORY_DEFAULT_VISUAL_INDEX] = visual;
49003            }
49004
49005            visual = setVisualToOption(thisOption, visualArr);
49006        }
49007
49008        // Remove categories that has no visual,
49009        // then we can mapping them to CATEGORY_DEFAULT_VISUAL_INDEX.
49010        for (var i = categories.length - 1; i >= 0; i--) {
49011            if (visual[i] == null) {
49012                delete categoryMap[categories[i]];
49013                categories.pop();
49014            }
49015        }
49016    }
49017
49018    function normalizeVisualRange(thisOption, isCategory) {
49019        var visual = thisOption.visual;
49020        var visualArr = [];
49021
49022        if (isObject$1(visual)) {
49023            each$9(visual, function (v) {
49024                visualArr.push(v);
49025            });
49026        }
49027        else if (visual != null) {
49028            visualArr.push(visual);
49029        }
49030
49031        var doNotNeedPair = {color: 1, symbol: 1};
49032
49033        if (!isCategory
49034            && visualArr.length === 1
49035            && !doNotNeedPair.hasOwnProperty(thisOption.type)
49036        ) {
49037            // Do not care visualArr.length === 0, which is illegal.
49038            visualArr[1] = visualArr[0];
49039        }
49040
49041        setVisualToOption(thisOption, visualArr);
49042    }
49043
49044    function makePartialColorVisualHandler(applyValue) {
49045        return {
49046            applyVisual: function (value, getter, setter) {
49047                value = this.mapValueToVisual(value);
49048                // Must not be array value
49049                setter('color', applyValue(getter('color'), value));
49050            },
49051            _doMap: makeDoMap([0, 1])
49052        };
49053    }
49054
49055    function doMapToArray(normalized) {
49056        var visual = this.option.visual;
49057        return visual[
49058            Math.round(linearMap(normalized, [0, 1], [0, visual.length - 1], true))
49059            ] || {};
49060    }
49061
49062    function makeApplyVisual(visualType) {
49063        return function (value, getter, setter) {
49064            setter(visualType, this.mapValueToVisual(value));
49065        };
49066    }
49067
49068    function doMapCategory(normalized) {
49069        var visual = this.option.visual;
49070        return visual[
49071            (this.option.loop && normalized !== CATEGORY_DEFAULT_VISUAL_INDEX)
49072                ? normalized % visual.length
49073                : normalized
49074            ];
49075    }
49076
49077    function doMapFixed() {
49078        return this.option.visual[0];
49079    }
49080
49081    function makeDoMap(sourceExtent) {
49082        return {
49083            linear: function (normalized) {
49084                return linearMap(normalized, sourceExtent, this.option.visual, true);
49085            },
49086            category: doMapCategory,
49087            piecewise: function (normalized, value) {
49088                var result = getSpecifiedVisual.call(this, value);
49089                if (result == null) {
49090                    result = linearMap(normalized, sourceExtent, this.option.visual, true);
49091                }
49092                return result;
49093            },
49094            fixed: doMapFixed
49095        };
49096    }
49097
49098    function getSpecifiedVisual(value) {
49099        var thisOption = this.option;
49100        var pieceList = thisOption.pieceList;
49101        if (thisOption.hasSpecialVisual) {
49102            var pieceIndex = VisualMapping.findPieceIndex(value, pieceList);
49103            var piece = pieceList[pieceIndex];
49104            if (piece && piece.visual) {
49105                return piece.visual[this.type];
49106            }
49107        }
49108    }
49109
49110    function setVisualToOption(thisOption, visualArr) {
49111        thisOption.visual = visualArr;
49112        if (thisOption.type === 'color') {
49113            thisOption.parsedVisual = map(visualArr, function (item) {
49114                return parse(item);
49115            });
49116        }
49117        return visualArr;
49118    }
49119
49120
49121    /**
49122     * Normalizers by mapping methods.
49123     */
49124    var normalizers = {
49125
49126        linear: function (value) {
49127            return linearMap(value, this.option.dataExtent, [0, 1], true);
49128        },
49129
49130        piecewise: function (value) {
49131            var pieceList = this.option.pieceList;
49132            var pieceIndex = VisualMapping.findPieceIndex(value, pieceList, true);
49133            if (pieceIndex != null) {
49134                return linearMap(pieceIndex, [0, pieceList.length - 1], [0, 1], true);
49135            }
49136        },
49137
49138        category: function (value) {
49139            var index = this.option.categories
49140                ? this.option.categoryMap[value]
49141                : value; // ordinal
49142            return index == null ? CATEGORY_DEFAULT_VISUAL_INDEX : index;
49143        },
49144
49145        fixed: noop
49146    };
49147
49148
49149
49150    /**
49151     * List available visual types.
49152     *
49153     * @public
49154     * @return {Array.<string>}
49155     */
49156    VisualMapping.listVisualTypes = function () {
49157        var visualTypes = [];
49158        each$1(visualHandlers, function (handler, key) {
49159            visualTypes.push(key);
49160        });
49161        return visualTypes;
49162    };
49163
49164    /**
49165     * @public
49166     */
49167    VisualMapping.addVisualHandler = function (name, handler) {
49168        visualHandlers[name] = handler;
49169    };
49170
49171    /**
49172     * @public
49173     */
49174    VisualMapping.isValidType = function (visualType) {
49175        return visualHandlers.hasOwnProperty(visualType);
49176    };
49177
49178    /**
49179     * Convinent method.
49180     * Visual can be Object or Array or primary type.
49181     *
49182     * @public
49183     */
49184    VisualMapping.eachVisual = function (visual, callback, context) {
49185        if (isObject$1(visual)) {
49186            each$1(visual, callback, context);
49187        }
49188        else {
49189            callback.call(context, visual);
49190        }
49191    };
49192
49193    VisualMapping.mapVisual = function (visual, callback, context) {
49194        var isPrimary;
49195        var newVisual = isArray(visual)
49196            ? []
49197            : isObject$1(visual)
49198                ? {}
49199                : (isPrimary = true, null);
49200
49201        VisualMapping.eachVisual(visual, function (v, key) {
49202            var newVal = callback.call(context, v, key);
49203            isPrimary ? (newVisual = newVal) : (newVisual[key] = newVal);
49204        });
49205        return newVisual;
49206    };
49207
49208    /**
49209     * @public
49210     * @param {Object} obj
49211     * @return {Object} new object containers visual values.
49212     *                 If no visuals, return null.
49213     */
49214    VisualMapping.retrieveVisuals = function (obj) {
49215        var ret = {};
49216        var hasVisual;
49217
49218        obj && each$9(visualHandlers, function (h, visualType) {
49219            if (obj.hasOwnProperty(visualType)) {
49220                ret[visualType] = obj[visualType];
49221                hasVisual = true;
49222            }
49223        });
49224
49225        return hasVisual ? ret : null;
49226    };
49227
49228    /**
49229     * Give order to visual types, considering colorSaturation, colorAlpha depends on color.
49230     *
49231     * @public
49232     * @param {(Object|Array)} visualTypes If Object, like: {color: ..., colorSaturation: ...}
49233     *                                     IF Array, like: ['color', 'symbol', 'colorSaturation']
49234     * @return {Array.<string>} Sorted visual types.
49235     */
49236    VisualMapping.prepareVisualTypes = function (visualTypes) {
49237        if (isObject$5(visualTypes)) {
49238            var types = [];
49239            each$9(visualTypes, function (item, type) {
49240                types.push(type);
49241            });
49242            visualTypes = types;
49243        }
49244        else if (isArray(visualTypes)) {
49245            visualTypes = visualTypes.slice();
49246        }
49247        else {
49248            return [];
49249        }
49250
49251        visualTypes.sort(function (type1, type2) {
49252            // color should be front of colorSaturation, colorAlpha, ...
49253            // symbol and symbolSize do not matter.
49254            return (type2 === 'color' && type1 !== 'color' && type1.indexOf('color') === 0)
49255                ? 1 : -1;
49256        });
49257
49258        return visualTypes;
49259    };
49260
49261    /**
49262     * 'color', 'colorSaturation', 'colorAlpha', ... are depends on 'color'.
49263     * Other visuals are only depends on themself.
49264     *
49265     * @public
49266     * @param {string} visualType1
49267     * @param {string} visualType2
49268     * @return {boolean}
49269     */
49270    VisualMapping.dependsOn = function (visualType1, visualType2) {
49271        return visualType2 === 'color'
49272            ? !!(visualType1 && visualType1.indexOf(visualType2) === 0)
49273            : visualType1 === visualType2;
49274    };
49275
49276    /**
49277     * @param {number} value
49278     * @param {Array.<Object>} pieceList [{value: ..., interval: [min, max]}, ...]
49279     *                         Always from small to big.
49280     * @param {boolean} [findClosestWhenOutside=false]
49281     * @return {number} index
49282     */
49283    VisualMapping.findPieceIndex = function (value, pieceList, findClosestWhenOutside) {
49284        var possibleI;
49285        var abs = Infinity;
49286
49287        // value has the higher priority.
49288        for (var i = 0, len = pieceList.length; i < len; i++) {
49289            var pieceValue = pieceList[i].value;
49290            if (pieceValue != null) {
49291                if (pieceValue === value
49292                    // FIXME
49293                    // It is supposed to compare value according to value type of dimension,
49294                    // but currently value type can exactly be string or number.
49295                    // Compromise for numeric-like string (like '12'), especially
49296                    // in the case that visualMap.categories is ['22', '33'].
49297                    || (typeof pieceValue === 'string' && pieceValue === value + '')
49298                ) {
49299                    return i;
49300                }
49301                findClosestWhenOutside && updatePossible(pieceValue, i);
49302            }
49303        }
49304
49305        for (var i = 0, len = pieceList.length; i < len; i++) {
49306            var piece = pieceList[i];
49307            var interval = piece.interval;
49308            var close = piece.close;
49309
49310            if (interval) {
49311                if (interval[0] === -Infinity) {
49312                    if (littleThan(close[1], value, interval[1])) {
49313                        return i;
49314                    }
49315                }
49316                else if (interval[1] === Infinity) {
49317                    if (littleThan(close[0], interval[0], value)) {
49318                        return i;
49319                    }
49320                }
49321                else if (
49322                    littleThan(close[0], interval[0], value)
49323                    && littleThan(close[1], value, interval[1])
49324                ) {
49325                    return i;
49326                }
49327                findClosestWhenOutside && updatePossible(interval[0], i);
49328                findClosestWhenOutside && updatePossible(interval[1], i);
49329            }
49330        }
49331
49332        if (findClosestWhenOutside) {
49333            return value === Infinity
49334                ? pieceList.length - 1
49335                : value === -Infinity
49336                    ? 0
49337                    : possibleI;
49338        }
49339
49340        function updatePossible(val, index) {
49341            var newAbs = Math.abs(val - value);
49342            if (newAbs < abs) {
49343                abs = newAbs;
49344                possibleI = index;
49345            }
49346        }
49347
49348    };
49349
49350    function littleThan(close, a, b) {
49351        return close ? a <= b : a < b;
49352    }
49353
49354    /*
49355* Licensed to the Apache Software Foundation (ASF) under one
49356* or more contributor license agreements.  See the NOTICE file
49357* distributed with this work for additional information
49358* regarding copyright ownership.  The ASF licenses this file
49359* to you under the Apache License, Version 2.0 (the
49360* "License"); you may not use this file except in compliance
49361* with the License.  You may obtain a copy of the License at
49362*
49363*   http://www.apache.org/licenses/LICENSE-2.0
49364*
49365* Unless required by applicable law or agreed to in writing,
49366* software distributed under the License is distributed on an
49367* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
49368* KIND, either express or implied.  See the License for the
49369* specific language governing permissions and limitations
49370* under the License.
49371*/
49372
49373    var isArray$2 = isArray;
49374
49375    var ITEM_STYLE_NORMAL = 'itemStyle';
49376
49377    var treemapVisual = {
49378        seriesType: 'treemap',
49379        reset: function (seriesModel, ecModel, api, payload) {
49380            var tree = seriesModel.getData().tree;
49381            var root = tree.root;
49382            var seriesItemStyleModel = seriesModel.getModel(ITEM_STYLE_NORMAL);
49383
49384            if (root.isRemoved()) {
49385                return;
49386            }
49387
49388            var levelItemStyles = map(tree.levelModels, function (levelModel) {
49389                return levelModel ? levelModel.get(ITEM_STYLE_NORMAL) : null;
49390            });
49391
49392            travelTree(
49393                root, // Visual should calculate from tree root but not view root.
49394                {},
49395                levelItemStyles,
49396                seriesItemStyleModel,
49397                seriesModel.getViewRoot().getAncestors(),
49398                seriesModel
49399            );
49400        }
49401    };
49402
49403    function travelTree(
49404        node, designatedVisual, levelItemStyles, seriesItemStyleModel,
49405        viewRootAncestors, seriesModel
49406    ) {
49407        var nodeModel = node.getModel();
49408        var nodeLayout = node.getLayout();
49409
49410        // Optimize
49411        if (!nodeLayout || nodeLayout.invisible || !nodeLayout.isInView) {
49412            return;
49413        }
49414
49415        var nodeItemStyleModel = node.getModel(ITEM_STYLE_NORMAL);
49416        var levelItemStyle = levelItemStyles[node.depth];
49417        var visuals = buildVisuals(
49418            nodeItemStyleModel, designatedVisual, levelItemStyle, seriesItemStyleModel
49419        );
49420
49421        // calculate border color
49422        var borderColor = nodeItemStyleModel.get('borderColor');
49423        var borderColorSaturation = nodeItemStyleModel.get('borderColorSaturation');
49424        var thisNodeColor;
49425        if (borderColorSaturation != null) {
49426            // For performance, do not always execute 'calculateColor'.
49427            thisNodeColor = calculateColor(visuals, node);
49428            borderColor = calculateBorderColor(borderColorSaturation, thisNodeColor);
49429        }
49430        node.setVisual('borderColor', borderColor);
49431
49432        var viewChildren = node.viewChildren;
49433        if (!viewChildren || !viewChildren.length) {
49434            thisNodeColor = calculateColor(visuals, node);
49435            // Apply visual to this node.
49436            node.setVisual('color', thisNodeColor);
49437        }
49438        else {
49439            var mapping = buildVisualMapping(
49440                node, nodeModel, nodeLayout, nodeItemStyleModel, visuals, viewChildren
49441            );
49442
49443            // Designate visual to children.
49444            each$1(viewChildren, function (child, index) {
49445                // If higher than viewRoot, only ancestors of viewRoot is needed to visit.
49446                if (child.depth >= viewRootAncestors.length
49447                    || child === viewRootAncestors[child.depth]
49448                ) {
49449                    var childVisual = mapVisual$1(
49450                        nodeModel, visuals, child, index, mapping, seriesModel
49451                    );
49452                    travelTree(
49453                        child, childVisual, levelItemStyles, seriesItemStyleModel,
49454                        viewRootAncestors, seriesModel
49455                    );
49456                }
49457            });
49458        }
49459    }
49460
49461    function buildVisuals(
49462        nodeItemStyleModel, designatedVisual, levelItemStyle, seriesItemStyleModel
49463    ) {
49464        var visuals = extend({}, designatedVisual);
49465
49466        each$1(['color', 'colorAlpha', 'colorSaturation'], function (visualName) {
49467            // Priority: thisNode > thisLevel > parentNodeDesignated > seriesModel
49468            var val = nodeItemStyleModel.get(visualName, true); // Ignore parent
49469            val == null && levelItemStyle && (val = levelItemStyle[visualName]);
49470            val == null && (val = designatedVisual[visualName]);
49471            val == null && (val = seriesItemStyleModel.get(visualName));
49472
49473            val != null && (visuals[visualName] = val);
49474        });
49475
49476        return visuals;
49477    }
49478
49479    function calculateColor(visuals) {
49480        var color = getValueVisualDefine(visuals, 'color');
49481
49482        if (color) {
49483            var colorAlpha = getValueVisualDefine(visuals, 'colorAlpha');
49484            var colorSaturation = getValueVisualDefine(visuals, 'colorSaturation');
49485            if (colorSaturation) {
49486                color = modifyHSL(color, null, null, colorSaturation);
49487            }
49488            if (colorAlpha) {
49489                color = modifyAlpha(color, colorAlpha);
49490            }
49491
49492            return color;
49493        }
49494    }
49495
49496    function calculateBorderColor(borderColorSaturation, thisNodeColor) {
49497        return thisNodeColor != null
49498            ? modifyHSL(thisNodeColor, null, null, borderColorSaturation)
49499            : null;
49500    }
49501
49502    function getValueVisualDefine(visuals, name) {
49503        var value = visuals[name];
49504        if (value != null && value !== 'none') {
49505            return value;
49506        }
49507    }
49508
49509    function buildVisualMapping(
49510        node, nodeModel, nodeLayout, nodeItemStyleModel, visuals, viewChildren
49511    ) {
49512        if (!viewChildren || !viewChildren.length) {
49513            return;
49514        }
49515
49516        var rangeVisual = getRangeVisual(nodeModel, 'color')
49517            || (
49518                visuals.color != null
49519                && visuals.color !== 'none'
49520                && (
49521                    getRangeVisual(nodeModel, 'colorAlpha')
49522                    || getRangeVisual(nodeModel, 'colorSaturation')
49523                )
49524            );
49525
49526        if (!rangeVisual) {
49527            return;
49528        }
49529
49530        var visualMin = nodeModel.get('visualMin');
vendor: 22,597 bytes, lines 49531-50143
49531        var visualMax = nodeModel.get('visualMax');
49532        var dataExtent = nodeLayout.dataExtent.slice();
49533        visualMin != null && visualMin < dataExtent[0] && (dataExtent[0] = visualMin);
49534        visualMax != null && visualMax > dataExtent[1] && (dataExtent[1] = visualMax);
49535
49536        var colorMappingBy = nodeModel.get('colorMappingBy');
49537        var opt = {
49538            type: rangeVisual.name,
49539            dataExtent: dataExtent,
49540            visual: rangeVisual.range
49541        };
49542        if (opt.type === 'color'
49543            && (colorMappingBy === 'index' || colorMappingBy === 'id')
49544        ) {
49545            opt.mappingMethod = 'category';
49546            opt.loop = true;
49547            // categories is ordinal, so do not set opt.categories.
49548        }
49549        else {
49550            opt.mappingMethod = 'linear';
49551        }
49552
49553        var mapping = new VisualMapping(opt);
49554        mapping.__drColorMappingBy = colorMappingBy;
49555
49556        return mapping;
49557    }
49558
49559// Notice: If we dont have the attribute 'colorRange', but only use
49560// attribute 'color' to represent both concepts of 'colorRange' and 'color',
49561// (It means 'colorRange' when 'color' is Array, means 'color' when not array),
49562// this problem will be encountered:
49563// If a level-1 node dont have children, and its siblings has children,
49564// and colorRange is set on level-1, then the node can not be colored.
49565// So we separate 'colorRange' and 'color' to different attributes.
49566    function getRangeVisual(nodeModel, name) {
49567        // 'colorRange', 'colorARange', 'colorSRange'.
49568        // If not exsits on this node, fetch from levels and series.
49569        var range = nodeModel.get(name);
49570        return (isArray$2(range) && range.length) ? {name: name, range: range} : null;
49571    }
49572
49573    function mapVisual$1(nodeModel, visuals, child, index, mapping, seriesModel) {
49574        var childVisuals = extend({}, visuals);
49575
49576        if (mapping) {
49577            var mappingType = mapping.type;
49578            var colorMappingBy = mappingType === 'color' && mapping.__drColorMappingBy;
49579            var value =
49580                colorMappingBy === 'index'
49581                    ? index
49582                    : colorMappingBy === 'id'
49583                    ? seriesModel.mapIdToIndex(child.getId())
49584                    : child.getValue(nodeModel.get('visualDimension'));
49585
49586            childVisuals[mappingType] = mapping.mapValueToVisual(value);
49587        }
49588
49589        return childVisuals;
49590    }
49591
49592    /*
49593* Licensed to the Apache Software Foundation (ASF) under one
49594* or more contributor license agreements.  See the NOTICE file
49595* distributed with this work for additional information
49596* regarding copyright ownership.  The ASF licenses this file
49597* to you under the Apache License, Version 2.0 (the
49598* "License"); you may not use this file except in compliance
49599* with the License.  You may obtain a copy of the License at
49600*
49601*   http://www.apache.org/licenses/LICENSE-2.0
49602*
49603* Unless required by applicable law or agreed to in writing,
49604* software distributed under the License is distributed on an
49605* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
49606* KIND, either express or implied.  See the License for the
49607* specific language governing permissions and limitations
49608* under the License.
49609*/
49610
49611    /*
49612* The treemap layout implementation references to the treemap
49613* layout of d3.js (d3/src/layout/treemap.js in v3). The use of
49614* the source code of this file is also subject to the terms
49615* and consitions of its license (BSD-3Clause, see
49616* <echarts/src/licenses/LICENSE-d3>).
49617*/
49618
49619    var mathMax$4 = Math.max;
49620    var mathMin$4 = Math.min;
49621    var retrieveValue = retrieve;
49622    var each$10 = each$1;
49623
49624    var PATH_BORDER_WIDTH = ['itemStyle', 'borderWidth'];
49625    var PATH_GAP_WIDTH = ['itemStyle', 'gapWidth'];
49626    var PATH_UPPER_LABEL_SHOW = ['upperLabel', 'show'];
49627    var PATH_UPPER_LABEL_HEIGHT = ['upperLabel', 'height'];
49628
49629    /**
49630     * @public
49631     */
49632    var treemapLayout = {
49633        seriesType: 'treemap',
49634        reset: function (seriesModel, ecModel, api, payload) {
49635            // Layout result in each node:
49636            // {x, y, width, height, area, borderWidth}
49637            var ecWidth = api.getWidth();
49638            var ecHeight = api.getHeight();
49639            var seriesOption = seriesModel.option;
49640
49641            var layoutInfo = getLayoutRect(
49642                seriesModel.getBoxLayoutParams(),
49643                {
49644                    width: api.getWidth(),
49645                    height: api.getHeight()
49646                }
49647            );
49648
49649            var size = seriesOption.size || []; // Compatible with ec2.
49650            var containerWidth = parsePercent$1(
49651                retrieveValue(layoutInfo.width, size[0]),
49652                ecWidth
49653            );
49654            var containerHeight = parsePercent$1(
49655                retrieveValue(layoutInfo.height, size[1]),
49656                ecHeight
49657            );
49658
49659            // Fetch payload info.
49660            var payloadType = payload && payload.type;
49661            var types = ['treemapZoomToNode', 'treemapRootToNode'];
49662            var targetInfo = retrieveTargetInfo(payload, types, seriesModel);
49663            var rootRect = (payloadType === 'treemapRender' || payloadType === 'treemapMove')
49664                ? payload.rootRect : null;
49665            var viewRoot = seriesModel.getViewRoot();
49666            var viewAbovePath = getPathToRoot(viewRoot);
49667
49668            if (payloadType !== 'treemapMove') {
49669                var rootSize = payloadType === 'treemapZoomToNode'
49670                    ? estimateRootSize(
49671                        seriesModel, targetInfo, viewRoot, containerWidth, containerHeight
49672                    )
49673                    : rootRect
49674                        ? [rootRect.width, rootRect.height]
49675                        : [containerWidth, containerHeight];
49676
49677                var sort = seriesOption.sort;
49678                if (sort && sort !== 'asc' && sort !== 'desc') {
49679                    sort = 'desc';
49680                }
49681                var options = {
49682                    squareRatio: seriesOption.squareRatio,
49683                    sort: sort,
49684                    leafDepth: seriesOption.leafDepth
49685                };
49686
49687                // layout should be cleared because using updateView but not update.
49688                viewRoot.hostTree.clearLayouts();
49689
49690                // TODO
49691                // optimize: if out of view clip, do not layout.
49692                // But take care that if do not render node out of view clip,
49693                // how to calculate start po
49694
49695                var viewRootLayout = {
49696                    x: 0, y: 0,
49697                    width: rootSize[0], height: rootSize[1],
49698                    area: rootSize[0] * rootSize[1]
49699                };
49700                viewRoot.setLayout(viewRootLayout);
49701
49702                squarify(viewRoot, options, false, 0);
49703                // Supplement layout.
49704                var viewRootLayout = viewRoot.getLayout();
49705                each$10(viewAbovePath, function (node, index) {
49706                    var childValue = (viewAbovePath[index + 1] || viewRoot).getValue();
49707                    node.setLayout(extend(
49708                        {dataExtent: [childValue, childValue], borderWidth: 0, upperHeight: 0},
49709                        viewRootLayout
49710                    ));
49711                });
49712            }
49713
49714            var treeRoot = seriesModel.getData().tree.root;
49715
49716            treeRoot.setLayout(
49717                calculateRootPosition(layoutInfo, rootRect, targetInfo),
49718                true
49719            );
49720
49721            seriesModel.setLayoutInfo(layoutInfo);
49722
49723            // FIXME
49724            // 现在没有clip功能,暂时取ec高宽。
49725            prunning(
49726                treeRoot,
49727                // Transform to base element coordinate system.
49728                new BoundingRect(-layoutInfo.x, -layoutInfo.y, ecWidth, ecHeight),
49729                viewAbovePath,
49730                viewRoot,
49731                0
49732            );
49733        }
49734    };
49735
49736    /**
49737     * Layout treemap with squarify algorithm.
49738     * @see https://graphics.ethz.ch/teaching/scivis_common/Literature/squarifiedTreeMaps.pdf
49739     * The implementation references to the treemap layout of d3.js.
49740     * See the license statement at the head of this file.
49741     *
49742     * @protected
49743     * @param {module:echarts/data/Tree~TreeNode} node
49744     * @param {Object} options
49745     * @param {string} options.sort 'asc' or 'desc'
49746     * @param {number} options.squareRatio
49747     * @param {boolean} hideChildren
49748     * @param {number} depth
49749     */
49750    function squarify(node, options, hideChildren, depth) {
49751        var width;
49752        var height;
49753
49754        if (node.isRemoved()) {
49755            return;
49756        }
49757
49758        var thisLayout = node.getLayout();
49759        width = thisLayout.width;
49760        height = thisLayout.height;
49761
49762        // Considering border and gap
49763        var nodeModel = node.getModel();
49764        var borderWidth = nodeModel.get(PATH_BORDER_WIDTH);
49765        var halfGapWidth = nodeModel.get(PATH_GAP_WIDTH) / 2;
49766        var upperLabelHeight = getUpperLabelHeight(nodeModel);
49767        var upperHeight = Math.max(borderWidth, upperLabelHeight);
49768        var layoutOffset = borderWidth - halfGapWidth;
49769        var layoutOffsetUpper = upperHeight - halfGapWidth;
49770        var nodeModel = node.getModel();
49771
49772        node.setLayout({
49773            borderWidth: borderWidth,
49774            upperHeight: upperHeight,
49775            upperLabelHeight: upperLabelHeight
49776        }, true);
49777
49778        width = mathMax$4(width - 2 * layoutOffset, 0);
49779        height = mathMax$4(height - layoutOffset - layoutOffsetUpper, 0);
49780
49781        var totalArea = width * height;
49782        var viewChildren = initChildren(
49783            node, nodeModel, totalArea, options, hideChildren, depth
49784        );
49785
49786        if (!viewChildren.length) {
49787            return;
49788        }
49789
49790        var rect = {x: layoutOffset, y: layoutOffsetUpper, width: width, height: height};
49791        var rowFixedLength = mathMin$4(width, height);
49792        var best = Infinity; // the best row score so far
49793        var row = [];
49794        row.area = 0;
49795
49796        for (var i = 0, len = viewChildren.length; i < len;) {
49797            var child = viewChildren[i];
49798
49799            row.push(child);
49800            row.area += child.getLayout().area;
49801            var score = worst(row, rowFixedLength, options.squareRatio);
49802
49803            // continue with this orientation
49804            if (score <= best) {
49805                i++;
49806                best = score;
49807            }
49808            // abort, and try a different orientation
49809            else {
49810                row.area -= row.pop().getLayout().area;
49811                position(row, rowFixedLength, rect, halfGapWidth, false);
49812                rowFixedLength = mathMin$4(rect.width, rect.height);
49813                row.length = row.area = 0;
49814                best = Infinity;
49815            }
49816        }
49817
49818        if (row.length) {
49819            position(row, rowFixedLength, rect, halfGapWidth, true);
49820        }
49821
49822        if (!hideChildren) {
49823            var childrenVisibleMin = nodeModel.get('childrenVisibleMin');
49824            if (childrenVisibleMin != null && totalArea < childrenVisibleMin) {
49825                hideChildren = true;
49826            }
49827        }
49828
49829        for (var i = 0, len = viewChildren.length; i < len; i++) {
49830            squarify(viewChildren[i], options, hideChildren, depth + 1);
49831        }
49832    }
49833
49834    /**
49835     * Set area to each child, and calculate data extent for visual coding.
49836     */
49837    function initChildren(node, nodeModel, totalArea, options, hideChildren, depth) {
49838        var viewChildren = node.children || [];
49839        var orderBy = options.sort;
49840        orderBy !== 'asc' && orderBy !== 'desc' && (orderBy = null);
49841
49842        var overLeafDepth = options.leafDepth != null && options.leafDepth <= depth;
49843
49844        // leafDepth has higher priority.
49845        if (hideChildren && !overLeafDepth) {
49846            return (node.viewChildren = []);
49847        }
49848
49849        // Sort children, order by desc.
49850        viewChildren = filter(viewChildren, function (child) {
49851            return !child.isRemoved();
49852        });
49853
49854        sort$1(viewChildren, orderBy);
49855
49856        var info = statistic(nodeModel, viewChildren, orderBy);
49857
49858        if (info.sum === 0) {
49859            return (node.viewChildren = []);
49860        }
49861
49862        info.sum = filterByThreshold(nodeModel, totalArea, info.sum, orderBy, viewChildren);
49863
49864        if (info.sum === 0) {
49865            return (node.viewChildren = []);
49866        }
49867
49868        // Set area to each child.
49869        for (var i = 0, len = viewChildren.length; i < len; i++) {
49870            var area = viewChildren[i].getValue() / info.sum * totalArea;
49871            // Do not use setLayout({...}, true), because it is needed to clear last layout.
49872            viewChildren[i].setLayout({area: area});
49873        }
49874
49875        if (overLeafDepth) {
49876            viewChildren.length && node.setLayout({isLeafRoot: true}, true);
49877            viewChildren.length = 0;
49878        }
49879
49880        node.viewChildren = viewChildren;
49881        node.setLayout({dataExtent: info.dataExtent}, true);
49882
49883        return viewChildren;
49884    }
49885
49886    /**
49887     * Consider 'visibleMin'. Modify viewChildren and get new sum.
49888     */
49889    function filterByThreshold(nodeModel, totalArea, sum, orderBy, orderedChildren) {
49890
49891        // visibleMin is not supported yet when no option.sort.
49892        if (!orderBy) {
49893            return sum;
49894        }
49895
49896        var visibleMin = nodeModel.get('visibleMin');
49897        var len = orderedChildren.length;
49898        var deletePoint = len;
49899
49900        // Always travel from little value to big value.
49901        for (var i = len - 1; i >= 0; i--) {
49902            var value = orderedChildren[
49903                orderBy === 'asc' ? len - i - 1 : i
49904                ].getValue();
49905
49906            if (value / sum * totalArea < visibleMin) {
49907                deletePoint = i;
49908                sum -= value;
49909            }
49910        }
49911
49912        orderBy === 'asc'
49913            ? orderedChildren.splice(0, len - deletePoint)
49914            : orderedChildren.splice(deletePoint, len - deletePoint);
49915
49916        return sum;
49917    }
49918
49919    /**
49920     * Sort
49921     */
49922    function sort$1(viewChildren, orderBy) {
49923        if (orderBy) {
49924            viewChildren.sort(function (a, b) {
49925                var diff = orderBy === 'asc'
49926                    ?  a.getValue() - b.getValue() : b.getValue() - a.getValue();
49927                return diff === 0
49928                    ? (orderBy === 'asc'
49929                            ? a.dataIndex - b.dataIndex : b.dataIndex - a.dataIndex
49930                    )
49931                    : diff;
49932            });
49933        }
49934        return viewChildren;
49935    }
49936
49937    /**
49938     * Statistic
49939     */
49940    function statistic(nodeModel, children, orderBy) {
49941        // Calculate sum.
49942        var sum = 0;
49943        for (var i = 0, len = children.length; i < len; i++) {
49944            sum += children[i].getValue();
49945        }
49946
49947        // Statistic data extent for latter visual coding.
49948        // Notice: data extent should be calculate based on raw children
49949        // but not filtered view children, otherwise visual mapping will not
49950        // be stable when zoom (where children is filtered by visibleMin).
49951
49952        var dimension = nodeModel.get('visualDimension');
49953        var dataExtent;
49954
49955        // The same as area dimension.
49956        if (!children || !children.length) {
49957            dataExtent = [NaN, NaN];
49958        }
49959        else if (dimension === 'value' && orderBy) {
49960            dataExtent = [
49961                children[children.length - 1].getValue(),
49962                children[0].getValue()
49963            ];
49964            orderBy === 'asc' && dataExtent.reverse();
49965        }
49966        // Other dimension.
49967        else {
49968            var dataExtent = [Infinity, -Infinity];
49969            each$10(children, function (child) {
49970                var value = child.getValue(dimension);
49971                value < dataExtent[0] && (dataExtent[0] = value);
49972                value > dataExtent[1] && (dataExtent[1] = value);
49973            });
49974        }
49975
49976        return {sum: sum, dataExtent: dataExtent};
49977    }
49978
49979    /**
49980     * Computes the score for the specified row,
49981     * as the worst aspect ratio.
49982     */
49983    function worst(row, rowFixedLength, ratio) {
49984        var areaMax = 0;
49985        var areaMin = Infinity;
49986
49987        for (var i = 0, area, len = row.length; i < len; i++) {
49988            area = row[i].getLayout().area;
49989            if (area) {
49990                area < areaMin && (areaMin = area);
49991                area > areaMax && (areaMax = area);
49992            }
49993        }
49994
49995        var squareArea = row.area * row.area;
49996        var f = rowFixedLength * rowFixedLength * ratio;
49997
49998        return squareArea
49999            ? mathMax$4(
50000                (f * areaMax) / squareArea,
50001                squareArea / (f * areaMin)
50002            )
50003            : Infinity;
50004    }
50005
50006    /**
50007     * Positions the specified row of nodes. Modifies `rect`.
50008     */
50009    function position(row, rowFixedLength, rect, halfGapWidth, flush) {
50010        // When rowFixedLength === rect.width,
50011        // it is horizontal subdivision,
50012        // rowFixedLength is the width of the subdivision,
50013        // rowOtherLength is the height of the subdivision,
50014        // and nodes will be positioned from left to right.
50015
50016        // wh[idx0WhenH] means: when horizontal,
50017        //      wh[idx0WhenH] => wh[0] => 'width'.
50018        //      xy[idx1WhenH] => xy[1] => 'y'.
50019        var idx0WhenH = rowFixedLength === rect.width ? 0 : 1;
50020        var idx1WhenH = 1 - idx0WhenH;
50021        var xy = ['x', 'y'];
50022        var wh = ['width', 'height'];
50023
50024        var last = rect[xy[idx0WhenH]];
50025        var rowOtherLength = rowFixedLength
50026            ? row.area / rowFixedLength : 0;
50027
50028        if (flush || rowOtherLength > rect[wh[idx1WhenH]]) {
50029            rowOtherLength = rect[wh[idx1WhenH]]; // over+underflow
50030        }
50031        for (var i = 0, rowLen = row.length; i < rowLen; i++) {
50032            var node = row[i];
50033            var nodeLayout = {};
50034            var step = rowOtherLength
50035                ? node.getLayout().area / rowOtherLength : 0;
50036
50037            var wh1 = nodeLayout[wh[idx1WhenH]] = mathMax$4(rowOtherLength - 2 * halfGapWidth, 0);
50038
50039            // We use Math.max/min to avoid negative width/height when considering gap width.
50040            var remain = rect[xy[idx0WhenH]] + rect[wh[idx0WhenH]] - last;
50041            var modWH = (i === rowLen - 1 || remain < step) ? remain : step;
50042            var wh0 = nodeLayout[wh[idx0WhenH]] = mathMax$4(modWH - 2 * halfGapWidth, 0);
50043
50044            nodeLayout[xy[idx1WhenH]] = rect[xy[idx1WhenH]] + mathMin$4(halfGapWidth, wh1 / 2);
50045            nodeLayout[xy[idx0WhenH]] = last + mathMin$4(halfGapWidth, wh0 / 2);
50046
50047            last += modWH;
50048            node.setLayout(nodeLayout, true);
50049        }
50050
50051        rect[xy[idx1WhenH]] += rowOtherLength;
50052        rect[wh[idx1WhenH]] -= rowOtherLength;
50053    }
50054
50055// Return [containerWidth, containerHeight] as defualt.
50056    function estimateRootSize(seriesModel, targetInfo, viewRoot, containerWidth, containerHeight) {
50057        // If targetInfo.node exists, we zoom to the node,
50058        // so estimate whold width and heigth by target node.
50059        var currNode = (targetInfo || {}).node;
50060        var defaultSize = [containerWidth, containerHeight];
50061
50062        if (!currNode || currNode === viewRoot) {
50063            return defaultSize;
50064        }
50065
50066        var parent;
50067        var viewArea = containerWidth * containerHeight;
50068        var area = viewArea * seriesModel.option.zoomToNodeRatio;
50069
50070        while (parent = currNode.parentNode) { // jshint ignore:line
50071            var sum = 0;
50072            var siblings = parent.children;
50073
50074            for (var i = 0, len = siblings.length; i < len; i++) {
50075                sum += siblings[i].getValue();
50076            }
50077            var currNodeValue = currNode.getValue();
50078            if (currNodeValue === 0) {
50079                return defaultSize;
50080            }
50081            area *= sum / currNodeValue;
50082
50083            // Considering border, suppose aspect ratio is 1.
50084            var parentModel = parent.getModel();
50085            var borderWidth = parentModel.get(PATH_BORDER_WIDTH);
50086            var upperHeight = Math.max(borderWidth, getUpperLabelHeight(parentModel, borderWidth));
50087            area += 4 * borderWidth * borderWidth
50088                + (3 * borderWidth + upperHeight) * Math.pow(area, 0.5);
50089
50090            area > MAX_SAFE_INTEGER && (area = MAX_SAFE_INTEGER);
50091
50092            currNode = parent;
50093        }
50094
50095        area < viewArea && (area = viewArea);
50096        var scale = Math.pow(area / viewArea, 0.5);
50097
50098        return [containerWidth * scale, containerHeight * scale];
50099    }
50100
50101// Root postion base on coord of containerGroup
50102    function calculateRootPosition(layoutInfo, rootRect, targetInfo) {
50103        if (rootRect) {
50104            return {x: rootRect.x, y: rootRect.y};
50105        }
50106
50107        var defaultPosition = {x: 0, y: 0};
50108        if (!targetInfo) {
50109            return defaultPosition;
50110        }
50111
50112        // If targetInfo is fetched by 'retrieveTargetInfo',
50113        // old tree and new tree are the same tree,
50114        // so the node still exists and we can visit it.
50115
50116        var targetNode = targetInfo.node;
50117        var layout = targetNode.getLayout();
50118
50119        if (!layout) {
50120            return defaultPosition;
50121        }
50122
50123        // Transform coord from local to container.
50124        var targetCenter = [layout.width / 2, layout.height / 2];
50125        var node = targetNode;
50126        while (node) {
50127            var nodeLayout = node.getLayout();
50128            targetCenter[0] += nodeLayout.x;
50129            targetCenter[1] += nodeLayout.y;
50130            node = node.parentNode;
50131        }
50132
50133        return {
50134            x: layoutInfo.width / 2 - targetCenter[0],
50135            y: layoutInfo.height / 2 - targetCenter[1]
50136        };
50137    }
50138
50139// Mark nodes visible for prunning when visual coding and rendering.
50140// Prunning depends on layout and root position, so we have to do it after layout.
50141    function prunning(node, clipRect, viewAbovePath, viewRoot, depth) {
50142        var nodeLayout = node.getLayout();
50143        var nodeInViewAbovePath = viewAbovePath[depth];
50144        var isAboveViewRoot = nodeInViewAbovePath && nodeInViewAbovePath === node;
50145
50146        if (
50147            (nodeInViewAbovePath && !isAboveViewRoot)
50148            || (depth === viewAbovePath.length && node !== viewRoot)
50149        ) {
50150            return;
50151        }
50152
50153        node.setLayout({
50154            // isInView means: viewRoot sub tree + viewAbovePath
50155            isInView: true,
50156            // invisible only means: outside view clip so that the node can not
50157            // see but still layout for animation preparation but not render.
50158            invisible: !isAboveViewRoot && !clipRect.intersect(nodeLayout),
50159            isAboveViewRoot: isAboveViewRoot
50160        }, true);
50161
50162        // Transform to child coordinate.
50163        var childClipRect = new BoundingRect(
50164            clipRect.x - nodeLayout.x,
50165            clipRect.y - nodeLayout.y,
50166            clipRect.width,
50167            clipRect.height
50168        );
50169
50170        each$10(node.viewChildren || [], function (child) {
50171            prunning(child, childClipRect, viewAbovePath, viewRoot, depth + 1);
50172        });
50173    }
50174
50175    function getUpperLabelHeight(model) {
50176        return model.get(PATH_UPPER_LABEL_SHOW) ? model.get(PATH_UPPER_LABEL_HEIGHT) : 0;
50177    }
50178
50179    /*
50180* Licensed to the Apache Software Foundation (ASF) under one
50181* or more contributor license agreements.  See the NOTICE file
50182* distributed with this work for additional information
50183* regarding copyright ownership.  The ASF licenses this file
50184* to you under the Apache License, Version 2.0 (the
50185* "License"); you may not use this file except in compliance
50186* with the License.  You may obtain a copy of the License at
50187*
50188*   http://www.apache.org/licenses/LICENSE-2.0
50189*
50190* Unless required by applicable law or agreed to in writing,
50191* software distributed under the License is distributed on an
50192* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
50193* KIND, either express or implied.  See the License for the
50194* specific language governing permissions and limitations
50195* under the License.
50196*/
50197
50198    registerVisual(treemapVisual);
50199    registerLayout(treemapLayout);
50200
50201    /*
50202* Licensed to the Apache Software Foundation (ASF) under one
50203* or more contributor license agreements.  See the NOTICE file
50204* distributed with this work for additional information
50205* regarding copyright ownership.  The ASF licenses this file
50206* to you under the Apache License, Version 2.0 (the
50207* "License"); you may not use this file except in compliance
50208* with the License.  You may obtain a copy of the License at
50209*
50210*   http://www.apache.org/licenses/LICENSE-2.0
50211*
50212* Unless required by applicable law or agreed to in writing,
50213* software distributed under the License is distributed on an
50214* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
50215* KIND, either express or implied.  See the License for the
50216* specific language governing permissions and limitations
50217* under the License.
50218*/
50219
50220    /**
50221     * Graph data structure
50222     *
50223     * @module echarts/data/Graph
50224     * @author Yi Shen(https://www.github.com/pissang)
50225     */
50226
50227// id may be function name of Object, add a prefix to avoid this problem.
50228    function generateNodeKey (id) {
50229        return '_EC_' + id;
50230    }
50231    /**
50232     * @alias module:echarts/data/Graph
50233     * @constructor
50234     * @param {boolean} directed
50235     */
50236    var Graph = function(directed) {
50237        /**
50238         * 是否是有向图
50239         * @type {boolean}
50240         * @private
50241         */
50242        this._directed = directed || false;
50243
50244        /**
50245         * @type {Array.<module:echarts/data/Graph.Node>}
50246         * @readOnly
50247         */
50248        this.nodes = [];
50249
50250        /**
50251         * @type {Array.<module:echarts/data/Graph.Edge>}
50252         * @readOnly
50253         */
50254        this.edges = [];
50255
50256        /**
50257         * @type {Object.<string, module:echarts/data/Graph.Node>}
50258         * @private
50259         */
50260        this._nodesMap = {};
50261        /**
50262         * @type {Object.<string, module:echarts/data/Graph.Edge>}
50263         * @private
50264         */
50265        this._edgesMap = {};
50266
50267        /**
50268         * @type {module:echarts/data/List}
50269         * @readOnly
50270         */
50271        this.data;
50272
50273        /**
50274         * @type {module:echarts/data/List}
50275         * @readOnly
50276         */
50277        this.edgeData;
50278    };
50279
50280    var graphProto = Graph.prototype;
50281    /**
50282     * @type {string}
50283     */
50284    graphProto.type = 'graph';
50285
50286    /**
50287     * If is directed graph
50288     * @return {boolean}
50289     */
50290    graphProto.isDirected = function () {
50291        return this._directed;
50292    };
50293
50294    /**
50295     * Add a new node
50296     * @param {string} id
50297     * @param {number} [dataIndex]
50298     */
50299    graphProto.addNode = function (id, dataIndex) {
50300        id = id || ('' + dataIndex);
50301
50302        var nodesMap = this._nodesMap;
50303
50304        if (nodesMap[generateNodeKey(id)]) {
50305            if (__DEV__) {
50306                console.error('Graph nodes have duplicate name or id');
50307            }
50308            return;
50309        }
50310
50311        var node = new Node(id, dataIndex);
50312        node.hostGraph = this;
50313
50314        this.nodes.push(node);
50315
50316        nodesMap[generateNodeKey(id)] = node;
50317        return node;
50318    };
50319
50320    /**
50321     * Get node by data index
50322     * @param  {number} dataIndex
50323     * @return {module:echarts/data/Graph~Node}
50324     */
50325    graphProto.getNodeByIndex = function (dataIndex) {
50326        var rawIdx = this.data.getRawIndex(dataIndex);
50327        return this.nodes[rawIdx];
50328    };
50329    /**
50330     * Get node by id
50331     * @param  {string} id
50332     * @return {module:echarts/data/Graph.Node}
50333     */
50334    graphProto.getNodeById = function (id) {
50335        return this._nodesMap[generateNodeKey(id)];
50336    };
50337
50338    /**
50339     * Add a new edge
50340     * @param {number|string|module:echarts/data/Graph.Node} n1
50341     * @param {number|string|module:echarts/data/Graph.Node} n2
50342     * @param {number} [dataIndex=-1]
50343     * @return {module:echarts/data/Graph.Edge}
50344     */
50345    graphProto.addEdge = function (n1, n2, dataIndex) {
50346        var nodesMap = this._nodesMap;
50347        var edgesMap = this._edgesMap;
50348
50349        // PNEDING
50350        if (typeof n1 === 'number') {
50351            n1 = this.nodes[n1];
50352        }
50353        if (typeof n2 === 'number') {
50354            n2 = this.nodes[n2];
50355        }
50356
50357        if (!Node.isInstance(n1)) {
50358            n1 = nodesMap[generateNodeKey(n1)];
50359        }
50360        if (!Node.isInstance(n2)) {
50361            n2 = nodesMap[generateNodeKey(n2)];
50362        }
50363        if (!n1 || !n2) {
50364            return;
50365        }
50366
50367        var key = n1.id + '-' + n2.id;
50368        // PENDING
50369        if (edgesMap[key]) {
50370            return;
50371        }
50372
50373        var edge = new Edge(n1, n2, dataIndex);
50374        edge.hostGraph = this;
50375
50376        if (this._directed) {
50377            n1.outEdges.push(edge);
50378            n2.inEdges.push(edge);
50379        }
50380        n1.edges.push(edge);
50381        if (n1 !== n2) {
50382            n2.edges.push(edge);
50383        }
50384
50385        this.edges.push(edge);
50386        edgesMap[key] = edge;
50387
50388        return edge;
50389    };
50390
50391    /**
50392     * Get edge by data index
50393     * @param  {number} dataIndex
50394     * @return {module:echarts/data/Graph~Node}
50395     */
50396    graphProto.getEdgeByIndex = function (dataIndex) {
50397        var rawIdx = this.edgeData.getRawIndex(dataIndex);
50398        return this.edges[rawIdx];
50399    };
50400    /**
50401     * Get edge by two linked nodes
50402     * @param  {module:echarts/data/Graph.Node|string} n1
50403     * @param  {module:echarts/data/Graph.Node|string} n2
50404     * @return {module:echarts/data/Graph.Edge}
50405     */
50406    graphProto.getEdge = function (n1, n2) {
50407        if (Node.isInstance(n1)) {
50408            n1 = n1.id;
50409        }
50410        if (Node.isInstance(n2)) {
50411            n2 = n2.id;
50412        }
50413
50414        var edgesMap = this._edgesMap;
50415
50416        if (this._directed) {
50417            return edgesMap[n1 + '-' + n2];
50418        } else {
50419            return edgesMap[n1 + '-' + n2]
50420                || edgesMap[n2 + '-' + n1];
50421        }
50422    };
50423
50424    /**
50425     * Iterate all nodes
50426     * @param  {Function} cb
50427     * @param  {*} [context]
50428     */
50429    graphProto.eachNode = function (cb, context) {
50430        var nodes = this.nodes;
50431        var len = nodes.length;
50432        for (var i = 0; i < len; i++) {
50433            if (nodes[i].dataIndex >= 0) {
50434                cb.call(context, nodes[i], i);
50435            }
50436        }
50437    };
50438
50439    /**
50440     * Iterate all edges
50441     * @param  {Function} cb
50442     * @param  {*} [context]
50443     */
50444    graphProto.eachEdge = function (cb, context) {
50445        var edges = this.edges;
50446        var len = edges.length;
50447        for (var i = 0; i < len; i++) {
50448            if (edges[i].dataIndex >= 0
50449                && edges[i].node1.dataIndex >= 0
50450                && edges[i].node2.dataIndex >= 0
50451            ) {
50452                cb.call(context, edges[i], i);
50453            }
50454        }
50455    };
50456
50457    /**
50458     * Breadth first traverse
50459     * @param {Function} cb
50460     * @param {module:echarts/data/Graph.Node} startNode
50461     * @param {string} [direction='none'] 'none'|'in'|'out'
50462     * @param {*} [context]
50463     */
50464    graphProto.breadthFirstTraverse = function (
50465        cb, startNode, direction, context
50466    ) {
50467        if (!Node.isInstance(startNode)) {
50468            startNode = this._nodesMap[generateNodeKey(startNode)];
50469        }
50470        if (!startNode) {
50471            return;
50472        }
50473
50474        var edgeType = direction === 'out'
50475            ? 'outEdges' : (direction === 'in' ? 'inEdges' : 'edges');
50476
50477        for (var i = 0; i < this.nodes.length; i++) {
50478            this.nodes[i].__visited = false;
50479        }
50480
50481        if (cb.call(context, startNode, null)) {
50482            return;
50483        }
50484
50485        var queue = [startNode];
50486        while (queue.length) {
50487            var currentNode = queue.shift();
50488            var edges = currentNode[edgeType];
50489
50490            for (var i = 0; i < edges.length; i++) {
50491                var e = edges[i];
50492                var otherNode = e.node1 === currentNode
50493                    ? e.node2 : e.node1;
50494                if (!otherNode.__visited) {
50495                    if (cb.call(context, otherNode, currentNode)) {
50496                        // Stop traversing
50497                        return;
50498                    }
50499                    queue.push(otherNode);
50500                    otherNode.__visited = true;
50501                }
50502            }
50503        }
50504    };
50505
50506// TODO
50507// graphProto.depthFirstTraverse = function (
50508//     cb, startNode, direction, context
50509// ) {
50510
50511// };
50512
50513// Filter update
50514    graphProto.update = function () {
50515        var data = this.data;
50516        var edgeData = this.edgeData;
50517        var nodes = this.nodes;
50518        var edges = this.edges;
50519
50520        for (var i = 0, len = nodes.length; i < len; i++) {
50521            nodes[i].dataIndex = -1;
50522        }
50523        for (var i = 0, len = data.count(); i < len; i++) {
50524            nodes[data.getRawIndex(i)].dataIndex = i;
50525        }
50526
50527        edgeData.filterSelf(function (idx) {
50528            var edge = edges[edgeData.getRawIndex(idx)];
50529            return edge.node1.dataIndex >= 0 && edge.node2.dataIndex >= 0;
50530        });
50531
50532        // Update edge
50533        for (var i = 0, len = edges.length; i < len; i++) {
50534            edges[i].dataIndex = -1;
50535        }
50536        for (var i = 0, len = edgeData.count(); i < len; i++) {
50537            edges[edgeData.getRawIndex(i)].dataIndex = i;
50538        }
50539    };
50540
50541    /**
50542     * @return {module:echarts/data/Graph}
50543     */
50544    graphProto.clone = function () {
50545        var graph = new Graph(this._directed);
50546        var nodes = this.nodes;
50547        var edges = this.edges;
50548        for (var i = 0; i < nodes.length; i++) {
50549            graph.addNode(nodes[i].id, nodes[i].dataIndex);
50550        }
50551        for (var i = 0; i < edges.length; i++) {
50552            var e = edges[i];
50553            graph.addEdge(e.node1.id, e.node2.id, e.dataIndex);
50554        }
50555        return graph;
50556    };
50557
50558
50559    /**
50560     * @alias module:echarts/data/Graph.Node
50561     */
50562    function Node(id, dataIndex) {
50563        /**
50564         * @type {string}
50565         */
50566        this.id = id == null ? '' : id;
50567
50568        /**
50569         * @type {Array.<module:echarts/data/Graph.Edge>}
50570         */
50571        this.inEdges = [];
50572        /**
50573         * @type {Array.<module:echarts/data/Graph.Edge>}
50574         */
50575        this.outEdges = [];
50576        /**
50577         * @type {Array.<module:echarts/data/Graph.Edge>}
50578         */
50579        this.edges = [];
50580        /**
50581         * @type {module:echarts/data/Graph}
50582         */
50583        this.hostGraph;
50584
50585        /**
50586         * @type {number}
50587         */
50588        this.dataIndex = dataIndex == null ? -1 : dataIndex;
50589    }
50590
50591    Node.prototype = {
50592
50593        constructor: Node,
50594
50595        /**
50596         * @return {number}
50597         */
50598        degree: function () {
50599            return this.edges.length;
50600        },
50601
50602        /**
50603         * @return {number}
50604         */
50605        inDegree: function () {
50606            return this.inEdges.length;
50607        },
50608
50609        /**
50610         * @return {number}
50611         */
50612        outDegree: function () {
50613            return this.outEdges.length;
50614        },
50615
50616        /**
50617         * @param {string} [path]
50618         * @return {module:echarts/model/Model}
50619         */
50620        getModel: function (path) {
50621            if (this.dataIndex < 0) {
50622                return;
50623            }
50624            var graph = this.hostGraph;
50625            var itemModel = graph.data.getItemModel(this.dataIndex);
50626
50627            return itemModel.getModel(path);
50628        }
50629    };
50630
50631    /**
50632     * 图边
50633     * @alias module:echarts/data/Graph.Edge
50634     * @param {module:echarts/data/Graph.Node} n1
50635     * @param {module:echarts/data/Graph.Node} n2
50636     * @param {number} [dataIndex=-1]
50637     */
50638    function Edge(n1, n2, dataIndex) {
50639
50640        /**
50641         * 节点1,如果是有向图则为源节点
50642         * @type {module:echarts/data/Graph.Node}
50643         */
50644        this.node1 = n1;
50645
50646        /**
50647         * 节点2ï¼Œå¦‚æžœæ˜¯æœ‰å‘å›¾åˆ™ä¸ºç›®æ ‡èŠ‚ç‚¹
50648         * @type {module:echarts/data/Graph.Node}
50649         */
50650        this.node2 = n2;
50651
50652        this.dataIndex = dataIndex == null ? -1 : dataIndex;
50653    }
50654
50655    /**
50656     * @param {string} [path]
50657     * @return {module:echarts/model/Model}
50658     */
50659    Edge.prototype.getModel = function (path) {
50660        if (this.dataIndex < 0) {
50661            return;
50662        }
50663        var graph = this.hostGraph;
50664        var itemModel = graph.edgeData.getItemModel(this.dataIndex);
50665
50666        return itemModel.getModel(path);
50667    };
50668
50669    var createGraphDataProxyMixin = function (hostName, dataName) {
50670        return {
50671            /**
50672             * @param {string=} [dimension='value'] Default 'value'. can be 'a', 'b', 'c', 'd', 'e'.
50673             * @return {number}
50674             */
50675            getValue: function (dimension) {
50676                var data = this[hostName][dataName];
50677                return data.get(data.getDimension(dimension || 'value'), this.dataIndex);
50678            },
50679
50680            /**
50681             * @param {Object|string} key
50682             * @param {*} [value]
50683             */
50684            setVisual: function (key, value) {
50685                this.dataIndex >= 0
50686                && this[hostName][dataName].setItemVisual(this.dataIndex, key, value);
50687            },
50688
50689            /**
50690             * @param {string} key
50691             * @return {boolean}
50692             */
50693            getVisual: function (key, ignoreParent) {
50694                return this[hostName][dataName].getItemVisual(this.dataIndex, key, ignoreParent);
50695            },
50696
50697            /**
50698             * @param {Object} layout
50699             * @return {boolean} [merge=false]
50700             */
50701            setLayout: function (layout, merge$$1) {
50702                this.dataIndex >= 0
50703                && this[hostName][dataName].setItemLayout(this.dataIndex, layout, merge$$1);
50704            },
50705
50706            /**
50707             * @return {Object}
50708             */
50709            getLayout: function () {
50710                return this[hostName][dataName].getItemLayout(this.dataIndex);
50711            },
50712
50713            /**
50714             * @return {module:zrender/Element}
50715             */
50716            getGraphicEl: function () {
50717                return this[hostName][dataName].getItemGraphicEl(this.dataIndex);
50718            },
50719
50720            /**
50721             * @return {number}
50722             */
50723            getRawIndex: function () {
50724                return this[hostName][dataName].getRawIndex(this.dataIndex);
50725            }
50726        };
50727    };
50728
50729    mixin(Node, createGraphDataProxyMixin('hostGraph', 'data'));
50730    mixin(Edge, createGraphDataProxyMixin('hostGraph', 'edgeData'));
50731
50732    Graph.Node = Node;
50733    Graph.Edge = Edge;
50734
50735    enableClassCheck(Node);
50736    enableClassCheck(Edge);
50737
50738    /*
50739* Licensed to the Apache Software Foundation (ASF) under one
50740* or more contributor license agreements.  See the NOTICE file
50741* distributed with this work for additional information
50742* regarding copyright ownership.  The ASF licenses this file
50743* to you under the Apache License, Version 2.0 (the
50744* "License"); you may not use this file except in compliance
50745* with the License.  You may obtain a copy of the License at
50746*
50747*   http://www.apache.org/licenses/LICENSE-2.0
50748*
50749* Unless required by applicable law or agreed to in writing,
50750* software distributed under the License is distributed on an
50751* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
50752* KIND, either express or implied.  See the License for the
50753* specific language governing permissions and limitations
50754* under the License.
50755*/
50756
50757    var createGraphFromNodeEdge = function (nodes, edges, seriesModel, directed, beforeLink) {
50758        // ??? TODO
50759        // support dataset?
50760        var graph = new Graph(directed);
50761        for (var i = 0; i < nodes.length; i++) {
50762            graph.addNode(retrieve(
50763                // Id, name, dataIndex
50764                nodes[i].id, nodes[i].name, i
50765            ), i);
50766        }
50767
50768        var linkNameList = [];
50769        var validEdges = [];
50770        var linkCount = 0;
50771        for (var i = 0; i < edges.length; i++) {
50772            var link = edges[i];
50773            var source = link.source;
50774            var target = link.target;
50775            // addEdge may fail when source or target not exists
50776            if (graph.addEdge(source, target, linkCount)) {
50777                validEdges.push(link);
50778                linkNameList.push(retrieve(link.id, source + ' > ' + target));
50779                linkCount++;
50780            }
50781        }
50782
50783        var coordSys = seriesModel.get('coordinateSystem');
50784        var nodeData;
50785        if (coordSys === 'cartesian2d' || coordSys === 'polar') {
50786            nodeData = createListFromArray(nodes, seriesModel);
50787        }
50788        else {
50789            var coordSysCtor = CoordinateSystemManager.get(coordSys);
50790            var coordDimensions = (coordSysCtor && coordSysCtor.type !== 'view')
50791                ? (coordSysCtor.dimensions || []) : [];
50792            // FIXME: Some geo do not need `value` dimenson, whereas `calendar` needs
50793            // `value` dimension, but graph need `value` dimension. It's better to
50794            // uniform this behavior.
50795            if (indexOf(coordDimensions, 'value') < 0) {
50796                coordDimensions.concat(['value']);
50797            }
50798
50799            var dimensionNames = createDimensions(nodes, {
50800                coordDimensions: coordDimensions
50801            });
50802            nodeData = new List(dimensionNames, seriesModel);
50803            nodeData.initData(nodes);
50804        }
50805
50806        var edgeData = new List(['value'], seriesModel);
50807        edgeData.initData(validEdges, linkNameList);
50808
50809        beforeLink && beforeLink(nodeData, edgeData);
50810
50811        linkList({
50812            mainData: nodeData,
50813            struct: graph,
50814            structAttr: 'graph',
50815            datas: {node: nodeData, edge: edgeData},
50816            datasAttr: {node: 'data', edge: 'edgeData'}
50817        });
50818
50819        // Update dataIndex of nodes and edges because invalid edge may be removed
50820        graph.update();
50821
50822        return graph;
50823    };
50824
50825    /*
50826* Licensed to the Apache Software Foundation (ASF) under one
50827* or more contributor license agreements.  See the NOTICE file
50828* distributed with this work for additional information
50829* regarding copyright ownership.  The ASF licenses this file
50830* to you under the Apache License, Version 2.0 (the
50831* "License"); you may not use this file except in compliance
50832* with the License.  You may obtain a copy of the License at
50833*
50834*   http://www.apache.org/licenses/LICENSE-2.0
50835*
50836* Unless required by applicable law or agreed to in writing,
50837* software distributed under the License is distributed on an
50838* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
50839* KIND, either express or implied.  See the License for the
50840* specific language governing permissions and limitations
50841* under the License.
50842*/
50843
50844    var GraphSeries = extendSeriesModel({
50845
50846        type: 'series.graph',
50847
50848        init: function (option) {
50849            GraphSeries.superApply(this, 'init', arguments);
50850
50851            // Provide data for legend select
50852            this.legendDataProvider = function () {
50853                return this._categoriesData;
50854            };
50855
50856            this.fillDataTextStyle(option.edges || option.links);
50857
50858            this._updateCategoriesData();
50859        },
50860
50861        mergeOption: function (option) {
50862            GraphSeries.superApply(this, 'mergeOption', arguments);
50863
50864            this.fillDataTextStyle(option.edges || option.links);
50865
50866            this._updateCategoriesData();
50867        },
50868
50869        mergeDefaultAndTheme: function (option) {
50870            GraphSeries.superApply(this, 'mergeDefaultAndTheme', arguments);
50871            defaultEmphasis(option, ['edgeLabel'], ['show']);
50872        },
50873
50874        getInitialData: function (option, ecModel) {
50875            var edges = option.edges || option.links || [];
50876            var nodes = option.data || option.nodes || [];
50877            var self = this;
50878
50879            if (nodes && edges) {
50880                return createGraphFromNodeEdge(nodes, edges, this, true, beforeLink).data;
50881            }
50882
50883            function beforeLink(nodeData, edgeData) {
50884                // Overwrite nodeData.getItemModel to
50885                nodeData.wrapMethod('getItemModel', function (model) {
50886                    var categoriesModels = self._categoriesModels;
50887                    var categoryIdx = model.getShallow('category');
50888                    var categoryModel = categoriesModels[categoryIdx];
50889                    if (categoryModel) {
50890                        categoryModel.parentModel = model.parentModel;
50891                        model.parentModel = categoryModel;
50892                    }
50893                    return model;
50894                });
50895
50896                var edgeLabelModel = self.getModel('edgeLabel');
50897                // For option `edgeLabel` can be found by label.xxx.xxx on item mode.
50898                var fakeSeriesModel = new Model(
50899                    {label: edgeLabelModel.option},
50900                    edgeLabelModel.parentModel,
50901                    ecModel
50902                );
50903                var emphasisEdgeLabelModel = self.getModel('emphasis.edgeLabel');
50904                var emphasisFakeSeriesModel = new Model(
50905                    {emphasis: {label: emphasisEdgeLabelModel.option}},
50906                    emphasisEdgeLabelModel.parentModel,
50907                    ecModel
50908                );
50909
50910                edgeData.wrapMethod('getItemModel', function (model) {
50911                    model.customizeGetParent(edgeGetParent);
50912                    return model;
50913                });
50914
50915                function edgeGetParent(path) {
50916                    path = this.parsePath(path);
50917                    return (path && path[0] === 'label')
50918                        ? fakeSeriesModel
50919                        : (path && path[0] === 'emphasis' && path[1] === 'label')
50920                            ? emphasisFakeSeriesModel
50921                            : this.parentModel;
50922                }
50923            }
50924        },
50925
50926        /**
50927         * @return {module:echarts/data/Graph}
50928         */
50929        getGraph: function () {
50930            return this.getData().graph;
50931        },
50932
50933        /**
50934         * @return {module:echarts/data/List}
50935         */
50936        getEdgeData: function () {
50937            return this.getGraph().edgeData;
50938        },
50939
50940        /**
50941         * @return {module:echarts/data/List}
50942         */
50943        getCategoriesData: function () {
50944            return this._categoriesData;
50945        },
50946
50947        /**
50948         * @override
50949         */
50950        formatTooltip: function (dataIndex, multipleSeries, dataType) {
50951            if (dataType === 'edge') {
50952                var nodeData = this.getData();
50953                var params = this.getDataParams(dataIndex, dataType);
50954                var edge = nodeData.graph.getEdgeByIndex(dataIndex);
50955                var sourceName = nodeData.getName(edge.node1.dataIndex);
50956                var targetName = nodeData.getName(edge.node2.dataIndex);
50957
50958                var html = [];
50959                sourceName != null && html.push(sourceName);
50960                targetName != null && html.push(targetName);
50961                html = encodeHTML(html.join(' > '));
50962
50963                if (params.value) {
50964                    html += ' : ' + encodeHTML(params.value);
50965                }
50966                return html;
50967            }
50968            else { // dataType === 'node' or empty
50969                return GraphSeries.superApply(this, 'formatTooltip', arguments);
50970            }
50971        },
50972
50973        _updateCategoriesData: function () {
50974            var categories = map(this.option.categories || [], function (category) {
50975                // Data must has value
50976                return category.value != null ? category : extend({
50977                    value: 0
50978                }, category);
50979            });
50980            var categoriesData = new List(['value'], this);
50981            categoriesData.initData(categories);
50982
50983            this._categoriesData = categoriesData;
50984
50985            this._categoriesModels = categoriesData.mapArray(function (idx) {
50986                return categoriesData.getItemModel(idx, true);
50987            });
50988        },
50989
50990        setZoom: function (zoom) {
50991            this.option.zoom = zoom;
50992        },
50993
50994        setCenter: function (center) {
50995            this.option.center = center;
50996        },
50997
50998        isAnimationEnabled: function () {
50999            return GraphSeries.superCall(this, 'isAnimationEnabled')
51000                // Not enable animation when do force layout
51001                && !(this.get('layout') === 'force' && this.get('force.layoutAnimation'));
51002        },
51003
51004        defaultOption: {
51005            zlevel: 0,
51006            z: 2,
51007
51008            coordinateSystem: 'view',
51009
51010            // Default option for all coordinate systems
51011            // xAxisIndex: 0,
51012            // yAxisIndex: 0,
51013            // polarIndex: 0,
51014            // geoIndex: 0,
51015
51016            legendHoverLink: true,
51017
51018            hoverAnimation: true,
51019
51020            layout: null,
51021
51022            focusNodeAdjacency: false,
51023
51024            // Configuration of circular layout
51025            circular: {
51026                rotateLabel: false
51027            },
51028            // Configuration of force directed layout
51029            force: {
51030                initLayout: null,
51031                // Node repulsion. Can be an array to represent range.
51032                repulsion: [0, 50],
51033                gravity: 0.1,
51034
51035                // Edge length. Can be an array to represent range.
51036                edgeLength: 30,
51037
51038                layoutAnimation: true
51039            },
51040
51041            left: 'center',
51042            top: 'center',
51043            // right: null,
51044            // bottom: null,
51045            // width: '80%',
51046            // height: '80%',
51047
51048            symbol: 'circle',
51049            symbolSize: 10,
51050
51051            edgeSymbol: ['none', 'none'],
51052            edgeSymbolSize: 10,
51053            edgeLabel: {
51054                position: 'middle'
51055            },
51056
51057            draggable: false,
51058
51059            roam: false,
51060
51061            // Default on center of graph
51062            center: null,
51063
51064            zoom: 1,
51065            // Symbol size scale ratio in roam
51066            nodeScaleRatio: 0.6,
51067            // cursor: null,
51068
51069            // categories: [],
51070
51071            // data: []
51072            // Or
51073            // nodes: []
51074            //
51075            // links: []
51076            // Or
51077            // edges: []
51078
51079            label: {
51080                show: false,
51081                formatter: '{b}'
51082            },
51083
51084            itemStyle: {},
51085
51086            lineStyle: {
51087                color: '#aaa',
51088                width: 1,
51089                curveness: 0,
51090                opacity: 0.5
51091            },
51092            emphasis: {
51093                label: {
51094                    show: true
51095                }
51096            }
51097        }
51098    });
51099
51100    /*
51101* Licensed to the Apache Software Foundation (ASF) under one
51102* or more contributor license agreements.  See the NOTICE file
51103* distributed with this work for additional information
51104* regarding copyright ownership.  The ASF licenses this file
51105* to you under the Apache License, Version 2.0 (the
51106* "License"); you may not use this file except in compliance
51107* with the License.  You may obtain a copy of the License at
51108*
51109*   http://www.apache.org/licenses/LICENSE-2.0
51110*
51111* Unless required by applicable law or agreed to in writing,
51112* software distributed under the License is distributed on an
51113* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
51114* KIND, either express or implied.  See the License for the
51115* specific language governing permissions and limitations
51116* under the License.
51117*/
51118
51119    /**
51120     * Line path for bezier and straight line draw
51121     */
51122
51123    var straightLineProto = Line.prototype;
51124    var bezierCurveProto = BezierCurve.prototype;
51125
51126    function isLine(shape) {
51127        return isNaN(+shape.cpx1) || isNaN(+shape.cpy1);
51128    }
51129
51130    var LinePath = extendShape({
51131
51132        type: 'ec-line',
51133
51134        style: {
51135            stroke: '#000',
51136            fill: null
51137        },
51138
51139        shape: {
51140            x1: 0,
51141            y1: 0,
51142            x2: 0,
51143            y2: 0,
51144            percent: 1,
51145            cpx1: null,
51146            cpy1: null
51147        },
51148
51149        buildPath: function (ctx, shape) {
51150            (isLine(shape) ? straightLineProto : bezierCurveProto).buildPath(ctx, shape);
51151        },
51152
51153        pointAt: function (t) {
51154            return isLine(this.shape)
51155                ? straightLineProto.pointAt.call(this, t)
51156                : bezierCurveProto.pointAt.call(this, t);
51157        },
51158
51159        tangentAt: function (t) {
51160            var shape = this.shape;
51161            var p = isLine(shape)
51162                ? [shape.x2 - shape.x1, shape.y2 - shape.y1]
51163                : bezierCurveProto.tangentAt.call(this, t);
51164            return normalize(p, p);
51165        }
51166    });
51167
51168    /*
51169* Licensed to the Apache Software Foundation (ASF) under one
51170* or more contributor license agreements.  See the NOTICE file
51171* distributed with this work for additional information
51172* regarding copyright ownership.  The ASF licenses this file
51173* to you under the Apache License, Version 2.0 (the
51174* "License"); you may not use this file except in compliance
51175* with the License.  You may obtain a copy of the License at
51176*
51177*   http://www.apache.org/licenses/LICENSE-2.0
51178*
51179* Unless required by applicable law or agreed to in writing,
51180* software distributed under the License is distributed on an
51181* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
51182* KIND, either express or implied.  See the License for the
51183* specific language governing permissions and limitations
51184* under the License.
51185*/
51186
51187    /**
51188     * @module echarts/chart/helper/Line
51189     */
51190
51191    var SYMBOL_CATEGORIES = ['fromSymbol', 'toSymbol'];
51192
51193    function makeSymbolTypeKey(symbolCategory) {
51194        return '_' + symbolCategory + 'Type';
51195    }
51196    /**
51197     * @inner
51198     */
51199    function createSymbol$1(name, lineData, idx) {
51200        var color = lineData.getItemVisual(idx, 'color');
51201        var symbolType = lineData.getItemVisual(idx, name);
51202        var symbolSize = lineData.getItemVisual(idx, name + 'Size');
51203
51204        if (!symbolType || symbolType === 'none') {
51205            return;
51206        }
51207
51208        if (!isArray(symbolSize)) {
51209            symbolSize = [symbolSize, symbolSize];
51210        }
51211        var symbolPath = createSymbol(
51212            symbolType, -symbolSize[0] / 2, -symbolSize[1] / 2,
51213            symbolSize[0], symbolSize[1], color
51214        );
51215
51216        symbolPath.name = name;
51217
51218        return symbolPath;
51219    }
51220
51221    function createLine(points) {
51222        var line = new LinePath({
51223            name: 'line'
51224        });
51225        setLinePoints(line.shape, points);
51226        return line;
51227    }
51228
51229    function setLinePoints(targetShape, points) {
51230        var p1 = points[0];
51231        var p2 = points[1];
51232        var cp1 = points[2];
51233        targetShape.x1 = p1[0];
51234        targetShape.y1 = p1[1];
51235        targetShape.x2 = p2[0];
51236        targetShape.y2 = p2[1];
51237        targetShape.percent = 1;
51238
51239        if (cp1) {
51240            targetShape.cpx1 = cp1[0];
51241            targetShape.cpy1 = cp1[1];
51242        }
51243        else {
51244            targetShape.cpx1 = NaN;
51245            targetShape.cpy1 = NaN;
51246        }
51247    }
51248
51249    function updateSymbolAndLabelBeforeLineUpdate () {
51250        var lineGroup = this;
51251        var symbolFrom = lineGroup.childOfName('fromSymbol');
51252        var symbolTo = lineGroup.childOfName('toSymbol');
51253        var label = lineGroup.childOfName('label');
51254        // Quick reject
51255        if (!symbolFrom && !symbolTo && label.ignore) {
51256            return;
51257        }
51258
51259        var invScale = 1;
51260        var parentNode = this.parent;
51261        while (parentNode) {
51262            if (parentNode.scale) {
51263                invScale /= parentNode.scale[0];
51264            }
51265            parentNode = parentNode.parent;
51266        }
51267
51268        var line = lineGroup.childOfName('line');
51269        // If line not changed
51270        // FIXME Parent scale changed
51271        if (!this.__dirty && !line.__dirty) {
51272            return;
51273        }
51274
51275        var percent = line.shape.percent;
51276        var fromPos = line.pointAt(0);
51277        var toPos = line.pointAt(percent);
51278
51279        var d = sub([], toPos, fromPos);
51280        normalize(d, d);
51281
51282        if (symbolFrom) {
51283            symbolFrom.attr('position', fromPos);
51284            var tangent = line.tangentAt(0);
51285            symbolFrom.attr('rotation', Math.PI / 2 - Math.atan2(
51286                tangent[1], tangent[0]
51287            ));
51288            symbolFrom.attr('scale', [invScale * percent, invScale * percent]);
51289        }
51290        if (symbolTo) {
51291            symbolTo.attr('position', toPos);
51292            var tangent = line.tangentAt(1);
51293            symbolTo.attr('rotation', -Math.PI / 2 - Math.atan2(
51294                tangent[1], tangent[0]
51295            ));
51296            symbolTo.attr('scale', [invScale * percent, invScale * percent]);
51297        }
51298
51299        if (!label.ignore) {
51300            label.attr('position', toPos);
51301
51302            var textPosition;
51303            var textAlign;
51304            var textVerticalAlign;
51305
51306            var distance$$1 = 5 * invScale;
51307            // End
51308            if (label.__position === 'end') {
51309                textPosition = [d[0] * distance$$1 + toPos[0], d[1] * distance$$1 + toPos[1]];
51310                textAlign = d[0] > 0.8 ? 'left' : (d[0] < -0.8 ? 'right' : 'center');
51311                textVerticalAlign = d[1] > 0.8 ? 'top' : (d[1] < -0.8 ? 'bottom' : 'middle');
51312            }
51313            // Middle
51314            else if (label.__position === 'middle') {
51315                var halfPercent = percent / 2;
51316                var tangent = line.tangentAt(halfPercent);
51317                var n = [tangent[1], -tangent[0]];
51318                var cp = line.pointAt(halfPercent);
51319                if (n[1] > 0) {
51320                    n[0] = -n[0];
51321                    n[1] = -n[1];
51322                }
51323                textPosition = [cp[0] + n[0] * distance$$1, cp[1] + n[1] * distance$$1];
51324                textAlign = 'center';
51325                textVerticalAlign = 'bottom';
51326                var rotation = -Math.atan2(tangent[1], tangent[0]);
51327                if (toPos[0] < fromPos[0]) {
51328                    rotation = Math.PI + rotation;
51329                }
51330                label.attr('rotation', rotation);
51331            }
51332            // Start
51333            else {
51334                textPosition = [-d[0] * distance$$1 + fromPos[0], -d[1] * distance$$1 + fromPos[1]];
51335                textAlign = d[0] > 0.8 ? 'right' : (d[0] < -0.8 ? 'left' : 'center');
51336                textVerticalAlign = d[1] > 0.8 ? 'bottom' : (d[1] < -0.8 ? 'top' : 'middle');
51337            }
51338            label.attr({
51339                style: {
51340                    // Use the user specified text align and baseline first
51341                    textVerticalAlign: label.__verticalAlign || textVerticalAlign,
51342                    textAlign: label.__textAlign || textAlign
51343                },
51344                position: textPosition,
51345                scale: [invScale, invScale]
51346            });
51347        }
51348    }
51349
51350    /**
51351     * @constructor
51352     * @extends {module:zrender/graphic/Group}
51353     * @alias {module:echarts/chart/helper/Line}
51354     */
51355    function Line$1(lineData, idx, seriesScope) {
51356        Group.call(this);
51357
51358        this._createLine(lineData, idx, seriesScope);
51359    }
51360
51361    var lineProto = Line$1.prototype;
51362
51363// Update symbol position and rotation
51364    lineProto.beforeUpdate = updateSymbolAndLabelBeforeLineUpdate;
51365
51366    lineProto._createLine = function (lineData, idx, seriesScope) {
51367        var seriesModel = lineData.hostModel;
51368        var linePoints = lineData.getItemLayout(idx);
51369
51370        var line = createLine(linePoints);
51371        line.shape.percent = 0;
51372        initProps(line, {
51373            shape: {
51374                percent: 1
51375            }
51376        }, seriesModel, idx);
51377
51378        this.add(line);
51379
51380        var label = new Text({
51381            name: 'label'
51382        });
51383        this.add(label);
51384
51385        each$1(SYMBOL_CATEGORIES, function (symbolCategory) {
51386            var symbol = createSymbol$1(symbolCategory, lineData, idx);
51387            // symbols must added after line to make sure
51388            // it will be updated after line#update.
51389            // Or symbol position and rotation update in line#beforeUpdate will be one frame slow
51390            this.add(symbol);
51391            this[makeSymbolTypeKey(symbolCategory)] = lineData.getItemVisual(idx, symbolCategory);
51392        }, this);
51393
51394        this._updateCommonStl(lineData, idx, seriesScope);
51395    };
51396
51397    lineProto.updateData = function (lineData, idx, seriesScope) {
51398        var seriesModel = lineData.hostModel;
51399
51400        var line = this.childOfName('line');
51401        var linePoints = lineData.getItemLayout(idx);
51402        var target = {
51403            shape: {}
51404        };
51405        setLinePoints(target.shape, linePoints);
51406        updateProps(line, target, seriesModel, idx);
51407
51408        each$1(SYMBOL_CATEGORIES, function (symbolCategory) {
51409            var symbolType = lineData.getItemVisual(idx, symbolCategory);
51410            var key = makeSymbolTypeKey(symbolCategory);
51411            // Symbol changed
51412            if (this[key] !== symbolType) {
51413                this.remove(this.childOfName(symbolCategory));
51414                var symbol = createSymbol$1(symbolCategory, lineData, idx);
51415                this.add(symbol);
51416            }
51417            this[key] = symbolType;
51418        }, this);
51419
51420        this._updateCommonStl(lineData, idx, seriesScope);
51421    };
51422
51423    lineProto._updateCommonStl = function (lineData, idx, seriesScope) {
51424        var seriesModel = lineData.hostModel;
51425
51426        var line = this.childOfName('line');
51427
51428        var lineStyle = seriesScope && seriesScope.lineStyle;
51429        var hoverLineStyle = seriesScope && seriesScope.hoverLineStyle;
51430        var labelModel = seriesScope && seriesScope.labelModel;
51431        var hoverLabelModel = seriesScope && seriesScope.hoverLabelModel;
51432
51433        // Optimization for large dataset
51434        if (!seriesScope || lineData.hasItemOption) {
51435            var itemModel = lineData.getItemModel(idx);
51436
51437            lineStyle = itemModel.getModel('lineStyle').getLineStyle();
51438            hoverLineStyle = itemModel.getModel('emphasis.lineStyle').getLineStyle();
51439
51440            labelModel = itemModel.getModel('label');
51441            hoverLabelModel = itemModel.getModel('emphasis.label');
51442        }
51443
51444        var visualColor = lineData.getItemVisual(idx, 'color');
51445        var visualOpacity = retrieve3(
51446            lineData.getItemVisual(idx, 'opacity'),
51447            lineStyle.opacity,
51448            1
51449        );
51450
51451        line.useStyle(defaults(
51452            {
51453                strokeNoScale: true,
51454                fill: 'none',
51455                stroke: visualColor,
51456                opacity: visualOpacity
51457            },
51458            lineStyle
51459        ));
51460        line.hoverStyle = hoverLineStyle;
51461
51462        // Update symbol
51463        each$1(SYMBOL_CATEGORIES, function (symbolCategory) {
51464            var symbol = this.childOfName(symbolCategory);
51465            if (symbol) {
51466                symbol.setColor(visualColor);
51467                symbol.setStyle({
51468                    opacity: visualOpacity
51469                });
51470            }
51471        }, this);
51472
51473        var showLabel = labelModel.getShallow('show');
51474        var hoverShowLabel = hoverLabelModel.getShallow('show');
51475
51476        var label = this.childOfName('label');
51477        var defaultLabelColor;
51478        var baseText;
51479
51480        // FIXME: the logic below probably should be merged to `graphic.setLabelStyle`.
51481        if (showLabel || hoverShowLabel) {
51482            defaultLabelColor = visualColor || '#000';
51483
51484            baseText = seriesModel.getFormattedLabel(idx, 'normal', lineData.dataType);
51485            if (baseText == null) {
51486                var rawVal = seriesModel.getRawValue(idx);
51487                baseText = rawVal == null
51488                    ? lineData.getName(idx)
51489                    : isFinite(rawVal)
51490                        ? round$1(rawVal)
51491                        : rawVal;
51492            }
51493        }
51494        var normalText = showLabel ? baseText : null;
51495        var emphasisText = hoverShowLabel
51496            ? retrieve2(
51497                seriesModel.getFormattedLabel(idx, 'emphasis', lineData.dataType),
51498                baseText
51499            )
51500            : null;
51501
51502        var labelStyle = label.style;
51503
51504        // Always set `textStyle` even if `normalStyle.text` is null, because default
51505        // values have to be set on `normalStyle`.
51506        if (normalText != null || emphasisText != null) {
51507            setTextStyle(label.style, labelModel, {
51508                text: normalText
51509            }, {
51510                autoColor: defaultLabelColor
51511            });
51512
51513            label.__textAlign = labelStyle.textAlign;
51514            label.__verticalAlign = labelStyle.textVerticalAlign;
51515            // 'start', 'middle', 'end'
51516            label.__position = labelModel.get('position') || 'middle';
51517        }
51518
51519        if (emphasisText != null) {
51520            // Only these properties supported in this emphasis 
51520style here.
51521            label.hoverStyle = {
51522                text: emphasisText,
51523                textFill: hoverLabelModel.getTextColor(true),
51524                // For merging hover style to normal style, do not use
51525                // `hoverLabelModel.getFont()` here.
51526                fontStyle: hoverLabelModel.getShallow('fontStyle'),
51527                fontWeight: hoverLabelModel.getShallow('fontWeight'),
51528                fontSize: hoverLabelModel.getShallow('fontSize'),
51529                fontFamily: hoverLabelModel.getShallow('fontFamily')
51530            };
51531        }
51532        else {
51533            label.hoverStyle = {
51534                text: null
51535            };
51536        }
51537
51538        label.ignore = !showLabel && !hoverShowLabel;
51539
51540        setHoverStyle(this);
51541    };
51542
51543    lineProto.highlight = function () {
51544        this.trigger('emphasis');
51545    };
51546
51547    lineProto.downplay = function () {
51548        this.trigger('normal');
51549    };
51550
51551    lineProto.updateLayout = function (lineData, idx) {
51552        this.setLinePoints(lineData.getItemLayout(idx));
51553    };
51554
51555    lineProto.setLinePoints = function (points) {
51556        var linePath = this.childOfName('line');
51557        setLinePoints(linePath.shape, points);
51558        linePath.dirty();
51559    };
51560
51561    inherits(Line$1, Group);
51562
51563    /*
51564* Licensed to the Apache Software Foundation (ASF) under one
51565* or more contributor license agreements.  See the NOTICE file
51566* distributed with this work for additional information
51567* regarding copyright ownership.  The ASF licenses this file
51568* to you under the Apache License, Version 2.0 (the
51569* "License"); you may not use this file except in compliance
51570* with the License.  You may obtain a copy of the License at
51571*
51572*   http://www.apache.org/licenses/LICENSE-2.0
51573*
51574* Unless required by applicable law or agreed to in writing,
51575* software distributed under the License is distributed on an
51576* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
51577* KIND, either express or implied.  See the License for the
51578* specific language governing permissions and limitations
51579* under the License.
51580*/
51581
51582    /**
51583     * @module echarts/chart/helper/LineDraw
51584     */
51585
51586// import IncrementalDisplayable from 'zrender/src/graphic/IncrementalDisplayable';
51587
51588    /**
51589     * @alias module:echarts/component/marker/LineDraw
51590     * @constructor
51591     */
51592    function LineDraw(ctor) {
51593        this._ctor = ctor || Line$1;
51594
51595        this.group = new Group();
51596    }
51597
51598    var lineDrawProto = LineDraw.prototype;
51599
51600    lineDrawProto.isPersistent = function () {
51601        return true;
51602    };
51603
51604    /**
51605     * @param {module:echarts/data/List} lineData
51606     */
51607    lineDrawProto.updateData = function (lineData) {
51608        var lineDraw = this;
51609        var group = lineDraw.group;
51610
51611        var oldLineData = lineDraw._lineData;
51612        lineDraw._lineData = lineData;
51613
51614        // There is no oldLineData only when first rendering or switching from
51615        // stream mode to normal mode, where previous elements should be removed.
51616        if (!oldLineData) {
51617            group.removeAll();
51618        }
51619
51620        var seriesScope = makeSeriesScope$1(lineData);
51621
51622        lineData.diff(oldLineData)
51623            .add(function (idx) {
51624                doAdd(lineDraw, lineData, idx, seriesScope);
51625            })
51626            .update(function (newIdx, oldIdx) {
51627                doUpdate(lineDraw, oldLineData, lineData, oldIdx, newIdx, seriesScope);
51628            })
51629            .remove(function (idx) {
51630                group.remove(oldLineData.getItemGraphicEl(idx));
51631            })
51632            .execute();
51633    };
51634
51635    function doAdd(lineDraw, lineData, idx, seriesScope) {
51636        var itemLayout = lineData.getItemLayout(idx);
51637
51638        if (!lineNeedsDraw(itemLayout)) {
51639            return;
51640        }
51641
51642        var el = new lineDraw._ctor(lineData, idx, seriesScope);
51643        lineData.setItemGraphicEl(idx, el);
51644        lineDraw.group.add(el);
51645    }
51646
51647    function doUpdate(lineDraw, oldLineData, newLineData, oldIdx, newIdx, seriesScope) {
51648        var itemEl = oldLineData.getItemGraphicEl(oldIdx);
51649
51650        if (!lineNeedsDraw(newLineData.getItemLayout(newIdx))) {
51651            lineDraw.group.remove(itemEl);
51652            return;
51653        }
51654
51655        if (!itemEl) {
51656            itemEl = new lineDraw._ctor(newLineData, newIdx, seriesScope);
51657        }
51658        else {
51659            itemEl.updateData(newLineData, newIdx, seriesScope);
51660        }
51661
51662        newLineData.setItemGraphicEl(newIdx, itemEl);
51663
51664        lineDraw.group.add(itemEl);
51665    }
51666
51667    lineDrawProto.updateLayout = function () {
51668        var lineData = this._lineData;
51669
51670        // Do not support update layout in incremental mode.
51671        if (!lineData) {
51672            return;
51673        }
51674
51675        lineData.eachItemGraphicEl(function (el, idx) {
51676            el.updateLayout(lineData, idx);
51677        }, this);
51678    };
51679
51680    lineDrawProto.incrementalPrepareUpdate = function (lineData) {
51681        this._seriesScope = makeSeriesScope$1(lineData);
51682        this._lineData = null;
51683        this.group.removeAll();
51684    };
51685
51686    lineDrawProto.incrementalUpdate = function (taskParams, lineData) {
51687        function updateIncrementalAndHover(el) {
51688            if (!el.isGroup) {
51689                el.incremental = el.useHoverLayer = true;
51690            }
51691        }
51692
51693        for (var idx = taskParams.start; idx < taskParams.end; idx++) {
51694            var itemLayout = lineData.getItemLayout(idx);
51695
51696            if (lineNeedsDraw(itemLayout)) {
51697                var el = new this._ctor(lineData, idx, this._seriesScope);
51698                el.traverse(updateIncrementalAndHover);
51699
51700                this.group.add(el);
51701                lineData.setItemGraphicEl(idx, el);
51702            }
51703        }
51704    };
51705
51706    function makeSeriesScope$1(lineData) {
51707        var hostModel = lineData.hostModel;
51708        return {
51709            lineStyle: hostModel.getModel('lineStyle').getLineStyle(),
51710            hoverLineStyle: hostModel.getModel('emphasis.lineStyle').getLineStyle(),
51711            labelModel: hostModel.getModel('label'),
51712            hoverLabelModel: hostModel.getModel('emphasis.label')
51713        };
51714    }
51715
51716    lineDrawProto.remove = function () {
51717        this._clearIncremental();
51718        this._incremental = null;
51719        this.group.removeAll();
51720    };
51721
51722    lineDrawProto._clearIncremental = function () {
51723        var incremental = this._incremental;
51724        if (incremental) {
51725            incremental.clearDisplaybles();
51726        }
51727    };
51728
51729    function isPointNaN(pt) {
51730        return isNaN(pt[0]) || isNaN(pt[1]);
51731    }
51732
51733    function lineNeedsDraw(pts) {
51734        return !isPointNaN(pts[0]) && !isPointNaN(pts[1]);
51735    }
51736
51737    /*
51738* Licensed to the Apache Software Foundation (ASF) under one
51739* or more contributor license agreements.  See the NOTICE file
51740* distributed with this work for additional information
51741* regarding copyright ownership.  The ASF licenses this file
51742* to you under the Apache License, Version 2.0 (the
51743* "License"); you may not use this file except in compliance
51744* with the License.  You may obtain a copy of the License at
51745*
51746*   http://www.apache.org/licenses/LICENSE-2.0
51747*
51748* Unless required by applicable law or agreed to in writing,
51749* software distributed under the License is distributed on an
51750* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
51751* KIND, either express or implied.  See the License for the
51752* specific language governing permissions and limitations
51753* under the License.
51754*/
51755
51756    var v1 = [];
51757    var v2 = [];
51758    var v3 = [];
51759    var quadraticAt$1 = quadraticAt;
51760    var v2DistSquare = distSquare;
51761    var mathAbs$1 = Math.abs;
51762    function intersectCurveCircle(curvePoints, center, radius) {
51763        var p0 = curvePoints[0];
51764        var p1 = curvePoints[1];
51765        var p2 = curvePoints[2];
51766
51767        var d = Infinity;
51768        var t;
51769        var radiusSquare = radius * radius;
51770        var interval = 0.1;
51771
51772        for (var _t = 0.1; _t <= 0.9; _t += 0.1) {
51773            v1[0] = quadraticAt$1(p0[0], p1[0], p2[0], _t);
51774            v1[1] = quadraticAt$1(p0[1], p1[1], p2[1], _t);
51775            var diff = mathAbs$1(v2DistSquare(v1, center) - radiusSquare);
51776            if (diff < d) {
51777                d = diff;
51778                t = _t;
51779            }
51780        }
51781
51782        // Assume the segment is monotone,Find root through Bisection method
51783        // At most 32 iteration
51784        for (var i = 0; i < 32; i++) {
51785            // var prev = t - interval;
51786            var next = t + interval;
51787            // v1[0] = quadraticAt(p0[0], p1[0], p2[0], prev);
51788            // v1[1] = quadraticAt(p0[1], p1[1], p2[1], prev);
51789            v2[0] = quadraticAt$1(p0[0], p1[0], p2[0], t);
51790            v2[1] = quadraticAt$1(p0[1], p1[1], p2[1], t);
51791            v3[0] = quadraticAt$1(p0[0], p1[0], p2[0], next);
51792            v3[1] = quadraticAt$1(p0[1], p1[1], p2[1], next);
51793
51794            var diff = v2DistSquare(v2, center) - radiusSquare;
51795            if (mathAbs$1(diff) < 1e-2) {
51796                break;
51797            }
51798
51799            // var prevDiff = v2DistSquare(v1, center) - radiusSquare;
51800            var nextDiff = v2DistSquare(v3, center) - radiusSquare;
51801
51802            interval /= 2;
51803            if (diff < 0) {
51804                if (nextDiff >= 0) {
51805                    t = t + interval;
51806                }
51807                else {
51808                    t = t - interval;
51809                }
51810            }
51811            else {
51812                if (nextDiff >= 0) {
51813                    t = t - interval;
51814                }
51815                else {
51816                    t = t + interval;
51817                }
51818            }
51819        }
51820
51821        return t;
51822    }
51823
51824// Adjust edge to avoid
51825    var adjustEdge = function (graph, scale$$1) {
51826        var tmp0 = [];
51827        var quadraticSubdivide$$1 = quadraticSubdivide;
51828        var pts = [[], [], []];
51829        var pts2 = [[], []];
51830        var v = [];
51831        scale$$1 /= 2;
51832
51833        function getSymbolSize(node) {
51834            var symbolSize = node.getVisual('symbolSize');
51835            if (symbolSize instanceof Array) {
51836                symbolSize = (symbolSize[0] + symbolSize[1]) / 2;
51837            }
51838            return symbolSize;
51839        }
51840        graph.eachEdge(function (edge, idx) {
51841            var linePoints = edge.getLayout();
51842            var fromSymbol = edge.getVisual('fromSymbol');
51843            var toSymbol = edge.getVisual('toSymbol');
51844
51845            if (!linePoints.__original) {
51846                linePoints.__original = [
51847                    clone$1(linePoints[0]),
51848                    clone$1(linePoints[1])
51849                ];
51850                if (linePoints[2]) {
51851                    linePoints.__original.push(clone$1(linePoints[2]));
51852                }
51853            }
51854            var originalPoints = linePoints.__original;
51855            // Quadratic curve
51856            if (linePoints[2] != null) {
51857                copy(pts[0], originalPoints[0]);
51858                copy(pts[1], originalPoints[2]);
51859                copy(pts[2], originalPoints[1]);
51860                if (fromSymbol && fromSymbol != 'none') {
51861                    var symbolSize = getSymbolSize(edge.node1);
51862
51863                    var t = intersectCurveCircle(pts, originalPoints[0], symbolSize * scale$$1);
51864                    // Subdivide and get the second
51865                    quadraticSubdivide$$1(pts[0][0], pts[1][0], pts[2][0], t, tmp0);
51866                    pts[0][0] = tmp0[3];
51867                    pts[1][0] = tmp0[4];
51868                    quadraticSubdivide$$1(pts[0][1], pts[1][1], pts[2][1], t, tmp0);
51869                    pts[0][1] = tmp0[3];
51870                    pts[1][1] = tmp0[4];
51871                }
51872                if (toSymbol && toSymbol != 'none') {
51873                    var symbolSize = getSymbolSize(edge.node2);
51874
51875                    var t = intersectCurveCircle(pts, originalPoints[1], symbolSize * scale$$1);
51876                    // Subdivide and get the first
51877                    quadraticSubdivide$$1(pts[0][0], pts[1][0], pts[2][0], t, tmp0);
51878                    pts[1][0] = tmp0[1];
51879                    pts[2][0] = tmp0[2];
51880                    quadraticSubdivide$$1(pts[0][1], pts[1][1], pts[2][1], t, tmp0);
51881                    pts[1][1] = tmp0[1];
51882                    pts[2][1] = tmp0[2];
51883                }
51884                // Copy back to layout
51885                copy(linePoints[0], pts[0]);
51886                copy(linePoints[1], pts[2]);
51887                copy(linePoints[2], pts[1]);
51888            }
51889            // Line
51890            else {
51891                copy(pts2[0], originalPoints[0]);
51892                copy(pts2[1], originalPoints[1]);
51893
51894                sub(v, pts2[1], pts2[0]);
51895                normalize(v, v);
51896                if (fromSymbol && fromSymbol != 'none') {
51897
51898                    var symbolSize = getSymbolSize(edge.node1);
51899
51900                    scaleAndAdd(pts2[0], pts2[0], v, symbolSize * scale$$1);
51901                }
51902                if (toSymbol && toSymbol != 'none') {
51903                    var symbolSize = getSymbolSize(edge.node2);
51904
51905                    scaleAndAdd(pts2[1], pts2[1], v, -symbolSize * scale$$1);
51906                }
51907                copy(linePoints[0], pts2[0]);
51908                copy(linePoints[1], pts2[1]);
51909            }
51910        });
51911    };
51912
51913    /*
51914* Licensed to the Apache Software Foundation (ASF) under one
51915* or more contributor license agreements.  See the NOTICE file
51916* distributed with this work for additional information
51917* regarding copyright ownership.  The ASF licenses this file
51918* to you under the Apache License, Version 2.0 (the
51919* "License");
51919 you may not use this file except in compliance
51920* with the License.  You may obtain a copy of the License at
51921*
51922*   http://www.apache.org/licenses/LICENSE-2.0
51923*
51924* Unless required by applicable law or agreed to in writing,
51925* software distributed under the License is distributed on an
51926* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
51927* KIND, either express or implied.  See the License for the
51928* specific language governing permissions and limitations
51929* under the License.
51930*/
51931
51932    var nodeOpacityPath = ['itemStyle', 'opacity'];
51933    var lineOpacityPath = ['lineStyle', 'opacity'];
51934
51935    function getItemOpacity(item, opacityPath) {
51936        return item.getVisual('opacity') || item.getModel().get(opacityPath);
51937    }
51938
51939    function fadeOutItem(item, opacityPath, opacityRatio) {
51940        var el = item.getGraphicEl();
51941
51942        var opacity = getItemOpacity(item, opacityPath);
51943        if (opacityRatio != null) {
51944            opacity == null && (opacity = 1);
51945            opacity *= opacityRatio;
51946        }
51947
51948        el.downplay && el.downplay();
51949        el.traverse(function (child) {
51950            if (child.type !== 'group') {
51951                child.setStyle('opacity', opacity);
51952            }
51953        });
51954    }
51955
51956    function fadeInItem(item, opacityPath) {
51957        var opacity = getItemOpacity(item, opacityPath);
51958        var el = item.getGraphicEl();
51959
51960        el.highlight && el.highlight();
51961        el.traverse(function (child) {
51962            if (child.type !== 'group') {
51963                child.setStyle('opacity', opacity);
51964            }
51965        });
51966    }
51967
51968    extendChartView({
51969
51970        type: 'graph',
51971
51972        init: function (ecModel, api) {
51973            var symbolDraw = new SymbolDraw();
51974            var lineDraw = new LineDraw();
51975            var group = this.group;
51976
51977            this._controller = new RoamController(api.getZr());
51978            this._controllerHost = {target: group};
51979
51980            group.add(symbolDraw.group);
51981            group.add(lineDraw.group);
51982
51983            this._symbolDraw = symbolDraw;
51984            this._lineDraw = lineDraw;
51985
51986            this._firstRender = true;
51987        },
51988
51989        render: function (seriesModel, ecModel, api) {
51990            var coordSys = seriesModel.coordinateSystem;
51991
51992            this._model = seriesModel;
51993            this._nodeScaleRatio = seriesModel.get('nodeScaleRatio');
51994
51995            var symbolDraw = this._symbolDraw;
51996            var lineDraw = this._lineDraw;
51997
51998            var group = this.group;
51999
52000            if (coordSys.type === 'view') {
52001                var groupNewProp = {
52002                    position: coordSys.position,
52003                    scale: coordSys.scale
52004                };
52005                if (this._firstRender) {
52006                    group.attr(groupNewProp);
52007                }
52008                else {
52009                    updateProps(group, groupNewProp, seriesModel);
52010                }
52011            }
52012            // Fix edge contact point with node
52013            adjustEdge(seriesModel.getGraph(), this._getNodeGlobalScale(seriesModel));
52014
52015            var data = seriesModel.getData();
52016            symbolDraw.updateData(data);
52017
52018            var edgeData = seriesModel.getEdgeData();
52019            lineDraw.updateData(edgeData);
52020
52021            this._updateNodeAndLinkScale();
52022
52023            this._updateController(seriesModel, ecModel, api);
52024
52025            clearTimeout(this._layoutTimeout);
52026            var forceLayout = seriesModel.forceLayout;
52027            var layoutAnimation = seriesModel.get('force.layoutAnimation');
52028            if (forceLayout) {
52029                this._startForceLayoutIteration(forceLayout, layoutAnimation);
52030            }
52031
52032            data.eachItemGraphicEl(function (el, idx) {
52033                var itemModel = data.getItemModel(idx);
52034                // Update draggable
52035                el.off('drag').off('dragend');
52036                var draggable = data.getItemModel(idx).get('draggable');
52037                if (draggable) {
52038                    el.on('drag', function () {
52039                        if (forceLayout) {
52040                            forceLayout.warmUp();
52041                            !this._layouting
52042                            && this._startForceLayoutIteration(forceLayout, layoutAnimation);
52043                            forceLayout.setFixed(idx);
52044                            // Write position back to layout
52045                            data.setItemLayout(idx, el.position);
52046                        }
52047                    }, this).on('dragend', function () {
52048                        if (forceLayout) {
52049                            forceLayout.setUnfixed(idx);
52050                        }
52051                    }, this);
52052                }
52053                el.setDraggable(draggable && forceLayout);
52054
52055                el.off('mouseover', el.__focusNodeAdjacency);
52056                el.off('mouseout', el.__unfocusNodeAdjacency);
52057
52058                if (itemModel.get('focusNodeAdjacency')) {
52059                    el.on('mouseover', el.__focusNodeAdjacency = function () {
52060                        api.dispatchAction({
52061                            type: 'focusNodeAdjacency',
52062                            seriesId: seriesModel.id,
52063                            dataIndex: el.dataIndex
52064                        });
52065                    });
52066                    el.on('mouseout', el.__unfocusNodeAdjacency = function () {
52067                        api.dispatchAction({
52068                            type: 'unfocusNodeAdjacency',
52069                            seriesId: seriesModel.id
52070                        });
52071                    });
52072
52073                }
52074
52075            }, this);
52076
52077            data.graph.eachEdge(function (edge) {
52078                var el = edge.getGraphicEl();
52079
52080                el.off('mouseover', el.__focusNodeAdjacency);
52081                el.off('mouseout', el.__unfocusNodeAdjacency);
52082
52083                if (edge.getModel().get('focusNodeAdjacency')) {
52084                    el.on('mouseover', el.__focusNodeAdjacency = function () {
52085                        api.dispatchAction({
52086                            type: 'focusNodeAdjacency',
52087                            seriesId: seriesModel.id,
52088                            edgeDataIndex: edge.dataIndex
52089                        });
52090                    });
52091                    el.on('mouseout', el.__unfocusNodeAdjacency = function () {
52092                        api.dispatchAction({
52093                            type: 'unfocusNodeAdjacency',
52094                            seriesId: seriesModel.id
52095                        });
52096                    });
52097                }
52098            });
52099
52100            var circularRotateLabel = seriesModel.get('layout') === 'circular'
52101                && seriesModel.get('circular.rotateLabel');
52102            var cx = data.getLayout('cx');
52103            var cy = data.getLayout('cy');
52104            data.eachItemGraphicEl(function (el, idx) {
52105                var symbolPath = el.getSymbolPath();
52106                if (circularRotateLabel) {
52107                    var pos = data.getItemLayout(idx);
52108                    var rad = Math.atan2(pos[1] - cy, pos[0] - cx);
52109                    if (rad < 0) {
52110                        rad = Math.PI * 2 + rad;
52111                    }
52112                    var isLeft = pos[0] < cx;
52113                    if (isLeft) {
52114                        rad = rad - Math.PI;
52115                    }
52116                    var textPosition = isLeft ? 'left' : 'right';
52117                    symbolPath.setStyle({
52118                        textRotation: -rad,
52119                        textPosition: textPosition,
52120                        textOrigin: 'center'
52121                    });
52122                    symbolPath.hoverStyle && (symbolPath.hoverStyle.textPosition = textPosition);
52123                }
52124                else {
52125                    symbolPath.setStyle({
52126                        textRotation: 0
52127                    });
52128                }
52129            });
52130
52131            this._firstRender = false;
52132        },
52133
52134        dispose: function () {
52135            this._controller && this._controller.dispose();
52136            this._controllerHost = {};
52137        },
52138
52139        focusNodeAdjacency: function (seriesModel, ecModel, api, payload) {
52140            var data = this._model.getData();
52141            var graph = data.graph;
52142            var dataIndex = payload.dataIndex;
52143            var edgeDataIndex = payload.edgeDataIndex;
52144
52145            var node = graph.getNodeByIndex(dataIndex);
52146            var edge = graph.getEdgeByIndex(edgeDataIndex);
52147
52148            if (!node && !edge) {
52149                return;
52150            }
52151
52152            graph.eachNode(function (node) {
52153                fadeOutItem(node, nodeOpacityPath, 0.1);
52154            });
52155            graph.eachEdge(function (edge) {
52156                fadeOutItem(edge, lineOpacityPath, 0.1);
52157            });
52158
52159            if (node) {
52160                fadeInItem(node, nodeOpacityPath);
52161                each$1(node.edges, function (adjacentEdge) {
52162                    if (adjacentEdge.dataIndex < 0) {
52163                        return;
52164                    }
52165                    fadeInItem(adjacentEdge, lineOpacityPath);
52166                    fadeInItem(adjacentEdge.node1, nodeOpacityPath);
52167                    fadeInItem(adjacentEdge.node2, nodeOpacityPath);
52168                });
52169            }
52170            if (edge) {
52171                fadeInItem(edge, lineOpacityPath);
52172                fadeInItem(edge.node1, nodeOpacityPath);
52173                fadeInItem(edge.node2, nodeOpacityPath);
52174            }
52175        },
52176
52177        unfocusNodeAdjacency: function (seriesModel, ecModel, api, payload) {
52178            var graph = this._model.getData().graph;
52179
52180            graph.eachNode(function (node) {
52181                fadeOutItem(node, nodeOpacityPath);
52182            });
52183            graph.eachEdge(function (edge) {
52184                fadeOutItem(edge, lineOpacityPath);
52185            });
52186        },
52187
52188        _startForceLayoutIteration: function (forceLayout, layoutAnimation) {
52189            var self = this;
52190            (function step() {
52191                forceLayout.step(function (stopped) {
52192                    self.updateLayout(self._model);
52193                    (self._layouting = !stopped) && (
52194                        layoutAnimation
52195                            ? (self._layoutTimeout = setTimeout(step, 16))
52196                            : step()
52197                    );
52198                });
52199            })();
52200        },
52201
52202        _updateController: function (seriesModel, ecModel, api) {
52203            var controller = this._controller;
52204            var controllerHost = this._controllerHost;
52205            var group = this.group;
52206
52207            controller.setPointerChecker(function (e, x, y) {
52208                var rect = group.getBoundingRect();
52209                rect.applyTransform(group.transform);
52210                return rect.contain(x, y)
52211                    && !onIrrelevantElement(e, api, seriesModel);
52212            });
52213
52214            if (seriesModel.coordinateSystem.type !== 'view') {
52215                controller.disable();
52216                return;
52217            }
52218            controller.enable(seriesModel.get('roam'));
52219            controllerHost.zoomLimit = seriesModel.get('scaleLimit');
52220            controllerHost.zoom = seriesModel.coordinateSystem.getZoom();
52221
52222            controller
52223                .off('pan')
52224                .off('zoom')
52225                .on('pan', function (dx, dy) {
52226                    updateViewOnPan(controllerHost, dx, dy);
52227                    api.dispatchAction({
52228                        seriesId: seriesModel.id,
52229                        type: 'graphRoam',
52230                        dx: dx,
52231                        dy: dy
52232                    });
52233                })
52234                .on('zoom', function (zoom, mouseX, mouseY) {
52235                    updateViewOnZoom(controllerHost, zoom, mouseX, mouseY);
52236                    api.dispatchAction({
52237                        seriesId: seriesModel.id,
52238                        type: 'graphRoam',
52239                        zoom:  zoom,
52240                        originX: mouseX,
52241                        originY: mouseY
52242                    });
52243                    this._updateNodeAndLinkScale();
52244                    adjustEdge(seriesModel.getGraph(), this._getNodeGlobalScale(seriesModel));
52245                    this._lineDraw.updateLayout();
52246                }, this);
52247        },
52248
52249        _updateNodeAndLinkScale: function () {
52250            var seriesModel = this._model;
52251            var data = seriesModel.getData();
52252
52253            var nodeScale = this._getNodeGlobalScale(seriesModel);
52254            var invScale = [nodeScale, nodeScale];
52255
52256            data.eachItemGraphicEl(function (el, idx) {
52257                el.attr('scale', invScale);
52258            });
52259        },
52260
52261        _getNodeGlobalScale: function (seriesModel) {
52262            var coordSys = seriesModel.coordinateSystem;
52263            if (coordSys.type !== 'view') {
52264                return 1;
52265            }
52266
52267            var nodeScaleRatio = this._nodeScaleRatio;
52268
52269            var groupScale = coordSys.scale;
52270            var groupZoom = (groupScale && groupScale[0]) || 1;
52271            // Scale node when zoom changes
52272            var roamZoom = coordSys.getZoom();
52273            var nodeScale = (roamZoom - 1) * nodeScaleRatio + 1;
52274
52275            return nodeScale / groupZoom;
52276        },
52277
52278        updateLayout: function (seriesModel) {
52279            adjustEdge(seriesModel.getGraph(), this._getNodeGlobalScale(seriesModel));
52280
52281            this._symbolDraw.updateLayout();
52282            this._lineDraw.updateLayout();
52283        },
52284
52285        remove: function (ecModel, api) {
52286            this._symbolDraw && this._symbolDraw.remove();
52287            this._lineDraw && this._lineDraw.remove();
52288        }
52289    });
52290
52291    /*
52292* Licensed to the Apache Software Foundation (ASF) under one
52293* or more contributor license agreements.  See the NOTICE file
52294* distributed with this work for additional information
52295* regarding copyright ownership.  The ASF licenses this file
52296* to you under the Apache License, Version 2.0 (the
52297* "License"); you may not use this file except in compliance
52298* with the License.  You may obtain a copy of the License at
52299*
52300*   http://www.apache.org/licenses/LICENSE-2.0
52301*
52302* Unless required by applicable law or agreed to in writing,
52303* software distributed under the License is distributed on an
52304* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
52305* KIND, either express or implied.  See the License for the
52306* specific language governing permissions and limitations
52307* under the License.
52308*/
52309
52310    var actionInfo = {
52311        type: 'graphRoam',
52312        event: 'graphRoam',
52313        update: 'none'
52314    };
52315
52316    /**
52317     * @payload
52318     * @property {string} name Series name
52319     * @property {number} [dx]
52320     * @property {number} [dy]
52321     * @property {number} [zoom]
52322     * @property {number} [originX]
52323     * @property {number} [originY]
52324     */
52325    registerAction(actionInfo, function (payload, ecModel) {
52326        ecModel.eachComponent({mainType: 'series', query: payload}, function (seriesModel) {
52327            var coordSys = seriesModel.coordinateSystem;
52328
52329            var res = updateCenterAndZoom(coordSys, payload);
52330
52331            seriesModel.setCenter
52332            && seriesModel.setCenter(res.center);
52333
52334            seriesModel.setZoom
52335            && seriesModel.setZoom(res.zoom);
52336        });
52337    });
52338
52339
52340    /**
52341     * @payload
52342     * @property {number} [seriesIndex]
52343     * @property {string} [seriesId]
52344     * @property {string} [seriesName]
52345     * @property {number} [dataIndex]
52346     */
52347    registerAction({
52348        type: 'focusNodeAdjacency',
52349        event: 'focusNodeAdjacency',
52350        update: 'series.graph:focusNodeAdjacency'
52351    }, function () {});
52352
52353    /**
52354     * @payload
52355     * @property {number} [seriesIndex]
52356     * @property {string} [seriesId]
52357     * @property {string} [seriesName]
52358     */
52359    registerAction({
52360        type: 'unfocusNodeAdjacency',
52361        event: 'unfocusNodeAdjacency',
52362        update: 'series.graph:unfocusNodeAdjacency'
52363    }, function () {});
52364
52365    /*
52366* Licensed to the Apache Software Foundation (ASF) under one
52367* or more contributor license agreements.  See the NOTICE file
52368* distributed with this work for additional information
52369* regarding copyright ownership.  The ASF licenses this file
52370* to you under the Apache License, Version 2.0 (the
52371* "License"); you may not use this file except in compliance
52372* with the License.  You may obtain a copy of the License at
52373*
52374*   http://www.apache.org/licenses/LICENSE-2.0
52375*
52376* Unless required by applicable law or agreed to in writing,
52377* software distributed under the License is distributed on an
52378* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
52379* KIND, either express or implied.  See the License for the
52380* specific language governing permissions and limitations
52381* under the License.
52382*/
52383
52384
52385    var categoryFilter = function (ecModel) {
52386        var legendModels = ecModel.findComponents({
52387            mainType: 'legend'
52388        });
52389        if (!legendModels || !legendModels.length) {
52390            return;
52391        }
52392        ecModel.eachSeriesByType('graph', function (graphSeries) {
52393            var categoriesData = graphSeries.getCategoriesData();
52394            var graph = graphSeries.getGraph();
52395            var data = graph.data;
52396
52397            var categoryNames = categoriesData.mapArray(categoriesData.getName);
52398
52399            data.filterSelf(function (idx) {
52400                var model = data.getItemModel(idx);
52401                var category = model.getShallow('category');
52402                if (category != null) {
52403                    if (typeof category === 'number') {
52404                        category = categoryNames[category];
52405                    }
52406                    // If in any legend component the status is not selected.
52407                    for (var i = 0; i < legendModels.length; i++) {
52408                        if (!legendModels[i].isSelected(category)) {
52409                            return false;
52410                        }
52411                    }
52412                }
52413                return true;
52414            });
52415        }, this);
52416    };
52417
52418    /*
52419* Licensed to the Apache Software Foundation (ASF) under one
52420* or more contributor license agreements.  See the NOTICE file
52421* distributed with this work for additional information
52422* regarding copyright ownership.  The ASF licenses this file
52423* to you under the Apache License, Version 2.0 (the
52424* "License"); you may not use this file except in compliance
52425* with the License.  You may obtain a copy of the License at
52426*
52427*   http://www.apache.org/licenses/LICENSE-2.0
52428*
52429* Unless required by applicable law or agreed to in writing,
52430* software distributed under the License is distributed on an
52431* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
52432* KIND, either express or implied.  See the License for the
52433* specific language governing permissions and limitations
52434* under the License.
52435*/
52436
52437
52438    var categoryVisual = function (ecModel) {
52439
52440        var paletteScope = {};
52441        ecModel.eachSeriesByType('graph', function (seriesModel) {
52442            var categoriesData = seriesModel.getCategoriesData();
52443            var data = seriesModel.getData();
52444
52445            var categoryNameIdxMap = {};
52446
52447            categoriesData.each(function (idx) {
52448                var name = categoriesData.getName(idx);
52449                // Add prefix to avoid conflict with Object.prototype.
52450                categoryNameIdxMap['ec-' + name] = idx;
52451
52452                var itemModel = categoriesData.getItemModel(idx);
52453                var color = itemModel.get('itemStyle.color')
52454                    || seriesModel.getColorFromPalette(name, paletteScope);
52455                categoriesData.setItemVisual(idx, 'color', color);
52456            });
52457
52458            // Assign category color to visual
52459            if (categoriesData.count()) {
52460                data.each(function (idx) {
52461                    var model = data.getItemModel(idx);
52462                    var category = model.getShallow('category');
52463                    if (category != null) {
52464                        if (typeof category === 'string') {
52465                            category = categoryNameIdxMap['ec-' + category];
52466                        }
52467                        if (!data.getItemVisual(idx, 'color', true)) {
52468                            data.setItemVisual(
52469                                idx, 'color',
52470                                categoriesData.getItemVisual(category, 'color')
52471                            );
52472                        }
52473                    }
52474                });
52475            }
52476        });
52477    };
52478
52479    /*
52480* Licensed to the Apache Software Foundation (ASF) under one
52481* or more contributor license agreements.  See the NOTICE file
52482* distributed with this work for additional information
52483* regarding copyright ownership.  The ASF licenses this file
52484* to you under the Apache License, Version 2.0 (the
52485* "License");
52485 you may not use this file except in compliance
52486* with the License.  You may obtain a copy of the License at
52487*
52488*   http://www.apache.org/licenses/LICENSE-2.0
52489*
52490* Unless required by applicable law or agreed to in writing,
52491* software distributed under the License is distributed on an
52492* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
52493* KIND, either express or implied.  See the License for the
52494* specific language governing permissions and limitations
52495* under the License.
52496*/
52497
52498
52499    function normalize$1(a) {
52500        if (!(a instanceof Array)) {
52501            a = [a, a];
52502        }
52503        return a;
52504    }
52505
52506    var edgeVisual = function (ecModel) {
52507        ecModel.eachSeriesByType('graph', function (seriesModel) {
52508            var graph = seriesModel.getGraph();
52509            var edgeData = seriesModel.getEdgeData();
52510            var symbolType = normalize$1(seriesModel.get('edgeSymbol'));
52511            var symbolSize = normalize$1(seriesModel.get('edgeSymbolSize'));
52512
52513            var colorQuery = 'lineStyle.color'.split('.');
52514            var opacityQuery = 'lineStyle.opacity'.split('.');
52515
52516            edgeData.setVisual('fromSymbol', symbolType && symbolType[0]);
52517            edgeData.setVisual('toSymbol', symbolType && symbolType[1]);
52518            edgeData.setVisual('fromSymbolSize', symbolSize && symbolSize[0]);
52519            edgeData.setVisual('toSymbolSize', symbolSize && symbolSize[1]);
52520            edgeData.setVisual('color', seriesModel.get(colorQuery));
52521            edgeData.setVisual('opacity', seriesModel.get(opacityQuery));
52522
52523            edgeData.each(function (idx) {
52524                var itemModel = edgeData.getItemModel(idx);
52525                var edge = graph.getEdgeByIndex(idx);
52526                var symbolType = normalize$1(itemModel.getShallow('symbol', true));
52527                var symbolSize = normalize$1(itemModel.getShallow('symbolSize', true));
52528                // Edge visual must after node visual
52529                var color = itemModel.get(colorQuery);
52530                var opacity = itemModel.get(opacityQuery);
52531                switch (color) {
52532                    case 'source':
52533                        color = edge.node1.getVisual('color');
52534                        break;
52535                    case 'target':
52536                        color = edge.node2.getVisual('color');
52537                        break;
52538                }
52539
52540                symbolType[0] && edge.setVisual('fromSymbol', symbolType[0]);
52541                symbolType[1] && edge.setVisual('toSymbol', symbolType[1]);
52542                symbolSize[0] && edge.setVisual('fromSymbolSize', symbolSize[0]);
52543                symbolSize[1] && edge.setVisual('toSymbolSize', symbolSize[1]);
52544
52545                edge.setVisual('color', color);
52546                edge.setVisual('opacity', opacity);
52547            });
52548        });
52549    };
52550
52551    /*
52552* Licensed to the Apache Software Foundation (ASF) under one
52553* or more contributor license agreements.  See the NOTICE file
52554* distributed with this work for additional information
52555* regarding copyright ownership.  The ASF licenses this file
52556* to you under the Apache License, Version 2.0 (the
52557* "License"); you may not use this file except in compliance
52558* with the License.  You may obtain a copy of the License at
52559*
52560*   http://www.apache.org/licenses/LICENSE-2.0
52561*
52562* Unless required by applicable law or agreed to in writing,
52563* software distributed under the License is distributed on an
52564* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
52565* KIND, either express or implied.  See the License for the
52566* specific language governing permissions and limitations
52567* under the License.
52568*/
52569
52570    function simpleLayout$1(seriesModel) {
52571        var coordSys = seriesModel.coordinateSystem;
52572        if (coordSys && coordSys.type !== 'view') {
52573            return;
52574        }
52575        var graph = seriesModel.getGraph();
52576
52577        graph.eachNode(function (node) {
52578            var model = node.getModel();
52579            node.setLayout([+model.get('x'), +model.get('y')]);
52580        });
52581
52582        simpleLayoutEdge(graph);
52583    }
52584
52585    function simpleLayoutEdge(graph) {
52586        graph.eachEdge(function (edge) {
52587            var curveness = edge.getModel().get('lineStyle.curveness') || 0;
52588            var p1 = clone$1(edge.node1.getLayout());
52589            var p2 = clone$1(edge.node2.getLayout());
52590            var points = [p1, p2];
52591            if (+curveness) {
52592                points.push([
52593                    (p1[0] + p2[0]) / 2 - (p1[1] - p2[1]) * curveness,
52594                    (p1[1] + p2[1]) / 2 - (p2[0] - p1[0]) * curveness
52595                ]);
52596            }
52597            edge.setLayout(points);
52598        });
52599    }
52600
52601    /*
52602* Licensed to the Apache Software Foundation (ASF) under one
52603* or more contributor license agreements.  See the NOTICE file
52604* distributed with this work for additional information
52605* regarding copyright ownership.  The ASF licenses this file
52606* to you under the Apache License, Version 2.0 (the
52607* "License"); you may not use this file except in compliance
52608* with the License.  You may obtain a copy of the License at
52609*
52610*   http://www.apache.org/licenses/LICENSE-2.0
52611*
52612* Unless required by applicable law or agreed to in writing,
52613* software distributed under the License is distributed on an
52614* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
52615* KIND, either express or implied.  See the License for the
52616* specific language governing permissions and limitations
52617* under the License.
52618*/
52619
52620    var simpleLayout = function (ecModel, api) {
52621        ecModel.eachSeriesByType('graph', function (seriesModel) {
52622            var layout = seriesModel.get('layout');
52623            var coordSys = seriesModel.coordinateSystem;
52624            if (coordSys && coordSys.type !== 'view') {
52625                var data = seriesModel.getData();
52626
52627                var dimensions = [];
52628                each$1(coordSys.dimensions, function (coordDim) {
52629                    dimensions = dimensions.concat(data.mapDimension(coordDim, true));
52630                });
52631
52632                for (var dataIndex = 0; dataIndex < data.count(); dataIndex++) {
52633                    var value = [];
52634                    var hasValue = false;
52635                    for (var i = 0; i < dimensions.length; i++) {
52636                        var val = data.get(dimensions[i], dataIndex);
52637                        if (!isNaN(val)) {
52638                            hasValue = true;
52639                        }
52640                        value.push(val);
52641                    }
52642                    if (hasValue) {
52643                        data.setItemLayout(dataIndex, coordSys.dataToPoint(value));
52644                    }
52645                    else {
52646                        // Also {Array.<number>}, not undefined to avoid if...else... statement
52647                        data.setItemLayout(dataIndex, [NaN, NaN]);
52648                    }
52649                }
52650
52651                simpleLayoutEdge(data.graph);
52652            }
52653            else if (!layout || layout === 'none') {
52654                simpleLayout$1(seriesModel);
52655            }
52656        });
52657    };
52658
52659    /*
52660* Licensed to the Apache Software Foundation (ASF) under one
52661* or more contributor license agreements.  See the NOTICE file
52662* distributed with this work for additional information
52663* regarding copyright ownership.  The ASF licenses this file
52664* to you under the Apache License, Version 2.0 (the
52665* "License"); you may not use this file except in compliance
52666* with the License.  You may obtain a copy of the License at
52667*
52668*   http://www.apache.org/licenses/LICENSE-2.0
52669*
52670* Unless required by applicable law or agreed to in writing,
52671* software distributed under the License is distributed on an
52672* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
52673* KIND, either express or implied.  See the License for the
52674* specific language governing permissions and limitations
52675* under the License.
52676*/
52677
52678    function circularLayout$1(seriesModel) {
52679        var coordSys = seriesModel.coordinateSystem;
52680        if (coordSys && coordSys.type !== 'view') {
52681            return;
52682        }
52683
52684        var rect = coordSys.getBoundingRect();
52685
52686        var nodeData = seriesModel.getData();
52687        var graph = nodeData.graph;
52688
52689        var angle = 0;
52690        var sum = nodeData.getSum('value');
52691        var unitAngle = Math.PI * 2 / (sum || nodeData.count());
52692
52693        var cx = rect.width / 2 + rect.x;
52694        var cy = rect.height / 2 + rect.y;
52695
52696        var r = Math.min(rect.width, rect.height) / 2;
52697
52698        graph.eachNode(function (node) {
52699            var value = node.getValue('value');
52700
52701            angle += unitAngle * (sum ? value : 1) / 2;
52702
52703            node.setLayout([
52704                r * Math.cos(angle) + cx,
52705                r * Math.sin(angle) + cy
52706            ]);
52707
52708            angle += unitAngle * (sum ? value : 1) / 2;
52709        });
52710
52711        nodeData.setLayout({
52712            cx: cx,
52713            cy: cy
52714        });
52715
52716        graph.eachEdge(function (edge) {
52717            var curveness = edge.getModel().get('lineStyle.curveness') || 0;
52718            var p1 = clone$1(edge.node1.getLayout());
52719            var p2 = clone$1(edge.node2.getLayout());
52720            var cp1;
52721            var x12 = (p1[0] + p2[0]) / 2;
52722            var y12 = (p1[1] + p2[1]) / 2;
52723            if (+curveness) {
52724                curveness *= 3;
52725                cp1 = [
52726                    cx * curveness + x12 * (1 - curveness),
52727                    cy * curveness + y12 * (1 - curveness)
52728                ];
52729            }
52730            edge.setLayout([p1, p2, cp1]);
52731        });
52732    }
52733
52734    /*
52735* Licensed to the Apache Software Foundation (ASF) under one
52736* or more contributor license agreements.  See the NOTICE file
52737* distributed with this work for additional information
52738* regarding copyright ownership.  The ASF licenses this file
52739* to you under the Apache License, Version 2.0 (the
52740* "License"); you may not use this file except in compliance
52741* with the License.  You may obtain a copy of the License at
52742*
52743*   http://www.apache.org/licenses/LICENSE-2.0
52744*
52745* Unless required by applicable law or agreed to in writing,
52746* software distributed under the License is distributed on an
52747* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
52748* KIND, either express or implied.  See the License for the
52749* specific language governing permissions and limitations
52750* under the License.
52751*/
52752
52753    var circularLayout = function (ecModel) {
52754        ecModel.eachSeriesByType('graph', function (seriesModel) {
52755            if (seriesModel.get('layout') === 'circular') {
52756                circularLayout$1(seriesModel);
52757            }
52758        });
52759    };
52760
52761    /*
52762* Licensed to the Apache Software Foundation (ASF) under one
52763* or more contributor license agreements.  See the NOTICE file
52764* distributed with this work for additional information
52765* regarding copyright ownership.  The ASF licenses this file
52766* to you under the Apache License, Version 2.0 (the
52767* "License"); you may not use this file except in compliance
52768* with the License.  You may obtain a copy of the License at
52769*
52770*   http://www.apache.org/licenses/LICENSE-2.0
52771*
52772* Unless required by applicable law or agreed to in writing,
52773* software distributed under the License is distributed on an
52774* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
52775* KIND, either express or implied.  See the License for the
52776* specific language governing permissions and limitations
52777* under the License.
52778*/
52779
52780    /*
52781* The layout implementation references to d3.js. The use of
52782* the source code of this file is also subject to the terms
52783* and consitions of its license (BSD-3Clause, see
52784* <echarts/src/licenses/LICENSE-d3>).
52785*/
52786
52787    var scaleAndAdd$2 = scaleAndAdd;
52788
52789// function adjacentNode(n, e) {
52790//     return e.n1 === n ? e.n2 : e.n1;
52791// }
52792
52793    function forceLayout$1(nodes, edges, opts) {
52794        var rect = opts.rect;
52795        var width = rect.width;
52796        var height = rect.height;
52797        var center = [rect.x + width / 2, rect.y + height / 2];
52798        // var scale = opts.scale || 1;
52799        var gravity = opts.gravity == null ? 0.1 : opts.gravity;
52800
52801        // for (var i = 0; i < edges.length; i++) {
52802        //     var e = edges[i];
52803        //     var n1 = e.n1;
52804        //     var n2 = e.n2;
52805        //     n1.edges = n1.edges || [];
52806        //     n2.edges = n2.edges || [];
52807        //     n1.edges.push(e);
52808        //     n2.edges.push(e);
52809        // }
52810        // Init position
52811        for (var i = 0; i < nodes.length; i++) {
52812            var n = nodes[i];
52813            if (!n.p) {
52814                // Use the position from first adjecent node with defined position
52815                // Or use a random position
52816                // From d3
52817                // if (n.edges) {
52818                //     var j = -1;
52819                //     while (++j < n.edges.length) {
52820                //         var e = n.edges[j];
52821                //         var other = adjacentNode(n, e);
52822                //         if (other.p) {
52823                //             n.p = vec2.clone(other.p);
52824                //             break;
52825                //         }
52826                //     }
52827                // }
52828                // if (!n.p) {
52829                n.p = create(
52830                    width * (Math.random() - 0.5) + center[0],
52831                    height * (Math.random() - 0.5) + center[1]
52832                );
52833                // }
52834            }
52835            n.pp = clone$1(n.p);
52836            n.edges = null;
52837        }
52838
52839        // Formula in 'Graph Drawing by Force-directed Placement'
52840        // var k = scale * Math.sqrt(width * height / nodes.length);
52841        // var k2 = k * k;
52842
52843        var friction = 0.6;
52844
52845        return {
52846            warmUp: function () {
52847                friction = 0.5;
52848            },
52849
52850            setFixed: function (idx) {
52851                nodes[idx].fixed = true;
52852            },
52853
52854            setUnfixed: function (idx) {
52855                nodes[idx].fixed = false;
52856            },
52857
52858            step: function (cb) {
52859                var v12 = [];
52860                var nLen = nodes.length;
52861                for (var i = 0; i < edges.length; i++) {
52862                    var e = edges[i];
52863                    var n1 = e.n1;
52864                    var n2 = e.n2;
52865
52866                    sub(v12, n2.p, n1.p);
52867                    var d = len(v12) - e.d;
52868                    var w = n2.w / (n1.w + n2.w);
52869
52870                    if (isNaN(w)) {
52871                        w = 0;
52872                    }
52873
52874                    normalize(v12, v12);
52875
52876                    !n1.fixed && scaleAndAdd$2(n1.p, n1.p, v12, w * d * friction);
52877                    !n2.fixed && scaleAndAdd$2(n2.p, n2.p, v12, -(1 - w) * d * friction);
52878                }
52879                // Gravity
52880                for (var i = 0; i < nLen; i++) {
52881                    var n = nodes[i];
52882                    if (!n.fixed) {
52883                        sub(v12, center, n.p);
52884                        // var d = vec2.len(v12);
52885                        // vec2.scale(v12, v12, 1 / d);
52886                        // var gravityFactor = gravity;
52887                        scaleAndAdd$2(n.p, n.p, v12, gravity * friction);
52888                    }
52889                }
52890
52891                // Repulsive
52892                // PENDING
52893                for (var i = 0; i < nLen; i++) {
52894                    var n1 = nodes[i];
52895                    for (var j = i + 1; j < nLen; j++) {
52896                        var n2 = nodes[j];
52897                        sub(v12, n2.p, n1.p);
52898                        var d = len(v12);
52899                        if (d === 0) {
52900                            // Random repulse
52901                            set(v12, Math.random() - 0.5, Math.random() - 0.5);
52902                            d = 1;
52903                        }
52904                        var repFact = (n1.rep + n2.rep) / d / d;
52905                        !n1.fixed && scaleAndAdd$2(n1.pp, n1.pp, v12, repFact);
52906                        !n2.fixed && scaleAndAdd$2(n2.pp, n2.pp, v12, -repFact);
52907                    }
52908                }
52909                var v = [];
52910                for (var i = 0; i < nLen; i++) {
52911                    var n = nodes[i];
52912                    if (!n.fixed) {
52913                        sub(v, n.p, n.pp);
52914                        scaleAndAdd$2(n.p, n.p, v, friction);
52915                        copy(n.pp, n.p);
52916                    }
52917                }
52918
52919                friction = friction * 0.992;
52920
52921                cb && cb(nodes, edges, friction < 0.01);
52922            }
52923        };
52924    }
52925
52926    /*
52927* Licensed to the Apache Software Foundation (ASF) under one
52928* or more contributor license agreements.  See the NOTICE file
52929* distributed with this work for additional information
52930* regarding copyright ownership.  The ASF licenses this file
52931* to you under the Apache License, Version 2.0 (the
52932* "License");
52932 you may not use this file except in compliance
52933* with the License.  You may obtain a copy of the License at
52934*
52935*   http://www.apache.org/licenses/LICENSE-2.0
52936*
52937* Unless required by applicable law or agreed to in writing,
52938* software distributed under the License is distributed on an
52939* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
52940* KIND, either express or implied.  See the License for the
52941* specific language governing permissions and limitations
52942* under the License.
52943*/
52944
52945    var forceLayout = function (ecModel) {
52946        ecModel.eachSeriesByType('graph', function (graphSeries) {
52947            var coordSys = graphSeries.coordinateSystem;
52948            if (coordSys && coordSys.type !== 'view') {
52949                return;
52950            }
52951            if (graphSeries.get('layout') === 'force') {
52952                var preservedPoints = graphSeries.preservedPoints || {};
52953                var graph = graphSeries.getGraph();
52954                var nodeData = graph.data;
52955                var edgeData = graph.edgeData;
52956                var forceModel = graphSeries.getModel('force');
52957                var initLayout = forceModel.get('initLayout');
52958                if (graphSeries.preservedPoints) {
52959                    nodeData.each(function (idx) {
52960                        var id = nodeData.getId(idx);
52961                        nodeData.setItemLayout(idx, preservedPoints[id] || [NaN, NaN]);
52962                    });
52963                }
52964                else if (!initLayout || initLayout === 'none') {
52965                    simpleLayout$1(graphSeries);
52966                }
52967                else if (initLayout === 'circular') {
52968                    circularLayout$1(graphSeries);
52969                }
52970
52971                var nodeDataExtent = nodeData.getDataExtent('value');
52972                var edgeDataExtent = edgeData.getDataExtent('value');
52973                // var edgeDataExtent = edgeData.getDataExtent('value');
52974                var repulsion = forceModel.get('repulsion');
52975                var edgeLength = forceModel.get('edgeLength');
52976                if (!isArray(repulsion)) {
52977                    repulsion = [repulsion, repulsion];
52978                }
52979                if (!isArray(edgeLength)) {
52980                    edgeLength = [edgeLength, edgeLength];
52981                }
52982                // Larger value has smaller length
52983                edgeLength = [edgeLength[1], edgeLength[0]];
52984
52985                var nodes = nodeData.mapArray('value', function (value, idx) {
52986                    var point = nodeData.getItemLayout(idx);
52987                    var rep = linearMap(value, nodeDataExtent, repulsion);
52988                    if (isNaN(rep)) {
52989                        rep = (repulsion[0] + repulsion[1]) / 2;
52990                    }
52991                    return {
52992                        w: rep,
52993                        rep: rep,
52994                        fixed: nodeData.getItemModel(idx).get('fixed'),
52995                        p: (!point || isNaN(point[0]) || isNaN(point[1])) ? null : point
52996                    };
52997                });
52998                var edges = edgeData.mapArray('value', function (value, idx) {
52999                    var edge = graph.getEdgeByIndex(idx);
53000                    var d = linearMap(value, edgeDataExtent, edgeLength);
53001                    if (isNaN(d)) {
53002                        d = (edgeLength[0] + edgeLength[1]) / 2;
53003                    }
53004                    return {
53005                        n1: nodes[edge.node1.dataIndex],
53006                        n2: nodes[edge.node2.dataIndex],
53007                        d: d,
53008                        curveness: edge.getModel().get('lineStyle.curveness') || 0
53009                    };
53010                });
53011
53012                var coordSys = graphSeries.coordinateSystem;
53013                var rect = coordSys.getBoundingRect();
53014                var forceInstance = forceLayout$1(nodes, edges, {
53015                    rect: rect,
53016                    gravity: forceModel.get('gravity')
53017                });
53018                var oldStep = forceInstance.step;
53019                forceInstance.step = function (cb) {
53020                    for (var i = 0, l = nodes.length; i < l; i++) {
53021                        if (nodes[i].fixed) {
53022                            // Write back to layout instance
53023                            copy(nodes[i].p, graph.getNodeByIndex(i).getLayout());
53024                        }
53025                    }
53026                    oldStep(function (nodes, edges, stopped) {
53027                        for (var i = 0, l = nodes.length; i < l; i++) {
53028                            if (!nodes[i].fixed) {
53029                                graph.getNodeByIndex(i).setLayout(nodes[i].p);
53030                            }
53031                            preservedPoints[nodeData.getId(i)] = nodes[i].p;
53032                        }
53033                        for (var i = 0, l = edges.length; i < l; i++) {
53034                            var e = edges[i];
53035                            var edge = graph.getEdgeByIndex(i);
53036                            var p1 = e.n1.p;
53037                            var p2 = e.n2.p;
53038                            var points = edge.getLayout();
53039                            points = points ? points.slice() : [];
53040                            points[0] = points[0] || [];
53041                            points[1] = points[1] || [];
53042                            copy(points[0], p1);
53043                            copy(points[1], p2);
53044                            if (+e.curveness) {
53045                                points[2] = [
53046                                    (p1[0] + p2[0]) / 2 - (p1[1] - p2[1]) * e.curveness,
53047                                    (p1[1] + p2[1]) / 2 - (p2[0] - p1[0]) * e.curveness
53048                                ];
53049                            }
53050                            edge.setLayout(points);
53051                        }
53052                        // Update layout
53053
53054                        cb && cb(stopped);
53055                    });
53056                };
53057                graphSeries.forceLayout = forceInstance;
53058                graphSeries.preservedPoints = preservedPoints;
53059
53060                // Step to get the layout
53061                forceInstance.step();
53062            }
53063            else {
53064                // Remove prev injected forceLayout instance
53065                graphSeries.forceLayout = null;
53066            }
53067        });
53068    };
53069
53070    /*
53071* Licensed to the Apache Software Foundation (ASF) under one
53072* or more contributor license agreements.  See the NOTICE file
53073* distributed with this work for additional information
53074* regarding copyright ownership.  The ASF licenses this file
53075* to you under the Apache License, Version 2.0 (the
53076* "License");
53076 you may not use this file except in compliance
53077* with the License.  You may obtain a copy of the License at
53078*
53079*   http://www.apache.org/licenses/LICENSE-2.0
53080*
53081* Unless required by applicable law or agreed to in writing,
53082* software distributed under the License is distributed on an
53083* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
53084* KIND, either express or implied.  See the License for the
53085* specific language governing permissions and limitations
53086* under the License.
53087*/
53088
53089// FIXME Where to create the simple view coordinate system
53090    function getViewRect$1(seriesModel, api, aspect) {
53091        var option = seriesModel.getBoxLayoutParams();
53092        option.aspect = aspect;
53093        return getLayoutRect(option, {
53094            width: api.getWidth(),
53095            height: api.getHeight()
53096        });
53097    }
53098
53099    var createView = function (ecModel, api) {
53100        var viewList = [];
53101        ecModel.eachSeriesByType('graph', function (seriesModel) {
53102            var coordSysType = seriesModel.get('coordinateSystem');
53103            if (!coordSysType || coordSysType === 'view') {
53104
53105                var data = seriesModel.getData();
53106                var positions = data.mapArray(function (idx) {
53107                    var itemModel = data.getItemModel(idx);
53108                    return [+itemModel.get('x'), +itemModel.get('y')];
53109                });
53110
53111                var min = [];
53112                var max = [];
53113
53114                fromPoints(positions, min, max);
53115
53116                // If width or height is 0
53117                if (max[0] - min[0] === 0) {
53118                    max[0] += 1;
53119                    min[0] -= 1;
53120                }
53121                if (max[1] - min[1] === 0) {
53122                    max[1] += 1;
53123                    min[1] -= 1;
53124                }
53125                var aspect = (max[0] - min[0]) / (max[1] - min[1]);
53126                // FIXME If get view rect after data processed?
53127                var viewRect = getViewRect$1(seriesModel, api, aspect);
53128                // Position may be NaN, use view rect instead
53129                if (isNaN(aspect)) {
53130                    min = [viewRect.x, viewRect.y];
53131                    max = [viewRect.x + viewRect.width, viewRect.y + viewRect.height];
53132                }
53133
53134                var bbWidth = max[0] - min[0];
53135                var bbHeight = max[1] - min[1];
53136
53137                var viewWidth = viewRect.width;
53138                var viewHeight = viewRect.height;
53139
53140                var viewCoordSys = seriesModel.coordinateSystem = new View();
53141                viewCoordSys.zoomLimit = seriesModel.get('scaleLimit');
53142
53143                viewCoordSys.setBoundingRect(
53144                    min[0], min[1], bbWidth, bbHeight
53145                );
53146                viewCoordSys.setViewRect(
53147                    viewRect.x, viewRect.y, viewWidth, viewHeight
53148                );
53149
53150                // Update roam info
53151                viewCoordSys.setCenter(seriesModel.get('center'));
53152                viewCoordSys.setZoom(seriesModel.get('zoom'));
53153
53154                viewList.push(viewCoordSys);
53155            }
53156        });
53157
53158        return viewList;
53159    };
53160
53161    /*
53162* Licensed to the Apache Software Foundation (ASF) under one
53163* or more contributor license agreements.  See the NOTICE file
53164* distributed with this work for additional information
53165* regarding copyright ownership.  The ASF licenses this file
53166* to you under the Apache License, Version 2.0 (the
53167* "License"); you may not use this file except in compliance
53168* with the License.  You may obtain a copy of the License at
53169*
53170*   http://www.apache.org/licenses/LICENSE-2.0
53171*
53172* Unless required by applicable law or agreed to in writing,
53173* software distributed under the License is distributed on an
53174* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
53175* KIND, either express or implied.  See the License for the
53176* specific language governing permissions and limitations
53177* under the License.
53178*/
53179
53180    registerProcessor(categoryFilter);
53181
53182    registerVisual(visualSymbol('graph', 'circle', null));
53183    registerVisual(categoryVisual);
53184    registerVisual(edgeVisual);
53185
53186    registerLayout(simpleLayout);
53187    registerLayout(circularLayout);
53188    registerLayout(forceLayout);
53189
53190// Graph view coordinate system
53191    registerCoordinateSystem('graphView', {
53192        create: createView
53193    });
53194
53195    /*
53196* Licensed to the Apache Software Foundation (ASF) under one
53197* or more contributor license agreements.  See the NOTICE file
53198* distributed with this work for additional information
53199* regarding copyright ownership.  The ASF licenses this file
53200* to you under the Apache License, Version 2.0 (the
53201* "License"); you may not use this file except in compliance
53202* with the License.  You may obtain a copy of the License at
53203*
53204*   http://www.apache.org/licenses/LICENSE-2.0
53205*
53206* Unless required by applicable law or agreed to in writing,
53207* software distributed under the License is distributed on an
53208* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
53209* KIND, either express or implied.  See the License for the
53210* specific language governing permissions and limitations
53211* under the License.
53212*/
53213
53214    var GaugeSeries = SeriesModel.extend({
53215
53216        type: 'series.gauge',
53217
53218        getInitialData: function (option, ecModel) {
53219            var dataOpt = option.data || [];
53220            if (!isArray(dataOpt)) {
53221                dataOpt = [dataOpt];
53222            }
53223            option.data = dataOpt;
53224            return createListSimply(this, ['value']);
53225        },
53226
53227        defaultOption: {
53228            zlevel: 0,
53229            z: 2,
53230            // 默认全局居中
53231            center: ['50%', '50%'],
53232            legendHoverLink: true,
53233            radius: '75%',
53234            startAngle: 225,
53235            endAngle: -45,
53236            clockwise: true,
53237            // 最小值
53238            min: 0,
53239            // 最大值
53240            max: 100,
53241            // 分割段数,默认为10
53242            splitNumber: 10,
53243            // åæ ‡è½´çº¿
53244            axisLine: {
53245                // 默认显示,属性show控制显示与否
53246                show: true,
53247                lineStyle: {       // 属性lineStyleæŽ§åˆ¶çº¿æ¡æ ·å¼
53248                    color: [[0.2, '#91c7ae'], [0.8, '#63869e'], [1, '#c23531']],
53249                    width: 30
53250                }
53251            },
53252            // 分隔线
53253            splitLine: {
53254                // 默认显示,属性show控制显示与否
53255                show: true,
53256                // 属性length控制线长
53257                length: 30,
53258                // 属性lineStyle(详见lineStyleï¼‰æŽ§åˆ¶çº¿æ¡æ ·å¼
53259                lineStyle: {
53260                    color: '#eee',
53261                    width: 2,
53262                    type: 'solid'
53263                }
53264            },
53265            // åæ ‡è½´å°æ ‡è®°
53266            axisTick: {
53267                // 属性show控制显示与否,默认不显示
53268                show: true,
53269                // 每份split细分多少段
53270                splitNumber: 5,
53271                // 属性length控制线长
53272                length: 8,
53273                // 属性lineStyleæŽ§åˆ¶çº¿æ¡æ ·å¼
53274                lineStyle: {
53275                    color: '#eee',
53276                    width: 1,
53277                    type: 'solid'
53278                }
53279            },
53280            axisLabel: {
53281                show: true,
53282                distance: 5,
53283                // formatter: null,
53284                color: 'auto'
53285            },
53286            pointer: {
53287                show: true,
53288                length: '80%',
53289                width: 8
53290            },
53291            itemStyle: {
53292                color: 'auto'
53293            },
53294            title: {
53295                show: true,
53296                // x, y,单位px
53297                offsetCenter: [0, '-40%'],
53298                // å…¶ä½™å±žæ€§é»˜è®¤ä½¿ç”¨å…¨å±€æ–‡æœ¬æ ·å¼ï¼Œè¯¦è§TEXTSTYLE
53299                color: '#333',
53300                fontSize: 15
53301            },
53302            detail: {
53303                show: true,
53304                backgroundColor: 'rgba(0,0,0,0)',
53305                borderWidth: 0,
53306                borderColor: '#ccc',
53307                width: 100,
53308                height: null, // self-adaption
53309                padding: [5, 10],
53310                // x, y,单位px
53311                offsetCenter: [0, '40%'],
53312                // formatter: null,
53313                // å…¶ä½™å±žæ€§é»˜è®¤ä½¿ç”¨å…¨å±€æ–‡æœ¬æ ·å¼ï¼Œè¯¦è§TEXTSTYLE
53314                color: 'auto',
53315                fontSize: 30
53316            }
53317        }
53318    });
53319
53320    /*
53321* Licensed to the Apache Software Foundation (ASF) under one
53322* or more contributor license agreements.  See the NOTICE file
53323* distributed with this work for additional information
53324* regarding copyright ownership.  The ASF licenses this file
53325* to you under the Apache License, Version 2.0 (the
53326* "License"); you may not use this file except in compliance
53327* with the License.  You may obtain a copy of the License at
53328*
53329*   http://www.apache.org/licenses/LICENSE-2.0
53330*
53331* Unless required by applicable law or agreed to in writing,
53332* software distributed under the License is distributed on an
53333* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
53334* KIND, either express or implied.  See the License for the
53335* specific language governing permissions and limitations
53336* under the License.
53337*/
53338
53339    var PointerPath = Path.extend({
53340
53341        type: 'echartsGaugePointer',
53342
53343        shape: {
53344            angle: 0,
53345
53346            width: 10,
53347
53348            r: 10,
53349
53350            x: 0,
53351
53352            y: 0
53353        },
53354
53355        buildPath: function (ctx, shape) {
53356            var mathCos = Math.cos;
53357            var mathSin = Math.sin;
53358
53359            var r = shape.r;
53360            var width = shape.width;
53361            var angle = shape.angle;
53362            var x = shape.x - mathCos(angle) * width * (width >= r / 3 ? 1 : 2);
53363            var y = shape.y - mathSin(angle) * width * (width >= r / 3 ? 1 : 2);
53364
53365            angle = shape.angle - Math.PI / 2;
53366            ctx.moveTo(x, y);
53367            ctx.lineTo(
53368                shape.x + mathCos(angle) * width,
53369                shape.y + mathSin(angle) * width
53370            );
53371            ctx.lineTo(
53372                shape.x + mathCos(shape.angle) * r,
53373                shape.y + mathSin(shape.angle) * r
53374            );
53375            ctx.lineTo(
53376                shape.x - mathCos(angle) * width,
53377                shape.y - mathSin(angle) * width
53378            );
53379            ctx.lineTo(x, y);
53380            return;
53381        }
53382    });
53383
53384    /*
53385* Licensed to the Apache Software Foundation (ASF) under one
53386* or more contributor license agreements.  See the NOTICE file
53387* distributed with this work for additional information
53388* regarding copyright ownership.  The ASF licenses this file
53389* to you under the Apache License, Version 2.0 (the
53390* "License"); you may not use this file except in compliance
53391* with the License.  You may obtain a copy of the License at
53392*
53393*   http://www.apache.org/licenses/LICENSE-2.0
53394*
53395* Unless required by applicable law or agreed to in writing,
53396* software distributed under the License is distributed on an
53397* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
53398* KIND, either express or implied.  See the License for the
53399* specific language governing permissions and limitations
53400* under the License.
53401*/
53402
53403    function parsePosition(seriesModel, api) {
53404        var center = seriesModel.get('center');
53405        var width = api.getWidth();
53406        var height = api.getHeight();
53407        var size = Math.min(width, height);
53408        var cx = parsePercent$1(center[0], api.getWidth());
53409        var cy = parsePercent$1(center[1], api.getHeight());
53410        var r = parsePercent$1(seriesModel.get('radius'), size / 2);
53411
53412        return {
53413            cx: cx,
53414            cy: cy,
53415            r: r
53416        };
53417    }
53418
53419    function formatLabel(label, labelFormatter) {
53420        if (labelFormatter) {
53421            if (typeof labelFormatter === 'string') {
53422                label = labelFormatter.replace('{value}', label != null ? label : '');
53423            }
53424            else if (typeof labelFormatter === 'function') {
53425                label = labelFormatter(label);
53426            }
53427        }
53428
53429        return label;
53430    }
53431
53432    var PI2$5 = Math.PI * 2;
53433
53434    var GaugeView = Chart.extend({
53435
53436        type: 'gauge',
53437
53438        render: function (seriesModel, ecModel, api) {
53439
53440            this.group.removeAll();
53441
53442            var colorList = seriesModel.get('axisLine.lineStyle.color');
53443            var posInfo = parsePosition(seriesModel, api);
53444
53445            this._renderMain(
53446                seriesModel, ecModel, api, colorList, posInfo
53447            );
53448        },
53449
53450        dispose: function () {},
53451
53452        _renderMain: function (seriesModel, ecModel, api, colorList, posInfo) {
53453            var group = this.group;
53454
53455            var axisLineModel = seriesModel.getModel('axisLine');
53456            var lineStyleModel = axisLineModel.getModel('lineStyle');
53457
53458            var clockwise = seriesModel.get('clockwise');
53459            var startAngle = -seriesModel.get('startAngle') / 180 * Math.PI;
53460            var endAngle = -seriesModel.get('endAngle') / 180 * Math.PI;
53461
53462            var angleRangeSpan = (endAngle - startAngle) % PI2$5;
53463
53464            var prevEndAngle = startAngle;
53465            var axisLineWidth = lineStyleModel.get('width');
53466
53467            for (var i = 0; i < colorList.length; i++) {
53468                // Clamp
53469                var percent = Math.min(Math.max(colorList[i][0], 0), 1);
53470                var endAngle = startAngle + angleRangeSpan * percent;
53471                var sector = new Sector({
53472                    shape: {
53473                        startAngle: prevEndAngle,
53474                        endAngle: endAngle,
53475                        cx: posInfo.cx,
53476                        cy: posInfo.cy,
53477                        clockwise: clockwise,
53478                        r0: posInfo.r - axisLineWidth,
53479                        r: posInfo.r
53480                    },
53481                    silent: true
53482                });
53483
53484                sector.setStyle({
53485                    fill: colorList[i][1]
53486                });
53487
53488                sector.setStyle(lineStyleModel.getLineStyle(
53489                    // Because we use sector to simulate arc
53490                    // so the properties for stroking are useless
53491                    ['color', 'borderWidth', 'borderColor']
53492                ));
53493
53494                group.add(sector);
53495
53496                prevEndAngle = endAngle;
53497            }
53498
53499            var getColor = function (percent) {
53500                // Less than 0
53501                if (percent <= 0) {
53502                    return colorList[0][1];
53503                }
53504                for (var i = 0; i < colorList.length; i++) {
53505                    if (colorList[i][0] >= percent
53506                        && (i === 0 ? 0 : colorList[i - 1][0]) < percent
53507                    ) {
53508                        return colorList[i][1];
53509                    }
53510                }
53511                // More than 1
53512                return colorList[i - 1][1];
53513            };
53514
53515            if (!clockwise) {
53516                var tmp = startAngle;
53517                startAngle = endAngle;
53518                endAngle = tmp;
53519            }
53520
53521            this._renderTicks(
53522                seriesModel, ecModel, api, getColor, posInfo,
53523                startAngle, endAngle, clockwise
53524            );
53525
53526            this._renderPointer(
53527                seriesModel, ecModel, api, getColor, posInfo,
53528                startAngle, endAngle, clockwise
53529            );
53530
53531            this._renderTitle(
53532                seriesModel, ecModel, api, getColor, posInfo
53533            );
53534            this._renderDetail(
53535                seriesModel, ecModel, api, getColor, posInfo
53536            );
53537        },
53538
53539        _renderTicks: function (
53540            seriesModel, ecModel, api, getColor, posInfo,
53541            startAngle, endAngle, clockwise
53542        ) {
53543            var group = this.group;
53544            var cx = posInfo.cx;
53545            var cy = posInfo.cy;
53546            var r = posInfo.r;
53547
53548            var minVal = +seriesModel.get('min');
53549            var maxVal = +seriesModel.get('max');
53550
53551            var splitLineModel = seriesModel.getModel('splitLine');
53552            var tickModel = seriesModel.getModel('axisTick');
53553            var labelModel = seriesModel.getModel('axisLabel');
53554
53555            var splitNumber = seriesModel.get('splitNumber');
53556            var subSplitNumber = tickModel.get('splitNumber');
53557
53558            var splitLineLen = parsePercent$1(
53559                splitLineModel.get('length'), r
53560            );
53561            var tickLen = parsePercent$1(
53562                tickModel.get('length'), r
53563            );
53564
53565            var angle = startAngle;
53566            var step = (endAngle - startAngle) / splitNumber;
53567            var subStep = step / subSplitNumber;
53568
53569            var splitLineStyle = splitLineModel.getModel('lineStyle').getLineStyle();
53570            var tickLineStyle = tickModel.getModel('lineStyle').getLineStyle();
53571
53572            for (var i = 0; i <= splitNumber; i++) {
53573                var unitX = Math.cos(angle);
53574                var unitY = Math.sin(angle);
53575                // Split line
53576                if (splitLineModel.get('show')) {
53577                    var splitLine = new Line({
53578                        shape: {
53579                            x1: unitX * r + cx,
53580                            y1: unitY * r + cy,
53581                            x2: unitX * (r - splitLineLen) + cx,
53582                            y2: unitY * (r - splitLineLen) + cy
53583                        },
53584                        style: splitLineStyle,
53585                        silent: true
53586                    });
53587                    if (splitLineStyle.stroke === 'auto') {
53588                        splitLine.setStyle({
53589                            stroke: getColor(i / splitNumber)
53590                        });
53591                    }
53592
53593                    group.add(splitLine);
53594                }
53595
53596                // Label
53597                if (labelModel.get('show')) {
53598                    var label = formatLabel(
53599                        round$1(i / splitNumber * (maxVal - minVal) + minVal),
53600                        labelModel.get('formatter')
53601                    );
53602                    var distance = labelModel.get('distance');
53603                    var autoColor = getColor(i / splitNumber);
53604
53605                    group.add(new Text({
53606                        style: setTextStyle({}, labelModel, {
53607                            text: label,
53608                            x: unitX * (r - splitLineLen - distance) + cx,
53609                            y: unitY * (r - splitLineLen - distance) + cy,
53610                            textVerticalAlign: unitY < -0.4 ? 'top' : (unitY > 0.4 ? 'bottom' : 'middle'),
53611                            textAlign: unitX < -0.4 ? 'left' : (unitX > 0.4 ? 'right' : 'center')
53612                        }, {autoColor: autoColor}),
53613                        silent: true
53614                    }));
53615                }
53616
53617                // Axis tick
53618                if (tickModel.get('show') && i !== splitNumber) {
53619                    for (var j = 0; j <= subSplitNumber; j++) {
53620                        var unitX = Math.cos(angle);
53621                        var unitY = Math.sin(angle);
53622                        var tickLine = new Line({
53623                            shape: {
53624                                x1: unitX * r + cx,
53625                                y1: unitY * r + cy,
53626                                x2: unitX * (r - tickLen) + cx,
53627                                y2: unitY * (r - tickLen) + cy
53628                            },
53629                            silent: true,
53630                            style: tickLineStyle
53631                        });
53632
53633                        if (tickLineStyle.stroke === 'auto') {
53634                            tickLine.setStyle({
53635                                stroke: getColor((i + j / subSplitNumber) / splitNumber)
53636                            });
53637                        }
53638
53639                        group.add(tickLine);
53640                        angle += subStep;
53641                    }
53642                    angle -= subStep;
53643                }
53644                else {
53645                    angle += step;
53646                }
53647            }
53648        },
53649
53650        _renderPointer: function (
53651            seriesModel, ecModel, api, getColor, posInfo,
53652            startAngle, endAngle, clockwise
53653        ) {
53654
53655            var group = this.group;
53656            var oldData = this._data;
53657
53658            if (!seriesModel.get('pointer.show')) {
53659                // Remove old element
53660                oldData && oldData.eachItemGraphicEl(function (el) {
53661                    group.remove(el);
53662                });
53663                return;
53664            }
53665
53666            var valueExtent = [+seriesModel.get('min'), +seriesModel.get('max')];
53667            var angleExtent = [startAngle, endAngle];
53668
53669            var data = seriesModel.getData();
53670            var valueDim = data.mapDimension('value');
53671
53672            data.diff(oldData)
53673                .add(function (idx) {
53674                    var pointer = new PointerPath({
53675                        shape: {
53676                            angle: startAngle
53677                        }
53678                    });
53679
53680                    initProps(pointer, {
53681                        shape: {
53682                            angle: linearMap(data.get(valueDim, idx), valueExtent, angleExtent, true)
53683                        }
53684                    }, seriesModel);
53685
53686                    group.add(pointer);
53687                    data.setItemGraphicEl(idx, pointer);
53688                })
53689                .update(function (newIdx, oldIdx) {
53690                    var pointer = oldData.getItemGraphicEl(oldIdx);
53691
53692                    updateProps(pointer, {
53693                        shape: {
53694                            angle: linearMap(data.get(valueDim, newIdx), valueExtent, angleExtent, true)
53695                        }
53696                    }, seriesModel);
53697
53698                    group.add(pointer);
53699                    data.setItemGraphicEl(newIdx, pointer);
53700                })
53701                .remove(function (idx) {
53702                    var pointer = oldData.getItemGraphicEl(idx);
53703                    group.remove(pointer);
53704                })
53705                .execute();
53706
53707            data.eachItemGraphicEl(function (pointer, idx) {
53708                var itemModel = data.getItemModel(idx);
53709                var pointerModel = itemModel.getModel('pointer');
53710
53711                pointer.setShape({
53712                    x: posInfo.cx,
53713                    y: posInfo.cy,
53714                    width: parsePercent$1(
53715                        pointerModel.get('width'), posInfo.r
53716                    ),
53717                    r: parsePercent$1(pointerModel.get('length'), posInfo.r)
53718                });
53719
53720                pointer.useStyle(itemModel.getModel('itemStyle').getItemStyle());
53721
53722                if (pointer.style.fill === 'auto') {
53723                    pointer.setStyle('fill', getColor(
53724                        linearMap(data.get(valueDim, idx), valueExtent, [0, 1], true)
53725                    ));
53726                }
53727
53728                setHoverStyle(
53729                    pointer, itemModel.getModel('emphasis.itemStyle').getItemStyle()
53730                );
53731            });
53732
53733            this._data = data;
53734        },
53735
53736        _renderTitle: function (
53737            seriesModel, ecModel, api, getColor, posInfo
53738        ) {
53739            var data = seriesModel.getData();
53740            var valueDim = data.mapDimension('value');
53741            var titleModel = seriesModel.getModel('title');
53742            if (titleModel.get('show')) {
53743                var offsetCenter = titleModel.get('offsetCenter');
53744                var x = posInfo.cx + parsePercent$1(offsetCenter[0], posInfo.r);
53745                var y = posInfo.cy + parsePercent$1(offsetCenter[1], posInfo.r);
53746
53747                var minVal = +seriesModel.get('min');
53748                var maxVal = +seriesModel.get('max');
53749                var value = seriesModel.getData().get(valueDim, 0);
53750                var autoColor = getColor(
53751                    linearMap(value, [minVal, maxVal], [0, 1], true)
53752                );
53753
53754                this.group.add(new Text({
53755                    silent: true,
53756                    style: setTextStyle({}, titleModel, {
53757                        x: x,
53758                        y: y,
53759                        // FIXME First data name ?
53760                        text: data.getName(0),
53761                        textAlign: 'center',
53762                        textVerticalAlign: 'middle'
53763                    }, {autoColor: autoColor, forceRich: true})
53764                }));
53765            }
53766        },
53767
53768        _renderDetail: function (
53769            seriesModel, ecModel, api, getColor, posInfo
53770        ) {
53771            var detailModel = seriesModel.getModel('detail');
53772            var minVal = +seriesModel.get('min');
53773            var maxVal = +seriesModel.get('max');
53774            if (detailModel.get('show')) {
53775                var offsetCenter = detailModel.get('offsetCenter');
53776                var x = posInfo.cx + parsePercent$1(offsetCenter[0], posInfo.r);
53777                var y = posInfo.cy + parsePercent$1(offsetCenter[1], posInfo.r);
53778                var width = parsePercent$1(detailModel.get('width'), posInfo.r);
53779                var height = parsePercent$1(detailModel.get('height'), posInfo.r);
53780                var data = seriesModel.getData();
53781                var value = data.get(data.mapDimension('value'), 0);
53782                var autoColor = getColor(
53783                    linearMap(value, [minVal, maxVal], [0, 1], true)
53784                );
53785
53786                this.group.add(new Text({
53787                    silent: true,
53788                    style: setTextStyle({}, detailModel, {
53789                        x: x,
53790                        y: y,
53791                        text: formatLabel(
53792                            // FIXME First data name ?
53793                            value, detailModel.get('formatter')
53794                        ),
53795                        textWidth: isNaN(width) ? null : width,
53796                        textHeight: isNaN(height) ? null : height,
53797                        textAlign: 'center',
53798                        textVerticalAlign: 'middle'
53799                    }, {autoColor: autoColor, forceRich: true})
53800                }));
53801            }
53802        }
53803    });
53804
53805    /*
53806* Licensed to the Apache Software Foundation (ASF) under one
53807* or more contributor license agreements.  See the NOTICE file
53808* distributed with this work for additional information
53809* regarding copyright ownership.  The ASF licenses this file
53810* to you under the Apache License, Version 2.0 (the
53811* "License"); you may not use this file except in compliance
53812* with the License.  You may obtain a copy of the License at
53813*
53814*   http://www.apache.org/licenses/LICENSE-2.0
53815*
53816* Unless required by applicable law or agreed to in writing,
53817* software distributed under the License is distributed on an
53818* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
53819* KIND, either express or implied.  See the License for the
53820* specific language governing permissions and limitations
53821* under the License.
53822*/
53823
53824    /*
53825* Licensed to the Apache Software Foundation (ASF) under one
53826* or more contributor license agreements.  See the NOTICE file
53827* distributed with this work for additional information
53828* regarding copyright ownership.  The ASF licenses this file
53829* to you under the Apache License, Version 2.0 (the
53830* "License"); you may not use this file except in compliance
53831* with the License.  You may obtain a copy of the License at
53832*
53833*   http://www.apache.org/licenses/LICENSE-2.0
53834*
53835* Unless required by applicable law or agreed to in writing,
53836* software distributed under the License is distributed on an
53837* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
53838* KIND, either express or implied.  See the License for the
53839* specific language governing permissions and limitations
53840* under the License.
53841*/
53842
53843    var FunnelSeries = extendSeriesModel({
53844
53845        type: 'series.funnel',
53846
53847        init: function (option) {
53848            FunnelSeries.superApply(this, 'init', arguments);
53849
53850            // Enable legend selection for each data item
53851            // Use a function instead of direct access because data reference may changed
53852            this.legendDataProvider = function () {
53853                return this.getRawData();
53854            };
53855            // Extend labelLine emphasis
53856            this._defaultLabelLine(option);
53857        },
53858
53859        getInitialData: function (option, ecModel) {
53860            return createListSimply(this, ['value']);
53861        },
53862
53863        _defaultLabelLine: function (option) {
53864            // Extend labelLine emphasis
53865            defaultEmphasis(option, 'labelLine', ['show']);
53866
53867            var labelLineNormalOpt = option.labelLine;
53868            var labelLineEmphasisOpt = option.emphasis.labelLine;
53869            // Not show label line if `label.normal.show = false`
53870            labelLineNormalOpt.show = labelLineNormalOpt.show
53871                && option.label.show;
53872            labelLineEmphasisOpt.show = labelLineEmphasisOpt.show
53873                && option.emphasis.label.show;
53874        },
53875
53876        // Overwrite
53877        getDataParams: function (dataIndex) {
53878            var data = this.getData();
53879            var params = FunnelSeries.superCall(this, 'getDataParams', dataIndex);
53880            var valueDim = data.mapDimension('value');
53881            var sum = data.getSum(valueDim);
53882            // Percent is 0 if sum is 0
53883            params.percent = !sum ? 0 : +(data.get(valueDim, dataIndex) / sum * 100).toFixed(2);
53884
53885            params.$vars.push('percent');
53886            return params;
53887        },
53888
53889        defaultOption: {
53890            zlevel: 0,                  // 一级层å
53891            z: 2,                       // 二级层å
53892            legendHoverLink: true,
53893            left: 80,
53894            top: 60,
53895            right: 80,
53896            bottom: 60,
53897            // width: {totalWidth} - left - right,
53898            // height: {totalHeight} - top - bottom,
53899
53900            // 默认取数据最小最大值
53901            // min: 0,
53902            // max: 100,
53903            minSize: '0%',
53904            maxSize: '100%',
53905            sort: 'descending', // 'ascending', 'descending'
53906            gap: 0,
53907            funnelAlign: 'center',
53908            label: {
53909                show: true,
53910                position: 'outer'
53911                // formatter: æ ‡ç­¾æ–‡æœ¬æ ¼å¼å™¨ï¼ŒåŒTooltip.formatter,不支持异步回调
53912            },
53913            labelLine: {
53914                show: true,
53915                length: 20,
53916                lineStyle: {
53917                    // color: 各异,
53918                    width: 1,
53919                    type: 'solid'
53920                }
53921            },
53922            itemStyle: {
53923                // color: 各异,
53924                borderColor: '#fff',
53925                borderWidth: 1
53926            },
53927            emphasis: {
53928                label: {
53929                    show: true
53930                }
53931            }
53932        }
53933    });
53934
53935    /*
53936* Licensed to the Apache Software Foundation (ASF) under one
53937* or more contributor license agreements.  See the NOTICE file
53938* distributed with this work for additional information
53939* regarding copyright ownership.  The ASF licenses this file
53940* to you under the Apache License, Version 2.0 (the
53941* "License"); you may not use this file except in compliance
53942* with the License.  You may obtain a copy of the License at
53943*
53944*   http://www.apache.org/licenses/LICENSE-2.0
53945*
53946* Unless required by applicable law or agreed to in writing,
53947* software distributed under the License is distributed on an
53948* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
53949* KIND, either express or implied.  See the License for the
53950* specific language governing permissions and limitations
53951* under the License.
53952*/
53953
53954    /**
53955     * Piece of pie including Sector, Label, LabelLine
53956     * @constructor
53957     * @extends {module:zrender/graphic/Group}
53958     */
53959    function FunnelPiece(data, idx) {
53960
53961        Group.call(this);
53962
53963        var polygon = new Polygon();
53964        var labelLine = new Polyline();
53965        var text = new Text();
53966        this.add(polygon);
53967        this.add(labelLine);
53968        this.add(text);
53969
53970        this.updateData(data, idx, true);
53971
53972        // Hover to change label and labelLine
53973        function onEmphasis() {
53974            labelLine.ignore = labelLine.hoverIgnore;
53975            text.ignore = text.hoverIgnore;
53976        }
53977        function onNormal() {
53978            labelLine.ignore = labelLine.normalIgnore;
53979            text.ignore = text.normalIgnore;
53980        }
53981        this.on('emphasis', onEmphasis)
53982            .on('normal', onNormal)
53983            .on('mouseover', onEmphasis)
53984            .on('mouseout', onNormal);
53985    }
53986
53987    var funnelPieceProto = FunnelPiece.prototype;
53988
53989    var opacityAccessPath = ['itemStyle', 'opacity'];
53990    funnelPieceProto.updateData = function (data, idx, firstCreate) {
53991
53992        var polygon = this.childAt(0);
53993
53994        var seriesModel = data.hostModel;
53995        var itemModel = data.getItemModel(idx);
53996        var layout = data.getItemLayout(idx);
53997        var opacity = data.getItemModel(idx).get(opacityAccessPath);
53998        opacity = opacity == null ? 1 : opacity;
53999
54000        // Reset style
54001        polygon.useStyle({});
54002
54003        if (firstCreate) {
54004            polygon.setShape({
54005                points: layout.points
54006            });
54007            polygon.setStyle({ opacity : 0 });
54008            initProps(polygon, {
54009                style: {
54010                    opacity: opacity
54011                }
54012            }, seriesModel, idx);
54013        }
54014        else {
54015            updateProps(polygon, {
54016                style: {
54017                    opacity: opacity
54018                },
54019                shape: {
54020                    points: layout.points
54021                }
54022            }, seriesModel, idx);
54023        }
54024
54025        // Update common style
54026        var itemStyleModel = itemModel.getModel('itemStyle');
54027        var visualColor = data.getItemVisual(idx, 'color');
54028
54029        polygon.setStyle(
54030            defaults(
54031                {
54032                    lineJoin: 'round',
54033                    fill: visualColor
54034                },
54035                itemStyleModel.getItemStyle(['opacity'])
54036            )
54037        );
54038        polygon.hoverStyle = itemStyleModel.getModel('emphasis').getItemStyle();
54039
54040        this._updateLabel(data, idx);
54041
54042        setHoverStyle(this);
54043    };
54044
54045    funnelPieceProto._updateLabel = function (data, idx) {
54046
54047        var labelLine = this.childAt(1);
54048        var labelText = this.childAt(2);
54049
54050        var seriesModel = data.hostModel;
54051        var itemModel = data.getItemModel(idx);
54052        var layout = data.getItemLayout(idx);
54053        var labelLayout = layout.label;
54054        var visualColor = data.getItemVisual(idx, 'color');
54055
54056        updateProps(labelLine, {
54057            shape: {
54058                points: labelLayout.linePoints || labelLayout.linePoints
54059            }
54060        }, seriesModel, idx);
54061
54062        updateProps(labelText, {
54063            style: {
54064                x: labelLayout.x,
54065                y: labelLayout.y
54066            }
54067        }, seriesModel, idx);
54068        labelText.attr({
54069            rotation: labelLayout.rotation,
54070            origin: [labelLayout.x, labelLayout.y],
54071            z2: 10
54072        });
54073
54074        var labelModel = itemModel.getModel('label');
54075        var labelHoverModel = itemModel.getModel('emphasis.label');
54076        var labelLineModel = itemModel.getModel('labelLine');
54077        var labelLineHoverModel = itemModel.getModel('emphasis.labelLine');
54078        var visualColor = data.getItemVisual(idx, 'color');
54079
54080        setLabelStyle(
54081            labelText.style, labelText.hoverStyle = {}, labelModel, labelHoverModel,
54082            {
54083                labelFetcher: data.hostModel,
54084                labelDataIndex: idx,
54085                defaultText: data.getName(idx),
54086                autoColor: visualColor,
54087                useInsideStyle: !!labelLayout.inside
54088            },
54089            {
54090                textAlign: labelLayout.textAlign,
54091                textVerticalAlign: labelLayout.verticalAlign
54092            }
54093        );
54094
54095        labelText.ignore = labelText.normalIgnore = !labelModel.get('show');
54096        labelText.hoverIgnore = !labelHoverModel.get('show');
54097
54098        labelLine.ignore = labelLine.normalIgnore = !labelLineModel.get('show');
54099        labelLine.hoverIgnore = !labelLineHoverModel.get('show');
54100
54101        // Default use item visual color
54102        labelLine.setStyle({
54103            stroke: visualColor
54104        });
54105        labelLine.setStyle(labelLineModel.getModel('lineStyle').getLineStyle());
54106
54107        labelLine.hoverStyle = labelLineHoverModel.getModel('lineStyle').getLineStyle();
54108    };
54109
54110    inherits(FunnelPiece, Group);
54111
54112
54113    var FunnelView = Chart.extend({
54114
54115        type: 'funnel',
54116
54117        render: function (seriesModel, ecModel, api) {
54118            var data = seriesModel.getData();
54119            var oldData = this._data;
54120
54121            var group = this.group;
54122
54123            data.diff(oldData)
54124                .add(function (idx) {
54125                    var funnelPiece = new FunnelPiece(data, idx);
54126
54127                    data.setItemGraphicEl(idx, funnelPiece);
54128
54129                    group.add(funnelPiece);
54130                })
54131                .update(function (newIdx, oldIdx) {
54132                    var piePiece = oldData.getItemGraphicEl(oldIdx);
54133
54134                    piePiece.updateData(data, newIdx);
54135
54136                    group.add(piePiece);
54137                    data.setItemGraphicEl(newIdx, piePiece);
54138                })
54139                .remove(function (idx) {
54140                    var piePiece = oldData.getItemGraphicEl(idx);
54141                    group.remove(piePiece);
54142                })
54143                .execute();
54144
54145            this._data = data;
54146        },
54147
54148        remove: function () {
54149            this.group.removeAll();
54150            this._data = null;
54151        },
54152
54153        dispose: function () {}
54154    });
54155
54156    /*
54157* Licensed to the Apache Software Foundation (ASF) under one
54158* or more contributor license agreements.  See the NOTICE file
54159* distributed with this work for additional information
54160* regarding copyright ownership.  The ASF licenses this file
54161* to you under the Apache License, Version 2.0 (the
54162* "License"); you may not use this file except in compliance
54163* with the License.  You may obtain a copy of the License at
54164*
54165*   http://www.apache.org/licenses/LICENSE-2.0
54166*
54167* Unless required by applicable law or agreed to in writing,
54168* software distributed under the License is distributed on an
54169* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
54170* KIND, either express or implied.  See the License for the
54171* specific language governing permissions and limitations
54172* under the License.
54173*/
54174
54175    function getViewRect$2(seriesModel, api) {
54176        return getLayoutRect(
54177            seriesModel.getBoxLayoutParams(), {
54178                width: api.getWidth(),
54179                height: api.getHeight()
54180            }
54181        );
54182    }
54183
54184    function getSortedIndices(data, sort) {
54185        var valueDim = data.mapDimension('value');
54186        var valueArr = data.mapArray(valueDim, function (val) {
54187            return val;
54188        });
54189        var indices = [];
54190        var isAscending = sort === 'ascending';
54191        for (var i = 0, len = data.count(); i < len; i++) {
54192            indices[i] = i;
54193        }
54194
54195        // Add custom sortable function & none sortable opetion by "options.sort"
54196        if (typeof sort === 'function') {
54197            indices.sort(sort);
54198        } else if (sort !== 'none') {
54199            indices.sort(function (a, b) {
54200                return isAscending ? valueArr[a] - valueArr[b] : valueArr[b] - valueArr[a];
54201            });
54202        }
54203        return indices;
54204    }
54205
54206    function labelLayout$1(data) {
54207        data.each(function (idx) {
54208            var itemModel = data.getItemModel(idx);
54209            var labelModel = itemModel.getModel('label');
54210            var labelPosition = labelModel.get('position');
54211
54212            var labelLineModel = itemModel.getModel('labelLine');
54213
54214            var layout = data.getItemLayout(idx);
54215            var points = layout.points;
54216
54217            var isLabelInside = labelPosition === 'inner'
54218                || labelPosition === 'inside' || labelPosition === 'center';
54219
54220            var textAlign;
54221            var textX;
54222            var textY;
54223            var linePoints;
54224
54225            if (isLabelInside) {
54226                textX = (points[0][0] + points[1][0] + points[2][0] + points[3][0]) / 4;
54227                textY = (points[0][1] + points[1][1] + points[2][1] + points[3][1]) / 4;
54228                textAlign = 'center';
54229                linePoints = [
54230                    [textX, textY], [textX, textY]
54231                ];
54232            }
54233            else {
54234                var x1;
54235                var y1;
54236                var x2;
54237                var labelLineLen = labelLineModel.get('length');
54238                if (labelPosition === 'left') {
54239                    // Left side
54240                    x1 = (points[3][0] + points[0][0]) / 2;
54241                    y1 = (points[3][1] + points[0][1]) / 2;
54242                    x2 = x1 - labelLineLen;
54243                    textX = x2 - 5;
54244                    textAlign = 'right';
54245                }
54246                else {
54247                    // Right side
54248                    x1 = (points[1][0] + points[2][0]) / 2;
54249                    y1 = (points[1][1] + points[2][1]) / 2;
54250                    x2 = x1 + labelLineLen;
54251                    textX = x2 + 5;
54252                    textAlign = 'left';
54253                }
54254                var y2 = y1;
54255
54256                linePoints = [[x1, y1], [x2, y2]];
54257                textY = y2;
54258            }
54259
54260            layout.label = {
54261                linePoints: linePoints,
54262                x: textX,
54263                y: textY,
54264                verticalAlign: 'middle',
54265                textAlign: textAlign,
54266                inside: isLabelInside
54267            };
54268        });
54269    }
54270
54271    var funnelLayout = function (ecModel, api, payload) {
54272        ecModel.eachSeriesByType('funnel', function (seriesModel) {
54273            var data = seriesModel.getData();
54274            var valueDim = data.mapDimension('value');
54275            var sort = seriesModel.get('sort');
54276            var viewRect = getViewRect$2(seriesModel, api);
54277            var indices = getSortedIndices(data, sort);
54278
54279            var sizeExtent = [
54280                parsePercent$1(seriesModel.get('minSize'), viewRect.width),
54281                parsePercent$1(seriesModel.get('maxSize'), viewRect.width)
54282            ];
54283            var dataExtent = data.getDataExtent(valueDim);
54284            var min = seriesModel.get('min');
54285            var max = seriesModel.get('max');
54286            if (min == null) {
54287                min = Math.min(dataExtent[0], 0);
54288            }
54289            if (max == null) {
54290                max = dataExtent[1];
54291            }
54292
54293            var funnelAlign = seriesModel.get('funnelAlign');
54294            var gap = seriesModel.get('gap');
54295            var itemHeight = (viewRect.height - gap * (data.count() - 1)) / data.count();
54296
54297            var y = viewRect.y;
54298
54299            var getLinePoints = function (idx, offY) {
54300                // End point index is data.count() and we assign it 0
54301                var val = data.get(valueDim, idx) || 0;
54302                var itemWidth = linearMap(val, [min, max], sizeExtent, true);
54303                var x0;
54304                switch (funnelAlign) {
54305                    case 'left':
54306                        x0 = viewRect.x;
54307                        break;
54308                    case 'center':
54309                        x0 = viewRect.x + (viewRect.width - itemWidth) / 2;
54310                        break;
54311                    case 'right':
54312                        x0 = viewRect.x + viewRect.width - itemWidth;
54313                        break;
54314                }
54315                return [
54316                    [x0, offY],
54317                    [x0 + itemWidth, offY]
54318                ];
54319            };
54320
54321            if (sort === 'ascending') {
54322                // From bottom to top
54323                itemHeight = -itemHeight;
54324                gap = -gap;
54325                y += viewRect.height;
54326                indices = indices.reverse();
54327            }
54328
54329            for (var i = 0; i < indices.length; i++) {
54330                var idx = indices[i];
54331                var nextIdx = indices[i + 1];
54332
54333                var itemModel = data.getItemModel(idx);
54334                var height = itemModel.get('itemStyle.height');
54335                if (height == null) {
54336                    height = itemHeight;
54337                }
54338                else {
54339                    height = parsePercent$1(height, viewRect.height);
54340                    if (sort === 'ascending') {
54341                        height = -height;
54342                    }
54343                }
54344
54345                var start = getLinePoints(idx, y);
54346                var end = getLinePoints(nextIdx, y + height);
54347
54348                y += height + gap;
54349
54350                data.setItemLayout(idx, {
54351                    points: start.concat(end.slice().reverse())
54352                });
54353            }
54354
54355            labelLayout$1(data);
54356        });
54357    };
54358
54359    /*
54360* Licensed to the Apache Software Foundation (ASF) under one
54361* or more contributor license agreements.  See the NOTICE file
54362* distributed with this work for additional information
54363* regarding copyright ownership.  The ASF licenses this file
54364* to you under the Apache License, Version 2.0 (the
54365* "License"); you may not use this file except in compliance
54366* with the License.  You may obtain a copy of the License at
54367*
54368*   http://www.apache.org/licenses/LICENSE-2.0
54369*
54370* Unless required by applicable law or agreed to in writing,
54371* software distributed under the License is distributed on an
54372* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
54373* KIND, either express or implied.  See the License for the
54374* specific language governing permissions and limitations
54375* under the License.
54376*/
54377
54378    registerVisual(dataColor('funnel'));
54379    registerLayout(funnelLayout);
54380    registerProcessor(dataFilter('funnel'));
54381
54382    /*
54383* Licensed to the Apache Software Foundation (ASF) under one
54384* or more contributor license agreements.  See the NOTICE file
54385* distributed with this work for additional information
54386* regarding copyright ownership.  The ASF licenses this file
54387* to you under the Apache License, Version 2.0 (the
54388* "License");
54388 you may not use this file except in compliance
54389* with the License.  You may obtain a copy of the License at
54390*
54391*   http://www.apache.org/licenses/LICENSE-2.0
54392*
54393* Unless required by applicable law or agreed to in writing,
54394* software distributed under the License is distributed on an
54395* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
54396* KIND, either express or implied.  See the License for the
54397* specific language governing permissions and limitations
54398* under the License.
54399*/
54400
54401    var parallelPreprocessor = function (option) {
54402        createParallelIfNeeded(option);
54403        mergeAxisOptionFromParallel(option);
54404    };
54405
54406    /**
54407     * Create a parallel coordinate if not exists.
54408     * @inner
54409     */
54410    function createParallelIfNeeded(option) {
54411        if (option.parallel) {
54412            return;
54413        }
54414
54415        var hasParallelSeries = false;
54416
54417        each$1(option.series, function (seriesOpt) {
54418            if (seriesOpt && seriesOpt.type === 'parallel') {
54419                hasParallelSeries = true;
54420            }
54421        });
54422
54423        if (hasParallelSeries) {
54424            option.parallel = [{}];
54425        }
54426    }
54427
54428    /**
54429     * Merge aixs definition from parallel option (if exists) to axis option.
54430     * @inner
54431     */
54432    function mergeAxisOptionFromParallel(option) {
54433        var axes = normalizeToArray(option.parallelAxis);
54434
54435        each$1(axes, function (axisOption) {
54436            if (!isObject$1(axisOption)) {
54437                return;
54438            }
54439
54440            var parallelIndex = axisOption.parallelIndex || 0;
54441            var parallelOption = normalizeToArray(option.parallel)[parallelIndex];
54442
54443            if (parallelOption && parallelOption.parallelAxisDefault) {
54444                merge(axisOption, parallelOption.parallelAxisDefault, false);
54445            }
54446        });
54447    }
54448
54449    /*
54450* Licensed to the Apache Software Foundation (ASF) under one
54451* or more contributor license agreements.  See the NOTICE file
54452* distributed with this work for additional information
54453* regarding copyright ownership.  The ASF licenses this file
54454* to you under the Apache License, Version 2.0 (the
54455* "License"); you may not use this file except in compliance
54456* with the License.  You may obtain a copy of the License at
54457*
54458*   http://www.apache.org/licenses/LICENSE-2.0
54459*
54460* Unless required by applicable law or agreed to in writing,
54461* software distributed under the License is distributed on an
54462* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
54463* KIND, either express or implied.  See the License for the
54464* specific language governing permissions and limitations
54465* under the License.
54466*/
54467
54468    /**
54469     * @constructor module:echarts/coord/parallel/ParallelAxis
54470     * @extends {module:echarts/coord/Axis}
54471     * @param {string} dim
54472     * @param {*} scale
54473     * @param {Array.<number>} coordExtent
54474     * @param {string} axisType
54475     */
54476    var ParallelAxis = function (dim, scale, coordExtent, axisType, axisIndex) {
54477
54478        Axis.call(this, dim, scale, coordExtent);
54479
54480        /**
54481         * Axis type
54482         *  - 'category'
54483         *  - 'value'
54484         *  - 'time'
54485         *  - 'log'
54486         * @type {string}
54487         */
54488        this.type = axisType || 'value';
54489
54490        /**
54491         * @type {number}
54492         * @readOnly
54493         */
54494        this.axisIndex = axisIndex;
54495    };
54496
54497    ParallelAxis.prototype = {
54498
54499        constructor: ParallelAxis,
54500
54501        /**
54502         * Axis model
54503         * @param {module:echarts/coord/parallel/AxisModel}
54504         */
54505        model: null,
54506
54507        /**
54508         * @override
54509         */
54510        isHorizontal: function () {
54511            return this.coordinateSystem.getModel().get('layout') !== 'horizontal';
54512        }
54513
54514    };
54515
54516    inherits(ParallelAxis, Axis);
54517
54518    /*
54519* Licensed to the Apache Software Foundation (ASF) under one
54520* or more contributor license agreements.  See the NOTICE file
54521* distributed with this work for additional information
54522* regarding copyright ownership.  The ASF licenses this file
54523* to you under the Apache License, Version 2.0 (the
54524* "License");
54524 you may not use this file except in compliance
54525* with the License.  You may obtain a copy of the License at
54526*
54527*   http://www.apache.org/licenses/LICENSE-2.0
54528*
54529* Unless required by applicable law or agreed to in writing,
54530* software distributed under the License is distributed on an
54531* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
54532* KIND, either express or implied.  See the License for the
54533* specific language governing permissions and limitations
54534* under the License.
54535*/
54536
54537    /**
54538     * Calculate slider move result.
54539     * Usage:
54540     * (1) If both handle0 and handle1 are needed to be moved, set minSpan the same as
54541     * maxSpan and the same as `Math.abs(handleEnd[1] - handleEnds[0])`.
54542     * (2) If handle0 is forbidden to cross handle1, set minSpan as `0`.
54543     *
54544     * @param {number} delta Move length.
54545     * @param {Array.<number>} handleEnds handleEnds[0] can be bigger then handleEnds[1].
54546     *              handleEnds will be modified in this method.
54547     * @param {Array.<number>} extent handleEnds is restricted by extent.
54548     *              extent[0] should less or equals than extent[1].
54549     * @param {number|string} handleIndex Can be 'all', means that both move the two handleEnds,
54550     *              where the input minSpan and maxSpan will not work.
54551     * @param {number} [minSpan] The range of dataZoom can not be smaller than that.
54552     *              If not set, handle0 and cross handle1. If set as a non-negative
54553     *              number (including `0`), handles will push each other when reaching
54554     *              the minSpan.
54555     * @param {number} [maxSpan] The range of dataZoom can not be larger than that.
54556     * @return {Array.<number>} The input handleEnds.
54557     */
54558    var sliderMove = function (delta, handleEnds, extent, handleIndex, minSpan, maxSpan) {
54559        // Normalize firstly.
54560        handleEnds[0] = restrict$1(handleEnds[0], extent);
54561        handleEnds[1] = restrict$1(handleEnds[1], extent);
54562
54563        delta = delta || 0;
54564
54565        var extentSpan = extent[1] - extent[0];
54566
54567        // Notice maxSpan and minSpan can be null/undefined.
54568        if (minSpan != null) {
54569            minSpan = restrict$1(minSpan, [0, extentSpan]);
54570        }
54571        if (maxSpan != null) {
54572            maxSpan = Math.max(maxSpan, minSpan != null ? minSpan : 0);
54573        }
54574        if (handleIndex === 'all') {
54575            minSpan = maxSpan = Math.abs(handleEnds[1] - handleEnds[0]);
54576            handleIndex = 0;
54577        }
54578
54579        var originalDistSign = getSpanSign(handleEnds, handleIndex);
54580
54581        handleEnds[handleIndex] += delta;
54582
54583        // Restrict in extent.
54584        var extentMinSpan = minSpan || 0;
54585        var realExtent = extent.slice();
54586        originalDistSign.sign < 0 ? (realExtent[0] += extentMinSpan) : (realExtent[1] -= extentMinSpan);
54587        handleEnds[handleIndex] = restrict$1(handleEnds[handleIndex], realExtent);
54588
54589        // Expand span.
54590        var currDistSign = getSpanSign(handleEnds, handleIndex);
54591        if (minSpan != null && (
54592                currDistSign.sign !== originalDistSign.sign || currDistSign.span < minSpan
54593            )) {
54594            // If minSpan exists, 'cross' is forbinden.
54595            handleEnds[1 - handleIndex] = handleEnds[handleIndex] + originalDistSign.sign * minSpan;
54596        }
54597
54598        // Shrink span.
54599        var currDistSign = getSpanSign(handleEnds, handleIndex);
54600        if (maxSpan != null && currDistSign.span > maxSpan) {
54601            handleEnds[1 - handleIndex] = handleEnds[handleIndex] + currDistSign.sign * maxSpan;
54602        }
54603
54604        return handleEnds;
54605    };
54606
54607    function getSpanSign(handleEnds, handleIndex) {
54608        var dist = handleEnds[handleIndex] - handleEnds[1 - handleIndex];
54609        // If `handleEnds[0] === handleEnds[1]`, always believe that handleEnd[0]
54610        // is at left of handleEnds[1] for non-cross case.
54611        return {span: Math.abs(dist), sign: dist > 0 ? -1 : dist < 0 ? 1 : handleIndex ? -1 : 1};
54612    }
54613
54614    function restrict$1(value, extend) {
54615        return Math.min(extend[1], Math.max(extend[0], value));
54616    }
54617
54618    /*
54619* Licensed to the Apache Software Foundation (ASF) under one
54620* or more contributor license agreements.  See the NOTICE file
54621* distributed with this work for additional information
54622* regarding copyright ownership.  The ASF licenses this file
54623* to you under the Apache License, Version 2.0 (the
54624* "License");
54624 you may not use this file except in compliance
54625* with the License.  You may obtain a copy of the License at
54626*
54627*   http://www.apache.org/licenses/LICENSE-2.0
54628*
54629* Unless required by applicable law or agreed to in writing,
54630* software distributed under the License is distributed on an
54631* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
54632* KIND, either express or implied.  See the License for the
54633* specific language governing permissions and limitations
54634* under the License.
54635*/
54636
54637    /**
54638     * Parallel Coordinates
54639     * <https://en.wikipedia.org/wiki/Parallel_coordinates>
54640     */
54641
54642    var each$11 = each$1;
54643    var mathMin$5 = Math.min;
54644    var mathMax$5 = Math.max;
54645    var mathFloor$2 = Math.floor;
54646    var mathCeil$2 = Math.ceil;
54647    var round$2 = round$1;
54648
54649    var PI$3 = Math.PI;
54650
54651    function Parallel(parallelModel, ecModel, api) {
54652
54653        /**
54654         * key: dimension
54655         * @type {Object.<string, module:echarts/coord/parallel/Axis>}
54656         * @private
54657         */
54658        this._axesMap = createHashMap();
54659
54660        /**
54661         * key: dimension
54662         * value: {position: [], rotation, }
54663         * @type {Object.<string, Object>}
54664         * @private
54665         */
54666        this._axesLayout = {};
54667
54668        /**
54669         * Always follow axis order.
54670         * @type {Array.<string>}
54671         * @readOnly
54672         */
54673        this.dimensions = parallelModel.dimensions;
54674
54675        /**
54676         * @type {module:zrender/core/BoundingRect}
54677         */
54678        this._rect;
54679
54680        /**
54681         * @type {module:echarts/coord/parallel/ParallelModel}
54682         */
54683        this._model = parallelModel;
54684
54685        this._init(parallelModel, ecModel, api);
54686    }
54687
54688    Parallel.prototype = {
54689
54690        type: 'parallel',
54691
54692        constructor: Parallel,
54693
54694        /**
54695         * Initialize cartesian coordinate systems
54696         * @private
54697         */
54698        _init: function (parallelModel, ecModel, api) {
54699
54700            var dimensions = parallelModel.dimensions;
54701            var parallelAxisIndex = parallelModel.parallelAxisIndex;
54702
54703            each$11(dimensions, function (dim, idx) {
54704
54705                var axisIndex = parallelAxisIndex[idx];
54706                var axisModel = ecModel.getComponent('parallelAxis', axisIndex);
54707
54708                var axis = this._axesMap.set(dim, new ParallelAxis(
54709                    dim,
54710                    createScaleByModel(axisModel),
54711                    [0, 0],
54712                    axisModel.get('type'),
54713                    axisIndex
54714                ));
54715
54716                var isCategory = axis.type === 'category';
54717                axis.onBand = isCategory && axisModel.get('boundaryGap');
54718                axis.inverse = axisModel.get('inverse');
54719
54720                // Injection
54721                axisModel.axis = axis;
54722                axis.model = axisModel;
54723                axis.coordinateSystem = axisModel.coordinateSystem = this;
54724
54725            }, this);
54726        },
54727
54728        /**
54729         * Update axis scale after data processed
54730         * @param  {module:echarts/model/Global} ecModel
54731         * @param  {module:echarts/ExtensionAPI} api
54732         */
54733        update: function (ecModel, api) {
54734            this._updateAxesFromSeries(this._model, ecModel);
54735        },
54736
54737        /**
54738         * @override
54739         */
54740        containPoint: function (point) {
54741            var layoutInfo = this._makeLayoutInfo();
54742            var axisBase = layoutInfo.axisBase;
54743            var layoutBase = layoutInfo.layoutBase;
54744            var pixelDimIndex = layoutInfo.pixelDimIndex;
54745            var pAxis = point[1 - pixelDimIndex];
54746            var pLayout = point[pixelDimIndex];
54747
54748            return pAxis >= axisBase
54749                && pAxis <= axisBase + layoutInfo.axisLength
54750                && pLayout >= layoutBase
54751                && pLayout <= layoutBase + layoutInfo.layoutLength;
54752        },
54753
54754        getModel: function () {
54755            return this._model;
54756        },
54757
54758        /**
54759         * Update properties from series
54760         * @private
54761         */
54762        _updateAxesFromSeries: function (parallelModel, ecModel) {
54763            ecModel.eachSeries(function (seriesModel) {
54764
vendor: 7,615 bytes, lines 54765-54960
54765                if (!parallelModel.contains(seriesModel, ecModel)) {
54766                    return;
54767                }
54768
54769                var data = seriesModel.getData();
54770
54771                each$11(this.dimensions, function (dim) {
54772                    var axis = this._axesMap.get(dim);
54773                    axis.scale.unionExtentFromData(data, data.mapDimension(dim));
54774                    niceScaleExtent(axis.scale, axis.model);
54775                }, this);
54776            }, this);
54777        },
54778
54779        /**
54780         * Resize the parallel coordinate system.
54781         * @param {module:echarts/coord/parallel/ParallelModel} parallelModel
54782         * @param {module:echarts/ExtensionAPI} api
54783         */
54784        resize: function (parallelModel, api) {
54785            this._rect = getLayoutRect(
54786                parallelModel.getBoxLayoutParams(),
54787                {
54788                    width: api.getWidth(),
54789                    height: api.getHeight()
54790                }
54791            );
54792
54793            this._layoutAxes();
54794        },
54795
54796        /**
54797         * @return {module:zrender/core/BoundingRect}
54798         */
54799        getRect: function () {
54800            return this._rect;
54801        },
54802
54803        /**
54804         * @private
54805         */
54806        _makeLayoutInfo: function () {
54807            var parallelModel = this._model;
54808            var rect = this._rect;
54809            var xy = ['x', 'y'];
54810            var wh = ['width', 'height'];
54811            var layout = parallelModel.get('layout');
54812            var pixelDimIndex = layout === 'horizontal' ? 0 : 1;
54813            var layoutLength = rect[wh[pixelDimIndex]];
54814            var layoutExtent = [0, layoutLength];
54815            var axisCount = this.dimensions.length;
54816
54817            var axisExpandWidth = restrict(parallelModel.get('axisExpandWidth'), layoutExtent);
54818            var axisExpandCount = restrict(parallelModel.get('axisExpandCount') || 0, [0, axisCount]);
54819            var axisExpandable = parallelModel.get('axisExpandable')
54820                && axisCount > 3
54821                && axisCount > axisExpandCount
54822                && axisExpandCount > 1
54823                && axisExpandWidth > 0
54824                && layoutLength > 0;
54825
54826            // `axisExpandWindow` is According to the coordinates of [0, axisExpandLength],
54827            // for sake of consider the case that axisCollapseWidth is 0 (when screen is narrow),
54828            // where collapsed axes should be overlapped.
54829            var axisExpandWindow = parallelModel.get('axisExpandWindow');
54830            var winSize;
54831            if (!axisExpandWindow) {
54832                winSize = restrict(axisExpandWidth * (axisExpandCount - 1), layoutExtent);
54833                var axisExpandCenter = parallelModel.get('axisExpandCenter') || mathFloor$2(axisCount / 2);
54834                axisExpandWindow = [axisExpandWidth * axisExpandCenter - winSize / 2];
54835                axisExpandWindow[1] = axisExpandWindow[0] + winSize;
54836            }
54837            else {
54838                winSize = restrict(axisExpandWindow[1] - axisExpandWindow[0], layoutExtent);
54839                axisExpandWindow[1] = axisExpandWindow[0] + winSize;
54840            }
54841
54842            var axisCollapseWidth = (layoutLength - winSize) / (axisCount - axisExpandCount);
54843            // Avoid axisCollapseWidth is too small.
54844            axisCollapseWidth < 3 && (axisCollapseWidth = 0);
54845
54846            // Find the first and last indices > ewin[0] and < ewin[1].
54847            var winInnerIndices = [
54848                mathFloor$2(round$2(axisExpandWindow[0] / axisExpandWidth, 1)) + 1,
54849                mathCeil$2(round$2(axisExpandWindow[1] / axisExpandWidth, 1)) - 1
54850            ];
54851
54852            // Pos in ec coordinates.
54853            var axisExpandWindow0Pos = axisCollapseWidth / axisExpandWidth * axisExpandWindow[0];
54854
54855            return {
54856                layout: layout,
54857                pixelDimIndex: pixelDimIndex,
54858                layoutBase: rect[xy[pixelDimIndex]],
54859                layoutLength: layoutLength,
54860                axisBase: rect[xy[1 - pixelDimIndex]],
54861                axisLength: rect[wh[1 - pixelDimIndex]],
54862                axisExpandable: axisExpandable,
54863                axisExpandWidth: axisExpandWidth,
54864                axisCollapseWidth: axisCollapseWidth,
54865                axisExpandWindow: axisExpandWindow,
54866                axisCount: axisCount,
54867                winInnerIndices: winInnerIndices,
54868                axisExpandWindow0Pos: axisExpandWindow0Pos
54869            };
54870        },
54871
54872        /**
54873         * @private
54874         */
54875        _layoutAxes: function () {
54876            var rect = this._rect;
54877            var axes = this._axesMap;
54878            var dimensions = this.dimensions;
54879            var layoutInfo = this._makeLayoutInfo();
54880            var layout = layoutInfo.layout;
54881
54882            axes.each(function (axis) {
54883                var axisExtent = [0, layoutInfo.axisLength];
54884                var idx = axis.inverse ? 1 : 0;
54885                axis.setExtent(axisExtent[idx], axisExtent[1 - idx]);
54886            });
54887
54888            each$11(dimensions, function (dim, idx) {
54889                var posInfo = (layoutInfo.axisExpandable
54890                        ? layoutAxisWithExpand : layoutAxisWithoutExpand
54891                )(idx, layoutInfo);
54892
54893                var positionTable = {
54894                    horizontal: {
54895                        x: posInfo.position,
54896                        y: layoutInfo.axisLength
54897                    },
54898                    vertical: {
54899                        x: 0,
54900                        y: posInfo.position
54901                    }
54902                };
54903                var rotationTable = {
54904                    horizontal: PI$3 / 2,
54905                    vertical: 0
54906                };
54907
54908                var position = [
54909                    positionTable[layout].x + rect.x,
54910                    positionTable[layout].y + rect.y
54911                ];
54912
54913                var rotation = rotationTable[layout];
54914                var transform = create$1();
54915                rotate(transform, transform, rotation);
54916                translate(transform, transform, position);
54917
54918                // TODO
54919                // tick等排布信息。
54920
54921                // TODO
54922                // æ ¹æ®axis order æ›´æ–° dimensions顺序。
54923
54924                this._axesLayout[dim] = {
54925                    position: position,
54926                    rotation: rotation,
54927                    transform: transform,
54928                    axisNameAvailableWidth: posInfo.axisNameAvailableWidth,
54929                    axisLabelShow: posInfo.axisLabelShow,
54930                    nameTruncateMaxWidth: posInfo.nameTruncateMaxWidth,
54931                    tickDirection: 1,
54932                    labelDirection: 1
54933                };
54934            }, this);
54935        },
54936
54937        /**
54938         * Get axis by dim.
54939         * @param {string} dim
54940         * @return {module:echarts/coord/parallel/ParallelAxis} [description]
54941         */
54942        getAxis: function (dim) {
54943            return this._axesMap.get(dim);
54944        },
54945
54946        /**
54947         * Convert a dim value of a single item of series data to Point.
54948         * @param {*} value
54949         * @param {string} dim
54950         * @return {Array}
54951         */
54952        dataToPoint: function (value, dim) {
54953            return this.axisCoordToPoint(
54954                this._axesMap.get(dim).dataToCoord(value),
54955                dim
54956            );
54957        },
54958
54959        /**
54960         * Travel data for one time, get activeState of each data item.
54961         * @param {module:echarts/data/List} data
54962         * @param {Functio} cb param: {string} activeState 'active' or 'inactive' or 'normal'
54963         *                            {number} dataIndex
54964         * @param {number} [start=0] the start dataIndex that travel from.
54965         * @param {number} [end=data.count()] the next dataIndex of the last dataIndex will be travel.
54966         */
54967        eachActiveState: function (data, callback, start, end) {
54968            start == null && (start = 0);
54969            end == null && (end = data.count());
54970
54971            var axesMap = this._axesMap;
54972            var dimensions = this.dimensions;
54973            var dataDimensions = [];
54974            var axisModels = [];
54975
54976            each$1(dimensions, function (axisDim) {
54977                dataDimensions.push(data.mapDimension(axisDim));
54978                axisModels.push(axesMap.get(axisDim).model);
54979            });
54980
54981            var hasActiveSet = this.hasAxisBrushed();
54982
54983            for (var dataIndex = start; dataIndex < end; dataIndex++) {
54984                var activeState;
54985
54986                if (!hasActiveSet) {
54987                    activeState = 'normal';
54988                }
54989                else {
54990                    activeState = 'active';
54991                    var values = data.getValues(dataDimensions, dataIndex);
54992                    for (var j = 0, lenj = dimensions.length; j < lenj; j++) {
54993                        var state = axisModels[j].getActiveState(values[j]);
54994
54995                        if (state === 'inactive') {
54996                            activeState = 'inactive';
54997                            break;
54998                        }
54999                    }
55000                }
55001
55002                callback(activeState, dataIndex);
55003            }
55004        },
55005
55006        /**
55007         * Whether has any activeSet.
55008         * @return {boolean}
55009         */
55010        hasAxisBrushed: function () {
55011            var dimensions = this.dimensions;
55012            var axesMap = this._axesMap;
55013            var hasActiveSet = false;
55014
55015            for (var j = 0, lenj = dimensions.length; j < lenj; j++) {
55016                if (axesMap.get(dimensions[j]).model.getActiveState() !== 'normal') {
55017                    hasActiveSet = true;
55018                }
55019            }
55020
55021            return hasActiveSet;
55022        },
55023
55024        /**
55025         * Convert coords of each axis to Point.
55026         *  Return point. For example: [10, 20]
55027         * @param {Array.<number>} coords
55028         * @param {string} dim
55029         * @return {Array.<number>}
55030         */
55031        axisCoordToPoint: function (coord, dim) {
55032            var axisLayout = this._axesLayout[dim];
55033            return applyTransform$1([coord, 0], axisLayout.transform);
55034        },
55035
55036        /**
55037         * Get axis layout.
55038         */
55039        getAxisLayout: function (dim) {
55040            return clone(this._axesLayout[dim]);
55041        },
55042
55043        /**
55044         * @param {Array.<number>} point
55045         * @return {Object} {axisExpandWindow, delta, behavior: 'jump' | 'slide' | 'none'}.
55046         */
55047        getSlidedAxisExpandWindow: function (point) {
55048            var layoutInfo = this._makeLayoutInfo();
55049            var pixelDimIndex = layoutInfo.pixelDimIndex;
55050            var axisExpandWindow = layoutInfo.axisExpandWindow.slice();
55051            var winSize = axisExpandWindow[1] - axisExpandWindow[0];
55052            var extent = [0, layoutInfo.axisExpandWidth * (layoutInfo.axisCount - 1)];
55053
55054            // Out of the area of coordinate system.
55055            if (!this.containPoint(point)) {
55056                return {behavior: 'none', axisExpandWindow: axisExpandWindow};
55057            }
55058
55059            // Conver the point from global to expand coordinates.
55060            var pointCoord = point[pixelDimIndex] - layoutInfo.layoutBase - layoutInfo.axisExpandWindow0Pos;
55061
55062            // For dragging operation convenience, the window should not be
55063            // slided when mouse is the center area of the window.
55064            var delta;
55065            var behavior = 'slide';
55066            var axisCollapseWidth = layoutInfo.axisCollapseWidth;
55067            var triggerArea = this._model.get('axisExpandSlideTriggerArea');
55068            // But consider touch device, jump is necessary.
55069            var useJump = triggerArea[0] != null;
55070
55071            if (axisCollapseWidth) {
55072                if (useJump && axisCollapseWidth && pointCoord < winSize * triggerArea[0]) {
55073                    behavior = 'jump';
55074                    delta = pointCoord - winSize * triggerArea[2];
55075                }
55076                else if (useJump && axisCollapseWidth && pointCoord > winSize * (1 - triggerArea[0])) {
55077                    behavior = 'jump';
55078                    delta = pointCoord - winSize * (1 - triggerArea[2]);
55079                }
55080                else {
55081                    (delta = pointCoord - winSize * triggerArea[1]) >= 0
55082                    && (delta = pointCoord - winSize * (1 - triggerArea[1])) <= 0
55083                    && (delta = 0);
55084                }
55085                delta *= layoutInfo.axisExpandWidth / axisCollapseWidth;
55086                delta
55087                    ? sliderMove(delta, axisExpandWindow, extent, 'all')
vendor: 4,190 bytes, lines 55088-55197
55088                    // Avoid nonsense triger on mousemove.
55089                    : (behavior = 'none');
55090            }
55091            // When screen is too narrow, make it visible and slidable, although it is hard to interact.
55092            else {
55093                var winSize = axisExpandWindow[1] - axisExpandWindow[0];
55094                var pos = extent[1] * pointCoord / winSize;
55095                axisExpandWindow = [mathMax$5(0, pos - winSize / 2)];
55096                axisExpandWindow[1] = mathMin$5(extent[1], axisExpandWindow[0] + winSize);
55097                axisExpandWindow[0] = axisExpandWindow[1] - winSize;
55098            }
55099
55100            return {
55101                axisExpandWindow: axisExpandWindow,
55102                behavior: behavior
55103            };
55104        }
55105    };
55106
55107    function restrict(len, extent) {
55108        return mathMin$5(mathMax$5(len, extent[0]), extent[1]);
55109    }
55110
55111    function layoutAxisWithoutExpand(axisIndex, layoutInfo) {
55112        var step = layoutInfo.layoutLength / (layoutInfo.axisCount - 1);
55113        return {
55114            position: step * axisIndex,
55115            axisNameAvailableWidth: step,
55116            axisLabelShow: true
55117        };
55118    }
55119
55120    function layoutAxisWithExpand(axisIndex, layoutInfo) {
55121        var layoutLength = layoutInfo.layoutLength;
55122        var axisExpandWidth = layoutInfo.axisExpandWidth;
55123        var axisCount = layoutInfo.axisCount;
55124        var axisCollapseWidth = layoutInfo.axisCollapseWidth;
55125        var winInnerIndices = layoutInfo.winInnerIndices;
55126
55127        var position;
55128        var axisNameAvailableWidth = axisCollapseWidth;
55129        var axisLabelShow = false;
55130        var nameTruncateMaxWidth;
55131
55132        if (axisIndex < winInnerIndices[0]) {
55133            position = axisIndex * axisCollapseWidth;
55134            nameTruncateMaxWidth = axisCollapseWidth;
55135        }
55136        else if (axisIndex <= winInnerIndices[1]) {
55137            position = layoutInfo.axisExpandWindow0Pos
55138                + axisIndex * axisExpandWidth - layoutInfo.axisExpandWindow[0];
55139            axisNameAvailableWidth = axisExpandWidth;
55140            axisLabelShow = true;
55141        }
55142        else {
55143            position = layoutLength - (axisCount - 1 - axisIndex) * axisCollapseWidth;
55144            nameTruncateMaxWidth = axisCollapseWidth;
55145        }
55146
55147        return {
55148            position: position,
55149            axisNameAvailableWidth: axisNameAvailableWidth,
55150            axisLabelShow: axisLabelShow,
55151            nameTruncateMaxWidth: nameTruncateMaxWidth
55152        };
55153    }
55154
55155    /*
55156* Licensed to the Apache Software Foundation (ASF) under one
55157* or more contributor license agreements.  See the NOTICE file
55158* distributed with this work for additional information
55159* regarding copyright ownership.  The ASF licenses this file
55160* to you under the Apache License, Version 2.0 (the
55161* "License"); you may not use this file except in compliance
55162* with the License.  You may obtain a copy of the License at
55163*
55164*   http://www.apache.org/licenses/LICENSE-2.0
55165*
55166* Unless required by applicable law or agreed to in writing,
55167* software distributed under the License is distributed on an
55168* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
55169* KIND, either express or implied.  See the License for the
55170* specific language governing permissions and limitations
55171* under the License.
55172*/
55173
55174    /**
55175     * Parallel coordinate system creater.
55176     */
55177
55178    function create$2(ecModel, api) {
55179        var coordSysList = [];
55180
55181        ecModel.eachComponent('parallel', function (parallelModel, idx) {
55182            var coordSys = new Parallel(parallelModel, ecModel, api);
55183
55184            coordSys.name = 'parallel_' + idx;
55185            coordSys.resize(parallelModel, api);
55186
55187            parallelModel.coordinateSystem = coordSys;
55188            coordSys.model = parallelModel;
55189
55190            coordSysList.push(coordSys);
55191        });
55192
55193        // Inject the coordinateSystems into seriesModel
55194        ecModel.eachSeries(function (seriesModel) {
55195            if (seriesModel.get('coordinateSystem') === 'parallel') {
55196                var parallelModel = ecModel.queryComponents({
55197                    mainType: 'parallel',
55198                    index: seriesModel.get('parallelIndex'),
55199                    id: seriesModel.get('parallelId')
55200                })[0];
55201                seriesModel.coordinateSystem = parallelModel.coordinateSystem;
55202            }
55203        });
55204
55205        return coordSysList;
55206    }
55207
55208    CoordinateSystemManager.register('parallel', {create: create$2});
55209
55210    /*
55211* Licensed to the Apache Software Foundation (ASF) under one
55212* or more contributor license agreements.  See the NOTICE file
55213* distributed with this work for additional information
55214* regarding copyright ownership.  The ASF licenses this file
55215* to you under the Apache License, Version 2.0 (the
55216* "License"); you may not use this file except in compliance
55217* with the License.  You may obtain a copy of the License at
55218*
55219*   http://www.apache.org/licenses/LICENSE-2.0
55220*
55221* Unless required by applicable law or agreed to in writing,
55222* software distributed under the License is distributed on an
55223* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
55224* KIND, either express or implied.  See the License for the
55225* specific language governing permissions and limitations
55226* under the License.
55227*/
55228
55229    var AxisModel$2 = ComponentModel.extend({
55230
55231        type: 'baseParallelAxis',
55232
55233        /**
55234         * @type {module:echarts/coord/parallel/Axis}
55235         */
55236        axis: null,
55237
55238        /**
55239         * @type {Array.<Array.<number>}
55240         * @readOnly
55241         */
55242        activeIntervals: [],
55243
55244        /**
55245         * @return {Object}
55246         */
55247        getAreaSelectStyle: function () {
55248            return makeStyleMapper(
55249                [
55250                    ['fill', 'color'],
55251                    ['lineWidth', 'borderWidth'],
55252                    ['stroke', 'borderColor'],
55253                    ['width', 'width'],
55254                    ['opacity', 'opacity']
55255                ]
55256            )(this.getModel('areaSelectStyle'));
55257        },
55258
55259        /**
55260         * The code of this feature is put on AxisModel but not ParallelAxis,
55261         * because axisModel can be alive after echarts updating but instance of
55262         * ParallelAxis having been disposed. this._activeInterval should be kept
55263         * when action dispatched (i.e. legend click).
55264         *
55265         * @param {Array.<Array<number>>} intervals interval.length === 0
55266         *                                          means set all active.
55267         * @public
55268         */
55269        setActiveIntervals: function (intervals) {
55270            var activeIntervals = this.activeIntervals = clone(intervals);
55271
55272            // Normalize
55273            if (activeIntervals) {
55274                for (var i = activeIntervals.length - 1; i >= 0; i--) {
55275                    asc(activeIntervals[i]);
55276                }
55277            }
55278        },
55279
55280        /**
55281         * @param {number|string} [value] When attempting to detect 'no activeIntervals set',
55282         *                         value can not be input.
55283         * @return {string} 'normal': no activeIntervals set,
55284         *                  'active',
55285         *                  'inactive'.
55286         * @public
55287         */
55288        getActiveState: function (value) {
55289            var activeIntervals = this.activeIntervals;
55290
55291            if (!activeIntervals.length) {
55292                return 'normal';
55293            }
55294
55295            if (value == null || isNaN(value)) {
55296                return 'inactive';
55297            }
55298
55299            // Simple optimization
55300            if (activeIntervals.length === 1) {
55301                var interval = activeIntervals[0];
55302                if (interval[0] <= value && value <= interval[1]) {
55303                    return 'active';
55304                }
55305            }
55306            else {
55307                for (var i = 0, len = activeIntervals.length; i < len; i++) {
55308                    if (activeIntervals[i][0] <= value && value <= activeIntervals[i][1]) {
55309                        return 'active';
55310                    }
55311                }
55312            }
55313
55314            return 'inactive';
55315        }
55316
55317    });
55318
55319    var defaultOption$1 = {
55320
55321        type: 'value',
55322
55323        /**
55324         * @type {Array.<number>}
55325         */
55326        dim: null, // 0, 1, 2, ...
55327
55328        // parallelIndex: null,
55329
55330        areaSelectStyle: {
55331            width: 20,
55332            borderWidth: 1,
55333            borderColor: 'rgba(160,197,232)',
55334            color: 'rgba(160,197,232)',
55335            opacity: 0.3
55336        },
55337
55338        realtime: true, // Whether realtime update view when select.
55339
55340        z: 10
55341    };
55342
55343    merge(AxisModel$2.prototype, axisModelCommonMixin);
55344
55345    function getAxisType$1(axisName, option) {
55346        return option.type || (option.data ? 'category' : 'value');
55347    }
55348
55349    axisModelCreator('parallel', AxisModel$2, getAxisType$1, defaultOption$1);
55350
55351    /*
55352* Licensed to the Apache Software Foundation (ASF) under one
55353* or more contributor license agreements.  See the NOTICE file
55354* distributed with this work for additional information
55355* regarding copyright ownership.  The ASF licenses this file
55356* to you under the Apache License, Version 2.0 (the
55357* "License"); you may not use this file except in compliance
55358* with the License.  You may obtain a copy of the License at
55359*
55360*   http://www.apache.org/licenses/LICENSE-2.0
55361*
55362* Unless required by applicable law or agreed to in writing,
55363* software distributed under the License is distributed on an
55364* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
55365* KIND, either express or implied.  See the License for the
55366* specific language governing permissions and limitations
55367* under the License.
55368*/
55369
55370    ComponentModel.extend({
55371
55372        type: 'parallel',
55373
55374        dependencies: ['parallelAxis'],
55375
55376        /**
55377         * @type {module:echarts/coord/parallel/Parallel}
55378         */
55379        coordinateSystem: null,
55380
55381        /**
55382         * Each item like: 'dim0', 'dim1', 'dim2', ...
55383         * @type {Array.<string>}
55384         * @readOnly
55385         */
55386        dimensions: null,
55387
55388        /**
55389         * Coresponding to dimensions.
55390         * @type {Array.<number>}
55391         * @readOnly
55392         */
55393        parallelAxisIndex: null,
55394
55395        layoutMode: 'box',
55396
55397        defaultOption: {
55398            zlevel: 0,
55399            z: 0,
55400            left: 80,
55401            top: 60,
55402            right: 80,
55403            bottom: 60,
55404            // width: {totalWidth} - left - right,
55405            // height: {totalHeight} - top - bottom,
55406
55407            layout: 'horizontal',      // 'horizontal' or 'vertical'
55408
55409            // FIXME
55410            // naming?
55411            axisExpandable: false,
55412            axisExpandCenter: null,
55413            axisExpandCount: 0,
55414            axisExpandWidth: 50,      // FIXME '10%' ?
55415            axisExpandRate: 17,
55416            axisExpandDebounce: 50,
55417            // [out, in, jumpTarget]. In percentage. If use [null, 0.05], null means full.
55418            // Do not doc to user until necessary.
55419            axisExpandSlideTriggerArea: [-0.15, 0.05, 0.4],
55420            axisExpandTriggerOn: 'click', // 'mousemove' or 'click'
55421
55422            parallelAxisDefault: null
55423        },
55424
55425        /**
55426         * @override
55427         */
55428        init: function () {
55429            ComponentModel.prototype.init.apply(this, arguments);
55430
55431            this.mergeOption({});
55432        },
55433
55434        /**
55435         * @override
55436         */
55437        mergeOption: function (newOption) {
55438            var thisOption = this.option;
55439
55440            newOption && merge(thisOption, newOption, true);
55441
55442            this._initDimensions();
55443        },
55444
55445        /**
55446         * Whether series or axis is in this coordinate system.
55447         * @param {module:echarts/model/Series|module:echarts/coord/parallel/AxisModel} model
55448         * @param {module:echarts/model/Global} ecModel
55449         */
55450        contains: function (model, ecModel) {
55451            var parallelIndex = model.get('parallelIndex');
55452            return parallelIndex != null
55453                && ecModel.getComponent('parallel', parallelIndex) === this;
55454        },
55455
55456        setAxisExpand: function (opt) {
55457            each$1(
55458                ['axisExpandable', 'axisExpandCenter', 'axisExpandCount', 'axisExpandWidth', 'axisExpandWindow'],
55459                function (name) {
55460                    if (opt.hasOwnProperty(name)) {
55461                        this.option[name] = opt[name];
55462                    }
55463                },
55464                this
55465            );
55466        },
55467
55468        /**
55469         * @private
55470         */
55471        _initDimensions: function () {
55472            var dimensions = this.dimensions = [];
55473            var parallelAxisIndex = this.parallelAxisIndex = [];
55474
55475            var axisModels = filter(this.dependentModels.parallelAxis, function (axisModel) {
55476                // Can not use this.contains here, because
55477                // initialization has not been completed yet.
55478                return (axisModel.get('parallelIndex') || 0) === this.componentIndex;
55479            }, this);
55480
55481            each$1(axisModels, function (axisModel) {
55482                dimensions.push('dim' + axisModel.get('dim'));
55483                parallelAxisIndex.push(axisModel.componentIndex);
55484            });
55485        }
55486
55487    });
55488
55489    /*
55490* Licensed to the Apache Software Foundation (ASF) under one
55491* or more contributor license agreements.  See the NOTICE file
55492* distributed with this work for additional information
55493* regarding copyright ownership.  The ASF licenses this file
55494* to you under the Apache License, Version 2.0 (the
55495* "License"); you may not use this file except in compliance
55496* with the License.  You may obtain a copy of the License at
55497*
55498*   http://www.apache.org/licenses/LICENSE-2.0
55499*
55500* Unless required by applicable law or agreed to in writing,
55501* software distributed under the License is distributed on an
55502* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
55503* KIND, either express or implied.  See the License for the
55504* specific language governing permissions and limitations
55505* under the License.
55506*/
55507
55508    /**
55509     * @payload
55510     * @property {string} parallelAxisId
55511     * @property {Array.<Array.<number>>} intervals
55512     */
55513    var actionInfo$1 = {
55514        type: 'axisAreaSelect',
55515        event: 'axisAreaSelected'
55516        // update: 'updateVisual'
55517    };
55518
55519    registerAction(actionInfo$1, function (payload, ecModel) {
55520        ecModel.eachComponent(
55521            {mainType: 'parallelAxis', query: payload},
55522            function (parallelAxisModel) {
55523                parallelAxisModel.axis.model.setActiveIntervals(payload.intervals);
55524            }
55525        );
55526    });
55527
55528    /**
55529     * @payload
55530     */
55531    registerAction('parallelAxisExpand', function (payload, ecModel) {
55532        ecModel.eachComponent(
55533            {mainType: 'parallel', query: payload},
55534            function (parallelModel) {
55535                parallelModel.setAxisExpand(payload);
55536            }
55537        );
55538
55539    });
55540
55541    /*
55542* Licensed to the Apache Software Foundation (ASF) under one
55543* or more contributor license agreements.  See the NOTICE file
55544* distributed with this work for additional information
55545* regarding copyright ownership.  The ASF licenses this file
55546* to you under the Apache License, Version 2.0 (the
55547* "License"); you may not use this file except in compliance
55548* with the License.  You may obtain a copy of the License at
55549*
55550*   http://www.apache.org/licenses/LICENSE-2.0
55551*
55552* Unless required by applicable law or agreed to in writing,
55553* software distributed under the License is distributed on an
55554* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
55555* KIND, either express or implied.  See the License for the
55556* specific language governing permissions and limitations
55557* under the License.
55558*/
55559
55560    var curry$2 = curry;
55561    var each$12 = each$1;
55562    var map$2 = map;
55563    var mathMin$6 = Math.min;
55564    var mathMax$6 = Math.max;
55565    var mathPow$2 = Math.pow;
55566
55567    var COVER_Z = 10000;
55568    var UNSELECT_THRESHOLD = 6;
55569    var MIN_RESIZE_LINE_WIDTH = 6;
55570    var MUTEX_RESOURCE_KEY = 'globalPan';
55571
55572    var DIRECTION_MAP = {
55573        w: [0, 0],
55574        e: [0, 1],
55575        n: [1, 0],
55576        s: [1, 1]
55577    };
55578    var CURSOR_MAP = {
55579        w: 'ew',
55580        e: 'ew',
55581        n: 'ns',
55582        s: 'ns',
55583        ne: 'nesw',
55584        sw: 'nesw',
55585        nw: 'nwse',
55586        se: 'nwse'
55587    };
55588    var DEFAULT_BRUSH_OPT = {
55589        brushStyle: {
55590            lineWidth: 2,
55591            stroke: 'rgba(0,0,0,0.3)',
55592            fill: 'rgba(0,0,0,0.1)'
55593        },
55594        transformable: true,
55595        brushMode: 'single',
55596        removeOnClick: false
55597    };
55598
55599    var baseUID = 0;
55600
55601    /**
55602     * @alias module:echarts/component/helper/BrushController
55603     * @constructor
55604     * @mixin {module:zrender/mixin/Eventful}
55605     * @event module:echarts/component/helper/BrushController#brush
55606     *        params:
55607     *            areas: Array.<Array>, coord relates to container group,
55608     *                                    If no container specified, to global.
55609     *            opt {
55610 *                isEnd: boolean,
55611 *                removeOnClick: boolean
55612 *            }
55613     *
55614     * @param {module:zrender/zrender~ZRender} zr
55615     */
55616    function BrushController(zr) {
55617
55618        if (__DEV__) {
55619            assert$1(zr);
55620        }
55621
55622        Eventful.call(this);
55623
55624        /**
55625         * @type {module:zrender/zrender~ZRender}
55626         * @private
55627         */
55628        this._zr = zr;
55629
55630        /**
55631         * @type {module:zrender/container/Group}
55632         * @readOnly
55633         */
55634        this.group = new Group();
55635
55636        /**
55637         * Only for drawing (after enabledBrush).
55638         *     'line', 'rect', 'polygon' or false
55639         *     If passing false/null/undefined, disable brush.
55640         *     If passing 'auto', determined by panel.defaultBrushType
55641         * @private
55642         * @type {string}
55643         */
55644        this._brushType;
55645
55646        /**
55647         * Only for drawing (after enabledBrush).
55648         *
55649         * @private
55650         * @type {Object}
55651         */
55652        this._brushOption;
55653
55654        /**
55655         * @private
55656         * @type {Object}
55657         */
55658        this._panels;
55659
55660        /**
55661         * @private
55662         * @type {Array.<nubmer>}
55663         */
55664        this._track = [];
55665
55666        /**
55667         * @private
55668         * @type {boolean}
55669         */
55670        this._dragging;
55671
55672        /**
55673         * @private
55674         * @type {Array}
55675         */
55676        this._covers = [];
55677
55678        /**
55679         * @private
55680         * @type {moudule:zrender/container/Group}
55681         */
55682        this._creatingCover;
55683
55684        /**
55685         * `true` means global panel
55686         * @private
55687         * @type {module:zrender/container/Group|boolean}
55688         */
55689        this._creatingPanel;
55690
55691        /**
55692         * @private
55693         * @type {boolean}
55694         */
55695        this._enableGlobalPan;
55696
55697        /**
55698         * @private
55699         * @type {boolean}
55700         */
55701        if (__DEV__) {
55702            this._mounted;
55703        }
55704
55705        /**
55706         * @private
55707         * @type {string}
55708         */
55709        this._uid = 'brushController_' + baseUID++;
55710
55711        /**
55712         * @private
55713         * @type {Object}
55714         */
55715        this._handlers = {};
55716        each$12(mouseHandlers, function (handler, eventName) {
55717            this._handlers[eventName] = bind(handler, this);
55718        }, this);
55719    }
55720
55721    BrushController.prototype = {
55722
55723        constructor: BrushController,
55724
55725        /**
55726         * If set to null/undefined/false, select disabled.
55727         * @param {Object} brushOption
55728         * @param {string|boolean} brushOption.brushType 'line', 'rect', 'polygon' or false
55729         *                          If passing false/null/undefined, disable brush.
55730         *                          If passing 'auto', determined by panel.defaultBrushType.
55731         *                              ('auto' can not be used in global panel)
55732         * @param {number} [brushOption.brushMode='single'] 'single' or 'multiple'
55733         * @param {boolean} [brushOption.transformable=true]
55734         * @param {boolean} [brushOption.removeOnClick=false]
55735         * @param {Object} [brushOption.brushStyle]
55736         * @param {number} [brushOption.brushStyle.width]
55737         * @param {number} [brushOption.brushStyle.lineWidth]
55738         * @param {string} [brushOption.brushStyle.stroke]
55739         * @param {string} [brushOption.brushStyle.fill]
55740         * @param {number} [brushOption.z]
55741         */
55742        enableBrush: function (brushOption) {
55743            if (__DEV__) {
55744                assert$1(this._mounted);
55745            }
55746
55747            this._brushType && doDisableBrush(this);
55748            brushOption.brushType && doEnableBrush(this, brushOption);
55749
55750            return this;
55751        },
55752
55753        /**
55754         * @param {Array.<Object>} panelOpts If not pass, it is global brush.
55755         *        Each items: {
55756     *            panelId, // mandatory.
55757     *            clipPath, // mandatory. function.
55758     *            isTargetByCursor, // mandatory. function.
55759     *            defaultBrushType, // optional, only used when brushType is 'auto'.
55760     *            getLinearBrushOtherExtent, // optional. function.
55761     *        }
55762         */
55763        setPanels: function (panelOpts) {
55764            if (panelOpts && panelOpts.length) {
55765                var panels = this._panels = {};
55766                each$1(panelOpts, function (panelOpts) {
55767                    panels[panelOpts.panelId] = clone(panelOpts);
55768                });
55769            }
55770            else {
55771                this._panels = null;
55772            }
55773            return this;
55774        },
55775
55776        /**
55777         * @param {Object} [opt]
55778         * @return {boolean} [opt.enableGlobalPan=false]
55779         */
55780        mount: function (opt) {
55781            opt = opt || {};
55782
55783            if (__DEV__) {
55784                this._mounted = true; // should be at first.
55785            }
55786
55787            this._enableGlobalPan = opt.enableGlobalPan;
55788
55789            var thisGroup = this.group;
55790            this._zr.add(thisGroup);
55791
55792            thisGroup.attr({
55793                position: opt.position || [0, 0],
55794                rotation: opt.rotation || 0,
55795                scale: opt.scale || [1, 1]
55796            });
55797            this._transform = thisGroup.getLocalTransform();
55798
55799            return this;
55800        },
55801
55802        eachCover: function (cb, context) {
55803            each$12(this._covers, cb, context);
55804        },
55805
55806        /**
55807         * Update covers.
55808         * @param {Array.<Object>} brushOptionList Like:
55809         *        [
55810         *            {id: 'xx', brushType: 'line', range: [23, 44], brushStyle, transformable},
55811         *            {id: 'yy', brushType: 'rect', range: [[23, 44], [23, 54]]},
55812         *            ...
55813         *        ]
55814         *        `brushType` is required in each cover info. (can not be 'auto')
55815         *        `id` is not mandatory.
55816         *        `brushStyle`, `transformable` is not mandatory, use DEFAULT_BRUSH_OPT by default.
55817         *        If brushOptionList is null/undefined, all covers removed.
55818         */
55819        updateCovers: function (brushOptionList) {
55820            if (__DEV__) {
55821                assert$1(this._mounted);
55822            }
55823
55824            brushOptionList = map(brushOptionList, function (brushOption) {
55825                return merge(clone(DEFAULT_BRUSH_OPT), brushOption, true);
55826            });
55827
55828            var tmpIdPrefix = '\0-brush-index-';
55829            var oldCovers = this._covers;
55830            var newCovers = this._covers = [];
55831            var controller = this;
55832            var creatingCover = this._creatingCover;
55833
55834            (new DataDiffer(oldCovers, brushOptionList, oldGetKey, getKey))
55835                .add(addOrUpdate)
55836                .update(addOrUpdate)
55837                .remove(remove)
55838                .execute();
55839
55840            return this;
55841
55842            function getKey(brushOption, index) {
55843                return (brushOption.id != null ? brushOption.id : tmpIdPrefix + index)
55844                    + '-' + brushOption.brushType;
55845            }
55846
55847            function oldGetKey(cover, index) {
55848                return getKey(cover.__brushOption, index);
55849            }
55850
55851            function addOrUpdate(newIndex, oldIndex) {
55852                var newBrushOption = brushOptionList[newIndex];
55853                // Consider setOption in event listener of brushSelect,
55854                // where updating cover when creating should be forbiden.
55855                if (oldIndex != null && oldCovers[oldIndex] === creatingCover) {
55856                    newCovers[newIndex] = oldCovers[oldIndex];
55857                }
55858                else {
55859                    var cover = newCovers[newIndex] = oldIndex != null
55860                        ? (
55861                            oldCovers[oldIndex].__brushOption = newBrushOption,
55862                                oldCovers[oldIndex]
55863                        )
55864                        : endCreating(controller, createCover(controller, newBrushOption));
55865                    updateCoverAfterCreation(controller, cover);
55866                }
55867            }
55868
55869            function remove(oldIndex) {
55870                if (oldCovers[oldIndex] !== creatingCover) {
55871                    controller.group.remove(oldCovers[oldIndex]);
55872                }
55873            }
55874        },
55875
55876        unmount: function () {
55877            if (__DEV__) {
55878                if (!this._mounted) {
55879                    return;
55880                }
55881            }
55882
55883            this.enableBrush(false);
55884
55885            // container may 'removeAll' outside.
55886            clearCovers(this);
55887            this._zr.remove(this.group);
55888
55889            if (__DEV__) {
55890                this._mounted = false; // should be at last.
55891            }
55892
55893            return this;
55894        },
55895
55896        dispose: function () {
55897            this.unmount();
55898            this.off();
55899        }
55900    };
55901
55902    mixin(BrushController, Eventful);
55903
55904    function doEnableBrush(controller, brushOption) {
55905        var zr = controller._zr;
55906
55907        // Consider roam, which takes globalPan too.
55908        if (!controller._enableGlobalPan) {
55909            take(zr, MUTEX_RESOURCE_KEY, controller._uid);
55910        }
55911
55912        each$12(controller._handlers, function (handler, eventName) {
55913            zr.on(eventName, handler);
55914        });
55915
55916        controller._brushType = brushOption.brushType;
55917        controller._brushOption = merge(clone(DEFAULT_BRUSH_OPT), brushOption, true);
55918    }
55919
55920    function doDisableBrush(controller) {
55921        var zr = controller._zr;
55922
55923        release(zr, MUTEX_RESOURCE_KEY, controller._uid);
55924
55925        each$12(controller._handlers, function (handler, eventName) {
55926            zr.off(eventName, handler);
55927        });
55928
55929        controller._brushType = controller._brushOption = null;
55930    }
55931
55932    function createCover(controller, brushOption) {
55933        var cover = coverRenderers[brushOption.brushType].createCover(controller, brushOption);
55934        cover.__brushOption = brushOption;
55935        updateZ$1(cover, brushOption);
55936        controller.group.add(cover);
55937        return cover;
55938    }
55939
55940    function endCreating(controller, creatingCover) {
55941        var coverRenderer = getCoverRenderer(creatingCover);
55942        if (coverRenderer.endCreating) {
55943            coverRenderer.endCreating(controller, creatingCover);
55944            updateZ$1(creatingCover, creatingCover.__brushOption);
55945        }
55946        return creatingCover;
55947    }
55948
55949    function updateCoverShape(controller, cover) {
55950        var brushOption = cover.__brushOption;
55951        getCoverRenderer(cover).updateCoverShape(
55952            controller, cover, brushOption.range, brushOption
55953        );
55954    }
55955
55956    function updateZ$1(cover, brushOption) {
55957        var z = brushOption.z;
55958        z == null && (z = COVER_Z);
55959        cover.traverse(function (el) {
55960            el.z = z;
55961            el.z2 = z; // Consider in given container.
55962        });
55963    }
55964
55965    function updateCoverAfterCreation(controller, cover) {
55966        getCoverRenderer(cover).updateCommon(controller, cover);
55967        updateCoverShape(controller, cover);
55968    }
55969
55970    function getCoverRenderer(cover) {
55971        return coverRenderers[cover.__brushOption.brushType];
55972    }
55973
55974// return target panel or `true` (means global panel)
55975    function getPanelByPoint(controller, e, localCursorPoint) {
55976        var panels = controller._panels;
55977        if (!panels) {
55978            return true; // Global panel
55979        }
55980        var panel;
55981        var transform = controller._transform;
55982        each$12(panels, function (pn) {
55983            pn.isTargetByCursor(e, localCursorPoint, transform) && (panel = pn);
55984        });
55985        return panel;
55986    }
55987
55988// Return a panel or true
55989    function getPanelByCover(controller, cover) {
55990        var panels = controller._panels;
55991        if (!panels) {
55992            return true; // Global panel
55993        }
55994        var panelId = cover.__brushOption.panelId;
55995        // User may give cover without coord sys info,
55996        // which is then treated as global panel.
55997        return panelId != null ? panels[panelId] : true;
55998    }
55999
56000    function clearCovers(controller) {
56001        var covers = controller._covers;
56002        var originalLength = covers.length;
56003        each$12(covers, function (cover) {
56004            controller.group.remove(cover);
56005        }, controller);
56006        covers.length = 0;
56007
56008        return !!originalLength;
56009    }
56010
56011    function trigger(controller, opt) {
56012        var areas = map$2(controller._covers, function (cover) {
56013            var brushOption = cover.__brushOption;
56014            var range = clone(brushOption.range);
56015            return {
56016                brushType: brushOption.brushType,
56017                panelId: brushOption.panelId,
56018                range: range
56019            };
56020        });
56021
56022        controller.trigger('brush', areas, {
56023            isEnd: !!opt.isEnd,
56024            removeOnClick: !!opt.removeOnClick
56025        });
56026    }
56027
56028    function shouldShowCover(controller) {
56029        var track = controller._track;
56030
56031        if (!track.length) {
56032            return false;
56033        }
56034
56035        var p2 = track[track.length - 1];
56036        var p1 = track[0];
56037        var dx = p2[0] - p1[0];
56038        var dy = p2[1] - p1[1];
56039        var dist = mathPow$2(dx * dx + dy * dy, 0.5);
56040
56041        return dist > UNSELECT_THRESHOLD;
56042    }
56043
56044    function getTrackEnds(track) {
56045        var tail = track.length - 1;
56046        tail < 0 && (tail = 0);
56047        return [track[0], track[tail]];
56048    }
56049
56050    function createBaseRectCover(doDrift, controller, brushOption, edgeNames) {
56051        var cover = new Group();
56052
56053        cover.add(new Rect({
56054            name: 'main',
56055            style: makeStyle(brushOption),
56056            silent: true,
56057            draggable: true,
56058            cursor: 'move',
56059            drift: curry$2(doDrift, controller, cover, 'nswe'),
56060            ondragend: curry$2(trigger, controller, {isEnd: true})
56061        }));
56062
56063        each$12(
56064            edgeNames,
56065            function (name) {
56066                cover.add(new Rect({
56067                    name: name,
56068                    style: {opacity: 0},
56069                    draggable: true,
56070                    silent: true,
56071                    invisible: true,
56072                    drift: curry$2(doDrift, controller, cover, name),
vendor: 6,840 bytes, lines 56073-56259
56073                    ondragend: curry$2(trigger, controller, {isEnd: true})
56074                }));
56075            }
56076        );
56077
56078        return cover;
56079    }
56080
56081    function updateBaseRect(controller, cover, localRange, brushOption) {
56082        var lineWidth = brushOption.brushStyle.lineWidth || 0;
56083        var handleSize = mathMax$6(lineWidth, MIN_RESIZE_LINE_WIDTH);
56084        var x = localRange[0][0];
56085        var y = localRange[1][0];
56086        var xa = x - lineWidth / 2;
56087        var ya = y - lineWidth / 2;
56088        var x2 = localRange[0][1];
56089        var y2 = localRange[1][1];
56090        var x2a = x2 - handleSize + lineWidth / 2;
56091        var y2a = y2 - handleSize + lineWidth / 2;
56092        var width = x2 - x;
56093        var height = y2 - y;
56094        var widtha = width + lineWidth;
56095        var heighta = height + lineWidth;
56096
56097        updateRectShape(controller, cover, 'main', x, y, width, height);
56098
56099        if (brushOption.transformable) {
56100            updateRectShape(controller, cover, 'w', xa, ya, handleSize, heighta);
56101            updateRectShape(controller, cover, 'e', x2a, ya, handleSize, heighta);
56102            updateRectShape(controller, cover, 'n', xa, ya, widtha, handleSize);
56103            updateRectShape(controller, cover, 's', xa, y2a, widtha, handleSize);
56104
56105            updateRectShape(controller, cover, 'nw', xa, ya, handleSize, handleSize);
56106            updateRectShape(controller, cover, 'ne', x2a, ya, handleSize, handleSize);
56107            updateRectShape(controller, cover, 'sw', xa, y2a, handleSize, handleSize);
56108            updateRectShape(controller, cover, 'se', x2a, y2a, handleSize, handleSize);
56109        }
56110    }
56111
56112    function updateCommon(controller, cover) {
56113        var brushOption = cover.__brushOption;
56114        var transformable = brushOption.transformable;
56115
56116        var mainEl = cover.childAt(0);
56117        mainEl.useStyle(makeStyle(brushOption));
56118        mainEl.attr({
56119            silent: !transformable,
56120            cursor: transformable ? 'move' : 'default'
56121        });
56122
56123        each$12(
56124            ['w', 'e', 'n', 's', 'se', 'sw', 'ne', 'nw'],
56125            function (name) {
56126                var el = cover.childOfName(name);
56127                var globalDir = getGlobalDirection(controller, name);
56128
56129                el && el.attr({
56130                    silent: !transformable,
56131                    invisible: !transformable,
56132                    cursor: transformable ? CURSOR_MAP[globalDir] + '-resize' : null
56133                });
56134            }
56135        );
56136    }
56137
56138    function updateRectShape(controller, cover, name, x, y, w, h) {
56139        var el = cover.childOfName(name);
56140        el && el.setShape(pointsToRect(
56141            clipByPanel(controller, cover, [[x, y], [x + w, y + h]])
56142        ));
56143    }
56144
56145    function makeStyle(brushOption) {
56146        return defaults({strokeNoScale: true}, brushOption.brushStyle);
56147    }
56148
56149    function formatRectRange(x, y, x2, y2) {
56150        var min = [mathMin$6(x, x2), mathMin$6(y, y2)];
56151        var max = [mathMax$6(x, x2), mathMax$6(y, y2)];
56152
56153        return [
56154            [min[0], max[0]], // x range
56155            [min[1], max[1]] // y range
56156        ];
56157    }
56158
56159    function getTransform$1(controller) {
56160        return getTransform(controller.group);
56161    }
56162
56163    function getGlobalDirection(controller, localDirection) {
56164        if (localDirection.length > 1) {
56165            localDirection = localDirection.split('');
56166            var globalDir = [
56167                getGlobalDirection(controller, localDirection[0]),
56168                getGlobalDirection(controller, localDirection[1])
56169            ];
56170            (globalDir[0] === 'e' || globalDir[0] === 'w') && globalDir.reverse();
56171            return globalDir.join('');
56172        }
56173        else {
56174            var map$$1 = {w: 'left', e: 'right', n: 'top', s: 'bottom'};
56175            var inverseMap = {left: 'w', right: 'e', top: 'n', bottom: 's'};
56176            var globalDir = transformDirection(
56177                map$$1[localDirection], getTransform$1(controller)
56178            );
56179            return inverseMap[globalDir];
56180        }
56181    }
56182
56183    function driftRect(toRectRange, fromRectRange, controller, cover, name, dx, dy, e) {
56184        var brushOption = cover.__brushOption;
56185        var rectRange = toRectRange(brushOption.range);
56186        var localDelta = toLocalDelta(controller, dx, dy);
56187
56188        each$12(name.split(''), function (namePart) {
56189            var ind = DIRECTION_MAP[namePart];
56190            rectRange[ind[0]][ind[1]] += localDelta[ind[0]];
56191        });
56192
56193        brushOption.range = fromRectRange(formatRectRange(
56194            rectRange[0][0], rectRange[1][0], rectRange[0][1], rectRange[1][1]
56195        ));
56196
56197        updateCoverAfterCreation(controller, cover);
56198        trigger(controller, {isEnd: false});
56199    }
56200
56201    function driftPolygon(controller, cover, dx, dy, e) {
56202        var range = cover.__brushOption.range;
56203        var localDelta = toLocalDelta(controller, dx, dy);
56204
56205        each$12(range, function (point) {
56206            point[0] += localDelta[0];
56207            point[1] += localDelta[1];
56208        });
56209
56210        updateCoverAfterCreation(controller, cover);
56211        trigger(controller, {isEnd: false});
56212    }
56213
56214    function toLocalDelta(controller, dx, dy) {
56215        var thisGroup = controller.group;
56216        var localD = thisGroup.transformCoordToLocal(dx, dy);
56217        var localZero = thisGroup.transformCoordToLocal(0, 0);
56218
56219        return [localD[0] - localZero[0], localD[1] - localZero[1]];
56220    }
56221
56222    function clipByPanel(controller, cover, data) {
56223        var panel = getPanelByCover(controller, cover);
56224
56225        return (panel && panel !== true)
56226            ? panel.clipPath(data, controller._transform)
56227            : clone(data);
56228    }
56229
56230    function pointsToRect(points) {
56231        var xmin = mathMin$6(points[0][0], points[1][0]);
56232        var ymin = mathMin$6(points[0][1], points[1][1]);
56233        var xmax = mathMax$6(points[0][0], points[1][0]);
56234        var ymax = mathMax$6(points[0][1], points[1][1]);
56235
56236        return {
56237            x: xmin,
56238            y: ymin,
56239            width: xmax - xmin,
56240            height: ymax - ymin
56241        };
56242    }
56243
56244    function resetCursor(controller, e, localCursorPoint) {
56245        // Check active
56246        if (!controller._brushType) {
56247            return;
56248        }
56249
56250        var zr = controller._zr;
56251        var covers = controller._covers;
56252        var currPanel = getPanelByPoint(controller, e, localCursorPoint);
56253
56254        // Check whether in covers.
56255        if (!controller._dragging) {
56256            for (var i = 0; i < covers.length; i++) {
56257                var brushOption = covers[i].__brushOption;
56258                if (currPanel
56259                    && (currPanel === true || brushOption.panelId === currPanel.panelId)
56260                    && coverRenderers[brushOption.brushType].contain(
56261                        covers[i], localCursorPoint[0], localCursorPoint[1]
56262                    )
56263                ) {
56264                    // Use cursor style set on cover.
56265                    return;
56266                }
56267            }
56268        }
56269
56270        currPanel && zr.setCursorStyle('crosshair');
56271    }
56272
56273    function preventDefault(e) {
56274        var rawE = e.event;
56275        rawE.preventDefault && rawE.preventDefault();
56276    }
56277
56278    function mainShapeContain(cover, x, y) {
56279        return cover.childOfName('main').contain(x, y);
56280    }
56281
56282    function updateCoverByMouse(controller, e, localCursorPoint, isEnd) {
56283        var creatingCover = controller._creatingCover;
56284        var panel = controller._creatingPanel;
56285        var thisBrushOption = controller._brushOption;
56286        var eventParams;
56287
56288        controller._track.push(localCursorPoint.slice());
56289
56290        if (shouldShowCover(controller) || creatingCover) {
56291
56292            if (panel && !creatingCover) {
56293                thisBrushOption.brushMode === 'single' && clearCovers(controller);
56294                var brushOption = clone(thisBrushOption);
56295                brushOption.brushType = determineBrushType(brushOption.brushType, panel);
56296                brushOption.panelId = panel === true ? null : panel.panelId;
56297                creatingCover = controller._creatingCover = createCover(controller, brushOption);
56298                controller._covers.push(creatingCover);
56299            }
56300
56301            if (creatingCover) {
vendor: 1,401 bytes, lines 56302-56333
56302                var coverRenderer = coverRenderers[determineBrushType(controller._brushType, panel)];
56303                var coverBrushOption = creatingCover.__brushOption;
56304
56305                coverBrushOption.range = coverRenderer.getCreatingRange(
56306                    clipByPanel(controller, creatingCover, controller._track)
56307                );
56308
56309                if (isEnd) {
56310                    endCreating(controller, creatingCover);
56311                    coverRenderer.updateCommon(controller, creatingCover);
56312                }
56313
56314                updateCoverShape(controller, creatingCover);
56315
56316                eventParams = {isEnd: isEnd};
56317            }
56318        }
56319        else if (
56320            isEnd
56321            && thisBrushOption.brushMode === 'single'
56322            && thisBrushOption.removeOnClick
56323        ) {
56324            // Help user to remove covers easily, only by a tiny drag, in 'single' mode.
56325            // But a single click do not clear covers, because user may have casual
56326            // clicks (for example, click on other component and do not expect covers
56327            // disappear).
56328            // Only some cover removed, trigger action, but not every click trigger action.
56329            if (getPanelByPoint(controller, e, localCursorPoint) && clearCovers(controller)) {
56330                eventParams = {isEnd: isEnd, removeOnClick: true};
56331            }
56332        }
56333
56334        return eventParams;
56335    }
56336
56337    function determineBrushType(brushType, panel) {
56338        if (brushType === 'auto') {
56339            if (__DEV__) {
56340                assert$1(
56341                    panel && panel.defaultBrushType,
56342                    'MUST have defaultBrushType when brushType is "atuo"'
56343                );
56344            }
56345            return panel.defaultBrushType;
56346        }
56347        return brushType;
56348    }
56349
56350    var mouseHandlers = {
56351
56352        mousedown: function (e) {
56353            if (this._dragging) {
56354                // In case some browser do not support globalOut,
56355                // and release mose out side the browser.
56356                handleDragEnd.call(this, e);
56357            }
56358            else if (!e.target || !e.target.draggable) {
56359
56360                preventDefault(e);
56361
56362                var localCursorPoint = this.group.transformCoordToLocal(e.offsetX, e.offsetY);
56363
56364                this._creatingCover = null;
56365                var panel = this._creatingPanel = getPanelByPoint(this, e, localCursorPoint);
56366
56367                if (panel) {
56368                    this._dragging = true;
56369                    this._track = [localCursorPoint.slice()];
56370                }
56371            }
56372        },
56373
56374        mousemove: function (e) {
56375            var localCursorPoint = this.group.transformCoordToLocal(e.offsetX, e.offsetY);
56376
56377            resetCursor(this, e, localCursorPoint);
56378
56379            if (this._dragging) {
56380
56381                preventDefault(e);
56382
56383                var eventParams = updateCoverByMouse(this, e, localCursorPoint, false);
56384
56385                eventParams && trigger(this, eventParams);
56386            }
56387        },
56388
56389        mouseup: handleDragEnd //,
56390
56391        // FIXME
56392        // in tooltip, globalout should not be triggered.
56393        // globalout: handleDragEnd
56394    };
56395
56396    function handleDragEnd(e) {
56397        if (this._dragging) {
56398
56399            preventDefault(e);
56400
56401            var localCursorPoint = this.group.transformCoordToLocal(e.offsetX, e.offsetY);
56402            var eventParams = updateCoverByMouse(this, e, localCursorPoint, true);
56403
56404            this._dragging = false;
56405            this._track = [];
56406            this._creatingCover = null;
56407
56408            // trigger event shoule be at final, after procedure will be nested.
56409            eventParams && trigger(this, eventParams);
56410        }
56411    }
56412
56413    /**
56414     * key: brushType
56415     * @type {Object}
56416     */
56417    var coverRenderers = {
56418
56419        lineX: getLineRenderer(0),
56420
56421        lineY: getLineRenderer(1),
56422
56423        rect: {
56424            createCover: function (controller, brushOption) {
56425                return createBaseRectCover(
56426                    curry$2(
56427                        driftRect,
56428                        function (range) {
56429                            return range;
56430                        },
56431                        function (range) {
56432                            return range;
56433                        }
56434                    ),
56435                    controller,
56436                    brushOption,
56437                    ['w', 'e', 'n', 's', 'se', 'sw', 'ne', 'nw']
56438                );
56439            },
56440            getCreatingRange: function (localTrack) {
56441                var ends = getTrackEnds(localTrack);
56442                return formatRectRange(ends[1][0], ends[1][1], ends[0][0], ends[0][1]);
56443            },
56444            updateCoverShape: function (controller, cover, localRange, brushOption) {
56445                updateBaseRect(controller, cover, localRange, brushOption);
56446            },
56447            updateCommon: updateCommon,
56448            contain: mainShapeContain
56449        },
56450
56451        polygon: {
56452            createCover: function (controller, brushOption) {
56453                var cover = new Group();
56454
56455                // Do not use graphic.Polygon because graphic.Polyline do not close the
56456                // border of the shape when drawing, which is a better experience for user.
56457                cover.add(new Polyline({
56458                    name: 'main',
56459                    style: makeStyle(brushOption),
56460                    silent: true
56461                }));
56462
56463                return cover;
56464            },
56465            getCreatingRange: function (localTrack) {
56466                return localTrack;
56467            },
56468            endCreating: function (controller, cover) {
56469                cover.remove(cover.childAt(0));
56470                // Use graphic.Polygon close the shape.
56471                cover.add(new Polygon({
56472                    name: 'main',
56473                    draggable: true,
56474                    drift: curry$2(driftPolygon, controller, cover),
56475                    ondragend: curry$2(trigger, controller, {isEnd: true})
56476                }));
56477            },
56478            updateCoverShape: function (controller, cover, localRange, brushOption) {
56479                cover.childAt(0).setShape({
56480                    points: clipByPanel(controller, cover, localRange)
56481                });
56482            },
56483            updateCommon: updateCommon,
56484            contain: mainShapeContain
56485        }
56486    };
56487
56488    function getLineRenderer(xyIndex) {
56489        return {
56490            createCover: function (controller, brushOption) {
56491                return createBaseRectCover(
56492                    curry$2(
56493                        driftRect,
56494                        function (range) {
56495                            var rectRange = [range, [0, 100]];
56496                            xyIndex && rectRange.reverse();
56497                            return rectRange;
56498                        },
56499                        function (rectRange) {
56500                            return rectRange[xyIndex];
56501                        }
56502                    ),
56503                    controller,
56504                    brushOption,
56505                    [['w', 'e'], ['n', 's']][xyIndex]
56506                );
56507            },
56508            getCreatingRange: function (localTrack) {
56509                var ends = getTrackEnds(localTrack);
56510                var min = mathMin$6(ends[0][xyIndex], ends[1][xyIndex]);
56511                var max = mathMax$6(ends[0][xyIndex], ends[1][xyIndex]);
56512
56513                return [min, max];
56514            },
56515            updateCoverShape: function (controller, cover, localRange, brushOption) {
56516                var otherExtent;
56517                // If brushWidth not specified, fit the panel.
56518                var panel = getPanelByCover(controller, cover);
56519                if (panel !== true && panel.getLinearBrushOtherExtent) {
56520                    otherExtent = panel.getLinearBrushOtherExtent(
56521                        xyIndex, controller._transform
56522                    );
56523                }
56524                else {
56525                    var zr = controller._zr;
56526                    otherExtent = [0, [zr.getWidth(), zr.getHeight()][1 - xyIndex]];
56527                }
56528                var rectRange = [localRange, otherExtent];
56529                xyIndex && rectRange.reverse();
56530
56531                updateBaseRect(controller, cover, rectRange, brushOption);
56532            },
56533            updateCommon: updateCommon,
56534            contain: mainShapeContain
56535        };
56536    }
56537
56538    /*
56539* Licensed to the Apache Software Foundation (ASF) under one
56540* or more contributor license agreements.  See the NOTICE file
56541* distributed with this work for additional information
56542* regarding copyright ownership.  The ASF licenses this file
56543* to you under the Apache License, Version 2.0 (the
56544* "License");
56544 you may not use this file except in compliance
56545* with the License.  You may obtain a copy of the License at
56546*
56547*   http://www.apache.org/licenses/LICENSE-2.0
56548*
56549* Unless required by applicable law or agreed to in writing,
56550* software distributed under the License is distributed on an
56551* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
56552* KIND, either express or implied.  See the License for the
56553* specific language governing permissions and limitations
56554* under the License.
56555*/
56556
56557    function makeRectPanelClipPath(rect) {
56558        rect = normalizeRect(rect);
56559        return function (localPoints, transform) {
56560            return clipPointsByRect(localPoints, rect);
56561        };
56562    }
56563
56564    function makeLinearBrushOtherExtent(rect, specifiedXYIndex) {
56565        rect = normalizeRect(rect);
56566        return function (xyIndex) {
56567            var idx = specifiedXYIndex != null ? specifiedXYIndex : xyIndex;
56568            var brushWidth = idx ? rect.width : rect.height;
56569            var base = idx ? rect.x : rect.y;
56570            return [base, base + (brushWidth || 0)];
56571        };
56572    }
56573
56574    function makeRectIsTargetByCursor(rect, api, targetModel) {
56575        rect = normalizeRect(rect);
56576        return function (e, localCursorPoint, transform) {
56577            return rect.contain(localCursorPoint[0], localCursorPoint[1])
56578                && !onIrrelevantElement(e, api, targetModel);
56579        };
56580    }
56581
56582// Consider width/height is negative.
56583    function normalizeRect(rect) {
56584        return BoundingRect.create(rect);
56585    }
56586
56587    /*
56588* Licensed to the Apache Software Foundation (ASF) under one
56589* or more contributor license agreements.  See the NOTICE file
56590* distributed with this work for additional information
56591* regarding copyright ownership.  The ASF licenses this file
56592* to you under the Apache License, Version 2.0 (the
56593* "License"); you may not use this file except in compliance
56594* with the License.  You may obtain a copy of the License at
56595*
56596*   http://www.apache.org/licenses/LICENSE-2.0
56597*
56598* Unless required by applicable law or agreed to in writing,
56599* software distributed under the License is distributed on an
56600* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
56601* KIND, either express or implied.  See the License for the
56602* specific language governing permissions and limitations
56603* under the License.
56604*/
56605
56606    var elementList = ['axisLine', 'axisTickLabel', 'axisName'];
56607
56608    var AxisView$2 = extendComponentView({
56609
56610        type: 'parallelAxis',
56611
56612        /**
56613         * @override
56614         */
56615        init: function (ecModel, api) {
56616            AxisView$2.superApply(this, 'init', arguments);
56617
56618            /**
56619             * @type {module:echarts/component/helper/BrushController}
56620             */
56621            (this._brushController = new BrushController(api.getZr()))
56622                .on('brush', bind(this._onBrush, this));
56623        },
56624
56625        /**
56626         * @override
56627         */
56628        render: function (axisModel, ecModel, api, payload) {
56629            if (fromAxisAreaSelect(axisModel, ecModel, payload)) {
56630                return;
56631            }
56632
56633            this.axisModel = axisModel;
56634            this.api = api;
56635
56636            this.group.removeAll();
56637
56638            var oldAxisGroup = this._axisGroup;
56639            this._axisGroup = new Group();
56640            this.group.add(this._axisGroup);
56641
56642            if (!axisModel.get('show')) {
56643                return;
56644            }
56645
56646            var coordSysModel = getCoordSysModel(axisModel, ecModel);
56647            var coordSys = coordSysModel.coordinateSystem;
56648
56649            var areaSelectStyle = axisModel.getAreaSelectStyle();
56650            var areaWidth = areaSelectStyle.width;
56651
56652            var dim = axisModel.axis.dim;
56653            var axisLayout = coordSys.getAxisLayout(dim);
56654
56655            var builderOpt = extend(
56656                {strokeContainThreshold: areaWidth},
56657                axisLayout
56658            );
56659
56660            var axisBuilder = new AxisBuilder(axisModel, builderOpt);
56661
56662            each$1(elementList, axisBuilder.add, axisBuilder);
56663
56664            this._axisGroup.add(axisBuilder.getGroup());
56665
56666            this._refreshBrushController(
56667                builderOpt, areaSelectStyle, axisModel, coordSysModel, areaWidth, api
56668            );
56669
56670            var animationModel = (payload && payload.animation === false) ? null : axisModel;
56671            groupTransition(oldAxisGroup, this._axisGroup, animationModel);
56672        },
56673
56674        // /**
56675        //  * @override
56676        //  */
56677        // updateVisual: function (axisModel, ecModel, api, payload) {
56678        //     this._brushController && this._brushController
56679        //         .updateCovers(getCoverInfoList(axisModel));
56680        // },
56681
56682        _refreshBrushController: function (
56683            builderOpt, areaSelectStyle, axisModel, coordSysModel, areaWidth, api
56684        ) {
56685            // After filtering, axis may change, select area needs to be update.
56686            var extent = axisModel.axis.getExtent();
56687            var extentLen = extent[1] - extent[0];
56688            var extra = Math.min(30, Math.abs(extentLen) * 0.1); // Arbitrary value.
56689
56690            // width/height might be negative, which will be
56691            // normalized in BoundingRect.
56692            var rect = BoundingRect.create({
56693                x: extent[0],
56694                y: -areaWidth / 2,
56695                width: extentLen,
56696                height: areaWidth
56697            });
56698            rect.x -= extra;
56699            rect.width += 2 * extra;
56700
56701            this._brushController
56702                .mount({
56703                    enableGlobalPan: true,
56704                    rotation: builderOpt.rotation,
56705                    position: builderOpt.position
56706                })
56707                .setPanels([{
56708                    panelId: 'pl',
56709                    clipPath: makeRectPanelClipPath(rect),
56710                    isTargetByCursor: makeRectIsTargetByCursor(rect, api, coordSysModel),
56711                    getLinearBrushOtherExtent: makeLinearBrushOtherExtent(rect, 0)
56712                }])
56713                .enableBrush({
56714                    brushType: 'lineX',
56715                    brushStyle: areaSelectStyle,
56716                    removeOnClick: true
56717                })
56718                .updateCovers(getCoverInfoList(axisModel));
56719        },
56720
56721        _onBrush: function (coverInfoList, opt) {
56722            // Do not cache these object, because the mey be changed.
56723            var axisModel = this.axisModel;
56724            var axis = axisModel.axis;
56725            var intervals = map(coverInfoList, function (coverInfo) {
56726                return [
56727                    axis.coordToData(coverInfo.range[0], true),
56728                    axis.coordToData(coverInfo.range[1], true)
56729                ];
56730            });
56731
56732            // If realtime is true, action is not dispatched on drag end, because
56733            // the drag end emits the same params with the last drag move event,
56734            // and may have some delay when using touch pad.
56735            if (!axisModel.option.realtime === opt.isEnd || opt.removeOnClick) { // jshint ignore:line
56736                this.api.dispatchAction({
56737                    type: 'axisAreaSelect',
56738                    parallelAxisId: axisModel.id,
56739                    intervals: intervals
56740                });
56741            }
56742        },
56743
56744        /**
56745         * @override
56746         */
56747        dispose: function () {
56748            this._brushController.dispose();
56749        }
56750    });
56751
56752    function fromAxisAreaSelect(axisModel, ecModel, payload) {
56753        return payload
56754            && payload.type === 'axisAreaSelect'
56755            && ecModel.findComponents(
56756                {mainType: 'parallelAxis', query: payload}
56757            )[0] === axisModel;
56758    }
56759
56760    function getCoverInfoList(axisModel) {
56761        var axis = axisModel.axis;
56762        return map(axisModel.activeIntervals, function (interval) {
56763            return {
56764                brushType: 'lineX',
56765                panelId: 'pl',
56766                range: [
56767                    axis.dataToCoord(interval[0], true),
56768                    axis.dataToCoord(interval[1], true)
56769                ]
56770            };
56771        });
56772    }
56773
56774    function getCoordSysModel(axisModel, ecModel) {
56775        return ecModel.getComponent(
56776            'parallel', axisModel.get('parallelIndex')
56777        );
56778    }
56779
56780    /*
56781* Licensed to the Apache Software Foundation (ASF) under one
56782* or more contributor license agreements.  See the NOTICE file
56783* distributed with this work for additional information
56784* regarding copyright ownership.  The ASF licenses this file
56785* to you under the Apache License, Version 2.0 (the
56786* "License"); you may not use this file except in compliance
56787* with the License.  You may obtain a copy of the License at
56788*
56789*   http://www.apache.org/licenses/LICENSE-2.0
56790*
56791* Unless required by applicable law or agreed to in writing,
56792* software distributed under the License is distributed on an
56793* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
56794* KIND, either express or implied.  See the License for the
56795* specific language governing permissions and limitations
56796* under the License.
56797*/
56798
56799    /*
56800* Licensed to the Apache Software Foundation (ASF) under one
56801* or more contributor license agreements.  See the NOTICE file
56802* distributed with this work for additional information
56803* regarding copyright ownership.  The ASF licenses this file
56804* to you under the Apache License, Version 2.0 (the
56805* "License"); you may not use this file except in compliance
56806* with the License.  You may obtain a copy of the License at
56807*
56808*   http://www.apache.org/licenses/LICENSE-2.0
56809*
56810* Unless required by applicable law or agreed to in writing,
56811* software distributed under the License is distributed on an
56812* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
56813* KIND, either express or implied.  See the License for the
56814* specific language governing permissions and limitations
56815* under the License.
56816*/
56817
56818    var CLICK_THRESHOLD = 5; // > 4
56819
56820// Parallel view
56821    extendComponentView({
56822        type: 'parallel',
56823
56824        render: function (parallelModel, ecModel, api) {
56825            this._model = parallelModel;
56826            this._api = api;
56827
56828            if (!this._handlers) {
56829                this._handlers = {};
56830                each$1(handlers, function (handler, eventName) {
56831                    api.getZr().on(eventName, this._handlers[eventName] = bind(handler, this));
56832                }, this);
56833            }
56834
56835            createOrUpdate(
56836                this,
56837                '_throttledDispatchExpand',
56838                parallelModel.get('axisExpandRate'),
56839                'fixRate'
56840            );
56841        },
56842
56843        dispose: function (ecModel, api) {
56844            each$1(this._handlers, function (handler, eventName) {
56845                api.getZr().off(eventName, handler);
56846            });
56847            this._handlers = null;
56848        },
56849
56850        /**
56851         * @param {Object} [opt] If null, cancle the last action triggering for debounce.
56852         */
56853        _throttledDispatchExpand: function (opt) {
56854            this._dispatchExpand(opt);
56855        },
56856
56857        _dispatchExpand: function (opt) {
56858            opt && this._api.dispatchAction(
56859                extend({type: 'parallelAxisExpand'}, opt)
56860            );
56861        }
56862
56863    });
56864
56865    var handlers = {
56866
56867        mousedown: function (e) {
56868            if (checkTrigger(this, 'click')) {
56869                this._mouseDownPoint = [e.offsetX, e.offsetY];
56870            }
56871        },
56872
56873        mouseup: function (e) {
56874            var mouseDownPoint = this._mouseDownPoint;
56875
56876            if (checkTrigger(this, 'click') && mouseDownPoint) {
56877                var point = [e.offsetX, e.offsetY];
56878                var dist = Math.pow(mouseDownPoint[0] - point[0], 2)
56879                    + Math.pow(mouseDownPoint[1] - point[1], 2);
56880
56881                if (dist > CLICK_THRESHOLD) {
56882                    return;
56883                }
56884
56885                var result = this._model.coordinateSystem.getSlidedAxisExpandWindow(
56886                    [e.offsetX, e.offsetY]
56887                );
56888
56889                result.behavior !== 'none' && this._dispatchExpand({
56890                    axisExpandWindow: result.axisExpandWindow
56891                });
56892            }
56893
56894            this._mouseDownPoint = null;
56895        },
56896
56897        mousemove: function (e) {
56898            // Should do nothing when brushing.
56899            if (this._mouseDownPoint || !checkTrigger(this, 'mousemove')) {
56900                return;
56901            }
56902            var model = this._model;
56903            var result = model.coordinateSystem.getSlidedAxisExpandWindow(
56904                [e.offsetX, e.offsetY]
56905            );
56906
56907            var behavior = result.behavior;
56908            behavior === 'jump' && this._throttledDispatchExpand.debounceNextCall(model.get('axisExpandDebounce'));
56909            this._throttledDispatchExpand(
56910                behavior === 'none'
56911                    ? null // Cancle the last trigger, in case that mouse slide out of the area quickly.
56912                    : {
56913                        axisExpandWindow: result.axisExpandWindow,
56914                        // Jumping uses animation, and sliding suppresses animation.
56915                        animation: behavior === 'jump'  ? null : false
56916                    }
56917            );
56918        }
56919    };
56920
56921    function checkTrigger(view, triggerOn) {
56922        var model = view._model;
56923        return model.get('axisExpandable') && model.get('axisExpandTriggerOn') === triggerOn;
56924    }
56925
56926    registerPreprocessor(parallelPreprocessor);
56927
56928    /*
56929* Licensed to the Apache Software Foundation (ASF) under one
56930* or more contributor license agreements.  See the NOTICE file
56931* distributed with this work for additional information
56932* regarding copyright ownership.  The ASF licenses this file
56933* to you under the Apache License, Version 2.0 (the
56934* "License"); you may not use this file except in compliance
56935* with the License.  You may obtain a copy of the License at
56936*
56937*   http://www.apache.org/licenses/LICENSE-2.0
56938*
56939* Unless required by applicable law or agreed to in writing,
56940* software distributed under the License is distributed on an
56941* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
56942* KIND, either express or implied.  See the License for the
56943* specific language governing permissions and limitations
56944* under the License.
56945*/
56946
56947    SeriesModel.extend({
56948
56949        type: 'series.parallel',
56950
56951        dependencies: ['parallel'],
56952
56953        visualColorAccessPath: 'lineStyle.color',
56954
56955        getInitialData: function (option, ecModel) {
56956            var source = this.getSource();
56957
56958            setEncodeAndDimensions(source, this);
56959
56960            return createListFromArray(source, this);
56961        },
56962
56963        /**
56964         * User can get data raw indices on 'axisAreaSelected' event received.
56965         *
56966         * @public
56967         * @param {string} activeState 'active' or 'inactive' or 'normal'
56968         * @return {Array.<number>} Raw indices
56969         */
56970        getRawIndicesByActiveState: function (activeState) {
56971            var coordSys = this.coordinateSystem;
56972            var data = this.getData();
56973            var indices = [];
56974
56975            coordSys.eachActiveState(data, function (theActiveState, dataIndex) {
56976                if (activeState === theActiveState) {
56977                    indices.push(data.getRawIndex(dataIndex));
56978                }
56979            });
56980
56981            return indices;
56982        },
56983
56984        defaultOption: {
56985            zlevel: 0,                  // 一级层å
56986            z: 2,                       // 二级层å
56987
56988            coordinateSystem: 'parallel',
56989            parallelIndex: 0,
56990
56991            label: {
56992                show: false
56993            },
56994
56995            inactiveOpacity: 0.05,
56996            activeOpacity: 1,
56997
56998            lineStyle: {
56999                width: 1,
57000                opacity: 0.45,
57001                type: 'solid'
57002            },
57003            emphasis: {
57004                label: {
57005                    show: false
57006                }
57007            },
57008
57009            progressive: 500,
57010            smooth: false, // true | false | number
57011
57012            animationEasing: 'linear'
57013        }
57014    });
57015
57016    function setEncodeAndDimensions(source, seriesModel) {
57017        // The mapping of parallelAxis dimension to data dimension can
57018        // be specified in parallelAxis.option.dim. For example, if
57019        // parallelAxis.option.dim is 'dim3', it mapping to the third
57020        // dimension of data. But `data.encode` has higher priority.
57021        // Moreover, parallelModel.dimension should not be regarded as data
57022        // dimensions. Consider dimensions = ['dim4', 'dim2', 'dim6'];
57023
57024        if (source.encodeDefine) {
57025            return;
57026        }
57027
57028        var parallelModel = seriesModel.ecModel.getComponent(
57029            'parallel', seriesModel.get('parallelIndex')
57030        );
57031        if (!parallelModel) {
57032            return;
57033        }
57034
57035        var encodeDefine = source.encodeDefine = createHashMap();
57036        each$1(parallelModel.dimensions, function (axisDim) {
57037            var dataDimIndex = convertDimNameToNumber(axisDim);
57038            encodeDefine.set(axisDim, dataDimIndex);
57039        });
57040    }
57041
57042    function convertDimNameToNumber(dimName) {
57043        return +dimName.replace('dim', '');
57044    }
57045
57046    /*
57047* Licensed to the Apache Software Foundation (ASF) under one
57048* or more contributor license agreements.  See the NOTICE file
57049* distributed with this work for additional information
57050* regarding copyright ownership.  The ASF licenses this file
57051* to you under the Apache License, Version 2.0 (the
57052* "License"); you may not use this file except in compliance
57053* with the License.  You may obtain a copy of the License at
57054*
57055*   http://www.apache.org/licenses/LICENSE-2.0
57056*
57057* Unless required by applicable law or agreed to in writing,
57058* software distributed under the License is distributed on an
57059* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
57060* KIND, either express or implied.  See the License for the
57061* specific language governing permissions and limitations
57062* under the License.
57063*/
57064
57065    var DEFAULT_SMOOTH = 0.3;
57066
57067    var ParallelView = Chart.extend({
57068
57069        type: 'parallel',
57070
57071        init: function () {
57072
57073            /**
57074             * @type {module:zrender/container/Group}
57075             * @private
57076             */
57077            this._dataGroup = new Group();
57078
57079            this.group.add(this._dataGroup);
57080
57081            /**
57082             * @type {module:echarts/data/List}
57083             */
57084            this._data;
57085
57086            /**
57087             * @type {boolean}
57088             */
57089            this._initialized;
57090        },
57091
57092        /**
57093         * @override
57094         */
57095        render: function (seriesModel, ecModel, api, payload) {
57096            var dataGroup = this._dataGroup;
57097            var data = seriesModel.getData();
57098            var oldData = this._data;
57099            var coordSys = seriesModel.coordinateSystem;
57100            var dimensions = coordSys.dimensions;
57101            var seriesScope = makeSeriesScope$2(seriesModel);
57102
57103            data.diff(oldData)
57104                .add(add)
57105                .update(update)
57106                .remove(remove)
57107                .execute();
57108
57109            function add(newDataIndex) {
57110                var line = addEl(data, dataGroup, newDataIndex, dimensions, coordSys);
57111                updateElCommon(line, data, newDataIndex, seriesScope);
57112            }
57113
57114            function update(newDataIndex, oldDataIndex) {
57115                var line = oldData.getItemGraphicEl(oldDataIndex);
57116                var points = createLinePoints(data, newDataIndex, dimensions, coordSys);
57117                data.setItemGraphicEl(newDataIndex, line);
57118                var animationModel = (payload && payload.animation === false) ? null : seriesModel;
57119                updateProps(line, {shape: {points: points}}, animationModel, newDataIndex);
57120
57121                updateElCommon(line, data, newDataIndex, seriesScope);
57122            }
57123
57124            function remove(oldDataIndex) {
57125                var line = oldData.getItemGraphicEl(oldDataIndex);
57126                dataGroup.remove(line);
57127            }
57128
57129            // First create
57130            if (!this._initialized) {
57131                this._initialized = true;
57132                var clipPath = createGridClipShape$1(
57133                    coordSys, seriesModel, function () {
57134                        // Callback will be invoked immediately if there is no animation
57135                        setTimeout(function () {
57136                            dataGroup.removeClipPath();
57137                        });
57138                    }
57139                );
57140                dataGroup.setClipPath(clipPath);
57141            }
57142
57143            this._data = data;
57144        },
57145
57146        incrementalPrepareRender: function (seriesModel, ecModel, api) {
57147            this._initialized = true;
57148            this._data = null;
57149            this._dataGroup.removeAll();
57150        },
57151
57152        incrementalRender: function (taskParams, seriesModel, ecModel) {
57153            var data = seriesModel.getData();
57154            var coordSys = seriesModel.coordinateSystem;
57155            var dimensions = coordSys.dimensions;
57156            var seriesScope = makeSeriesScope$2(seriesModel);
57157
57158            for (var dataIndex = taskParams.start; dataIndex < taskParams.end; dataIndex++) {
57159                var line = addEl(data, this._dataGroup, dataIndex, dimensions, coordSys);
57160                line.incremental = true;
57161                updateElCommon(line, data, dataIndex, seriesScope);
57162            }
57163        },
57164
57165        dispose: function () {},
57166
57167        // _renderForProgressive: function (seriesModel) {
57168        //     var dataGroup = this._dataGroup;
57169        //     var data = seriesModel.getData();
57170        //     var oldData = this._data;
57171        //     var coordSys = seriesModel.coordinateSystem;
57172        //     var dimensions = coordSys.dimensions;
57173        //     var option = seriesModel.option;
57174        //     var progressive = option.progressive;
57175        //     var smooth = option.smooth ? SMOOTH : null;
57176
57177        //     // In progressive animation is disabled, so use simple data diff,
57178        //     // which effects performance less.
57179        //     // (Typically performance for data with length 7000+ like:
57180        //     // simpleDiff: 60ms, addEl: 184ms,
57181        //     // in RMBP 2.4GHz intel i7, OSX 10.9 chrome 50.0.2661.102 (64-bit))
57182        //     if (simpleDiff(oldData, data, dimensions)) {
57183        //         dataGroup.removeAll();
57184        //         data.each(function (dataIndex) {
57185        //             addEl(data, dataGroup, dataIndex, dimensions, coordSys);
57186        //         });
57187        //     }
57188
57189        //     updateElCommon(data, progressive, smooth);
57190
57191        //     // Consider switch between progressive and not.
57192        //     data.__plProgressive = true;
57193        //     this._data = data;
57194        // },
57195
57196        /**
57197         * @override
57198         */
57199        remove: function () {
57200            this._dataGroup && this._dataGroup.removeAll();
57201            this._data = null;
57202        }
57203    });
57204
57205    function createGridClipShape$1(coordSys, seriesModel, cb) {
57206        var parallelModel = coordSys.model;
57207        var rect = coordSys.getRect();
57208        var rectEl = new Rect({
57209            shape: {
57210                x: rect.x,
57211                y: rect.y,
57212                width: rect.width,
57213                height: rect.height
57214            }
57215        });
57216
57217        var dim = parallelModel.get('layout') === 'horizontal' ? 'width' : 'height';
57218        rectEl.setShape(dim, 0);
57219        initProps(rectEl, {
57220            shape: {
57221                width: rect.width,
57222                height: rect.height
57223            }
57224        }, seriesModel, cb);
57225        return rectEl;
57226    }
57227
57228    function createLinePoints(data, dataIndex, dimensions, coordSys) {
57229        var points = [];
57230        for (var i = 0; i < dimensions.length; i++) {
57231            var dimName = dimensions[i];
57232            var value = data.get(data.mapDimension(dimName), dataIndex);
57233            if (!isEmptyValue(value, coordSys.getAxis(dimName).type)) {
57234                points.push(coordSys.dataToPoint(value, dimName));
57235            }
57236        }
57237        return points;
57238    }
57239
57240    function addEl(data, dataGroup, dataIndex, dimensions, coordSys) {
57241        var points = createLinePoints(data, dataIndex, dimensions, coordSys);
57242        var line = new Polyline({
57243            shape: {points: points},
57244            silent: true,
57245            z2: 10
57246        });
57247        dataGroup.add(line);
57248        data.setItemGraphicEl(dataIndex, line);
57249        return line;
57250    }
57251
57252    function makeSeriesScope$2(seriesModel) {
57253        var smooth = seriesModel.get('smooth', true);
57254        smooth === true && (smooth = DEFAULT_SMOOTH);
57255        return {
57256            lineStyle: seriesModel.getModel('lineStyle').getLineStyle(),
57257            smooth: smooth != null ? smooth : DEFAULT_SMOOTH
57258        };
57259    }
57260
57261    function updateElCommon(el, data, dataIndex, seriesScope) {
57262        var lineStyle = seriesScope.lineStyle;
57263
57264        if (data.hasItemOption) {
57265            var lineStyleModel = data.getItemModel(dataIndex).getModel('lineStyle');
57266            lineStyle = lineStyleModel.getLineStyle();
57267        }
57268
57269        el.useStyle(lineStyle);
57270
57271        var elStyle = el.style;
57272        elStyle.fill = null;
57273        // lineStyle.color have been set to itemVisual in module:echarts/visual/seriesColor.
57274        elStyle.stroke = data.getItemVisual(dataIndex, 'color');
57275        // lineStyle.opacity have been set to itemVisual in parallelVisual.
57276        elStyle.opacity = data.getItemVisual(dataIndex, 'opacity');
57277
57278        seriesScope.smooth && (el.shape.smooth = seriesScope.smooth);
57279    }
57280
57281// function simpleDiff(oldData, newData, dimensions) {
57282//     var oldLen;
57283//     if (!oldData
57284//         || !oldData.__plProgressive
57285//         || (oldLen = oldData.count()) !== newData.count()
57286//     ) {
57287//         return true;
57288//     }
57289
57290//     var dimLen = dimensions.length;
57291//     for (var i = 0; i < oldLen; i++) {
57292//         for (var j = 0; j < dimLen; j++) {
57293//             if (oldData.get(dimensions[j], i) !== newData.get(dimensions[j], i)) {
57294//                 return true;
57295//             }
57296//         }
57297//     }
57298
57299//     return false;
57300// }
57301
57302// FIXME
57303// 公用方法?
57304    function isEmptyValue(val, axisType) {
57305        return axisType === 'category'
57306            ? val == null
57307            : (val == null || isNaN(val)); // axisType === 'value'
57308    }
57309
57310    /*
57311* Licensed to the Apache Software Foundation (ASF) under one
57312* or more contributor license agreements.  See the NOTICE file
57313* distributed with this work for additional information
57314* regarding copyright ownership.  The ASF licenses this file
57315* to you under the Apache License, Version 2.0 (the
57316* "License"); you may not use this file except in compliance
57317* with the License.  You may obtain a copy of the License at
57318*
57319*   http://www.apache.org/licenses/LICENSE-2.0
57320*
57321* Unless required by applicable law or agreed to in writing,
57322* software distributed under the License is distributed on an
57323* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
57324* KIND, either express or implied.  See the License for the
57325* specific language governing permissions and limitations
57326* under the License.
57327*/
57328
57329
57330    var opacityAccessPath$1 = ['lineStyle', 'normal', 'opacity'];
57331
57332    var parallelVisual = {
57333
57334        seriesType: 'parallel',
57335
57336        reset: function (seriesModel, ecModel, api) {
57337
57338            var itemStyleModel = seriesModel.getModel('itemStyle');
57339            var lineStyleModel = seriesModel.getModel('lineStyle');
57340            var globalColors = ecModel.get('color');
57341
57342            var color = lineStyleModel.get('color')
57343                || itemStyleModel.get('color')
57344                || globalColors[seriesModel.seriesIndex % globalColors.length];
57345            var inactiveOpacity = seriesModel.get('inactiveOpacity');
57346            var activeOpacity = seriesModel.get('activeOpacity');
57347            var lineStyle = seriesModel.getModel('lineStyle').getLineStyle();
57348
57349            var coordSys = seriesModel.coordinateSystem;
57350            var data = seriesModel.getData();
57351
57352            var opacityMap = {
57353                normal: lineStyle.opacity,
57354                active: activeOpacity,
57355                inactive: inactiveOpacity
57356            };
57357
57358            data.setVisual('color', color);
57359
57360            function progress(params, data) {
57361                coordSys.eachActiveState(data, function (activeState, dataIndex) {
57362                    var opacity = opacityMap[activeState];
57363                    if (activeState === 'normal' && data.hasItemOption) {
57364                        var itemOpacity = data.getItemModel(dataIndex).get(opacityAccessPath$1, true);
57365                        itemOpacity != null && (opacity = itemOpacity);
57366                    }
57367                    data.setItemVisual(dataIndex, 'opacity', opacity);
57368                }, params.start, params.end);
57369            }
57370
57371            return {progress: progress};
57372        }
57373    };
57374
57375    /*
57376* Licensed to the Apache Software Foundation (ASF) under one
57377* or more contributor license agreements.  See the NOTICE file
57378* distributed with this work for additional information
57379* regarding copyright ownership.  The ASF licenses this file
57380* to you under the Apache License, Version 2.0 (the
57381* "License"); you may not use this file except in compliance
57382* with the License.  You may obtain a copy of the License at
57383*
57384*   http://www.apache.org/licenses/LICENSE-2.0
57385*
57386* Unless required by applicable law or agreed to in writing,
57387* software distributed under the License is distributed on an
57388* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
57389* KIND, either express or implied.  See the License for the
57390* specific language governing permissions and limitations
57391* under the License.
57392*/
57393
57394    registerVisual(parallelVisual);
57395
57396    /*
57397* Licensed to the Apache Software Foundation (ASF) under one
57398* or more contributor license agreements.  See the NOTICE file
57399* distributed with this work for additional information
57400* regarding copyright ownership.  The ASF licenses this file
57401* to you under the Apache License, Version 2.0 (the
57402* "License");
57402 you may not use this file except in compliance
57403* with the License.  You may obtain a copy of the License at
57404*
57405*   http://www.apache.org/licenses/LICENSE-2.0
57406*
57407* Unless required by applicable law or agreed to in writing,
57408* software distributed under the License is distributed on an
57409* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
57410* KIND, either express or implied.  See the License for the
57411* specific language governing permissions and limitations
57412* under the License.
57413*/
57414
57415    /**
57416     * @file Get initial data and define sankey view's series model
57417     * @author Deqing Li([email protected])
57418     */
57419
57420    var SankeySeries = SeriesModel.extend({
57421
57422        type: 'series.sankey',
57423
57424        layoutInfo: null,
57425
57426        /**
57427         * Init a graph data structure from data in option series
57428         *
57429         * @param  {Object} option  the object used to config echarts view
57430         * @return {module:echarts/data/List} storage initial data
57431         */
57432        getInitialData: function (option) {
57433            var links = option.edges || option.links;
57434            var nodes = option.data || option.nodes;
57435            if (nodes && links) {
57436                var graph = createGraphFromNodeEdge(nodes, links, this, true);
57437                return graph.data;
57438            }
57439        },
57440
57441        setNodePosition: function (dataIndex, localPosition) {
57442            var dataItem = this.option.data[dataIndex];
57443            dataItem.localX = localPosition[0];
57444            dataItem.localY = localPosition[1];
57445        },
57446
57447        /**
57448         * Return the graphic data structure
57449         *
57450         * @return {module:echarts/data/Graph} graphic data structure
57451         */
57452        getGraph: function () {
57453            return this.getData().graph;
57454        },
57455
57456        /**
57457         * Get edge data of graphic data structure
57458         *
57459         * @return {module:echarts/data/List} data structure of list
57460         */
57461        getEdgeData: function () {
57462            return this.getGraph().edgeData;
57463        },
57464
57465        /**
57466         * @override
57467         */
57468        formatTooltip: function (dataIndex, multipleSeries, dataType) {
57469            // dataType === 'node' or empty do not show tooltip by default
57470            if (dataType === 'edge') {
57471                var params = this.getDataParams(dataIndex, dataType);
57472                var rawDataOpt = params.data;
57473                var html = rawDataOpt.source + ' -- ' + rawDataOpt.target;
57474                if (params.value) {
57475                    html += ' : ' + params.value;
57476                }
57477                return encodeHTML(html);
57478            }
57479
57480            return SankeySeries.superCall(this, 'formatTooltip', dataIndex, multipleSeries);
57481        },
57482
57483        defaultOption: {
57484            zlevel: 0,
57485            z: 2,
57486
57487            coordinateSystem: 'view',
57488
57489            layout: null,
57490
57491            // the position of the whole view
57492            left: '5%',
57493            top: '5%',
57494            right: '20%',
57495            bottom: '5%',
57496
57497            // the dx of the node
57498            nodeWidth: 20,
57499
57500            // the vertical distance between two nodes
57501            nodeGap: 8,
57502
57503            // control if the node can move or not
57504            draggable: true,
57505
57506            // the number of iterations to change the position of the node
57507            layoutIterations: 32,
57508
57509            label: {
57510                show: true,
57511                position: 'right',
57512                color: '#000',
57513                fontSize: 12
57514            },
57515
57516            itemStyle: {
57517                borderWidth: 1,
57518                borderColor: '#333'
57519            },
57520
57521            lineStyle: {
57522                color: '#314656',
57523                opacity: 0.2,
57524                curveness: 0.5
57525            },
57526
57527            emphasis: {
57528                label: {
57529                    show: true
57530                },
57531                lineStyle: {
57532                    opacity: 0.6
57533                }
57534            },
57535
57536            animationEasing: 'linear',
57537
57538            animationDuration: 1000
57539        }
57540
57541    });
57542
57543    /*
57544* Licensed to the Apache Software Foundation (ASF) under one
57545* or more contributor license agreements.  See the NOTICE file
57546* distributed with this work for additional information
57547* regarding copyright ownership.  The ASF licenses this file
57548* to you under the Apache License, Version 2.0 (the
57549* "License"); you may not use this file except in compliance
57550* with the License.  You may obtain a copy of the License at
57551*
57552*   http://www.apache.org/licenses/LICENSE-2.0
57553*
57554* Unless required by applicable law or agreed to in writing,
57555* software distributed under the License is distributed on an
57556* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
57557* KIND, either express or implied.  See the License for the
57558* specific language governing permissions and limitations
57559* under the License.
57560*/
57561
57562    /**
57563     * @file  The file used to draw sankey view
57564     * @author  Deqing Li([email protected])
57565     */
57566
57567    var SankeyShape = extendShape({
57568        shape: {
57569            x1: 0, y1: 0,
57570            x2: 0, y2: 0,
57571            cpx1: 0, cpy1: 0,
57572            cpx2: 0, cpy2: 0,
57573
57574            extent: 0
57575        },
57576
57577        buildPath: function (ctx, shape) {
57578            var halfExtent = shape.extent / 2;
57579            ctx.moveTo(shape.x1, shape.y1 - halfExtent);
57580            ctx.bezierCurveTo(
57581                shape.cpx1, shape.cpy1 - halfExtent,
57582                shape.cpx2, shape.cpy2 - halfExtent,
57583                shape.x2, shape.y2 - halfExtent
57584            );
57585            ctx.lineTo(shape.x2, shape.y2 + halfExtent);
57586            ctx.bezierCurveTo(
57587                shape.cpx2, shape.cpy2 + halfExtent,
57588                shape.cpx1, shape.cpy1 + halfExtent,
57589                shape.x1, shape.y1 + halfExtent
57590            );
57591            ctx.closePath();
57592        }
57593    });
57594
57595    extendChartView({
57596
57597        type: 'sankey',
57598
57599        /**
57600         * @private
57601         * @type {module:echarts/chart/sankey/SankeySeries}
57602         */
57603        _model: null,
57604
57605        render: function (seriesModel, ecModel, api) {
57606            var graph = seriesModel.getGraph();
57607            var group = this.group;
57608            var layoutInfo = seriesModel.layoutInfo;
57609            // view width
57610            var width = layoutInfo.width;
57611            // view height
57612            var height = layoutInfo.height;
57613
57614            var nodeData = seriesModel.getData();
57615            var edgeData = seriesModel.getData('edge');
57616
57617            this._model = seriesModel;
57618
57619            group.removeAll();
57620
57621            group.attr('position', [layoutInfo.x, layoutInfo.y]);
57622
57623            // generate a bezire Curve for each edge
57624            graph.eachEdge(function (edge) {
57625                var curve = new SankeyShape();
57626                curve.dataIndex = edge.dataIndex;
57627                curve.seriesIndex = seriesModel.seriesIndex;
57628                curve.dataType = 'edge';
57629                var lineStyleModel = edge.getModel('lineStyle');
57630                var curvature = lineStyleModel.get('curveness');
57631                var n1Layout = edge.node1.getLayout();
57632                var node1Model =edge.node1.getModel();
57633                var dragX1 = node1Model.get('localX');
57634                var dragY1 = node1Model.get('localY');
57635                var n2Layout = edge.node2.getLayout();
57636                var node2Model = edge.node2.getModel();
57637                var dragX2 = node2Model.get('localX');
57638                var dragY2 = node2Model.get('localY');
57639                var edgeLayout = edge.getLayout();
57640
57641                curve.shape.extent = Math.max(1, edgeLayout.dy);
57642
57643                var x1 = (dragX1 != null ? dragX1 * width : n1Layout.x) + n1Layout.dx;
57644                var y1 = (dragY1 != null ? dragY1 * height : n1Layout.y) + edgeLayout.sy + edgeLayout.dy / 2;
57645                var x2 = dragX2 != null ? dragX2 * width : n2Layout.x;
57646                var y2 = (dragY2 != null ? dragY2 * height : n2Layout.y) + edgeLayout.ty + edgeLayout.dy / 2;
57647                var cpx1 = x1 * (1 - curvature) + x2 * curvature;
57648                var cpy1 = y1;
57649                var cpx2 = x1 * curvature + x2 * (1 - curvature);
57650                var cpy2 = y2;
57651
57652                curve.setShape({
57653                    x1: x1,
57654                    y1: y1,
57655                    x2: x2,
57656                    y2: y2,
57657                    cpx1: cpx1,
57658                    cpy1: cpy1,
57659                    cpx2: cpx2,
57660                    cpy2: cpy2
57661                });
57662
57663                curve.setStyle(lineStyleModel.getItemStyle());
57664                // Special color, use source node color or target node color
57665                switch (curve.style.fill) {
57666                    case 'source':
57667                        curve.style.fill = edge.node1.getVisual('color');
57668                        break;
57669                    case 'target':
57670                        curve.style.fill = edge.node2.getVisual('color');
57671                        break;
57672                }
57673
57674                setHoverStyle(curve, edge.getModel('emphasis.lineStyle').getItemStyle());
57675
57676                group.add(curve);
57677
57678                edgeData.setItemGraphicEl(edge.dataIndex, curve);
57679            });
57680
57681            // generate a rect for each node
57682            graph.eachNode(function (node) {
57683                var layout = node.getLayout();
57684                var itemModel = node.getModel();
57685                var dragX = itemModel.get('localX');
57686                var dragY = itemModel.get('localY');
57687                var labelModel = itemModel.getModel('label');
57688                var labelHoverModel = itemModel.getModel('emphasis.label');
57689
57690                var rect = new Rect({
57691                    shape: {
57692                        x: dragX != null ? dragX * width : layout.x,
57693                        y: dragY != null ? dragY * height : layout.y,
57694                        width: layout.dx,
57695                        height: layout.dy
57696                    },
57697                    style: itemModel.getModel('itemStyle').getItemStyle()
57698                });
57699
57700                var hoverStyle = node.getModel('emphasis.itemStyle').getItemStyle();
57701
57702                setLabelStyle(
57703                    rect.style, hoverStyle, labelModel, labelHoverModel,
57704                    {
57705                        labelFetcher: seriesModel,
57706                        labelDataIndex: node.dataIndex,
57707                        defaultText: node.id,
57708                        isRectText: true
57709                    }
57710                );
57711
57712                rect.setStyle('fill', node.getVisual('color'));
57713
57714                setHoverStyle(rect, hoverStyle);
57715
57716                group.add(rect);
57717
57718                nodeData.setItemGraphicEl(node.dataIndex, rect);
57719
57720                rect.dataType = 'node';
57721            });
57722
57723            var draggable = seriesModel.get('draggable');
57724            if (draggable) {
57725                nodeData.eachItemGraphicEl(function (el, dataIndex) {
57726                    el.drift = function (dx, dy) {
57727                        this.shape.x += dx;
57728                        this.shape.y += dy;
57729                        this.dirty();
57730                        api.dispatchAction({
57731                            type: 'dragNode',
57732                            seriesId: seriesModel.id,
57733                            dataIndex: nodeData.getRawIndex(dataIndex),
57734                            localX: this.shape.x / width,
57735                            localY: this.shape.y / height
57736                        });
57737                    };
57738
57739                    el.draggable = true;
57740                    el.cursor = 'move';
57741                });
57742            }
57743
57744            if (!this._data && seriesModel.get('animation')) {
57745                group.setClipPath(createGridClipShape$2(group.getBoundingRect
57745(), seriesModel, function () {
57746                    group.removeClipPath();
57747                }));
57748            }
57749
57750            this._data = seriesModel.getData();
57751        },
57752
57753        dispose: function () {}
57754    });
57755
57756// add animation to the view
57757    function createGridClipShape$2(rect, seriesModel, cb) {
57758        var rectEl = new Rect({
57759            shape: {
57760                x: rect.x - 10,
57761                y: rect.y - 10,
57762                width: 0,
57763                height: rect.height + 20
57764            }
57765        });
57766        initProps(rectEl, {
57767            shape: {
57768                width: rect.width + 20,
57769                height: rect.height + 20
57770            }
57771        }, seriesModel, cb);
57772
57773        return rectEl;
57774    }
57775
57776    /*
57777* Licensed to the Apache Software Foundation (ASF) under one
57778* or more contributor license agreements.  See the NOTICE file
57779* distributed with this work for additional information
57780* regarding copyright ownership.  The ASF licenses this file
57781* to you under the Apache License, Version 2.0 (the
57782* "License"); you may not use this file except in compliance
57783* with the License.  You may obtain a copy of the License at
57784*
57785*   http://www.apache.org/licenses/LICENSE-2.0
57786*
57787* Unless required by applicable law or agreed to in writing,
57788* software distributed under the License is distributed on an
57789* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
57790* KIND, either express or implied.  See the License for the
57791* specific language governing permissions and limitations
57792* under the License.
57793*/
57794
57795    registerAction({
57796        type: 'dragNode',
57797        event: 'dragNode',
57798        // here can only use 'update' now, other value is not support in echarts.
57799        update: 'update'
57800    }, function (payload, ecModel) {
57801        ecModel.eachComponent({mainType: 'series', subType: 'sankey', query: payload}, function (seriesModel) {
57802            seriesModel.setNodePosition(payload.dataIndex, [payload.localX, payload.localY]);
57803        });
57804    });
57805
57806    /*
57807* Licensed to the Apache Software Foundation (ASF) under one
57808* or more contributor license agreements.  See the NOTICE file
57809* distributed with this work for additional information
57810* regarding copyright ownership.  The ASF licenses this file
57811* to you under the Apache License, Version 2.0 (the
57812* "License"); you may not use this file except in compliance
57813* with the License.  You may obtain a copy of the License at
57814*
57815*   http://www.apache.org/licenses/LICENSE-2.0
57816*
57817* Unless required by applicable law or agreed to in writing,
57818* software distributed under the License is distributed on an
57819* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
57820* KIND, either express or implied.  See the License for the
57821* specific language governing permissions and limitations
57822* under the License.
57823*/
57824
57825    /*
57826* The implementation references to d3.js. The use of the source
57827* code of this file is also subject to the terms and consitions
57828* of its license (BSD-3Clause, see <echarts/src/licenses/LICENSE-d3>).
57829*/
57830
57831
57832    /**
57833     * nest helper used to group by the array.
57834     * can specified the keys and sort the keys.
57835     */
57836    function nest() {
57837
57838        var keysFunction = [];
57839        var sortKeysFunction = [];
57840
57841        /**
57842         * map an Array into the mapObject.
57843         * @param {Array} array
57844         * @param {number} depth
57845         */
57846        function map$$1(array, depth) {
57847            if (depth >= keysFunction.length) {
57848                return array;
57849            }
57850            var i = -1;
57851            var n = array.length;
57852            var keyFunction = keysFunction[depth++];
57853            var mapObject = {};
57854            var valuesByKey = {};
57855
57856            while (++i < n) {
57857                var keyValue = keyFunction(array[i]);
57858                var values = valuesByKey[keyValue];
57859
57860                if (values) {
57861                    values.push(array[i]);
57862                }
57863                else {
57864                    valuesByKey[keyValue] = [array[i]];
57865                }
57866            }
57867
57868            each$1(valuesByKey, function (value, key) {
57869                mapObject[key] = map$$1(value, depth);
57870            });
57871
57872            return mapObject;
57873        }
57874
57875        /**
57876         * transform the Map Object to multidimensional Array
57877         * @param {Object} map
57878         * @param {number} depth
57879         */
57880        function entriesMap(mapObject, depth) {
57881            if (depth >= keysFunction.length) {
57882                return mapObject;
57883            }
57884            var array = [];
57885            var sortKeyFunction = sortKeysFunction[depth++];
57886
57887            each$1(mapObject, function (value, key) {
57888                array.push({
57889                    key: key, values: entriesMap(value, depth)
57890                });
57891            });
57892
57893            if (sortKeyFunction) {
57894                return array.sort(function (a, b) {
57895                    return sortKeyFunction(a.key, b.key);
57896                });
57897            }
57898            else {
57899                return array;
57900            }
57901        }
57902
57903        return {
57904            /**
57905             * specified the key to groupby the arrays.
57906             * users can specified one more keys.
57907             * @param {Function} d
57908             */
57909            key: function (d) {
57910                keysFunction.push(d);
57911                return this;
57912            },
57913
57914            /**
57915             * specified the comparator to sort the keys
57916             * @param {Function} order
57917             */
57918            sortKeys: function (order) {
57919                sortKeysFunction[keysFunction.length - 1] = order;
57920                return this;
57921            },
57922
57923            /**
57924             * the array to be grouped by.
57925             * @param {Array} array
57926             */
57927            entries: function (array) {
57928                return entriesMap(map$$1(array, 0), 0);
57929            }
57930        };
57931    }
57932
57933    /*
57934* Licensed to the Apache Software Foundation (ASF) under one
57935* or more contributor license agreements.  See the NOTICE file
57936* distributed with this work for additional information
57937* regarding copyright ownership.  The ASF licenses this file
57938* to you under the Apache License, Version 2.0 (the
57939* "License"); you may not use this file except in compliance
57940* with the License.  You may obtain a copy of the License at
57941*
57942*   http://www.apache.org/licenses/LICENSE-2.0
57943*
57944* Unless required by applicable law or agreed to in writing,
57945* software distributed under the License is distributed on an
57946* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
57947* KIND, either express or implied.  See the License for the
57948* specific language governing permissions and limitations
57949* under the License.
57950*/
57951
57952    /**
57953     * @file The layout algorithm of sankey view
57954     * @author Deqing Li([email protected])
57955     */
57956
57957    var sankeyLayout = function (ecModel, api, payload) {
57958
57959        ecModel.eachSeriesByType('sankey', function (seriesModel) {
57960
57961            var nodeWidth = seriesModel.get('nodeWidth');
57962            var nodeGap = seriesModel.get('nodeGap');
57963
57964            var layoutInfo = getViewRect$3(seriesModel, api);
57965
57966            seriesModel.layoutInfo = layoutInfo;
57967
57968            var width = layoutInfo.width;
57969            var height = layoutInfo.height;
57970
57971            var graph = seriesModel.getGraph();
57972
57973            var nodes = graph.nodes;
57974            var edges = graph.edges;
57975
57976            computeNodeValues(nodes);
57977
57978            var filteredNodes = filter(nodes, function (node) {
57979                return node.getLayout().value === 0;
57980            });
57981
57982            var iterations = filteredNodes.length !== 0
57983                ? 0 : seriesModel.get('layoutIterations');
57984
57985            layoutSankey(nodes, edges, nodeWidth, nodeGap, width, height, iterations);
57986        });
57987    };
57988
57989    /**
57990     * Get the layout position of the whole view
57991     *
57992     * @param {module:echarts/model/Series} seriesModel  the model object of sankey series
57993     * @param {module:echarts/ExtensionAPI} api  provide the API list that the developer can call
57994     * @return {module:zrender/core/BoundingRect}  size of rect to draw the sankey view
57995     */
57996    function getViewRect$3(seriesModel, api) {
57997        return getLayoutRect(
57998            seriesModel.getBoxLayoutParams(), {
57999                width: api.getWidth(),
58000                height: api.getHeight()
58001            }
58002        );
58003    }
58004
58005    function layoutSankey(nodes, edges, nodeWidth, nodeGap, width, height, iterations) {
58006        computeNodeBreadths(nodes, edges, nodeWidth, width);
58007        computeNodeDepths(nodes, edges, height, nodeGap, iterations);
58008        computeEdgeDepths(nodes);
58009    }
58010
58011    /**
58012     * Compute the value of each node by summing the associated edge's value
58013     *
58014     * @param {module:echarts/data/Graph~Node} nodes  node of sankey view
58015     */
58016    function computeNodeValues(nodes) {
58017        each$1(nodes, function (node) {
58018            var value1 = sum(node.outEdges, getEdgeValue);
58019            var value2 = sum(node.inEdges, getEdgeValue);
58020            var value = Math.max(value1, value2);
58021            node.setLayout({value: value}, true);
58022        });
58023    }
58024
58025    /**
58026     * Compute the x-position for each node.
58027     *
58028     * Here we use Kahn algorithm to detect cycle when we traverse
58029     * the node to computer the initial x position.
58030     *
58031     * @param {module:echarts/data/Graph~Node} nodes  node of sankey view
58032     * @param  {number} nodeWidth  the dx of the node
58033     * @param  {number} width  the whole width of the area to draw the view
58034     */
58035    function computeNodeBreadths(nodes, edges, nodeWidth, width) {
58036        // Used to mark whether the edge is deleted. if it is deleted,
58037        // the value is 0, otherwise it is 1.
58038        var remainEdges = [];
58039        // Storage each node's indegree.
58040        var indegreeArr = [];
58041        //Used to storage the node with indegree is equal to 0.
58042        var zeroIndegrees = [];
58043        var nextNode = [];
58044        var x = 0;
58045        var kx = 0;
58046
58047        for (var i = 0; i < edges.length; i++) {
58048            remainEdges[i] = 1;
58049        }
58050
58051        for (var i = 0; i < nodes.length; i++) {
58052            indegreeArr[i] = nodes[i].inEdges.length;
58053            if (indegreeArr[i] === 0) {
58054                zeroIndegrees.push(nodes[i]);
58055            }
58056        }
58057
58058        while (zeroIndegrees.length) {
58059            each$1(zeroIndegrees, function (node) {
58060                node.setLayout({x: x}, true);
58061                node.setLayout({dx: nodeWidth}, true);
58062                each$1(node.outEdges, function (edge) {
58063                    var indexEdge = edges.indexOf(edge);
58064                    remainEdges[indexEdge] = 0;
58065                    var targetNode = edge.node2;
58066                    var nodeIndex = nodes.indexOf(targetNode);
58067                    if (--indegreeArr[nodeIndex] === 0) {
58068                        nextNode.push(targetNode);
58069                    }
58070                });
58071            });
58072
58073            ++x;
58074            zeroIndegrees = nextNode;
58075            nextNode = [];
58076        }
58077
58078        for (var i = 0; i < remainEdges.length; i++) {
58079            if (__DEV__) {
58080                if (remainEdges[i] === 1) {
58081                    throw new Error('Sankey is a DAG, the original data has cycle!');
58082                }
58083            }
58084        }
58085
58086        moveSinksRight(nodes, x);
58087        kx = (width - nodeWidth) / (x - 1);
58088
58089        scaleNodeBreadths(nodes, kx);
58090    }
58091
58092    /**
58093     * All the node without outEgdes are assigned maximum x-position and
58094     *     be aligned in the last column.
58095     *
58096     * @param {module:echarts/data/Graph~Node} nodes  node of sankey view
58097     * @param {number} x  value (x-1) use to assign to node without outEdges
58098     *     as x-position
58099     */
58100    function moveSinksRight(nodes, x) {
58101        each$1(nodes, function (node) {
58102            if (!node.outEdges.length) {
58103                node.setLayout({x: x - 1}, true);
58104            }
58105        });
58106    }
58107
58108    /**
58109     * Scale node x-position to the width
58110     *
58111     * @param {module:echarts/data/Graph~Node} nodes  node of sankey view
58112     * @param {number} kx   multiple used to scale nodes
58113     */
58114    function scaleNodeBreadths(nodes, kx) {
58115        each$1(nodes, function (node) {
58116            var nodeX = node.getLayout().x * kx;
58117            node.setLayout({x: nodeX}, true);
58118        });
58119    }
58120
58121    /**
58122     * Using Gauss-Seidel iterations method to compute the node depth(y-position)
58123     *
58124     * @param {module:echarts/data/Graph~Node} nodes  node of sankey view
58125     * @param {module:echarts/data/Graph~Edge} edges  edge of sankey view
58126     * @param {number} height  the whole height of the area to draw the view
58127     * @param {number} nodeGap  the vertical distance between two nodes
58128     *     in the same column.
58129     * @param {number} iterations  the number of iterations for the algorithm
58130     */
58131    function computeNodeDepths(nodes, edges, height, nodeGap, iterations) {
58132        var nodesByBreadth = nest()
58133            .key(function (d) {
58134                return d.getLayout().x;
58135            })
58136            .sortKeys(ascending)
58137            .entries(nodes)
58138            .map(function (d) {
58139                return d.values;
58140            });
58141
58142        initializeNodeDepth(nodes, nodesByBreadth, edges, height, nodeGap);
58143        resolveCollisions(nodesByBreadth, nodeGap, height);
58144
58145        for (var alpha = 1; iterations > 0; iterations--) {
58146            // 0.99 is a experience parameter, ensure that each iterations of
58147            // changes as small as possible.
58148            alpha *= 0.99;
58149            relaxRightToLeft(nodesByBreadth, alpha);
58150            resolveCollisions(nodesByBreadth, nodeGap, height);
58151            relaxLeftToRight(nodesByBreadth, alpha);
58152            resolveCollisions(nodesByBreadth, nodeGap, height);
58153        }
58154    }
58155
58156    /**
58157     * Compute the original y-position for each node
58158     *
58159     * @param {module:echarts/data/Graph~Node} nodes  node of sankey view
58160     * @param {Array.<Array.<module:echarts/data/Graph~Node>>} nodesByBreadth
58161     *     group by the array of all sankey nodes based on the nodes x-position.
58162     * @param {module:echarts/data/Graph~Edge} edges  edge of sankey view
58163     * @param {number} height  the whole height of the area to draw the view
58164     * @param {number} nodeGap  the vertical distance between two nodes
58165     */
58166    function initializeNodeDepth(nodes, nodesByBreadth, edges, height, nodeGap) {
58167        var kyArray = [];
58168        each$1(nodesByBreadth, function (nodes) {
58169            var n = nodes.length;
58170            var sum = 0;
58171            each$1(nodes, function (node) {
58172                sum += node.getLayout().value;
58173            });
58174            var ky = (height - (n - 1) * nodeGap) / sum;
58175            kyArray.push(ky);
58176        });
58177
58178        kyArray.sort(function (a, b) {
58179            return a - b;
58180        });
58181        var ky0 = kyArray[0];
58182
58183        each$1(nodesByBreadth, function (nodes) {
58184            each$1(nodes, function (node, i) {
58185                node.setLayout({y: i}, true);
58186                var nodeDy = node.getLayout().value * ky0;
58187                node.setLayout({dy: nodeDy}, true);
58188            });
58189        });
58190
58191        each$1(edges, function (edge) {
58192            var edgeDy = +edge.getValue() * ky0;
58193            edge.setLayout({dy: edgeDy}, true);
58194        });
58195    }
58196
58197    /**
58198     * Resolve the collision of initialized depth (y-position)
58199     *
58200     * @param {Array.<Array.<module:echarts/data/Graph~Node>>} nodesByBreadth
58201     *     group by the array of all sankey nodes based on the nodes x-position.
58202     * @param {number} nodeGap  the vertical distance between two nodes
58203     * @param {number} height  the whole height of the area to draw the view
58204     */
58205    function resolveCollisions(nodesByBreadth, nodeGap, height) {
58206        each$1(nodesByBreadth, function (nodes) {
58207            var node;
58208            var dy;
58209            var y0 = 0;
58210            var n = nodes.length;
58211            var i;
58212
58213            nodes.sort(ascendingDepth);
58214
58215            for (i = 0; i < n; i++) {
58216                node = nodes[i];
58217                dy = y0 - node.getLayout().y;
58218                if (dy > 0) {
58219                    var nodeY = node.getLayout().y + dy;
58220                    node.setLayout({y: nodeY}, true);
58221                }
58222                y0 = node.getLayout().y + node.getLayout().dy + nodeGap;
58223            }
58224
58225            // If the bottommost node goes outside the bounds, push it back up
58226            dy = y0 - nodeGap - height;
58227            if (dy > 0) {
58228                var nodeY = node.getLayout().y - dy;
58229                node.setLayout({y: nodeY}, true);
58230                y0 = node.getLayout().y;
58231                for (i = n - 2; i >= 0; --i) {
58232                    node = nodes[i];
58233                    dy = node.getLayout().y + node.getLayout().dy + nodeGap - y0;
58234                    if (dy > 0) {
58235                        nodeY = node.getLayout().y - dy;
58236                        node.setLayout({y: nodeY}, true);
58237                    }
58238                    y0 = node.getLayout().y;
58239                }
58240            }
58241        });
58242    }
58243
58244    /**
58245     * Change the y-position of the nodes, except most the right side nodes
58246     *
58247     * @param {Array.<Array.<module:echarts/data/Graph~Node>>} nodesByBreadth
58248     *     group by the array of all sankey nodes based on the node x-position.
58249     * @param {number} alpha  parameter used to adjust the nodes y-position
58250     */
58251    function relaxRightToLeft(nodesByBreadth, alpha) {
58252        each$1(nodesByBreadth.slice().reverse(), function (nodes) {
58253            each$1(nodes, function (node) {
58254                if (node.outEdges.length) {
58255                    var y = sum(node.outEdges, weightedTarget) / sum(node.outEdges, getEdgeValue);
58256                    var nodeY = node.getLayout().y + (y - center$1(node)) * alpha;
58257                    node.setLayout({y: nodeY}, true);
58258                }
58259            });
58260        });
58261    }
58262
58263    function weightedTarget(edge) {
58264        return center$1(edge.node2) * edge.getValue();
58265    }
58266
58267    /**
58268     * Change the y-position of the nodes, except most the left side nodes
58269     *
58270     * @param {Array.<Array.<module:echarts/data/Graph~Node>>} nodesByBreadth
58271     *     group by the array of all sankey nodes based on the node x-position.
58272     * @param {number} alpha  parameter used to adjust the nodes y-position
58273     */
58274    function relaxLeftToRight(nodesByBreadth, alpha) {
58275        each$1(nodesByBreadth, function (nodes) {
58276            each$1(nodes, function (node) {
58277                if (node.inEdges.length) {
58278                    var y = sum(node.inEdges, weightedSource) / sum(node.inEdges, getEdgeValue);
58279                    var nodeY = node.getLayout().y + (y - center$1(node)) * alpha;
58280                    node.setLayout({y: nodeY}, true);
58281                }
58282            });
58283        });
58284    }
58285
58286    function weightedSource(edge) {
58287        return center$1(edge.node1) * edge.getValue();
58288    }
58289
58290    /**
58291     * Compute the depth(y-position) of each edge
58292     *
58293     * @param {module:echarts/data/Graph~Node} nodes  node of sankey view
58294     */
58295    function computeEdgeDepths(nodes) {
58296        each$1(nodes, function (node) {
58297            node.outEdges.sort(ascendingTargetDepth);
58298            node.inEdges.sort(ascendingSourceDepth);
58299        });
58300        each$1(nodes, function (node) {
58301            var sy = 0;
58302            var ty = 0;
58303            each$1(node.outEdges, function (edge) {
58304                edge.setLayout({sy: sy}, true);
58305                sy += edge.getLayout().dy;
58306            });
58307            each$1(node.inEdges, function (edge) {
58308                edge.setLayout({ty: ty}, true);
58309                ty += edge.getLayout().dy;
58310            });
58311        });
58312    }
58313
58314    function ascendingTargetDepth(a, b) {
58315        return a.node2.getLayout().y - b.node2.getLayout().y;
58316    }
58317
58318    function ascendingSourceDepth(a, b) {
58319        return a.node1.getLayout().y - b.node1.getLayout().y;
58320    }
58321
58322    function sum(array, f) {
58323        var sum = 0;
58324        var len = array.length;
58325        var i = -1;
58326        while (++i < len) {
58327            var value = +f.call(array, array[i], i);
58328            if (!isNaN(value)) {
58329                sum += value;
58330            }
58331        }
58332        return sum;
58333    }
58334
58335    function center$1(node) {
58336        return node.getLayout().y + node.getLayout().dy / 2;
58337    }
58338
58339    function ascendingDepth(a, b) {
58340        return a.getLayout().y - b.getLayout().y;
58341    }
58342
58343    function ascending(a, b) {
58344        return a - b;
58345    }
58346
58347    function getEdgeValue(edge) {
58348        return edge.getValue();
58349    }
58350
58351    /*
58352* Licensed to the Apache Software Foundation (ASF) under one
58353* or more contributor license agreements.  See the NOTICE file
58354* distributed with this work for additional information
58355* regarding copyright ownership.  The ASF licenses this file
58356* to you under the Apache License, Version 2.0 (the
58357* "License"); you may not use this file except in compliance
58358* with the License.  You may obtain a copy of the License at
58359*
58360*   http://www.apache.org/licenses/LICENSE-2.0
58361*
58362* Unless required by applicable law or agreed to in writing,
58363* software distributed under the License is distributed on an
58364* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
58365* KIND, either express or implied.  See the License for the
58366* specific language governing permissions and limitations
58367* under the License.
58368*/
58369
58370    /**
58371     * @file Visual encoding for sankey view
58372     * @author  Deqing Li([email protected])
58373     */
58374
58375    var sankeyVisual = function (ecModel, payload) {
58376        ecModel.eachSeriesByType('sankey', function (seriesModel) {
58377            var graph = seriesModel.getGraph();
58378            var nodes = graph.nodes;
58379            if (nodes.length) {
58380                var minValue = Infinity;
58381                var maxValue = -Infinity;
58382                each$1(nodes, function (node) {
58383                    var nodeValue = node.getLayout().value;
58384                    if (nodeValue < minValue) {
58385                        minValue = nodeValue;
58386                    }
58387                    if (nodeValue > maxValue) {
58388                        maxValue = nodeValue;
58389                    }
58390                });
58391
58392                each$1(nodes, function (node) {
58393                    var mapping = new VisualMapping({
58394                        type: 'color',
58395                        mappingMethod: 'linear',
58396                        dataExtent: [minValue, maxValue],
58397                        visual: seriesModel.get('color')
58398                    });
58399
58400                    var mapValueToColor = mapping.mapValueToVisual(node.getLayout().value);
58401                    node.setVisual('color', mapValueToColor);
58402                    // If set itemStyle.normal.color
58403                    var itemModel = node.getModel();
58404                    var customColor = itemModel.get('itemStyle.color');
58405                    if (customColor != null) {
58406                        node.setVisual('color', customColor);
58407                    }
58408                });
58409            }
58410        });
58411    };
58412
58413    /*
58414* Licensed to the Apache Software Foundation (ASF) under one
58415* or more contributor license agreements.  See the NOTICE file
58416* distributed with this work for additional information
58417* regarding copyright ownership.  The ASF licenses this file
58418* to you under the Apache License, Version 2.0 (the
58419* "License"); you may not use this file except in compliance
58420* with the License.  You may obtain a copy of the License at
58421*
58422*   http://www.apache.org/licenses/LICENSE-2.0
58423*
58424* Unless required by applicable law or agreed to in writing,
58425* software distributed under the License is distributed on an
58426* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
58427* KIND, either express or implied.  See the License for the
58428* specific language governing permissions and limitations
58429* under the License.
58430*/
58431
58432    registerLayout(sankeyLayout);
58433    registerVisual(sankeyVisual);
58434
58435    /*
58436* Licensed to the Apache Software Foundation (ASF) under one
58437* or more contributor license agreements.  See the NOTICE file
58438* distributed with this work for additional information
58439* regarding copyright ownership.  The ASF licenses this file
58440* to you under the Apache License, Version 2.0 (the
58441* "License"); you may not use this file except in compliance
58442* with the License.  You may obtain a copy of the License at
58443*
58444*   http://www.apache.org/licenses/LICENSE-2.0
58445*
58446* Unless required by applicable law or agreed to in writing,
58447* software distributed under the License is distributed on an
58448* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
58449* KIND, either express or implied.  See the License for the
58450* specific language governing permissions and limitations
58451* under the License.
58452*/
58453
58454
58455    var seriesModelMixin = {
58456
58457        /**
58458         * @private
58459         * @type {string}
58460         */
58461        _baseAxisDim: null,
58462
58463        /**
58464         * @override
58465         */
58466        getInitialData: function (option, ecModel) {
58467            // When both types of xAxis and yAxis are 'value', layout is
58468            // needed to be specified by user. Otherwise, layout can be
58469            // judged by which axis is category.
58470
58471            var ordinalMeta;
58472
58473            var xAxisModel = ecModel.getComponent('xAxis', this.get('xAxisIndex'));
58474            var yAxisModel = ecModel.getComponent('yAxis', this.get('yAxisIndex'));
58475            var xAxisType = xAxisModel.get('type');
58476            var yAxisType = yAxisModel.get('type');
58477            var addOrdinal;
58478
58479            // FIXME
58480            // 考虑时间轴
58481
58482            if (xAxisType === 'category') {
58483                option.layout = 'horizontal';
58484                ordinalMeta = xAxisModel.getOrdinalMeta();
58485                addOrdinal = true;
58486            }
58487            else if (yAxisType  === 'category') {
58488                option.layout = 'vertical';
58489                ordinalMeta = yAxisModel.getOrdinalMeta();
58490                addOrdinal = true;
58491            }
58492            else {
58493                option.layout = option.layout || 'horizontal';
58494            }
58495
58496            var coordDims = ['x', 'y'];
58497            var baseAxisDimIndex = option.layout === 'horizontal' ? 0 : 1;
58498            var baseAxisDim = this._baseAxisDim = coordDims[baseAxisDimIndex];
58499            var otherAxisDim = coordDims[1 - baseAxisDimIndex];
58500            var axisModels = [xAxisModel, yAxisModel];
58501            var baseAxisType = axisModels[baseAxisDimIndex].get('type');
58502            var otherAxisType = axisModels[1 - baseAxisDimIndex].get('type');
58503            var data = option.data;
58504
58505            // ??? FIXME make a stage to perform data transfrom.
58506            // MUST create a new data, consider setOption({}) again.
58507            if (data && addOrdinal) {
58508                var newOptionData = [];
58509                each$1(data, function (item, index) {
58510                    var newItem;
58511                    if (item.value && isArray(item.value)) {
58512                        newItem = item.value.slice();
58513                        item.value.unshift(index);
58514                    }
58515                    else if (isArray(item)) {
58516                        newItem = item.slice();
58517                        item.unshift(index);
58518                    }
58519                    else {
58520                        newItem = item;
58521                    }
58522                    newOptionData.push(newItem);
58523                });
58524                option.data = newOptionData;
58525            }
58526
58527            var defaultValueDimensions = this.defaultValueDimensions;
58528
58529            return createListSimply(
58530                this,
58531                {
58532                    coordDimensions: [{
58533                        name: baseAxisDim,
58534                        type: getDimensionTypeByAxis(baseAxisType),
58535                        ordinalMeta: ordinalMeta,
58536                        otherDims: {
58537                            tooltip: false,
58538                            itemName: 0
58539                        },
58540                        dimsDef: ['base']
58541                    }, {
58542                        name: otherAxisDim,
58543                        type: getDimensionTypeByAxis(otherAxisType),
58544                        dimsDef: defaultValueDimensions.slice()
58545                    }],
58546                    dimensionsCount: defaultValueDimensions.length + 1
58547                }
58548            );
58549        },
58550
58551        /**
58552         * If horizontal, base axis is x, otherwise y.
58553         * @override
58554         */
58555        getBaseAxis: function () {
58556            var dim = this._baseAxisDim;
58557            return this.ecModel.getComponent(dim + 'Axis', this.get(dim + 'AxisIndex')).axis;
58558        }
58559
58560    };
58561
58562    /*
58563* Licensed to the Apache Software Foundation (ASF) under one
58564* or more contributor license agreements.  See the NOTICE file
58565* distributed with this work for additional information
58566* regarding copyright ownership.  The ASF licenses this file
58567* to you under the Apache License, Version 2.0 (the
58568* "License");
58568 you may not use this file except in compliance
58569* with the License.  You may obtain a copy of the License at
58570*
58571*   http://www.apache.org/licenses/LICENSE-2.0
58572*
58573* Unless required by applicable law or agreed to in writing,
58574* software distributed under the License is distributed on an
58575* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
58576* KIND, either express or implied.  See the License for the
58577* specific language governing permissions and limitations
58578* under the License.
58579*/
58580
58581    var BoxplotSeries = SeriesModel.extend({
58582
58583        type: 'series.boxplot',
58584
58585        dependencies: ['xAxis', 'yAxis', 'grid'],
58586
58587        // TODO
58588        // box width represents group size, so dimension should have 'size'.
58589
58590        /**
58591         * @see <https://en.wikipedia.org/wiki/Box_plot>
58592         * The meanings of 'min' and 'max' depend on user,
58593         * and echarts do not need to know it.
58594         * @readOnly
58595         */
58596        defaultValueDimensions: [
58597            {name: 'min', defaultTooltip: true},
58598            {name: 'Q1', defaultTooltip: true},
58599            {name: 'median', defaultTooltip: true},
58600            {name: 'Q3', defaultTooltip: true},
58601            {name: 'max', defaultTooltip: true}
58602        ],
58603
58604        /**
58605         * @type {Array.<string>}
58606         * @readOnly
58607         */
58608        dimensions: null,
58609
58610        /**
58611         * @override
58612         */
58613        defaultOption: {
58614            zlevel: 0,                  // 一级层å
58615            z: 2,                       // 二级层å
58616            coordinateSystem: 'cartesian2d',
58617            legendHoverLink: true,
58618
58619            hoverAnimation: true,
58620
58621            // xAxisIndex: 0,
58622            // yAxisIndex: 0,
58623
58624            layout: null,               // 'horizontal' or 'vertical'
58625            boxWidth: [7, 50],       // [min, max] can be percent of band width.
58626
58627            itemStyle: {
58628                color: '#fff',
58629                borderWidth: 1
58630            },
58631
58632            emphasis: {
58633                itemStyle: {
58634                    borderWidth: 2,
58635                    shadowBlur: 5,
58636                    shadowOffsetX: 2,
58637                    shadowOffsetY: 2,
58638                    shadowColor: 'rgba(0,0,0,0.4)'
58639                }
58640            },
58641
58642            animationEasing: 'elasticOut',
58643            animationDuration: 800
58644        }
58645    });
58646
58647    mixin(BoxplotSeries, seriesModelMixin, true);
58648
58649    /*
58650* Licensed to the Apache Software Foundation (ASF) under one
58651* or more contributor license agreements.  See the NOTICE file
58652* distributed with this work for additional information
58653* regarding copyright ownership.  The ASF licenses this file
58654* to you under the Apache License, Version 2.0 (the
58655* "License"); you may not use this file except in compliance
58656* with the License.  You may obtain a copy of the License at
58657*
58658*   http://www.apache.org/licenses/LICENSE-2.0
58659*
58660* Unless required by applicable law or agreed to in writing,
58661* software distributed under the License is distributed on an
58662* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
58663* KIND, either express or implied.  See the License for the
58664* specific language governing permissions and limitations
58665* under the License.
58666*/
58667
58668// Update common properties
58669    var NORMAL_ITEM_STYLE_PATH = ['itemStyle'];
58670    var EMPHASIS_ITEM_STYLE_PATH = ['emphasis', 'itemStyle'];
58671
58672    var BoxplotView = Chart.extend({
58673
58674        type: 'boxplot',
58675
58676        render: function (seriesModel, ecModel, api) {
58677            var data = seriesModel.getData();
58678            var group = this.group;
58679            var oldData = this._data;
58680
58681            // There is no old data only when first rendering or switching from
58682            // stream mode to normal mode, where previous elements should be removed.
58683            if (!this._data) {
58684                group.removeAll();
58685            }
58686
58687            var constDim = seriesModel.get('layout') === 'horizontal' ? 1 : 0;
58688
58689            data.diff(oldData)
58690                .add(function (newIdx) {
58691                    if (data.hasValue(newIdx)) {
58692                        var itemLayout = data.getItemLayout(newIdx);
58693                        var symbolEl = createNormalBox(itemLayout, data, newIdx, constDim, true);
58694                        data.setItemGraphicEl(newIdx, symbolEl);
58695                        group.add(symbolEl);
58696                    }
58697                })
58698                .update(function (newIdx, oldIdx) {
58699                    var symbolEl = oldData.getItemGraphicEl(oldIdx);
58700
58701                    // Empty data
58702                    if (!data.hasValue(newIdx)) {
58703                        group.remove(symbolEl);
58704                        return;
58705                    }
58706
58707                    var itemLayout = data.getItemLayout(newIdx);
58708                    if (!symbolEl) {
58709                        symbolEl = createNormalBox(itemLayout, data, newIdx, constDim);
58710                    }
58711                    else {
58712                        updateNormalBoxData(itemLayout, symbolEl, data, newIdx);
58713                    }
58714
58715                    group.add(symbolEl);
58716
58717                    data.setItemGraphicEl(newIdx, symbolEl);
58718                })
58719                .remove(function (oldIdx) {
58720                    var el = oldData.getItemGraphicEl(oldIdx);
58721                    el && group.remove(el);
58722                })
58723                .execute();
58724
58725            this._data = data;
58726        },
58727
58728        remove: function (ecModel) {
58729            var group = this.group;
58730            var data = this._data;
58731            this._data = null;
58732            data && data.eachItemGraphicEl(function (el) {
58733                el && group.remove(el);
58734            });
58735        },
58736
58737        dispose: noop
58738
58739    });
58740
58741
58742    var BoxPath = Path.extend({
58743
58744        type: 'boxplotBoxPath',
58745
58746        shape: {},
58747
58748        buildPath: function (ctx, shape) {
58749            var ends = shape.points;
58750
58751            var i = 0;
58752            ctx.moveTo(ends[i][0], ends[i][1]);
58753            i++;
58754            for (; i < 4; i++) {
58755                ctx.lineTo(ends[i][0], ends[i][1]);
58756            }
58757            ctx.closePath();
58758
58759            for (; i < ends.length; i++) {
58760                ctx.moveTo(ends[i][0], ends[i][1]);
58761                i++;
58762                ctx.lineTo(ends[i][0], ends[i][1]);
58763            }
58764        }
58765    });
58766
58767
58768    function createNormalBox(itemLayout, data, dataIndex, constDim, isInit) {
58769        var ends = itemLayout.ends;
58770
58771        var el = new BoxPath({
58772            shape: {
58773                points: isInit
58774                    ? transInit(ends, constDim, itemLayout)
58775                    : ends
58776            }
58777        });
58778
58779        updateNormalBoxData(itemLayout, el, data, dataIndex, isInit);
58780
58781        return el;
58782    }
58783
58784    function updateNormalBoxData(itemLayout, el, data, dataIndex, isInit) {
58785        var seriesModel = data.hostModel;
58786        var updateMethod = graphic[isInit ? 'initProps' : 'updateProps'];
58787
58788        updateMethod(
58789            el,
58790            {shape: {points: itemLayout.ends}},
58791            seriesModel,
58792            dataIndex
58793        );
58794
58795        var itemModel = data.getItemModel(dataIndex);
58796        var normalItemStyleModel = itemModel.getModel(NORMAL_ITEM_STYLE_PATH);
58797        var borderColor = data.getItemVisual(dataIndex, 'color');
58798
58799        // Exclude borderColor.
58800        var itemStyle = normalItemStyleModel.getItemStyle(['borderColor']);
58801        itemStyle.stroke = borderColor;
58802        itemStyle.strokeNoScale = true;
58803        el.useStyle(itemStyle);
58804
58805        el.z2 = 100;
58806
58807        var hoverStyle = itemModel.getModel(EMPHASIS_ITEM_STYLE_PATH).getItemStyle();
58808        setHoverStyle(el, hoverStyle);
58809    }
58810
58811    function transInit(points, dim, itemLayout) {
58812        return map(points, function (point) {
58813            point = point.slice();
58814            point[dim] = itemLayout.initBaseline;
58815            return point;
58816        });
58817    }
58818
58819    /*
58820* Licensed to the Apache Software Foundation (ASF) under one
58821* or more contributor license agreements.  See the NOTICE file
58822* distributed with this work for additional information
58823* regarding copyright ownership.  The ASF licenses this file
58824* to you under the Apache License, Version 2.0 (the
58825* "License"); you may not use this file except in compliance
58826* with the License.  You may obtain a copy of the License at
58827*
58828*   http://www.apache.org/licenses/LICENSE-2.0
58829*
58830* Unless required by applicable law or agreed to in writing,
58831* software distributed under the License is distributed on an
58832* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
58833* KIND, either express or implied.  See the License for the
58834* specific language governing permissions and limitations
58835* under the License.
58836*/
58837
58838
58839    var borderColorQuery = ['itemStyle', 'borderColor'];
58840
58841    var boxplotVisual = function (ecModel, api) {
58842
58843        var globalColors = ecModel.get('color');
58844
58845        ecModel.eachRawSeriesByType('boxplot', function (seriesModel) {
58846
58847            var defaulColor = globalColors[seriesModel.seriesIndex % globalColors.length];
58848            var data = seriesModel.getData();
58849
58850            data.setVisual({
58851                legendSymbol: 'roundRect',
58852                // Use name 'color' but not 'borderColor' for legend usage and
58853                // visual coding from other component like dataRange.
58854                color: seriesModel.get(borderColorQuery) || defaulColor
58855            });
58856
58857            // Only visible series has each data be visual encoded
58858            if (!ecModel.isSeriesFiltered(seriesModel)) {
58859                data.each(function (idx) {
58860                    var itemModel = data.getItemModel(idx);
58861                    data.setItemVisual(
58862                        idx,
58863                        {color: itemModel.get(borderColorQuery, true)}
58864                    );
58865                });
58866            }
58867        });
58868
58869    };
58870
58871    /*
58872* Licensed to the Apache Software Foundation (ASF) under one
58873* or more contributor license agreements.  See the NOTICE file
58874* distributed with this work for additional information
58875* regarding copyright ownership.  The ASF licenses this file
58876* to you under the Apache License, Version 2.0 (the
58877* "License");
58877 you may not use this file except in compliance
58878* with the License.  You may obtain a copy of the License at
58879*
58880*   http://www.apache.org/licenses/LICENSE-2.0
58881*
58882* Unless required by applicable law or agreed to in writing,
58883* software distributed under the License is distributed on an
58884* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
58885* KIND, either express or implied.  See the License for the
58886* specific language governing permissions and limitations
58887* under the License.
58888*/
58889
58890    var each$13 = each$1;
58891
58892    var boxplotLayout = function (ecModel) {
58893
58894        var groupResult = groupSeriesByAxis(ecModel);
58895
58896        each$13(groupResult, function (groupItem) {
58897            var seriesModels = groupItem.seriesModels;
58898
58899            if (!seriesModels.length) {
58900                return;
58901            }
58902
58903            calculateBase(groupItem);
58904
58905            each$13(seriesModels, function (seriesModel, idx) {
58906                layoutSingleSeries(
58907                    seriesModel,
58908                    groupItem.boxOffsetList[idx],
58909                    groupItem.boxWidthList[idx]
58910                );
58911            });
58912        });
58913    };
58914
58915    /**
58916     * Group series by axis.
58917     */
58918    function groupSeriesByAxis(ecModel) {
58919        var result = [];
58920        var axisList = [];
58921
58922        ecModel.eachSeriesByType('boxplot', function (seriesModel) {
58923            var baseAxis = seriesModel.getBaseAxis();
58924            var idx = indexOf(axisList, baseAxis);
58925
58926            if (idx < 0) {
58927                idx = axisList.length;
58928                axisList[idx] = baseAxis;
58929                result[idx] = {axis: baseAxis, seriesModels: []};
58930            }
58931
vendor: 4,931 bytes, lines 58932-59072
58932            result[idx].seriesModels.push(seriesModel);
58933        });
58934
58935        return result;
58936    }
58937
58938    /**
58939     * Calculate offset and box width for each series.
58940     */
58941    function calculateBase(groupItem) {
58942        var extent;
58943        var baseAxis = groupItem.axis;
58944        var seriesModels = groupItem.seriesModels;
58945        var seriesCount = seriesModels.length;
58946
58947        var boxWidthList = groupItem.boxWidthList = [];
58948        var boxOffsetList = groupItem.boxOffsetList = [];
58949        var boundList = [];
58950
58951        var bandWidth;
58952        if (baseAxis.type === 'category') {
58953            bandWidth = baseAxis.getBandWidth();
58954        }
58955        else {
58956            var maxDataCount = 0;
58957            each$13(seriesModels, function (seriesModel) {
58958                maxDataCount = Math.max(maxDataCount, seriesModel.getData().count());
58959            });
58960            extent = baseAxis.getExtent(),
58961            Math.abs(extent[1] - extent[0]) / maxDataCount;
58962        }
58963
58964        each$13(seriesModels, function (seriesModel) {
58965            var boxWidthBound = seriesModel.get('boxWidth');
58966            if (!isArray(boxWidthBound)) {
58967                boxWidthBound = [boxWidthBound, boxWidthBound];
58968            }
58969            boundList.push([
58970                parsePercent$1(boxWidthBound[0], bandWidth) || 0,
58971                parsePercent$1(boxWidthBound[1], bandWidth) || 0
58972            ]);
58973        });
58974
58975        var availableWidth = bandWidth * 0.8 - 2;
58976        var boxGap = availableWidth / seriesCount * 0.3;
58977        var boxWidth = (availableWidth - boxGap * (seriesCount - 1)) / seriesCount;
58978        var base = boxWidth / 2 - availableWidth / 2;
58979
58980        each$13(seriesModels, function (seriesModel, idx) {
58981            boxOffsetList.push(base);
58982            base += boxGap + boxWidth;
58983
58984            boxWidthList.push(
58985                Math.min(Math.max(boxWidth, boundList[idx][0]), boundList[idx][1])
58986            );
58987        });
58988    }
58989
58990    /**
58991     * Calculate points location for each series.
58992     */
58993    function layoutSingleSeries(seriesModel, offset, boxWidth) {
58994        var coordSys = seriesModel.coordinateSystem;
58995        var data = seriesModel.getData();
58996        var halfWidth = boxWidth / 2;
58997        var cDimIdx = seriesModel.get('layout') === 'horizontal' ? 0 : 1;
58998        var vDimIdx = 1 - cDimIdx;
58999        var coordDims = ['x', 'y'];
59000        var cDim = data.mapDimension(coordDims[cDimIdx]);
59001        var vDims = data.mapDimension(coordDims[vDimIdx], true);
59002
59003        if (cDim == null || vDims.length < 5) {
59004            return;
59005        }
59006
59007        for (var dataIndex = 0; dataIndex < data.count(); dataIndex++) {
59008            var axisDimVal = data.get(cDim, dataIndex);
59009
59010            var median = getPoint(axisDimVal, vDims[2], dataIndex);
59011            var end1 = getPoint(axisDimVal, vDims[0], dataIndex);
59012            var end2 = getPoint(axisDimVal, vDims[1], dataIndex);
59013            var end4 = getPoint(axisDimVal, vDims[3], dataIndex);
59014            var end5 = getPoint(axisDimVal, vDims[4], dataIndex);
59015
59016            var ends = [];
59017            addBodyEnd(ends, end2, 0);
59018            addBodyEnd(ends, end4, 1);
59019
59020            ends.push(end1, end2, end5, end4);
59021            layEndLine(ends, end1);
59022            layEndLine(ends, end5);
59023            layEndLine(ends, median);
59024
59025            data.setItemLayout(dataIndex, {
59026                initBaseline: median[vDimIdx],
59027                ends: ends
59028            });
59029        }
59030
59031        function getPoint(axisDimVal, dimIdx, dataIndex) {
59032            var val = data.get(dimIdx, dataIndex);
59033            var p = [];
59034            p[cDimIdx] = axisDimVal;
59035            p[vDimIdx] = val;
59036            var point;
59037            if (isNaN(axisDimVal) || isNaN(val)) {
59038                point = [NaN, NaN];
59039            }
59040            else {
59041                point = coordSys.dataToPoint(p);
59042                point[cDimIdx] += offset;
59043            }
59044            return point;
59045        }
59046
59047        function addBodyEnd(ends, point, start) {
59048            var point1 = point.slice();
59049            var point2 = point.slice();
59050            point1[cDimIdx] += halfWidth;
59051            point2[cDimIdx] -= halfWidth;
59052            start
59053                ? ends.push(point1, point2)
59054                : ends.push(point2, point1);
59055        }
59056
59057        function layEndLine(ends, endCenter) {
59058            var from = endCenter.slice();
59059            var to = endCenter.slice();
59060            from[cDimIdx] -= halfWidth;
59061            to[cDimIdx] += halfWidth;
59062            ends.push(from, to);
59063        }
59064    }
59065
59066    /*
59067* Licensed to the Apache Software Foundation (ASF) under one
59068* or more contributor license agreements.  See the NOTICE file
59069* distributed with this work for additional information
59070* regarding copyright ownership.  The ASF licenses this file
59071* to you under the Apache License, Version 2.0 (the
59072* "License");
59072 you may not use this file except in compliance
59073* with the License.  You may obtain a copy of the License at
59074*
59075*   http://www.apache.org/licenses/LICENSE-2.0
59076*
59077* Unless required by applicable law or agreed to in writing,
59078* software distributed under the License is distributed on an
59079* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
59080* KIND, either express or implied.  See the License for the
59081* specific language governing permissions and limitations
59082* under the License.
59083*/
59084
59085    registerVisual(boxplotVisual);
59086    registerLayout(boxplotLayout);
59087
59088    /*
59089* Licensed to the Apache Software Foundation (ASF) under one
59090* or more contributor license agreements.  See the NOTICE file
59091* distributed with this work for additional information
59092* regarding copyright ownership.  The ASF licenses this file
59093* to you under the Apache License, Version 2.0 (the
59094* "License"); you may not use this file except in compliance
59095* with the License.  You may obtain a copy of the License at
59096*
59097*   http://www.apache.org/licenses/LICENSE-2.0
59098*
59099* Unless required by applicable law or agreed to in writing,
59100* software distributed under the License is distributed on an
59101* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
59102* KIND, either express or implied.  See the License for the
59103* specific language governing permissions and limitations
59104* under the License.
59105*/
59106
59107    var CandlestickSeries = SeriesModel.extend({
59108
59109        type: 'series.candlestick',
59110
59111        dependencies: ['xAxis', 'yAxis', 'grid'],
59112
59113        /**
59114         * @readOnly
59115         */
59116        defaultValueDimensions: [
59117            {name: 'open', defaultTooltip: true},
59118            {name: 'close', defaultTooltip: true},
59119            {name: 'lowest', defaultTooltip: true},
59120            {name: 'highest', defaultTooltip: true}
59121        ],
59122
59123        /**
59124         * @type {Array.<string>}
59125         * @readOnly
59126         */
59127        dimensions: null,
59128
59129        /**
59130         * @override
59131         */
59132        defaultOption: {
59133            zlevel: 0,
59134            z: 2,
59135            coordinateSystem: 'cartesian2d',
59136            legendHoverLink: true,
59137
59138            hoverAnimation: true,
59139
59140            // xAxisIndex: 0,
59141            // yAxisIndex: 0,
59142
59143            layout: null, // 'horizontal' or 'vertical'
59144
59145            itemStyle: {
59146                color: '#c23531', // 阳线 positive
59147                color0: '#314656', // 阴线 negative     '#c23531', '#314656'
59148                borderWidth: 1,
59149                // FIXME
59150                // ec2中使用的是lineStyle.color 和 lineStyle.color0
59151                borderColor: '#c23531',
59152                borderColor0: '#314656'
59153            },
59154
59155            emphasis: {
59156                itemStyle: {
59157                    borderWidth: 2
59158                }
59159            },
59160
59161            barMaxWidth: null,
59162            barMinWidth: null,
59163            barWidth: null,
59164
59165            large: true,
59166            largeThreshold: 600,
59167
59168            progressive: 3e3,
59169            progressiveThreshold: 1e4,
59170            progressiveChunkMode: 'mod',
59171
59172            animationUpdate: false,
59173            animationEasing: 'linear',
59174            animationDuration: 300
59175        },
59176
59177        /**
59178         * Get dimension for shadow in dataZoom
59179         * @return {string} dimension name
59180         */
59181        getShadowDim: function () {
59182            return 'open';
59183        },
59184
59185        brushSelector: function (dataIndex, data, selectors) {
59186            var itemLayout = data.getItemLayout(dataIndex);
59187            return itemLayout && selectors.rect(itemLayout.brushRect);
59188        }
59189
59190    });
59191
59192    mixin(CandlestickSeries, seriesModelMixin, true);
59193
59194    /*
59195* Licensed to the Apache Software Foundation (ASF) under one
59196* or more contributor license agreements.  See the NOTICE file
59197* distributed with this work for additional information
59198* regarding copyright ownership.  The ASF licenses this file
59199* to you under the Apache License, Version 2.0 (the
59200* "License"); you may not use this file except in compliance
59201* with the License.  You may obtain a copy of the License at
59202*
59203*   http://www.apache.org/licenses/LICENSE-2.0
59204*
59205* Unless required by applicable law or agreed to in writing,
59206* software distributed under the License is distributed on an
59207* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
59208* KIND, either express or implied.  See the License for the
59209* specific language governing permissions and limitations
59210* under the License.
59211*/
59212
59213    var NORMAL_ITEM_STYLE_PATH$1 = ['itemStyle'];
59214    var EMPHASIS_ITEM_STYLE_PATH$1 = ['emphasis', 'itemStyle'];
59215    var SKIP_PROPS = ['color', 'color0', 'borderColor', 'borderColor0'];
59216
59217
59218    var CandlestickView = Chart.extend({
59219
59220        type: 'candlestick',
59221
59222        render: function (seriesModel, ecModel, api) {
59223            this._updateDrawMode(seriesModel);
59224
59225            this._isLargeDraw
59226                ? this._renderLarge(seriesModel)
59227                : this._renderNormal(seriesModel);
59228        },
59229
59230        incrementalPrepareRender: function (seriesModel, ecModel, api) {
59231            this._clear();
59232            this._updateDrawMode(seriesModel);
59233        },
59234
59235        incrementalRender: function (params, seriesModel, ecModel, api) {
59236            this._isLargeDraw
59237                ? this._incrementalRenderLarge(params, seriesModel)
59238                : this._incrementalRenderNormal(params, seriesModel);
59239        },
59240
59241        _updateDrawMode: function (seriesModel) {
59242            var isLargeDraw = seriesModel.pipelineContext.large;
59243            if (this._isLargeDraw == null || isLargeDraw ^ this._isLargeDraw) {
59244                this._isLargeDraw = isLargeDraw;
59245                this._clear();
59246            }
59247        },
59248
59249        _renderNormal: function (seriesModel) {
59250            var data = seriesModel.getData();
59251            var oldData = this._data;
59252            var group = this.group;
59253            var isSimpleBox = data.getLayout('isSimpleBox');
59254
59255            // There is no old data only when first rendering or switching from
59256            // stream mode to normal mode, where previous elements should be removed.
59257            if (!this._data) {
59258                group.removeAll();
59259            }
59260
59261            data.diff(oldData)
59262                .add(function (newIdx) {
59263                    if (data.hasValue(newIdx)) {
59264                        var el;
59265
59266                        var itemLayout = data.getItemLayout(newIdx);
59267                        el = createNormalBox$1(itemLayout, newIdx, true);
59268                        initProps(el, {shape: {points: itemLayout.ends}}, seriesModel, newIdx);
59269
59270                        setBoxCommon(el, data, newIdx, isSimpleBox);
59271
59272                        group.add(el);
59273                        data.setItemGraphicEl(newIdx, el);
59274                    }
59275                })
59276                .update(function (newIdx, oldIdx) {
59277                    var el = oldData.getItemGraphicEl(oldIdx);
59278
59279                    // Empty data
59280                    if (!data.hasValue(newIdx)) {
59281                        group.remove(el);
59282                        return;
59283                    }
59284
59285                    var itemLayout = data.getItemLayout(newIdx);
59286                    if (!el) {
59287                        el = createNormalBox$1(itemLayout, newIdx);
59288                    }
59289                    else {
59290                        updateProps(el, {shape: {points: itemLayout.ends}}, seriesModel, newIdx);
59291                    }
59292
59293                    setBoxCommon(el, data, newIdx, isSimpleBox);
59294
59295                    group.add(el);
59296                    data.setItemGraphicEl(newIdx, el);
59297                })
59298                .remove(function (oldIdx) {
59299                    var el = oldData.getItemGraphicEl(oldIdx);
59300                    el && group.remove(el);
59301                })
59302                .execute();
59303
59304            this._data = data;
59305        },
59306
59307        _renderLarge: function (seriesModel) {
59308            this._clear();
59309            createLarge$1(seriesModel, this.group);
59310        },
59311
59312        _incrementalRenderNormal: function (params, seriesModel) {
59313            var data = seriesModel.getData();
59314            var isSimpleBox = data.getLayout('isSimpleBox');
59315
59316            var dataIndex;
59317            while ((dataIndex = params.next()) != null) {
59318                var el;
59319
59320                var itemLayout = data.getItemLayout(dataIndex);
59321                el = createNormalBox$1(itemLayout, dataIndex);
59322                setBoxCommon(el, data, dataIndex, isSimpleBox);
59323
59324                el.incremental = true;
59325                this.group.add(el);
59326            }
59327        },
59328
59329        _incrementalRenderLarge: function (params, seriesModel) {
59330            createLarge$1(seriesModel, this.group, true);
59331        },
59332
59333        remove: function (ecModel) {
59334            this._clear();
59335        },
59336
59337        _clear: function () {
59338            this.group.removeAll();
59339            this._data = null;
59340        },
59341
59342        dispose: noop
59343
59344    });
59345
59346
59347    var NormalBoxPath = Path.extend({
59348
59349        type: 'normalCandlestickBox',
59350
59351        shape: {},
59352
59353        buildPath: function (ctx, shape) {
59354            var ends = shape.points;
59355
59356            if (this.__simpleBox) {
59357                ctx.moveTo(ends[4][0], ends[4][1]);
59358                ctx.lineTo(ends[6][0], ends[6][1]);
59359            }
59360            else {
59361                ctx.moveTo(ends[0][0], ends[0][1]);
59362                ctx.lineTo(ends[1][0], ends[1][1]);
59363                ctx.lineTo(ends[2][0], ends[2][1]);
59364                ctx.lineTo(ends[3][0], ends[3][1]);
59365                ctx.closePath();
59366
59367                ctx.moveTo(ends[4][0], ends[4][1]);
59368                ctx.lineTo(ends[5][0], ends[5][1]);
59369                ctx.moveTo(ends[6][0], ends[6][1]);
59370                ctx.lineTo(ends[7][0], ends[7][1]);
59371            }
59372        }
59373    });
59374
59375    function createNormalBox$1(itemLayout, dataIndex, isInit) {
59376        var ends = itemLayout.ends;
59377        return new NormalBoxPath({
59378            shape: {
59379                points: isInit
59380                    ? transInit$1(ends, itemLayout)
59381                    : ends
59382            },
59383            z2: 100
59384        });
59385    }
59386
59387    function setBoxCommon(el, data, dataIndex, isSimpleBox) {
59388        var itemModel = data.getItemModel(dataIndex);
59389        var normalItemStyleModel = itemModel.getModel(NORMAL_ITEM_STYLE_PATH$1);
59390        var color = data.getItemVisual(dataIndex, 'color');
59391        var borderColor = data.getItemVisual(dataIndex, 'borderColor') || color;
59392
59393        // Color must be excluded.
59394        // Because symbol provide setColor individually to set fill and stroke
59395        var itemStyle = normalItemStyleModel.getItemStyle(SKIP_PROPS);
59396
59397        el.useStyle(itemStyle);
59398        el.style.strokeNoScale = true;
59399        el.style.fill = color;
59400        el.style.stroke = borderColor;
59401
59402        el.__simpleBox = isSimpleBox;
59403
59404        var hoverStyle = itemModel.getModel(EMPHASIS_ITEM_STYLE_PATH$1).getItemStyle();
59405        setHoverStyle(el, hoverStyle);
59406    }
59407
59408    function transInit$1(points, itemLayout) {
59409        return map(points, function (point) {
59410            point = point.slice();
59411            point[1] = itemLayout.initBaseline;
59412            return point;
59413        });
59414    }
59415
59416
59417
59418    var LargeBoxPath = Path.extend({
59419
59420        type: 'largeCandlestickBox',
59421
59422        shape: {},
59423
59424        buildPath: function (ctx, shape) {
59425            // Drawing lines is more efficient than drawing
59426            // a whole line or drawing rects.
59427            var points = shape.points;
59428            for (var i = 0; i < points.length;) {
59429                if (this.__sign === points[i++]) {
59430                    var x = points[i++];
59431                    ctx.moveTo(x, points[i++]);
59432                    ctx.lineTo(x, points[i++]);
59433                }
59434                else {
59435                    i += 3;
59436                }
59437            }
59438        }
59439    });
59440
59441    function createLarge$1(seriesModel, group, incremental) {
59442        var data = seriesModel.getData();
59443        var largePoints = data.getLayout('largePoints');
59444
59445        var elP = new LargeBoxPath({
59446            shape: {points: largePoints},
59447            __sign: 1
59448        });
59449        group.add(elP);
59450        var elN = new LargeBoxPath({
59451            shape: {points: largePoints},
59452            __sign: -1
59453        });
59454        group.add(elN);
59455
59456        setLargeStyle$1(1, elP, seriesModel, data);
59457        setLargeStyle$1(-1, elN, seriesModel, data);
59458
59459        if (incremental) {
59460            elP.incremental = true;
59461            elN.incremental = true;
59462        }
59463    }
59464
59465    function setLargeStyle$1(sign, el, seriesModel, data) {
59466        var suffix = sign > 0 ? 'P' : 'N';
59467        var borderColor = data.getVisual('borderColor' + suffix)
59468            || data.getVisual('color' + suffix);
59469
59470        // Color must be excluded.
59471        // Because symbol provide setColor individually to set fill and stroke
59472        var itemStyle = seriesModel.getModel(NORMAL_ITEM_STYLE_PATH$1).getItemStyle(SKIP_PROPS);
59473
59474        el.useStyle(itemStyle);
59475        el.style.fill = null;
59476        el.style.stroke = borderColor;
59477        // No different
59478        // el.style.lineWidth = .5;
59479    }
59480
59481    /*
59482* Licensed to the Apache Software Foundation (ASF) under one
59483* or more contributor license agreements.  See the NOTICE file
59484* distributed with this work for additional information
59485* regarding copyright ownership.  The ASF licenses this file
59486* to you under the Apache License, Version 2.0 (the
59487* "License"); you may not use this file except in compliance
59488* with the License.  You may obtain a copy of the License at
59489*
59490*   http://www.apache.org/licenses/LICENSE-2.0
59491*
59492* Unless required by applicable law or agreed to in writing,
59493* software distributed under the License is distributed on an
59494* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
59495* KIND, either express or implied.  See the License for the
59496* specific language governing permissions and limitations
59497* under the License.
59498*/
59499
59500    var preprocessor = function (option) {
59501        if (!option || !isArray(option.series)) {
59502            return;
59503        }
59504
59505        // Translate 'k' to 'candlestick'.
59506        each$1(option.series, function (seriesItem) {
59507            if (isObject$1(seriesItem) && seriesItem.type === 'k') {
59508                seriesItem.type = 'candlestick';
59509            }
59510        });
59511    };
59512
59513    /*
59514* Licensed to the Apache Software Foundation (ASF) under one
59515* or more contributor license agreements.  See the NOTICE file
59516* distributed with this work for additional information
59517* regarding copyright ownership.  The ASF licenses this file
59518* to you under the Apache License, Version 2.0 (the
59519* "License"); you may not use this file except in compliance
59520* with the License.  You may obtain a copy of the License at
59521*
59522*   http://www.apache.org/licenses/LICENSE-2.0
59523*
59524* Unless required by applicable law or agreed to in writing,
59525* software distributed under the License is distributed on an
59526* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
59527* KIND, either express or implied.  See the License for the
59528* specific language governing permissions and limitations
59529* under the License.
59530*/
59531
59532    var positiveBorderColorQuery = ['itemStyle', 'borderColor'];
59533    var negativeBorderColorQuery = ['itemStyle', 'borderColor0'];
59534    var positiveColorQuery = ['itemStyle', 'color'];
59535    var negativeColorQuery = ['itemStyle', 'color0'];
59536
59537    var candlestickVisual = {
59538
59539        seriesType: 'candlestick',
59540
59541        plan: createRenderPlanner(),
59542
59543        // For legend.
59544        performRawSeries: true,
59545
59546        reset: function (seriesModel, ecModel) {
59547
59548            var data = seriesModel.getData();
59549            var isLargeRender = seriesModel.pipelineContext.large;
59550
59551            data.setVisual({
59552                legendSymbol: 'roundRect',
59553                colorP: getColor(1, seriesModel),
59554                colorN: getColor(-1, seriesModel),
59555                borderColorP: getBorderColor(1, seriesModel),
59556                borderColorN: getBorderColor(-1, seriesModel)
59557            });
59558
59559            // Only visible series has each data be visual encoded
59560            if (ecModel.isSeriesFiltered(seriesModel)) {
59561                return;
59562            }
59563
59564            return !isLargeRender && {progress: progress};
59565
59566
59567            function progress(params, data) {
59568                var dataIndex;
59569                while ((dataIndex = params.next()) != null) {
59570                    var itemModel = data.getItemModel(dataIndex);
59571                    var sign = data.getItemLayout(dataIndex).sign;
59572
59573                    data.setItemVisual(
59574                        dataIndex,
59575                        {
59576                            color: getColor(sign, itemModel),
59577                            borderColor: getBorderColor(sign, itemModel)
59578                        }
59579                    );
59580                }
59581            }
59582
59583            function getColor(sign, model) {
59584                return model.get(
59585                    sign > 0 ? positiveColorQuery : negativeColorQuery
59586                );
59587            }
59588
59589            function getBorderColor(sign, model) {
59590                return model.get(
59591                    sign > 0 ? positiveBorderColorQuery : negativeBorderColorQuery
59592                );
59593            }
59594
59595        }
59596
59597    };
59598
59599    /*
59600* Licensed to the Apache Software Foundation (ASF) under one
59601* or more contributor license agreements.  See the NOTICE file
59602* distributed with this work for additional information
59603* regarding copyright ownership.  The ASF licenses this file
59604* to you under the Apache License, Version 2.0 (the
59605* "License"); you may not use this file except in compliance
59606* with the License.  You may obtain a copy of the License at
59607*
59608*   http://www.apache.org/licenses/LICENSE-2.0
59609*
59610* Unless required by applicable law or agreed to in writing,
59611* software distributed under the License is distributed on an
59612* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
59613* KIND, either express or implied.  See the License for the
59614* specific language governing permissions and limitations
59615* under the License.
59616*/
59617
59618    var LargeArr$1 = typeof Float32Array !== 'undefined' ? Float32Array : Array;
59619
59620    var candlestickLayout = {
59621
59622        seriesType: 'candlestick',
59623
59624        plan: createRenderPlanner(),
59625
59626        reset: function (seriesModel) {
59627
59628            var coordSys = seriesModel.coordinateSystem;
59629            var data = seriesModel.getData();
59630            var candleWidth = calculateCandleWidth(seriesModel, data);
59631            var cDimIdx = 0;
59632            var vDimIdx = 1;
59633            var coordDims = ['x', 'y'];
59634            var cDim = data.mapDimension(coordDims[cDimIdx]);
59635            var vDims = data.mapDimension(coordDims[vDimIdx], true);
59636            var openDim = vDims[0];
59637            var closeDim = vDims[1];
59638            var lowestDim = vDims[2];
59639            var highestDim = vDims[3];
59640
59641            data.setLayout({
59642                candleWidth: candleWidth,
59643                // The value is experimented visually.
59644                isSimpleBox: candleWidth <= 1.3
59645            });
59646
59647            if (cDim == null || vDims.length < 4) {
59648                return;
59649            }
59650
59651            return {
59652                progress: seriesModel.pipelineContext.large
59653                    ? largeProgress : normalProgress
59654            };
59655
59656            function normalProgress(params, data) {
59657                var dataIndex;
59658                while ((dataIndex = params.next()) != null) {
59659
59660                    var axisDimVal = data.get(cDim, dataIndex);
59661                    var openVal = data.get(openDim, dataIndex);
59662                    var closeVal = data.get(closeDim, dataIndex);
59663                    var lowestVal = data.get(lowestDim, dataIndex);
59664                    var highestVal = data.get(highestDim, dataIndex);
59665
59666                    var ocLow = Math.min(openVal, closeVal);
59667                    var ocHigh = Math.max(openVal, closeVal);
59668
59669                    var ocLowPoint = getPoint(ocLow, axisDimVal);
59670                    var ocHighPoint = getPoint(ocHigh, axisDimVal);
59671                    var lowestPoint = getPoint(lowestVal, axisDimVal);
59672                    var highestPoint = getPoint(highestVal, axisDimVal);
59673
59674                    var ends = [];
59675                    addBodyEnd(ends, ocHighPoint, 0);
59676                    addBodyEnd(ends, ocLowPoint, 1);
59677
59678                    ends.push(
59679                        subPixelOptimizePoint(highestPoint),
59680                        subPixelOptimizePoint(ocHighPoint),
59681                        subPixelOptimizePoint(lowestPoint),
59682                        subPixelOptimizePoint(ocLowPoint)
59683                    );
59684
59685                    data.setItemLayout(dataIndex, {
59686                        sign: getSign(data, dataIndex, openVal, closeVal, closeDim),
59687                        initBaseline: openVal > closeVal
59688                            ? ocHighPoint[vDimIdx] : ocLowPoint[vDimIdx], // open point.
59689                        ends: ends,
59690                        brushRect: makeBrushRect(lowestVal, highestVal, axisDimVal)
59691                    });
59692                }
59693
59694                function getPoint(val, axisDimVal) {
59695                    var p = [];
59696                    p[cDimIdx] = axisDimVal;
59697                    p[vDimIdx] = val;
59698                    return (isNaN(axisDimVal) || isNaN(val))
59699                        ? [NaN, NaN]
59700                        : coordSys.dataToPoint(p);
59701                }
59702
59703                function addBodyEnd(ends, point, start) {
59704                    var point1 = point.slice();
59705                    var point2 = point.slice();
59706
59707                    point1[cDimIdx] = subPixelOptimize(
59708                        point1[cDimIdx] + candleWidth / 2, 1, false
59709                    );
59710                    point2[cDimIdx] = subPixelOptimize(
59711                        point2[cDimIdx] - candleWidth / 2, 1, true
59712                    );
59713
59714                    start
59715                        ? ends.push(point1, point2)
59716                        : ends.push(point2, point1);
59717                }
59718
59719                function makeBrushRect(lowestVal, highestVal, axisDimVal) {
59720                    var pmin = getPoint(lowestVal, axisDimVal);
59721                    var pmax = getPoint(highestVal, axisDimVal);
59722
59723                    pmin[cDimIdx] -= candleWidth / 2;
59724                    pmax[cDimIdx] -= candleWidth / 2;
59725
59726                    return {
59727                        x: pmin[0],
59728                        y: pmin[1],
59729                        width: vDimIdx ? candleWidth : pmax[0] - pmin[0],
59730                        height: vDimIdx ? pmax[1] - pmin[1] : candleWidth
59731                    };
59732                }
59733
59734                function subPixelOptimizePoint(point) {
59735                    point[cDimIdx] = subPixelOptimize(point[cDimIdx], 1);
59736                    return point;
59737                }
59738            }
59739
59740            function largeProgress(params, data) {
59741                // Structure: [sign, x, yhigh, ylow, sign, x, yhigh, ylow, ...]
59742                var points = new LargeArr$1(params.count * 5);
59743                var offset = 0;
59744                var point;
59745                var tmpIn = [];
59746                var tmpOut = [];
59747                var dataIndex;
59748
59749                while ((dataIndex = params.next()) != null) {
59750                    var axisDimVal = data.get(cDim, dataIndex);
59751                    var openVal = data.get(openDim, dataIndex);
59752                    var closeVal = data.get(closeDim, dataIndex);
59753                    var lowestVal = data.get(lowestDim, dataIndex);
59754                    var highestVal = data.get(highestDim, dataIndex);
59755
59756                    if (isNaN(axisDimVal) || isNaN(lowestVal) || isNaN(highestVal)) {
59757                        points[offset++] = NaN;
59758                        offset += 4;
59759                        continue;
59760                    }
59761
59762                    points[offset++] = getSign(data, dataIndex, openVal, closeVal, closeDim);
59763
59764                    tmpIn[cDimIdx] = axisDimVal;
59765
59766                    tmpIn[vDimIdx] = lowestVal;
59767                    point = coordSys.dataToPoint(tmpIn, null, tmpOut);
59768                    points[offset++] = point ? point[0] : NaN;
59769                    points[offset++] = point ? point[1] : NaN;
59770                    tmpIn[vDimIdx] = highestVal;
59771                    point = coordSys.dataToPoint(tmpIn, null, tmpOut);
59772                    points[offset++] = point ? point[1] : NaN;
59773                }
59774
59775                data.setLayout('largePoints', points);
59776            }
59777        }
59778    };
59779
59780    function getSign(data, dataIndex, openVal, closeVal, closeDim) {
59781        var sign;
59782        if (openVal > closeVal) {
59783            sign = -1;
59784        }
59785        else if (openVal < closeVal) {
59786            sign = 1;
59787        }
59788        else {
59789            sign = dataIndex > 0
59790                // If close === open, compare with close of last record
59791                ? (data.get(closeDim, dataIndex - 1) <= closeVal ? 1 : -1)
59792                // No record of previous, set to be positive
59793                : 1;
59794        }
59795
59796        return sign;
59797    }
59798
59799    function calculateCandleWidth(seriesModel, data) {
59800        var baseAxis = seriesModel.getBaseAxis();
59801        var extent;
59802
59803        var bandWidth = baseAxis.type === 'category'
59804            ? baseAxis.getBandWidth()
59805            : (
59806                extent = baseAxis.getExtent(),
59807                Math.abs(extent[1] - extent[0]) / data.count()
59808            );
59809
59810        var barMaxWidth = parsePercent$1(
59811            retrieve2(seriesModel.get('barMaxWidth'), bandWidth),
59812            bandWidth
59813        );
59814        var barMinWidth = parsePercent$1(
59815            retrieve2(seriesModel.get('barMinWidth'), 1),
59816            bandWidth
59817        );
59818        var barWidth = seriesModel.get('barWidth');
59819
59820        return barWidth != null
59821            ? parsePercent$1(barWidth, bandWidth)
59822            // Put max outer to ensure bar visible in spite of overlap.
59823            : Math.max(Math.min(bandWidth / 2, barMaxWidth), barMinWidth);
59824    }
59825
59826    /*
59827* Licensed to the Apache Software Foundation (ASF) under one
59828* or more contributor license agreements.  See the NOTICE file
59829* distributed with this work for additional information
59830* regarding copyright ownership.  The ASF licenses this file
59831* to you under the Apache License, Version 2.0 (the
59832* "License"); you may not use this file except in compliance
59833* with the License.  You may obtain a copy of the License at
59834*
59835*   http://www.apache.org/licenses/LICENSE-2.0
59836*
59837* Unless required by applicable law or agreed to in writing,
59838* software distributed under the License is distributed on an
59839* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
59840* KIND, either express or implied.  See the License for the
59841* specific language governing permissions and limitations
59842* under the License.
59843*/
59844
59845    registerPreprocessor(preprocessor);
59846    registerVisual(candlestickVisual);
59847    registerLayout(candlestickLayout);
59848
59849    /*
59850* Licensed to the Apache Software Foundation (ASF) under one
59851* or more contributor license agreements.  See the NOTICE file
59852* distributed with this work for additional information
59853* regarding copyright ownership.  The ASF licenses this file
59854* to you under the Apache License, Version 2.0 (the
59855* "License");
59855 you may not use this file except in compliance
59856* with the License.  You may obtain a copy of the License at
59857*
59858*   http://www.apache.org/licenses/LICENSE-2.0
59859*
59860* Unless required by applicable law or agreed to in writing,
59861* software distributed under the License is distributed on an
59862* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
59863* KIND, either express or implied.  See the License for the
59864* specific language governing permissions and limitations
59865* under the License.
59866*/
59867
59868    SeriesModel.extend({
59869
59870        type: 'series.effectScatter',
59871
59872        dependencies: ['grid', 'polar'],
59873
59874        getInitialData: function (option, ecModel) {
59875            return createListFromArray(this.getSource(), this);
59876        },
59877
59878        brushSelector: 'point',
59879
59880        defaultOption: {
59881            coordinateSystem: 'cartesian2d',
59882            zlevel: 0,
59883            z: 2,
59884            legendHoverLink: true,
59885
59886            effectType: 'ripple',
59887
59888            progressive: 0,
59889
59890            // When to show the effect, option: 'render'|'emphasis'
59891            showEffectOn: 'render',
59892
59893            // Ripple effect config
59894            rippleEffect: {
59895                period: 4,
59896                // Scale of ripple
59897                scale: 2.5,
59898                // Brush type can be fill or stroke
59899                brushType: 'fill'
59900            },
59901
59902            // Cartesian coordinate system
59903            // xAxisIndex: 0,
59904            // yAxisIndex: 0,
59905
59906            // Polar coordinate system
59907            // polarIndex: 0,
59908
59909            // Geo coordinate system
59910            // geoIndex: 0,
59911
59912            // symbol: null,        // 图形类型
59913            symbolSize: 10          // 图形大小,半宽(半径)参数,当图形为方向或菱形则总宽度为symbolSize * 2
59914            // symbolRotate: null,  // 图形旋转控制
59915
59916            // large: false,
59917            // Available when large is true
59918            // largeThreshold: 2000,
59919
59920            // itemStyle: {
59921            //     opacity: 1
59922            // }
59923        }
59924
59925    });
59926
59927    /*
59928* Licensed to the Apache Software Foundation (ASF) under one
59929* or more contributor license agreements.  See the NOTICE file
59930* distributed with this work for additional information
59931* regarding copyright ownership.  The ASF licenses this file
59932* to you under the Apache License, Version 2.0 (the
59933* "License"); you may not use this file except in compliance
59934* with the License.  You may obtain a copy of the License at
59935*
59936*   http://www.apache.org/licenses/LICENSE-2.0
59937*
59938* Unless required by applicable law or agreed to in writing,
59939* software distributed under the License is distributed on an
59940* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
59941* KIND, either express or implied.  See the License for the
59942* specific language governing permissions and limitations
59943* under the License.
59944*/
59945
59946    /**
59947     * Symbol with ripple effect
59948     * @module echarts/chart/helper/EffectSymbol
59949     */
59950
59951    var EFFECT_RIPPLE_NUMBER = 3;
59952
59953    function normalizeSymbolSize$1(symbolSize) {
59954        if (!isArray(symbolSize)) {
59955            symbolSize = [+symbolSize, +symbolSize];
59956        }
59957        return symbolSize;
59958    }
59959
59960    function updateRipplePath(rippleGroup, effectCfg) {
59961        rippleGroup.eachChild(function (ripplePath) {
59962            ripplePath.attr({
59963                z: effectCfg.z,
59964                zlevel: effectCfg.zlevel,
59965                style: {
59966                    stroke: effectCfg.brushType === 'stroke' ? effectCfg.color : null,
59967                    fill: effectCfg.brushType === 'fill' ? effectCfg.color : null
59968                }
59969            });
59970        });
59971    }
59972    /**
59973     * @constructor
59974     * @param {module:echarts/data/List} data
59975     * @param {number} idx
59976     * @extends {module:zrender/graphic/Group}
59977     */
59978    function EffectSymbol(data, idx) {
59979        Group.call(this);
59980
59981        var symbol = new SymbolClz$1(data, idx);
59982        var rippleGroup = new Group();
59983        this.add(symbol);
59984        this.add(rippleGroup);
59985
59986        rippleGroup.beforeUpdate = function () {
59987            this.attr(symbol.getScale());
59988        };
59989        this.updateData(data, idx);
59990    }
59991
59992    var effectSymbolProto = EffectSymbol.prototype;
59993
59994    effectSymbolProto.stopEffectAnimation = function () {
59995        this.childAt(1).removeAll();
59996    };
59997
59998    effectSymbolProto.startEffectAnimation = function (effectCfg) {
59999        var symbolType = effectCfg.symbolType;
60000        var color = effectCfg.color;
60001        var rippleGroup = this.childAt(1);
60002
60003        for (var i = 0; i < EFFECT_RIPPLE_NUMBER; i++) {
60004            // var ripplePath = createSymbol(
60005            //     symbolType, -0.5, -0.5, 1, 1, color
60006            // );
60007            // If width/height are set too small (e.g., set to 1) on ios10
60008            // and macOS Sierra, a circle stroke become a rect, no matter what
60009            // the scale is set. So we set width/height as 2. See #4136.
60010            var ripplePath = createSymbol(
60011                symbolType, -1, -1, 2, 2, color
60012            );
60013            ripplePath.attr({
60014                style: {
60015                    strokeNoScale: true
60016                },
60017                z2: 99,
60018                silent: true,
60019                scale: [0.5, 0.5]
60020            });
60021
60022            var delay = -i / EFFECT_RIPPLE_NUMBER * effectCfg.period + effectCfg.effectOffset;
60023            // TODO Configurable effectCfg.period
60024            ripplePath.animate('', true)
60025                .when(effectCfg.period, {
60026                    scale: [effectCfg.rippleScale / 2, effectCfg.rippleScale / 2]
60027                })
60028                .delay(delay)
60029                .start();
60030            ripplePath.animateStyle(true)
60031                .when(effectCfg.period, {
60032                    opacity: 0
60033                })
60034                .delay(delay)
60035                .start();
60036
60037            rippleGroup.add(ripplePath);
60038        }
60039
60040        updateRipplePath(rippleGroup, effectCfg);
60041    };
60042
60043    /**
60044     * Update effect symbol
60045     */
60046    effectSymbolProto.updateEffectAnimation = function (effectCfg) {
60047        var oldEffectCfg = this._effectCfg;
60048        var rippleGroup = this.childAt(1);
60049
60050        // Must reinitialize effect if following configuration changed
60051        var DIFFICULT_PROPS = ['symbolType', 'period', 'rippleScale'];
60052        for (var i = 0; i < DIFFICULT_PROPS.length; i++) {
60053            var propName = DIFFICULT_PROPS[i];
60054            if (oldEffectCfg[propName] !== effectCfg[propName]) {
60055                this.stopEffectAnimation();
60056                this.startEffectAnimation(effectCfg);
60057                return;
60058            }
60059        }
60060
60061        updateRipplePath(rippleGroup, effectCfg);
60062    };
60063
60064    /**
60065     * Highlight symbol
60066     */
60067    effectSymbolProto.highlight = function () {
60068        this.trigger('emphasis');
60069    };
60070
60071    /**
60072     * Downplay symbol
60073     */
60074    effectSymbolProto.downplay = function () {
60075        this.trigger('normal');
60076    };
60077
60078    /**
60079     * Update symbol properties
60080     * @param  {module:echarts/data/List} data
60081     * @param  {number} idx
60082     */
60083    effectSymbolProto.updateData = function (data, idx) {
60084        var seriesModel = data.hostModel;
60085
60086        this.childAt(0).updateData(data, idx);
60087
60088        var rippleGroup = this.childAt(1);
60089        var itemModel = data.getItemModel(idx);
60090        var symbolType = data.getItemVisual(idx, 'symbol');
60091        var symbolSize = normalizeSymbolSize$1(data.getItemVisual(idx, 'symbolSize'));
60092        var color = data.getItemVisual(idx, 'color');
60093
60094        rippleGroup.attr('scale', symbolSize);
60095
60096        rippleGroup.traverse(function (ripplePath) {
60097            ripplePath.attr({
60098                fill: color
60099            });
60100        });
60101
60102        var symbolOffset = itemModel.getShallow('symbolOffset');
60103        if (symbolOffset) {
60104            var pos = rippleGroup.position;
60105            pos[0] = parsePercent$1(symbolOffset[0], symbolSize[0]);
60106            pos[1] = parsePercent$1(symbolOffset[1], symbolSize[1]);
60107        }
60108        rippleGroup.rotation = (itemModel.getShallow('symbolRotate') || 0) * Math.PI / 180 || 0;
60109
60110        var effectCfg = {};
60111
60112        effectCfg.showEffectOn = seriesModel.get('showEffectOn');
60113        effectCfg.rippleScale = itemModel.get('rippleEffect.scale');
60114        effectCfg.brushType = itemModel.get('rippleEffect.brushType');
60115        effectCfg.period = itemModel.get('rippleEffect.period') * 1000;
60116        effectCfg.effectOffset = idx / data.count();
60117        effectCfg.z = itemModel.getShallow('z') || 0;
60118        effectCfg.zlevel = itemModel.getShallow('zlevel') || 0;
60119        effectCfg.symbolType = symbolType;
60120        effectCfg.color = color;
60121
60122        this.off('mouseover').off('mouseout').off('emphasis').off('normal');
60123
60124        if (effectCfg.showEffectOn === 'render') {
60125            this._effectCfg
60126                ? this.updateEffectAnimation(effectCfg)
60127                : this.startEffectAnimation(effectCfg);
60128
60129            this._effectCfg = effectCfg;
60130        }
60131        else {
60132            // Not keep old effect config
60133            this._effectCfg = null;
60134
60135            this.stopEffectAnimation();
60136            var symbol = this.childAt(0);
60137            var onEmphasis = function () {
60138                symbol.highlight();
60139                if (effectCfg.showEffectOn !== 'render') {
60140                    this.startEffectAnimation(effectCfg);
60141                }
60142            };
60143            var onNormal = function () {
60144                symbol.downplay();
60145                if (effectCfg.showEffectOn !== 'render') {
60146                    this.stopEffectAnimation();
60147                }
60148            };
60149            this.on('mouseover', onEmphasis, this)
60150                .on('mouseout', onNormal, this)
60151                .on('emphasis', onEmphasis, this)
60152                .on('normal', onNormal, this);
60153        }
60154
60155        this._effectCfg = effectCfg;
60156    };
60157
60158    effectSymbolProto.fadeOut = function (cb) {
60159        this.off('mouseover').off('mouseout').off('emphasis').off('normal');
60160        cb && cb();
60161    };
60162
60163    inherits(EffectSymbol, Group);
60164
60165    /*
60166* Licensed to the Apache Software Foundation (ASF) under one
60167* or more contributor license agreements.  See the NOTICE file
60168* distributed with this work for additional information
60169* regarding copyright ownership.  The ASF licenses this file
60170* to you under the Apache License, Version 2.0 (the
60171* "License"); you may not use this file except in compliance
60172* with the License.  You may obtain a copy of the License at
60173*
60174*   http://www.apache.org/licenses/LICENSE-2.0
60175*
60176* Unless required by applicable law or agreed to in writing,
60177* software distributed under the License is distributed on an
60178* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
60179* KIND, either express or implied.  See the License for the
60180* specific language governing permissions and limitations
60181* under the License.
60182*/
60183
60184    extendChartView({
60185
60186        type: 'effectScatter',
60187
60188        init: function () {
60189            this._symbolDraw = new SymbolDraw(EffectSymbol);
60190        },
60191
60192        render: function (seriesModel, ecModel, api) {
60193            var data = seriesModel.getData();
60194            var effectSymbolDraw = this._symbolDraw;
60195            effectSymbolDraw.updateData(data);
60196            this.group.add(effectSymbolDraw.group);
60197        },
60198
60199        updateTransform: function (seriesModel, ecModel, api) {
60200            var data = seriesModel.getData();
60201
60202            this.group.dirty();
60203
60204            var res = pointsLayout().reset(seriesModel);
60205            if (res.progress) {
60206                res.progress({ start: 0, end: data.count() }, data);
60207            }
60208
60209            this._symbolDraw.updateLayout(data);
60210        },
60211
60212        _updateGroupTransform: function (seriesModel) {
60213            var coordSys = seriesModel.coordinateSystem;
60214            if (coordSys && coordSys.getRoamTransform) {
60215                this.group.transform = clone$2(coordSys.getRoamTransform());
60216                this.group.decomposeTransform();
60217            }
60218        },
60219
60220        remove: function (ecModel, api) {
60221            this._symbolDraw && this._symbolDraw.remove(api);
60222        },
60223
60224        dispose: function () {}
60225    });
60226
60227    /*
60228* Licensed to the Apache Software Foundation (ASF) under one
60229* or more contributor license agreements.  See the NOTICE file
60230* distributed with this work for additional information
60231* regarding copyright ownership.  The ASF licenses this file
60232* to you under the Apache License, Version 2.0 (the
60233* "License");
60233 you may not use this file except in compliance
60234* with the License.  You may obtain a copy of the License at
60235*
60236*   http://www.apache.org/licenses/LICENSE-2.0
60237*
60238* Unless required by applicable law or agreed to in writing,
60239* software distributed under the License is distributed on an
60240* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
60241* KIND, either express or implied.  See the License for the
60242* specific language governing permissions and limitations
60243* under the License.
60244*/
60245
60246    registerVisual(visualSymbol('effectScatter', 'circle'));
60247    registerLayout(pointsLayout('effectScatter'));
60248
60249    /*
60250* Licensed to the Apache Software Foundation (ASF) under one
60251* or more contributor license agreements.  See the NOTICE file
60252* distributed with this work for additional information
60253* regarding copyright ownership.  The ASF licenses this file
60254* to you under the Apache License, Version 2.0 (the
60255* "License"); you may not use this file except in compliance
60256* with the License.  You may obtain a copy of the License at
60257*
60258*   http://www.apache.org/licenses/LICENSE-2.0
60259*
60260* Unless required by applicable law or agreed to in writing,
60261* software distributed under the License is distributed on an
60262* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
60263* KIND, either express or implied.  See the License for the
60264* specific language governing permissions and limitations
60265* under the License.
60266*/
60267
60268    var Uint32Arr = typeof Uint32Array === 'undefined' ? Array : Uint32Array;
60269    var Float64Arr = typeof Float64Array === 'undefined' ? Array : Float64Array;
60270
60271    function compatEc2(seriesOpt) {
60272        var data = seriesOpt.data;
60273        if (data && data[0] && data[0][0] && data[0][0].coord) {
60274            if (__DEV__) {
60275                console.warn('Lines data configuration has been changed to'
60276                    + ' { coords:[[1,2],[2,3]] }');
60277            }
60278            seriesOpt.data = map(data, function (itemOpt) {
60279                var coords = [
60280                    itemOpt[0].coord, itemOpt[1].coord
60281                ];
60282                var target = {
60283                    coords: coords
60284                };
60285                if (itemOpt[0].name) {
60286                    target.fromName = itemOpt[0].name;
60287                }
60288                if (itemOpt[1].name) {
60289                    target.toName = itemOpt[1].name;
60290                }
60291                return mergeAll([target, itemOpt[0], itemOpt[1]]);
60292            });
60293        }
60294    }
60295
60296    var LinesSeries = SeriesModel.extend({
60297
60298        type: 'series.lines',
60299
60300        dependencies: ['grid', 'polar'],
60301
60302        visualColorAccessPath: 'lineStyle.color',
60303
60304        init: function (option) {
60305            // The input data may be null/undefined.
60306            option.data = option.data || [];
60307
60308            // Not using preprocessor because mergeOption may not have series.type
60309            compatEc2(option);
60310
60311            var result = this._processFlatCoordsArray(option.data);
60312            this._flatCoords = result.flatCoords;
60313            this._flatCoordsOffset = result.flatCoordsOffset;
60314            if (result.flatCoords) {
60315                option.data = new Float32Array(result.count);
60316            }
60317
60318            LinesSeries.superApply(this, 'init', arguments);
60319        },
60320
60321        mergeOption: function (option) {
60322            // The input data may be null/undefined.
60323            option.data = option.data || [];
60324
60325            compatEc2(option);
60326
60327            if (option.data) {
60328                // Only update when have option data to merge.
60329                var result = this._processFlatCoordsArray(option.data);
60330                this._flatCoords = result.flatCoords;
60331                this._flatCoordsOffset = result.flatCoordsOffset;
60332                if (result.flatCoords) {
60333                    option.data = new Float32Array(result.count);
60334                }
60335            }
60336
60337            LinesSeries.superApply(this, 'mergeOption', arguments);
60338        },
60339
60340        appendData: function (params) {
60341            var result = this._processFlatCoordsArray(params.data);
60342            if (result.flatCoords) {
60343                if (!this._flatCoords) {
60344                    this._flatCoords = result.flatCoords;
60345                    this._flatCoordsOffset = result.flatCoordsOffset;
60346                }
60347                else {
60348                    this._flatCoords = concatArray(this._flatCoords, result.flatCoords);
60349                    this._flatCoordsOffset = concatArray(this._flatCoordsOffset, result.flatCoordsOffset);
60350                }
60351                params.data = new Float32Array(result.count);
60352            }
60353
60354            this.getRawData().appendData(params.data);
60355        },
60356
60357        _getCoordsFromItemModel: function (idx) {
60358            var itemModel = this.getData().getItemModel(idx);
60359            var coords = (itemModel.option instanceof Array)
60360                ? itemModel.option : itemModel.getShallow('coords');
60361
60362            if (__DEV__) {
60363                if (!(coords instanceof Array && coords.length > 0 && coords[0] instanceof Array)) {
60364                    throw new Error('Invalid coords ' + JSON.stringify(coords) + '. Lines must have 2d coords array in data item.');
60365                }
60366            }
60367            return coords;
60368        },
60369
60370        getLineCoordsCount: function (idx) {
60371            if (this._flatCoordsOffset) {
60372                return this._flatCoordsOffset[idx * 2 + 1];
60373            }
60374            else {
60375                return this._getCoordsFromItemModel(idx).length;
60376            }
60377        },
60378
60379        getLineCoords: function (idx, out) {
60380            if (this._flatCoordsOffset) {
60381                var offset = this._flatCoordsOffset[idx * 2];
60382                var len = this._flatCoordsOffset[idx * 2 + 1];
60383                for (var i = 0; i < len; i++) {
60384                    out[i] = out[i] || [];
60385                    out[i][0] = this._flatCoords[offset + i * 2];
60386                    out[i][1] = this._flatCoords[offset + i * 2 + 1];
60387                }
60388                return len;
60389            }
60390            else {
60391                var coords = this._getCoordsFromItemModel(idx);
60392                for (var i = 0; i < coords.length; i++) {
60393                    out[i] = out[i] || [];
60394                    out[i][0] = coords[i][0];
60395                    out[i][1] = coords[i][1];
60396                }
60397                return coords.length;
60398            }
60399        },
60400
60401        _processFlatCoordsArray: function (data) {
60402            var startOffset = 0;
60403            if (this._flatCoords) {
60404                startOffset = this._flatCoords.length;
60405            }
60406            // Stored as a typed array. In format
60407            // Points Count(2) | x | y | x | y | Points Count(3) | x |  y | x | y | x | y |
60408            if (typeof data[0] === 'number') {
60409                var len = data.length;
60410                // Store offset and len of each segment
60411                var coordsOffsetAndLenStorage = new Uint32Arr(len);
60412                var coordsStorage = new Float64Arr(len);
60413                var coordsCursor = 0;
60414                var offsetCursor = 0;
60415                var dataCount = 0;
60416                for (var i = 0; i < len;) {
60417                    dataCount++;
60418                    var count = data[i++];
60419                    // Offset
60420                    coordsOffsetAndLenStorage[offsetCursor++] = coordsCursor + startOffset;
60421                    // Len
60422                    coordsOffsetAndLenStorage[offsetCursor++] = count;
60423                    for (var k = 0; k < count; k++) {
60424                        var x = data[i++];
60425                        var y = data[i++];
60426                        coordsStorage[coordsCursor++] = x;
60427                        coordsStorage[coordsCursor++] = y;
60428
60429                        if (i > len) {
60430                            if (__DEV__) {
60431                                throw new Error('Invalid data format.');
60432                            }
60433                        }
60434                    }
60435                }
60436
60437                return {
60438                    flatCoordsOffset: new Uint32Array(coordsOffsetAndLenStorage.buffer, 0, offsetCursor),
60439                    flatCoords: coordsStorage,
60440                    count: dataCount
60441                };
60442            }
60443
60444            return {
60445                flatCoordsOffset: null,
60446                flatCoords: null,
60447                count: data.length
60448            };
60449        },
60450
60451        getInitialData: function (option, ecModel) {
60452            if (__DEV__) {
60453                var CoordSys = CoordinateSystemManager.get(option.coordinateSystem);
60454                if (!CoordSys) {
60455                    throw new Error('Unkown coordinate system ' + option.coordinateSystem);
60456                }
60457            }
60458
60459            var lineData = new List(['value'], this);
60460            lineData.hasItemOption = false;
60461
60462            lineData.initData(option.data, [], function (dataItem, dimName, dataIndex, dimIndex) {
60463                // dataItem is simply coords
60464                if (dataItem instanceof Array) {
60465                    return NaN;
60466                }
60467                else {
60468                    lineData.hasItemOption = true;
60469                    var value = dataItem.value;
60470                    if (value != null) {
60471                        return value instanceof Array ? value[dimIndex] : value;
60472                    }
60473                }
60474            });
60475
60476            return lineData;
60477        },
60478
60479        formatTooltip: function (dataIndex) {
60480            var data = this.getData();
60481            var itemModel = data.getItemModel(dataIndex);
60482            var name = itemModel.get('name');
60483            if (name) {
60484                return name;
60485            }
60486            var fromName = itemModel.get('fromName');
60487            var toName = itemModel.get('toName');
60488            var html = [];
60489            fromName != null && html.push(fromName);
60490            toName != null && html.push(toName);
60491
60492            return encodeHTML(html.join(' > '));
60493        },
60494
60495        preventIncremental: function () {
60496            return !!this.get('effect.show');
60497        },
60498
60499        getProgressive: function () {
60500            var progressive = this.option.progressive;
60501            if (progressive == null) {
60502                return this.option.large ? 1e4 : this.get('progressive');
60503            }
60504            return progressive;
60505        },
60506
60507        getProgressiveThreshold: function () {
60508            var progressiveThreshold = this.option.progressiveThreshold;
60509            if (progressiveThreshold == null) {
60510                return this.option.large ? 2e4 : this.get('progressiveThreshold');
60511            }
60512            return progressiveThreshold;
60513        },
60514
60515        defaultOption: {
60516            coordinateSystem: 'geo',
60517            zlevel: 0,
60518            z: 2,
60519            legendHoverLink: true,
60520
60521            hoverAnimation: true,
60522            // Cartesian coordinate system
60523            xAxisIndex: 0,
60524            yAxisIndex: 0,
60525
60526            symbol: ['none', 'none'],
60527            symbolSize: [10, 10],
60528            // Geo coordinate system
60529            geoIndex: 0,
60530
60531            effect: {
60532                show: false,
60533                period: 4,
60534                // Animation delay. support callback
60535                // delay: 0,
60536                // If move with constant speed px/sec
60537                // period will be ignored if this property is > 0,
60538                constantSpeed: 0,
60539                symbol: 'circle',
60540                symbolSize: 3,
60541                loop: true,
60542                // Length of trail, 0 - 1
60543                trailLength: 0.2
60544                // Same with lineStyle.color
60545                // color
60546            },
60547
60548            large: false,
60549            // Available when large is true
60550            largeThreshold: 2000,
60551
60552            // If lines are polyline
60553            // polyline not support curveness, label, animation
60554            polyline: false,
60555
60556            label: {
60557                show: false,
60558                position: 'end'
60559                // distance: 5,
60560                // formatter: æ ‡ç­¾æ–‡æœ¬æ ¼å¼å™¨ï¼ŒåŒTooltip.formatter,不支持异步回调
60561            },
60562
60563            lineStyle: {
60564                opacity: 0.5
60565            }
60566        }
60567    });
60568
60569    /*
60570* Licensed to the Apache Software Foundation (ASF) under one
60571* or more contributor license agreements.  See the NOTICE file
60572* distributed with this work for additional information
60573* regarding copyright ownership.  The ASF licenses this file
60574* to you under the Apache License, Version 2.0 (the
60575* "License"); you may not use this file except in compliance
60576* with the License.  You may obtain a copy of the License at
60577*
60578*   http://www.apache.org/licenses/LICENSE-2.0
60579*
60580* Unless required by applicable law or agreed to in writing,
60581* software distributed under the License is distributed on an
60582* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
60583* KIND, either express or implied.  See the License for the
60584* specific language governing permissions and limitations
60585* under the License.
60586*/
60587
60588    /**
60589     * Provide effect for line
60590     * @module echarts/chart/helper/EffectLine
60591     */
60592
60593    /**
60594     * @constructor
60595     * @extends {module:zrender/graphic/Group}
60596     * @alias {module:echarts/chart/helper/Line}
60597     */
60598    function EffectLine(lineData, idx, seriesScope) {
60599        Group.call(this);
60600
60601        this.add(this.createLine(lineData, idx, seriesScope));
60602
60603        this._updateEffectSymbol(lineData, idx);
60604    }
60605
60606    var effectLineProto = EffectLine.prototype;
60607
60608    effectLineProto.createLine = function (lineData, idx, seriesScope) {
60609        return new Line$1(lineData, idx, seriesScope);
60610    };
60611
60612    effectLineProto._updateEffectSymbol = function (lineData, idx) {
60613        var itemModel = lineData.getItemModel(idx);
60614        var effectModel = itemModel.getModel('effect');
60615        var size = effectModel.get('symbolSize');
60616        var symbolType = effectModel.get('symbol');
60617        if (!isArray(size)) {
60618            size = [size, size];
60619        }
60620        var color = effectModel.get('color') || lineData.getItemVisual(idx, 'color');
60621        var symbol = this.childAt(1);
60622
60623        if (this._symbolType !== symbolType) {
60624            // Remove previous
60625            this.remove(symbol);
60626
60627            symbol = createSymbol(
60628                symbolType, -0.5, -0.5, 1, 1, color
60629            );
60630            symbol.z2 = 100;
60631            symbol.culling = true;
60632
60633            this.add(symbol);
60634        }
60635
60636        // Symbol may be removed if loop is false
60637        if (!symbol) {
60638            return;
60639        }
60640
60641        // Shadow color is same with color in default
60642        symbol.setStyle('shadowColor', color);
60643        symbol.setStyle(effectModel.getItemStyle(['color']));
60644
60645        symbol.attr('scale', size);
60646
60647        symbol.setColor(color);
60648        symbol.attr('scale', size);
60649
60650        this._symbolType = symbolType;
60651
60652        this._updateEffectAnimation(lineData, effectModel, idx);
60653    };
60654
60655    effectLineProto._updateEffectAnimation = function (lineData, effectModel, idx) {
60656
60657        var symbol = this.childAt(1);
60658        if (!symbol) {
60659            return;
60660        }
60661
60662        var self = this;
60663
60664        var points = lineData.getItemLayout(idx);
60665
60666        var period = effectModel.get('period') * 1000;
60667        var loop = effectModel.get('loop');
60668        var constantSpeed = effectModel.get('constantSpeed');
60669        var delayExpr = retrieve(effectModel.get('delay'), function (idx) {
60670            return idx / lineData.count() * period / 3;
60671        });
60672        var isDelayFunc = typeof delayExpr === 'function';
60673
60674        // Ignore when updating
60675        symbol.ignore = true;
60676
60677        this.updateAnimationPoints(symbol, points);
60678
60679        if (constantSpeed > 0) {
60680            period = this.getLineLength(symbol) / constantSpeed * 1000;
60681        }
60682
60683        if (period !== this._period || loop !== this._loop) {
60684
60685            symbol.stopAnimation();
60686
60687            var delay = delayExpr;
60688            if (isDelayFunc) {
60689                delay = delayExpr(idx);
60690            }
60691            if (symbol.__t > 0) {
60692                delay = -period * symbol.__t;
60693            }
60694            symbol.__t = 0;
60695            var animator = symbol.animate('', loop)
60696                .when(period, {
60697                    __t: 1
60698                })
60699                .delay(delay)
60700                .during(function () {
60701                    self.updateSymbolPosition(symbol);
60702                });
60703            if (!loop) {
60704                animator.done(function () {
60705                    self.remove(symbol);
60706                });
60707            }
60708            animator.start();
60709        }
60710
60711        this._period = period;
60712        this._loop = loop;
60713    };
60714
60715    effectLineProto.getLineLength = function (symbol) {
60716        // Not so accurate
60717        return (dist(symbol.__p1, symbol.__cp1)
60718            + dist(symbol.__cp1, symbol.__p2));
60719    };
60720
60721    effectLineProto.updateAnimationPoints = function (symbol, points) {
60722        symbol.__p1 = points[0];
60723        symbol.__p2 = points[1];
60724        symbol.__cp1 = points[2] || [
60725            (points[0][0] + points[1][0]) / 2,
60726            (points[0][1] + points[1][1]) / 2
60727        ];
60728    };
60729
60730    effectLineProto.updateData = function (lineData, idx, seriesScope) {
60731        this.childAt(0).updateData(lineData, idx, seriesScope);
60732        this._updateEffectSymbol(lineData, idx);
60733    };
60734
60735    effectLineProto.updateSymbolPosition = function (symbol) {
60736        var p1 = symbol.__p1;
60737        var p2 = symbol.__p2;
60738        var cp1 = symbol.__cp1;
60739        var t = symbol.__t;
60740        var pos = symbol.position;
60741        var quadraticAt$$1 = quadraticAt;
60742        var quadraticDerivativeAt$$1 = quadraticDerivativeAt;
60743        pos[0] = quadraticAt$$1(p1[0], cp1[0], p2[0], t);
60744        pos[1] = quadraticAt$$1(p1[1], cp1[1], p2[1], t);
60745
60746        // Tangent
60747        var tx = quadraticDerivativeAt$$1(p1[0], cp1[0], p2[0], t);
60748        var ty = quadraticDerivativeAt$$1(p1[1], cp1[1], p2[1], t);
60749
60750        symbol.rotation = -Math.atan2(ty, tx) - Math.PI / 2;
60751
60752        symbol.ignore = false;
60753    };
60754
60755
60756    effectLineProto.updateLayout = function (lineData, idx) {
60757        this.childAt(0).updateLayout(lineData, idx);
60758
60759        var effectModel = lineData.getItemModel(idx).getModel('effect');
60760        this._updateEffectAnimation(lineData, effectModel, idx);
60761    };
60762
60763    inherits(EffectLine, Group);
60764
60765    /*
60766* Licensed to the Apache Software Foundation (ASF) under one
60767* or more contributor license agreements.  See the NOTICE file
60768* distributed with this work for additional information
60769* regarding copyright ownership.  The ASF licenses this file
60770* to you under the Apache License, Version 2.0 (the
60771* "License"); you may not use this file except in compliance
60772* with the License.  You may obtain a copy of the License at
60773*
60774*   http://www.apache.org/licenses/LICENSE-2.0
60775*
60776* Unless required by applicable law or agreed to in writing,
60777* software distributed under the License is distributed on an
60778* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
60779* KIND, either express or implied.  See the License for the
60780* specific language governing permissions and limitations
60781* under the License.
60782*/
60783
60784    /**
60785     * @module echarts/chart/helper/Line
60786     */
60787
60788    /**
60789     * @constructor
60790     * @extends {module:zrender/graphic/Group}
60791     * @alias {module:echarts/chart/helper/Polyline}
60792     */
60793    function Polyline$2(lineData, idx, seriesScope) {
60794        Group.call(this);
60795
60796        this._createPolyline(lineData, idx, seriesScope);
60797    }
60798
60799    var polylineProto = Polyline$2.prototype;
60800
60801    polylineProto._createPolyline = function (lineData, idx, seriesScope) {
60802        // var seriesModel = lineData.hostModel;
60803        var points = lineData.getItemLayout(idx);
60804
60805        var line = new Polyline({
60806            shape: {
60807                points: points
60808            }
60809        });
60810
60811        this.add(line);
60812
60813        this._updateCommonStl(lineData, idx, seriesScope);
60814    };
60815
60816    polylineProto.updateData = function (lineData, idx, seriesScope) {
60817        var seriesModel = lineData.hostModel;
60818
60819        var line = this.childAt(0);
60820        var target = {
60821            shape: {
60822                points: lineData.getItemLayout(idx)
60823            }
60824        };
60825        updateProps(line, target, seriesModel, idx);
60826
60827        this._updateCommonStl(lineData, idx, seriesScope);
60828    };
60829
60830    polylineProto._updateCommonStl = function (lineData, idx, seriesScope) {
60831        var line = this.childAt(0);
60832        var itemModel = lineData.getItemModel(idx);
60833
60834        var visualColor = lineData.getItemVisual(idx, 'color');
60835
60836        var lineStyle = seriesScope && seriesScope.lineStyle;
60837        var hoverLineStyle = seriesScope && seriesScope.hoverLineStyle;
60838
60839        if (!seriesScope || lineData.hasItemOption) {
60840            lineStyle = itemModel.getModel('lineStyle').getLineStyle();
60841            hoverLineStyle = itemModel.getModel('emphasis.lineStyle').getLineStyle();
60842        }
60843        line.useStyle(defaults(
60844            {
60845                strokeNoScale: true,
60846                fill: 'none',
60847                stroke: visualColor
60848            },
60849            lineStyle
60850        ));
60851        line.hoverStyle = hoverLineStyle;
60852
60853        setHoverStyle(this);
60854    };
60855
60856    polylineProto.updateLayout = function (lineData, idx) {
60857        var polyline = this.childAt(0);
60858        polyline.setShape('points', lineData.getItemLayout(idx));
60859    };
60860
60861    inherits(Polyline$2, Group);
60862
60863    /*
60864* Licensed to the Apache Software Foundation (ASF) under one
60865* or more contributor license agreements.  See the NOTICE file
60866* distributed with this work for additional information
60867* regarding copyright ownership.  The ASF licenses this file
60868* to you under the Apache License, Version 2.0 (the
60869* "License"); you may not use this file except in compliance
60870* with the License.  You may obtain a copy of the License at
60871*
60872*   http://www.apache.org/licenses/LICENSE-2.0
60873*
60874* Unless required by applicable law or agreed to in writing,
60875* software distributed under the License is distributed on an
60876* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
60877* KIND, either express or implied.  See the License for the
60878* specific language governing permissions and limitations
60879* under the License.
60880*/
60881
60882    /**
60883     * Provide effect for line
60884     * @module echarts/chart/helper/EffectLine
60885     */
60886
60887    /**
60888     * @constructor
60889     * @extends {module:echarts/chart/helper/EffectLine}
60890     * @alias {module:echarts/chart/helper/Polyline}
60891     */
60892    function EffectPolyline(lineData, idx, seriesScope) {
60893        EffectLine.call(this, lineData, idx, seriesScope);
60894        this._lastFrame = 0;
60895        this._lastFramePercent = 0;
60896    }
60897
60898    var effectPolylineProto = EffectPolyline.prototype;
60899
60900// Overwrite
60901    effectPolylineProto.createLine = function (lineData, idx, seriesScope) {
60902        return new Polyline$2(lineData, idx, seriesScope);
60903    };
60904
60905// Overwrite
60906    effectPolylineProto.updateAnimationPoints = function (symbol, points) {
60907        this._points = points;
60908        var accLenArr = [0];
60909        var len$$1 = 0;
60910        for (var i = 1; i < points.length; i++) {
60911            var p1 = points[i - 1];
60912            var p2 = points[i];
60913            len$$1 += dist(p1, p2);
60914            accLenArr.push(len$$1);
60915        }
60916        if (len$$1 === 0) {
60917            return;
60918        }
60919
60920        for (var i = 0; i < accLenArr.length; i++) {
60921            accLenArr[i] /= len$$1;
60922        }
60923        this._offsets = accLenArr;
60924        this._length = len$$1;
60925    };
60926
60927// Overwrite
60928    effectPolylineProto.getLineLength = function (symbol) {
60929        return this._length;
60930    };
60931
60932// Overwrite
60933    effectPolylineProto.updateSymbolPosition = function (symbol) {
60934        var t = symbol.__t;
60935        var points = this._points;
60936        var offsets = this._offsets;
60937        var len$$1 = points.length;
60938
60939        if (!offsets) {
60940            // Has length 0
60941            return;
60942        }
60943
60944        var lastFrame = this._lastFrame;
60945        var frame;
60946
60947        if (t < this._lastFramePercent) {
60948            // Start from the next frame
60949            // PENDING start from lastFrame ?
60950            var start = Math.min(lastFrame + 1, len$$1 - 1);
60951            for (frame = start; frame >= 0; frame--) {
60952                if (offsets[frame] <= t) {
60953                    break;
60954                }
60955            }
60956            // PENDING really need to do this ?
60957            frame = Math.min(frame, len$$1 - 2);
60958        }
60959        else {
60960            for (var frame = lastFrame; frame < len$$1; frame++) {
60961                if (offsets[frame] > t) {
60962                    break;
60963                }
60964            }
60965            frame = Math.min(frame - 1, len$$1 - 2);
60966        }
60967
60968        lerp(
60969            symbol.position, points[frame], points[frame + 1],
60970            (t - offsets[frame]) / (offsets[frame + 1] - offsets[frame])
60971        );
60972
60973        var tx = points[frame + 1][0] - points[frame][0];
60974        var ty = points[frame + 1][1] - points[frame][1];
60975        symbol.rotation = -Math.atan2(ty, tx) - Math.PI / 2;
60976
60977        this._lastFrame = frame;
60978        this._lastFramePercent = t;
60979
60980        symbol.ignore = false;
60981    };
60982
60983    inherits(EffectPolyline, EffectLine);
60984
60985    /*
60986* Licensed to the Apache Software Foundation (ASF) under one
60987* or more contributor license agreements.  See the NOTICE file
60988* distributed with this work for additional information
60989* regarding copyright ownership.  The ASF licenses this file
60990* to you under the Apache License, Version 2.0 (the
60991* "License"); you may not use this file except in compliance
60992* with the License.  You may obtain a copy of the License at
60993*
60994*   http://www.apache.org/licenses/LICENSE-2.0
60995*
60996* Unless required by applicable law or agreed to in writing,
60997* software distributed under the License is distributed on an
60998* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
60999* KIND, either express or implied.  See the License for the
61000* specific language governing permissions and limitations
61001* under the License.
61002*/
61003
61004// TODO Batch by color
61005
61006    var LargeLineShape = extendShape({
61007
61008        shape: {
61009            polyline: false,
61010            curveness: 0,
61011            segs: []
61012        },
61013
61014        buildPath: function (path, shape) {
61015            var segs = shape.segs;
61016            var curveness = shape.curveness;
61017
61018            if (shape.polyline) {
61019                for (var i = 0; i < segs.length;) {
61020                    var count = segs[i++];
61021                    if (count > 0) {
61022                        path.moveTo(segs[i++], segs[i++]);
61023                        for (var k = 1; k < count;
61023 k++) {
61024                            path.lineTo(segs[i++], segs[i++]);
61025                        }
61026                    }
61027                }
61028            }
61029            else {
61030                for (var i = 0; i < segs.length;) {
61031                    var x0 = segs[i++];
61032                    var y0 = segs[i++];
61033                    var x1 = segs[i++];
61034                    var y1 = segs[i++];
61035                    path.moveTo(x0, y0);
61036                    if (curveness > 0) {
61037                        var x2 = (x0 + x1) / 2 - (y0 - y1) * curveness;
61038                        var y2 = (y0 + y1) / 2 - (x1 - x0) * curveness;
61039                        path.quadraticCurveTo(x2, y2, x1, y1);
61040                    }
61041                    else {
61042                        path.lineTo(x1, y1);
61043                    }
61044                }
61045            }
61046        },
61047
61048        findDataIndex: function (x, y) {
61049
61050            var shape = this.shape;
61051            var segs = shape.segs;
61052            var curveness = shape.curveness;
61053
61054            if (shape.polyline) {
61055                var dataIndex = 0;
61056                for (var i = 0; i < segs.length;) {
61057                    var count = segs[i++];
61058                    if (count > 0) {
61059                        var x0 = segs[i++];
61060                        var y0 = segs[i++];
61061                        for (var k = 1; k < count; k++) {
61062                            var x1 = segs[i++];
61063                            var y1 = segs[i++];
61064                            if (containStroke$1(x0, y0, x1, y1)) {
61065                                return dataIndex;
61066                            }
61067                        }
61068                    }
61069
61070                    dataIndex++;
61071                }
61072            }
61073            else {
61074                var dataIndex = 0;
61075                for (var i = 0; i < segs.length;) {
61076                    var x0 = segs[i++];
61077                    var y0 = segs[i++];
61078                    var x1 = segs[i++];
61079                    var y1 = segs[i++];
61080                    if (curveness > 0) {
61081                        var x2 = (x0 + x1) / 2 - (y0 - y1) * curveness;
61082                        var y2 = (y0 + y1) / 2 - (x1 - x0) * curveness;
61083
61084                        if (containStroke$3(x0, y0, x2, y2, x1, y1)) {
61085                            return dataIndex;
61086                        }
61087                    }
61088                    else {
61089                        if (containStroke$1(x0, y0, x1, y1)) {
61090                            return dataIndex;
61091                        }
61092                    }
61093
61094                    dataIndex++;
61095                }
61096            }
61097
61098            return -1;
61099        }
61100    });
61101
61102    function LargeLineDraw() {
61103        this.group = new Group();
61104    }
61105
61106    var largeLineProto = LargeLineDraw.prototype;
61107
61108    largeLineProto.isPersistent = function () {
61109        return !this._incremental;
61110    };
61111
61112    /**
61113     * Update symbols draw by new data
61114     * @param {module:echarts/data/List} data
61115     */
61116    largeLineProto.updateData = function (data) {
61117        this.group.removeAll();
61118
61119        var lineEl = new LargeLineShape({
61120            rectHover: true,
61121            cursor: 'default'
61122        });
61123        lineEl.setShape({
61124            segs: data.getLayout('linesPoints')
61125        });
61126
61127        this._setCommon(lineEl, data);
61128
61129        // Add back
61130        this.group.add(lineEl);
61131
61132        this._incremental = null;
61133    };
61134
61135    /**
61136     * @override
61137     */
61138    largeLineProto.incrementalPrepareUpdate = function (data) {
61139        this.group.removeAll();
61140
61141        this._clearIncremental();
61142
61143        if (data.count() > 5e5) {
61144            if (!this._incremental) {
61145                this._incremental = new IncrementalDisplayble({
61146                    silent: true
61147                });
61148            }
61149            this.group.add(this._incremental);
61150        }
61151        else {
61152            this._incremental = null;
61153        }
61154    };
61155
61156    /**
61157     * @override
61158     */
61159    largeLineProto.incrementalUpdate = function (taskParams, data) {
61160        var lineEl = new LargeLineShape();
61161        lineEl.setShape({
61162            segs: data.getLayout('linesPoints')
61163        });
61164
61165        this._setCommon(lineEl, data, !!this._incremental);
61166
61167        if (!this._incremental) {
61168            lineEl.rectHover = true;
61169            lineEl.cursor = 'default';
61170            lineEl.__startIndex = taskParams.start;
61171            this.group.add(lineEl);
61172        }
61173        else {
61174            this._incremental.addDisplayable(lineEl, true);
61175        }
61176    };
61177
61178    /**
61179     * @override
61180     */
61181    largeLineProto.remove = function () {
61182        this._clearIncremental();
61183        this._incremental = null;
61184        this.group.removeAll();
61185    };
61186
61187    largeLineProto._setCommon = function (lineEl, data, isIncremental) {
61188        var hostModel = data.hostModel;
61189
61190        lineEl.setShape({
61191            polyline: hostModel.get('polyline'),
61192            curveness: hostModel.get('lineStyle.curveness')
61193        });
61194
61195        lineEl.useStyle(
61196            hostModel.getModel('lineStyle').getLineStyle()
61197        );
61198        lineEl.style.strokeNoScale = true;
61199
61200        var visualColor = data.getVisual('color');
61201        if (visualColor) {
61202            lineEl.setStyle('stroke', visualColor);
61203        }
61204        lineEl.setStyle('fill');
61205
61206        if (!isIncremental) {
61207            // Enable tooltip
61208            // PENDING May have performance issue when path is extremely large
61209            lineEl.seriesIndex = hostModel.seriesIndex;
61210            lineEl.on('mousemove', function (e) {
61211                lineEl.dataIndex = null;
61212                var dataIndex = lineEl.findDataIndex(e.offsetX, e.offsetY);
61213                if (dataIndex > 0) {
61214                    // Provide dataIndex for tooltip
61215                    lineEl.dataIndex = dataIndex + lineEl.__startIndex;
61216                }
61217            });
61218        }
61219    };
61220
61221    largeLineProto._clearIncremental = function () {
61222        var incremental = this._incremental;
61223        if (incremental) {
61224            incremental.clearDisplaybles();
61225        }
61226    };
61227
61228    /*
61229* Licensed to the Apache Software Foundation (ASF) under one
61230* or more contributor license agreements.  See the NOTICE file
61231* distributed with this work for additional information
61232* regarding copyright ownership.  The ASF licenses this file
61233* to you under the Apache License, Version 2.0 (the
61234* "License"); you may not use this file except in compliance
61235* with the License.  You may obtain a copy of the License at
61236*
61237*   http://www.apache.org/licenses/LICENSE-2.0
61238*
61239* Unless required by applicable law or agreed to in writing,
61240* software distributed under the License is distributed on an
61241* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
61242* KIND, either express or implied.  See the License for the
61243* specific language governing permissions and limitations
61244* under the License.
61245*/
61246
61247    var linesLayout = {
61248        seriesType: 'lines',
61249
61250        plan: createRenderPlanner(),
61251
61252        reset: function (seriesModel) {
61253            var coordSys = seriesModel.coordinateSystem;
61254            var isPolyline = seriesModel.get('polyline');
61255            var isLarge = seriesModel.pipelineContext.large;
61256
61257            function progress(params, lineData) {
61258                var lineCoords = [];
61259                if (isLarge) {
61260                    var points;
61261                    var segCount = params.end - params.start;
61262                    if (isPolyline) {
61263                        var totalCoordsCount = 0;
61264                        for (var i = params.start; i < params.end; i++) {
61265                            totalCoordsCount += seriesModel.getLineCoordsCount(i);
61266                        }
61267                        points = new Float32Array(segCount + totalCoordsCount * 2);
61268                    }
61269                    else {
61270                        points = new Float32Array(segCount * 4);
61271                    }
61272
61273                    var offset = 0;
61274                    var pt = [];
61275                    for (var i = params.start; i < params.end; i++) {
61276                        var len = seriesModel.getLineCoords(i, lineCoords);
61277                        if (isPolyline) {
61278                            points[offset++] = len;
61279                        }
61280                        for (var k = 0; k < len; k++) {
61281                            pt = coordSys.dataToPoint(lineCoords[k], false, pt);
61282                            points[offset++] = pt[0];
61283                            points[offset++] = pt[1];
61284                        }
61285                    }
61286
61287                    lineData.setLayout('linesPoints', points);
61288                }
61289                else {
61290                    for (var i = params.start; i < params.end; i++) {
61291                        var itemModel = lineData.getItemModel(i);
61292                        var len = seriesModel.getLineCoords(i, lineCoords);
61293
61294                        var pts = [];
61295                        if (isPolyline) {
61296                            for (var j = 0; j < len; j++) {
61297                                pts.push(coordSys.dataToPoint(lineCoords[j]));
61298                            }
61299                        }
61300                        else {
61301                            pts[0] = coordSys.dataToPoint(lineCoords[0]);
61302                            pts[1] = coordSys.dataToPoint(lineCoords[1]);
61303
61304                            var curveness = itemModel.get('lineStyle.curveness');
61305                            if (+curveness) {
61306                                pts[2] = [
61307                                    (pts[0][0] + pts[1][0]) / 2 - (pts[0][1] - pts[1][1]) * curveness,
61308                                    (pts[0][1] + pts[1][1]) / 2 - (pts[1][0] - pts[0][0]) * curveness
61309                                ];
61310                            }
61311                        }
61312                        lineData.setItemLayout(i, pts);
61313                    }
61314                }
61315            }
61316
61317            return { progress: progress };
61318        }
61319    };
61320
61321    /*
61322* Licensed to the Apache Software Foundation (ASF) under one
61323* or more contributor license agreements.  See the NOTICE file
61324* distributed with this work for additional information
61325* regarding copyright ownership.  The ASF licenses this file
61326* to you under the Apache License, Version 2.0 (the
61327* "License"); you may not use this file except in compliance
61328* with the License.  You may obtain a copy of the License at
61329*
61330*   http://www.apache.org/licenses/LICENSE-2.0
61331*
61332* Unless required by applicable law or agreed to in writing,
61333* software distributed under the License is distributed on an
61334* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
61335* KIND, either express or implied.  See the License for the
61336* specific language governing permissions and limitations
61337* under the License.
61338*/
61339
61340    extendChartView({
61341
61342        type: 'lines',
61343
61344        init: function () {},
61345
61346        render: function (seriesModel, ecModel, api) {
61347            var data = seriesModel.getData();
61348
61349            var lineDraw = this._updateLineDraw(data, seriesModel);
61350
61351            var zlevel = seriesModel.get('zlevel');
61352            var trailLength = seriesModel.get('effect.trailLength');
61353
61354            var zr = api.getZr();
61355            // Avoid the drag cause ghost shadow
61356            // FIXME Better way ?
61357            // SVG doesn't support
61358            var isSvg = zr.painter.getType() === 'svg';
61359            if (!isSvg) {
61360                zr.painter.getLayer(zlevel).clear(true);
61361            }
61362            // Config layer with motion blur
61363            if (this._lastZlevel != null && !isSvg) {
61364                zr.configLayer(this._lastZlevel, {
61365                    motionBlur: false
61366                });
61367            }
61368            if (this._showEffect(seriesModel) && trailLength) {
61369                if (__DEV__) {
61370                    var notInIndividual = false;
61371                    ecModel.eachSeries(function (otherSeriesModel) {
61372                        if (otherSeriesModel !== seriesModel && otherSeriesModel.get('zlevel') === zlevel) {
61373                            notInIndividual = true;
61374                        }
61375                    });
61376                    notInIndividual && console.warn('Lines with trail effect should have an individual zlevel');
61377                }
61378
61379                if (!isSvg) {
61380                    zr.configLayer(zlevel, {
61381                        motionBlur: true,
61382                        lastFrameAlpha: Math.max(Math.min(trailLength / 10 + 0.9, 1), 0)
61383                    });
61384                }
61385            }
61386
61387            lineDraw.updateData(data);
61388
61389            this._lastZlevel = zlevel;
61390
61391            this._finished = true;
61392        },
61393
61394        incrementalPrepareRender: function (seriesModel, ecModel, api) {
61395            var data = seriesModel.getData();
61396
61397            var lineDraw = this._updateLineDraw(data, seriesModel);
61398
61399            lineDraw.incrementalPrepareUpdate(data);
61400
61401            this._clearLayer(api);
61402
61403            this._finished = false;
61404        },
61405
61406        incrementalRender: function (taskParams, seriesModel, ecModel) {
61407            this._lineDraw.incrementalUpdate(taskParams, seriesModel.getData());
61408
61409            this._finished = taskParams.end === seriesModel.getData().count();
61410        },
61411
61412        updateTransform: function (seriesModel, ecModel, api) {
61413            var data = seriesModel.getData();
61414            var pipelineContext = seriesModel.pipelineContext;
61415
61416            if (!this._finished || pipelineContext.large || pipelineContext.progressiveRen
61416der) {
61417                // TODO Don't have to do update in large mode. Only do it when there are millions of data.
61418                return {
61419                    update: true
61420                };
61421            }
61422            else {
61423                // TODO Use same logic with ScatterView.
61424                // Manually update layout
61425                var res = linesLayout.reset(seriesModel);
61426                if (res.progress) {
61427                    res.progress({ start: 0, end: data.count() }, data);
61428                }
61429                this._lineDraw.updateLayout();
61430                this._clearLayer(api);
61431            }
61432        },
61433
61434        _updateLineDraw: function (data, seriesModel) {
61435            var lineDraw = this._lineDraw;
61436            var hasEffect = this._showEffect(seriesModel);
61437            var isPolyline = !!seriesModel.get('polyline');
61438            var pipelineContext = seriesModel.pipelineContext;
61439            var isLargeDraw = pipelineContext.large;
61440
61441            if (__DEV__) {
61442                if (hasEffect && isLargeDraw) {
61443                    console.warn('Large lines not support effect');
61444                }
61445            }
61446            if (!lineDraw
61447                || hasEffect !== this._hasEffet
61448                || isPolyline !== this._isPolyline
61449                || isLargeDraw !== this._isLargeDraw
61450            ) {
61451                if (lineDraw) {
61452                    lineDraw.remove();
61453                }
61454                lineDraw = this._lineDraw = isLargeDraw
61455                    ? new LargeLineDraw()
61456                    : new LineDraw(
61457                        isPolyline
61458                            ? (hasEffect ? EffectPolyline : Polyline$2)
61459                            : (hasEffect ? EffectLine : Line$1)
61460                    );
61461                this._hasEffet = hasEffect;
61462                this._isPolyline = isPolyline;
61463                this._isLargeDraw = isLargeDraw;
61464                this.group.removeAll();
61465            }
61466
61467            this.group.add(lineDraw.group);
61468
61469            return lineDraw;
61470        },
61471
61472        _showEffect: function (seriesModel) {
61473            return !!seriesModel.get('effect.show');
61474        },
61475
61476        _clearLayer: function (api) {
61477            // Not use motion when dragging or zooming
61478            var zr = api.getZr();
61479            var isSvg = zr.painter.getType() === 'svg';
61480            if (!isSvg && this._lastZlevel != null) {
61481                zr.painter.getLayer(this._lastZlevel).clear(true);
61482            }
61483        },
61484
61485        remove: function (ecModel, api) {
61486            this._lineDraw && this._lineDraw.remove();
61487            this._lineDraw = null;
61488            // Clear motion when lineDraw is removed
61489            this._clearLayer(api);
61490        },
61491
61492        dispose: function () {}
61493    });
61494
61495    /*
61496* Licensed to the Apache Software Foundation (ASF) under one
61497* or more contributor license agreements.  See the NOTICE file
61498* distributed with this work for additional information
61499* regarding copyright ownership.  The ASF licenses this file
61500* to you under the Apache License, Version 2.0 (the
61501* "License"); you may not use this file except in compliance
61502* with the License.  You may obtain a copy of the License at
61503*
61504*   http://www.apache.org/licenses/LICENSE-2.0
61505*
61506* Unless required by applicable law or agreed to in writing,
61507* software distributed under the License is distributed on an
61508* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
61509* KIND, either express or implied.  See the License for the
61510* specific language governing permissions and limitations
61511* under the License.
61512*/
61513
61514
61515    function normalize$2(a) {
61516        if (!(a instanceof Array)) {
61517            a = [a, a];
61518        }
61519        return a;
61520    }
61521
61522    var opacityQuery = 'lineStyle.opacity'.split('.');
61523
61524    var linesVisual = {
61525        seriesType: 'lines',
61526        reset: function (seriesModel, ecModel, api) {
61527            var symbolType = normalize$2(seriesModel.get('symbol'));
61528            var symbolSize = normalize$2(seriesModel.get('symbolSize'));
61529            var data = seriesModel.getData();
61530
61531            data.setVisual('fromSymbol', symbolType && symbolType[0]);
61532            data.setVisual('toSymbol', symbolType && symbolType[1]);
61533            data.setVisual('fromSymbolSize', symbolSize && symbolSize[0]);
61534            data.setVisual('toSymbolSize', symbolSize && symbolSize[1]);
61535            data.setVisual('opacity', seriesModel.get(opacityQuery));
61536
61537            function dataEach(data, idx) {
61538                var itemModel = data.getItemModel(idx);
61539                var symbolType = normalize$2(itemModel.getShallow('symbol', true));
61540                var symbolSize = normalize$2(itemModel.getShallow('symbolSize', true));
61541                var opacity = itemModel.get(opacityQuery);
61542
61543                symbolType[0] && data.setItemVisual(idx, 'fromSymbol', symbolType[0]);
61544                symbolType[1] && data.setItemVisual(idx, 'toSymbol', symbolType[1]);
61545                symbolSize[0] && data.setItemVisual(idx, 'fromSymbolSize', symbolSize[0]);
61546                symbolSize[1] && data.setItemVisual(idx, 'toSymbolSize', symbolSize[1]);
61547
61548                data.setItemVisual(idx, 'opacity', opacity);
61549            }
61550
61551            return {dataEach: data.hasItemOption ? dataEach : null};
61552        }
61553    };
61554
61555    /*
61556* Licensed to the Apache Software Foundation (ASF) under one
61557* or more contributor license agreements.  See the NOTICE file
61558* distributed with this work for additional information
61559* regarding copyright ownership.  The ASF licenses this file
61560* to you under the Apache License, Version 2.0 (the
61561* "License");
61561 you may not use this file except in compliance
61562* with the License.  You may obtain a copy of the License at
61563*
61564*   http://www.apache.org/licenses/LICENSE-2.0
61565*
61566* Unless required by applicable law or agreed to in writing,
61567* software distributed under the License is distributed on an
61568* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
61569* KIND, either express or implied.  See the License for the
61570* specific language governing permissions and limitations
61571* under the License.
61572*/
61573
61574    registerLayout(linesLayout);
61575    registerVisual(linesVisual);
61576
61577    /*
61578* Licensed to the Apache Software Foundation (ASF) under one
61579* or more contributor license agreements.  See the NOTICE file
61580* distributed with this work for additional information
61581* regarding copyright ownership.  The ASF licenses this file
61582* to you under the Apache License, Version 2.0 (the
61583* "License"); you may not use this file except in compliance
61584* with the License.  You may obtain a copy of the License at
61585*
61586*   http://www.apache.org/licenses/LICENSE-2.0
61587*
61588* Unless required by applicable law or agreed to in writing,
61589* software distributed under the License is distributed on an
61590* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
61591* KIND, either express or implied.  See the License for the
61592* specific language governing permissions and limitations
61593* under the License.
61594*/
61595
61596    SeriesModel.extend({
61597        type: 'series.heatmap',
61598
61599        getInitialData: function (option, ecModel) {
61600            return createListFromArray(this.getSource(), this, {
61601                generateCoord: 'value'
61602            });
61603        },
61604
61605        preventIncremental: function () {
61606            var coordSysCreator = CoordinateSystemManager.get(this.get('coordinateSystem'));
61607            if (coordSysCreator && coordSysCreator.dimensions) {
61608                return coordSysCreator.dimensions[0] === 'lng' && coordSysCreator.dimensions[1] === 'lat';
61609            }
61610        },
61611
61612        defaultOption: {
61613
61614            // Cartesian2D or geo
61615            coordinateSystem: 'cartesian2d',
61616
61617            zlevel: 0,
61618
61619            z: 2,
61620
61621            // Cartesian coordinate system
61622            // xAxisIndex: 0,
61623            // yAxisIndex: 0,
61624
61625            // Geo coordinate system
61626            geoIndex: 0,
61627
61628            blurSize: 30,
61629
61630            pointSize: 20,
61631
61632            maxOpacity: 1,
61633
61634            minOpacity: 0
61635        }
61636    });
61637
61638    /*
61639* Licensed to the Apache Software Foundation (ASF) under one
61640* or more contributor license agreements.  See the NOTICE file
61641* distributed with this work for additional information
61642* regarding copyright ownership.  The ASF licenses this file
61643* to you under the Apache License, Version 2.0 (the
61644* "License"); you may not use this file except in compliance
61645* with the License.  You may obtain a copy of the License at
61646*
61647*   http://www.apache.org/licenses/LICENSE-2.0
61648*
61649* Unless required by applicable law or agreed to in writing,
61650* software distributed under the License is distributed on an
61651* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
61652* KIND, either express or implied.  See the License for the
61653* specific language governing permissions and limitations
61654* under the License.
61655*/
61656
61657    /**
61658     * @file defines echarts Heatmap Chart
61659     * @author Ovilia ([email protected])
61660     * Inspired by https://github.com/mourner/simpleheat
61661     *
61662     * @module
61663     */
61664
61665    var GRADIENT_LEVELS = 256;
61666
61667    /**
61668     * Heatmap Chart
61669     *
61670     * @class
61671     */
61672    function Heatmap() {
61673        var canvas = createCanvas();
61674        this.canvas = canvas;
61675
61676        this.blurSize = 30;
61677        this.pointSize = 20;
61678
61679        this.maxOpacity = 1;
61680        this.minOpacity = 0;
61681
61682        this._gradientPixels = {};
61683    }
61684
61685    Heatmap.prototype = {
61686        /**
61687         * Renders Heatmap and returns the rendered canvas
61688         * @param {Array} data array of data, each has x, y, value
61689         * @param {number} width canvas width
61690         * @param {number} height canvas height
61691         */
61692        update: function(data, width, height, normalize, colorFunc, isInRange) {
61693            var brush = this._getBrush();
61694            var gradientInRange = this._getGradient(data, colorFunc, 'inRange');
61695            var gradientOutOfRange = this._getGradient(data, colorFunc, 'outOfRange');
61696            var r = this.pointSize + this.blurSize;
61697
61698            var canvas = this.canvas;
61699            var ctx = canvas.getContext('2d');
61700            var len = data.length;
61701            canvas.width = width;
61702            canvas.height = height;
61703            for (var i = 0; i < len; ++i) {
61704                var p = data[i];
61705                var x = p[0];
61706                var y = p[1];
61707                var value = p[2];
61708
61709                // calculate alpha using value
61710                var alpha = normalize(value);
61711
61712                // draw with the circle brush with alpha
61713                ctx.globalAlpha = alpha;
61714                ctx.drawImage(brush, x - r, y - r);
61715            }
61716
61717            if (!canvas.width || !canvas.height) {
61718                // Avoid "Uncaught DOMException: Failed to execute 'getImageData' on
61719                // 'CanvasRenderingContext2D': The source height is 0."
61720                return canvas;
61721            }
61722
61723            // colorize the canvas using alpha value and set with gradient
61724            var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
61725
61726            var pixels = imageData.data;
61727            var offset = 0;
61728            var pixelLen = pixels.length;
61729            var minOpacity = this.minOpacity;
61730            var maxOpacity = this.maxOpacity;
61731            var diffOpacity = maxOpacity - minOpacity;
61732
61733            while(offset < pixelLen) {
61734                var alpha = pixels[offset + 3] / 256;
vendor: 5,991 bytes, lines 61735-61886
61735                var gradientOffset = Math.floor(alpha * (GRADIENT_LEVELS - 1)) * 4;
61736                // Simple optimize to ignore the empty data
61737                if (alpha > 0) {
61738                    var gradient = isInRange(alpha) ? gradientInRange : gradientOutOfRange;
61739                    // Any alpha > 0 will be mapped to [minOpacity, maxOpacity]
61740                    alpha > 0 && (alpha = alpha * diffOpacity + minOpacity);
61741                    pixels[offset++] = gradient[gradientOffset];
61742                    pixels[offset++] = gradient[gradientOffset + 1];
61743                    pixels[offset++] = gradient[gradientOffset + 2];
61744                    pixels[offset++] = gradient[gradientOffset + 3] * alpha * 256;
61745                }
61746                else {
61747                    offset += 4;
61748                }
61749            }
61750            ctx.putImageData(imageData, 0, 0);
61751
61752            return canvas;
61753        },
61754
61755        /**
61756         * get canvas of a black circle brush used for canvas to draw later
61757         * @private
61758         * @returns {Object} circle brush canvas
61759         */
61760        _getBrush: function() {
61761            var brushCanvas = this._brushCanvas || (this._brushCanvas = createCanvas());
61762            // set brush size
61763            var r = this.pointSize + this.blurSize;
61764            var d = r * 2;
61765            brushCanvas.width = d;
61766            brushCanvas.height = d;
61767
61768            var ctx = brushCanvas.getContext('2d');
61769            ctx.clearRect(0, 0, d, d);
61770
61771            // in order to render shadow without the distinct circle,
61772            // draw the distinct circle in an invisible place,
61773            // and use shadowOffset to draw shadow in the center of the canvas
61774            ctx.shadowOffsetX = d;
61775            ctx.shadowBlur = this.blurSize;
61776            // draw the shadow in black, and use alpha and shadow blur to generate
61777            // color in color map
61778            ctx.shadowColor = '#000';
61779
61780            // draw circle in the left to the canvas
61781            ctx.beginPath();
61782            ctx.arc(-r, r, this.pointSize, 0, Math.PI * 2, true);
61783            ctx.closePath();
61784            ctx.fill();
61785            return brushCanvas;
61786        },
61787
61788        /**
61789         * get gradient color map
61790         * @private
61791         */
61792        _getGradient: function (data, colorFunc, state) {
61793            var gradientPixels = this._gradientPixels;
61794            var pixelsSingleState = gradientPixels[state] || (gradientPixels[state] = new Uint8ClampedArray(256 * 4));
61795            var color = [0, 0, 0, 0];
61796            var off = 0;
61797            for (var i = 0; i < 256; i++) {
61798                colorFunc[state](i / 255, true, color);
61799                pixelsSingleState[off++] = color[0];
61800                pixelsSingleState[off++] = color[1];
61801                pixelsSingleState[off++] = color[2];
61802                pixelsSingleState[off++] = color[3];
61803            }
61804            return pixelsSingleState;
61805        }
61806    };
61807
61808    /*
61809* Licensed to the Apache Software Foundation (ASF) under one
61810* or more contributor license agreements.  See the NOTICE file
61811* distributed with this work for additional information
61812* regarding copyright ownership.  The ASF licenses this file
61813* to you under the Apache License, Version 2.0 (the
61814* "License"); you may not use this file except in compliance
61815* with the License.  You may obtain a copy of the License at
61816*
61817*   http://www.apache.org/licenses/LICENSE-2.0
61818*
61819* Unless required by applicable law or agreed to in writing,
61820* software distributed under the License is distributed on an
61821* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
61822* KIND, either express or implied.  See the License for the
61823* specific language governing permissions and limitations
61824* under the License.
61825*/
61826
61827    function getIsInPiecewiseRange(dataExtent, pieceList, selected) {
61828        var dataSpan = dataExtent[1] - dataExtent[0];
61829        pieceList = map(pieceList, function (piece) {
61830            return {
61831                interval: [
61832                    (piece.interval[0] - dataExtent[0]) / dataSpan,
61833                    (piece.interval[1] - dataExtent[0]) / dataSpan
61834                ]
61835            };
61836        });
61837        var len = pieceList.length;
61838        var lastIndex = 0;
61839
61840        return function (val) {
61841            // Try to find in the location of the last found
61842            for (var i = lastIndex; i < len; i++) {
61843                var interval = pieceList[i].interval;
61844                if (interval[0] <= val && val <= interval[1]) {
61845                    lastIndex = i;
61846                    break;
61847                }
61848            }
61849            if (i === len) { // Not found, back interation
61850                for (var i = lastIndex - 1; i >= 0; i--) {
61851                    var interval = pieceList[i].interval;
61852                    if (interval[0] <= val && val <= interval[1]) {
61853                        lastIndex = i;
61854                        break;
61855                    }
61856                }
61857            }
61858            return i >= 0 && i < len && selected[i];
61859        };
61860    }
61861
61862    function getIsInContinuousRange(dataExtent, range) {
61863        var dataSpan = dataExtent[1] - dataExtent[0];
61864        range = [
61865            (range[0] - dataExtent[0]) / dataSpan,
61866            (range[1] - dataExtent[0]) / dataSpan
61867        ];
61868        return function (val) {
61869            return val >= range[0] && val <= range[1];
61870        };
61871    }
61872
61873    function isGeoCoordSys(coordSys) {
61874        var dimensions = coordSys.dimensions;
61875        // Not use coorSys.type === 'geo' because coordSys maybe extended
61876        return dimensions[0] === 'lng' && dimensions[1] === 'lat';
61877    }
61878
61879    extendChartView({
61880
61881        type: 'heatmap',
61882
61883        render: function (seriesModel, ecModel, api) {
61884            var visualMapOfThisSeries;
61885            ecModel.eachComponent('visualMap', function (visualMap) {
61886                visualMap.eachTargetSeries(function (targetSeries) {
61887                    if (targetSeries === seriesModel) {
61888                        visualMapOfThisSeries = visualMap;
61889                    }
61890                });
61891            });
61892
61893            if (__DEV__) {
61894                if (!visualMapOfThisSeries) {
61895                    throw new Error('Heatmap must use with visualMap');
61896                }
61897            }
61898
61899            this.group.removeAll();
61900
61901            this._incrementalDisplayable = null;
61902
61903            var coordSys = seriesModel.coordinateSystem;
61904            if (coordSys.type === 'cartesian2d' || coordSys.type === 'calendar') {
61905                this._renderOnCartesianAndCalendar(seriesModel, api, 0, seriesModel.getData().count());
61906            }
61907            else if (isGeoCoordSys(coordSys)) {
61908                this._renderOnGeo(
61909                    coordSys, seriesModel, visualMapOfThisSeries, api
61910                );
61911            }
61912        },
61913
61914        incrementalPrepareRender: function (seriesModel, ecModel, api) {
61915            this.group.removeAll();
61916        },
61917
61918        incrementalRender: function (params, seriesModel, ecModel, api) {
61919            var coordSys = seriesModel.coordinateSystem;
61920            if (coordSys) {
61921                this._renderOnCartesianAndCalendar(seriesModel, api, params.start, params.end, true);
61922            }
61923        },
61924
61925        _renderOnCartesianAndCalendar: function (seriesModel, api, start, end, incremental) {
61926
61927            var coordSys = seriesModel.coordinateSystem;
61928            var width;
61929            var height;
61930
61931            if (coordSys.type === 'cartesian2d') {
61932                var xAxis = coordSys.getAxis('x');
61933                var yAxis = coordSys.getAxis('y');
61934
61935                if (__DEV__) {
61936                    if (!(xAxis.type === 'category' && yAxis.type === 'category')) {
61937                        throw new Error('Heatmap on cartesian must have two category axes');
61938                    }
61939                    if (!(xAxis.onBand && yAxis.onBand)) {
61940                        throw new Error('Heatmap on cartesian must have two axes with boundaryGap true');
61941                    }
61942                }
61943
61944                width = xAxis.getBandWidth();
61945                height = yAxis.getBandWidth();
61946            }
61947
61948            var group = this.group;
61949            var data = seriesModel.getData();
61950
61951            var itemStyleQuery = 'itemStyle';
61952            var hoverItemStyleQuery = 'emphasis.itemStyle';
61953            var labelQuery = 'label';
61954            var hoverLabelQuery = 'emphasis.label';
61955            var style = seriesModel.getModel(itemStyleQuery).getItemStyle(['color']);
61956            var hoverStl = seriesModel.getModel(hoverItemStyleQuery).getItemStyle();
61957            var labelModel = seriesModel.getModel(labelQuery);
61958            var hoverLabelModel = seriesModel.getModel(hoverLabelQuery);
61959            var coordSysType = coordSys.type;
61960
61961
61962            var dataDims = coordSysType === 'cartesian2d'
61963                ? [
61964                    data.mapDimension('x'),
61965                    data.mapDimension('y'),
61966                    data.mapDimension('value')
61967                ]
61968                : [
61969                    data.mapDimension('time'),
61970                    data.mapDimension('value')
61971                ];
61972
61973            for (var idx = start; idx < end; idx++) {
61974                var rect;
61975
61976                if (coordSysType === 'cartesian2d') {
61977                    // Ignore empty data
61978                    if (isNaN(data.get(dataDims[2], idx))) {
61979                        continue;
61980                    }
61981
61982                    var point = coordSys.dataToPoint([
61983                        data.get(dataDims[0], idx),
61984                        data.get(dataDims[1], idx)
61985                    ]);
61986
61987                    rect = new Rect({
61988                        shape: {
61989                            x: point[0] - width / 2,
61990                            y: point[1] - height / 2,
61991                            width: width,
61992                            height: height
61993                        },
61994                        style: {
61995                            fill: data.getItemVisual(idx, 'color'),
61996                            opacity: data.getItemVisual(idx, 'opacity')
61997                        }
61998                    });
61999                }
62000                else {
62001                    // Ignore empty data
62002                    if (isNaN(data.get(dataDims[1], idx))) {
62003                        continue;
62004                    }
62005
62006                    rect = new Rect({
62007                        z2: 1,
62008                        shape: coordSys.dataToRect([data.get(dataDims[0], idx)]).contentShape,
62009                        style: {
62010                            fill: data.getItemVisual(idx, 'color'),
62011                            opacity: data.getItemVisual(idx, 'opacity')
62012                        }
62013                    });
62014                }
62015
62016                var itemModel = data.getItemModel(idx);
62017
62018                // Optimization for large datset
62019                if (data.hasItemOption) {
62020                    style = itemModel.getModel(itemStyleQuery).getItemStyle(['color']);
62021                    hoverStl = itemModel.getModel(hoverItemStyleQuery).getItemStyle();
62022                    labelModel = itemModel.getModel(labelQuery);
62023                    hoverLabelModel = itemModel.getModel(hoverLabelQuery);
62024                }
62025
62026                var rawValue = seriesModel.getRawValue(idx);
62027                var defaultText = '-';
62028                if (rawValue && rawValue[2] != null) {
62029                    defaultText = rawValue[2];
62030                }
62031
62032                setLabelStyle(
62033                    style, hoverStl, labelModel, hoverLabelModel,
62034                    {
62035                        labelFetcher: seriesModel,
62036                        labelDataIndex: idx,
62037                        defaultText: defaultText,
62038                        isRectText: true
62039                    }
62040                );
62041
62042                rect.setStyle(style);
62043                setHoverStyle(rect, data.hasItemOption ? hoverStl : extend({}, hoverStl));
62044
62045                rect.incremental = incremental;
62046                // PENDING
62047                if (incremental) {
62048                    // Rect must use hover layer if it's incremental.
62049                    rect.useHoverLayer = true;
62050                }
62051
62052                group.add(rect);
62053                data.setItemGraphicEl(idx, rect);
62054            }
62055        },
62056
62057        _renderOnGeo: function (geo, seriesModel, visualMapModel, api) {
62058            var inRangeVisuals = visualMapModel.targetVisuals.inRange;
62059            var outOfRangeVisuals = visualMapModel.targetVisuals.outOfRange;
62060            // if (!visualMapping) {
62061            //     throw new Error('Data range must have color visuals');
62062            // }
62063
62064            var data = seriesModel.getData();
62065            var hmLayer = this._hmLayer || (this._hmLayer || new Heatmap());
62066            hmLayer.blurSize = seriesModel.get('blurSize');
62067            hmLayer.pointSize = seriesModel.get('pointSize');
62068            hmLayer.minOpacity = seriesModel.get('minOpacity');
62069            hmLayer.maxOpacity = seriesModel.get('maxOpacity');
62070
62071            var rect = geo.getViewRect().clone();
62072            var roamTransform = geo.getRoamTransform();
62073            rect.applyTransform(roamTransform);
62074
62075            // Clamp on viewport
62076            var x = Math.max(rect.x, 0);
62077            var y = Math.max(rect.y, 0);
62078            var x2 = Math.min(rect.width + rect.x, api.getWidth());
62079            var y2 = Math.min(rect.height + rect.y, api.getHeight());
62080            var width = x2 - x;
62081            var height = y2 - y;
62082
62083            var dims = [
62084                data.mapDimension('lng'),
62085                data.mapDimension('lat'),
62086                data.mapDimension('value')
62087            ];
62088
62089            var points = data.mapArray(dims, function (lng, lat, value) {
62090                var pt = geo.dataToPoint([lng, lat]);
62091                pt[0] -= x;
62092                pt[1] -= y;
62093                pt.push(value);
62094                return pt;
62095            });
62096
62097            var dataExtent = visualMapModel.getExtent();
62098            var isInRange = visualMapModel.type === 'visualMap.continuous'
62099                ? getIsInContinuousRange(dataExtent, visualMapModel.option.range)
62100                : getIsInPiecewiseRange(
62101                    dataExtent, visualMapModel.getPieceList(), visualMapModel.option.selected
62102                );
62103
62104            hmLayer.update(
62105                points, width, height,
62106                inRangeVisuals.color.getNormalizer(),
62107                {
62108                    inRange: inRangeVisuals.color.getColorMapper(),
62109                    outOfRange: outOfRangeVisuals.color.getColorMapper()
62110                },
62111                isInRange
62112            );
62113            var img = new ZImage({
62114                style: {
62115                    width: width,
62116                    height: height,
62117                    x: x,
62118                    y: y,
62119                    image: hmLayer.canvas
62120                },
62121                silent: true
62122            });
62123            this.group.add(img);
62124        },
62125
62126        dispose: function () {}
62127    });
62128
62129    /*
62130* Licensed to the Apache Software Foundation (ASF) under one
62131* or more contributor license agreements.  See the NOTICE file
62132* distributed with this work for additional information
62133* regarding copyright ownership.  The ASF licenses this file
62134* to you under the Apache License, Version 2.0 (the
62135* "License"); you may not use this file except in compliance
62136* with the License.  You may obtain a copy of the License at
62137*
62138*   http://www.apache.org/licenses/LICENSE-2.0
62139*
62140* Unless required by applicable law or agreed to in writing,
62141* software distributed under the License is distributed on an
62142* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
62143* KIND, either express or implied.  See the License for the
62144* specific language governing permissions and limitations
62145* under the License.
62146*/
62147
62148    /*
62149* Licensed to the Apache Software Foundation (ASF) under one
62150* or more contributor license agreements.  See the NOTICE file
62151* distributed with this work for additional information
62152* regarding copyright ownership.  The ASF licenses this file
62153* to you under the Apache License, Version 2.0 (the
62154* "License");
62154 you may not use this file except in compliance
62155* with the License.  You may obtain a copy of the License at
62156*
62157*   http://www.apache.org/licenses/LICENSE-2.0
62158*
62159* Unless required by applicable law or agreed to in writing,
62160* software distributed under the License is distributed on an
62161* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
62162* KIND, either express or implied.  See the License for the
62163* specific language governing permissions and limitations
62164* under the License.
62165*/
62166
62167    var PictorialBarSeries = BaseBarSeries.extend({
62168
62169        type: 'series.pictorialBar',
62170
62171        dependencies: ['grid'],
62172
62173        defaultOption: {
62174            symbol: 'circle',     // Customized bar shape
62175            symbolSize: null,     // Can be ['100%', '100%'], null means auto.
62176            symbolRotate: null,
62177
62178            symbolPosition: null, // 'start' or 'end' or 'center', null means auto.
62179            symbolOffset: null,
62180            symbolMargin: null,   // start margin and end margin. Can be a number or a percent string.
62181            // Auto margin by defualt.
62182            symbolRepeat: false,  // false/null/undefined, means no repeat.
62183            // Can be true, means auto calculate repeat times and cut by data.
62184            // Can be a number, specifies repeat times, and do not cut by data.
62185            // Can be 'fixed', means auto calculate repeat times but do not cut by data.
62186            symbolRepeatDirection: 'end', // 'end' means from 'start' to 'end'.
62187
62188            symbolClip: false,
62189            symbolBoundingData: null, // Can be 60 or -40 or [-40, 60]
62190            symbolPatternSize: 400, // 400 * 400 px
62191
62192            barGap: '-100%',      // In most case, overlap is needed.
62193
62194            // z can be set in data item, which is z2 actually.
62195
62196            // Disable progressive
62197            progressive: 0,
62198            hoverAnimation: false // Open only when needed.
62199        },
62200
62201        getInitialData: function (option) {
62202            // Disable stack.
62203            option.stack = null;
62204            return PictorialBarSeries.superApply(this, 'getInitialData', arguments);
62205        }
62206    });
62207
62208    /*
62209* Licensed to the Apache Software Foundation (ASF) under one
62210* or more contributor license agreements.  See the NOTICE file
62211* distributed with this work for additional information
62212* regarding copyright ownership.  The ASF licenses this file
62213* to you under the Apache License, Version 2.0 (the
62214* "License"); you may not use this file except in compliance
62215* with the License.  You may obtain a copy of the License at
62216*
62217*   http://www.apache.org/licenses/LICENSE-2.0
62218*
62219* Unless required by applicable law or agreed to in writing,
62220* software distributed under the License is distributed on an
62221* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
62222* KIND, either express or implied.  See the License for the
62223* specific language governing permissions and limitations
62224* under the License.
62225*/
62226
62227    var BAR_BORDER_WIDTH_QUERY$1 = ['itemStyle', 'borderWidth'];
62228
62229// index: +isHorizontal
62230    var LAYOUT_ATTRS = [
62231        {xy: 'x', wh: 'width', index: 0, posDesc: ['left', 'right']},
62232        {xy: 'y', wh: 'height', index: 1, posDesc: ['top', 'bottom']}
62233    ];
62234
62235    var pathForLineWidth = new Circle();
62236
62237    var BarView$1 = extendChartView({
62238
62239        type: 'pictorialBar',
62240
62241        render: function (seriesModel, ecModel, api) {
62242            var group = this.group;
62243            var data = seriesModel.getData();
62244            var oldData = this._data;
62245
62246            var cartesian = seriesModel.coordinateSystem;
62247            var baseAxis = cartesian.getBaseAxis();
62248            var isHorizontal = !!baseAxis.isHorizontal();
62249            var coordSysRect = cartesian.grid.getRect();
62250
62251            var opt = {
62252                ecSize: {width: api.getWidth(), height: api.getHeight()},
62253                seriesModel: seriesModel,
62254                coordSys: cartesian,
62255                coordSysExtent: [
62256                    [coordSysRect.x, coordSysRect.x + coordSysRect.width],
62257                    [coordSysRect.y, coordSysRect.y + coordSysRect.height]
62258                ],
62259                isHorizontal: isHorizontal,
62260                valueDim: LAYOUT_ATTRS[+isHorizontal],
62261                categoryDim: LAYOUT_ATTRS[1 - isHorizontal]
62262            };
62263
62264            data.diff(oldData)
62265                .add(function (dataIndex) {
62266                    if (!data.hasValue(dataIndex)) {
62267                        return;
62268                    }
62269
62270                    var itemModel = getItemModel(data, dataIndex);
62271                    var symbolMeta = getSymbolMeta(data, dataIndex, itemModel, opt);
62272
62273                    var bar = createBar(data, opt, symbolMeta);
62274
62275                    data.setItemGraphicEl(dataIndex, bar);
62276                    group.add(bar);
62277
62278                    updateCommon$1(bar, opt, symbolMeta);
62279                })
62280                .update(function (newIndex, oldIndex) {
62281                    var bar = oldData.getItemGraphicEl(oldIndex);
62282
62283                    if (!data.hasValue(newIndex)) {
62284                        group.remove(bar);
62285                        return;
62286                    }
62287
62288                    var itemModel = getItemModel(data, newIndex);
62289                    var symbolMeta = getSymbolMeta(data, newIndex, itemModel, opt);
62290
62291                    var pictorialShapeStr = getShapeStr(data, symbolMeta);
62292                    if (bar && pictorialShapeStr !== bar.__pictorialShapeStr) {
62293                        group.remove(bar);
62294                        data.setItemGraphicEl(newIndex, null);
62295                        bar = null;
62296                    }
62297
62298                    if (bar) {
62299                        updateBar(bar, opt, symbolMeta);
62300                    }
62301                    else {
62302                        bar = createBar(data, opt, symbolMeta, true);
62303                    }
62304
62305                    data.setItemGraphicEl(newIndex, bar);
62306                    bar.__pictorialSymbolMeta = symbolMeta;
62307                    // Add back
62308                    group.add(bar);
62309
62310                    updateCommon$1(bar, opt, symbolMeta);
62311                })
62312                .remove(function (dataIndex) {
62313                    var bar = oldData.getItemGraphicEl(dataIndex);
62314                    bar && removeBar(oldData, dataIndex, bar.__pictorialSymbolMeta.animationModel, bar);
62315                })
62316                .execute();
62317
62318            this._data = data;
62319
62320            return this.group;
62321        },
62322
62323        dispose: noop,
62324
62325        remove: function (ecModel, api) {
62326            var group = this.group;
62327            var data = this._data;
62328            if (ecModel.get('animation')) {
62329                if (data) {
62330                    data.eachItemGraphicEl(function (bar) {
62331                        removeBar(data, bar.dataIndex, ecModel, bar);
62332                    });
62333                }
62334            }
62335            else {
62336                group.removeAll();
62337            }
62338        }
62339    });
62340
62341
62342// Set or calculate default value about symbol, and calculate layout info.
62343    function getSymbolMeta(data, dataIndex, itemModel, opt) {
62344        var layout = data.getItemLayout(dataIndex);
62345        var symbolRepeat = itemModel.get('symbolRepeat');
62346        var symbolClip = itemModel.get('symbolClip');
62347        var symbolPosition = itemModel.get('symbolPosition') || 'start';
62348        var symbolRotate = itemModel.get('symbolRotate');
62349        var rotation = (symbolRotate || 0) * Math.PI / 180 || 0;
62350        var symbolPatternSize = itemModel.get('symbolPatternSize') || 2;
62351        var isAnimationEnabled = itemModel.isAnimationEnabled();
62352
62353        var symbolMeta = {
62354            dataIndex: dataIndex,
62355            layout: layout,
62356            itemModel: itemModel,
62357            symbolType: data.getItemVisual(dataIndex, 'symbol') || 'circle',
62358            color: data.getItemVisual(dataIndex, 'color'),
62359            symbolClip: symbolClip,
62360            symbolRepeat: symbolRepeat,
62361            symbolRepeatDirection: itemModel.get('symbolRepeatDirection'),
62362            symbolPatternSize: symbolPatternSize,
62363            rotation: rotation,
62364            animationModel: isAnimationEnabled ? itemModel : null,
62365            hoverAnimation: isAnimationEnabled && itemModel.get('hoverAnimation'),
62366            z2: itemModel.getShallow('z', true) || 0
62367        };
62368
62369        prepareBarLength(itemModel, symbolRepeat, layout, opt, symbolMeta);
62370
62371        prepareSymbolSize(
62372            data, dataIndex, layout, symbolRepeat, symbolClip, symbolMeta.boundingLength,
62373            symbolMeta.pxSign, symbolPatternSize, opt, symbolMeta
62374        );
62375
62376        prepareLineWidth(itemModel, symbolMeta.symbolScale, rotation, opt, symbolMeta);
62377
62378        var symbolSize = symbolMeta.symbolSize;
62379        var symbolOffset = itemModel.get('symbolOffset');
62380        if (isArray(symbolOffset)) {
62381            symbolOffset = [
62382                parsePercent$1(symbolOffset[0], symbolSize[0]),
62383                parsePercent$1(symbolOffset[1], symbolSize[1])
62384            ];
62385        }
62386
62387        prepareLayoutInfo(
62388            itemModel, symbolSize, layout, symbolRepeat, symbolClip, symbolOffset,
62389            symbolPosition, symbolMeta.valueLineWidth, symbolMeta.boundingLength, symbolMeta.repeatCutLength,
62390            opt, symbolMeta
62391        );
62392
62393        return symbolMeta;
62394    }
62395
62396// bar length can be negative.
62397    function prepareBarLength(itemModel, symbolRepeat, layout, opt, output) {
62398        var valueDim = opt.valueDim;
62399        var symbolBoundingData = itemModel.get('symbolBoundingData');
62400        var valueAxis = opt.coordSys.getOtherAxis(opt.coordSys.getBaseAxis());
62401        var zeroPx = valueAxis.toGlobalCoord(valueAxis.dataToCoord(0));
62402        var pxSignIdx = 1 - +(layout[valueDim.wh] <= 0);
62403        var boundingLength;
62404
62405        if (isArray(symbolBoundingData)) {
62406            var symbolBoundingExtent = [
62407                convertToCoordOnAxis(valueAxis, symbolBoundingData[0]) - zeroPx,
62408                convertToCoordOnAxis(valueAxis, symbolBoundingData[1]) - zeroPx
62409            ];
62410            symbolBoundingExtent[1] < symbolBoundingExtent[0] && (symbolBoundingExtent.reverse());
62411            boundingLength = symbolBoundingExtent[pxSignIdx];
62412        }
62413        else if (symbolBoundingData != null) {
62414            boundingLength = convertToCoordOnAxis(valueAxis, symbolBoundingData) - zeroPx;
62415        }
62416        else if (symbolRepeat) {
62417            boundingLength = opt.coordSysExtent[valueDim.index][pxSignIdx] - zeroPx;
62418        }
62419        else {
62420            boundingLength = layout[valueDim.wh];
62421        }
62422
62423        output.boundingLength = boundingLength;
62424
62425        if (symbolRepeat) {
62426            output.repeatCutLength = layout[valueDim.wh];
62427        }
62428
62429        output.pxSign = boundingLength > 0 ? 1 : boundingLength < 0 ? -1 : 0;
62430    }
62431
62432    function convertToCoordOnAxis(axis, value) {
62433        return axis.toGlobalCoord(axis.dataToCoord(axis.scale.parse(value)));
62434    }
62435
62436// Support ['100%', '100%']
62437    function prepareSymbolSize(
62438        data, dataIndex, layout, symbolRepeat, symbolClip, boundingLength,
62439        pxSign, symbolPatternSize, opt, output
62440    ) {
62441        var valueDim = opt.valueDim;
62442        var categoryDim = opt.categoryDim;
62443        var categorySize = Math.abs(layout[categoryDim.wh]);
62444
62445        var symbolSize = data.getItemVisual(dataIndex, 'symbolSize');
62446        if (isArray(symbolSize)) {
62447            symbolSize = symbolSize.slice();
62448        }
62449        else {
62450            if (symbolSize == null) {
62451                symbolSize = '100%';
62452            }
62453            symbolSize = [symbolSize, symbolSize];
62454        }
62455
62456        // Note: percentage symbolSize (like '100%') do not consider lineWidth, because it is
62457        // to complicated to calculate real percent value if considering scaled lineWidth.
62458        // So the actual size will bigger than layout size if lineWidth is bigger than zero,
62459        // which can be tolerated in pictorial chart.
62460
62461        symbolSize[categoryDim.index] = parsePercent$1(
62462            symbolSize[categoryDim.index],
62463            categorySize
62464        );
62465        symbolSize[valueDim.index] = parsePercent$1(
62466            symbolSize[valueDim.index],
62467            symbolRepeat ? categorySize : Math.abs(boundingLength)
62468        );
62469
62470        output.symbolSize = symbolSize;
62471
62472        // If x or y is less than zero, show reversed shape.
62473        var symbolScale = output.symbolScale = [
62474            symbolSize[0] / symbolPatternSize,
62475            symbolSize[1] / symbolPatternSize
62476        ];
62477        // Follow convention, 'right' and 'top' is the normal scale.
62478        symbolScale[valueDim.index] *= (opt.isHorizontal ? -1 : 1) * pxSign;
62479    }
62480
62481    function prepareLineWidth(itemModel, symbolScale, rotation, opt, output) {
62482        // In symbols are drawn with scale, so do not need to care about the case that width
62483        // or height are too small. But symbol use strokeNoScale, where acture lineWidth should
62484        // be calculated.
62485        var valueLineWidth = itemModel.get(BAR_BORDER_WIDTH_QUERY$1) || 0;
62486
62487        if (valueLineWidth) {
62488            pathForLineWidth.attr({
62489                scale: symbolScale.slice(),
62490                rotation: rotation
62491            });
62492            pathForLineWidth.updateTransform();
62493            valueLineWidth /= pathForLineWidth.getLineScale();
62494            valueLineWidth *= symbolScale[opt.valueDim.index];
62495        }
62496
62497        output.valueLineWidth = valueLineWidth;
62498    }
62499
62500    function prepareLayoutInfo(
62501        itemModel, symbolSize, layout, symbolRepeat, symbolClip, symbolOffset,
62502        symbolPosition, valueLineWidth, boundingLength, repeatCutLength, opt, output
62503    ) {
62504        var categoryDim = opt.categoryDim;
62505        var valueDim = opt.valueDim;
62506        var pxSign = output.pxSign;
62507
62508        var unitLength = Math.max(symbolSize[valueDim.index] + valueLineWidth, 0);
62509        var pathLen = unitLength;
62510
62511        // Note: rotation will not effect the layout of symbols, because user may
62512        // want symbols to rotate on its center, which should not be translated
62513        // when rotating.
62514
62515        if (symbolRepeat) {
62516            var absBoundingLength = Math.abs(boundingLength);
62517
62518            var symbolMargin = retrieve(itemModel.get('symbolMargin'), '15%') + '';
62519            var hasEndGap = false;
62520            if (symbolMargin.lastIndexOf('!') === symbolMargin.length - 1) {
62521                hasEndGap = true;
62522                symbolMargin = symbolMargin.slice(0, symbolMargin.length - 1);
62523            }
62524            symbolMargin = parsePercent$1(symbolMargin, symbolSize[valueDim.index]);
62525
62526            var uLenWithMargin = Math.max(unitLength + symbolMargin * 2, 0);
62527
62528            // When symbol margin is less than 0, margin at both ends will be subtracted
62529            // to ensure that all of the symbols will not be overflow the given area.
62530            var endFix = hasEndGap ? 0 : symbolMargin * 2;
62531
62532            // Both final repeatTimes and final symbolMargin area calculated based on
62533            // boundingLength.
62534            var repeatSpecified = isNumeric(symbolRepeat);
62535            var repeatTimes = repeatSpecified
62536                ? symbolRepeat
62537                : toIntTimes((absBoundingLength + endFix) / uLenWithMargin);
62538
62539            // Adjust calculate margin, to ensure each symbol is displayed
62540            // entirely in the given layout area.
62541            var mDiff = absBoundingLength - repeatTimes * unitLength;
62542            symbolMargin = mDiff / 2 / (hasEndGap ? repeatTimes : repeatTimes - 1);
62543            uLenWithMargin = unitLength + symbolMargin * 2;
62544            endFix = hasEndGap ? 0 : symbolMargin * 2;
62545
62546            // Update repeatTimes when not all symbol will be shown.
62547            if (!repeatSpecified && symbolRepeat !== 'fixed') {
62548                repeatTimes = repeatCutLength
62549                    ? toIntTimes((Math.abs(repeatCutLength) + endFix) / uLenWithMargin)
62550                    : 0;
62551            }
62552
62553            pathLen = repeatTimes * uLenWithMargin - endFix;
62554            output.repeatTimes = repeatTimes;
62555            output.symbolMargin = symbolMargin;
62556        }
62557
62558        var sizeFix = pxSign * (pathLen / 2);
62559        var pathPosition = output.pathPosition = [];
62560        pathPosition[categoryDim.index] = layout[categoryDim.wh] / 2;
62561        pathPosition[valueDim.index] = symbolPosition === 'start'
62562            ? sizeFix
62563            : symbolPosition === 'end'
62564                ? boundingLength - sizeFix
62565                : boundingLength / 2; // 'center'
62566        if (symbolOffset) {
62567            pathPosition[0] += symbolOffset[0];
62568            pathPosition[1] += symbolOffset[1];
62569        }
62570
62571        var bundlePosition = output.bundlePosition = [];
62572        bundlePosition[categoryDim.index] = layout[categoryDim.xy];
62573        bundlePosition[valueDim.index] = layout[valueDim.xy];
62574
62575        var barRectShape = output.barRectShape = extend({}, layout);
62576        barRectShape[valueDim.wh] = pxSign * Math.max(
62577            Math.abs(layout[valueDim.wh]), Math.abs(pathPosition[valueDim.index] + sizeFix)
62578        );
62579        barRectShape[categoryDim.wh] = layout[categoryDim.wh];
62580
62581        var clipShape = output.clipShape = {};
62582        // Consider that symbol may be overflow layout rect.
62583        clipShape[categoryDim.xy] = -layout[categoryDim.xy];
62584        clipShape[categoryDim.wh] = opt.ecSize[categoryDim.wh];
62585        clipShape[valueDim.xy] = 0;
62586        clipShape[valueDim.wh] = layout[valueDim.wh];
62587    }
62588
62589    function createPath(symbolMeta) {
62590        var symbolPatternSize = symbolMeta.symbolPatternSize;
62591        var path = createSymbol(
62592            // Consider texture img, make a big size.
62593            symbolMeta.symbolType,
62594            -symbolPatternSize / 2,
62595            -symbolPatternSize / 2,
62596            symbolPatternSize,
62597            symbolPatternSize,
62598            symbolMeta.color
62599        );
62600        path.attr({
62601            culling: true
62602        });
62603        path.type !== 'image' && path.setStyle({
62604            strokeNoScale: true
62605        });
62606
62607        return path;
62608    }
62609
62610    function createOrUpdateRepeatSymbols(bar, opt, symbolMeta, isUpdate) {
62611        var bundle = bar.__pictorialBundle;
62612        var symbolSize = symbolMeta.symbolSize;
62613        var valueLineWidth = symbolMeta.valueLineWidth;
62614        var pathPosition = symbolMeta.pathPosition;
62615        var valueDim = opt.valueDim;
62616        var repeatTimes = symbolMeta.repeatTimes || 0;
62617
62618        var index = 0;
62619        var unit = symbolSize[opt.valueDim.index] + valueLineWidth + symbolMeta.symbolMargin * 2;
62620
62621        eachPath(bar, function (path) {
62622            path.__pictorialAnimationIndex = index;
62623            path.__pictorialRepeatTimes = repeatTimes;
62624            if (index < repeatTimes) {
62625                updateAttr(path, null, makeTarget(index), symbolMeta, isUpdate);
62626            }
62627            else {
62628                updateAttr(path, null, {scale: [0, 0]}, symbolMeta, isUpdate, function () {
62629                    bundle.remove(path);
62630                });
62631            }
62632
62633            updateHoverAnimation(path, symbolMeta);
62634
62635            index++;
62636        });
62637
62638        for (; index < repeatTimes; index++) {
62639            var path = createPath(symbolMeta);
62640            path.__pictorialAnimationIndex = index;
62641            path.__pictorialRepeatTimes = repeatTimes;
62642            bundle.add(path);
62643
62644            var target = makeTarget(index);
62645
62646            updateAttr(
62647                path,
62648                {
62649                    position: target.position,
62650                    scale: [0, 0]
62651                },
62652                {
62653                    scale: target.scale,
62654                    rotation: target.rotation
62655                },
62656                symbolMeta,
62657                isUpdate
62658            );
62659
62660            // FIXME
62661            // If all emphasis/normal through action.
62662            path
62663                .on('mouseover', onMouseOver)
62664                .on('mouseout', onMouseOut);
62665
62666            updateHoverAnimation(path, symbolMeta);
62667        }
62668
62669        function makeTarget(index) {
62670            var position = pathPosition.slice();
62671            // (start && pxSign > 0) || (end && pxSign < 0): i = repeatTimes - index
62672            // Otherwise: i = index;
62673            var pxSign = symbolMeta.pxSign;
62674            var i = index;
62675            if (symbolMeta.symbolRepeatDirection === 'start' ? pxSign > 0 : pxSign < 0) {
62676                i = repeatTimes - 1 - index;
62677            }
62678            position[valueDim.index] = unit * (i - repeatTimes / 2 + 0.5) + pathPosition[valueDim.index];
62679
62680            return {
62681                position: position,
62682                scale: symbolMeta.symbolScale.slice(),
62683                rotation: symbolMeta.rotation
62684            };
62685        }
62686
62687        function onMouseOver() {
62688            eachPath(bar, function (path) {
62689                path.trigger('emphasis');
62690            });
62691        }
62692
62693        function onMouseOut() {
62694            eachPath(bar, function (path) {
62695                path.trigger('normal');
62696            });
62697        }
62698    }
62699
62700    function createOrUpdateSingleSymbol(bar, opt, symbolMeta, isUpdate) {
62701        var bundle = bar.__pictorialBundle;
62702        var mainPath = bar.__pictorialMainPath;
62703
62704        if (!mainPath) {
62705            mainPath = bar.__pictorialMainPath = createPath(symbolMeta);
62706            bundle.add(mainPath);
62707
62708            updateAttr(
62709                mainPath,
62710                {
62711                    position: symbolMeta.pathPosition.slice(),
62712                    scale: [0, 0],
62713                    rotation: symbolMeta.rotation
62714                },
62715                {
62716                    scale: symbolMeta.symbolScale.slice()
62717                },
62718                symbolMeta,
62719                isUpdate
62720            );
62721
62722            mainPath
62723                .on('mouseover', onMouseOver)
62724                .on('mouseout', onMouseOut);
62725        }
62726        else {
62727            updateAttr(
62728                mainPath,
62729                null,
62730                {
62731                    position: symbolMeta.pathPosition.slice(),
62732                    scale: symbolMeta.symbolScale.slice(),
62733                    rotation: symbolMeta.rotation
62734                },
62735                symbolMeta,
62736                isUpdate
62737            );
62738        }
62739
62740        updateHoverAnimation(mainPath, symbolMeta);
62741
62742        function onMouseOver() {
62743            this.trigger('emphasis');
62744        }
62745
62746        function onMouseOut() {
62747            this.trigger('normal');
62748        }
62749    }
62750
62751// bar rect is used for label.
62752    function createOrUpdateBarRect(bar, symbolMeta, isUpdate) {
62753        var rectShape = extend({}, symbolMeta.barRectShape);
62754
62755        var barRect = bar.__pictorialBarRect;
62756        if (!barRect) {
62757            barRect = bar.__pictorialBarRect = new Rect({
62758                z2: 2,
62759                shape: rectShape,
62760                silent: true,
62761                style: {
62762                    stroke: 'transparent',
62763                    fill: 'transparent',
62764                    lineWidth: 0
62765                }
62766            });
62767
62768            bar.add(barRect);
62769        }
62770        else {
62771            updateAttr(barRect, null, {shape: rectShape}, symbolMeta, isUpdate);
62772        }
62773    }
62774
62775    function createOrUpdateClip(bar, opt, symbolMeta, isUpdate) {
62776        // If not clip, symbol will be remove and rebuilt.
62777        if (symbolMeta.symbolClip) {
62778            var clipPath = bar.__pictorialClipPath;
62779            var clipShape = extend({}, symbolMeta.clipShape);
62780            var valueDim = opt.valueDim;
62781            var animationModel = symbolMeta.animationModel;
62782            var dataIndex = symbolMeta.dataIndex;
62783
62784            if (clipPath) {
62785                updateProps(
62786                    clipPath, {shape: clipShape}, animationModel, dataIndex
62787                );
62788            }
62789            else {
62790                clipShape[valueDim.wh] = 0;
62791                clipPath = new Rect({shape: clipShape});
62792                bar.__pictorialBundle.setClipPath(clipPath);
62793                bar.__pictorialClipPath = clipPath;
62794
62795                var target = {};
62796                target[valueDim.wh] = symbolMeta.clipShape[valueDim.wh];
62797
62798                graphic[isUpdate ? 'updateProps' : 'initProps'](
62799                    clipPath, {shape: target}, animationModel, dataIndex
62800                );
62801            }
62802        }
62803    }
62804
62805    function getItemModel(data, dataIndex) {
62806        var itemModel = data.getItemModel(dataIndex);
62807        itemModel.getAnimationDelayParams = getAnimationDelayParams;
62808        itemModel.isAnimationEnabled = isAnimationEnabled;
62809        return itemModel;
62810    }
62811
62812    function getAnimationDelayParams(path) {
62813        // The order is the same as the z-order, see `symbolRepeatDiretion`.
62814        return {
62815            index: path.__pictorialAnimationIndex,
62816            count: path.__pictorialRepeatTimes
62817        };
62818    }
62819
62820    function isAnimationEnabled() {
62821        // `animation` prop can be set on itemModel in pictorial bar chart.
62822        return this.parentModel.isAnimationEnabled() && !!this.getShallow('animation');
62823    }
62824
62825    function updateHoverAnimation(path, symbolMeta) {
62826        path.off('emphasis').off('normal');
62827
62828        var scale = symbolMeta.symbolScale.slice();
62829
62830        symbolMeta.hoverAnimation && path
62831            .on('emphasis', function() {
62832                this.animateTo({
62833                    scale: [scale[0] * 1.1, scale[1] * 1.1]
62834                }, 400, 'elasticOut');
62835            })
62836            .on('normal', function() {
62837                this.animateTo({
62838                    scale: scale.slice()
62839                }, 400, 'elasticOut');
62840            });
62841    }
62842
62843    function createBar(data, opt, symbolMeta, isUpdate) {
62844        // bar is the main element for each data.
62845        var bar = new Group();
62846        // bundle is used for location and clip.
62847        var bundle = new Group();
62848        bar.add(bundle);
62849        bar.__pictorialBundle = bundle;
62850        bundle.attr('position', symbolMeta.bundlePosition.slice());
62851
62852        if (symbolMeta.symbolRepeat) {
62853            createOrUpdateRepeatSymbols(bar, opt, symbolMeta);
62854        }
62855        else {
62856            createOrUpdateSingleSymbol(bar, opt, symbolMeta);
62857        }
62858
62859        createOrUpdateBarRect(bar, symbolMeta, isUpdate);
62860
62861        createOrUpdateClip(bar, opt, symbolMeta, isUpdate);
62862
62863        bar.__pictorialShapeStr = getShapeStr(data, symbolMeta);
62864        bar.__pictorialSymbolMeta = symbolMeta;
62865
62866        return bar;
62867    }
62868
62869    function updateBar(bar, opt, symbolMeta) {
62870        var animationModel = symbolMeta.animationModel;
62871        var dataIndex = symbolMeta.dataIndex;
62872        var bundle = bar.__pictorialBundle;
62873
62874        updateProps(
62875            bundle, {position: symbolMeta.bundlePosition.slice()}, animationModel, dataIndex
62876        );
62877
62878        if (symbolMeta.symbolRepeat) {
62879            createOrUpdateRepeatSymbols(bar, opt, symbolMeta, true);
62880        }
62881        else {
62882            createOrUpdateSingleSymbol(bar, opt, symbolMeta, true);
62883        }
62884
62885        createOrUpdateBarRect(bar, symbolMeta, true);
62886
62887        createOrUpdateClip(bar, opt, symbolMeta, true);
62888    }
62889
62890    function removeBar(data, dataIndex, animationModel, bar) {
62891        // Not show text when animating
62892        var labelRect = bar.__pictorialBarRect;
62893        labelRect && (labelRect.style.text = null);
62894
62895        var pathes = [];
62896        eachPath(bar, function (path) {
62897            pathes.push(path);
62898        });
62899        bar.__pictorialMainPath && pathes.push(bar.__pictorialMainPath);
62900
62901        // I do not find proper remove animation for clip yet.
62902        bar.__pictorialClipPath && (animationModel = null);
62903
62904        each$1(pathes, function (path) {
62905            updateProps(
62906                path, {scale: [0, 0]}, animationModel, dataIndex,
62907                function () {
62908                    bar.parent && bar.parent.remove(bar);
62909                }
62910            );
62911        });
62912
62913        data.setItemGraphicEl(dataIndex, null);
62914    }
62915
62916    function getShapeStr(data, symbolMeta) {
62917        return [
62918            data.getItemVisual(symbolMeta.dataIndex, 'symbol') || 'none',
62919            !!symbolMeta.symbolRepeat,
62920            !!symbolMeta.symbolClip
62921        ].join(':');
62922    }
62923
62924    function eachPath(bar, cb, context) {
62925        // Do not use Group#eachChild, because it do not support remove.
62926        each$1(bar.__pictorialBundle.children(), function (el) {
62927            el !== bar.__pictorialBarRect && cb.call(context, el);
62928        });
62929    }
62930
62931    function updateAttr(el, immediateAttrs, animationAttrs, symbolMeta, isUpdate, cb) {
62932        immediateAttrs && el.attr(immediateAttrs);
62933        // when symbolCip used, only clip path has init animation, otherwise it would be weird effect.
62934        if (symbolMeta.symbolClip && !isUpdate) {
62935            animationAttrs && el.attr(animationAttrs);
62936        }
62937        else {
62938            animationAttrs && graphic[isUpdate ? 'updateProps' : 'initProps'](
62939                el, animationAttrs, symbolMeta.animationModel, symbolMeta.dataIndex, cb
62940            );
62941        }
62942    }
62943
62944    function updateCommon$1(bar, opt, symbolMeta) {
62945        var color = symbolMeta.color;
62946        var dataIndex = symbolMeta.dataIndex;
62947        var itemModel = symbolMeta.itemModel;
62948        // Color must be excluded.
62949        // Because symbol provide setColor individually to set fill and stroke
62950        var normalStyle = itemModel.getModel('itemStyle').getItemStyle(['color']);
62951        var hoverStyle = itemModel.getModel('emphasis.itemStyle').getItemStyle();
62952        var cursorStyle = itemModel.getShallow('cursor');
62953
62954        eachPath(bar, function (path) {
62955            // PENDING setColor should be before setStyle!!!
62956            path.setColor(color);
62957            path.setStyle(defaults(
62958                {
62959                    fill: color,
62960                    opacity: symbolMeta.opacity
62961                },
62962                normalStyle
62963            ));
62964            setHoverStyle(path, hoverStyle);
62965
62966            cursorStyle && (path.cursor = cursorStyle);
62967            path.z2 = symbolMeta.z2;
62968        });
62969
62970        var barRectHoverStyle = {};
62971        var barPositionOutside = opt.valueDim.posDesc[+(symbolMeta.boundingLength > 0)];
62972        var barRect = bar.__pictorialBarRect;
62973
62974        setLabel(
62975            barRect.style, barRectHoverStyle, itemModel,
62976            color, opt.seriesModel, dataIndex, barPositionOutside
62977        );
62978
62979        setHoverStyle(barRect, barRectHoverStyle);
62980    }
62981
62982    function toIntTimes(times) {
62983        var roundedTimes = Math.round(times);
62984        // Escapse accurate error
62985        return Math.abs(times - roundedTimes) < 1e-4
62986            ? roundedTimes
62987            : Math.ceil(times);
62988    }
62989
62990    /*
62991* Licensed to the Apache Software Foundation (ASF) under one
62992* or more contributor license agreements.  See the NOTICE file
62993* distributed with this work for additional information
62994* regarding copyright ownership.  The ASF licenses this file
62995* to you under the Apache License, Version 2.0 (the
62996* "License"); you may not use this file except in compliance
62997* with the License.  You may obtain a copy of the License at
62998*
62999*   http://www.apache.org/licenses/LICENSE-2.0
63000*
63001* Unless required by applicable law or agreed to in writing,
63002* software distributed under the License is distributed on an
63003* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
63004* KIND, either express or implied.  See the License for the
63005* specific language governing permissions and limitations
63006* under the License.
63007*/
63008
63009// In case developer forget to include grid component
63010    registerLayout(curry(
63011        layout, 'pictorialBar'
63012    ));
63013    registerVisual(visualSymbol('pictorialBar', 'roundRect'));
63014
63015    /*
63016* Licensed to the Apache Software Foundation (ASF) under one
63017* or more contributor license agreements.  See the NOTICE file
63018* distributed with this work for additional information
63019* regarding copyright ownership.  The ASF licenses this file
63020* to you under the Apache License, Version 2.0 (the
63021* "License");
63021 you may not use this file except in compliance
63022* with the License.  You may obtain a copy of the License at
63023*
63024*   http://www.apache.org/licenses/LICENSE-2.0
63025*
63026* Unless required by applicable law or agreed to in writing,
63027* software distributed under the License is distributed on an
63028* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
63029* KIND, either express or implied.  See the License for the
63030* specific language governing permissions and limitations
63031* under the License.
63032*/
63033
63034    /**
63035     * @constructor  module:echarts/coord/single/SingleAxis
63036     * @extends {module:echarts/coord/Axis}
63037     * @param {string} dim
63038     * @param {*} scale
63039     * @param {Array.<number>} coordExtent
63040     * @param {string} axisType
63041     * @param {string} position
63042     */
63043    var SingleAxis = function (dim, scale, coordExtent, axisType, position) {
63044
63045        Axis.call(this, dim, scale, coordExtent);
63046
63047        /**
63048         * Axis type
63049         * - 'category'
63050         * - 'value'
63051         * - 'time'
63052         * - 'log'
63053         * @type {string}
63054         */
63055        this.type = axisType || 'value';
63056
63057        /**
63058         * Axis position
63059         *  - 'top'
63060         *  - 'bottom'
63061         *  - 'left'
63062         *  - 'right'
63063         *  @type {string}
63064         */
63065        this.position = position || 'bottom';
63066
63067        /**
63068         * Axis orient
63069         *  - 'horizontal'
63070         *  - 'vertical'
63071         * @type {[type]}
63072         */
63073        this.orient = null;
63074
63075    };
63076
63077    SingleAxis.prototype = {
63078
63079        constructor: SingleAxis,
63080
63081        /**
63082         * Axis model
63083         * @type {module:echarts/coord/single/AxisModel}
63084         */
63085        model: null,
63086
63087        /**
63088         * Judge the orient of the axis.
63089         * @return {boolean}
63090         */
63091        isHorizontal: function () {
63092            var position = this.position;
63093            return position === 'top' || position === 'bottom';
63094
63095        },
63096
63097        /**
63098         * @override
63099         */
63100        pointToData: function (point, clamp) {
63101            return this.coordinateSystem.pointToData(point, clamp)[0];
63102        },
63103
63104        /**
63105         * Convert the local coord(processed by dataToCoord())
63106         * to global coord(concrete pixel coord).
63107         * designated by module:echarts/coord/single/Single.
63108         * @type {Function}
63109         */
63110        toGlobalCoord: null,
63111
63112        /**
63113         * Convert the global coord to local coord.
63114         * designated by module:echarts/coord/single/Single.
63115         * @type {Function}
63116         */
63117        toLocalCoord: null
63118
63119    };
63120
63121    inherits(SingleAxis, Axis);
63122
63123    /*
63124* Licensed to the Apache Software Foundation (ASF) under one
63125* or more contributor license agreements.  See the NOTICE file
63126* distributed with this work for additional information
63127* regarding copyright ownership.  The ASF licenses this file
63128* to you under the Apache License, Version 2.0 (the
63129* "License"); you may not use this file except in compliance
63130* with the License.  You may obtain a copy of the License at
63131*
63132*   http://www.apache.org/licenses/LICENSE-2.0
63133*
63134* Unless required by applicable law or agreed to in writing,
63135* software distributed under the License is distributed on an
63136* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
63137* KIND, either express or implied.  See the License for the
63138* specific language governing permissions and limitations
63139* under the License.
63140*/
63141
63142    /**
63143     * Single coordinates system.
63144     */
63145
63146    /**
63147     * Create a single coordinates system.
63148     *
63149     * @param {module:echarts/coord/single/AxisModel} axisModel
63150     * @param {module:echarts/model/Global} ecModel
63151     * @param {module:echarts/ExtensionAPI} api
63152     */
63153    function Single(axisModel, ecModel, api) {
63154
63155        /**
63156         * @type {string}
63157         * @readOnly
63158         */
63159        this.dimension = 'single';
63160
63161        /**
63162         * Add it just for draw tooltip.
63163         *
63164         * @type {Array.<string>}
63165         * @readOnly
63166         */
63167        this.dimensions = ['single'];
63168
63169        /**
63170         * @private
63171         * @type {module:echarts/coord/single/SingleAxis}.
63172         */
63173        this._axis = null;
63174
63175        /**
63176         * @private
63177         * @type {module:zrender/core/BoundingRect}
63178         */
63179        this._rect;
63180
63181        this._init(axisModel, ecModel, api);
63182
63183        /**
63184         * @type {module:echarts/coord/single/AxisModel}
63185         */
63186        this.model = axisModel;
63187    }
63188
63189    Single.prototype = {
63190
63191        type: 'singleAxis',
63192
63193        axisPointerEnabled: true,
63194
63195        constructor: Single,
63196
63197        /**
63198         * Initialize single coordinate system.
63199         *
63200         * @param  {module:echarts/coord/single/AxisModel} axisModel
63201         * @param  {module:echarts/model/Global} ecModel
63202         * @param  {module:echarts/ExtensionAPI} api
63203         * @private
63204         */
63205        _init: function (axisModel, ecModel, api) {
63206
63207            var dim = this.dimension;
63208
63209            var axis = new SingleAxis(
63210                dim,
63211                createScaleByModel(axisModel),
63212                [0, 0],
63213                axisModel.get('type'),
63214                axisModel.get('position')
63215            );
63216
63217            var isCategory = axis.type === 'category';
63218            axis.onBand = isCategory && axisModel.get('boundaryGap');
63219            axis.inverse = axisModel.get('inverse');
63220            axis.orient = axisModel.get('orient');
63221
63222            axisModel.axis = axis;
63223            axis.model = axisModel;
63224            axis.coordinateSystem = this;
63225            this._axis = axis;
63226        },
63227
63228        /**
63229         * Update axis scale after data processed
63230         * @param  {module:echarts/model/Global} ecModel
63231         * @param  {module:echarts/ExtensionAPI} api
63232         */
63233        update: function (ecModel, api) {
63234            ecModel.eachSeries(function (seriesModel) {
63235                if (seriesModel.coordinateSystem === this) {
63236                    var data = seriesModel.getData();
63237                    each$1(data.mapDimension(this.dimension, true), function (dim) {
63238                        this._axis.scale.unionExtentFromData(data, dim);
63239                    }, this);
63240                    niceScaleExtent(this._axis.scale, this._axis.model);
63241                }
63242            }, this);
63243        },
63244
63245        /**
63246         * Resize the single coordinate system.
63247         *
63248         * @param  {module:echarts/coord/single/AxisModel} axisModel
63249         * @param  {module:echarts/ExtensionAPI} api
63250         */
63251        resize: function (axisModel, api) {
63252            this._rect = getLayoutRect(
63253                {
63254                    left: axisModel.get('left'),
63255                    top: axisModel.get('top'),
63256                    right: axisModel.get('right'),
63257                    bottom: axisModel.get('bottom'),
63258                    width: axisModel.get('width'),
63259                    height: axisModel.get('height')
63260                },
63261                {
63262                    width: api.getWidth(),
63263                    height: api.getHeight()
63264                }
63265            );
63266
63267            this._adjustAxis();
63268        },
63269
63270        /**
63271         * @return {module:zrender/core/BoundingRect}
63272         */
63273        getRect: function () {
63274            return this._rect;
63275        },
63276
63277        /**
63278         * @private
63279         */
63280        _adjustAxis: function () {
63281
63282            var rect = this._rect;
63283            var axis = this._axis;
63284
63285            var isHorizontal = axis.isHorizontal();
63286            var extent = isHorizontal ? [0, rect.width] : [0, rect.height];
63287            var idx =  axis.reverse ? 1 : 0;
63288
63289            axis.setExtent(extent[idx], extent[1 - idx]);
63290
63291            this._updateAxisTransform(axis, isHorizontal ? rect.x : rect.y);
63292
63293        },
63294
63295        /**
63296         * @param  {module:echarts/coord/single/SingleAxis} axis
63297         * @param  {number} coordBase
63298         */
63299        _updateAxisTransform: function (axis, coordBase) {
63300
63301            var axisExtent = axis.getExtent();
63302            var extentSum = axisExtent[0] + axisExtent[1];
63303            var isHorizontal = axis.isHorizontal();
63304
63305            axis.toGlobalCoord = isHorizontal
63306                ? function (coord) {
63307                    return coord + coordBase;
63308                }
63309                : function (coord) {
63310                    return extentSum - coord + coordBase;
63311                };
63312
63313            axis.toLocalCoord = isHorizontal
63314                ? function (coord) {
63315                    return coord - coordBase;
63316                }
63317                : function (coord) {
63318                    return extentSum - coord + coordBase;
63319                };
63320        },
63321
63322        /**
63323         * Get axis.
63324         *
63325         * @return {module:echarts/coord/single/SingleAxis}
63326         */
63327        getAxis: function () {
63328            return this._axis;
63329        },
63330
63331        /**
63332         * Get axis, add it just for draw tooltip.
63333         *
63334         * @return {[type]} [description]
63335         */
63336        getBaseAxis: function () {
63337            return this._axis;
63338        },
63339
63340        /**
63341         * @return {Array.<module:echarts/coord/Axis>}
63342         */
63343        getAxes: function () {
63344            return [this._axis];
63345        },
63346
63347        /**
63348         * @return {Object} {baseAxes: [], otherAxes: []}
63349         */
63350        getTooltipAxes: function () {
63351            return {baseAxes: [this.getAxis()]};
63352        },
63353
63354        /**
63355         * If contain point.
63356         *
63357         * @param  {Array.<number>} point
63358         * @return {boolean}
63359         */
63360        containPoint: function (point) {
63361            var rect = this.getRect();
63362            var axis = this.getAxis();
63363            var orient = axis.orient;
63364            if (orient === 'horizontal') {
63365                return axis.contain(axis.toLocalCoord(point[0]))
63366                    && (point[1] >= rect.y && point[1] <= (rect.y + rect.height));
63367            }
63368            else {
63369                return axis.contain(axis.toLocalCoord(point[1]))
63370                    && (point[0] >= rect.y && point[0] <= (rect.y + rect.height));
63371            }
63372        },
63373
63374        /**
63375         * @param {Array.<number>} point
63376         * @return {Array.<number>}
63377         */
63378        pointToData: function (point) {
63379            var axis = this.getAxis();
63380            return [axis.coordToData(axis.toLocalCoord(
63381                point[axis.orient === 'horizontal' ? 0 : 1]
63382            ))];
63383        },
63384
63385        /**
63386         * Convert the series data to concrete point.
63387         *
63388         * @param  {number|Array.<number>} val
63389         * @return {Array.<number>}
63390         */
63391        dataToPoint: function (val) {
63392            var axis = this.getAxis();
63393            var rect = this.getRect();
63394            var pt = [];
63395            var idx = axis.orient === 'horizontal' ? 0 : 1;
63396
63397            if (val instanceof Array) {
63398                val = val[0];
63399            }
63400
63401            pt[idx] = axis.toGlobalCoord(axis.dataToCoord(+val));
63402            pt[1 - idx] = idx === 0 ? (rect.y + rect.height / 2) : (rect.x + rect.width / 2);
63403            return pt;
63404        }
63405
63406    };
63407
63408    /*
63409* Licensed to the Apache Software Foundation (ASF) under one
63410* or more contributor license agreements.  See the NOTICE file
63411* distributed with this work for additional information
63412* regarding copyright ownership.  The ASF licenses this file
63413* to you under the Apache License, Version 2.0 (the
63414* "License");
63414 you may not use this file except in compliance
63415* with the License.  You may obtain a copy of the License at
63416*
63417*   http://www.apache.org/licenses/LICENSE-2.0
63418*
63419* Unless required by applicable law or agreed to in writing,
63420* software distributed under the License is distributed on an
63421* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
63422* KIND, either express or implied.  See the License for the
63423* specific language governing permissions and limitations
63424* under the License.
63425*/
63426
63427    /**
63428     * Single coordinate system creator.
63429     */
63430
63431    /**
63432     * Create single coordinate system and inject it into seriesModel.
63433     *
63434     * @param {module:echarts/model/Global} ecModel
63435     * @param {module:echarts/ExtensionAPI} api
63436     * @return {Array.<module:echarts/coord/single/Single>}
63437     */
63438    function create$3(ecModel, api) {
63439        var singles = [];
63440
63441        ecModel.eachComponent('singleAxis', function(axisModel, idx) {
63442
63443            var single = new Single(axisModel, ecModel, api);
63444            single.name = 'single_' + idx;
63445            single.resize(axisModel, api);
63446            axisModel.coordinateSystem = single;
63447            singles.push(single);
63448
63449        });
63450
63451        ecModel.eachSeries(function (seriesModel) {
63452            if (seriesModel.get('coordinateSystem') === 'singleAxis') {
63453                var singleAxisModel = ecModel.queryComponents({
63454                    mainType: 'singleAxis',
63455                    index: seriesModel.get('singleAxisIndex'),
63456                    id: seriesModel.get('singleAxisId')
63457                })[0];
63458                seriesModel.coordinateSystem = singleAxisModel && singleAxisModel.coordinateSystem;
63459            }
63460        });
63461
63462        return singles;
63463    }
63464
63465    CoordinateSystemManager.register('single', {
63466        create: create$3,
63467        dimensions: Single.prototype.dimensions
63468    });
63469
63470    /*
63471* Licensed to the Apache Software Foundation (ASF) under one
63472* or more contributor license agreements.  See the NOTICE file
63473* distributed with this work for additional information
63474* regarding copyright ownership.  The ASF licenses this file
63475* to you under the Apache License, Version 2.0 (the
63476* "License"); you may not use this file except in compliance
63477* with the License.  You may obtain a copy of the License at
63478*
63479*   http://www.apache.org/licenses/LICENSE-2.0
63480*
63481* Unless required by applicable law or agreed to in writing,
63482* software distributed under the License is distributed on an
63483* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
63484* KIND, either express or implied.  See the License for the
63485* specific language governing permissions and limitations
63486* under the License.
63487*/
63488
63489    /**
63490     * @param {Object} opt {labelInside}
63491     * @return {Object} {
63492 *  position, rotation, labelDirection, labelOffset,
63493 *  tickDirection, labelRotate, z2
63494 * }
63495     */
63496    function layout$2(axisModel, opt) {
63497        opt = opt || {};
63498        var single = axisModel.coordinateSystem;
63499        var axis = axisModel.axis;
63500        var layout = {};
63501
63502        var axisPosition = axis.position;
63503        var orient = axis.orient;
63504
63505        var rect = single.getRect();
63506        var rectBound = [rect.x, rect.x + rect.width, rect.y, rect.y + rect.height];
63507
63508        var positionMap = {
63509            horizontal: {top: rectBound[2], bottom: rectBound[3]},
63510            vertical: {left: rectBound[0], right: rectBound[1]}
63511        };
63512
63513        layout.position = [
63514            orient === 'vertical'
63515                ? positionMap.vertical[axisPosition]
63516                : rectBound[0],
63517            orient === 'horizontal'
63518                ? positionMap.horizontal[axisPosition]
63519                : rectBound[3]
63520        ];
63521
63522        var r = {horizontal: 0, vertical: 1};
63523        layout.rotation = Math.PI / 2 * r[orient];
63524
63525        var directionMap = {top: -1, bottom: 1, right: 1, left: -1};
63526
63527        layout.labelDirection = layout.tickDirection
63528            = layout.nameDirection
63529            = directionMap[axisPosition];
63530
63531        if (axisModel.get('axisTick.inside')) {
63532            layout.tickDirection = -layout.tickDirection;
63533        }
63534
63535        if (retrieve(opt.labelInside, axisModel.get('axisLabel.inside'))) {
63536            layout.labelDirection = -layout.labelDirection;
63537        }
63538
63539        var labelRotation = opt.rotate;
63540        labelRotation == null && (labelRotation = axisModel.get('axisLabel.rotate'));
63541        layout.labelRotation = axisPosition === 'top' ? -labelRotation : labelRotation;
63542
63543        layout.z2 = 1;
63544
63545        return layout;
63546    }
63547
63548    /*
63549* Licensed to the Apache Software Foundation (ASF) under one
63550* or more contributor license agreements.  See the NOTICE file
63551* distributed with this work for additional information
63552* regarding copyright ownership.  The ASF licenses this file
63553* to you under the Apache License, Version 2.0 (the
63554* "License"); you may not use this file except in compliance
63555* with the License.  You may obtain a copy of the License at
63556*
63557*   http://www.apache.org/licenses/LICENSE-2.0
63558*
63559* Unless required by applicable law or agreed to in writing,
63560* software distributed under the License is distributed on an
63561* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
63562* KIND, either express or implied.  See the License for the
63563* specific language governing permissions and limitations
63564* under the License.
63565*/
63566
63567
63568    var axisBuilderAttrs$2 = [
63569        'axisLine', 'axisTickLabel', 'axisName'
63570    ];
63571
63572    var selfBuilderAttr = 'splitLine';
63573
63574    var SingleAxisView = AxisView.extend({
63575
63576        type: 'singleAxis',
63577
63578        axisPointerClass: 'SingleAxisPointer',
63579
63580        render: function (axisModel, ecModel, api, payload) {
63581
63582            var group = this.group;
63583
63584            group.removeAll();
63585
63586            var layout =  layout$2(axisModel);
63587
63588            var axisBuilder = new AxisBuilder(axisModel, layout);
63589
63590            each$1(axisBuilderAttrs$2, axisBuilder.add, axisBuilder);
63591
63592            group.add(axisBuilder.getGroup());
63593
63594            if (axisModel.get(selfBuilderAttr + '.show')) {
63595                this['_' + selfBuilderAttr](axisModel);
63596            }
63597
63598            SingleAxisView.superCall(this, 'render', axisModel, ecModel, api, payload);
63599        },
63600
63601        _splitLine: function(axisModel) {
63602            var axis = axisModel.axis;
63603
63604            if (axis.scale.isBlank()) {
63605                return;
63606            }
63607
63608            var splitLineModel = axisModel.getModel('splitLine');
63609            var lineStyleModel = splitLineModel.getModel('lineStyle');
63610            var lineWidth = lineStyleModel.get('width');
63611            var lineColors = lineStyleModel.get('color');
63612
63613            lineColors = lineColors instanceof Array ? lineColors : [lineColors];
63614
63615            var gridRect = axisModel.coordinateSystem.getRect();
63616            var isHorizontal = axis.isHorizontal();
63617
63618            var splitLines = [];
63619            var lineCount = 0;
63620
63621            var ticksCoords = axis.getTicksCoords({
63622                tickModel: splitLineModel
63623            });
63624
63625            var p1 = [];
63626            var p2 = [];
63627
63628            for (var i = 0; i < ticksCoords.length; ++i) {
63629                var tickCoord = axis.toGlobalCoord(ticksCoords[i].coord);
63630                if (isHorizontal) {
63631                    p1[0] = tickCoord;
63632                    p1[1] = gridRect.y;
63633                    p2[0] = tickCoord;
63634                    p2[1] = gridRect.y + gridRect.height;
63635                }
63636                else {
63637                    p1[0] = gridRect.x;
63638                    p1[1] = tickCoord;
63639                    p2[0] = gridRect.x + gridRect.width;
63640                    p2[1] = tickCoord;
63641                }
63642                var colorIndex = (lineCount++) % lineColors.length;
63643                splitLines[colorIndex] = splitLines[colorIndex] || [];
63644                splitLines[colorIndex].push(new Line(
63645                    subPixelOptimizeLine({
63646                        shape: {
63647                            x1: p1[0],
63648                            y1: p1[1],
63649                            x2: p2[0],
63650                            y2: p2[1]
63651                        },
63652                        style: {
63653                            lineWidth: lineWidth
63654                        },
63655                        silent: true
63656                    })));
63657            }
63658
63659            for (var i = 0; i < splitLines.length; ++i) {
63660                this.group.add(mergePath(splitLines[i], {
63661                    style: {
63662                        stroke: lineColors[i % lineColors.length],
63663                        lineDash: lineStyleModel.getLineDash(lineWidth),
63664                        lineWidth: lineWidth
63665                    },
63666                    silent: true
63667                }));
63668            }
63669        }
63670    });
63671
63672    /*
63673* Licensed to the Apache Software Foundation (ASF) under one
63674* or more contributor license agreements.  See the NOTICE file
63675* distributed with this work for additional information
63676* regarding copyright ownership.  The ASF licenses this file
63677* to you under the Apache License, Version 2.0 (the
63678* "License"); you may not use this file except in compliance
63679* with the License.  You may obtain a copy of the License at
63680*
63681*   http://www.apache.org/licenses/LICENSE-2.0
63682*
63683* Unless required by applicable law or agreed to in writing,
63684* software distributed under the License is distributed on an
63685* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
63686* KIND, either express or implied.  See the License for the
63687* specific language governing permissions and limitations
63688* under the License.
63689*/
63690
63691    var AxisModel$4 = ComponentModel.extend({
63692
63693        type: 'singleAxis',
63694
63695        layoutMode: 'box',
63696
63697        /**
63698         * @type {module:echarts/coord/single/SingleAxis}
63699         */
63700        axis: null,
63701
63702        /**
63703         * @type {module:echarts/coord/single/Single}
63704         */
63705        coordinateSystem: null,
63706
63707        /**
63708         * @override
63709         */
63710        getCoordSysModel: function () {
63711            return this;
63712        }
63713
63714    });
63715
63716    var defaultOption$2 = {
63717
63718        left: '5%',
63719        top: '5%',
63720        right: '5%',
63721        bottom: '5%',
63722
63723        type: 'value',
63724
63725        position: 'bottom',
63726
63727        orient: 'horizontal',
63728
63729        axisLine: {
63730            show: true,
63731            lineStyle: {
63732                width: 2,
63733                type: 'solid'
63734            }
63735        },
63736
63737        // Single coordinate system and single axis is the,
63738        // which is used as the parent tooltip model.
63739        // same model, so we set default tooltip show as true.
63740        tooltip: {
63741            show: true
63742        },
63743
63744        axisTick: {
63745            show: true,
63746            length: 6,
63747            lineStyle: {
63748                width: 2
63749            }
63750        },
63751
63752        axisLabel: {
63753            show: true,
63754            interval: 'auto'
63755        },
63756
63757        splitLine: {
63758            show: true,
63759            lineStyle: {
63760                type: 'dashed',
63761                opacity: 0.2
63762            }
63763        }
63764    };
63765
63766    function getAxisType$2(axisName, option) {
63767        return option.type || (option.data ? 'category' : 'value');
63768    }
63769
63770    merge(AxisModel$4.prototype, axisModelCommonMixin);
63771
63772    axisModelCreator('single', AxisModel$4, getAxisType$2, defaultOption$2);
63773
63774    /*
63775* Licensed to the Apache Software Foundation (ASF) under one
63776* or more contributor license agreements.  See the NOTICE file
63777* distributed with this work for additional information
63778* regarding copyright ownership.  The ASF licenses this file
63779* to you under the Apache License, Version 2.0 (the
63780* "License");
63780 you may not use this file except in compliance
63781* with the License.  You may obtain a copy of the License at
63782*
63783*   http://www.apache.org/licenses/LICENSE-2.0
63784*
63785* Unless required by applicable law or agreed to in writing,
63786* software distributed under the License is distributed on an
63787* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
63788* KIND, either express or implied.  See the License for the
63789* specific language governing permissions and limitations
63790* under the License.
63791*/
63792
63793    /**
63794     * @param {Object} finder contains {seriesIndex, dataIndex, dataIndexInside}
63795     * @param {module:echarts/model/Global} ecModel
63796     * @return {Object} {point: [x, y], el: ...} point Will not be null.
63797     */
63798    var findPointFromSeries = function (finder, ecModel) {
63799        var point = [];
63800        var seriesIndex = finder.seriesIndex;
63801        var seriesModel;
63802        if (seriesIndex == null || !(
63803                seriesModel = ecModel.getSeriesByIndex(seriesIndex)
63804            )) {
63805            return {point: []};
63806        }
63807
63808        var data = seriesModel.getData();
63809        var dataIndex = queryDataIndex(data, finder);
63810        if (dataIndex == null || dataIndex < 0 || isArray(dataIndex)) {
63811            return {point: []};
63812        }
63813
63814        var el = data.getItemGraphicEl(dataIndex);
63815        var coordSys = seriesModel.coordinateSystem;
63816
63817        if (seriesModel.getTooltipPosition) {
63818            point = seriesModel.getTooltipPosition(dataIndex) || [];
63819        }
63820        else if (coordSys && coordSys.dataToPoint) {
63821            point = coordSys.dataToPoint(
63822                data.getValues(
63823                    map(coordSys.dimensions, function (dim) {
63824                        return data.mapDimension(dim);
63825                    }), dataIndex, true
63826                )
63827            ) || [];
63828        }
63829        else if (el) {
63830            // Use graphic bounding rect
63831            var rect = el.getBoundingRect().clone();
63832            rect.applyTransform(el.transform);
63833            point = [
63834                rect.x + rect.width / 2,
63835                rect.y + rect.height / 2
63836            ];
63837        }
63838
63839        return {point: point, el: el};
63840    };
63841
63842    /*
63843* Licensed to the Apache Software Foundation (ASF) under one
63844* or more contributor license agreements.  See the NOTICE file
63845* distributed with this work for additional information
63846* regarding copyright ownership.  The ASF licenses this file
63847* to you under the Apache License, Version 2.0 (the
63848* "License"); you may not use this file except in compliance
63849* with the License.  You may obtain a copy of the License at
63850*
63851*   http://www.apache.org/licenses/LICENSE-2.0
63852*
63853* Unless required by applicable law or agreed to in writing,
63854* software distributed under the License is distributed on an
63855* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
63856* KIND, either express or implied.  See the License for the
63857* specific language governing permissions and limitations
63858* under the License.
63859*/
63860
63861    var each$14 = each$1;
63862    var curry$3 = curry;
63863    var inner$7 = makeInner();
63864
63865    /**
63866     * Basic logic: check all axis, if they do not demand show/highlight,
63867     * then hide/downplay them.
63868     *
63869     * @param {Object} coordSysAxesInfo
63870     * @param {Object} payload
63871     * @param {string} [payload.currTrigger] 'click' | 'mousemove' | 'leave'
63872     * @param {Array.<number>} [payload.x] x and y, which are mandatory, specify a point to
63873     *              trigger axisPointer and tooltip.
63874     * @param {Array.<number>} [payload.y] x and y, which are mandatory, specify a point to
63875     *              trigger axisPointer and tooltip.
63876     * @param {Object} [payload.seriesIndex] finder, optional, restrict target axes.
63877     * @param {Object} [payload.dataIndex] finder, restrict target axes.
63878     * @param {Object} [payload.axesInfo] finder, restrict target axes.
63879     *        [{
63880 *          axisDim: 'x'|'y'|'angle'|...,
63881 *          axisIndex: ...,
63882 *          value: ...
63883 *        }, ...]
63884     * @param {Function} [payload.dispatchAction]
63885     * @param {Object} [payload.tooltipOption]
63886     * @param {Object|Array.<number>|Function} [payload.position] Tooltip position,
63887     *        which can be specified in dispatchAction
63888     * @param {module:echarts/model/Global} ecModel
63889     * @param {module:echarts/ExtensionAPI} api
63890     * @return {Object} content of event obj for echarts.connect.
63891     */
63892    var axisTrigger = function (payload, ecModel, api) {
63893        var currTrigger = payload.currTrigger;
63894        var point = [payload.x, payload.y];
vendor: 6,257 bytes, lines 63895-64035
63895        var finder = payload;
63896        var dispatchAction = payload.dispatchAction || bind(api.dispatchAction, api);
63897        var coordSysAxesInfo = ecModel.getComponent('axisPointer').coordSysAxesInfo;
63898
63899        // Pending
63900        // See #6121. But we are not able to reproduce it yet.
63901        if (!coordSysAxesInfo) {
63902            return;
63903        }
63904
63905        if (illegalPoint(point)) {
63906            // Used in the default behavior of `connection`: use the sample seriesIndex
63907            // and dataIndex. And also used in the tooltipView trigger.
63908            point = findPointFromSeries({
63909                seriesIndex: finder.seriesIndex,
63910                // Do not use dataIndexInside from other ec instance.
63911                // FIXME: auto detect it?
63912                dataIndex: finder.dataIndex
63913            }, ecModel).point;
63914        }
63915        var isIllegalPoint = illegalPoint(point);
63916
63917        // Axis and value can be specified when calling dispatchAction({type: 'updateAxisPointer'}).
63918        // Notice: In this case, it is difficult to get the `point` (which is necessary to show
63919        // tooltip, so if point is not given, we just use the point found by sample seriesIndex
63920        // and dataIndex.
63921        var inputAxesInfo = finder.axesInfo;
63922
63923        var axesInfo = coordSysAxesInfo.axesInfo;
63924        var shouldHide = currTrigger === 'leave' || illegalPoint(point);
63925        var outputFinder = {};
63926
63927        var showValueMap = {};
63928        var dataByCoordSys = {list: [], map: {}};
63929        var updaters = {
63930            showPointer: curry$3(showPointer, showValueMap),
63931            showTooltip: curry$3(showTooltip, dataByCoordSys)
63932        };
63933
63934        // Process for triggered axes.
63935        each$14(coordSysAxesInfo.coordSysMap, function (coordSys, coordSysKey) {
63936            // If a point given, it must be contained by the coordinate system.
63937            var coordSysContainsPoint = isIllegalPoint || coordSys.containPoint(point);
63938
63939            each$14(coordSysAxesInfo.coordSysAxesInfo[coordSysKey], function (axisInfo, key) {
63940                var axis = axisInfo.axis;
63941                var inputAxisInfo = findInputAxisInfo(inputAxesInfo, axisInfo);
63942                // If no inputAxesInfo, no axis is restricted.
63943                if (!shouldHide && coordSysContainsPoint && (!inputAxesInfo || inputAxisInfo)) {
63944                    var val = inputAxisInfo && inputAxisInfo.value;
63945                    if (val == null && !isIllegalPoint) {
63946                        val = axis.pointToData(point);
63947                    }
63948                    val != null && processOnAxis(axisInfo, val, updaters, false, outputFinder);
63949                }
63950            });
63951        });
63952
63953        // Process for linked axes.
63954        var linkTriggers = {};
63955        each$14(axesInfo, function (tarAxisInfo, tarKey) {
63956            var linkGroup = tarAxisInfo.linkGroup;
63957
63958            // If axis has been triggered in the previous stage, it should not be triggered by link.
63959            if (linkGroup && !showValueMap[tarKey]) {
63960                each$14(linkGroup.axesInfo, function (srcAxisInfo, srcKey) {
63961                    var srcValItem = showValueMap[srcKey];
63962                    // If srcValItem exist, source axis is triggered, so link to target axis.
63963                    if (srcAxisInfo !== tarAxisInfo && srcValItem) {
63964                        var val = srcValItem.value;
63965                        linkGroup.mapper && (val = tarAxisInfo.axis.scale.parse(linkGroup.mapper(
63966                            val, makeMapperParam(srcAxisInfo), makeMapperParam(tarAxisInfo)
63967                        )));
63968                        linkTriggers[tarAxisInfo.key] = val;
63969                    }
63970                });
63971            }
63972        });
63973        each$14(linkTriggers, function (val, tarKey) {
63974            processOnAxis(axesInfo[tarKey], val, updaters, true, outputFinder);
63975        });
63976
63977        updateModelActually(showValueMap, axesInfo, outputFinder);
63978        dispatchTooltipActually(dataByCoordSys, point, payload, dispatchAction);
63979        dispatchHighDownActually(axesInfo, dispatchAction, api);
63980
63981        return outputFinder;
63982    };
63983
63984    function processOnAxis(axisInfo, newValue, updaters, dontSnap, outputFinder) {
63985        var axis = axisInfo.axis;
63986
63987        if (axis.scale.isBlank() || !axis.containData(newValue)) {
63988            return;
63989        }
63990
63991        if (!axisInfo.involveSeries) {
63992            updaters.showPointer(axisInfo, newValue);
63993            return;
63994        }
63995
63996        // Heavy calculation. So put it after axis.containData checking.
63997        var payloadInfo = buildPayloadsBySeries(newValue, axisInfo);
63998        var payloadBatch = payloadInfo.payloadBatch;
63999        var snapToValue = payloadInfo.snapToValue;
64000
64001        // Fill content of event obj for echarts.connect.
64002        // By defualt use the first involved series data as a sample to connect.
64003        if (payloadBatch[0] && outputFinder.seriesIndex == null) {
64004            extend(outputFinder, payloadBatch[0]);
64005        }
64006
64007        // If no linkSource input, this process is for collecting link
64008        // target, where snap should not be accepted.
64009        if (!dontSnap && axisInfo.snap) {
64010            if (axis.containData(snapToValue) && snapToValue != null) {
64011                newValue = snapToValue;
64012            }
64013        }
64014
64015        updaters.showPointer(axisInfo, newValue, payloadBatch, outputFinder);
64016        // Tooltip should always be snapToValue, otherwise there will be
64017        // incorrect "axis value ~ series value" mapping displayed in tooltip.
64018        updaters.showTooltip(axisInfo, payloadInfo, snapToValue);
64019    }
64020
64021    function buildPayloadsBySeries(value, axisInfo) {
64022        var axis = axisInfo.axis;
64023        var dim = axis.dim;
64024        var snapToValue = value;
64025        var payloadBatch = [];
64026        var minDist = Number.MAX_VALUE;
64027        var minDiff = -1;
64028
64029        each$14(axisInfo.seriesModels, function (series, idx) {
64030            var dataDim = series.getData().mapDimension(dim, true);
64031            var seriesNestestValue;
64032            var dataIndices;
64033
64034            if (series.getAxisTooltipData) {
64035                var result = series.getAxisTooltipData(dataDim, value, axis);
vendor: 9,151 bytes, lines 64036-64258
64036                dataIndices = result.dataIndices;
64037                seriesNestestValue = result.nestestValue;
64038            }
64039            else {
64040                dataIndices = series.getData().indicesOfNearest(
64041                    dataDim[0],
64042                    value,
64043                    // Add a threshold to avoid find the wrong dataIndex
64044                    // when data length is not same.
64045                    // false,
64046                    axis.type === 'category' ? 0.5 : null
64047                );
64048                if (!dataIndices.length) {
64049                    return;
64050                }
64051                seriesNestestValue = series.getData().get(dataDim[0], dataIndices[0]);
64052            }
64053
64054            if (seriesNestestValue == null || !isFinite(seriesNestestValue)) {
64055                return;
64056            }
64057
64058            var diff = value - seriesNestestValue;
64059            var dist = Math.abs(diff);
64060            // Consider category case
64061            if (dist <= minDist) {
64062                if (dist < minDist || (diff >= 0 && minDiff < 0)) {
64063                    minDist = dist;
64064                    minDiff = diff;
64065                    snapToValue = seriesNestestValue;
64066                    payloadBatch.length = 0;
64067                }
64068                each$14(dataIndices, function (dataIndex) {
64069                    payloadBatch.push({
64070                        seriesIndex: series.seriesIndex,
64071                        dataIndexInside: dataIndex,
64072                        dataIndex: series.getData().getRawIndex(dataIndex)
64073                    });
64074                });
64075            }
64076        });
64077
64078        return {
64079            payloadBatch: payloadBatch,
64080            snapToValue: snapToValue
64081        };
64082    }
64083
64084    function showPointer(showValueMap, axisInfo, value, payloadBatch) {
64085        showValueMap[axisInfo.key] = {value: value, payloadBatch: payloadBatch};
64086    }
64087
64088    function showTooltip(dataByCoordSys, axisInfo, payloadInfo, value) {
64089        var payloadBatch = payloadInfo.payloadBatch;
64090        var axis = axisInfo.axis;
64091        var axisModel = axis.model;
64092        var axisPointerModel = axisInfo.axisPointerModel;
64093
64094        // If no data, do not create anything in dataByCoordSys,
64095        // whose length will be used to judge whether dispatch action.
64096        if (!axisInfo.triggerTooltip || !payloadBatch.length) {
64097            return;
64098        }
64099
64100        var coordSysModel = axisInfo.coordSys.model;
64101        var coordSysKey = makeKey(coordSysModel);
64102        var coordSysItem = dataByCoordSys.map[coordSysKey];
64103        if (!coordSysItem) {
64104            coordSysItem = dataByCoordSys.map[coordSysKey] = {
64105                coordSysId: coordSysModel.id,
64106                coordSysIndex: coordSysModel.componentIndex,
64107                coordSysType: coordSysModel.type,
64108                coordSysMainType: coordSysModel.mainType,
64109                dataByAxis: []
64110            };
64111            dataByCoordSys.list.push(coordSysItem);
64112        }
64113
64114        coordSysItem.dataByAxis.push({
64115            axisDim: axis.dim,
64116            axisIndex: axisModel.componentIndex,
64117            axisType: axisModel.type,
64118            axisId: axisModel.id,
64119            value: value,
64120            // Caustion: viewHelper.getValueLabel is actually on "view stage", which
64121            // depends that all models have been updated. So it should not be performed
64122            // here. Considering axisPointerModel used here is volatile, which is hard
64123            // to be retrieve in TooltipView, we prepare parameters here.
64124            valueLabelOpt: {
64125                precision: axisPointerModel.get('label.precision'),
64126                formatter: axisPointerModel.get('label.formatter')
64127            },
64128            seriesDataIndices: payloadBatch.slice()
64129        });
64130    }
64131
64132    function updateModelActually(showValueMap, axesInfo, outputFinder) {
64133        var outputAxesInfo = outputFinder.axesInfo = [];
64134        // Basic logic: If no 'show' required, 'hide' this axisPointer.
64135        each$14(axesInfo, function (axisInfo, key) {
64136            var option = axisInfo.axisPointerModel.option;
64137            var valItem = showValueMap[key];
64138
64139            if (valItem) {
64140                !axisInfo.useHandle && (option.status = 'show');
64141                option.value = valItem.value;
64142                // For label formatter param and highlight.
64143                option.seriesDataIndices = (valItem.payloadBatch || []).slice();
64144            }
64145            // When always show (e.g., handle used), remain
64146            // original value and status.
64147            else {
64148                // If hide, value still need to be set, consider
64149                // click legend to toggle axis blank.
64150                !axisInfo.useHandle && (option.status = 'hide');
64151            }
64152
64153            // If status is 'hide', should be no info in payload.
64154            option.status === 'show' && outputAxesInfo.push({
64155                axisDim: axisInfo.axis.dim,
64156                axisIndex: axisInfo.axis.model.componentIndex,
64157                value: option.value
64158            });
64159        });
64160    }
64161
64162    function dispatchTooltipActually(dataByCoordSys, point, payload, dispatchAction) {
64163        // Basic logic: If no showTip required, hideTip will be dispatched.
64164        if (illegalPoint(point) || !dataByCoordSys.list.length) {
64165            dispatchAction({type: 'hideTip'});
64166            return;
64167        }
64168
64169        // In most case only one axis (or event one series is used). It is
64170        // convinient to fetch payload.seriesIndex and payload.dataIndex
64171        // dirtectly. So put the first seriesIndex and dataIndex of the first
64172        // axis on the payload.
64173        var sampleItem = ((dataByCoordSys.list[0].dataByAxis[0] || {}).seriesDataIndices || [])[0] || {};
64174
64175        dispatchAction({
64176            type: 'showTip',
64177            escapeConnect: true,
64178            x: point[0],
64179            y: point[1],
64180            tooltipOption: payload.tooltipOption,
64181            position: payload.position,
64182            dataIndexInside: sampleItem.dataIndexInside,
64183            dataIndex: sampleItem.dataIndex,
64184            seriesIndex: sampleItem.seriesIndex,
64185            dataByCoordSys: dataByCoordSys.list
64186        });
64187    }
64188
64189    function dispatchHighDownActually(axesInfo, dispatchAction, api) {
64190        // FIXME
64191        // highlight status modification shoule be a stage of main process?
64192        // (Consider confilct (e.g., legend and axisPointer) and setOption)
64193
64194        var zr = api.getZr();
64195        var highDownKey = 'axisPointerLastHighlights';
64196        var lastHighlights = inner$7(zr)[highDownKey] || {};
64197        var newHighlights = inner$7(zr)[highDownKey] = {};
64198
64199        // Update highlight/downplay status according to axisPointer model.
64200        // Build hash map and remove duplicate incidentally.
64201        each$14(axesInfo, function (axisInfo, key) {
64202            var option = axisInfo.axisPointerModel.option;
64203            option.status === 'show' && each$14(option.seriesDataIndices, function (batchItem) {
64204                var key = batchItem.seriesIndex + ' | ' + batchItem.dataIndex;
64205                newHighlights[key] = batchItem;
64206            });
64207        });
64208
64209        // Diff.
64210        var toHighlight = [];
64211        var toDownplay = [];
64212        each$1(lastHighlights, function (batchItem, key) {
64213            !newHighlights[key] && toDownplay.push(batchItem);
64214        });
64215        each$1(newHighlights, function (batchItem, key) {
64216            !lastHighlights[key] && toHighlight.push(batchItem);
64217        });
64218
64219        toDownplay.length && api.dispatchAction({
64220            type: 'downplay', escapeConnect: true, batch: toDownplay
64221        });
64222        toHighlight.length && api.dispatchAction({
64223            type: 'highlight', escapeConnect: true, batch: toHighlight
64224        });
64225    }
64226
64227    function findInputAxisInfo(inputAxesInfo, axisInfo) {
64228        for (var i = 0; i < (inputAxesInfo || []).length; i++) {
64229            var inputAxisInfo = inputAxesInfo[i];
64230            if (axisInfo.axis.dim === inputAxisInfo.axisDim
64231                && axisInfo.axis.model.componentIndex === inputAxisInfo.axisIndex
64232            ) {
64233                return inputAxisInfo;
64234            }
64235        }
64236    }
64237
64238    function makeMapperParam(axisInfo) {
64239        var axisModel = axisInfo.axis.model;
64240        var item = {};
64241        var dim = item.axisDim = axisInfo.axis.dim;
64242        item.axisIndex = item[dim + 'AxisIndex'] = axisModel.componentIndex;
64243        item.axisName = item[dim + 'AxisName'] = axisModel.name;
64244        item.axisId = item[dim + 'AxisId'] = axisModel.id;
64245        return item;
64246    }
64247
64248    function illegalPoint(point) {
64249        return !point || point[0] == null || isNaN(point[0]) || point[1] == null || isNaN(point[1]);
64250    }
64251
64252    /*
64253* Licensed to the Apache Software Foundation (ASF) under one
64254* or more contributor license agreements.  See the NOTICE file
64255* distributed with this work for additional information
64256* regarding copyright ownership.  The ASF licenses this file
64257* to you under the Apache License, Version 2.0 (the
64258* "License");
64258 you may not use this file except in compliance
64259* with the License.  You may obtain a copy of the License at
64260*
64261*   http://www.apache.org/licenses/LICENSE-2.0
64262*
64263* Unless required by applicable law or agreed to in writing,
64264* software distributed under the License is distributed on an
64265* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
64266* KIND, either express or implied.  See the License for the
64267* specific language governing permissions and limitations
64268* under the License.
64269*/
64270
64271    var AxisPointerModel = extendComponentModel({
64272
64273        type: 'axisPointer',
64274
64275        coordSysAxesInfo: null,
64276
64277        defaultOption: {
64278            // 'auto' means that show when triggered by tooltip or handle.
64279            show: 'auto',
64280            // 'click' | 'mousemove' | 'none'
64281            triggerOn: null, // set default in AxisPonterView.js
64282
64283            zlevel: 0,
64284            z: 50,
64285
64286            type: 'line',
64287            // axispointer triggered by tootip determine snap automatically,
64288            // see `modelHelper`.
64289            snap: false,
64290            triggerTooltip: true,
64291
64292            value: null,
64293            status: null, // Init value depends on whether handle is used.
64294
64295            // [group0, group1, ...]
64296            // Each group can be: {
64297            //      mapper: function () {},
64298            //      singleTooltip: 'multiple',  // 'multiple' or 'single'
64299            //      xAxisId: ...,
64300            //      yAxisName: ...,
64301            //      angleAxisIndex: ...
64302            // }
64303            // mapper: can be ignored.
64304            //      input: {axisInfo, value}
64305            //      output: {axisInfo, value}
64306            link: [],
64307
64308            // Do not set 'auto' here, otherwise global animation: false
64309            // will not effect at this axispointer.
64310            animation: null,
64311            animationDurationUpdate: 200,
64312
64313            lineStyle: {
64314                color: '#aaa',
64315                width: 1,
64316                type: 'solid'
64317            },
64318
64319            shadowStyle: {
64320                color: 'rgba(150,150,150,0.3)'
64321            },
64322
64323            label: {
64324                show: true,
64325                formatter: null, // string | Function
64326                precision: 'auto', // Or a number like 0, 1, 2 ...
64327                margin: 3,
64328                color: '#fff',
64329                padding: [5, 7, 5, 7],
64330                backgroundColor: 'auto', // default: axis line color
64331                borderColor: null,
64332                borderWidth: 0,
64333                shadowBlur: 3,
64334                shadowColor: '#aaa'
64335                // Considering applicability, common style should
64336                // better not have shadowOffset.
64337                // shadowOffsetX: 0,
64338                // shadowOffsetY: 2
64339            },
64340
64341            handle: {
64342                show: false,
64343                icon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4h1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7v-1.2h6.6z M13.3,22H6.7v-1.2h6.6z M13.3,19.6H6.7v-1.2h6.6z', // jshint ignore:line
64344                size: 45,
64345                // handle margin is from symbol center to axis, which is stable when circular move.
64346                margin: 50,
64347                // color: '#1b8bbd'
64348                // color: '#2f4554'
64349                color: '#333',
64350                shadowBlur: 3,
64351                shadowColor: '#aaa',
64352                shadowOffsetX: 0,
64353                shadowOffsetY: 2,
64354
64355                // For mobile performance
64356                throttle: 40
64357            }
64358        }
64359
64360    });
64361
64362    /*
64363* Licensed to the Apache Software Foundation (ASF) under one
64364* or more contributor license agreements.  See the NOTICE file
64365* distributed with this work for additional information
64366* regarding copyright ownership.  The ASF licenses this file
64367* to you under the Apache License, Version 2.0 (the
64368* "License"); you may not use this file except in compliance
64369* with the License.  You may obtain a copy of the License at
64370*
64371*   http://www.apache.org/licenses/LICENSE-2.0
64372*
64373* Unless required by applicable law or agreed to in writing,
64374* software distributed under the License is distributed on an
64375* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
64376* KIND, either express or implied.  See the License for the
64377* specific language governing permissions and limitations
64378* under the License.
64379*/
64380
64381    var inner$8 = makeInner();
64382    var each$15 = each$1;
64383
64384    /**
64385     * @param {string} key
64386     * @param {module:echarts/ExtensionAPI} api
64387     * @param {Function} handler
64388     *      param: {string} currTrigger
64389     *      param: {Array.<number>} point
64390     */
64391    function register(key, api, handler) {
64392        if (env$1.node) {
64393            return;
64394        }
64395
64396        var zr = api.getZr();
64397        inner$8(zr).records || (inner$8(zr).records = {});
64398
64399        initGlobalListeners(zr, api);
64400
64401        var record = inner$8(zr).records[key] || (inner$8(zr).records[key] = {});
64402        record.handler = handler;
64403    }
64404
64405    function initGlobalListeners(zr, api) {
64406        if (inner$8(zr).initialized) {
64407            return;
64408        }
64409
64410        inner$8(zr).initialized = true;
64411
64412        useHandler('click', curry(doEnter, 'click'));
64413        useHandler('mousemove', curry(doEnter, 'mousemove'));
64414        // useHandler('mouseout', onLeave);
64415        useHandler('globalout', onLeave);
64416
64417        function useHandler(eventType, cb) {
64418            zr.on(eventType, function (e) {
64419                var dis = makeDispatchAction(api);
64420
64421                each$15(inner$8(zr).records, function (record) {
64422                    record && cb(record, e, dis.dispatchAction);
64423                });
64424
64425                dispatchTooltipFinally(dis.pendings, api);
64426            });
64427        }
64428    }
64429
64430    function dispatchTooltipFinally(pendings, api) {
64431        var showLen = pendings.showTip.length;
64432        var hideLen = pendings.hideTip.length;
64433
64434        var actuallyPayload;
64435        if (showLen) {
64436            actuallyPayload = pendings.showTip[showLen - 1];
64437        }
64438        else if (hideLen) {
64439            actuallyPayload = pendings.hideTip[hideLen - 1];
64440        }
64441        if (actuallyPayload) {
64442            actuallyPayload.dispatchAction = null;
64443            api.dispatchAction(actuallyPayload);
64444        }
64445    }
64446
64447    function onLeave(record, e, dispatchAction) {
64448        record.handler('leave', null, dispatchAction);
64449    }
64450
64451    function doEnter(currTrigger, record, e, dispatchAction) {
64452        record.handler(currTrigger, e, dispatchAction);
64453    }
64454
64455    function makeDispatchAction(api) {
64456        var pendings = {
64457            showTip: [],
64458            hideTip: []
64459        };
64460        // FIXME
64461        // better approach?
64462        // 'showTip' and 'hideTip' can be triggered by axisPointer and tooltip,
64463        // which may be conflict, (axisPointer call showTip but tooltip call hideTip);
64464        // So we have to add "final stage" to merge those dispatched actions.
64465        var dispatchAction = function (payload) {
64466            var pendingList = pendings[payload.type];
64467            if (pendingList) {
64468                pendingList.push(payload);
64469            }
64470            else {
64471                payload.dispatchAction = dispatchAction;
64472                api.dispatchAction(payload);
64473            }
64474        };
64475
64476        return {
64477            dispatchAction: dispatchAction,
64478            pendings: pendings
64479        };
64480    }
64481
64482    /**
64483     * @param {string} key
64484     * @param {module:echarts/ExtensionAPI} api
64485     */
64486    function unregister(key, api) {
64487        if (env$1.node) {
64488            return;
64489        }
64490        var zr = api.getZr();
64491        var record = (inner$8(zr).records || {})[key];
64492        if (record) {
64493            inner$8(zr).records[key] = null;
64494        }
64495    }
64496
64497    /*
64498* Licensed to the Apache Software Foundation (ASF) under one
64499* or more contributor license agreements.  See the NOTICE file
64500* distributed with this work for additional information
64501* regarding copyright ownership.  The ASF licenses this file
64502* to you under the Apache License, Version 2.0 (the
64503* "License"); you may not use this file except in compliance
64504* with the License.  You may obtain a copy of the License at
64505*
64506*   http://www.apache.org/licenses/LICENSE-2.0
64507*
64508* Unless required by applicable law or agreed to in writing,
64509* software distributed under the License is distributed on an
64510* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
64511* KIND, either express or implied.  See the License for the
64512* specific language governing permissions and limitations
64513* under the License.
64514*/
64515
64516    var AxisPointerView = extendComponentView({
64517
64518        type: 'axisPointer',
64519
64520        render: function (globalAxisPointerModel, ecModel, api) {
64521            var globalTooltipModel = ecModel.getComponent('tooltip');
64522            var triggerOn = globalAxisPointerModel.get('triggerOn')
64523                || (globalTooltipModel && globalTooltipModel.get('triggerOn') || 'mousemove|click');
64524
64525            // Register global listener in AxisPointerView to enable
64526            // AxisPointerView to be independent to Tooltip.
64527            register(
64528                'axisPointer',
64529                api,
64530                function (currTrigger, e, dispatchAction) {
64531                    // If 'none', it is not controlled by mouse totally.
64532                    if (triggerOn !== 'none'
64533                        && (currTrigger === 'leave' || triggerOn.indexOf(currTrigger) >= 0)
64534                    ) {
64535                        dispatchAction({
64536                            type: 'updateAxisPointer',
64537                            currTrigger: currTrigger,
64538                            x: e && e.offsetX,
64539                            y: e && e.offsetY
64540                        });
64541                    }
64542                }
64543            );
64544        },
64545
64546        /**
64547         * @override
64548         */
64549        remove: function (ecModel, api) {
64550            unregister(api.getZr(), 'axisPointer');
64551            AxisPointerView.superApply(this._model, 'remove', arguments);
64552        },
64553
64554        /**
64555         * @override
64556         */
64557        dispose: function (ecModel, api) {
64558            unregister('axisPointer', api);
64559            AxisPointerView.superApply(this._model, 'dispose', arguments);
64560        }
64561
64562    });
64563
64564    /*
64565* Licensed to the Apache Software Foundation (ASF) under one
64566* or more contributor license agreements.  See the NOTICE file
64567* distributed with this work for additional information
64568* regarding copyright ownership.  The ASF licenses this file
64569* to you under the Apache License, Version 2.0 (the
64570* "License"); you may not use this file except in compliance
64571* with the License.  You may obtain a copy of the License at
64572*
64573*   http://www.apache.org/licenses/LICENSE-2.0
64574*
64575* Unless required by applicable law or agreed to in writing,
64576* software distributed under the License is distributed on an
64577* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
64578* KIND, either express or implied.  See the License for the
64579* specific language governing permissions and limitations
64580* under the License.
64581*/
64582
64583    var inner$9 = makeInner();
64584    var clone$4 = clone;
64585    var bind$2 = bind;
64586
64587    /**
64588     * Base axis pointer class in 2D.
64589     * Implemenents {module:echarts/component/axis/IAxisPointer}.
64590     */
vendor: 4,557 bytes, lines 64591-64740
64591    function BaseAxisPointer () {
64592    }
64593
64594    BaseAxisPointer.prototype = {
64595
64596        /**
64597         * @private
64598         */
64599        _group: null,
64600
64601        /**
64602         * @private
64603         */
64604        _lastGraphicKey: null,
64605
64606        /**
64607         * @private
64608         */
64609        _handle: null,
64610
64611        /**
64612         * @private
64613         */
64614        _dragging: false,
64615
64616        /**
64617         * @private
64618         */
64619        _lastValue: null,
64620
64621        /**
64622         * @private
64623         */
64624        _lastStatus: null,
64625
64626        /**
64627         * @private
64628         */
64629        _payloadInfo: null,
64630
64631        /**
64632         * In px, arbitrary value. Do not set too small,
64633         * no animation is ok for most cases.
64634         * @protected
64635         */
64636        animationThreshold: 15,
64637
64638        /**
64639         * @implement
64640         */
64641        render: function (axisModel, axisPointerModel, api, forceRender) {
64642            var value = axisPointerModel.get('value');
64643            var status = axisPointerModel.get('status');
64644
64645            // Bind them to `this`, not in closure, otherwise they will not
64646            // be replaced when user calling setOption in not merge mode.
64647            this._axisModel = axisModel;
64648            this._axisPointerModel = axisPointerModel;
64649            this._api = api;
64650
64651            // Optimize: `render` will be called repeatly during mouse move.
64652            // So it is power consuming if performing `render` each time,
64653            // especially on mobile device.
64654            if (!forceRender
64655                && this._lastValue === value
64656                && this._lastStatus === status
64657            ) {
64658                return;
64659            }
64660            this._lastValue = value;
64661            this._lastStatus = status;
64662
64663            var group = this._group;
64664            var handle = this._handle;
64665
64666            if (!status || status === 'hide') {
64667                // Do not clear here, for animation better.
64668                group && group.hide();
64669                handle && handle.hide();
64670                return;
64671            }
64672            group && group.show();
64673            handle && handle.show();
64674
64675            // Otherwise status is 'show'
64676            var elOption = {};
64677            this.makeElOption(elOption, value, axisModel, axisPointerModel, api);
64678
64679            // Enable change axis pointer type.
64680            var graphicKey = elOption.graphicKey;
64681            if (graphicKey !== this._lastGraphicKey) {
64682                this.clear(api);
64683            }
64684            this._lastGraphicKey = graphicKey;
64685
64686            var moveAnimation = this._moveAnimation =
64687                this.determineAnimation(axisModel, axisPointerModel);
64688
64689            if (!group) {
64690                group = this._group = new Group();
64691                this.createPointerEl(group, elOption, axisModel, axisPointerModel);
64692                this.createLabelEl(group, elOption, axisModel, axisPointerModel);
64693                api.getZr().add(group);
64694            }
64695            else {
64696                var doUpdateProps = curry(updateProps$1, axisPointerModel, moveAnimation);
64697                this.updatePointerEl(group, elOption, doUpdateProps, axisPointerModel);
64698                this.updateLabelEl(group, elOption, doUpdateProps, axisPointerModel);
64699            }
64700
64701            updateMandatoryProps(group, axisPointerModel, true);
64702
64703            this._renderHandle(value);
64704        },
64705
64706        /**
64707         * @implement
64708         */
64709        remove: function (api) {
64710            this.clear(api);
64711        },
64712
64713        /**
64714         * @implement
64715         */
64716        dispose: function (api) {
64717            this.clear(api);
64718        },
64719
64720        /**
64721         * @protected
64722         */
64723        determineAnimation: function (axisModel, axisPointerModel) {
64724            var animation = axisPointerModel.get('animation');
64725            var axis = axisModel.axis;
64726            var isCategoryAxis = axis.type === 'category';
64727            var useSnap = axisPointerModel.get('snap');
64728
64729            // Value axis without snap always do not snap.
64730            if (!useSnap && !isCategoryAxis) {
64731                return false;
64732            }
64733
64734            if (animation === 'auto' || animation == null) {
64735                var animationThreshold = this.animationThreshold;
64736                if (isCategoryAxis && axis.getBandWidth() > animationThreshold) {
64737                    return true;
64738                }
64739
64740                // It is important to auto animation when snap used. Consider if there is
64741                // a dataZoom, animation will be disabled when too many points exist, while
64742                // it will be enabled for better visual effect when little points exist.
64743                if (useSnap) {
64744                    var seriesDataCount = getAxisInfo(axisModel).seriesDataCount;
64745                    var axisExtent = axis.getExtent();
64746                    // Approximate band width
64747                    return Math.abs(axisExtent[0] - axisExtent[1]) / seriesDataCount > animationThreshold;
64748                }
64749
64750                return false;
64751            }
64752
64753            return animation === true;
64754        },
64755
64756        /**
64757         * add {pointer, label, graphicKey} to elOption
64758         * @protected
64759         */
64760        makeElOption: function (elOption, value, axisModel, axisPointerModel, api) {
64761            // Shoule be implemenented by sub-class.
64762        },
64763
64764        /**
64765         * @protected
64766         */
64767        createPointerEl: function (group, elOption, axisModel, axisPointerModel) {
64768            var pointerOption = elOption.pointer;
64769            if (pointerOption) {
64770                var pointerEl = inner$9(group).pointerEl = new graphic[pointerOption.type](
64771                    clone$4(elOption.pointer)
64772                );
64773                group.add(pointerEl);
64774            }
64775        },
64776
64777        /**
64778         * @protected
64779         */
64780        createLabelEl: function (group, elOption, axisModel, axisPointerModel) {
64781            if (elOption.label) {
64782                var labelEl = inner$9(group).labelEl = new Rect(
64783                    clone$4(elOption.label)
64784                );
64785
64786                group.add(labelEl);
64787                updateLabelShowHide(labelEl, axisPointerModel);
64788            }
64789        },
64790
64791        /**
64792         * @protected
64793         */
64794        updatePointerEl: function (group, elOption, updateProps$$1) {
64795            var pointerEl = inner$9(group).pointerEl;
64796            if (pointerEl) {
64797                pointerEl.setStyle(elOption.pointer.style);
64798                updateProps$$1(pointerEl, {shape: elOption.pointer.shape});
64799            }
64800        },
64801
64802        /**
64803         * @protected
64804         */
64805        updateLabelEl: function (group, elOption, updateProps$$1, axisPointerModel) {
64806            var labelEl = inner$9(group).labelEl;
64807            if (labelEl) {
64808                labelEl.setStyle(elOption.label.style);
64809                updateProps$$1(labelEl, {
64810                    // Consider text length change in vertical axis, animation should
64811                    // be used on shape, otherwise the effect will be weird.
64812                    shape: elOption.label.shape,
64813                    position: elOption.label.position
64814                });
64815
64816                updateLabelShowHide(labelEl, axisPointerModel);
64817            }
64818        },
64819
64820        /**
64821         * @private
64822         */
64823        _renderHandle: function (value) {
64824            if (this._dragging || !this.updateHandleTransform) {
64825                return;
64826            }
64827
64828            var axisPointerModel = this._axisPointerModel;
64829            var zr = this._api.getZr();
64830            var handle = this._handle;
64831            var handleModel = axisPointerModel.getModel('handle');
64832
64833            var status = axisPointerModel.get('status');
64834            if (!handleModel.get('show') || !status || status === 'hide') {
64835                handle && zr.remove(handle);
64836                this._handle = null;
64837                return;
64838            }
64839
64840            var isInit;
64841            if (!this._handle) {
64842                isInit = true;
64843                handle = this._handle = createIcon(
64844                    handleModel.get('icon'),
64845                    {
64846                        cursor: 'move',
64847                        draggable: true,
64848                        onmousemove: function (e) {
64849                            // Fot mobile devicem, prevent screen slider on the button.
64850                            stop(e.event);
64851                        },
64852                        onmousedown: bind$2(this._onHandleDragMove, this, 0, 0),
64853                        drift: bind$2(this._onHandleDragMove, this),
64854                        ondragend: bind$2(this._onHandleDragEnd, this)
64855                    }
64856                );
64857                zr.add(handle);
64858            }
64859
64860            updateMandatoryProps(handle, axisPointerModel, false);
64861
64862            // update style
64863            var includeStyles = [
64864                'color', 'borderColor', 'borderWidth', 'opacity',
64865                'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY'
64866            ];
64867            handle.setStyle(handleModel.getItemStyle(null, includeStyles));
64868
64869            // update position
64870            var handleSize = handleModel.get('size');
64871            if (!isArray(handleSize)) {
64872                handleSize = [handleSize, handleSize];
64873            }
64874            handle.attr('scale', [handleSize[0] / 2, handleSize[1] / 2]);
64875
64876            createOrUpdate(
64877                this,
64878                '_doDispatchAxisPointer',
64879                handleModel.get('throttle') || 0,
64880                'fixRate'
64881            );
64882
64883            this._moveHandleToValue(value, isInit);
64884        },
64885
64886        /**
64887         * @private
64888         */
64889        _moveHandleToValue: function (value, isInit) {
64890            updateProps$1(
64891                this._axisPointerModel,
64892                !isInit && this._moveAnimation,
64893                this._handle,
64894                getHandleTransProps(this.getHandleTransform(
64895                    value, this._axisModel, this._axisPointerModel
64896                ))
64897            );
64898        },
64899
64900        /**
64901         * @private
64902         */
64903        _onHandleDragMove: function (dx, dy) {
64904            var handle = this._handle;
64905            if (!handle) {
64906                return;
64907            }
64908
64909            this._dragging = true;
64910
64911            // Persistent for throttle.
64912            var trans = this.updateHandleTransform(
64913                getHandleTransProps(handle),
64914                [dx, dy],
64915                this._axisModel,
64916                this._axisPointerModel
64917            );
64918            this._payloadInfo = trans;
64919
64920            handle.stopAnimation();
64921            handle.attr(getHandleTransProps(trans));
64922            inner$9(handle).lastProp = null;
64923
64924            this._doDispatchAxisPointer();
64925        },
64926
64927        /**
64928         * Throttled method.
64929         * @private
64930         */
64931        _doDispatchAxisPointer: function () {
64932            var handle = this._handle;
64933            if (!handle) {
64934                return;
64935            }
64936
64937            var payloadInfo = this._payloadInfo;
64938            var axisModel = this._axisModel;
64939            this._api.dispatchAction({
64940                type: 'updateAxisPointer',
64941                x: payloadInfo.cursorPoint[0],
64942                y: payloadInfo.cursorPoint[1],
64943                tooltipOption: payloadInfo.tooltipOption,
64944                axesInfo: [{
64945                    axisDim: axisModel.axis.dim,
64946                    axisIndex: axisModel.componentIndex
64947                }]
64948            });
64949        },
64950
64951        /**
64952         * @private
64953         */
64954        _onHandleDragEnd: function (moveAnimation) {
64955            this._dragging = false;
64956            var handle = this._handle;
64957            if (!handle) {
64958                return;
64959            }
64960
64961            var value = this._axisPointerModel.get('value');
64962            // Consider snap or categroy axis, handle may be not consistent with
64963            // axisPointer. So move handle to align the exact value position when
64964            // drag ended.
64965            this._moveHandleToValue(value);
64966
64967            // For the effect: tooltip will be shown when finger holding on handle
64968            // button, and will be hidden after finger left handle button.
64969            this._api.dispatchAction({
64970                type: 'hideTip'
64971            });
64972        },
64973
64974        /**
64975         * Should be implemenented by sub-class if support `handle`.
64976         * @protected
64977         * @param {number} value
64978         * @param {module:echarts/model/Model} axisModel
64979         * @param {module:echarts/model/Model} axisPointerModel
64980         * @return {Object} {position: [x, y], rotation: 0}
64981         */
64982        getHandleTransform: null,
64983
64984        /**
64985         * * Should be implemenented by sub-class if support `handle`.
64986         * @protected
64987         * @param {Object} transform {position, rotation}
64988         * @param {Array.<number>} delta [dx, dy]
64989         * @param {module:echarts/model/Model} axisModel
64990         * @param {module:echarts/model/Model} axisPointerModel
64991         * @return {Object} {position: [x, y], rotation: 0, cursorPoint: [x, y]}
64992         */
64993        updateHandleTransform: null,
64994
64995        /**
64996         * @private
64997         */
64998        clear: function (api) {
64999            this._lastValue = null;
65000            this._lastStatus = null;
65001
65002            var zr = api.getZr();
65003            var group = this._group;
65004            var handle = this._handle;
65005            if (zr && group) {
65006                this._lastGraphicKey = null;
65007                group && zr.remove(group);
65008                handle && zr.remove(handle);
65009                this._group = null;
65010                this._handle = null;
65011                this._payloadInfo = null;
65012            }
65013        },
65014
65015        /**
65016         * @protected
65017         */
65018        doClear: function () {
65019            // Implemented by sub-class if necessary.
65020        },
65021
65022        /**
65023         * @protected
65024         * @param {Array.<number>} xy
65025         * @param {Array.<number>} wh
65026         * @param {number} [xDimIndex=0] or 1
65027         */
65028        buildLabel: function (xy, wh, xDimIndex) {
65029            xDimIndex = xDimIndex || 0;
65030            return {
65031                x: xy[xDimIndex],
65032                y: xy[1 - xDimIndex],
65033                width: wh[xDimIndex],
65034                height: wh[1 - xDimIndex]
65035            };
65036        }
65037    };
65038
65039    BaseAxisPointer.prototype.constructor = BaseAxisPointer;
65040
65041
65042    function updateProps$1(animationModel, moveAnimation, el, props) {
65043        // Animation optimize.
65044        if (!propsEqual(inner$9(el).lastProp, props)) {
65045            inner$9(el).lastProp = props;
65046            moveAnimation
65047                ? updateProps(el, props, animationModel)
65048                : (el.stopAnimation(), el.attr(props));
65049        }
65050    }
65051
65052    function propsEqual(lastProps, newProps) {
65053        if (isObject$1(lastProps) && isObject$1(newProps)) {
65054            var equals = true;
65055            each$1(newProps, function (item, key) {
65056                equals = equals && propsEqual(lastProps[key], item);
65057            });
65058            return !!equals;
65059        }
65060        else {
65061            return lastProps === newProps;
65062        }
65063    }
65064
65065    function updateLabelShowHide(labelEl, axisPointerModel) {
65066        labelEl[axisPointerModel.get('label.show') ? 'show' : 'hide']();
65067    }
65068
65069    function getHandleTransProps(trans) {
65070        return {
65071            position: trans.position.slice(),
65072            rotation: trans.rotation || 0
65073        };
65074    }
65075
65076    function updateMandatoryProps(group, axisPointerModel, silent) {
65077        var z = axisPointerModel.get('z');
65078        var zlevel = axisPointerModel.get('zlevel');
65079
65080        group && group.traverse(function (el) {
65081            if (el.type !== 'group') {
65082                z != null && (el.z = z);
65083                zlevel != null && (el.zlevel = zlevel);
65084                el.silent = silent;
65085            }
65086        });
65087    }
65088
65089    enableClassExtend(BaseAxisPointer);
65090
65091    /*
65092* Licensed to the Apache Software Foundation (ASF) under one
65093* or more contributor license agreements.  See the NOTICE file
65094* distributed with this work for additional information
65095* regarding copyright ownership.  The ASF licenses this file
65096* to you under the Apache License, Version 2.0 (the
65097* "License");
65097 you may not use this file except in compliance
65098* with the License.  You may obtain a copy of the License at
65099*
65100*   http://www.apache.org/licenses/LICENSE-2.0
65101*
65102* Unless required by applicable law or agreed to in writing,
65103* software distributed under the License is distributed on an
65104* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
65105* KIND, either express or implied.  See the License for the
65106* specific language governing permissions and limitations
65107* under the License.
65108*/
65109
65110    /**
65111     * @param {module:echarts/model/Model} axisPointerModel
65112     */
65113    function buildElStyle(axisPointerModel) {
65114        var axisPointerType = axisPointerModel.get('type');
65115        var styleModel = axisPointerModel.getModel(axisPointerType + 'Style');
65116        var style;
65117        if (axisPointerType === 'line') {
65118            style = styleModel.getLineStyle();
65119            style.fill = null;
65120        }
65121        else if (axisPointerType === 'shadow') {
65122            style = styleModel.getAreaStyle();
65123            style.stroke = null;
65124        }
65125        return style;
65126    }
65127
65128    /**
65129     * @param {Function} labelPos {align, verticalAlign, position}
65130     */
65131    function buildLabelElOption(
65132        elOption, axisModel, axisPointerModel, api, labelPos
65133    ) {
65134        var value = axisPointerModel.get('value');
65135        var text = getValueLabel(
65136            value, axisModel.axis, axisModel.ecModel,
65137            axisPointerModel.get('seriesDataIndices'),
65138            {
65139                precision: axisPointerModel.get('label.precision'),
65140                formatter: axisPointerModel.get('label.formatter')
65141            }
65142        );
65143        var labelModel = axisPointerModel.getModel('label');
65144        var paddings = normalizeCssArray$1(labelModel.get('padding') || 0);
65145
65146        var font = labelModel.getFont();
65147        var textRect = getBoundingRect(text, font);
65148
65149        var position = labelPos.position;
65150        var width = textRect.width + paddings[1] + paddings[3];
65151        var height = textRect.height + paddings[0] + paddings[2];
65152
65153        // Adjust by align.
65154        var align = labelPos.align;
65155        align === 'right' && (position[0] -= width);
65156        align === 'center' && (position[0] -= width / 2);
65157        var verticalAlign = labelPos.verticalAlign;
65158        verticalAlign === 'bottom' && (position[1] -= height);
65159        verticalAlign === 'middle' && (position[1] -= height / 2);
65160
65161        // Not overflow ec container
65162        confineInContainer(position, width, height, api);
65163
65164        var bgColor = labelModel.get('backgroundColor');
65165        if (!bgColor || bgColor === 'auto') {
65166            bgColor = axisModel.get('axisLine.lineStyle.color');
65167        }
65168
65169        elOption.label = {
65170            shape: {x: 0, y: 0, width: width, height: height, r: labelModel.get('borderRadius')},
65171            position: position.slice(),
65172            // TODO: rich
65173            style: {
65174                text: text,
65175                textFont: font,
65176                textFill: labelModel.getTextColor(),
65177                textPosition: 'inside',
65178                fill: bgColor,
65179                stroke: labelModel.get('borderColor') || 'transparent',
65180                lineWidth: labelModel.get('borderWidth') || 0,
65181                shadowBlur: labelModel.get('shadowBlur'),
65182                shadowColor: labelModel.get('shadowColor'),
65183                shadowOffsetX: labelModel.get('shadowOffsetX'),
65184                shadowOffsetY: labelModel.get('shadowOffsetY')
65185            },
65186            // Lable should be over axisPointer.
65187            z2: 10
65188        };
65189    }
65190
65191// Do not overflow ec container
65192    function confineInContainer(position, width, height, api) {
65193        var viewWidth = api.getWidth();
65194        var viewHeight = api.getHeight();
65195        position[0] = Math.min(position[0] + width, viewWidth) - width;
65196        position[1] = Math.min(position[1] + height, viewHeight) - height;
65197        position[0] = Math.max(position[0], 0);
65198        position[1] = Math.max(position[1], 0);
65199    }
65200
65201    /**
65202     * @param {number} value
65203     * @param {module:echarts/coord/Axis} axis
65204     * @param {module:echarts/model/Global} ecModel
65205     * @param {Object} opt
65206     * @param {Array.<Object>} seriesDataIndices
65207     * @param {number|string} opt.precision 'auto' or a number
65208     * @param {string|Function} opt.formatter label formatter
65209     */
65210    function getValueLabel(value, axis, ecModel, seriesDataIndices, opt) {
65211        value = axis.scale.parse(value);
65212        var text = axis.scale.getLabel(
65213            // If `precision` is set, width can be fixed (like '12.00500'), which
65214            // helps to debounce when when moving label.
65215            value, {precision: opt.precision}
65216        );
65217        var formatter = opt.formatter;
65218
65219        if (formatter) {
65220            var params = {
65221                value: getAxisRawValue(axis, value),
65222                seriesData: []
65223            };
vendor: 6,734 bytes, lines 65224-65402
65224            each$1(seriesDataIndices, function (idxItem) {
65225                var series = ecModel.getSeriesByIndex(idxItem.seriesIndex);
65226                var dataIndex = idxItem.dataIndexInside;
65227                var dataParams = series && series.getDataParams(dataIndex);
65228                dataParams && params.seriesData.push(dataParams);
65229            });
65230
65231            if (isString(formatter)) {
65232                text = formatter.replace('{value}', text);
65233            }
65234            else if (isFunction$1(formatter)) {
65235                text = formatter(params);
65236            }
65237        }
65238
65239        return text;
65240    }
65241
65242    /**
65243     * @param {module:echarts/coord/Axis} axis
65244     * @param {number} value
65245     * @param {Object} layoutInfo {
65246 *  rotation, position, labelOffset, labelDirection, labelMargin
65247 * }
65248     */
65249    function getTransformedPosition (axis, value, layoutInfo) {
65250        var transform = create$1();
65251        rotate(transform, transform, layoutInfo.rotation);
65252        translate(transform, transform, layoutInfo.position);
65253
65254        return applyTransform$1([
65255            axis.dataToCoord(value),
65256            (layoutInfo.labelOffset || 0)
65257            + (layoutInfo.labelDirection || 1) * (layoutInfo.labelMargin || 0)
65258        ], transform);
65259    }
65260
65261    function buildCartesianSingleLabelElOption(
65262        value, elOption, layoutInfo, axisModel, axisPointerModel, api
65263    ) {
65264        var textLayout = AxisBuilder.innerTextLayout(
65265            layoutInfo.rotation, 0, layoutInfo.labelDirection
65266        );
65267        layoutInfo.labelMargin = axisPointerModel.get('label.margin');
65268        buildLabelElOption(elOption, axisModel, axisPointerModel, api, {
65269            position: getTransformedPosition(axisModel.axis, value, layoutInfo),
65270            align: textLayout.textAlign,
65271            verticalAlign: textLayout.textVerticalAlign
65272        });
65273    }
65274
65275    /**
65276     * @param {Array.<number>} p1
65277     * @param {Array.<number>} p2
65278     * @param {number} [xDimIndex=0] or 1
65279     */
65280    function makeLineShape(p1, p2, xDimIndex) {
65281        xDimIndex = xDimIndex || 0;
65282        return {
65283            x1: p1[xDimIndex],
65284            y1: p1[1 - xDimIndex],
65285            x2: p2[xDimIndex],
65286            y2: p2[1 - xDimIndex]
65287        };
65288    }
65289
65290    /**
65291     * @param {Array.<number>} xy
65292     * @param {Array.<number>} wh
65293     * @param {number} [xDimIndex=0] or 1
65294     */
65295    function makeRectShape(xy, wh, xDimIndex) {
65296        xDimIndex = xDimIndex || 0;
65297        return {
65298            x: xy[xDimIndex],
65299            y: xy[1 - xDimIndex],
65300            width: wh[xDimIndex],
65301            height: wh[1 - xDimIndex]
65302        };
65303    }
65304
65305    function makeSectorShape(cx, cy, r0, r, startAngle, endAngle) {
65306        return {
65307            cx: cx,
65308            cy: cy,
65309            r0: r0,
65310            r: r,
65311            startAngle: startAngle,
65312            endAngle: endAngle,
65313            clockwise: true
65314        };
65315    }
65316
65317    /*
65318* Licensed to the Apache Software Foundation (ASF) under one
65319* or more contributor license agreements.  See the NOTICE file
65320* distributed with this work for additional information
65321* regarding copyright ownership.  The ASF licenses this file
65322* to you under the Apache License, Version 2.0 (the
65323* "License"); you may not use this file except in compliance
65324* with the License.  You may obtain a copy of the License at
65325*
65326*   http://www.apache.org/licenses/LICENSE-2.0
65327*
65328* Unless required by applicable law or agreed to in writing,
65329* software distributed under the License is distributed on an
65330* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
65331* KIND, either express or implied.  See the License for the
65332* specific language governing permissions and limitations
65333* under the License.
65334*/
65335
65336    var CartesianAxisPointer = BaseAxisPointer.extend({
65337
65338        /**
65339         * @override
65340         */
65341        makeElOption: function (elOption, value, axisModel, axisPointerModel, api) {
65342            var axis = axisModel.axis;
65343            var grid = axis.grid;
65344            var axisPointerType = axisPointerModel.get('type');
65345            var otherExtent = getCartesian(grid, axis).getOtherAxis(axis).getGlobalExtent();
65346            var pixelValue = axis.toGlobalCoord(axis.dataToCoord(value, true));
65347
65348            if (axisPointerType && axisPointerType !== 'none') {
65349                var elStyle = buildElStyle(axisPointerModel);
65350                var pointerOption = pointerShapeBuilder[axisPointerType](
65351                    axis, pixelValue, otherExtent, elStyle
65352                );
65353                pointerOption.style = elStyle;
65354                elOption.graphicKey = pointerOption.type;
65355                elOption.pointer = pointerOption;
65356            }
65357
65358            var layoutInfo = layout$1(grid.model, axisModel);
65359            buildCartesianSingleLabelElOption(
65360                value, elOption, layoutInfo, axisModel, axisPointerModel, api
65361            );
65362        },
65363
65364        /**
65365         * @override
65366         */
65367        getHandleTransform: function (value, axisModel, axisPointerModel) {
65368            var layoutInfo = layout$1(axisModel.axis.grid.model, axisModel, {
65369                labelInside: false
65370            });
65371            layoutInfo.labelMargin = axisPointerModel.get('handle.margin');
65372            return {
65373                position: getTransformedPosition(axisModel.axis, value, layoutInfo),
65374                rotation: layoutInfo.rotation + (layoutInfo.labelDirection < 0 ? Math.PI : 0)
65375            };
65376        },
65377
65378        /**
65379         * @override
65380         */
65381        updateHandleTransform: function (transform, delta, axisModel, axisPointerModel) {
65382            var axis = axisModel.axis;
65383            var grid = axis.grid;
65384            var axisExtent = axis.getGlobalExtent(true);
65385            var otherExtent = getCartesian(grid, axis).getOtherAxis(axis).getGlobalExtent();
65386            var dimIndex = axis.dim === 'x' ? 0 : 1;
65387
65388            var currPosition = transform.position;
65389            currPosition[dimIndex] += delta[dimIndex];
65390            currPosition[dimIndex] = Math.min(axisExtent[1], currPosition[dimIndex]);
65391            currPosition[dimIndex] = Math.max(axisExtent[0], currPosition[dimIndex]);
65392
65393            var cursorOtherValue = (otherExtent[1] + otherExtent[0]) / 2;
65394            var cursorPoint = [cursorOtherValue, cursorOtherValue];
65395            cursorPoint[dimIndex] = currPosition[dimIndex];
65396
65397            // Make tooltip do not overlap axisPointer and in the middle of the grid.
65398            var tooltipOptions = [{verticalAlign: 'middle'}, {align: 'center'}];
65399
65400            return {
65401                position: currPosition,
65402                rotation: transform.rotation,
65403                cursorPoint: cursorPoint,
65404                tooltipOption: tooltipOptions[dimIndex]
65405            };
65406        }
65407
65408    });
65409
65410    function getCartesian(grid, axis) {
65411        var opt = {};
65412        opt[axis.dim + 'AxisIndex'] = axis.index;
65413        return grid.getCartesian(opt);
65414    }
65415
65416    var pointerShapeBuilder = {
65417
65418        line: function (axis, pixelValue, otherExtent, elStyle) {
65419            var targetShape = makeLineShape(
65420                [pixelValue, otherExtent[0]],
65421                [pixelValue, otherExtent[1]],
65422                getAxisDimIndex(axis)
65423            );
65424            subPixelOptimizeLine({
65425                shape: targetShape,
65426                style: elStyle
65427            });
65428            return {
65429                type: 'Line',
65430                shape: targetShape
65431            };
65432        },
65433
65434        shadow: function (axis, pixelValue, otherExtent, elStyle) {
65435            var bandWidth = Math.max(1, axis.getBandWidth());
65436            var span = otherExtent[1] - otherExtent[0];
65437            return {
65438                type: 'Rect',
65439                shape: makeRectShape(
65440                    [pixelValue - bandWidth / 2, otherExtent[0]],
65441                    [bandWidth, span],
65442                    getAxisDimIndex(axis)
65443                )
65444            };
65445        }
65446    };
65447
65448    function getAxisDimIndex(axis) {
65449        return axis.dim === 'x' ? 0 : 1;
65450    }
65451
65452    AxisView.registerAxisPointerClass('CartesianAxisPointer', CartesianAxisPointer);
65453
65454    /*
65455* Licensed to the Apache Software Foundation (ASF) under one
65456* or more contributor license agreements.  See the NOTICE file
65457* distributed with this work for additional information
65458* regarding copyright ownership.  The ASF licenses this file
65459* to you under the Apache License, Version 2.0 (the
65460* "License"); you may not use this file except in compliance
65461* with the License.  You may obtain a copy of the License at
65462*
65463*   http://www.apache.org/licenses/LICENSE-2.0
65464*
65465* Unless required by applicable law or agreed to in writing,
65466* software distributed under the License is distributed on an
65467* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
65468* KIND, either express or implied.  See the License for the
65469* specific language governing permissions and limitations
65470* under the License.
65471*/
65472
65473// CartesianAxisPointer is not supposed to be required here. But consider
65474// echarts.simple.js and online build tooltip, which only require gridSimple,
65475// CartesianAxisPointer should be able to required somewhere.
65476    registerPreprocessor(function (option) {
65477        // Always has a global axisPointerModel for default setting.
65478        if (option) {
65479            (!option.axisPointer || option.axisPointer.length === 0)
65480            && (option.axisPointer = {});
65481
65482            var link = option.axisPointer.link;
65483            // Normalize to array to avoid object mergin. But if link
65484            // is not set, remain null/undefined, otherwise it will
65485            // override existent link setting.
65486            if (link && !isArray(link)) {
65487                option.axisPointer.link = [link];
65488            }
65489        }
65490    });
65491
65492// This process should proformed after coordinate systems created
65493// and series data processed. So put it on statistic processing stage.
65494    registerProcessor(PRIORITY.PROCESSOR.STATISTIC, function (ecModel, api) {
65495        // Build axisPointerModel, mergin tooltip.axisPointer model for each axis.
65496        // allAxesInfo should be updated when setOption performed.
65497        ecModel.getComponent('axisPointer').coordSysAxesInfo
65498            = collect(ecModel, api);
65499    });
65500
65501// Broadcast to all views.
65502    registerAction({
65503        type: 'updateAxisPointer',
65504        event: 'updateAxisPointer',
65505        update: ':updateAxisPointer'
65506    }, axisTrigger);
65507
65508    /*
65509* Licensed to the Apache Software Foundation (ASF) under one
65510* or more contributor license agreements.  See the NOTICE file
65511* distributed with this work for additional information
65512* regarding copyright ownership.  The ASF licenses this file
65513* to you under the Apache License, Version 2.0 (the
65514* "License");
65514 you may not use this file except in compliance
65515* with the License.  You may obtain a copy of the License at
65516*
65517*   http://www.apache.org/licenses/LICENSE-2.0
65518*
65519* Unless required by applicable law or agreed to in writing,
65520* software distributed under the License is distributed on an
65521* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
65522* KIND, either express or implied.  See the License for the
65523* specific language governing permissions and limitations
65524* under the License.
65525*/
65526
65527    var XY = ['x', 'y'];
65528    var WH = ['width', 'height'];
65529
65530    var SingleAxisPointer = BaseAxisPointer.extend({
65531
65532        /**
65533         * @override
65534         */
65535        makeElOption: function (elOption, value, axisModel, axisPointerModel, api) {
65536            var axis = axisModel.axis;
65537            var coordSys = axis.coordinateSystem;
65538            var otherExtent = getGlobalExtent(coordSys, 1 - getPointDimIndex(axis));
65539            var pixelValue = coordSys.dataToPoint(value)[0];
65540
65541            var axisPointerType = axisPointerModel.get('type');
65542            if (axisPointerType && axisPointerType !== 'none') {
65543                var elStyle = buildElStyle(axisPointerModel);
65544                var pointerOption = pointerShapeBuilder$1[axisPointerType](
65545                    axis, pixelValue, otherExtent, elStyle
65546                );
65547                pointerOption.style = elStyle;
65548
65549                elOption.graphicKey = pointerOption.type;
65550                elOption.pointer = pointerOption;
65551            }
65552
65553            var layoutInfo = layout$2(axisModel);
65554            buildCartesianSingleLabelElOption(
65555                value, elOption, layoutInfo, axisModel, axisPointerModel, api
65556            );
65557        },
65558
65559        /**
65560         * @override
65561         */
65562        getHandleTransform: function (value, axisModel, axisPointerModel) {
65563            var layoutInfo = layout$2(axisModel, {labelInside: false});
65564            layoutInfo.labelMargin = axisPointerModel.get('handle.margin');
65565            return {
65566                position: getTransformedPosition(axisModel.axis, value, layoutInfo),
65567                rotation: layoutInfo.rotation + (layoutInfo.labelDirection < 0 ? Math.PI : 0)
65568            };
65569        },
65570
65571        /**
65572         * @override
65573         */
65574        updateHandleTransform: function (transform, delta, axisModel, axisPointerModel) {
65575            var axis = axisModel.axis;
65576            var coordSys = axis.coordinateSystem;
65577            var dimIndex = getPointDimIndex(axis);
65578            var axisExtent = getGlobalExtent(coordSys, dimIndex);
65579            var currPosition = transform.position;
65580            currPosition[dimIndex] += delta[dimIndex];
65581            currPosition[dimIndex] = Math.min(axisExtent[1], currPosition[dimIndex]);
65582            currPosition[dimIndex] = Math.max(axisExtent[0], currPosition[dimIndex]);
65583            var otherExtent = getGlobalExtent(coordSys, 1 - dimIndex);
65584            var cursorOtherValue = (otherExtent[1] + otherExtent[0]) / 2;
65585            var cursorPoint = [cursorOtherValue, cursorOtherValue];
65586            cursorPoint[dimIndex] = currPosition[dimIndex];
65587
65588            return {
65589                position: currPosition,
65590                rotation: transform.rotation,
65591                cursorPoint: cursorPoint,
65592                tooltipOption: {
65593                    verticalAlign: 'middle'
65594                }
65595            };
65596        }
65597    });
65598
65599    var pointerShapeBuilder$1 = {
65600
65601        line: function (axis, pixelValue, otherExtent, elStyle) {
65602            var targetShape = makeLineShape(
65603                [pixelValue, otherExtent[0]],
65604                [pixelValue, otherExtent[1]],
65605                getPointDimIndex(axis)
65606            );
65607            subPixelOptimizeLine({
65608                shape: targetShape,
65609                style: elStyle
65610            });
65611            return {
65612                type: 'Line',
65613                shape: targetShape
65614            };
65615        },
65616
65617        shadow: function (axis, pixelValue, otherExtent, elStyle) {
65618            var bandWidth = axis.getBandWidth();
65619            var span = otherExtent[1] - otherExtent[0];
65620            return {
65621                type: 'Rect',
65622                shape: makeRectShape(
65623                    [pixelValue - bandWidth / 2, otherExtent[0]],
65624                    [bandWidth, span],
65625                    getPointDimIndex(axis)
65626                )
65627            };
65628        }
65629    };
65630
65631    function getPointDimIndex(axis) {
65632        return axis.isHorizontal() ? 0 : 1;
65633    }
65634
65635    function getGlobalExtent(coordSys, dimIndex) {
65636        var rect = coordSys.getRect();
65637        return [rect[XY[dimIndex]], rect[XY[dimIndex]] + rect[WH[dimIndex]]];
65638    }
65639
65640    AxisView.registerAxisPointerClass('SingleAxisPointer', SingleAxisPointer);
65641
65642    /*
65643* Licensed to the Apache Software Foundation (ASF) under one
65644* or more contributor license agreements.  See the NOTICE file
65645* distributed with this work for additional information
65646* regarding copyright ownership.  The ASF licenses this file
65647* to you under the Apache License, Version 2.0 (the
65648* "License");
65648 you may not use this file except in compliance
65649* with the License.  You may obtain a copy of the License at
65650*
65651*   http://www.apache.org/licenses/LICENSE-2.0
65652*
65653* Unless required by applicable law or agreed to in writing,
65654* software distributed under the License is distributed on an
65655* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
65656* KIND, either express or implied.  See the License for the
65657* specific language governing permissions and limitations
65658* under the License.
65659*/
65660
65661    extendComponentView({
65662        type: 'single'
65663    });
65664
65665    /*
65666* Licensed to the Apache Software Foundation (ASF) under one
65667* or more contributor license agreements.  See the NOTICE file
65668* distributed with this work for additional information
65669* regarding copyright ownership.  The ASF licenses this file
65670* to you under the Apache License, Version 2.0 (the
65671* "License"); you may not use this file except in compliance
65672* with the License.  You may obtain a copy of the License at
65673*
65674*   http://www.apache.org/licenses/LICENSE-2.0
65675*
65676* Unless required by applicable law or agreed to in writing,
65677* software distributed under the License is distributed on an
65678* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
65679* KIND, either express or implied.  See the License for the
65680* specific language governing permissions and limitations
65681* under the License.
65682*/
65683
65684    /**
65685     * @file  Define the themeRiver view's series model
65686     * @author Deqing Li([email protected])
65687     */
65688
65689    var DATA_NAME_INDEX = 2;
65690
65691    var ThemeRiverSeries = SeriesModel.extend({
65692
65693        type: 'series.themeRiver',
65694
65695        dependencies: ['singleAxis'],
65696
65697        /**
65698         * @readOnly
65699         * @type {module:zrender/core/util#HashMap}
65700         */
65701        nameMap: null,
65702
65703        /**
65704         * @override
65705         */
65706        init: function (option) {
65707            ThemeRiverSeries.superApply(this, 'init', arguments);
65708
65709            // Put this function here is for the sake of consistency of code style.
65710            // Enable legend selection for each data item
65711            // Use a function instead of direct access because data reference may changed
65712            this.legendDataProvider = function () {
65713                return this.getRawData();
65714            };
65715        },
65716
65717        /**
65718         * If there is no value of a certain point in the time for some event,set it value to 0.
65719         *
65720         * @param {Array} data  initial data in the option
65721         * @return {Array}
65722         */
65723        fixData: function (data) {
65724            var rawDataLength = data.length;
65725
65726            // grouped data by name
65727            var dataByName = nest()
65728                .key(function (dataItem) {
65729                    return dataItem[2];
65730                })
65731                .entries(data);
65732
65733            // data group in each layer
65734            var layData = map(dataByName, function (d) {
65735                return {
65736                    name: d.key,
65737                    dataList: d.values
65738                };
65739            });
65740
65741            var layerNum = layData.length;
65742            var largestLayer = -1;
65743            var index = -1;
65744            for (var i = 0; i < layerNum; ++i) {
65745                var len = layData[i].dataList.length;
65746                if (len > largestLayer) {
65747                    largestLayer = len;
65748                    index = i;
65749                }
65750            }
65751
65752            for (var k = 0; k < layerNum; ++k) {
65753                if (k === index) {
65754                    continue;
65755                }
65756                var name = layData[k].name;
65757                for (var j = 0; j < largestLayer; ++j) {
65758                    var timeValue = layData[index].dataList[j][0];
65759                    var length = layData[k].dataList.length;
65760                    var keyIndex = -1;
65761                    for (var l = 0; l < length; ++l) {
65762                        var value = layData[k].dataList[l][0];
65763                        if (value === timeValue) {
65764                            keyIndex = l;
65765                            break;
65766                        }
65767                    }
65768                    if (keyIndex === -1) {
65769                        data[rawDataLength] = [];
65770                        data[rawDataLength][0] = timeValue;
65771                        data[rawDataLength][1] = 0;
65772                        data[rawDataLength][2] = name;
65773                        rawDataLength++;
65774
65775                    }
65776                }
65777            }
65778            return data;
65779        },
65780
65781        /**
65782         * @override
65783         * @param  {Object} option  the initial option that user gived
65784         * @param  {module:echarts/model/Model} ecModel  the model object for themeRiver option
65785         * @return {module:echarts/data/List}
65786         */
65787        getInitialData: function (option, ecModel) {
65788
65789            var singleAxisModel = ecModel.queryComponents({
65790                mainType: 'singleAxis',
65791                index: this.get('singleAxisIndex'),
65792                id: this.get('singleAxisId')
65793            })[0];
65794
65795            var axisType = singleAxisModel.get('type');
65796
65797            // filter the data item with the value of label is undefined
65798            var filterData = filter(option.data, function (dataItem) {
65799                return dataItem[2] !== undefined;
65800            });
65801
65802            // ??? TODO design a stage to transfer data for themeRiver and lines?
65803            var data = this.fixData(filterData || []);
65804            var nameList = [];
65805            var nameMap = this.nameMap = createHashMap();
65806            var count = 0;
65807
65808            for (var i = 0; i < data.length; ++i) {
65809                nameList.push(data[i][DATA_NAME_INDEX]);
65810                if (!nameMap.get(data[i][DATA_NAME_INDEX])) {
65811                    nameMap.set(data[i][DATA_NAME_INDEX], count);
65812                    count++;
65813                }
65814            }
65815
65816            var dimensionsInfo = createDimensions(data, {
65817                coordDimensions: ['single'],
65818                dimensionsDefine: [
65819                    {
65820                        name: 'time',
65821                        type: getDimensionTypeByAxis(axisType)
65822                    },
65823                    {
65824                        name: 'value',
65825                        type: 'float'
65826                    },
65827                    {
65828                        name: 'name',
65829                        type: 'ordinal'
65830                    }
65831                ],
65832                encodeDefine: {
65833                    single: 0,
65834                    value: 1,
65835                    itemName: 2
65836                }
65837            });
65838
65839            var list = new List(dimensionsInfo, this);
65840            list.initData(data);
65841
65842            return list;
65843        },
65844
65845        /**
65846         * The raw data is divided into multiple layers and each layer
65847         *     has same name.
65848         *
65849         * @return {Array.<Array.<number>>}
65850         */
65851        getLayerSeries: function () {
65852            var data = this.getData();
65853            var lenCount = data.count();
65854            var indexArr = [];
65855
65856            for (var i = 0; i < lenCount; ++i) {
65857                indexArr[i] = i;
65858            }
65859            // data group by name
65860            var dataByName = nest()
65861                .key(function (index) {
65862                    return data.get('name', index);
65863                })
65864                .entries(indexArr);
65865
65866            var layerSeries = map(dataByName, function (d) {
65867                return {
65868                    name: d.key,
65869                    indices: d.values
65870                };
65871            });
65872
65873            var timeDim = data.mapDimension('single');
65874
65875            for (var j = 0; j < layerSeries.length; ++j) {
65876                layerSeries[j].indices.sort(comparer);
65877            }
65878
65879            function comparer(index1, index2) {
65880                return data.get(timeDim, index1) - data.get(timeDim, index2);
65881            }
65882
65883            return layerSeries;
65884        },
65885
65886        /**
65887         * Get data indices for show tooltip content
65888         *
65889         * @param {Array.<string>|string} dim  single coordinate dimension
65890         * @param {number} value axis value
65891         * @param {module:echarts/coord/single/SingleAxis} baseAxis  single Axis used
65892         *     the themeRiver.
65893         * @return {Object} {dataIndices, nestestValue}
65894         */
65895        getAxisTooltipData: function (dim, value, baseAxis) {
65896            if (!isArray(dim)) {
65897                dim = dim ? [dim] : [];
65898            }
65899
65900            var data = this.getData();
65901            var layerSeries = this.getLayerSeries();
65902            var indices = [];
65903            var layerNum = layerSeries.length;
65904            var nestestValue;
65905
65906            for (var i = 0; i < layerNum; ++i) {
65907                var minDist = Number.MAX_VALUE;
65908                var nearestIdx = -1;
65909                var pointNum = layerSeries[i].indices.length;
65910                for (var j = 0; j < pointNum; ++j) {
65911                    var theValue = data.get(dim[0], layerSeries[i].indices[j]);
65912                    var dist = Math.abs(theValue - value);
65913                    if (dist <= minDist) {
65914                        nestestValue = theValue;
65915                        minDist = dist;
65916                        nearestIdx = layerSeries[i].indices[j];
65917                    }
65918                }
65919                indices.push(nearestIdx);
65920            }
65921
65922            return {dataIndices: indices, nestestValue: nestestValue};
65923        },
65924
65925        /**
65926         * @override
65927         * @param {number} dataIndex  index of data
65928         */
65929        formatTooltip: function (dataIndex) {
65930            var data = this.getData();
65931            var htmlName = data.getName(dataIndex);
65932            var htmlValue = data.get(data.mapDimension('value'), dataIndex);
65933            if (isNaN(htmlValue) || htmlValue == null) {
65934                htmlValue = '-';
65935            }
65936            return encodeHTML(htmlName + ' : ' + htmlValue);
65937        },
65938
65939        defaultOption: {
65940            zlevel: 0,
65941            z: 2,
65942
65943            coordinateSystem: 'singleAxis',
65944
65945            // gap in axis's orthogonal orientation
65946            boundaryGap: ['10%', '10%'],
65947
65948            // legendHoverLink: true,
65949
65950            singleAxisIndex: 0,
65951
65952            animationEasing: 'linear',
65953
65954            label: {
65955                margin: 4,
65956                show: true,
65957                position: 'left',
65958                color: '#000',
65959                fontSize: 11
65960            },
65961
65962            emphasis: {
65963                label: {
65964                    show: true
65965                }
65966            }
65967        }
65968    });
65969
65970    /*
65971* Licensed to the Apache Software Foundation (ASF) under one
65972* or more contributor license agreements.  See the NOTICE file
65973* distributed with this work for additional information
65974* regarding copyright ownership.  The ASF licenses this file
65975* to you under the Apache License, Version 2.0 (the
65976* "License"); you may not use this file except in compliance
65977* with the License.  You may obtain a copy of the License at
65978*
65979*   http://www.apache.org/licenses/LICENSE-2.0
65980*
65981* Unless required by applicable law or agreed to in writing,
65982* software distributed under the License is distributed on an
65983* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
65984* KIND, either express or implied.  See the License for the
65985* specific language governing permissions and limitations
65986* under the License.
65987*/
65988
65989    /**
65990     * @file  The file used to draw themeRiver view
65991     * @author  Deqing Li([email protected])
65992     */
65993
65994    extendChartView({
65995
65996        type: 'themeRiver',
65997
65998        init: function () {
65999            this._layers = [];
66000        },
66001
66002        render: function (seriesModel, ecModel, api) {
66003            var data = seriesModel.getData();
66004
66005            var group = this.group;
66006
66007            var layerSeries = seriesModel.getLayerSeries();
66008
66009            var layoutInfo = data.getLayout('layoutInfo');
66010            var rect = layoutInfo.rect;
66011            var boundaryGap = layoutInfo.boundaryGap;
66012
66013            group.attr('position', [0, rect.y + boundaryGap[0]]);
66014
66015            function keyGetter(item) {
66016                return item.name;
66017            }
66018            var dataDiffer = new DataDiffer(
66019                this._layersSeries || [], layerSeries,
66020                keyGetter, keyGetter
66021            );
66022
66023            var newLayersGroups = {};
66024
66025            dataDiffer
66026                .add(bind(process, this, 'add'))
66027                .update(bind(process, this, 'update'))
66028                .remove(bind(process, this, 'remove'))
66029                .execute();
66030
66031            function process(status, idx, oldIdx) {
66032                var oldLayersGroups = this._layers;
66033                if (status === 'remove') {
66034                    group.remove(oldLayersGroups[idx]);
66035                    return;
66036                }
66037                var points0 = [];
66038                var points1 = [];
66039                var color;
66040                var indices = layerSeries[idx].indices;
66041                for (var j = 0; j < indices.length; j++) {
66042                    var layout = data.getItemLayout(indices[j]);
66043                    var x = layout.x;
66044                    var y0 = layout.y0;
66045                    var y = layout.y;
66046
66047                    points0.push([x, y0]);
66048                    points1.push([x, y0 + y]);
66049
66050                    color = data.getItemVisual(indices[j], 'color');
66051                }
66052
66053                var polygon;
66054                var text;
66055                var textLayout = data.getItemLayout(indices[0]);
66056                var itemModel = data.getItemModel(indices[j - 1]);
66057                var labelModel = itemModel.getModel('label');
66058                var margin = labelModel.get('margin');
66059                if (status === 'add') {
66060                    var layerGroup = newLayersGroups[idx] = new Group();
66061                    polygon = new Polygon$1({
66062                        shape: {
66063                            points: points0,
66064                            stackedOnPoints: points1,
66065                            smooth: 0.4,
66066                            stackedOnSmooth: 0.4,
66067                            smoothConstraint: false
66068                        },
66069                        z2: 0
66070                    });
66071                    text = new Text({
66072                        style: {
66073                            x: textLayout.x - margin,
66074                            y: textLayout.y0 + textLayout.y / 2
66075                        }
66076                    });
66077                    layerGroup.add(polygon);
66078                    layerGroup.add(text);
66079                    group.add(layerGroup);
66080
66081                    polygon.setClipPath(createGridClipShape$3(polygon.getBoundingRect
66081(), seriesModel, function () {
66082                        polygon.removeClipPath();
66083                    }));
66084                }
66085                else {
66086                    var layerGroup = oldLayersGroups[oldIdx];
66087                    polygon = layerGroup.childAt(0);
66088                    text = layerGroup.childAt(1);
66089                    group.add(layerGroup);
66090
66091                    newLayersGroups[idx] = layerGroup;
66092
66093                    updateProps(polygon, {
66094                        shape: {
66095                            points: points0,
66096                            stackedOnPoints: points1
66097                        }
66098                    }, seriesModel);
66099
66100                    updateProps(text, {
66101                        style: {
66102                            x: textLayout.x - margin,
66103                            y: textLayout.y0 + textLayout.y / 2
66104                        }
66105                    }, seriesModel);
66106                }
66107
66108                var hoverItemStyleModel = itemModel.getModel('emphasis.itemStyle');
66109                var itemStyleModel = itemModel.getModel('itemStyle');
66110
66111                setTextStyle(text.style, labelModel, {
66112                    text: labelModel.get('show')
66113                        ? seriesModel.getFormattedLabel(indices[j - 1], 'normal')
66114                        || data.getName(indices[j - 1])
66115                        : null,
66116                    textVerticalAlign: 'middle'
66117                });
66118
66119                polygon.setStyle(extend({
66120                    fill: color
66121                }, itemStyleModel.getItemStyle(['color'])));
66122
66123                setHoverStyle(polygon, hoverItemStyleModel.getItemStyle());
66124            }
66125
66126            this._layersSeries = layerSeries;
66127            this._layers = newLayersGroups;
66128        },
66129
66130        dispose: function () {}
66131    });
66132
66133// add animation to the view
66134    function createGridClipShape$3(rect, seriesModel, cb) {
66135        var rectEl = new Rect({
66136            shape: {
66137                x: rect.x - 10,
66138                y: rect.y - 10,
66139                width: 0,
66140                height: rect.height + 20
66141            }
66142        });
66143        initProps(rectEl, {
66144            shape: {
66145                width: rect.width + 20,
66146                height: rect.height + 20
66147            }
66148        }, seriesModel, cb);
66149
66150        return rectEl;
66151    }
66152
66153    /*
66154* Licensed to the Apache Software Foundation (ASF) under one
66155* or more contributor license agreements.  See the NOTICE file
66156* distributed with this work for additional information
66157* regarding copyright ownership.  The ASF licenses this file
66158* to you under the Apache License, Version 2.0 (the
66159* "License"); you may not use this file except in compliance
66160* with the License.  You may obtain a copy of the License at
66161*
66162*   http://www.apache.org/licenses/LICENSE-2.0
66163*
66164* Unless required by applicable law or agreed to in writing,
66165* software distributed under the License is distributed on an
66166* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
66167* KIND, either express or implied.  See the License for the
66168* specific language governing permissions and limitations
66169* under the License.
66170*/
66171
66172    /**
66173     * @file  Using layout algorithm transform the raw data to layout information.
66174     * @author Deqing Li([email protected])
66175     */
66176
66177    var themeRiverLayout = function (ecModel, api) {
66178
66179        ecModel.eachSeriesByType('themeRiver', function (seriesModel) {
66180
66181            var data = seriesModel.getData();
66182
66183            var single = seriesModel.coordinateSystem;
66184
66185            var layoutInfo = {};
66186
66187            // use the axis boundingRect for view
66188            var rect = single.getRect();
66189
66190            layoutInfo.rect = rect;
66191
66192            var boundaryGap = seriesModel.get('boundaryGap');
66193
66194            var axis = single.getAxis();
66195
66196            layoutInfo.boundaryGap = boundaryGap;
66197
66198            if (axis.orient === 'horizontal') {
66199                boundaryGap[0] = parsePercent$1(boundaryGap[0], rect.height);
66200                boundaryGap[1] = parsePercent$1(boundaryGap[1], rect.height);
66201                var height = rect.height - boundaryGap[0] - boundaryGap[1];
66202                themeRiverLayout$1(data, seriesModel, height);
66203            }
66204            else {
66205                boundaryGap[0] = parsePercent$1(boundaryGap[0], rect.width);
66206                boundaryGap[1] = parsePercent$1(boundaryGap[1], rect.width);
66207                var width = rect.width - boundaryGap[0] - boundaryGap[1];
66208                themeRiverLayout$1(data, seriesModel, width);
66209            }
66210
66211            data.setLayout('layoutInfo', layoutInfo);
66212        });
66213    };
66214
66215    /**
66216     * The layout information about themeriver
66217     *
66218     * @param {module:echarts/data/List} data  data in the series
66219     * @param {module:echarts/model/Series} seriesModel  the model object of themeRiver series
66220     * @param {number} height  value used to compute every series height
66221     */
66222    function themeRiverLayout$1(data, seriesModel, height) {
66223        if (!data.count()) {
66224            return;
66225        }
66226        var coordSys = seriesModel.coordinateSystem;
66227        // the data in each layer are organized into a series.
66228        var layerSeries = seriesModel.getLayerSeries();
66229
66230        // the points in each layer.
66231        var timeDim = data.mapDimension('single');
66232        var valueDim = data.mapDimension('value');
66233        var layerPoints = map(layerSeries, function (singleLayer) {
66234            return map(singleLayer.indices, function (idx) {
66235                var pt = coordSys.dataToPoint(data.get(timeDim, idx));
66236                pt[1] = data.get(valueDim, idx);
66237                return pt;
66238            });
66239        });
66240
66241        var base = computeBaseline(layerPoints);
66242        var baseLine = base.y0;
66243        var ky = height / base.max;
66244
66245        // set layout information for each item.
66246        var n = layerSeries.length;
66247        var m = layerSeries[0].indices.length;
66248        var baseY0;
66249        for (var j = 0; j < m; ++j) {
66250            baseY0 = baseLine[j] * ky;
66251            data.setItemLayout(layerSeries[0].indices[j], {
66252                layerIndex: 0,
66253                x: layerPoints[0][j][0],
66254                y0: baseY0,
66255                y: layerPoints[0][j][1] * ky
66256            });
66257            for (var i = 1; i < n; ++i) {
66258                baseY0 += layerPoints[i - 1][j][1] * ky;
66259                data.setItemLayout(layerSeries[i].indices[j], {
66260                    layerIndex: i,
66261                    x: layerPoints[i][j][0],
66262                    y0: baseY0,
66263                    y: layerPoints[i][j][1] * ky
66264                });
66265            }
66266        }
66267    }
66268
66269    /**
66270     * Compute the baseLine of the rawdata
66271     * Inspired by Lee Byron's paper Stacked Graphs - Geometry & Aesthetics
66272     *
66273     * @param  {Array.<Array>} data  the points in each layer
66274     * @return {Object}
66275     */
66276    function computeBaseline(data) {
66277        var layerNum = data.length;
66278        var pointNum = data[0].length;
66279        var sums = [];
66280        var y0 = [];
66281        var max = 0;
66282        var temp;
66283        var base = {};
66284
66285        for (var i = 0; i < pointNum; ++i) {
66286            for (var j = 0, temp = 0; j < layerNum; ++j) {
66287                temp += data[j][i][1];
66288            }
66289            if (temp > max) {
66290                max = temp;
66291            }
66292            sums.push(temp);
66293        }
66294
66295        for (var k = 0; k < pointNum; ++k) {
66296            y0[k] = (max - sums[k]) / 2;
66297        }
66298        max = 0;
66299
66300        for (var l = 0; l < pointNum; ++l) {
66301            var sum = sums[l] + y0[l];
66302            if (sum > max) {
66303                max = sum;
66304            }
66305        }
66306        base.y0 = y0;
66307        base.max = max;
66308
66309        return base;
66310    }
66311
66312    /*
66313* Licensed to the Apache Software Foundation (ASF) under one
66314* or more contributor license agreements.  See the NOTICE file
66315* distributed with this work for additional information
66316* regarding copyright ownership.  The ASF licenses this file
66317* to you under the Apache License, Version 2.0 (the
66318* "License"); you may not use this file except in compliance
66319* with the License.  You may obtain a copy of the License at
66320*
66321*   http://www.apache.org/licenses/LICENSE-2.0
66322*
66323* Unless required by applicable law or agreed to in writing,
66324* software distributed under the License is distributed on an
66325* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
66326* KIND, either express or implied.  See the License for the
66327* specific language governing permissions and limitations
66328* under the License.
66329*/
66330
66331    /**
66332     * @file Visual encoding for themeRiver view
66333     * @author  Deqing Li([email protected])
66334     */
66335
66336    var themeRiverVisual = function (ecModel) {
66337        ecModel.eachSeriesByType('themeRiver', function (seriesModel) {
66338            var data = seriesModel.getData();
66339            var rawData = seriesModel.getRawData();
66340            var colorList = seriesModel.get('color');
66341            var idxMap = createHashMap();
66342
66343            data.each(function (idx) {
66344                idxMap.set(data.getRawIndex(idx), idx);
66345            });
66346
66347            rawData.each(function (rawIndex) {
66348                var name = rawData.getName(rawIndex);
66349                var color = colorList[(seriesModel.nameMap.get(name) - 1) % colorList.length];
66350
66351                rawData.setItemVisual(rawIndex, 'color', color);
66352
66353                var idx = idxMap.get(rawIndex);
66354
66355                if (idx != null) {
66356                    data.setItemVisual(idx, 'color', color);
66357                }
66358            });
66359        });
66360    };
66361
66362    /*
66363* Licensed to the Apache Software Foundation (ASF) under one
66364* or more contributor license agreements.  See the NOTICE file
66365* distributed with this work for additional information
66366* regarding copyright ownership.  The ASF licenses this file
66367* to you under the Apache License, Version 2.0 (the
66368* "License");
66368 you may not use this file except in compliance
66369* with the License.  You may obtain a copy of the License at
66370*
66371*   http://www.apache.org/licenses/LICENSE-2.0
66372*
66373* Unless required by applicable law or agreed to in writing,
66374* software distributed under the License is distributed on an
66375* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
66376* KIND, either express or implied.  See the License for the
66377* specific language governing permissions and limitations
66378* under the License.
66379*/
66380
66381    registerLayout(themeRiverLayout);
66382    registerVisual(themeRiverVisual);
66383    registerProcessor(dataFilter('themeRiver'));
66384
66385    /*
66386* Licensed to the Apache Software Foundation (ASF) under one
66387* or more contributor license agreements.  See the NOTICE file
66388* distributed with this work for additional information
66389* regarding copyright ownership.  The ASF licenses this file
66390* to you under the Apache License, Version 2.0 (the
66391* "License"); you may not use this file except in compliance
66392* with the License.  You may obtain a copy of the License at
66393*
66394*   http://www.apache.org/licenses/LICENSE-2.0
66395*
66396* Unless required by applicable law or agreed to in writing,
66397* software distributed under the License is distributed on an
66398* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
66399* KIND, either express or implied.  See the License for the
66400* specific language governing permissions and limitations
66401* under the License.
66402*/
66403
66404    SeriesModel.extend({
66405
66406        type: 'series.sunburst',
66407
66408        /**
66409         * @type {module:echarts/data/Tree~Node}
66410         */
66411        _viewRoot: null,
66412
66413        getInitialData: function (option, ecModel) {
66414            // Create a virtual root.
66415            var root = { name: option.name, children: option.data };
66416
66417            completeTreeValue$1(root);
66418
66419            var levels = option.levels || [];
66420
66421            // levels = option.levels = setDefault(levels, ecModel);
66422
66423            var treeOption = {};
66424
66425            treeOption.levels = levels;
66426
66427            // Make sure always a new tree is created when setOption,
66428            // in TreemapView, we check whether oldTree === newTree
66429            // to choose mappings approach among old shapes and new shapes.
66430            return Tree.createTree(root, this, treeOption).data;
66431        },
66432
66433        optionUpdated: function () {
66434            this.resetViewRoot();
66435        },
66436
66437        /*
66438     * @override
66439     */
66440        getDataParams: function (dataIndex) {
66441            var params = SeriesModel.prototype.getDataParams.apply(this, arguments);
66442
66443            var node = this.getData().tree.getNodeByDataIndex(dataIndex);
66444            params.treePathInfo = wrapTreePathInfo(node, this);
66445
66446            return params;
66447        },
66448
66449        defaultOption: {
66450            zlevel: 0,
66451            z: 2,
66452
66453            // 默认全局居中
66454            center: ['50%', '50%'],
66455            radius: [0, '75%'],
66456            // 默认顺时针
66457            clockwise: true,
66458            startAngle: 90,
66459            // 最小角度改为0
66460            minAngle: 0,
66461
66462            percentPrecision: 2,
66463
66464            // If still show when all data zero.
66465            stillShowZeroSum: true,
66466
66467            // Policy of highlighting pieces when hover on one
66468            // Valid values: 'none' (for not downplay others), 'descendant',
66469            // 'ancestor', 'self'
66470            highlightPolicy: 'descendant',
66471
66472            // 'rootToNode', 'link', or false
66473            nodeClick: 'rootToNode',
66474
66475            renderLabelForZeroData: false,
66476
66477            label: {
66478                // could be: 'radial', 'tangential', or 'none'
66479                rotate: 'radial',
66480                show: true,
66481                opacity: 1,
66482                // 'left' is for inner side of inside, and 'right' is for outter
66483                // side for inside
66484                align: 'center',
66485                position: 'inside',
66486                distance: 5,
66487                silent: true,
66488                emphasis: {}
66489            },
66490            itemStyle: {
66491                borderWidth: 1,
66492                borderColor: 'white',
66493                opacity: 1,
66494                emphasis: {},
66495                highlight: {
66496                    opacity: 1
66497                },
66498                downplay: {
66499                    opacity: 0.9
66500                }
66501            },
66502
66503            // Animation type canbe expansion, scale
66504            animationType: 'expansion',
66505            animationDuration: 1000,
66506            animationDurationUpdate: 500,
66507            animationEasing: 'cubicOut',
66508
66509            data: [],
66510
66511            levels: [],
66512
66513            /**
66514             * Sort order.
66515             *
66516             * Valid values: 'desc', 'asc', null, or callback function.
66517             * 'desc' and 'asc' for descend and ascendant order;
66518             * null for not sorting;
66519             * example of callback function:
66520             * function(nodeA, nodeB) {
66521         *     return nodeA.getValue() - nodeB.getValue();
66522         * }
66523             */
66524            sort: 'desc'
66525        },
66526
66527        getViewRoot: function () {
66528            return this._viewRoot;
66529        },
66530
66531        /**
66532         * @param {module:echarts/data/Tree~Node} [viewRoot]
66533         */
66534        resetViewRoot: function (viewRoot) {
66535            viewRoot
66536                ? (this._viewRoot = viewRoot)
66537                : (viewRoot = this._viewRoot);
66538
66539            var root = this.getRawData().tree.root;
66540
66541            if (!viewRoot
66542                || (viewRoot !== root && !root.contains(viewRoot))
66543            ) {
66544                this._viewRoot = root;
66545            }
66546        }
66547    });
66548
66549
66550
66551    /**
66552     * @param {Object} dataNode
66553     */
66554    function completeTreeValue$1(dataNode) {
66555        // Postorder travel tree.
66556        // If value of none-leaf node is not set,
66557        // calculate it by suming up the value of all children.
66558        var sum = 0;
66559
66560        each$1(dataNode.children, function (child) {
66561
66562            completeTreeValue$1(child);
66563
66564            var childValue = child.value;
66565            isArray(childValue) && (childValue = childValue[0]);
66566
66567            sum += childValue;
66568        });
66569
66570        var thisValue = dataNode.value;
66571        if (isArray(thisValue)) {
66572            thisValue = thisValue[0];
66573        }
66574
66575        if (thisValue == null || isNaN(thisValue)) {
66576            thisValue = sum;
66577        }
66578        // Value should not less than 0.
66579        if (thisValue < 0) {
66580            thisValue = 0;
66581        }
66582
66583        isArray(dataNode.value)
66584            ? (dataNode.value[0] = thisValue)
66585            : (dataNode.value = thisValue);
66586    }
66587
66588    /*
66589* Licensed to the Apache Software Foundation (ASF) under one
66590* or more contributor license agreements.  See the NOTICE file
66591* distributed with this work for additional information
66592* regarding copyright ownership.  The ASF licenses this file
66593* to you under the Apache License, Version 2.0 (the
66594* "License"); you may not use this file except in compliance
66595* with the License.  You may obtain a copy of the License at
66596*
66597*   http://www.apache.org/licenses/LICENSE-2.0
66598*
66599* Unless required by applicable law or agreed to in writing,
66600* software distributed under the License is distributed on an
66601* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
66602* KIND, either express or implied.  See the License for the
66603* specific language governing permissions and limitations
66604* under the License.
66605*/
66606
66607    var NodeHighlightPolicy = {
66608        NONE: 'none', // not downplay others
66609        DESCENDANT: 'descendant',
66610        ANCESTOR: 'ancestor',
66611        SELF: 'self'
66612    };
66613
66614    var DEFAULT_SECTOR_Z = 2;
66615    var DEFAULT_TEXT_Z = 4;
66616
66617    /**
66618     * Sunburstce of Sunburst including Sector, Label, LabelLine
66619     * @constructor
66620     * @extends {module:zrender/graphic/Group}
66621     */
66622    function SunburstPiece(node, seriesModel, ecModel) {
66623
66624        Group.call(this);
66625
66626        var sector = new Sector({
66627            z2: DEFAULT_SECTOR_Z
66628        });
66629        sector.seriesIndex = seriesModel.seriesIndex;
66630
66631        var text = new Text({
66632            z2: DEFAULT_TEXT_Z,
66633            silent: node.getModel('label').get('silent')
66634        });
66635        this.add(sector);
66636        this.add(text);
66637
66638        this.updateData(true, node, 'normal', seriesModel, ecModel);
66639
66640        // Hover to change label and labelLine
66641        function onEmphasis() {
66642            text.ignore = text.hoverIgnore;
66643        }
66644        function onNormal() {
66645            text.ignore = text.normalIgnore;
66646        }
66647        this.on('emphasis', onEmphasis)
66648            .on('normal', onNormal)
66649            .on('mouseover', onEmphasis)
66650            .on('mouseout', onNormal);
66651    }
66652
66653    var SunburstPieceProto = SunburstPiece.prototype;
66654
66655    SunburstPieceProto.updateData = function (
66656        firstCreate,
66657        node,
66658        state,
66659        seriesModel,
66660        ecModel
66661    ) {
66662        this.node = node;
66663        node.piece = this;
66664
66665        seriesModel = seriesModel || this._seriesModel;
66666        ecModel = ecModel || this._ecModel;
66667
66668        var sector = this.childAt(0);
66669        sector.dataIndex = node.dataIndex;
66670
66671        var itemModel = node.getModel();
66672        var layout = node.getLayout();
66673        if (!layout) {
66674            console.log(node.getLayout());
66675        }
66676        var sectorShape = extend({}, layout);
66677        sectorShape.label = null;
66678
66679        var visualColor = getNodeColor(node, seriesModel, ecModel);
66680
66681        var normalStyle = itemModel.getModel('itemStyle').getItemStyle();
66682        var style;
66683        if (state === 'normal') {
66684            style = normalStyle;
66685        }
66686        else {
66687            var stateStyle = itemModel.getModel(state + '.itemStyle')
66688                .getItemStyle();
66689            style = merge(stateStyle, normalStyle);
66690        }
66691        style = defaults(
66692            {
66693                lineJoin: 'bevel',
66694                fill: style.fill || visualColor
66695            },
66696            style
66697        );
66698
66699        if (firstCreate) {
66700            sector.setShape(sectorShape);
66701            sector.shape.r = layout.r0;
66702            updateProps(
66703                sector,
66704                {
66705                    shape: {
66706                        r: layout.r
66707                    }
66708                },
66709                seriesModel,
66710                node.dataIndex
66711            );
66712            sector.useStyle(style);
66713        }
66714        else if (typeof style.fill === 'object' && style.fill.type
66715            || typeof sector.style.fill === 'object' && sector.style.fill.type
66716        ) {
66717            // Disable animation for gradient since no interpolation method
66718            // is supported for gradient
66719            updateProps(sector, {
66720                shape: sectorShape
66721            }, seriesModel);
66722            sector.useStyle(style);
66723        }
66724        else {
66725            updateProps(sector, {
66726                shape: sectorShape,
66727                style: style
66728            }, seriesModel);
66729        }
66730
66731        this._updateLabel(seriesModel, visualColor, state);
66732
66733        var cursorStyle = itemModel.getShallow('cursor');
66734        cursorStyle && sector.attr('cursor', cursorStyle);
66735
66736        if (firstCreate) {
66737            var highlightPolicy = seriesModel.getShallow('highlightPolicy');
66738            this._initEvents(sector, node, seriesModel, highlightPolicy);
66739        }
66740
66741        this._seriesModel = seriesModel || this._seriesModel;
66742        this._ecModel = ecModel || this._ecModel;
66743    };
66744
66745    SunburstPieceProto.onEmphasis = function (highlightPolicy) {
66746        var that = this;
66747        this.node.hostTree.root.eachNode(function (n) {
66748            if (n.piece) {
66749                if (that.node === n) {
66750                    n.piece.updateData(false, n, 'emphasis');
66751                }
66752                else if (isNodeHighlighted(n, that.node, highlightPolicy)) {
66753                    n.piece.childAt(0).trigger('highlight');
66754                }
66755                else if (highlightPolicy !== NodeHighlightPolicy.NONE) {
66756                    n.piece.childAt(0).trigger('downplay');
66757                }
66758            }
66759        });
66760    };
66761
66762    SunburstPieceProto.onNormal = function () {
66763        this.node.hostTree.root.eachNode(function (n) {
66764            if (n.piece) {
66765                n.piece.updateData(false, n, 'normal');
66766            }
66767        });
66768    };
66769
66770    SunburstPieceProto.onHighlight = function () {
66771        this.updateData(false, this.node, 'highlight');
66772    };
66773
66774    SunburstPieceProto.onDownplay = function () {
66775        this.updateData(false, this.node, 'downplay');
66776    };
66777
66778    SunburstPieceProto._updateLabel = function (seriesModel, visualColor, state) {
66779        var itemModel = this.node.getModel();
66780        var normalModel = itemModel.getModel('label');
66781        var labelModel = state === 'normal' || state === 'emphasis'
66782            ? normalModel
66783            : itemModel.getModel(state + '.label');
66784        var labelHoverModel = itemModel.getModel('emphasis.label');
66785
66786        var text = retrieve(
66787            seriesModel.getFormattedLabel(
66788                this.node.dataIndex, 'normal', null, null, 'label'
66789            ),
66790            this.node.name
66791        );
66792        if (getLabelAttr('show') === false) {
66793            text = '';
66794        }
66795
66796        var layout = this.node.getLayout();
66797        var labelMinAngle = labelModel.get('minAngle');
66798        if (labelMinAngle == null) {
66799            labelMinAngle = normalModel.get('minAngle');
66800        }
66801        labelMinAngle = labelMinAngle / 180 * Math.PI;
66802        var angle = layout.endAngle - layout.startAngle;
66803        if (labelMinAngle != null && Math.abs(angle) < labelMinAngle) {
66804            // Not displaying text when angle is too small
66805            text = '';
66806        }
66807
66808        var label = this.childAt(1);
66809
66810        setLabelStyle(
66811            label.style, label.hoverStyle || {}, normalModel, labelHoverModel,
66812            {
66813                defaultText: labelModel.getShallow('show') ? text : null,
66814                autoColor: visualColor,
66815                useInsideStyle: true
66816            }
66817        );
66818
66819        var midAngle = (layout.startAngle + layout.endAngle) / 2;
66820        var dx = Math.cos(midAngle);
66821        var dy = Math.sin(midAngle);
66822
66823        var r;
66824        var labelPosition = getLabelAttr('position');
66825        var labelPadding = getLabelAttr('distance') || 0;
66826        var textAlign = getLabelAttr('align');
66827        if (labelPosition === 'outside') {
66828            r = layout.r + labelPadding;
66829            textAlign = midAngle > Math.PI / 2 ? 'right' : 'left';
66830        }
66831        else {
66832            if (!textAlign || textAlign === 'center') {
66833                r = (layout.r + layout.r0) / 2;
66834                textAlign = 'center';
66835            }
66836            else if (textAlign === 'left') {
66837                r = layout.r0 + labelPadding;
66838                if (midAngle > Math.PI / 2) {
66839                    textAlign = 'right';
66840                }
66841            }
66842            else if (textAlign === 'right') {
66843                r = layout.r - labelPadding;
66844                if (midAngle > Math.PI / 2) {
66845                    textAlign = 'left';
66846                }
66847            }
66848        }
66849
66850        label.attr('style', {
66851            text: text,
66852            textAlign: textAlign,
66853            textVerticalAlign: getLabelAttr('verticalAlign') || 'middle',
66854            opacity: getLabelAttr('opacity')
66855        });
66856
66857        var textX = r * dx + layout.cx;
66858        var textY = r * dy + layout.cy;
66859        label.attr('position', [textX, textY]);
66860
66861        var rotateType = getLabelAttr('rotate');
66862        var rotate = 0;
66863        if (rotateType === 'radial') {
66864            rotate = -midAngle;
66865            if (rotate < -Math.PI / 2) {
66866                rotate += Math.PI;
66867            }
66868        }
66869        else if (rotateType === 'tangential') {
66870            rotate = Math.PI / 2 - midAngle;
66871            if (rotate > Math.PI / 2) {
66872                rotate -= Math.PI;
66873            }
66874            else if (rotate < -Math.PI / 2) {
66875                rotate += Math.PI;
66876            }
66877        } else if (typeof rotateType === 'number') {
66878            rotate = rotateType * Math.PI / 180;
66879        }
66880        label.attr('rotation', rotate);
66881
66882        function getLabelAttr(name) {
66883            var stateAttr = labelModel.get(name);
66884            if (stateAttr == null) {
66885                return normalModel.get(name);
66886            }
66887            else {
66888                return stateAttr;
66889            }
66890        }
66891    };
66892
66893    SunburstPieceProto._initEvents = function (
66894        sector,
66895        node,
66896        seriesModel,
66897        highlightPolicy
66898    ) {
66899        sector.off('mouseover').off('mouseout').off('emphasis').off('normal');
66900
66901        var that = this;
66902        var onEmphasis = function () {
66903            that.onEmphasis(highlightPolicy);
66904        };
66905        var onNormal = function () {
66906            that.onNormal();
66907        };
66908        var onDownplay = function () {
66909            that.onDownplay();
66910        };
66911        var onHighlight = function () {
66912            that.onHighlight();
66913        };
66914
66915        if (seriesModel.isAnimationEnabled()) {
66916            sector
66917                .on('mouseover', onEmphasis)
66918                .on('mouseout', onNormal)
66919                .on('emphasis', onEmphasis)
66920                .on('normal', onNormal)
66921                .on('downplay', onDownplay)
66922                .on('highlight', onHighlight);
66923        }
66924    };
66925
66926    inherits(SunburstPiece, Group);
66927
66928    /**
66929     * Get node color
66930     *
66931     * @param {TreeNode} node the node to get color
66932     * @param {module:echarts/model/Series} seriesModel series
66933     * @param {module:echarts/model/Global} ecModel echarts defaults
66934     */
66935    function getNodeColor(node, seriesModel, ecModel) {
66936        // Color from visualMap
66937        var visualColor = node.getVisual('color');
66938        var visualMetaList = node.getVisual('visualMeta');
66939        if (!visualMetaList || visualMetaList.length === 0) {
66940            // Use first-generation color if has no visualMap
66941            visualColor = null;
66942        }
66943
66944        // Self color or level color
66945        var color = node.getModel('itemStyle').get('color');
66946        if (color) {
66947            return color;
66948        }
66949        else if (visualColor) {
66950            // Color mapping
66951            return visualColor;
66952        }
66953        else if (node.depth === 0) {
66954            // Virtual root node
66955            return ecModel.option.color[0];
66956        }
66957        else {
66958            // First-generation color
66959            var length = ecModel.option.color.length;
66960            color = ecModel.option.color[getRootId(node) % length];
66961        }
66962        return color;
66963    }
66964
66965    /**
66966     * Get index of root in sorted order
66967     *
66968     * @param {TreeNode} node current node
66969     * @return {number} index in root
66970     */
66971    function getRootId(node) {
66972        var ancestor = node;
66973        while (ancestor.depth > 1) {
66974            ancestor = ancestor.parentNode;
66975        }
66976
66977        var virtualRoot = node.getAncestors()[0];
66978        return indexOf(virtualRoot.children, ancestor);
66979    }
66980
66981    function isNodeHighlighted(node, activeNode, policy) {
66982        if (policy === NodeHighlightPolicy.NONE) {
66983            return false;
66984        }
66985        else if (policy === NodeHighlightPolicy.SELF) {
66986            return node === activeNode;
66987        }
66988        else if (policy === NodeHighlightPolicy.ANCESTOR) {
66989            return node === activeNode || node.isAncestorOf(activeNode);
66990        }
66991        else {
66992            return node === activeNode || node.isDescendantOf(activeNode);
66993        }
66994    }
66995
66996    /*
66997* Licensed to the Apache Software Foundation (ASF) under one
66998* or more contributor license agreements.  See the NOTICE file
66999* distributed with this work for additional information
67000* regarding copyright ownership.  The ASF licenses this file
67001* to you under the Apache License, Version 2.0 (the
67002* "License"); you may not use this file except in compliance
67003* with the License.  You may obtain a copy of the License at
67004*
67005*   http://www.apache.org/licenses/LICENSE-2.0
67006*
67007* Unless required by applicable law or agreed to in writing,
67008* software distributed under the License is distributed on an
67009* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
67010* KIND, either express or implied.  See the License for the
67011* specific language governing permissions and limitations
67012* under the License.
67013*/
67014
67015    var ROOT_TO_NODE_ACTION = 'sunburstRootToNode';
67016
67017    var SunburstView = Chart.extend({
67018
67019        type: 'sunburst',
67020
67021        init: function () {
67022        },
67023
67024        render: function (seriesModel, ecModel, api, payload) {
67025            var that = this;
67026
67027            this.seriesModel = seriesModel;
67028            this.api = api;
67029            this.ecModel = ecModel;
67030
67031            var data = seriesModel.getData();
67032            var virtualRoot = data.tree.root;
67033
67034            var newRoot = seriesModel.getViewRoot();
67035
67036            var group = this.group;
67037
67038            var renderLabelForZeroData = seriesModel.get('renderLabelForZeroData');
67039
67040            var newChildren = [];
67041            newRoot.eachNode(function (node) {
67042                newChildren.push(node);
67043            });
67044            var oldChildren = this._oldChildren || [];
67045
67046            dualTravel(newChildren, oldChildren);
67047
67048            renderRollUp(virtualRoot, newRoot);
67049
67050            if (payload && payload.highlight && payload.highlight.piece) {
67051                var highlightPolicy = seriesModel.getShallow('highlightPolicy');
67052                payload.highlight.piece.onEmphasis(highlightPolicy);
67053            }
67054            else if (payload && payload.unhighlight) {
67055                var piece = this.virtualPiece;
67056                if (!piece && virtualRoot.children.length) {
67057                    piece = virtualRoot.children[0].piece;
67058                }
67059                if (piece) {
67060                    piece.onNormal();
67061                }
67062            }
67063
67064            this._initEvents();
67065
67066            this._oldChildren = newChildren;
67067
67068            function dualTravel(newChildren, oldChildren) {
67069                if (newChildren.length === 0 && oldChildren.length === 0) {
67070                    return;
67071                }
67072
67073                new DataDiffer(oldChildren, newChildren, getKey, getKey)
67074                    .add(processNode)
67075                    .update(processNode)
67076                    .remove(curry(processNode, null))
67077                    .execute();
67078
67079                function getKey(node) {
67080                    return node.getId();
67081                }
67082
67083                function processNode(newId, oldId) {
67084                    var newNode = newId == null ? null : newChildren[newId];
67085                    var oldNode = oldId == null ? null : oldChildren[oldId];
67086
67087                    doRenderNode(newNode, oldNode);
67088                }
67089            }
67090
67091            function doRenderNode(newNode, oldNode) {
67092                if (!renderLabelForZeroData && newNode && !newNode.getValue()) {
67093                    // Not render data with value 0
67094                    newNode = null;
67095                }
67096
67097                if (newNode !== virtualRoot && oldNode !== virtualRoot) {
67098                    if (oldNode && oldNode.piece) {
67099                        if (newNode) {
67100                            // Update
67101                            oldNode.piece.updateData(
67102                                false, newNode, 'normal', seriesModel, ecModel);
67103
67104                            // For tooltip
67105                            data.setItemGraphicEl(newNode.dataIndex, oldNode.piece);
67106                        }
67107                        else {
67108                            // Remove
67109                            removeNode(oldNode);
67110                        }
67111                    }
67112                    else if (newNode) {
67113                        // Add
67114                        var piece = new SunburstPiece(
67115                            newNode,
67116                            seriesModel,
67117                            ecModel
67118                        );
67119                        group.add(piece);
67120
67121                        // For tooltip
67122                        data.setItemGraphicEl(newNode.dataIndex, piece);
67123                    }
67124                }
67125            }
67126
67127            function removeNode(node) {
67128                if (!node) {
67129                    return;
67130                }
67131
67132                if (node.piece) {
67133                    group.remove(node.piece);
67134                    node.piece = null;
67135                }
67136            }
67137
67138            function renderRollUp(virtualRoot, viewRoot) {
67139                if (viewRoot.depth > 0) {
67140                    // Render
67141                    if (that.virtualPiece) {
67142                        // Update
67143                        that.virtualPiece.updateData(
67144                            false, virtualRoot, 'normal', seriesModel, ecModel);
67145                    }
67146                    else {
67147                        // Add
67148                        that.virtualPiece = new SunburstPiece(
67149                            virtualRoot,
67150                            seriesModel,
67151                            ecModel
67152                        );
67153                        group.add(that.virtualPiece);
67154                    }
67155
67156                    if (viewRoot.piece._onclickEvent) {
67157                        viewRoot.piece.off('click', viewRoot.piece._onclickEvent);
67158                    }
67159                    var event = function (e) {
67160                        that._rootToNode(viewRoot.parentNode);
67161                    };
67162                    viewRoot.piece._onclickEvent = event;
67163                    that.virtualPiece.on('click', event);
67164                }
67165                else if (that.virtualPiece) {
67166                    // Remove
67167                    group.remove(that.virtualPiece);
67168                    that.virtualPiece = null;
67169                }
67170            }
67171        },
67172
67173        dispose: function () {
67174        },
67175
67176        /**
67177         * @private
67178         */
67179        _initEvents: function () {
67180            var that = this;
67181
67182            var event = function (e) {
67183                var targetFound = false;
67184                var viewRoot = that.seriesModel.getViewRoot();
67185                viewRoot.eachNode(function (node) {
67186                    if (!targetFound
67187                        && node.piece && node.piece.childAt(0) === e.target
67188                    ) {
67189                        var nodeClick = node.getModel().get('nodeClick');
67190                        if (nodeClick === 'rootToNode') {
67191                            that._rootToNode(node);
67192                        }
67193                        else if (nodeClick === 'link') {
67194                            var itemModel = node.getModel();
67195                            var link = itemModel.get('link');
67196                            if (link) {
67197                                var linkTarget = itemModel.get('target', true)
67198                                    || '_blank';
67199                                window.open(link, linkTarget);
67200                            }
67201                        }
67202                        targetFound = true;
67203                    }
67204                });
67205            };
67206
67207            if (this.group._onclickEvent) {
67208                this.group.off('click', this.group._onclickEvent);
67209            }
67210            this.group.on('click', event);
67211            this.group._onclickEvent = event;
67212        },
67213
67214        /**
67215         * @private
67216         */
67217        _rootToNode: function (node) {
67218            if (node !== this.seriesModel.getViewRoot()) {
67219                this.api.dispatchAction({
67220                    type: ROOT_TO_NODE_ACTION,
67221                    from: this.uid,
67222                    seriesId: this.seriesModel.id,
67223                    targetNode: node
67224                });
67225            }
67226        },
67227
67228        /**
67229         * @implement
67230         */
67231        containPoint: function (point, seriesModel) {
67232            var treeRoot = seriesModel.getData();
67233            var itemLayout = treeRoot.getItemLayout(0);
67234            if (itemLayout) {
67235                var dx = point[0] - itemLayout.cx;
67236                var dy = point[1] - itemLayout.cy;
67237                var radius = Math.sqrt(dx * dx + dy * dy);
67238                return radius <= itemLayout.r && radius >= itemLayout.r0;
67239            }
67240        }
67241
67242    });
67243
67244    /*
67245* Licensed to the Apache Software Foundation (ASF) under one
67246* or more contributor license agreements.  See the NOTICE file
67247* distributed with this work for additional information
67248* regarding copyright ownership.  The ASF licenses this file
67249* to you under the Apache License, Version 2.0 (the
67250* "License"); you may not use this file except in compliance
67251* with the License.  You may obtain a copy of the License at
67252*
67253*   http://www.apache.org/licenses/LICENSE-2.0
67254*
67255* Unless required by applicable law or agreed to in writing,
67256* software distributed under the License is distributed on an
67257* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
67258* KIND, either express or implied.  See the License for the
67259* specific language governing permissions and limitations
67260* under the License.
67261*/
67262
67263    /**
67264     * @file Sunburst action
67265     */
67266
67267    var ROOT_TO_NODE_ACTION$1 = 'sunburstRootToNode';
67268
67269    registerAction(
67270        {type: ROOT_TO_NODE_ACTION$1, update: 'updateView'},
67271        function (payload, ecModel) {
67272
67273            ecModel.eachComponent(
67274                {mainType: 'series', subType: 'sunburst', query: payload},
67275                handleRootToNode
67276            );
67277
67278            function handleRootToNode(model, index) {
67279                var targetInfo = retrieveTargetInfo(payload, [ROOT_TO_NODE_ACTION$1], model);
67280
67281                if (targetInfo) {
67282                    var originViewRoot = model.getViewRoot();
67283                    if (originViewRoot) {
67284                        payload.direction = aboveViewRoot(originViewRoot, targetInfo.node)
67285                            ? 'rollUp' : 'drillDown';
67286                    }
67287                    model.resetViewRoot(targetInfo.node);
67288                }
67289            }
67290        }
67291    );
67292
67293
67294    var HIGHLIGHT_ACTION = 'sunburstHighlight';
67295
67296    registerAction(
67297        {type: HIGHLIGHT_ACTION, update: 'updateView'},
67298        function (payload, ecModel) {
67299
67300            ecModel.eachComponent(
67301                {mainType: 'series', subType: 'sunburst', query: payload},
67302                handleHighlight
67303            );
67304
67305            function handleHighlight(model, index) {
67306                var targetInfo = retrieveTargetInfo(payload, [HIGHLIGHT_ACTION], model);
67307
67308                if (targetInfo) {
67309                    payload.highlight = targetInfo.node;
67310                }
67311            }
67312        }
67313    );
67314
67315
67316    var UNHIGHLIGHT_ACTION = 'sunburstUnhighlight';
67317
67318    registerAction(
67319        {type: UNHIGHLIGHT_ACTION, update: 'updateView'},
67320        function (payload, ecModel) {
67321
67322            ecModel.eachComponent(
67323                {mainType: 'series', subType: 'sunburst', query: payload},
67324                handleUnhighlight
67325            );
67326
67327            function handleUnhighlight(model, index) {
67328                payload.unhighlight = true;
67329            }
67330        }
67331    );
67332
67333    /*
67334* Licensed to the Apache Software Foundation (ASF) under one
67335* or more contributor license agreements.  See the NOTICE file
67336* distributed with this work for additional information
67337* regarding copyright ownership.  The ASF licenses this file
67338* to you under the Apache License, Version 2.0 (the
67339* "License"); you may not use this file except in compliance
67340* with the License.  You may obtain a copy of the License at
67341*
67342*   http://www.apache.org/licenses/LICENSE-2.0
67343*
67344* Unless required by applicable law or agreed to in writing,
67345* software distributed under the License is distributed on an
67346* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
67347* KIND, either express or implied.  See the License for the
67348* specific language governing permissions and limitations
67349* under the License.
67350*/
67351
67352
67353    var RADIAN$1 = Math.PI / 180;
67354
67355    var sunburstLayout = function (seriesType, ecModel, api, payload) {
67356        ecModel.eachSeriesByType(seriesType, function (seriesModel) {
67357            var center = seriesModel.get('center');
67358            var radius = seriesModel.get('radius');
67359
67360            if (!isArray(radius)) {
67361                radius = [0, radius];
67362            }
67363            if (!isArray(center)) {
67364                center = [center, center];
67365            }
67366
67367            var width = api.getWidth();
67368            var height = api.getHeight();
67369            var size = Math.min(width, height);
67370            var cx = parsePercent$1(center[0], width);
67371            var cy = parsePercent$1(center[1], height);
67372            var r0 = parsePercent$1(radius[0], size / 2);
67373            var r = parsePercent$1(radius[1], size / 2);
67374
67375            var startAngle = -seriesModel.get('startAngle') * RADIAN$1;
67376            var minAngle = seriesModel.get('minAngle') * RADIAN$1;
67377
67378            var virtualRoot = seriesModel.getData().tree.root;
67379            var treeRoot = seriesModel.getViewRoot();
67380            var rootDepth = treeRoot.depth;
67381
67382            var sort = seriesModel.get('sort');
67383            if (sort != null) {
67384                initChildren$1(treeRoot, sort);
67385            }
67386
67387            var validDataCount = 0;
67388            each$1(treeRoot.children, function (child) {
67389                !isNaN(child.getValue()) && validDataCount++;
67390            });
67391
67392            var sum = treeRoot.getValue();
67393            // Sum may be 0
67394            var unitRadian = Math.PI / (sum || validDataCount) * 2;
67395
67396            var renderRollupNode = treeRoot.depth > 0;
67397            var levels = treeRoot.height - (renderRollupNode ? -1 : 1);
67398            var rPerLevel = (r - r0) / (levels || 1);
67399
67400            var clockwise = seriesModel.get('clockwise');
67401
67402            var stillShowZeroSum = seriesModel.get('stillShowZeroSum');
67403
67404            // In the case some sector angle is smaller than minAngle
67405            var dir = clockwise ? 1 : -1;
67406
67407            /**
67408             * Render a tree
67409             * @return increased angle
67410             */
67411            var renderNode = function (node, startAngle) {
67412                if (!node) {
67413                    return;
67414                }
67415
67416                var endAngle = startAngle;
67417
67418                // Render self
67419                if (node !== virtualRoot) {
67420                    // Tree node is virtual, so it doesn't need to be drawn
67421                    var value = node.getValue();
67422
67423                    var angle = (sum === 0 && stillShowZeroSum)
67424                        ? unitRadian : (value * unitRadian);
67425                    if (angle < minAngle) {
67426                        angle = minAngle;
67427
67428                    }
67429                    else {
67430
67431                    }
67432
67433                    endAngle = startAngle + dir * angle;
67434
67435                    var depth = node.depth - rootDepth
67436                        - (renderRollupNode ? -1 : 1);
67437                    var rStart = r0 + rPerLevel * depth;
67438                    var rEnd = r0 + rPerLevel * (depth + 1);
67439
67440                    var itemModel = node.getModel();
67441                    if (itemModel.get('r0') != null) {
67442                        rStart = parsePercent$1(itemModel.get('r0'), size / 2);
67443                    }
67444                    if (itemModel.get('r') != null) {
67445                        rEnd = parsePercent$1(itemModel.get('r'), size / 2);
67446                    }
67447
67448                    node.setLayout({
67449                        angle: angle,
67450                        startAngle: startAngle,
67451                        endAngle: endAngle,
67452                        clockwise: clockwise,
67453                        cx: cx,
67454                        cy: cy,
67455                        r0: rStart,
67456                        r: rEnd
67457                    });
67458                }
67459
67460                // Render children
67461                if (node.children && node.children.length) {
67462                    // currentAngle = startAngle;
67463                    var siblingAngle = 0;
67464                    each$1(node.children, function (node) {
67465                        siblingAngle += renderNode(node, startAngle + siblingAngle);
67466                    });
67467                }
67468
67469                return endAngle - startAngle;
67470            };
67471
67472            // Virtual root node for roll up
67473            if (renderRollupNode) {
67474                var rStart = r0;
67475                var rEnd = r0 + rPerLevel;
67476
67477                var angle = Math.PI * 2;
67478                virtualRoot.setLayout({
67479                    angle: angle,
67480                    startAngle: startAngle,
67481                    endAngle: startAngle + angle,
67482                    clockwise: clockwise,
67483                    cx: cx,
67484                    cy: cy,
67485                    r0: rStart,
67486                    r: rEnd
67487                });
67488            }
67489
67490            renderNode(treeRoot, startAngle);
67491        });
67492    };
67493
67494    /**
67495     * Init node children by order and update visual
67496     *
67497     * @param {TreeNode} node  root node
67498     * @param {boolean}  isAsc if is in ascendant order
67499     */
67500    function initChildren$1(node, isAsc) {
67501        var children = node.children || [];
67502
67503        node.children = sort$2(children, isAsc);
67504
67505        // Init children recursively
67506        if (children.length) {
67507            each$1(node.children, function (child) {
67508                initChildren$1(child, isAsc);
67509            });
67510        }
67511    }
67512
67513    /**
67514     * Sort children nodes
67515     *
67516     * @param {TreeNode[]}               children children of node to be sorted
67517     * @param {string | function | null} sort sort method
67518     *                                   See SunburstSeries.js for details.
67519     */
67520    function sort$2(children, sortOrder) {
67521        if (typeof sortOrder === 'function') {
67522            return children.sort(sortOrder);
67523        }
67524        else {
67525            var isAsc = sortOrder === 'asc';
67526            return children.sort(function (a, b) {
67527                var diff = (a.getValue() - b.getValue()) * (isAsc ? 1 : -1);
67528                return diff === 0
67529                    ? (a.dataIndex - b.dataIndex) * (isAsc ? -1 : 1)
67530                    : diff;
67531            });
67532        }
67533    }
67534
67535    /*
67536* Licensed to the Apache Software Foundation (ASF) under one
67537* or more contributor license agreements.  See the NOTICE file
67538* distributed with this work for additional information
67539* regarding copyright ownership.  The ASF licenses this file
67540* to you under the Apache License, Version 2.0 (the
67541* "License"); you may not use this file except in compliance
67542* with the License.  You may obtain a copy of the License at
67543*
67544*   http://www.apache.org/licenses/LICENSE-2.0
67545*
67546* Unless required by applicable law or agreed to in writing,
67547* software distributed under the License is distributed on an
67548* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
67549* KIND, either express or implied.  See the License for the
67550* specific language governing permissions and limitations
67551* under the License.
67552*/
67553
67554    registerVisual(curry(dataColor, 'sunburst'));
67555    registerLayout(curry(sunburstLayout, 'sunburst'));
67556    registerProcessor(curry(dataFilter, 'sunburst'));
67557
67558    /*
67559* Licensed to the Apache Software Foundation (ASF) under one
67560* or more contributor license agreements.  See the NOTICE file
67561* distributed with this work for additional information
67562* regarding copyright ownership.  The ASF licenses this file
67563* to you under the Apache License, Version 2.0 (the
67564* "License");
67564 you may not use this file except in compliance
67565* with the License.  You may obtain a copy of the License at
67566*
67567*   http://www.apache.org/licenses/LICENSE-2.0
67568*
67569* Unless required by applicable law or agreed to in writing,
67570* software distributed under the License is distributed on an
67571* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
67572* KIND, either express or implied.  See the License for the
67573* specific language governing permissions and limitations
67574* under the License.
67575*/
67576
67577    function dataToCoordSize(dataSize, dataItem) {
67578        // dataItem is necessary in log axis.
67579        dataItem = dataItem || [0, 0];
67580        return map(['x', 'y'], function (dim, dimIdx) {
67581            var axis = this.getAxis(dim);
67582            var val = dataItem[dimIdx];
67583            var halfSize = dataSize[dimIdx] / 2;
67584            return axis.type === 'category'
67585                ? axis.getBandWidth()
67586                : Math.abs(axis.dataToCoord(val - halfSize) - axis.dataToCoord(val + halfSize));
67587        }, this);
67588    }
67589
67590    var prepareCartesian2d = function (coordSys) {
67591        var rect = coordSys.grid.getRect();
67592        return {
67593            coordSys: {
67594                // The name exposed to user is always 'cartesian2d' but not 'grid'.
67595                type: 'cartesian2d',
67596                x: rect.x,
67597                y: rect.y,
67598                width: rect.width,
67599                height: rect.height
67600            },
67601            api: {
67602                coord: function (data) {
67603                    // do not provide "out" param
67604                    return coordSys.dataToPoint(data);
67605                },
67606                size: bind(dataToCoordSize, coordSys)
67607            }
67608        };
67609    };
67610
67611    /*
67612* Licensed to the Apache Software Foundation (ASF) under one
67613* or more contributor license agreements.  See the NOTICE file
67614* distributed with this work for additional information
67615* regarding copyright ownership.  The ASF licenses this file
67616* to you under the Apache License, Version 2.0 (the
67617* "License"); you may not use this file except in compliance
67618* with the License.  You may obtain a copy of the License at
67619*
67620*   http://www.apache.org/licenses/LICENSE-2.0
67621*
67622* Unless required by applicable law or agreed to in writing,
67623* software distributed under the License is distributed on an
67624* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
67625* KIND, either express or implied.  See the License for the
67626* specific language governing permissions and limitations
67627* under the License.
67628*/
67629
67630    function dataToCoordSize$1(dataSize, dataItem) {
67631        dataItem = dataItem || [0, 0];
67632        return map([0, 1], function (dimIdx) {
67633            var val = dataItem[dimIdx];
67634            var halfSize = dataSize[dimIdx] / 2;
67635            var p1 = [];
67636            var p2 = [];
67637            p1[dimIdx] = val - halfSize;
67638            p2[dimIdx] = val + halfSize;
67639            p1[1 - dimIdx] = p2[1 - dimIdx] = dataItem[1 - dimIdx];
67640            return Math.abs(this.dataToPoint(p1)[dimIdx] - this.dataToPoint(p2)[dimIdx]);
67641        }, this);
67642    }
67643
67644    var prepareGeo = function (coordSys) {
67645        var rect = coordSys.getBoundingRect();
67646        return {
67647            coordSys: {
67648                type: 'geo',
67649                x: rect.x,
67650                y: rect.y,
67651                width: rect.width,
67652                height: rect.height
67653            },
67654            api: {
67655                coord: function (data) {
67656                    // do not provide "out" and noRoam param,
67657                    // Compatible with this usage:
67658                    // echarts.util.map(item.points, api.coord)
67659                    return coordSys.dataToPoint(data);
67660                },
67661                size: bind(dataToCoordSize$1, coordSys)
67662            }
67663        };
67664    };
67665
67666    /*
67667* Licensed to the Apache Software Foundation (ASF) under one
67668* or more contributor license agreements.  See the NOTICE file
67669* distributed with this work for additional information
67670* regarding copyright ownership.  The ASF licenses this file
67671* to you under the Apache License, Version 2.0 (the
67672* "License");
67672 you may not use this file except in compliance
67673* with the License.  You may obtain a copy of the License at
67674*
67675*   http://www.apache.org/licenses/LICENSE-2.0
67676*
67677* Unless required by applicable law or agreed to in writing,
67678* software distributed under the License is distributed on an
67679* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
67680* KIND, either express or implied.  See the License for the
67681* specific language governing permissions and limitations
67682* under the License.
67683*/
67684
67685    function dataToCoordSize$2(dataSize, dataItem) {
67686        // dataItem is necessary in log axis.
67687        var axis = this.getAxis();
67688        var val = dataItem instanceof Array ? dataItem[0] : dataItem;
67689        var halfSize = (dataSize instanceof Array ? dataSize[0] : dataSize) / 2;
67690        return axis.type === 'category'
67691            ? axis.getBandWidth()
67692            : Math.abs(axis.dataToCoord(val - halfSize) - axis.dataToCoord(val + halfSize));
67693    }
67694
67695    var prepareSingleAxis = function (coordSys) {
67696        var rect = coordSys.getRect();
67697
67698        return {
67699            coordSys: {
67700                type: 'singleAxis',
67701                x: rect.x,
67702                y: rect.y,
67703                width: rect.width,
67704                height: rect.height
67705            },
67706            api: {
67707                coord: function (val) {
67708                    // do not provide "out" param
67709                    return coordSys.dataToPoint(val);
67710                },
67711                size: bind(dataToCoordSize$2, coordSys)
67712            }
67713        };
67714    };
67715
67716    /*
67717* Licensed to the Apache Software Foundation (ASF) under one
67718* or more contributor license agreements.  See the NOTICE file
67719* distributed with this work for additional information
67720* regarding copyright ownership.  The ASF licenses this file
67721* to you under the Apache License, Version 2.0 (the
67722* "License"); you may not use this file except in compliance
67723* with the License.  You may obtain a copy of the License at
67724*
67725*   http://www.apache.org/licenses/LICENSE-2.0
67726*
67727* Unless required by applicable law or agreed to in writing,
67728* software distributed under the License is distributed on an
67729* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
67730* KIND, either express or implied.  See the License for the
67731* specific language governing permissions and limitations
67732* under the License.
67733*/
67734
67735    function dataToCoordSize$3(dataSize, dataItem) {
67736        // dataItem is necessary in log axis.
67737        return map(['Radius', 'Angle'], function (dim, dimIdx) {
67738            var axis = this['get' + dim + 'Axis']();
67739            var val = dataItem[dimIdx];
67740            var halfSize = dataSize[dimIdx] / 2;
67741            var method = 'dataTo' + dim;
67742
67743            var result = axis.type === 'category'
67744                ? axis.getBandWidth()
67745                : Math.abs(axis[method](val - halfSize) - axis[method](val + halfSize));
67746
67747            if (dim === 'Angle') {
67748                result = result * Math.PI / 180;
67749            }
67750
67751            return result;
67752
67753        }, this);
67754    }
67755
67756    var preparePolar = function (coordSys) {
67757        var radiusAxis = coordSys.getRadiusAxis();
67758        var angleAxis = coordSys.getAngleAxis();
67759        var radius = radiusAxis.getExtent();
67760        radius[0] > radius[1] && radius.reverse();
67761
67762        return {
67763            coordSys: {
67764                type: 'polar',
67765                cx: coordSys.cx,
67766                cy: coordSys.cy,
67767                r: radius[1],
67768                r0: radius[0]
67769            },
67770            api: {
67771                coord: bind(function (data) {
67772                    var radius = radiusAxis.dataToRadius(data[0]);
67773                    var angle = angleAxis.dataToAngle(data[1]);
67774                    var coord = coordSys.coordToPoint([radius, angle]);
67775                    coord.push(radius, angle * Math.PI / 180);
67776                    return coord;
67777                }),
67778                size: bind(dataToCoordSize$3, coordSys)
67779            }
67780        };
67781    };
67782
67783    /*
67784* Licensed to the Apache Software Foundation (ASF) under one
67785* or more contributor license agreements.  See the NOTICE file
67786* distributed with this work for additional information
67787* regarding copyright ownership.  The ASF licenses this file
67788* to you under the Apache License, Version 2.0 (the
67789* "License");
67789 you may not use this file except in compliance
67790* with the License.  You may obtain a copy of the License at
67791*
67792*   http://www.apache.org/licenses/LICENSE-2.0
67793*
67794* Unless required by applicable law or agreed to in writing,
67795* software distributed under the License is distributed on an
67796* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
67797* KIND, either express or implied.  See the License for the
67798* specific language governing permissions and limitations
67799* under the License.
67800*/
67801
67802    var prepareCalendar = function (coordSys) {
67803        var rect = coordSys.getRect();
67804        var rangeInfo = coordSys.getRangeInfo();
67805
67806        return {
67807            coordSys: {
67808                type: 'calendar',
67809                x: rect.x,
67810                y: rect.y,
67811                width: rect.width,
67812                height: rect.height,
67813                cellWidth: coordSys.getCellWidth(),
67814                cellHeight: coordSys.getCellHeight(),
67815                rangeInfo: {
67816                    start: rangeInfo.start,
67817                    end: rangeInfo.end,
67818                    weeks: rangeInfo.weeks,
67819                    dayCount: rangeInfo.allDay
67820                }
67821            },
67822            api: {
67823                coord: function (data, clamp) {
67824                    return coordSys.dataToPoint(data, clamp);
67825                }
67826            }
67827        };
67828    };
67829
67830    /*
67831* Licensed to the Apache Software Foundation (ASF) under one
67832* or more contributor license agreements.  See the NOTICE file
67833* distributed with this work for additional information
67834* regarding copyright ownership.  The ASF licenses this file
67835* to you under the Apache License, Version 2.0 (the
67836* "License"); you may not use this file except in compliance
67837* with the License.  You may obtain a copy of the License at
67838*
67839*   http://www.apache.org/licenses/LICENSE-2.0
67840*
67841* Unless required by applicable law or agreed to in writing,
67842* software distributed under the License is distributed on an
67843* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
67844* KIND, either express or implied.  See the License for the
67845* specific language governing permissions and limitations
67846* under the License.
67847*/
67848
67849    var ITEM_STYLE_NORMAL_PATH = ['itemStyle'];
67850    var ITEM_STYLE_EMPHASIS_PATH = ['emphasis', 'itemStyle'];
67851    var LABEL_NORMAL = ['label'];
67852    var LABEL_EMPHASIS = ['emphasis', 'label'];
67853// Use prefix to avoid index to be the same as el.name,
67854// which will cause weird udpate animation.
67855    var GROUP_DIFF_PREFIX = 'e\0\0';
67856
67857    /**
67858     * To reduce total package size of each coordinate systems, the modules `prepareCustom`
67859     * of each coordinate systems are not required by each coordinate systems directly, but
67860     * required by the module `custom`.
67861     *
67862     * prepareInfoForCustomSeries {Function}: optional
67863     *     @return {Object} {coordSys: {...}, api: {
67864 *         coord: function (data, clamp) {}, // return point in global.
67865 *         size: function (dataSize, dataItem) {} // return size of each axis in coordSys.
67866 *     }}
67867     */
67868    var prepareCustoms = {
67869        cartesian2d: prepareCartesian2d,
67870        geo: prepareGeo,
67871        singleAxis: prepareSingleAxis,
67872        polar: preparePolar,
67873        calendar: prepareCalendar
67874    };
67875
67876// ------
67877// Model
67878// ------
67879
67880    extendSeriesModel({
67881
67882        type: 'series.custom',
67883
67884        dependencies: ['grid', 'polar', 'geo', 'singleAxis', 'calendar'],
67885
67886        defaultOption: {
67887            coordinateSystem: 'cartesian2d', // Can be set as 'none'
67888            zlevel: 0,
67889            z: 2,
67890            legendHoverLink: true
67891
67892            // Cartesian coordinate system
67893            // xAxisIndex: 0,
67894            // yAxisIndex: 0,
67895
67896            // Polar coordinate system
67897            // polarIndex: 0,
67898
67899            // Geo coordinate system
67900            // geoIndex: 0,
67901
67902            // label: {}
67903            // itemStyle: {}
67904        },
67905
67906        getInitialData: function (option, ecModel) {
67907            return createListFromArray(this.getSource(), this);
67908        }
67909    });
67910
67911// -----
67912// View
67913// -----
67914
67915    extendChartView({
67916
67917        type: 'custom',
67918
67919        /**
67920         * @private
67921         * @type {module:echarts/data/List}
67922         */
67923        _data: null,
67924
67925        /**
67926         * @override
67927         */
67928        render: function (customSeries, ecModel, api) {
67929            var oldData = this._data;
67930            var data = customSeries.getData();
67931            var group = this.group;
67932            var renderItem = makeRenderItem(customSeries, data, ecModel, api);
67933
67934            this.group.removeAll();
67935
67936            data.diff(oldData)
67937                .add(function (newIdx) {
67938                    createOrUpdate$1(
67939                        null, newIdx, renderItem(newIdx), customSeries, group, data
67940                    );
67941                })
67942                .update(function (newIdx, oldIdx) {
67943                    var el = oldData.getItemGraphicEl(oldIdx);
67944                    createOrUpdate$1(
67945                        el, newIdx, renderItem(newIdx), customSeries, group, data
67946                    );
67947                })
67948                .remove(function (oldIdx) {
67949                    var el = oldData.getItemGraphicEl(oldIdx);
67950                    el && group.remove(el);
67951                })
67952                .execute();
67953
67954            this._data = data;
67955        },
67956
67957        incrementalPrepareRender: function (customSeries, ecModel, api) {
67958            this.group.removeAll();
67959            this._data = null;
67960        },
67961
67962        incrementalRender: function (params, customSeries, ecModel, api) {
67963            var data = customSeries.getData();
67964            var renderItem = makeRenderItem(customSeries, data, ecModel, api);
67965            function setIncrementalAndHoverLayer(el) {
67966                if (!el.isGroup) {
67967                    el.incremental = true;
67968                    el.useHoverLayer = true;
67969                }
67970            }
67971            for (var idx = params.start; idx < params.end; idx++) {
67972                var el = createOrUpdate$1(null, idx, renderItem(idx), customSeries, this.group, data);
67973                el.traverse(setIncrementalAndHoverLayer);
67974            }
67975        },
67976
67977        /**
67978         * @override
67979         */
67980        dispose: noop
67981    });
67982
67983
67984    function createEl(elOption) {
67985        var graphicType = elOption.type;
67986        var el;
67987
67988        if (graphicType === 'path') {
67989            var shape = elOption.shape;
67990            el = makePath(
67991                shape.pathData,
67992                null,
67993                {
67994                    x: shape.x || 0,
67995                    y: shape.y || 0,
67996                    width: shape.width || 0,
67997                    height: shape.height || 0
67998                },
67999                'center'
68000            );
68001            el.__customPathData = elOption.pathData;
68002        }
68003        else if (graphicType === 'image') {
68004            el = new ZImage({
68005            });
68006            el.__customImagePath = elOption.style.image;
68007        }
68008        else if (graphicType === 'text') {
68009            el = new Text({
68010            });
68011            el.__customText = elOption.style.text;
68012        }
68013        else {
68014            var Clz = graphic[graphicType.charAt(0).toUpperCase() + graphicType.slice(1)];
68015
68016            if (__DEV__) {
68017                assert$1(Clz, 'graphic type "' + graphicType + '" can not be found.');
68018            }
68019
68020            el = new Clz();
68021        }
68022
68023        el.__customGraphicType = graphicType;
68024        el.name = elOption.name;
68025
68026        return el;
68027    }
68028
68029    function updateEl(el, dataIndex, elOption, animatableModel, data, isInit) {
68030        var targetProps = {};
68031        var elOptionStyle = elOption.style || {};
68032
68033        elOption.shape && (targetProps.shape = clone(elOption.shape));
68034        elOption.position && (targetProps.position = elOption.position.slice());
68035        elOption.scale && (targetProps.scale = elOption.scale.slice());
68036        elOption.origin && (targetProps.origin = elOption.origin.slice());
68037        elOption.rotation && (targetProps.rotation = elOption.rotation);
68038
68039        if (el.type === 'image' && elOption.style) {
68040            var targetStyle = targetProps.style = {};
68041            each$1(['x', 'y', 'width', 'height'], function (prop) {
68042                prepareStyleTransition(prop, targetStyle, elOptionStyle, el.style, isInit);
68043            });
68044        }
68045
68046        if (el.type === 'text' && elOption.style) {
68047            var targetStyle = targetProps.style = {};
68048            each$1(['x', 'y'], function (prop) {
68049                prepareStyleTransition(prop, targetStyle, elOptionStyle, el.style, isInit);
68050            });
68051            // Compatible with previous: both support
68052            // textFill and fill, textStroke and stroke in 'text' element.
68053            !elOptionStyle.hasOwnProperty('textFill') && elOptionStyle.fill && (
68054                elOptionStyle.textFill = elOptionStyle.fill
68055            );
68056            !elOptionStyle.hasOwnProperty('textStroke') && elOptionStyle.stroke && (
68057                elOptionStyle.textStroke = elOptionStyle.stroke
68058            );
68059        }
68060
68061        if (el.type !== 'group') {
68062            el.useStyle(elOptionStyle);
68063
68064            // Init animation.
68065            if (isInit) {
68066                el.style.opacity = 0;
68067                var targetOpacity = elOptionStyle.opacity;
68068                targetOpacity == null && (targetOpacity = 1);
68069                initProps(el, {style: {opacity: targetOpacity}}, animatableModel, dataIndex);
68070            }
68071        }
68072
68073        if (isInit) {
68074            el.attr(targetProps);
68075        }
68076        else {
68077            updateProps(el, targetProps, animatableModel, dataIndex);
68078        }
68079
68080        // z2 must not be null/undefined, otherwise sort error may occur.
68081        el.attr({z2: elOption.z2 || 0, silent: elOption.silent});
68082
68083        elOption.styleEmphasis !== false && setHoverStyle(el, elOption.styleEmphasis);
68084    }
68085
68086    function prepareStyleTransition(prop, targetStyle, elOptionStyle, oldElStyle, isInit) {
68087        if (elOptionStyle[prop] != null && !isInit) {
68088            targetStyle[prop] = elOptionStyle[prop];
68089            elOptionStyle[prop] = oldElStyle[prop];
68090        }
68091    }
68092
68093    function makeRenderItem(customSeries, data, ecModel, api) {
68094        var renderItem = customSeries.get('renderItem');
68095        var coordSys = customSeries.coordinateSystem;
68096        var prepareResult = {};
68097
68098        if (coordSys) {
68099            if (__DEV__) {
68100                assert$1(renderItem, 'series.render is required.');
68101                assert$1(
68102                    coordSys.prepareCustoms || prepareCustoms[coordSys.type],
68103                    'This coordSys does not support custom series.'
68104                );
68105            }
68106
68107            prepareResult = coordSys.prepareCustoms
68108                ? coordSys.prepareCustoms()
68109                : prepareCustoms[coordSys.type](coordSys);
68110        }
68111
68112        var userAPI = defaults({
68113            getWidth: api.getWidth,
68114            getHeight: api.getHeight,
68115            getZr: api.getZr,
68116            getDevicePixelRatio: api.getDevicePixelRatio,
68117            value: value,
68118            style: style,
68119            styleEmphasis: styleEmphasis,
68120            visual: visual,
68121            barLayout: barLayout,
68122            currentSeriesIndices: currentSeriesIndices,
68123            font: font
68124        }, prepareResult.api || {});
68125
68126        var userParams = {
68127            context: {},
68128            seriesId: customSeries.id,
68129            seriesName: customSeries.name,
68130            seriesIndex: customSeries.seriesIndex,
68131            coordSys: prepareResult.coordSys,
68132            dataInsideLength: data.count(),
68133            encode: wrapEncodeDef(customSeries.getData())
68134        };
68135
68136        // Do not support call `api` asynchronously without dataIndexInside input.
68137        var currDataIndexInside;
68138        var currDirty = true;
68139        var currItemModel;
68140        var currLabelNormalModel;
68141        var currLabelEmphasisModel;
68142        var currVisualColor;
68143
68144        return function (dataIndexInside) {
68145            currDataIndexInside = dataIndexInside;
68146            currDirty = true;
68147            return renderItem && renderItem(
68148                defaults({
68149                    dataIndexInside: dataIndexInside,
68150                    dataIndex: data.getRawIndex(dataIndexInside)
68151                }, userParams),
68152                userAPI
68153            ) || {};
68154        };
68155
68156        // Do not update cache until api called.
68157        function updateCache(dataIndexInside) {
68158            dataIndexInside == null && (dataIndexInside = currDataIndexInside);
68159            if (currDirty) {
68160                currItemModel = data.getItemModel(dataIndexInside);
68161                currLabelNormalModel = currItemModel.getModel(LABEL_NORMAL);
68162                currLabelEmphasisModel = currItemModel.getModel(LABEL_EMPHASIS);
68163                currVisualColor = data.getItemVisual(dataIndexInside, 'color');
68164
68165                currDirty = false;
68166            }
68167        }
68168
68169        /**
68170         * @public
68171         * @param {number|string} dim
68172         * @param {number} [dataIndexInside=currDataIndexInside]
68173         * @return {number|string} value
68174         */
68175        function value(dim, dataIndexInside) {
68176            dataIndexInside == null && (dataIndexInside = currDataIndexInside);
68177            return data.get(data.getDimension(dim || 0), dataIndexInside);
68178        }
68179
68180        /**
68181         * By default, `visual` is applied to style (to support visualMap).
68182         * `visual.color` is applied at `fill`. If user want apply visual.color on `stroke`,
68183         * it can be implemented as:
68184         * `api.style({stroke: api.visual('color'), fill: null})`;
68185         * @public
68186         * @param {Object} [extra]
68187         * @param {number} [dataIndexInside=currDataIndexInside]
68188         */
68189        function style(extra, dataIndexInside) {
68190            dataIndexInside == null && (dataIndexInside = currDataIndexInside);
68191            updateCache(dataIndexInside);
68192
68193            var itemStyle = currItemModel.getModel(ITEM_STYLE_NORMAL_PATH).getItemStyle();
68194
68195            currVisualColor != null && (itemStyle.fill = currVisualColor);
68196            var opacity = data.getItemVisual(dataIndexInside, 'opacity');
68197            opacity != null && (itemStyle.opacity = opacity);
68198
68199            setTextStyle(itemStyle, currLabelNormalModel, null, {
68200                autoColor: currVisualColor,
68201                isRectText: true
68202            });
68203
68204            itemStyle.text = currLabelNormalModel.getShallow('show')
68205                ? retrieve2(
68206                    customSeries.getFormattedLabel(dataIndexInside, 'normal'),
68207                    getDefaultLabel(data, dataIndexInside)
68208                )
68209                : null;
68210
68211            extra && extend(itemStyle, extra);
68212            return itemStyle;
68213        }
68214
68215        /**
68216         * @public
68217         * @param {Object} [extra]
68218         * @param {number} [dataIndexInside=currDataIndexInside]
68219         */
68220        function styleEmphasis(extra, dataIndexInside) {
68221            dataIndexInside == null && (dataIndexInside = currDataIndexInside);
68222            updateCache(dataIndexInside);
68223
68224            var itemStyle = currItemModel.getModel(ITEM_STYLE_EMPHASIS_PATH).getItemStyle();
68225
68226            setTextStyle(itemStyle, currLabelEmphasisModel, null, {
68227                isRectText: true
68228            }, true);
68229
68230            itemStyle.text = currLabelEmphasisModel.getShallow('show')
68231                ? retrieve3(
68232                    customSeries.getFormattedLabel(dataIndexInside, 'emphasis'),
68233                    customSeries.getFormattedLabel(dataIndexInside, 'normal'),
68234                    getDefaultLabel(data, dataIndexInside)
68235                )
68236                : null;
68237
68238            extra && extend(itemStyle, extra);
68239            return itemStyle;
68240        }
68241
68242        /**
68243         * @public
68244         * @param {string} visualType
68245         * @param {number} [dataIndexInside=currDataIndexInside]
68246         */
68247        function visual(visualType, dataIndexInside) {
68248            dataIndexInside == null && (dataIndexInside = currDataIndexInside);
68249            return data.getItemVisual(dataIndexInside, visualType);
68250        }
68251
68252        /**
68253         * @public
68254         * @param {number} opt.count Positive interger.
68255         * @param {number} [opt.barWidth]
68256         * @param {number} [opt.barMaxWidth]
68257         * @param {number} [opt.barGap]
68258         * @param {number} [opt.barCategoryGap]
68259         * @return {Object} {width, offset, offsetCenter} is not support, return undefined.
68260         */
68261        function barLayout(opt) {
68262            if (coordSys.getBaseAxis) {
68263                var baseAxis = coordSys.getBaseAxis();
68264                return getLayoutOnAxis(defaults({axis: baseAxis}, opt), api);
68265            }
68266        }
68267
68268        /**
68269         * @public
68270         * @return {Array.<number>}
68271         */
68272        function currentSeriesIndices() {
68273            return ecModel.getCurrentSeriesIndices();
68274        }
68275
68276        /**
68277         * @public
68278         * @param {Object} opt
68279         * @param {string} [opt.fontStyle]
68280         * @param {number} [opt.fontWeight]
68281         * @param {number} [opt.fontSize]
68282         * @param {string} [opt.fontFamily]
68283         * @return {string} font string
68284         */
68285        function font(opt) {
68286            return getFont(opt, ecModel);
68287        }
68288    }
68289
68290    function wrapEncodeDef(data) {
68291        var encodeDef = {};
68292        each$1(data.dimensions, function (dimName, dataDimIndex) {
68293            var dimInfo = data.getDimensionInfo(dimName);
68294            if (!dimInfo.isExtraCoord) {
68295                var coordDim = dimInfo.coordDim;
68296                var dataDims = encodeDef[coordDim] = encodeDef[coordDim] || [];
68297                dataDims[dimInfo.coordDimIndex] = dataDimIndex;
68298            }
68299        });
68300        return encodeDef;
68301    }
68302
68303    function createOrUpdate$1(el, dataIndex, elOption, animatableModel, group, data) {
68304        el = doCreateOrUpdate(el, dataIndex, elOption, animatableModel, group, data);
68305        el && data.setItemGraphicEl(dataIndex, el);
68306
68307        return el;
68308    }
68309
68310    function doCreateOrUpdate(el, dataIndex, elOption, animatableModel, group, data) {
68311        var elOptionType = elOption.type;
68312        if (el
68313            && elOptionType !== el.__customGraphicType
68314            && (elOptionType !== 'path' || elOption.pathData !== el.__customPathData)
68315            && (elOptionType !== 'image' || elOption.style.image !== el.__customImagePath)
68316            && (elOptionType !== 'text' || elOption.style.text !== el.__customText)
68317        ) {
68318            group.remove(el);
68319            el = null;
68320        }
68321
68322        // `elOption.type` is undefined when `renderItem` returns nothing.
68323        if (elOptionType == null) {
68324            return;
68325        }
68326
68327        var isInit = !el;
68328        !el && (el = createEl(elOption));
68329        updateEl(el, dataIndex, elOption, animatableModel, data, isInit);
68330
68331        if (elOptionType === 'group') {
68332            var oldChildren = el.children() || [];
68333            var newChildren = elOption.children || [];
68334
68335            if (elOption.diffChildrenByName) {
68336                // lower performance.
68337                diffGroupChildren({
68338                    oldChildren: oldChildren,
68339                    newChildren: newChildren,
68340                    dataIndex: dataIndex,
68341                    animatableModel: animatableModel,
68342                    group: el,
68343                    data: data
68344                });
68345            }
68346            else {
68347                // better performance.
68348                var index = 0;
68349                for (; index < newChildren.length; index++) {
68350                    doCreateOrUpdate(
68351                        el.childAt(index),
68352                        dataIndex,
68353                        newChildren[index],
68354                        animatableModel,
68355                        el,
68356                        data
68357                    );
68358                }
68359                for (; index < oldChildren.length; index++) {
68360                    oldChildren[index] && el.remove(oldChildren[index]);
68361                }
68362            }
68363        }
68364
68365        group.add(el);
68366
68367        return el;
68368    }
68369
68370    function diffGroupChildren(context) {
68371        (new DataDiffer(
68372            context.oldChildren,
68373            context.newChildren,
68374            getKey,
68375            getKey,
68376            context
68377        ))
68378            .add(processAddUpdate)
68379            .update(processAddUpdate)
68380            .remove(processRemove)
68381            .execute();
68382    }
68383
68384    function getKey(item, idx) {
68385        var name = item && item.name;
68386        return name != null ? name : GROUP_DIFF_PREFIX + idx;
68387    }
68388
68389    function processAddUpdate(newIndex, oldIndex) {
68390        var context = this.context;
68391        var childOption = newIndex != null ? context.newChildren[newIndex] : null;
68392        var child = oldIndex != null ? context.oldChildren[oldIndex] : null;
68393
68394        doCreateOrUpdate(
68395            child,
68396            context.dataIndex,
68397            childOption,
68398            context.animatableModel,
68399            context.group,
68400            context.data
68401        );
68402    }
68403
68404    function processRemove(oldIndex) {
68405        var context = this.context;
68406        var child = context.oldChildren[oldIndex];
68407        child && context.group.remove(child);
68408    }
68409
68410    /*
68411* Licensed to the Apache Software Foundation (ASF) under one
68412* or more contributor license agreements.  See the NOTICE file
68413* distributed with this work for additional information
68414* regarding copyright ownership.  The ASF licenses this file
68415* to you under the Apache License, Version 2.0 (the
68416* "License"); you may not use this file except in compliance
68417* with the License.  You may obtain a copy of the License at
68418*
68419*   http://www.apache.org/licenses/LICENSE-2.0
68420*
68421* Unless required by applicable law or agreed to in writing,
68422* software distributed under the License is distributed on an
68423* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
68424* KIND, either express or implied.  See the License for the
68425* specific language governing permissions and limitations
68426* under the License.
68427*/
68428
68429// -------------
68430// Preprocessor
68431// -------------
68432
68433    registerPreprocessor(function (option) {
68434        var graphicOption = option.graphic;
68435
68436        // Convert
68437        // {graphic: [{left: 10, type: 'circle'}, ...]}
68438        // or
68439        // {graphic: {left: 10, type: 'circle'}}
68440        // to
68441        // {graphic: [{elements: [{left: 10, type: 'circle'}, ...]}]}
68442        if (isArray(graphicOption)) {
68443            if (!graphicOption[0] || !graphicOption[0].elements) {
68444                option.graphic = [{elements: graphicOption}];
68445            }
68446            else {
68447                // Only one graphic instance can be instantiated. (We dont
68448                // want that too many views are created in echarts._viewMap)
68449                option.graphic = [option.graphic[0]];
68450            }
68451        }
68452        else if (graphicOption && !graphicOption.elements) {
68453            option.graphic = [{elements: [graphicOption]}];
68454        }
68455    });
68456
68457// ------
68458// Model
68459// ------
68460
68461    var GraphicModel = extendComponentModel({
68462
68463        type: 'graphic',
68464
68465        defaultOption: {
68466
68467            // Extra properties for each elements:
68468            //
68469            // left/right/top/bottom: (like 12, '22%', 'center', default undefined)
68470            //      If left/rigth is set, shape.x/shape.cx/position will not be used.
68471            //      If top/bottom is set, shape.y/shape.cy/position will not be used.
68472            //      This mechanism is useful when you want to position a group/element
68473            //      against the right side or the center of this container.
68474            //
68475            // width/height: (can only be pixel value, default 0)
68476            //      Only be used to specify contianer(group) size, if needed. And
68477            //      can not be percentage value (like '33%'). See the reason in the
68478            //      layout algorithm below.
68479            //
68480            // bounding: (enum: 'all' (default) | 'raw')
68481            //      Specify how to calculate boundingRect when locating.
68482            //      'all': Get uioned and transformed boundingRect
68483            //          from both itself and its descendants.
68484            //          This mode simplies confining a group of elements in the bounding
68485            //          of their ancester container (e.g., using 'right: 0').
68486            //      'raw': Only use the boundingRect of itself and before transformed.
68487            //          This mode is similar to css behavior, which is useful when you
68488            //          want an element to be able to overflow its container. (Consider
68489            //          a rotated circle needs to be located in a corner.)
68490
68491            // Note: elements is always behind its ancestors in this elements array.
68492            elements: [],
68493            parentId: null
68494        },
68495
68496        /**
68497         * Save el options for the sake of the performance (only update modified graphics).
68498         * The order is the same as those in option. (ancesters ->
68498 descendants)
68499         *
68500         * @private
68501         * @type {Array.<Object>}
68502         */
68503        _elOptionsToUpdate: null,
68504
68505        /**
68506         * @override
68507         */
68508        mergeOption: function (option) {
68509            // Prevent default merge to elements
68510            var elements = this.option.elements;
68511            this.option.elements = null;
68512
68513            GraphicModel.superApply(this, 'mergeOption', arguments);
68514
68515            this.option.elements = elements;
68516        },
68517
68518        /**
68519         * @override
68520         */
68521        optionUpdated: function (newOption, isInit) {
68522            var thisOption = this.option;
68523            var newList = (isInit ? thisOption : newOption).elements;
68524            var existList = thisOption.elements = isInit ? [] : thisOption.elements;
68525
68526            var flattenedList = [];
68527            this._flatten(newList, flattenedList);
68528
68529            var mappingResult = mappingToExists(existList, flattenedList);
68530            makeIdAndName(mappingResult);
68531
68532            // Clear elOptionsToUpdate
68533            var elOptionsToUpdate = this._elOptionsToUpdate = [];
68534
68535            each$1(mappingResult, function (resultItem, index) {
68536                var newElOption = resultItem.option;
68537
68538                if (__DEV__) {
68539                    assert$1(
68540                        isObject$1(newElOption) || resultItem.exist,
68541                        'Empty graphic option definition'
68542                    );
68543                }
68544
68545                if (!newElOption) {
68546                    return;
68547                }
68548
68549                elOptionsToUpdate.push(newElOption);
68550
68551                setKeyInfoToNewElOption(resultItem, newElOption);
68552
68553                mergeNewElOptionToExist(existList, index, newElOption);
68554
68555                setLayoutInfoToExist(existList[index], newElOption);
68556
68557            }, this);
68558
68559            // Clean
68560            for (var i = existList.length - 1; i >= 0; i--) {
68561                if (existList[i] == null) {
68562                    existList.splice(i, 1);
68563                }
68564                else {
68565                    // $action should be volatile, otherwise option gotten from
68566                    // `getOption` will contain unexpected $action.
68567                    delete existList[i].$action;
68568                }
68569            }
68570        },
68571
68572        /**
68573         * Convert
68574         * [{
68575     *  type: 'group',
68576     *  id: 'xx',
68577     *  children: [{type: 'circle'}, {type: 'polygon'}]
68578     * }]
68579         * to
68580         * [
68581         *  {type: 'group', id: 'xx'},
68582         *  {type: 'circle', parentId: 'xx'},
68583         *  {type: 'polygon', parentId: 'xx'}
68584         * ]
68585         *
68586         * @private
68587         * @param {Array.<Object>} optionList option list
68588         * @param {Array.<Object>} result result of flatten
68589         * @param {Object} parentOption parent option
68590         */
68591        _flatten: function (optionList, result, parentOption) {
68592            each$1(optionList, function (option) {
68593                if (!option) {
68594                    return;
68595                }
68596
68597                if (parentOption) {
68598                    option.parentOption = parentOption;
68599                }
68600
68601                result.push(option);
68602
68603                var children = option.children;
68604                if (option.type === 'group' && children) {
68605                    this._flatten(children, result, option);
68606                }
68607                // Deleting for JSON output, and for not affecting group creation.
68608                delete option.children;
68609            }, this);
68610        },
68611
68612        // FIXME
68613        // Pass to view using payload? setOption has a payload?
68614        useElOptionsToUpdate: function () {
68615            var els = this._elOptionsToUpdate;
68616            // Clear to avoid render duplicately when zooming.
68617            this._elOptionsToUpdate = null;
68618            return els;
68619        }
68620    });
68621
68622// -----
68623// View
68624// -----
68625
68626    extendComponentView({
68627
68628        type: 'graphic',
68629
68630        /**
68631         * @override
68632         */
68633        init: function (ecModel, api) {
68634
68635            /**
68636             * @private
68637             * @type {module:zrender/core/util.HashMap}
68638             */
68639            this._elMap = createHashMap();
68640
68641            /**
68642             * @private
68643             * @type {module:echarts/graphic/GraphicModel}
68644             */
68645            this._lastGraphicModel;
68646        },
68647
68648        /**
68649         * @override
68650         */
68651        render: function (graphicModel, ecModel, api) {
68652
68653            // Having leveraged between use cases and algorithm complexity, a very
68654            // simple layout mechanism is used:
68655            // The size(width/height) can be determined by itself or its parent (not
68656            // implemented yet), but can not by its children. (Top-down travel)
68657            // The location(x/y) can be determined by the bounding rect of itself
68658            // (can including its descendants or not) and the size of its parent.
68659            // (Bottom-up travel)
68660
68661            // When `chart.clear()` or `chart.setOption({...}, true)` with the same id,
68662            // view will be reused.
68663            if (graphicModel !== this._lastGraphicModel) {
68664                this._clear();
68665            }
68666            this._lastGraphicModel = graphicModel;
68667
68668            this._updateElements(graphicModel, api);
68669            this._relocate(graphicModel, api);
68670        },
68671
68672        /**
68673         * Update graphic elements.
68674         *
68675         * @private
68676         * @param {Object} graphicModel graphic model
68677         * @param {module:echarts/ExtensionAPI} api extension API
68678         */
68679        _updateElements: function (graphicModel, api) {
68680            var elOptionsToUpdate = graphicModel.useElOptionsToUpdate();
68681
68682            if (!elOptionsToUpdate) {
68683                return;
68684            }
68685
68686            var elMap = this._elMap;
68687            var rootGroup = this.group;
68688
68689            // Top-down tranverse to assign graphic settings to each elements.
68690            each$1(elOptionsToUpdate, function (elOption) {
68691                var $action = elOption.$action;
68692                var id = elOption.id;
68693                var existEl = elMap.get(id);
68694                var parentId = elOption.parentId;
68695                var targetElParent = parentId != null ? elMap.get(parentId) : rootGroup;
68696
68697                if (elOption.type === 'text') {
68698                    var elOptionStyle = elOption.style;
68699
68700                    // In top/bottom mode, textVerticalAlign should not be used, which cause
68701                    // inaccurately locating.
68702                    if (elOption.hv && elOption.hv[1]) {
68703                        elOptionStyle.textVerticalAlign = elOptionStyle.textBaseline = null;
68704                    }
68705
68706                    // Compatible with previous setting: both support fill and textFill,
68707                    // stroke and textStroke.
68708                    !elOptionStyle.hasOwnProperty('textFill') && elOptionStyle.fill && (
68709                        elOptionStyle.textFill = elOptionStyle.fill
68710                    );
68711                    !elOptionStyle.hasOwnProperty('textStroke') && elOptionStyle.stroke && (
68712                        elOptionStyle.textStroke = elOptionStyle.stroke
68713                    );
68714                }
68715
68716                // Remove unnecessary props to avoid potential problems.
68717                var elOptionCleaned = getCleanedElOption(elOption);
68718
68719                // For simple, do not support parent change, otherwise reorder is needed.
68720                if (__DEV__) {
68721                    existEl && assert$1(
68722                        targetElParent === existEl.parent,
68723                        'Changing parent is not supported.'
68724                    );
68725                }
68726
68727                if (!$action || $action === 'merge') {
68728                    existEl
68729                        ? existEl.attr(elOptionCleaned)
68730                        : createEl$1(id, targetElParent, elOptionCleaned, elMap);
68731                }
68732                else if ($action === 'replace') {
68733                    removeEl(existEl, elMap);
68734                    createEl$1(id, targetElParent, elOptionCleaned, elMap);
68735                }
68736                else if ($action === 'remove') {
68737                    removeEl(existEl, elMap);
68738                }
68739
68740                var el = elMap.get(id);
68741                if (el) {
68742                    el.__ecGraphicWidth = elOption.width;
68743                    el.__ecGraphicHeight = elOption.height;
68744                }
68745            });
68746        },
68747
68748        /**
68749         * Locate graphic elements.
68750         *
68751         * @private
68752         * @param {Object} graphicModel graphic model
68753         * @param {module:echarts/ExtensionAPI} api extension API
68754         */
68755        _relocate: function (graphicModel, api) {
68756            var elOptions = graphicModel.option.elements;
68757            var rootGroup = this.group;
68758            var elMap = this._elMap;
68759
68760            // Bottom-up tranvese all elements (consider ec resize) to locate elements.
68761            for (var i = elOptions.length - 1; i >= 0; i--) {
68762                var elOption = elOptions[i];
68763                var el = elMap.get(elOption.id);
68764
68765                if (!el) {
68766                    continue;
68767                }
68768
68769                var parentEl = el.parent;
68770                var containerInfo = parentEl === rootGroup
68771                    ? {
68772                        width: api.getWidth(),
68773                        height: api.getHeight()
68774                    }
68775                    : { // Like 'position:absolut' in css, default 0.
68776                        width: parentEl.__ecGraphicWidth || 0,
68777                        height: parentEl.__ecGraphicHeight || 0
68778                    };
68779
68780                positionElement(
68781                    el, elOption, containerInfo, null,
68782                    {hv: elOption.hv, boundingMode: elOption.bounding}
68783                );
68784            }
68785        },
68786
68787        /**
68788         * Clear all elements.
68789         *
68790         * @private
68791         */
68792        _clear: function () {
68793            var elMap = this._elMap;
68794            elMap.each(function (el) {
68795                removeEl(el, elMap);
68796            });
68797            this._elMap = createHashMap();
68798        },
68799
68800        /**
68801         * @override
68802         */
68803        dispose: function () {
68804            this._clear();
68805        }
68806    });
68807
68808    function createEl$1(id, targetElParent, elOption, elMap) {
68809        var graphicType = elOption.type;
68810
68811        if (__DEV__) {
68812            assert$1(graphicType, 'graphic type MUST be set');
68813        }
68814
68815        var Clz = graphic[graphicType.charAt(0).toUpperCase() + graphicType.slice(1)];
68816
68817        if (__DEV__) {
68818            assert$1(Clz, 'graphic type can not be found');
68819        }
68820
68821        var el = new Clz(elOption);
68822        targetElParent.add(el);
68823        elMap.set(id, el);
68824        el.__ecGraphicId = id;
68825    }
68826
68827    function removeEl(existEl, elMap) {
68828        var existElParent = existEl && existEl.parent;
68829        if (existElParent) {
68830            existEl.type === 'group' && existEl.traverse(function (el) {
68831                removeEl(el, elMap);
68832            });
68833            elMap.removeKey(existEl.__ecGraphicId);
68834            existElParent.remove(existEl);
68835        }
68836    }
68837
68838// Remove unnecessary props to avoid potential problems.
68839    function getCleanedElOption(elOption) {
68840        elOption = extend({}, elOption);
68841        each$1(
68842            ['id', 'parentId', '$action', 'hv', 'bounding'].concat(LOCATION_PARAMS),
68843            function (name) {
68844                delete elOption[name];
68845            }
68846        );
68847        return elOption;
68848    }
68849
68850    function isSetLoc(obj, props) {
68851        var isSet;
68852        each$1(props, function (prop) {
68853            obj[prop] != null && obj[prop] !== 'auto' && (isSet = true);
68854        });
68855        return isSet;
68856    }
68857
68858    function setKeyInfoToNewElOption(resultItem, newElOption) {
68859        var existElOption = resultItem.exist;
68860
68861        // Set id and type after id assigned.
68862        newElOption.id = resultItem.keyInfo.id;
68863        !newElOption.type && existElOption && (newElOption.type = existElOption.type);
68864
68865        // Set parent id if not specified
68866        if (newElOption.parentId == null) {
68867            var newElParentOption = newElOption.parentOption;
68868            if (newElParentOption) {
68869                newElOption.parentId = newElParentOption.id;
68870            }
68871            else if (existElOption) {
68872                newElOption.parentId = existElOption.parentId;
68873            }
68874        }
68875
68876        // Clear
68877        newElOption.parentOption = null;
68878    }
68879
68880    function mergeNewElOptionToExist(existList, index, newElOption) {
68881        // Update existing options, for `getOption` feature.
68882        var newElOptCopy = extend({}, newElOption);
68883        var existElOption = existList[index];
68884
68885        var $action = newElOption.$action || 'merge';
68886        if ($action === 'merge') {
68887            if (existElOption) {
68888
68889                if (__DEV__) {
68890                    var newType = newElOption.type;
68891                    assert$1(
68892                        !newType || existElOption.type === newType,
68893                        'Please set $action: "replace" to change `type`'
68894                    );
68895                }
68896
68897                // We can ensure that newElOptCopy and existElOption are not
68898                // the same object, so `merge` will not change newElOptCopy.
68899                merge(existElOption, newElOptCopy, true);
68900                // Rigid body, use ignoreSize.
68901                mergeLayoutParam(existElOption, newElOptCopy, {ignoreSize: true});
68902                // Will be used in render.
68903                copyLayoutParams(newElOption, existElOption);
68904            }
68905            else {
68906                existList[index] = newElOptCopy;
68907            }
68908        }
68909        else if ($action === 'replace') {
68910            existList[index] = newElOptCopy;
68911        }
68912        else if ($action === 'remove') {
68913            // null will be cleaned later.
68914            existElOption && (existList[index] = null);
68915        }
68916    }
68917
68918    function setLayoutInfoToExist(existItem, newElOption) {
68919        if (!existItem) {
68920            return;
68921        }
68922        existItem.hv = newElOption.hv = [
68923            // Rigid body, dont care `width`.
68924            isSetLoc(newElOption, ['left', 'right']),
68925            // Rigid body, dont care `height`.
68926            isSetLoc(newElOption, ['top', 'bottom'])
68927        ];
68928        // Give default group size. Otherwise layout error may occur.
68929        if (existItem.type === 'group') {
68930            existItem.width == null && (existItem.width = newElOption.width = 0);
68931            existItem.height == null && (existItem.height = newElOption.height = 0);
68932        }
68933    }
68934
68935    /*
68936* Licensed to the Apache Software Foundation (ASF) under one
68937* or more contributor license agreements.  See the NOTICE file
68938* distributed with this work for additional information
68939* regarding copyright ownership.  The ASF licenses this file
68940* to you under the Apache License, Version 2.0 (the
68941* "License"); you may not use this file except in compliance
68942* with the License.  You may obtain a copy of the License at
68943*
68944*   http://www.apache.org/licenses/LICENSE-2.0
68945*
68946* Unless required by applicable law or agreed to in writing,
68947* software distributed under the License is distributed on an
68948* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
68949* KIND, either express or implied.  See the License for the
68950* specific language governing permissions and limitations
68951* under the License.
68952*/
68953
68954    /*
68955* Licensed to the Apache Software Foundation (ASF) under one
68956* or more contributor license agreements.  See the NOTICE file
68957* distributed with this work for additional information
68958* regarding copyright ownership.  The ASF licenses this file
68959* to you under the Apache License, Version 2.0 (the
68960* "License"); you may not use this file except in compliance
68961* with the License.  You may obtain a copy of the License at
68962*
68963*   http://www.apache.org/licenses/LICENSE-2.0
68964*
68965* Unless required by applicable law or agreed to in writing,
68966* software distributed under the License is distributed on an
68967* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
68968* KIND, either express or implied.  See the License for the
68969* specific language governing permissions and limitations
68970* under the License.
68971*/
68972
68973    var LegendModel = extendComponentModel({
68974
68975        type: 'legend.plain',
68976
68977        dependencies: ['series'],
68978
68979        layoutMode: {
68980            type: 'box',
68981            // legend.width/height are maxWidth/maxHeight actually,
68982            // whereas realy width/height is calculated by its content.
68983            // (Setting {left: 10, right: 10} does not make sense).
68984            // So consider the case:
68985            // `setOption({legend: {left: 10});`
68986            // then `setOption({legend: {right: 10});`
68987            // The previous `left` should be cleared by setting `ignoreSize`.
68988            ignoreSize: true
68989        },
68990
68991        init: function (option, parentModel, ecModel) {
68992            this.mergeDefaultAndTheme(option, ecModel);
68993
68994            option.selected = option.selected || {};
68995        },
68996
68997        mergeOption: function (option) {
68998            LegendModel.superCall(this, 'mergeOption', option);
68999        },
69000
69001        optionUpdated: function () {
69002            this._updateData(this.ecModel);
69003
69004            var legendData = this._data;
69005
69006            // If selectedMode is single, try to select one
69007            if (legendData[0] && this.get('selectedMode') === 'single') {
69008                var hasSelected = false;
69009                // If has any selected in option.selected
69010                for (var i = 0; i < legendData.length; i++) {
69011                    var name = legendData[i].get('name');
69012                    if (this.isSelected(name)) {
69013                        // Force to unselect others
69014                        this.select(name);
69015                        hasSelected = true;
69016                        break;
69017                    }
69018                }
69019                // Try select the first if selectedMode is single
69020                !hasSelected && this.select(legendData[0].get('name'));
69021            }
69022        },
69023
69024        _updateData: function (ecModel) {
69025            var potentialData = [];
69026            var availableNames = [];
69027
69028            ecModel.eachRawSeries(function (seriesModel) {
69029                var seriesName = seriesModel.name;
69030                availableNames.push(seriesName);
69031                var isPotential;
69032
69033                if (seriesModel.legendDataProvider) {
69034                    var data = seriesModel.legendDataProvider();
69035                    var names = data.mapArray(data.getName);
69036
69037                    if (!ecModel.isSeriesFiltered(seriesModel)) {
69038                        availableNames = availableNames.concat(names);
69039                    }
69040
69041                    if (names.length) {
69042                        potentialData = potentialData.concat(names);
69043                    }
69044                    else {
69045                        isPotential = true;
69046                    }
69047                }
69048                else {
69049                    isPotential = true;
69050                }
69051
69052                if (isPotential && isNameSpecified(seriesModel)) {
69053                    potentialData.push(seriesModel.name);
69054                }
69055            });
69056
69057            /**
69058             * @type {Array.<string>}
69059             * @private
69060             */
69061            this._availableNames = availableNames;
69062
69063            // If legend.data not specified in option, use availableNames as data,
69064            // which is convinient for user preparing option.
69065            var rawData = this.get('data') || potentialData;
69066
69067            var legendData = map(rawData, function (dataItem) {
69068                // Can be string or number
69069                if (typeof dataItem === 'string' || typeof dataItem === 'number') {
69070                    dataItem = {
69071                        name: dataItem
69072                    };
69073                }
69074                return new Model(dataItem, this, this.ecModel);
69075            }, this);
69076
69077            /**
69078             * @type {Array.<module:echarts/model/Model>}
69079             * @private
69080             */
69081            this._data = legendData;
69082        },
69083
69084        /**
69085         * @return {Array.<module:echarts/model/Model>}
69086         */
69087        getData: function () {
69088            return this._data;
69089        },
69090
69091        /**
69092         * @param {string} name
69093         */
69094        select: function (name) {
69095            var selected = this.option.selected;
69096            var selectedMode = this.get('selectedMode');
69097            if (selectedMode === 'single') {
69098                var data = this._data;
69099                each$1(data, function (dataItem) {
69100                    selected[dataItem.get('name')] = false;
69101                });
69102            }
69103            selected[name] = true;
69104        },
69105
69106        /**
69107         * @param {string} name
69108         */
69109        unSelect: function (name) {
69110            if (this.get('selectedMode') !== 'single') {
69111                this.option.selected[name] = false;
69112            }
69113        },
69114
69115        /**
69116         * @param {string} name
69117         */
69118        toggleSelected: function (name) {
69119            var selected = this.option.selected;
69120            // Default is true
69121            if (!selected.hasOwnProperty(name)) {
69122                selected[name] = true;
69123            }
69124            this[selected[name] ? 'unSelect' : 'select'](name);
69125        },
69126
69127        /**
69128         * @param {string} name
69129         */
69130        isSelected: function (name) {
69131            var selected = this.option.selected;
69132            return !(selected.hasOwnProperty(name) && !selected[name])
69133                && indexOf(this._availableNames, name) >= 0;
69134        },
69135
69136        defaultOption: {
69137            // 一级层å
69138            zlevel: 0,
69139            // 二级层å
69140            z: 4,
69141            show: true,
69142
69143            // 布局方式,默认为水平布局,可选为:
69144            // 'horizontal' | 'vertical'
69145            orient: 'horizontal',
69146
69147            left: 'center',
69148            // right: 'center',
69149
69150            top: 0,
69151            // bottom: null,
69152
69153            // 水平对齐
69154            // 'auto' | 'left' | 'right'
69155            // 默认为 'auto', æ ¹æ® x 的位置判断是左对齐还是右对齐
69156            align: 'auto',
69157
69158            backgroundColor: 'rgba(0,0,0,0)',
69159            // 图例边框颜色
69160            borderColor: '#ccc',
69161            borderRadius: 0,
69162            // 图例边框线宽,单位px,默èÂ
69162®Â¤Ã¤Â¸Âº0(æ— 边框)
69163            borderWidth: 0,
69164            // 图例内边距,单位px,默认各方向内边距为5,
69165            // 接受数组分别设定上右下左边距,同css
69166            padding: 5,
69167            // 各个item之间的间隔,单位px,默认为10,
69168            // 横向布局时为水平间隔,纵向布局时为纵向间隔
69169            itemGap: 10,
69170            // 图例图形宽度
69171            itemWidth: 25,
69172            // 图例图形高度
69173            itemHeight: 14,
69174
69175            // 图例关闭时候的颜色
69176            inactiveColor: '#ccc',
69177
69178            textStyle: {
69179                // 图例文字颜色
69180                color: '#333'
69181            },
69182            // formatter: '',
69183            // 选择模式,默认开启图例开关
69184            selectedMode: true,
69185            // 配置默认选中状态,可配合LEGEND.SELECTED事件做动态数据载入
69186            // selected: null,
69187            // 图例内容(详见legend.data,数组中每一项代表一个item
69188            // data: [],
69189
69190            // Tooltip 相关配置
69191            tooltip: {
69192                show: false
69193            }
69194        }
69195    });
69196
69197    /*
69198* Licensed to the Apache Software Foundation (ASF) under one
69199* or more contributor license agreements.  See the NOTICE file
69200* distributed with this work for additional information
69201* regarding copyright ownership.  The ASF licenses this file
69202* to you under the Apache License, Version 2.0 (the
69203* "License"); you may not use this file except in compliance
69204* with the License.  You may obtain a copy of the License at
69205*
69206*   http://www.apache.org/licenses/LICENSE-2.0
69207*
69208* Unless required by applicable law or agreed to in writing,
69209* software distributed under the License is distributed on an
69210* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
69211* KIND, either express or implied.  See the License for the
69212* specific language governing permissions and limitations
69213* under the License.
69214*/
69215
69216    function legendSelectActionHandler(methodName, payload, ecModel) {
69217        var selectedMap = {};
69218        var isToggleSelect = methodName === 'toggleSelected';
69219        var isSelected;
69220        // Update all legend components
69221        ecModel.eachComponent('legend', function (legendModel) {
69222            if (isToggleSelect && isSelected != null) {
69223                // Force other legend has same selected status
69224                // Or the first is toggled to true and other are toggled to false
69225                // In the case one legend has some item unSelected in option. And if other legend
69226                // doesn't has the item, they will assume it is selected.
69227                legendModel[isSelected ? 'select' : 'unSelect'](payload.name);
69228            }
69229            else {
69230                legendModel[methodName](payload.name);
69231                isSelected = legendModel.isSelected(payload.name);
69232            }
69233            var legendData = legendModel.getData();
69234            each$1(legendData, function (model) {
69235                var name = model.get('name');
69236                // Wrap element
69237                if (name === '\n' || name === '') {
69238                    return;
69239                }
69240                var isItemSelected = legendModel.isSelected(name);
69241                if (selectedMap.hasOwnProperty(name)) {
69242                    // Unselected if any legend is unselected
69243                    selectedMap[name] = selectedMap[name] && isItemSelected;
69244                }
69245                else {
69246                    selectedMap[name] = isItemSelected;
69247                }
69248            });
69249        });
69250        // Return the event explicitly
69251        return {
69252            name: payload.name,
69253            selected: selectedMap
69254        };
69255    }
69256    /**
69257     * @event legendToggleSelect
69258     * @type {Object}
69259     * @property {string} type 'legendToggleSelect'
69260     * @property {string} [from]
69261     * @property {string}
69261 name Series name or data item name
69262     */
69263    registerAction(
69264        'legendToggleSelect', 'legendselectchanged',
69265        curry(legendSelectActionHandler, 'toggleSelected')
69266    );
69267
69268    /**
69269     * @event legendSelect
69270     * @type {Object}
69271     * @property {string} type 'legendSelect'
69272     * @property {string} name Series name or data item name
69273     */
69274    registerAction(
69275        'legendSelect', 'legendselected',
69276        curry(legendSelectActionHandler, 'select')
69277    );
69278
69279    /**
69280     * @event legendUnSelect
69281     * @type {Object}
69282     * @property {string} type 'legendUnSelect'
69283     * @property {string} name Series name or data item name
69284     */
69285    registerAction(
69286        'legendUnSelect', 'legendunselected',
69287        curry(legendSelectActionHandler, 'unSelect')
69288    );
69289
69290    /*
69291* Licensed to the Apache Software Foundation (ASF) under one
69292* or more contributor license agreements.  See the NOTICE file
69293* distributed with this work for additional information
69294* regarding copyright ownership.  The ASF licenses this file
69295* to you under the Apache License, Version 2.0 (the
69296* "License"); you may not use this file except in compliance
69297* with the License.  You may obtain a copy of the License at
69298*
69299*   http://www.apache.org/licenses/LICENSE-2.0
69300*
69301* Unless required by applicable law or agreed to in writing,
69302* software distributed under the License is distributed on an
69303* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
69304* KIND, either express or implied.  See the License for the
69305* specific language governing permissions and limitations
69306* under the License.
69307*/
69308
69309    /**
69310     * Layout list like component.
69311     * It will box layout each items in group of component and then position the whole group in the viewport
69312     * @param {module:zrender/group/Group} group
69313     * @param {module:echarts/model/Component} componentModel
69314     * @param {module:echarts/ExtensionAPI}
69315     */
69316    function layout$3(group, componentModel, api) {
69317        var boxLayoutParams = componentModel.getBoxLayoutParams();
69318        var padding = componentModel.get('padding');
69319        var viewportSize = {width: api.getWidth(), height: api.getHeight()};
69320
69321        var rect = getLayoutRect(
69322            boxLayoutParams,
69323            viewportSize,
69324            padding
69325        );
69326
69327        box(
69328            componentModel.get('orient'),
69329            group,
69330            componentModel.get('itemGap'),
69331            rect.width,
69332            rect.height
69333        );
69334
69335        positionElement(
69336            group,
69337            boxLayoutParams,
69338            viewportSize,
69339            padding
69340        );
69341    }
69342
69343    function makeBackground(rect, componentModel) {
69344        var padding = normalizeCssArray$1(
69345            componentModel.get('padding')
69346        );
69347        var style = componentModel.getItemStyle(['color', 'opacity']);
69348        style.fill = componentModel.get('backgroundColor');
69349        var rect = new Rect({
69350            shape: {
69351                x: rect.x - padding[3],
69352                y: rect.y - padding[0],
69353                width: rect.width + padding[1] + padding[3],
69354                height: rect.height + padding[0] + padding[2],
69355                r: componentModel.get('borderRadius')
69356            },
69357            style: style,
69358            silent: true,
69359            z2: -1
69360        });
69361        // FIXME
69362        // `subPixelOptimizeRect` may bring some gap between edge of viewpart
69363        // and background rect when setting like `left: 0`, `top: 0`.
69364        // graphic.subPixelOptimizeRect(rect);
69365
69366        return rect;
69367    }
69368
69369    /*
69370* Licensed to the Apache Software Foundation (ASF) under one
69371* or more contributor license agreements.  See the NOTICE file
69372* distributed with this work for additional information
69373* regarding copyright ownership.  The ASF licenses this file
69374* to you under the Apache License, Version 2.0 (the
69375* "License"); you may not use this file except in compliance
69376* with the License.  You may obtain a copy of the License at
69377*
69378*   http://www.apache.org/licenses/LICENSE-2.0
69379*
69380* Unless required by applicable law or agreed to in writing,
69381* software distributed under the License is distributed on an
69382* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
69383* KIND, either express or implied.  See the License for the
69384* specific language governing permissions and limitations
69385* under the License.
69386*/
69387
69388    var curry$4 = curry;
69389    var each$16 = each$1;
69390    var Group$3 = Group;
69391
69392    var LegendView = extendComponentView({
69393
69394        type: 'legend.plain',
69395
69396        newlineDisabled: false,
69397
69398        /**
69399         * @override
69400         */
69401        init: function () {
69402
69403            /**
69404             * @private
69405             * @type {module:zrender/container/Group}
69406             */
69407            this.group.add(this._contentGroup = new Group$3());
69408
69409            /**
69410             * @private
69411             * @type {module:zrender/Element}
69412             */
69413            this._backgroundEl;
69414        },
69415
69416        /**
69417         * @protected
69418         */
69419        getContentGroup: function () {
69420            return this._contentGroup;
69421        },
69422
69423        /**
69424         * @override
69425         */
69426        render: function (legendModel, ecModel, api) {
69427
69428            this.resetInner();
69429
69430            if (!legendModel.get('show', true)) {
69431                return;
69432            }
69433
69434            var itemAlign = legendModel.get('align');
69435            if (!itemAlign || itemAlign === 'auto') {
69436                itemAlign = (
69437                    legendModel.get('left') === 'right'
69438                    && legendModel.get('orient') === 'vertical'
69439                ) ? 'right' : 'left';
69440            }
69441
69442            this.renderInner(itemAlign, legendModel, ecModel, api);
69443
69444            // Perform layout.
69445            var positionInfo = legendModel.getBoxLayoutParams();
69446            var viewportSize = {width: api.getWidth(), height: api.getHeight()};
69447            var padding = legendModel.get('padding');
69448
69449            var maxSize = getLayoutRect(positionInfo, viewportSize, padding);
69450            var mainRect = this.layoutInner(legendModel, itemAlign, maxSize);
69451
69452            // Place mainGroup, based on the calculated `mainRect`.
69453            var layoutRect = getLayoutRect(
69454                defaults({width: mainRect.width, height: mainRect.height}, positionInfo),
69455                viewportSize,
69456                padding
69457            );
69458            this.group.attr('position', [layoutRect.x - mainRect.x, layoutRect.y - mainRect.y]);
69459
69460            // Render background after group is layout.
69461            this.group.add(
69462                this._backgroundEl = makeBackground(mainRect, legendModel)
69463            );
69464        },
69465
69466        /**
69467         * @protected
69468         */
69469        resetInner: function () {
69470            this.getContentGroup().removeAll();
69471            this._backgroundEl && this.group.remove(this._backgroundEl);
69472        },
69473
69474        /**
69475         * @protected
69476         */
69477        renderInner: function (itemAlign, legendModel, ecModel, api) {
69478            var contentGroup = this.getContentGroup();
69479            var legendDrawnMap = createHashMap();
69480            var selectMode = legendModel.get('selectedMode');
69481
69482            var excludeSeriesId = [];
69483            ecModel.eachRawSeries(function (seriesModel) {
69484                !seriesModel.get('legendHoverLink') && excludeSeriesId.push(seriesM
69484odel.id);
69485            });
69486
69487            each$16(legendModel.getData(), function (itemModel, dataIndex) {
69488                var name = itemModel.get('name');
69489
69490                // Use empty string or \n as a newline string
69491                if (!this.newlineDisabled && (name === '' || name === '\n')) {
69492                    contentGroup.add(new Group$3({
69493                        newline: true
69494                    }));
69495                    return;
69496                }
69497
69498                // Representitive series.
69499                var seriesModel = ecModel.getSeriesByName(name)[0];
69500
69501                if (legendDrawnMap.get(name)) {
69502                    // Have been drawed
69503                    return;
69504                }
69505
69506                // Series legend
69507                if (seriesModel) {
69508                    var data = seriesModel.getData();
69509                    var color = data.getVisual('color');
69510
69511                    // If color is a callback function
69512                    if (typeof color === 'function') {
69513                        // Use the first data
69514                        color = color(seriesModel.getDataParams(0));
69515                    }
69516
69517                    // Using rect symbol defaultly
69518                    var legendSymbolType = data.getVisual('legendSymbol') || 'roundRect';
69519                    var symbolType = data.getVisual('symbol');
69520
69521                    var itemGroup = this._createItem(
69522                        name, dataIndex, itemModel, legendModel,
69523                        legendSymbolType, symbolType,
69524                        itemAlign, color,
69525                        selectMode
69526                    );
69527
69528                    itemGroup.on('click', curry$4(dispatchSelectAction, name, api))
69529                        .on('mouseover', curry$4(dispatchHighlightAction, seriesModel, null, api, excludeSeriesId))
69530                        .on('mouseout', curry$4(dispatchDownplayAction, seriesModel, null, api, excludeSeriesId));
69531
69532                    legendDrawnMap.set(name, true);
69533                }
69534                else {
69535                    // Data legend of pie, funnel
69536                    ecModel.eachRawSeries(function (seriesModel) {
69537                        // In case multiple series has same data name
69538                        if (legendDrawnMap.get(name)) {
69539                            return;
69540                        }
69541
69542                        if (seriesModel.legendDataProvider) {
69543                            var data = seriesModel.legendDataProvider();
69544                            var idx = data.indexOfName(name);
69545                            if (idx < 0) {
69546                                return;
69547                            }
69548
69549                            var color = data.getItemVisual(idx, 'color');
69550
69551                            var legendSymbolType = 'roundRect';
69552
69553                            var itemGroup = this._createItem(
69554                                name, dataIndex, itemModel, legendModel,
69555                                legendSymbolType, null,
69556                                itemAlign, color,
69557                                selectMode
69558                            );
69559
69560                            // FIXME: consider different series has items with the same name.
69561                            itemGroup.on('click', curry$4(dispatchSelectAction, name, api))
69562                            // FIXME Should not specify the series name
69563                                .on('mouseover', curry$4(dispatchHighlightAction, seriesModel, name, api, excludeSeriesId))
69564                                .on('mouseout', curry$4(dispatchDownplayAction, seriesModel, name, api, excludeSeriesId));
69565
69566                            legendDrawnMap.set(name, true);
69567                        }
69568
69569                    }, this);
69570                }
69571
69572                if (__DEV__) {
69573                    if (!legendDrawnMap.get(name)) {
69574                        console.warn(name + ' series not exists. Legend data should be same with series name or data name.');
69575                    }
69576                }
69577            }, this);
69578        },
69579
69580        _createItem: function (
69581            name, dataIndex, itemModel, legendModel,
69582            legendSymbolType, symbolType,
69583            itemAlign, color, selectMode
69584        ) {
69585            var itemWidth = legendModel.get('itemWidth');
69586            var itemHeight = legendModel.get('itemHeight');
69587            var inactiveColor = legendModel.get('inactiveColor');
69588            var symbolKeepAspect = legendModel.get('symbolKeepAspect');
69589
69590            var isSelected = legendModel.isSelected(name);
69591            var itemGroup = new Group$3();
69592
69593            var textStyleModel = itemModel.getModel('textStyle');
69594
69595            var itemIcon = itemModel.get('icon');
69596
69597            var tooltipModel = itemModel.getModel('tooltip');
69598            var legendGlobalTooltipModel = tooltipModel.parentModel;
69599
69600            // Use user given icon first
69601            legendSymbolType = itemIcon || legendSymbolType;
69602            itemGroup.add(createSymbol(
69603                legendSymbolType,
69604                0,
69605                0,
69606                itemWidth,
69607                itemHeight,
69608                isSelected ? color : inactiveColor,
69609                // symbolKeepAspect default true for legend
69610                symbolKeepAspect == null ? true : symbolKeepAspect
69611            ));
69612
69613            // Compose symbols
69614            // PENDING
69615            if (!itemIcon && symbolType
69616                // At least show one symbol, can't be all none
69617                && ((symbolType !== legendSymbolType) || symbolType == 'none')
69618            ) {
69619                var size = itemHeight * 0.8;
69620                if (symbolType === 'none') {
69621                    symbolType = 'circle';
69622                }
69623                // Put symbol in the center
69624                itemGroup.add(createSymbol(
69625                    symbolType,
69626                    (itemWidth - size) / 2,
69627                    (itemHeight - size) / 2,
69628                    size,
69629                    size,
69630                    isSelected ? color : inactiveColor,
69631                    // symbolKeepAspect default true for legend
69632                    symbolKeepAspect == null ? true : symbolKeepAspect
69633                ));
69634            }
69635
69636            var textX = itemAlign === 'left' ? itemWidth + 5 : -5;
69637            var textAlign = itemAlign;
69638
69639            var formatter = legendModel.get('formatter');
69640            var content = name;
69641            if (typeof formatter === 'string' && formatter) {
69642                content = formatter.replace('{name}', name != null ? name : '');
69643            }
69644            else if (typeof formatter === 'function') {
69645                content = formatter(name);
69646            }
69647
69648            itemGroup.add(new Text({
69649                style: setTextStyle({}, textStyleModel, {
69650                    text: content,
69651                    x: textX,
69652                    y: itemHeight / 2,
69653                    textFill: isSelected ? textStyleModel.getTextColor() : inactiveColor,
69654                    textAlign: textAlign,
69655                    textVerticalAlign: 'middle'
69656                })
69657            }));
69658
69659            // Add a invisible rect to increase the area of mouse hover
69660            var hitRect = new Rect({
69661                shape: itemGroup.getBoundingRect(),
69662                invisible: true,
69663                tooltip: tooltipModel.get('show') ? extend({
69664                    content: name,
69665                    // Defaul formatter
69666                    formatter: legendGlobalTooltipModel.get('formatter', true) || function () {
69667                        return name;
69668                    },
69669                    formatterParams: {
69670                        componentType: 'legend',
69671                        legendIndex: legendModel.componentIndex,
69672                        name: name,
69673                        $vars: ['name']
69674                    }
69675                }, tooltipModel.option) : null
69676            });
69677            itemGroup.add(hitRect);
69678
69679            itemGroup.eachChild(function (child) {
69680                child.silent = true;
69681            });
69682
69683            hitRect.silent = !selectMode;
69684
69685            this.getContentGroup().add(itemGroup);
69686
69687            setHoverStyle(itemGroup);
69688
69689            itemGroup.__legendDataIndex = dataIndex;
69690
69691            return itemGroup;
69692        },
69693
69694        /**
69695         * @protected
69696         */
69697        layoutInner: function (legendModel, itemAlign, maxSize) {
69698            var contentGroup = this.getContentGroup();
69699
69700            // Place items in contentGroup.
69701            box(
69702                legendModel.get('orient'),
69703                contentGroup,
69704                legendModel.get('itemGap'),
69705                maxSize.width,
69706                maxSize.height
69707            );
69708
69709            var contentRect = contentGroup.getBoundingRect();
69710            contentGroup.attr('position', [-contentRect.x, -contentRect.y]);
69711
69712            return this.group.getBoundingRect();
69713        }
69714
69715    });
69716
69717    function dispatchSelectAction(name, api) {
69718        api.dispatchAction({
69719            type: 'legendToggleSelect',
69720            name: name
69721        });
69722    }
69723
69724    function dispatchHighlightAction(seriesModel, dataName, api, excludeSeriesId) {
69725        // If element hover will move to a hoverLayer.
69726        var el = api.getZr().storage.getDisplayList()[0];
69727        if (!(el && el.useHoverLayer)) {
69728            api.dispatchAction({
69729                type: 'highlight',
69730                seriesName: seriesModel.name,
69731                name: dataName,
69732                excludeSeriesId: excludeSeriesId
69733            });
69734        }
69735    }
69736
69737    function dispatchDownplayAction(seriesModel, dataName, api, excludeSeriesId) {
69738        // If element hover will move to a hoverLayer.
69739        var el = api.getZr().storage.getDisplayList()[0];
69740        if (!(el && el.useHoverLayer)) {
69741            api.dispatchAction({
69742                type: 'downplay',
69743                seriesName: seriesModel.name,
69744                name: dataName,
69745                excludeSeriesId: excludeSeriesId
69746            });
69747        }
69748    }
69749
69750    /*
69751* Licensed to the Apache Software Foundation (ASF) under one
69752* or more contributor license agreements.  See the NOTICE file
69753* distributed with this work for additional information
69754* regarding copyright ownership.  The ASF licenses this file
69755* to you under the Apache License, Version 2.0 (the
69756* "License"); you may not use this file except in compliance
69757* with the License.  You may obtain a copy of the License at
69758*
69759*   http://www.apache.org/licenses/LICENSE-2.0
69760*
69761* Unless required by applicable law or agreed to in writing,
69762* software distributed under the License is distributed on an
69763* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
69764* KIND, either express or implied.  See the License for the
69765* specific language governing permissions and limitations
69766* under the License.
69767*/
69768
69769    var legendFilter = function (ecModel) {
69770
69771        var legendModels = ecModel.findComponents({
69772            mainType: 'legend'
69773        });
69774        if (legendModels && legendModels.length) {
69775            ecModel.filterSeries(function (series) {
69776                // If in any legend component the status is not selected.
69777                // Because in legend series is assumed selected when it is not in the legend data.
69778                for (var i = 0; i < legendModels.length; i++) {
69779                    if (!legendModels[i].isSelected(series.name)) {
69780                        return false;
69781                    }
69782                }
69783                return true;
69784            });
69785        }
69786
69787    };
69788
69789    /*
69790* Licensed to the Apache Software Foundation (ASF) under one
69791* or more contributor license agreements.  See the NOTICE file
69792* distributed with this work for additional information
69793* regarding copyright ownership.  The ASF licenses this file
69794* to you under the Apache License, Version 2.0 (the
69795* "License");
69795 you may not use this file except in compliance
69796* with the License.  You may obtain a copy of the License at
69797*
69798*   http://www.apache.org/licenses/LICENSE-2.0
69799*
69800* Unless required by applicable law or agreed to in writing,
69801* software distributed under the License is distributed on an
69802* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
69803* KIND, either express or implied.  See the License for the
69804* specific language governing permissions and limitations
69805* under the License.
69806*/
69807
69808
69809// Do not contain scrollable legend, for sake of file size.
69810
69811// Series Filter
69812    registerProcessor(legendFilter);
69813
69814    ComponentModel.registerSubTypeDefaulter('legend', function () {
69815        // Default 'plain' when no type specified.
69816        return 'plain';
69817    });
69818
69819    /*
69820* Licensed to the Apache Software Foundation (ASF) under one
69821* or more contributor license agreements.  See the NOTICE file
69822* distributed with this work for additional information
69823* regarding copyright ownership.  The ASF licenses this file
69824* to you under the Apache License, Version 2.0 (the
69825* "License"); you may not use this file except in compliance
69826* with the License.  You may obtain a copy of the License at
69827*
69828*   http://www.apache.org/licenses/LICENSE-2.0
69829*
69830* Unless required by applicable law or agreed to in writing,
69831* software distributed under the License is distributed on an
69832* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
69833* KIND, either express or implied.  See the License for the
69834* specific language governing permissions and limitations
69835* under the License.
69836*/
69837
69838    var ScrollableLegendModel = LegendModel.extend({
69839
69840        type: 'legend.scroll',
69841
69842        /**
69843         * @param {number} scrollDataIndex
69844         */
69845        setScrollDataIndex: function (scrollDataIndex) {
69846            this.option.scrollDataIndex = scrollDataIndex;
69847        },
69848
69849        defaultOption: {
69850            scrollDataIndex: 0,
69851            pageButtonItemGap: 5,
69852            pageButtonGap: null,
69853            pageButtonPosition: 'end', // 'start' or 'end'
69854            pageFormatter: '{current}/{total}', // If null/undefined, do not show page.
69855            pageIcons: {
69856                horizontal: ['M0,0L12,-10L12,10z', 'M0,0L-12,-10L-12,10z'],
69857                vertical: ['M0,0L20,0L10,-20z', 'M0,0L20,0L10,20z']
69858            },
69859            pageIconColor: '#2f4554',
69860            pageIconInactiveColor: '#aaa',
69861            pageIconSize: 15, // Can be [10, 3], which represents [width, height]
69862            pageTextStyle: {
69863                color: '#333'
69864            },
69865
69866            animationDurationUpdate: 800
69867        },
69868
69869        /**
69870         * @override
69871         */
69872        init: function (option, parentModel, ecModel, extraOpt) {
69873            var inputPositionParams = getLayoutParams(option);
69874
69875            ScrollableLegendModel.superCall(this, 'init', option, parentModel, ecModel, extraOpt);
69876
69877            mergeAndNormalizeLayoutParams(this, option, inputPositionParams);
69878        },
69879
69880        /**
69881         * @override
69882         */
69883        mergeOption: function (option, extraOpt) {
69884            ScrollableLegendModel.superCall(this, 'mergeOption', option, extraOpt);
69885
69886            mergeAndNormalizeLayoutParams(this, this.option, option);
69887        },
69888
69889        getOrient: function () {
69890            return this.get('orient') === 'vertical'
69891                ? {index: 1, name: 'vertical'}
69892                : {index: 0, name: 'horizontal'};
69893        }
69894
69895    });
69896
69897// Do not `ignoreSize` to enable setting {left: 10, right: 10}.
69898    function mergeAndNormalizeLayoutParams(legendModel, target, raw) {
69899        var orient = legendModel.getOrient();
69900        var ignoreSize = [1, 1];
69901        ignoreSize[orient.index] = 0;
69902        mergeLayoutParam(target, raw, {
69903            type: 'box', ignoreSize: ignoreSize
69904        });
69905    }
69906
69907    /*
69908* Licensed to the Apache Software Foundation (ASF) under one
69909* or more contributor license agreements.  See the NOTICE file
69910* distributed with this work for additional information
69911* regarding copyright ownership.  The ASF licenses this file
69912* to you under the Apache License, Version 2.0 (the
69913* "License");
69913 you may not use this file except in compliance
69914* with the License.  You may obtain a copy of the License at
69915*
69916*   http://www.apache.org/licenses/LICENSE-2.0
69917*
69918* Unless required by applicable law or agreed to in writing,
69919* software distributed under the License is distributed on an
69920* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
69921* KIND, either express or implied.  See the License for the
69922* specific language governing permissions and limitations
69923* under the License.
69924*/
69925
69926    /**
69927     * Separate legend and scrollable legend to reduce package size.
69928     */
69929
69930    var Group$4 = Group;
69931
69932    var WH$1 = ['width', 'height'];
69933    var XY$1 = ['x', 'y'];
69934
69935    var ScrollableLegendView = LegendView.extend({
69936
69937        type: 'legend.scroll',
69938
69939        newlineDisabled: true,
69940
69941        init: function () {
69942
69943            ScrollableLegendView.superCall(this, 'init');
69944
69945            /**
69946             * @private
69947             * @type {number} For `scroll`.
69948             */
69949            this._currentIndex = 0;
69950
69951            /**
69952             * @private
69953             * @type {module:zrender/container/Group}
69954             */
69955            this.group.add(this._containerGroup = new Group$4());
69956            this._containerGroup.add(this.getContentGroup());
69957
69958            /**
69959             * @private
69960             * @type {module:zrender/container/Group}
69961             */
69962            this.group.add(this._controllerGroup = new Group$4());
69963
69964            /**
69965             *
69966             * @private
69967             */
69968            this._showController;
69969        },
69970
69971        /**
69972         * @override
69973         */
69974        resetInner: function () {
69975            ScrollableLegendView.superCall(this, 'resetInner');
69976
69977            this._controllerGroup.removeAll();
69978            this._containerGroup.removeClipPath();
69979            this._containerGroup.__rectSize = null;
69980        },
69981
69982        /**
69983         * @override
69984         */
69985        renderInner: function (itemAlign, legendModel, ecModel, api) {
69986            var me = this;
69987
69988            // Render content items.
69989            ScrollableLegendView.superCall(this, 'renderInner', itemAlign, legendModel, ecModel, api);
69990
69991            var controllerGroup = this._controllerGroup;
69992
69993            var pageIconSize = legendModel.get('pageIconSize', true);
69994            if (!isArray(pageIconSize)) {
69995                pageIconSize = [pageIconSize, pageIconSize];
69996            }
69997
69998            createPageButton('pagePrev', 0);
69999
70000            var pageTextStyleModel = legendModel.getModel('pageTextStyle');
70001            controllerGroup.add(new Text({
70002                name: 'pageText',
70003                style: {
70004                    textFill: pageTextStyleModel.getTextColor(),
70005                    font: pageTextStyleModel.getFont(),
70006                    textVerticalAlign: 'middle',
70007                    textAlign: 'center'
70008                },
70009                silent: true
70010            }));
70011
70012            createPageButton('pageNext', 1);
70013
70014            function createPageButton(name, iconIdx) {
70015                var pageDataIndexName = name + 'DataIndex';
70016                var icon = createIcon(
70017                    legendModel.get('pageIcons', true)[legendModel.getOrient().name][iconIdx],
70018                    {
70019                        // Buttons will be created in each render, so we do not need
70020                        // to worry about avoiding using legendModel kept in scope.
70021                        onclick: bind(
70022                            me._pageGo, me, pageDataIndexName, legendModel, api
70023                        )
70024                    },
70025                    {
70026                        x: -pageIconSize[0] / 2,
70027                        y: -pageIconSize[1] / 2,
70028                        width: pageIconSize[0],
70029                        height: pageIconSize[1]
70030                    }
70031                );
70032                icon.name = name;
70033                controllerGroup.add(icon);
70034            }
70035        },
70036
70037        /**
70038         * @override
70039         */
70040        layoutInner: function (legendModel, itemAlign, maxSize) {
70041            var contentGroup = this.getContentGroup();
70042            var containerGroup = this._containerGroup;
70043            var controllerGroup = this._controllerGroup;
70044
70045            var orientIdx = legendModel.getOrient().index;
70046            var wh = WH$1[orientIdx];
70047            var hw = WH$1[1 - orientIdx];
70048            var yx = XY$1[1 - orientIdx];
70049
70050            // Place items in contentGroup.
70051            box(
70052                legendModel.get('orient'),
70053                contentGroup,
70054                legendModel.get('itemGap'),
70055                !orientIdx ? null : maxSize.width,
70056                orientIdx ? null : maxSize.height
70057            );
70058
70059            box(
70060                // Buttons in controller are layout always horizontally.
70061                'horizontal',
70062                controllerGroup,
70063                legendModel.get('pageButtonItemGap', true)
70064            );
70065
70066            var contentRect = contentGroup.getBoundingRect();
70067            var controllerRect = controllerGroup.getBoundingRect();
70068            var showController = this._showController = contentRect[wh] > maxSize[wh];
70069
70070            var contentPos = [-contentRect.x, -contentRect.y];
70071            // Remain contentPos when scroll animation perfroming.
70072            contentPos[orientIdx] = contentGroup.position[orientIdx];
70073
70074            // Layout container group based on 0.
70075            var containerPos = [0, 0];
70076            var controllerPos = [-controllerRect.x, -controllerRect.y];
70077            var pageButtonGap = retrieve2(
70078                legendModel.get('pageButtonGap', true), legendModel.get('itemGap', true)
70079            );
70080
70081            // Place containerGroup and controllerGroup and contentGroup.
70082            if (showController) {
70083                var pageButtonPosition = legendModel.get('pageButtonPosition', true);
70084                // controller is on the right / bottom.
70085                if (pageButtonPosition === 'end') {
70086                    controllerPos[orientIdx] += maxSize[wh] - controllerRect[wh];
70087                }
70088                // controller is on the left / top.
70089                else {
70090                    containerPos[orientIdx] += controllerRect[wh] + pageButtonGap;
70091                }
70092            }
70093
70094            // Always align controller to content as 'middle'.
70095            controllerPos[1 - orientIdx] += contentRect[hw] / 2 - controllerRect[hw] / 2;
70096
70097            contentGroup.attr('position', contentPos);
70098            containerGroup.attr('position', containerPos);
70099            controllerGroup.attr('position', controllerPos);
70100
70101            // Calculate `mainRect` and set `clipPath`.
70102            // mainRect should not be calculated by `this.group.getBoundingRect()`
70103            // for sake of the overflow.
70104            var mainRect = this.group.getBoundingRect();
70105            var mainRect = {x: 0, y: 0};
70106            // Consider content may be overflow (should be clipped).
70107            mainRect[wh] = showController ? maxSize[wh] : contentRect[wh];
70108            mainRect[hw] = Math.max(contentRect[hw], controllerRect[hw]);
70109            // `containerRect[yx] + containerPos[1 - orientIdx]` is 0.
70110            mainRect[yx] = Math.min(0, controllerRect[yx] + controllerPos[1 - orientIdx]);
70111
70112            containerGroup.__rectSize = maxSize[wh];
70113            if (showController) {
70114                var clipShape = {x: 0, y: 0};
70115                clipShape[wh] = Math.max(maxSize[wh] - controllerRect[wh] - pageButtonGap, 0);
70116                clipShape[hw] = mainRect[hw];
70117                containerGroup.setClipPath(new Rect({shape: clipShape}));
70118                // Consider content may be larger than container, container rect
70119                // can not be obtained from `containerGroup.getBoundingRect()`.
70120                containerGroup.__rectSize = clipShape[wh];
70121            }
70122            else {
70123                // Do not remove or ignore controller. Keep them set as place holders.
70124                controllerGroup.eachChild(function (child) {
70125                    child.attr({invisible: true, silent: true});
70126                });
70127            }
70128
70129            // Content translate animation.
70130            var pageInfo = this._getPageInfo(legendModel);
70131            pageInfo.pageIndex != null && updateProps(
70132                contentGroup,
70133                {position: pageInfo.contentPosition},
70134                // When switch from "show controller" to "not show controller", view should be
70135                // updated immediately without animation, otherwise causes weird efffect.
70136                showController ? legendModel : false
70137            );
70138
70139            this._updatePageInfoView(legendModel, pageInfo);
70140
70141            return mainRect;
70142        },
70143
70144        _pageGo: function (to, legendModel, api) {
70145            var scrollDataIndex = this._getPageInfo(legendModel)[to];
70146
70147            scrollDataIndex != null && api.dispatchAction({
70148                type: 'legendScroll',
70149                scrollDataIndex: scrollDataIndex,
70150                legendId: legendModel.id
70151            });
70152        },
70153
70154        _updatePageInfoView: function (legendModel, pageInfo) {
70155            var controllerGroup = this._controllerGroup;
70156
70157            each$1(['pagePrev', 'pageNext'], function (name) {
70158                var canJump = pageInfo[name + 'DataIndex'] != null;
70159                var icon = controllerGroup.childOfName(name);
70160                if (icon) {
70161                    icon.setStyle(
70162                        'fill',
70163                        canJump
70164                            ? legendModel.get('pageIconColor', true)
70165                            : legendModel.get('pageIconInactiveColor', true)
70166                    );
70167                    icon.cursor = canJump ? 'pointer' : 'default';
70168                }
70169            });
70170
70171            var pageText = controllerGroup.childOfName('pageText');
70172            var pageFormatter = legendModel.get('pageFormatter');
70173            var pageIndex = pageInfo.pageIndex;
70174            var current = pageIndex != null ? pageIndex + 1 : 0;
70175            var total = pageInfo.pageCount;
70176
70177            pageText && pageFormatter && pageText.setStyle(
70178                'text',
70179                isString(pageFormatter)
70180                    ? pageFormatter.replace('{current}', current).replace('{total}', total)
70181                    : pageFormatter({current: current, total: total})
70182            );
70183        },
70184
70185        /**
70186         * @param {module:echarts/model/Model} legendModel
70187         * @return {Object} {
70188     *  contentPosition: Array.<number>, null when data item not found.
70189     *  pageIndex: number, null when data item not found.
70190     *  pageCount: number, always be a number, can be 0.
70191     *  pagePrevDataIndex: number, null when no next page.
70192     *  pageNextDataIndex: number, null when no previous page.
70193     * }
70194         */
70195        _getPageInfo: function (legendModel) {
70196            // Align left or top by the current dataIndex.
70197            var currDataIndex = legendModel.get('scrollDataIndex', true);
70198            var contentGroup = this.getContentGroup();
70199            var contentRect = contentGroup.getBoundingRect();
70200            var containerRectSize = this._containerGroup.__rectSize;
70201
70202            var orientIdx = legendModel.getOrient().index;
70203            var wh = WH$1[orientIdx];
70204            var hw = WH$1[1 - orientIdx];
70205            var xy = XY$1[orientIdx];
70206            var contentPos = contentGroup.position.slice();
70207
70208            var pageIndex;
70209            var pagePrevDataIndex;
70210            var pageNextDataIndex;
70211
70212            var targetItemGroup;
70213            if (this._showController) {
70214                contentGroup.eachChild(function (child) {
70215                    if (child.__legendDataIndex === currDataIndex) {
70216                        targetItemGroup = child;
70217                    }
70218                });
70219            }
70220            else {
70221                targetItemGroup = contentGroup.childAt(0);
70222            }
70223
70224            var pageCount = containerRectSize ? Math.ceil(contentRect[wh] / containerRectSize) : 0;
70225
70226            if (targetItemGroup) {
70227                var itemRect = targetItemGroup.getBoundingRect();
70228                var itemLoc = targetItemGroup.position[orientIdx] + itemRect[xy];
70229                contentPos[orientIdx] = -itemLoc - contentRect[xy];
70230                pageIndex = Math.floor(
70231                    pageCount * (itemLoc + itemRect[xy] + containerRectSize / 2) / contentRect[wh]
70232                );
70233                pageIndex = (contentRect[wh] && pageCount)
70234                    ? Math.max(0, Math.min(pageCount - 1, pageIndex))
70235                    : -1;
70236
70237                var winRect = {x: 0, y: 0};
70238                winRect[wh] = containerRectSize;
70239                winRect[hw] = contentRect[hw];
70240                winRect[xy] = -contentPos[orientIdx] - contentRect[xy];
70241
70242                var startIdx;
70243                var children = contentGroup.children();
70244
70245                contentGroup.eachChild(function (child, index) {
70246                    var itemRect = getItemRect(child);
70247
70248                    if (itemRect.intersect(winRect)) {
70249                        startIdx == null && (startIdx = index);
70250                        // It is user-friendly that the last item shown in the
70251                        // current window is shown at the begining of next window.
70252                        pageNextDataIndex = child.__legendDataIndex;
70253                    }
70254
70255                    // If the last item is shown entirely, no next page.
70256                    if (index === children.length - 1
70257                        && itemRect[xy] + itemRect[wh] <= winRect[xy] + winRect[wh]
70258                    ) {
70259                        pageNextDataIndex = null;
70260                    }
70261                });
70262
70263                // Always align based on the left/top most item, so the left/top most
70264                // item in the previous window is needed to be found here.
70265                if (startIdx != null) {
70266                    var startItem = children[startIdx];
70267                    var startRect = getItemRect(startItem);
70268                    winRect[xy] = startRect[xy] + startRect[wh] - winRect[wh];
70269
70270                    // If the first item is shown entirely, no previous page.
70271                    if (startIdx <= 0 && startRect[xy] >= winRect[xy]) {
70272                        pagePrevDataIndex = null;
70273                    }
70274                    else {
70275                        while (startIdx > 0 && getItemRect(children[startIdx - 1]).intersect(winRect)) {
70276                            startIdx--;
70277                        }
70278                        pagePrevDataIndex = children[startIdx].__legendDataIndex;
70279                    }
70280                }
70281            }
70282
70283            return {
70284                contentPosition: contentPos,
70285                pageIndex: pageIndex,
70286                pageCount: pageCount,
70287                pagePrevDataIndex: pagePrevDataIndex,
70288                pageNextDataIndex: pageNextDataIndex
70289            };
70290
70291            function getItemRect(el) {
70292                var itemRect = el.getBoundingRect().clone();
70293                itemRect[xy] += el.position[orientIdx];
70294                return itemRect;
70295            }
70296        }
70297
70298    });
70299
70300    /*
70301* Licensed to the Apache Software Foundation (ASF) under one
70302* or more contributor license agreements.  See the NOTICE file
70303* distributed with this work for additional information
70304* regarding copyright ownership.  The ASF licenses this file
70305* to you under the Apache License, Version 2.0 (the
70306* "License"); you may not use this file except in compliance
70307* with the License.  You may obtain a copy of the License at
70308*
70309*   http://www.apache.org/licenses/LICENSE-2.0
70310*
70311* Unless required by applicable law or agreed to in writing,
70312* software distributed under the License is distributed on an
70313* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
70314* KIND, either express or implied.  See the License for the
70315* specific language governing permissions and limitations
70316* under the License.
70317*/
70318
70319    /**
70320     * @event legendScroll
70321     * @type {Object}
70322     * @property {string} type 'legendScroll'
70323     * @property {string} scrollDataIndex
70324     */
70325    registerAction(
70326        'legendScroll', 'legendscroll',
70327        function (payload, ecModel) {
70328            var scrollDataIndex = payload.scrollDataIndex;
70329
70330            scrollDataIndex != null && ecModel.eachComponent(
70331                {mainType: 'legend', subType: 'scroll', query: payload},
70332                function (legendModel) {
70333                    legendModel.setScrollDataIndex(scrollDataIndex);
70334                }
70335            );
70336        }
70337    );
70338
70339    /*
70340* Licensed to the Apache Software Foundation (ASF) under one
70341* or more contributor license agreements.  See the NOTICE file
70342* distributed with this work for additional information
70343* regarding copyright ownership.  The ASF licenses this file
70344* to you under the Apache License, Version 2.0 (the
70345* "License"); you may not use this file except in compliance
70346* with the License.  You may obtain a copy of the License at
70347*
70348*   http://www.apache.org/licenses/LICENSE-2.0
70349*
70350* Unless required by applicable law or agreed to in writing,
70351* software distributed under the License is distributed on an
70352* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
70353* KIND, either express or implied.  See the License for the
70354* specific language governing permissions and limitations
70355* under the License.
70356*/
70357
70358    /**
70359     * Legend component entry file8
70360     */
70361
70362    /*
70363* Licensed to the Apache Software Foundation (ASF) under one
70364* or more contributor license agreements.  See the NOTICE file
70365* distributed with this work for additional information
70366* regarding copyright ownership.  The ASF licenses this file
70367* to you under the Apache License, Version 2.0 (the
70368* "License"); you may not use this file except in compliance
70369* with the License.  You may obtain a copy of the License at
70370*
70371*   http://www.apache.org/licenses/LICENSE-2.0
70372*
70373* Unless required by applicable law or agreed to in writing,
70374* software distributed under the License is distributed on an
70375* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
70376* KIND, either express or implied.  See the License for the
70377* specific language governing permissions and limitations
70378* under the License.
70379*/
70380
70381    extendComponentModel({
70382
70383        type: 'tooltip',
70384
70385        dependencies: ['axisPointer'],
70386
70387        defaultOption: {
70388            zlevel: 0,
70389
70390            z: 8,
70391
70392            show: true,
70393
70394            // tooltip主体内容
70395            showContent: true,
70396
70397            // 'trigger' only works on coordinate system.
70398            // 'item' | 'axis' | 'none'
70399            trigger: 'item',
70400
70401            // 'click' | 'mousemove' | 'none'
70402            triggerOn: 'mousemove|click',
70403
70404            alwaysShowContent: false,
70405
70406            displayMode: 'single', // 'single' | 'multipleByCoordSys'
70407
70408            // 位置 {Array} | {Function}
70409            // position: null
70410            // Consider triggered from axisPointer handle, verticalAlign should be 'middle'
70411            // align: null,
70412            // verticalAlign: null,
70413
70414            // 是否约束 content 在 viewRect 中。默èÂ
70414®Â¤ false 是为了兼容以前版本。
70415            confine: false,
70416
70417            // å†…å®¹æ ¼å¼å™¨ï¼š{string}(Template) ¦ {Function}
70418            // formatter: null
70419
70420            showDelay: 0,
70421
70422            // 隐藏延迟,单位ms
70423            hideDelay: 100,
70424
70425            // 动画变换时间,单位s
70426            transitionDuration: 0.4,
70427
70428            enterable: false,
70429
70430            // 提示背景颜色,默认为透明度为0.7的黑色
70431            backgroundColor: 'rgba(50,50,50,0.7)',
70432
70433            // 提示边框颜色
70434            borderColor: '#333',
70435
70436            // 提示边框圆角,单位px,默认为4
70437            borderRadius: 4,
70438
70439            // 提示边框线宽,单位px,默认为0ï¼ˆæ— è¾¹æ¡†ï¼‰
70440            borderWidth: 0,
70441
70442            // 提示内边距,单位px,默认各方向内边距为5,
70443            // 接受数组分别设定上右下左边距,同css
70444            padding: 5,
70445
70446            // Extra css text
70447            extraCssText: '',
70448
70449            // åæ ‡è½´æŒ‡ç¤ºå™¨ï¼Œåæ ‡è½´è§¦å‘æœ‰æ•ˆ
70450            axisPointer: {
70451                // 默认为直线
70452                // 可选为:'line' | 'shadow' | 'cross'
70453                type: 'line',
70454
70455                // type 为 line 的时候有效,指定 tooltip line 所在的轴,可选
70456                // 可选 'x' | 'y' | 'angle' | 'radius' | 'auto'
70457                // 默认 'auto',会选择类型为 category çš„è½´ï¼Œå¯¹äºŽåŒæ•°å€¼è½´ï¼Œç¬›å¡å°”åæ ‡ç³»ä¼šé»˜è®¤é€‰æ‹© x è½´
70458                // æžåæ ‡ç³»ä¼šé»˜è®¤é€‰æ‹© angle è½´
70459                axis: 'auto',
70460
70461                animation: 'auto',
70462                animationDurationUpdate: 200,
70463                animationEasingUpdate: 'exponentialOut',
70464
70465                crossStyle: {
70466                    color: '#999',
70467                    width: 1,
70468                    type: 'dashed',
70469
70470                    // TODO formatter
70471                    textStyle: {}
70472                }
70473
70474                // lineStyle and shadowStyle should not be specified here,
70475                // otherwise it will always override those styles on option.axisPointer.
70476            },
70477            textStyle: {
70478                color: '#fff',
70479                fontSize: 14
70480            }
70481        }
70482    });
70483
70484    /*
70485* Licensed to the Apache Software Foundation (ASF) under one
70486* or more contributor license agreements.  See the NOTICE file
70487* distributed with this work for additional information
70488* regarding copyright ownership.  The ASF licenses this file
70489* to you under the Apache License, Version 2.0 (the
70490* "License"); you may not use this file except in compliance
70491* with the License.  You may obtain a copy of the License at
70492*
70493*   http://www.apache.org/licenses/LICENSE-2.0
70494*
70495* Unless required by applicable law or agreed to in writing,
70496* software distributed under the License is distributed on an
70497* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
70498* KIND, either express or implied.  See the License for the
70499* specific language governing permissions and limitations
70500* under the License.
70501*/
70502
70503    var each$18 = each$1;
70504    var toCamelCase$1 = toCamelCase;
70505
70506    var vendors = ['', '-webkit-', '-moz-', '-o-'];
70507
70508    var gCssText = 'position:absolute;display:block;border-style:solid;white-space:nowrap;z-index:9999999;';
70509
70510    /**
70511     * @param {number} duration
70512     * @return {string}
70513     * @inner
70514     */
70515    function assembleTransition(duration) {
70516        var transitionCurve = 'cubic-bezier(0.23, 1, 0.32, 1)';
70517        var transitionText = 'left ' + duration + 's ' + transitionCurve + ','
70518            + 'top ' + duration + 's ' + transitionCurve;
70519        return map(vendors, function (vendorPrefix) {
70520            return vendorPrefix + 'transition:' + transitionText;
70521        }).join(';');
70522    }
70523
70524    /**
70525     * @param {Object} textStyle
70526     * @return {string}
70527     * @inner
70528     */
70529    function assembleFont(textStyleModel) {
70530        var cssText = [];
70531
70532        var fontSize = textStyleModel.get('fontSize');
70533        var color = textStyleModel.getTextColor();
70534
70535        color && cssText.push('color:' + color);
70536
70537        cssText.push('font:' + textStyleModel.getFont());
70538
70539        fontSize &&
70540        cssText.push('line-height:' + Math.round(fontSize * 3 / 2) + 'px');
70541
70542        each$18(['decoration', 'align'], function (name) {
70543            var val = textStyleModel.get(name);
70544            val && cssText.push('text-' + name + ':' + val);
70545        });
70546
70547        return cssText.join(';');
70548    }
70549
70550    /**
70551     * @param {Object} tooltipModel
70552     * @return {string}
70553     * @inner
70554     */
70555    function assembleCssText(tooltipModel) {
70556
70557        var cssText = [];
70558
70559        var transitionDuration = tooltipModel.get('transitionDuration');
70560        var backgroundColor = tooltipModel.get('backgroundColor');
70561        var textStyleModel = tooltipModel.getModel('textStyle');
70562        var padding = tooltipModel.get('padding');
70563
70564        // Animation transition. Do not animate when transitionDuration is 0.
70565        transitionDuration &&
70566        cssText.push(assembleTransition(transitionDuration));
70567
70568        if (backgroundColor) {
70569            if (env$1.canvasSupported) {
70570                cssText.push('background-Color:' + backgroundColor);
70571            }
70572            else {
70573                // for ie
70574                cssText.push(
70575                    'background-Color:#' + toHex(backgroundColor)
70576                );
70577                cssText.push('filter:alpha(opacity=70)');
70578            }
70579        }
70580
70581        // Border style
70582        each$18(['width', 'color', 'radius'], function (name) {
70583            var borderName = 'border-' + name;
70584            var camelCase = toCamelCase$1(borderName);
70585            var val = tooltipModel.get(camelCase);
70586            val != null &&
70587            cssText.push(borderName + ':' + val + (name === 'color' ? '' : 'px'));
70588        });
70589
70590        // Text style
70591        cssText.push(assembleFont(textStyleModel));
70592
70593        // Padding
70594        if (padding != null) {
70595            cssText.push('padding:' + normalizeCssArray$1(padding).join('px ') + 'px');
70596        }
70597
70598        return cssText.join(';') + ';';
70599    }
70600
70601    /**
70602     * @alias module:echarts/component/tooltip/TooltipContent
70603     * @constructor
70604     */
70605    function TooltipContent(container, api) {
70606        if (env$1.wxa) {
70607            return null;
70608        }
70609
70610        var el = document.createElement('div');
70611        var zr = this._zr = api.getZr();
70612
70613        this.el = el;
70614
70615        this._x = api.getWidth() / 2;
70616        this._y = api.getHeight() / 2;
70617
70618        container.appendChild(el);
70619
70620        this._container = container;
70621
70622        this._show = false;
70623
70624        /**
70625         * @private
70626         */
70627        this._hideTimeout;
70628
70629        var self = this;
70630        el.onmouseenter = function () {
70631            // clear the timeout in hideLater and keep showing tooltip
70632            if (self._enterable) {
70633                clearTimeout(self._hideTimeout);
70634                self._show = true;
70635            }
70636            self._inContent = true;
70637        };
70638        el.onmousemove = function (e) {
70639            e = e || window.event;
70640            if (!self._enterable) {
70641                // Try trigger zrender event to avoid mouse
70642                // in and out shape too frequently
70643                var handler = zr.handler;
70644                normalizeEvent(container, e, true);
70645                handler.dispatch('mousemove', e);
70646            }
70647        };
70648        el.onmouseleave = function () {
70649            if (self._enterable) {
70650                if (self._show) {
70651                    self.hideLater(self._hideDelay);
70652                }
70653            }
70654            self._inContent = false;
70655        };
70656    }
70657
70658    TooltipContent.prototype = {
70659
70660        constructor: TooltipContent,
70661
70662        /**
70663         * @private
70664         * @type {boolean}
70665         */
70666        _enterable: true,
70667
70668        /**
70669         * Update when tooltip is rendered
70670         */
70671        update: function () {
70672            // FIXME
70673            // Move this logic to ec main?
70674            var container = this._container;
70675            var stl = container.currentStyle
70676                || document.defaultView.getComputedStyle(container);
70677            var domStyle = container.style;
70678            if (domStyle.position !== 'absolute' && stl.position !== 'absolute') {
70679                domStyle.position = 'relative';
70680            }
70681            // Hide the tooltip
70682            // PENDING
70683            // this.hide();
70684        },
70685
70686        show: function (tooltipModel) {
70687            clearTimeout(this._hideTimeout);
70688            var el = this.el;
70689
70690            el.style.cssText = gCssText + assembleCssText(tooltipModel)
70691                // http://stackoverflow.com/questions/21125587/css3-transition-not-working-in-chrome-anymore
70692                + ';left:' + this._x + 'px;top:' + this._y + 'px;'
70693                + (tooltipModel.get('extraCssText') || '');
70694
70695            el.style.display = el.innerHTML ?  'block' : 'none';
70696
70697            this._show = true;
70698        },
70699
70700        setContent: function (content) {
70701            this.el.innerHTML = content == null ? '' : content;
70702        },
70703
70704        setEnterable: function (enterable) {
70705            this._enterable = enterable;
70706        },
70707
70708        getSize: function () {
70709            var el = this.el;
70710            return [el.clientWidth, el.clientHeight];
70711        },
70712
70713        moveTo: function (x, y) {
70714            // xy should be based on canvas root. But tooltipContent is
70715            // the sibling of canvas root. So padding of ec container
70716            // should be considered here.
70717            var zr = this._zr;
70718            var viewportRootOffset;
70719            if (zr && zr.painter && (viewportRootOffset = zr.painter.getViewportRootOffset())) {
70720                x += viewportRootOffset.offsetLeft;
70721                y += viewportRootOffset.offsetTop;
70722            }
70723
70724            var style = this.el.style;
70725            style.left = x + 'px';
70726            style.top = y + 'px';
70727
70728            this._x = x;
70729            this._y = y;
70730        },
70731
70732        hide: function () {
70733            this.el.style.display = 'none';
70734            this._show = false;
70735        },
70736
70737        hideLater: function (time) {
70738            if (this._show && !(this._inContent && this._enterable)) {
70739                if (time) {
70740                    this._hideDelay = time;
70741                    // Set show false to avoid invoke hideLater mutiple times
70742                    this._show = false;
70743                    this._hideTimeout = setTimeout(bind(this.hide, this), time);
70744                }
70745                else {
70746                    this.hide();
70747                }
70748            }
70749        },
70750
70751        isShow: function () {
70752            return this._show;
70753        }
70754    };
70755
70756    /*
70757* Licensed to the Apache Software Foundation (ASF) under one
70758* or more contributor license agreements.  See the NOTICE file
70759* distributed with this work for additional information
70760* regarding copyright ownership.  The ASF licenses this file
70761* to you under the Apache License, Version 2.0 (the
70762* "License"); you may not use this file except in compliance
70763* with the License.  You may obtain a copy of the License at
70764*
70765*   http://www.apache.org/licenses/LICENSE-2.0
70766*
70767* Unless required by applicable law or agreed to in writing,
70768* software distributed under the License is distributed on an
70769* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
70770* KIND, either express or implied.  See the License for the
70771* specific language governing permissions and limitations
70772* under the License.
70773*/
70774
70775    var bind$3 = bind;
70776    var each$17 = each$1;
70777    var parsePercent$2 = parsePercent$1;
70778
70779    var proxyRect = new Rect({
70780        shape: {x: -1, y: -1, width: 2, height: 2}
70781    });
70782
70783    extendComponentView({
70784
70785        type: 'tooltip',
70786
70787        init: function (ecModel, api) {
70788            if (env$1.node) {
70789                return;
70790            }
70791            var tooltipContent = new TooltipContent(api.getDom(), api);
70792            this._tooltipContent = tooltipContent;
70793        },
70794
70795        render: function (tooltipModel, ecModel, api) {
70796            if (env$1.node || env$1.wxa) {
70797                return;
70798            }
70799
70800            // Reset
70801            this.group.removeAll();
70802
70803            /**
70804             * @private
70805             * @type {module:echarts/component/tooltip/TooltipModel}
70806             */
70807            this._tooltipModel = tooltipModel;
70808
70809            /**
70810             * @private
70811             * @type {module:echarts/model/Global}
70812             */
70813            this._ecModel = ecModel;
70814
70815            /**
70816             * @private
70817             * @type {module:echarts/ExtensionAPI}
70818             */
70819            this._api = api;
70820
70821            /**
70822             * Should be cleaned when render.
70823             * @private
70824             * @type {Array.<Array.<Object>>}
70825             */
70826            this._lastDataByCoordSys = null;
70827
70828            /**
70829             * @private
70830             * @type {boolean}
70831             */
70832            this._alwaysShowContent = tooltipModel.get('alwaysShowContent');
70833
70834            var tooltipContent = this._tooltipContent;
70835            tooltipContent.update();
70836            tooltipContent.setEnterable(tooltipModel.get('enterable'));
70837
70838            this._initGlobalListener();
70839
70840            this._keepShow();
70841        },
70842
70843        _initGlobalListener: function () {
70844            var tooltipModel = this._tooltipModel;
70845            var triggerOn = tooltipModel.get('triggerOn');
70846
70847            register(
70848                'itemTooltip',
70849                this._api,
70850                bind$3(function (currTrigger, e, dispatchAction) {
70851                    // If 'none', it is not controlled by mouse totally.
70852                    if (triggerOn !== 'none') {
70853                        if (triggerOn.indexOf(currTrigger) >= 0) {
70854                            this._tryShow(e, dispatchAction);
70855                        }
70856                        else if (currTrigger === 'leave') {
70857                            this._hide(dispatchAction);
70858                        }
70859                    }
70860                }, this)
70861            );
70862        },
70863
70864        _keepShow: function () {
70865            var tooltipModel = this._tooltipModel;
70866            var ecModel = this._ecModel;
70867            var api = this._api;
70868
70869            // Try to keep the tooltip show when refreshing
70870            if (this._lastX != null
70871                && this._lastY != null
70872                // When user is willing to control tooltip totally using API,
70873                // self.manuallyShowTip({x, y}) might cause tooltip hide,
70874                // which is not expected.
70875                && tooltipModel.get('triggerOn') !== 'none'
70876            ) {
70877                var self = this;
70878                clearTimeout(this._refreshUpdateTimeout);
70879                this._refreshUpdateTimeout = setTimeout(function () {
70880                    // Show tip next tick after other charts are rendered
70881                    // In case highlight action has wrong result
70882                    // FIXME
70883                    self.manuallyShowTip(tooltipModel, ecModel, api, {
70884                        x: self._lastX,
70885                        y: self._lastY
70886                    });
70887                });
70888            }
70889        },
70890
70891        /**
70892         * Show tip manually by
70893         * dispatchAction({
70894     *     type: 'showTip',
70895     *     x: 10,
70896     *     y: 10
70897     * });
70898         * Or
70899         * dispatchAction({
70900     *      type: 'showTip',
70901     *      seriesIndex: 0,
70902     *      dataIndex or dataIndexInside or name
70903     * });
70904         *
70905         *  TODO Batch
70906         */
70907        manuallyShowTip: function (tooltipModel, ecModel, api, payload) {
70908            if (payload.from === this.uid || env$1.node) {
70909                return;
70910            }
70911
70912            var dispatchAction = makeDispatchAction$1(payload, api);
70913
70914            // Reset ticket
70915            this._ticket = '';
70916
70917            // When triggered from axisPointer.
70918            var dataByCoordSys = payload.dataByCoordSys;
70919
70920            if (payload.tooltip && payload.x != null && payload.y != null) {
70921                var el = proxyRect;
70922                el.position = [payload.x, payload.y];
70923                el.update();
70924                el.tooltip = payload.tooltip;
70925                // Manually show tooltip while view is not using zrender elements.
70926                this._tryShow({
70927                    offsetX: payload.x,
70928                    offsetY: payload.y,
70929                    target: el
70930                }, dispatchAction);
70931            }
70932            else if (dataByCoordSys) {
70933                this._tryShow({
70934                    offsetX: payload.x,
70935                    offsetY: payload.y,
70936                    position: payload.position,
70937                    event: {},
70938                    dataByCoordSys: payload.dataByCoordSys,
70939                    tooltipOption: payload.tooltipOption
70940                }, dispatchAction);
70941            }
70942            else if (payload.seriesIndex != null) {
70943
70944                if (this._manuallyAxisShowTip(tooltipModel, ecModel, api, payload)) {
70945                    return;
70946                }
70947
70948                var pointInfo = findPointFromSeries(payload, ecModel);
70949                var cx = pointInfo.point[0];
70950                var cy = pointInfo.point[1];
70951                if (cx != null && cy != null) {
70952                    this._tryShow({
70953                        offsetX: cx,
70954                        offsetY: cy,
70955                        position: payload.position,
70956                        target: pointInfo.el,
70957                        event: {}
70958                    }, dispatchAction);
70959                }
70960            }
70961            else if (payload.x != null && payload.y != null) {
70962                // FIXME
70963                // should wrap dispatchAction like `axisPointer/globalListener` ?
70964                api.dispatchAction({
70965                    type: 'updateAxisPointer',
70966                    x: payload.x,
70967                    y: payload.y
70968                });
70969
70970                this._tryShow({
70971                    offsetX: payload.x,
70972                    offsetY: payload.y,
70973                    position: payload.position,
70974                    target: api.getZr().findHover(payload.x, payload.y).target,
70975                    event: {}
70976                }, dispatchAction);
70977            }
70978        },
70979
70980        manuallyHideTip: function (tooltipModel, ecModel, api, payload) {
70981            var tooltipContent = this._tooltipContent;
70982
70983            if (!this._alwaysShowContent && this._tooltipModel) {
70984                tooltipContent.hideLater(this._tooltipModel.get('hideDelay'));
70985            }
70986
70987            this._lastX = this._lastY = null;
70988
70989            if (payload.from !== this.uid) {
70990                this._hide(makeDispatchAction$1(payload, api));
70991            }
70992        },
70993
70994        // Be compatible with previous design, that is, when tooltip.type is 'axis' and
70995        // dispatchAction 'showTip' with seriesIndex and dataIndex will trigger axis pointer
70996        // and tooltip.
70997        _manuallyAxisShowTip: function (tooltipModel, ecModel, api, payload) {
70998            var seriesIndex = payload.seriesIndex;
70999            var dataIndex = payload.dataIndex;
71000            var coordSysAxesInfo = ecModel.getComponent('axisPointer').coordSysAxesInfo;
71001
71002            if (seriesIndex == null || dataIndex == null || coordSysAxesInfo == null) {
71003                return;
71004            }
71005
71006            var seriesModel = ecModel.getSeriesByIndex(seriesIndex);
71007            if (!seriesModel) {
71008                return;
71009            }
71010
71011            var data = seriesModel.getData();
71012            var tooltipModel = buildTooltipModel([
71013                data.getItemModel(dataIndex),
71014                seriesModel,
71015                (seriesModel.coordinateSystem || {}).model,
71016                tooltipModel
71017            ]);
71018
71019            if (tooltipModel.get('trigger') !== 'axis') {
71020                return;
71021            }
71022
71023            api.dispatchAction({
71024                type: 'updateAxisPointer',
71025                seriesIndex: seriesIndex,
71026                dataIndex: dataIndex,
71027                position: payload.position
71028            });
71029
71030            return true;
71031        },
71032
71033        _tryShow: function (e, dispatchAction) {
71034            var el = e.target;
71035            var tooltipModel = this._tooltipModel;
71036
71037            if (!tooltipModel) {
71038                return;
71039            }
71040
71041            // Save mouse x, mouse y. So we can try to keep showing the tip if chart is refreshed
71042            this._lastX = e.offsetX;
71043            this._lastY = e.offsetY;
71044
71045            var dataByCoordSys = e.dataByCoordSys;
71046            if (dataByCoordSys && dataByCoordSys.length) {
71047                this._showAxisTooltip(dataByCoordSys, e);
71048            }
71049            // Always show item tooltip if mouse is on the element with dataIndex
71050            else if (el && el.dataIndex != null) {
71051                this._lastDataByCoordSys = null;
71052                this._showSeriesItemTooltip(e, el, dispatchAction);
71053            }
71054            // Tooltip provided directly. Like legend.
71055            else if (el && el.tooltip) {
71056                this._lastDataByCoordSys = null;
71057                this._showComponentItemTooltip(e, el, dispatchAction);
71058            }
71059            else {
71060                this._lastDataByCoordSys = null;
71061                this._hide(dispatchAction);
71062            }
71063        },
71064
71065        _showOrMove: function (tooltipModel, cb) {
71066            // showDelay is used in this case: tooltip.enterable is set
71067            // as true. User intent to move mouse into tooltip and click
71068            // something. `showDelay` makes it easyer to enter the content
71069            // but tooltip do not move immediately.
71070            var delay = tooltipModel.get('showDelay');
71071            cb = bind(cb, this);
71072            clearTimeout(this._showTimout);
71073            delay > 0
71074                ? (this._showTimout = setTimeout(cb, delay))
71075                : cb();
71076        },
71077
71078        _showAxisTooltip: function (dataByCoordSys, e) {
71079            var ecModel = this._ecModel;
71080            var globalTooltipModel = this._tooltipModel;
71081            var point = [e.offsetX, e.offsetY];
71082            var singleDefaultHTML = [];
71083            var singleParamsList = [];
71084            var singleTooltipModel = buildTooltipModel([
71085                e.tooltipOption,
71086                globalTooltipModel
71087            ]);
71088
71089            each$17(dataByCoordSys, function (itemCoordSys) {
71090                // var coordParamList = [];
71091                // var coordDefaultHTML = [];
71092                // var coordTooltipModel = buildTooltipModel([
71093                //     e.tooltipOption,
71094                //     itemCoordSys.tooltipOption,
71095                //     ecModel.getComponent(itemCoordSys.coordSysMainType, itemCoordSys.coordSysIndex),
71096                //     globalTooltipModel
71097                // ]);
71098                // var displayMode = coordTooltipModel.get('displayMode');
71099                // var paramsList = displayMode === 'single' ? singleParamsList : [];
71100
71101                each$17(itemCoordSys.dataByAxis, function (item) {
71102                    var axisModel = ecModel.getComponent(item.axisDim + 'Axis', item.axisIndex);
71103                    var axisValue = item.value;
71104                    var seriesDefaultHTML = [];
71105
71106                    if (!axisModel || axisValue == null) {
71107                        return;
71108                    }
71109
71110                    var valueLabel = getValueLabel(
71111                        axisValue, axisModel.axis, ecModel,
71112                        item.seriesDataIndices,
71113                        item.valueLabelOpt
71114                    );
71115
71116                    each$1(item.seriesDataIndices, function (idxItem) {
71117                        var series = ecModel.getSeriesByIndex(idxItem.seriesIndex);
71118                        var dataIndex = idxItem.dataIndexInside;
71119                        var dataParams = series && series.getDataParams(dataIndex);
71120                        dataParams.axisDim = item.axisDim;
71121                        dataParams.axisIndex = item.axisIndex;
71122                        dataParams.axisType = item.axisType;
71123                        dataParams.axisId = item.axisId;
71124                        dataParams.axisValue = getAxisRawValue(axisModel.axis, axisValue);
71125                        dataParams.axisValueLabel = valueLabel;
71126
71127                        if (dataParams) {
71128                            singleParamsList.push(dataParams);
71129                            seriesDefaultHTML.push(series.formatTooltip(dataIndex, true));
71130                        }
71131                    });
71132
71133                    // Default tooltip content
71134                    // FIXME
71135                    // (1) shold be the first data which has name?
71136                    // (2) themeRiver, firstDataIndex is array, and first line is unnecessary.
71137                    var firstLine = valueLabel;
71138                    singleDefaultHTML.push(
71139                        (firstLine ? encodeHTML(firstLine) + '<br />' : '')
71140                        + seriesDefaultHTML.join('<br />')
71141                    );
71142                });
71143            }, this);
71144
71145            // In most case, the second axis is shown upper than the first one.
71146            singleDefaultHTML.reverse();
71147            singleDefaultHTML = singleDefaultHTML.join('<br /><br />');
71148
71149            var positionExpr = e.position;
71150            this._showOrMove(singleTooltipModel, function () {
71151                if (this._updateContentNotChangedOnAxis(dataByCoordSys)) {
71152                    this._updatePosition(
71153                        singleTooltipModel,
71154                        positionExpr,
71155                        point[0], point[1],
71156                        this._tooltipContent,
71157                        singleParamsList
71158                    );
71159                }
71160                else {
71161                    this._showTooltipContent(
71162                        singleTooltipModel, singleDefaultHTML, singleParamsList, Math.random(),
71163                        point[0], point[1], positionExpr
71164                    );
71165                }
71166            });
71167
71168            // Do not trigger events here, because this branch only be entered
71169            // from dispatchAction.
71170        },
71171
71172        _showSeriesItemTooltip: function (e, el, dispatchAction) {
71173            var ecModel = this._ecModel;
71174            // Use dataModel in element if possible
71175            // Used when mouseover on a element like markPoint or edge
71176            // In which case, the data is not main data in series.
71177            var seriesIndex = el.seriesIndex;
71178            var seriesModel = ecModel.getSeriesByIndex(seriesIndex);
71179
71180            // For example, graph link.
71181            var dataModel = el.dataModel || seriesModel;
71182            var dataIndex = el.dataIndex;
71183            var dataType = el.dataType;
71184            var data = dataModel.getData();
71185
71186            var tooltipModel = buildTooltipModel([
71187                data.getItemModel(dataIndex),
71188                dataModel,
71189                seriesModel && (seriesModel.coordinateSystem || {}).model,
71190                this._tooltipModel
71191            ]);
71192
71193            var tooltipTrigger = tooltipModel.get('trigger');
71194            if (tooltipTrigger != null && tooltipTrigger !== 'item') {
71195                return;
71196            }
71197
71198            var params = dataModel.getDataParams(dataIndex, dataType);
71199            var defaultHtml = dataModel.formatTooltip(dataIndex, false, dataType);
71200            var asyncTicket = 'item_' + dataModel.name + '_' + dataIndex;
71201
71202            this._showOrMove(tooltipModel, function () {
71203                this._showTooltipContent(
71204                    tooltipModel, defaultHtml, params, asyncTicket,
71205                    e.offsetX, e.offsetY, e.position, e.target
71206                );
71207            });
71208
71209            // FIXME
71210            // duplicated showtip if manuallyShowTip is called from dispatchAction.
71211            dispatchAction({
71212                type: 'showTip',
71213                dataIndexInside: dataIndex,
71214                dataIndex: data.getRawIndex(dataIndex),
71215                seriesIndex: seriesIndex,
71216                from: this.uid
71217            });
71218        },
71219
71220        _showComponentItemTooltip: function (e, el, dispatchAction) {
71221            var tooltipOpt = el.tooltip;
71222            if (typeof tooltipOpt === 'string') {
71223                var content = tooltipOpt;
71224                tooltipOpt = {
71225                    content: content,
71226                    // Fixed formatter
71227                    formatter: content
71228                };
71229            }
71230            var subTooltipModel = new Model(tooltipOpt, this._tooltipModel, this._ecModel);
71231            var defaultHtml = subTooltipModel.get('content');
71232            var asyncTicket = Math.random();
71233
71234            // Do not check whether `trigger` is 'none' here, because `trigger`
71235            // only works on cooridinate system. In fact, we have not found case
71236            // that requires setting `trigger` nothing on component yet.
71237
71238            this._showOrMove(subTooltipModel, function () {
71239                this._showTooltipContent(
71240                    subTooltipModel, defaultHtml, subTooltipModel.get('formatterParams') || {},
71241                    asyncTicket, e.offsetX, e.offsetY, e.position, el
71242                );
71243            });
71244
71245            // If not dispatch showTip, tip may be hide triggered by axis.
71246            dispatchAction({
71247                type: 'showTip',
71248                from: this.uid
71249            });
71250        },
71251
71252        _showTooltipContent: function (
71253            tooltipModel, defaultHtml, params, asyncTicket, x, y, positionExpr, el
71254        ) {
71255            // Reset ticket
71256            this._ticket = '';
71257
71258            if (!tooltipModel.get('showContent') || !tooltipModel.get('show')) {
71259                return;
71260            }
71261
71262            var tooltipContent = this._tooltipContent;
71263
71264            var formatter = tooltipModel.get('formatter');
71265            positionExpr = positionExpr || tooltipModel.get('position');
71266            var html = defaultHtml;
71267
71268            if (formatter && typeof formatter === 'string') {
71269                html = formatTpl(formatter, params, true);
71270            }
71271            else if (typeof formatter === 'function') {
71272                var callback = bind$3(function (cbTicket, html) {
71273                    if (cbTicket === this._ticket) {
71274                        tooltipContent.setContent(html);
71275                        this._updatePosition(
71276                            tooltipModel, positionExpr, x, y, tooltipContent, params, el
71277                        );
71278                    }
71279                }, this);
71280                this._ticket = asyncTicket;
71281                html = formatter(params, asyncTicket, callback);
71282            }
71283
71284            tooltipContent.setContent(html);
71285            tooltipContent.show(tooltipModel);
71286
71287            this._updatePosition(
71288                tooltipModel, positionExpr, x, y, tooltipContent, params, el
71289            );
71290        },
71291
71292        /**
71293         * @param  {string|Function|Array.<number>|Object} positionExpr
71294         * @param  {number} x Mouse x
71295         * @param  {number} y Mouse y
71296         * @param  {boolean} confine Whether confine tooltip content in view rect.
71297         * @param  {Object|<Array.<Object>} params
71298         * @param  {module:zrender/Element} el target element
71299         * @param  {module:echarts/ExtensionAPI} api
71300         * @return {Array.<number>}
71301         */
71302        _updatePosition: function (tooltipModel, positionExpr, x, y, content, params, el) {
71303            var viewWidth = this._api.getWidth();
71304            var viewHeight = this._api.getHeight();
71305            positionExpr = positionExpr || tooltipModel.get('position');
71306
71307            var contentSize = content.getSize();
71308            var align = tooltipModel.get('align');
71309            var vAlign = tooltipModel.get('verticalAlign');
71310            var rect = el && el.getBoundingRect().clone();
71311            el && rect.applyTransform(el.transform);
71312
71313            if (typeof positionExpr === 'function') {
71314                // Callback of position can be an array or a string specify the position
71315                positionExpr = positionExpr([x, y], params, content.el, rect, {
71316                    viewSize: [viewWidth, viewHeight],
71317                    contentSize: contentSize.slice()
71318                });
71319            }
71320
71321            if (isArray(positionExpr)) {
71322                x = parsePercent$2(positionExpr[0], viewWidth);
71323                y = parsePercent$2(positionExpr[1], viewHeight);
71324            }
71325            else if (isObject$1(positionExpr)) {
71326                positionExpr.width = contentSize[0];
71327                positionExpr.height = contentSize[1];
71328                var layoutRect = getLayoutRect(
71329                    positionExpr, {width: viewWidth, height: viewHeight}
71330                );
71331                x = layoutRect.x;
71332                y = layoutRect.y;
71333                align = null;
71334                // When positionExpr is left/top/right/bottom,
71335                // align and verticalAlign will not work.
71336                vAlign = null;
71337            }
71338            // Specify tooltip position by string 'top' 'bottom' 'left' 'right' around graphic element
71339            else if (typeof positionExpr === 'string' && el) {
71340                var pos = calcTooltipPosition(
71341                    positionExpr, rect, contentSize
71342                );
71343                x = pos[0];
71344                y = pos[1];
71345            }
71346            else {
71347                var pos = refixTooltipPosition(
71348                    x, y, content.el, viewWidth, viewHeight, align ? null : 20, vAlign ? null : 20
71349                );
71350                x = pos[0];
71351                y = pos[1];
71352            }
71353
71354            align && (x -= isCenterAlign(align) ? contentSize[0] / 2 : align === 'right' ? contentSize[0] : 0);
71355            vAlign && (y -= isCenterAlign(vAlign) ? contentSize[1] / 2 : vAlign === 'bottom' ? contentSize[1] : 0);
71356
71357            if (tooltipModel.get('confine')) {
71358                var pos = confineTooltipPosition(
71359                    x, y, content.el, viewWidth, viewHeight
71360                );
71361                x = pos[0];
71362                y = pos[1];
71363            }
71364
71365            content.moveTo(x, y);
71366        },
71367
71368        // FIXME
71369        // Should we remove this but leave this to user?
71370        _updateContentNotChangedOnAxis: function (dataByCoordSys) {
71371            var lastCoordSys = this._lastDataByCoordSys;
71372            var contentNotChanged = !!lastCoordSys
71373                && lastCoordSys.length === dataByCoordSys.length;
71374
71375            contentNotChanged && each$17(lastCoordSys, function (lastItemCoordSys, indexCoordSys) {
71376                var lastDataByAxis = lastItemCoordSys.dataByAxis || {};
71377                var thisItemCoordSys = dataByCoordSys[indexCoordSys] || {};
71378                var thisDataByAxis = thisItemCoordSys.dataByAxis || [];
71379                contentNotChanged &= lastDataByAxis.length === thisDataByAxis.length;
71380
71381                contentNotChanged && each$17(lastDataByAxis, function (lastItem, indexAxis) {
71382                    var thisItem = thisDataByAxis[indexAxis] || {};
71383                    var lastIndices = lastItem.seriesDataIndices || [];
71384                    var newIndices = thisItem.seriesDataIndices || [];
71385
71386                    contentNotChanged &=
71387                        lastItem.value === thisItem.value
71388                        && lastItem.axisType === thisItem.axisType
71389                        && lastItem.axisId === thisItem.axisId
71390                        && lastIndices.length === newIndices.length;
71391
71392                    contentNotChanged && each$17(lastIndices, function (lastIdxItem, j) {
71393                        var newIdxItem = newIndices[j];
71394                        contentNotChanged &=
71395                            lastIdxItem.seriesIndex === newIdxItem.seriesIndex
71396                            && lastIdxItem.dataIndex === newIdxItem.dataIndex;
71397                    });
71398                });
71399            });
71400
71401            this._lastDataByCoordSys = dataByCoordSys;
71402
71403            return !!contentNotChanged;
71404        },
71405
71406        _hide: function (dispatchAction) {
71407            // Do not directly hideLater here, because this behavior may be prevented
71408            // in dispatchAction when showTip is dispatched.
71409
71410            // FIXME
71411            // duplicated hideTip if manuallyHideTip is called from dispatchAction.
71412            this._lastDataByCoordSys = null;
71413            dispatchAction({
71414                type: 'hideTip',
71415                from: this.uid
71416            });
71417        },
71418
71419        dispose: function (ecModel, api) {
71420            if (env$1.node || env$1.wxa) {
71421                return;
71422            }
71423            this._tooltipContent.hide();
71424            unregister('itemTooltip', api);
71425        }
71426    });
71427
71428
71429    /**
71430     * @param {Array.<Object|module:echarts/model/Model>} modelCascade
71431     * From top to bottom. (the last one should be globalTooltipModel);
71432     */
71433    function buildTooltipModel(modelCascade) {
71434        var resultModel = modelCascade.pop();
71435        while (modelCascade.length) {
71436            var tooltipOpt = modelCascade.pop();
71437            if (tooltipOpt) {
71438                if (Model.isInstance(tooltipOpt)) {
71439                    tooltipOpt = tooltipOpt.get('tooltip', true);
71440                }
71441                // In each data item tooltip can be simply write:
71442                // {
71443                //  value: 10,
71444                //  tooltip: 'Something you need to know'
71445                // }
71446                if (typeof tooltipOpt === 'string') {
71447                    tooltipOpt = {formatter: tooltipOpt};
71448                }
71449                resultModel = new Model(tooltipOpt, resultModel, resultModel.ecModel);
71450            }
71451        }
71452        return resultModel;
71453    }
71454
71455    function makeDispatchAction$1(payload, api) {
71456        return payload.dispatchAction || bind(api.dispatchAction, api);
71457    }
71458
71459    function refixTooltipPosition(x, y, el, viewWidth, viewHeight, gapH, gapV) {
71460        var size = getOuterSize(el);
71461        var width = size.width;
71462        var height = size.height;
71463
71464        if (gapH != null) {
71465            if (x + width + gapH > viewWidth) {
71466                x -= width + gapH;
71467            }
71468            else {
71469                x += gapH;
71470            }
71471        }
71472        if (gapV != null) {
71473            if (y + height + gapV > viewHeight) {
71474                y -= height + gapV;
71475            }
71476            else {
71477                y += gapV;
71478            }
71479        }
71480        return [x, y];
71481    }
71482
71483    function confineTooltipPosition(x, y, el, viewWidth, viewHeight) {
71484        var size = getOuterSize(el);
71485        var width = size.width;
71486        var height = size.height;
71487
71488        x = Math.min(x + width, viewWidth) - width;
71489        y = Math.min(y + height, viewHeight) - height;
71490        x = Math.max(x, 0);
71491        y = Math.max(y, 0);
71492
71493        return [x, y];
71494    }
71495
71496    function getOuterSize(el) {
71497        var width = el.clientWidth;
71498        var height = el.clientHeight;
71499
71500        // Consider browser compatibility.
71501        // IE8 does not support getComputedStyle.
71502        if (document.defaultView && document.defaultView.getComputedStyle) {
71503            var stl = document.defaultView.getComputedStyle(el);
71504            if (stl) {
71505                width += parseInt(stl.paddingLeft, 10) + parseInt(stl.paddingRight, 10)
71506                    + parseInt(stl.borderLeftWidth, 10) + parseInt(stl.borderRightWidth, 10);
71507                height += parseInt(stl.paddingTop, 10) + parseInt(stl.paddingBottom, 10)
71508                    + parseInt(stl.borderTopWidth, 10) + parseInt(stl.borderBottomWidth, 10);
71509            }
71510        }
71511
71512        return {width: width, height: height};
71513    }
71514
71515    function calcTooltipPosition(position, rect, contentSize) {
71516        var domWidth = contentSize[0];
71517        var domHeight = contentSize[1];
71518        var gap = 5;
71519        var x = 0;
71520        var y = 0;
71521        var rectWidth = rect.width;
71522        var rectHeight = rect.height;
71523        switch (position) {
71524            case 'inside':
71525                x = rect.x + rectWidth / 2 - domWidth / 2;
71526                y = rect.y + rectHeight / 2 - domHeight / 2;
71527                break;
71528            case 'top':
71529                x = rect.x + rectWidth / 2 - domWidth / 2;
71530                y = rect.y - domHeight - gap;
71531                break;
71532            case 'bottom':
71533                x = rect.x + rectWidth / 2 - domWidth / 2;
71534                y = rect.y + rectHeight + gap;
71535                break;
71536            case 'left':
71537                x = rect.x - domWidth - gap;
71538                y = rect.y + rectHeight / 2 - domHeight / 2;
71539                break;
71540            case 'right':
71541                x = rect.x + rectWidth + gap;
71542                y = rect.y + rectHeight / 2 - domHeight / 2;
71543        }
71544        return [x, y];
71545    }
71546
71547    function isCenterAlign(align) {
71548        return align === 'center' || align === 'middle';
71549    }
71550
71551    /*
71552* Licensed to the Apache Software Foundation (ASF) under one
71553* or more contributor license agreements.  See the NOTICE file
71554* distributed with this work for additional information
71555* regarding copyright ownership.  The ASF licenses this file
71556* to you under the Apache License, Version 2.0 (the
71557* "License"); you may not use this file except in compliance
71558* with the License.  You may obtain a copy of the License at
71559*
71560*   http://www.apache.org/licenses/LICENSE-2.0
71561*
71562* Unless required by applicable law or agreed to in writing,
71563* software distributed under the License is distributed on an
71564* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
71565* KIND, either express or implied.  See the License for the
71566* specific language governing permissions and limitations
71567* under the License.
71568*/
71569
71570// FIXME Better way to pack data in graphic element
71571
71572    /**
71573     * @action
71574     * @property {string} type
71575     * @property {number} seriesIndex
71576     * @property {number} dataIndex
71577     * @property {number} [x]
71578     * @property {number} [y]
71579     */
71580    registerAction(
71581        {
71582            type: 'showTip',
71583            event: 'showTip',
71584            update: 'tooltip:manuallyShowTip'
71585        },
71586        // noop
71587        function () {}
71588    );
71589
71590    registerAction(
71591        {
71592            type: 'hideTip',
71593            event: 'hideTip',
71594            update: 'tooltip:manuallyHideTip'
71595        },
71596        // noop
71597        function () {}
71598    );
71599
71600    /*
71601* Licensed to the Apache Software Foundation (ASF) under one
71602* or more contributor license agreements.  See the NOTICE file
71603* distributed with this work for additional information
71604* regarding copyright ownership.  The ASF licenses this file
71605* to you under the Apache License, Version 2.0 (the
71606* "License"); you may not use this file except in compliance
71607* with the License.  You may obtain a copy of the License at
71608*
71609*   http://www.apache.org/licenses/LICENSE-2.0
71610*
71611* Unless required by applicable law or agreed to in writing,
71612* software distributed under the License is distributed on an
71613* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
71614* KIND, either express or implied.  See the License for the
71615* specific language governing permissions and limitations
71616* under the License.
71617*/
71618
71619    function getSeriesStackId$1(seriesModel) {
71620        return seriesModel.get('stack')
71621            || '__ec_stack_' + seriesModel.seriesIndex;
71622    }
71623
71624    function getAxisKey$1(axis) {
71625        return axis.dim;
71626    }
71627
71628    /**
71629     * @param {string} seriesType
71630     * @param {module:echarts/model/Global} ecModel
71631     * @param {module:echarts/ExtensionAPI} api
71632     */
71633    function barLayoutPolar(seriesType, ecModel, api) {
71634
71635        var width = api.getWidth();
71636        var height = api.getHeight();
71637
71638        var lastStackCoords = {};
71639
71640        var barWidthAndOffset = calRadialBar(
71641            filter(
71642                ecModel.getSeriesByType(seriesType),
71643                function (seriesModel) {
71644                    return !ecModel.isSeriesFiltered(seriesModel)
71645                        && seriesModel.coordinateSystem
71646                        && seriesModel.coordinateSystem.type === 'polar';
71647                }
71648            )
71649        );
71650
71651        ecModel.eachSeriesByType(seriesType, function (seriesModel) {
71652            // Check series coordinate, do layout for polar only
71653            if (seriesModel.coordinateSystem.type !== 'polar') {
71654                return;
71655            }
71656
71657            var data = seriesModel.getData();
71658            var polar = seriesModel.coordinateSystem;
71659            var baseAxis = polar.getBaseAxis();
71660
71661            var stackId = getSeriesStackId$1(seriesModel);
71662            var columnLayoutInfo
71663                = barWidthAndOffset[getAxisKey$1(baseAxis)][stackId];
71664            var columnOffset = columnLayoutInfo.offset;
71665            var columnWidth = columnLayoutInfo.width;
71666            var valueAxis = polar.getOtherAxis(baseAxis);
71667
71668            var center = seriesModel.get('center') || ['50%', '50%'];
71669            var cx = parsePercent$1(center[0], width);
71670            var cy = parsePercent$1(center[1], height);
71671
71672            var barMinHeight = seriesModel.get('barMinHeight') || 0;
71673            var barMinAngle = seriesModel.get('barMinAngle') || 0;
71674
71675            lastStackCoords[stackId] = lastStackCoords[stackId] || [];
71676
71677            var valueDim = data.mapDimension(valueAxis.dim);
71678            var baseDim = data.mapDimension(baseAxis.dim);
71679            var stacked = isDimensionStacked(data, valueDim /*, baseDim*/);
71680
71681            var valueAxisStart = valueAxis.getExtent()[0];
71682
71683            for (var idx = 0, len = data.count(); idx < len; idx++) {
71684                var value = data.get(valueDim, idx);
71685                var baseValue = data.get(baseDim, idx);
71686
71687                if (isNaN(value)) {
71688                    continue;
71689                }
71690
71691                var sign = value >= 0 ? 'p' : 'n';
71692                var baseCoord = valueAxisStart;
71693
71694                // Because of the barMinHeight, we can not use the value in
71695                // stackResultDimension directly.
71696                // Only ordinal axis can be stacked.
71697                if (stacked) {
71698                    if (!lastStackCoords[stackId][baseValue]) {
71699                        lastStackCoords[stackId][baseValue] = {
71700                            p: valueAxisStart, // Positive stack
71701                            n: valueAxisStart  // Negative stack
71702                        };
71703                    }
71704                    // Should also consider #4243
71705                    baseCoord = lastStackCoords[stackId][baseValue][sign];
71706                }
71707
71708                var r0;
71709                var r;
71710                var startAngle;
71711                var endAngle;
71712
71713                // radial sector
71714                if (valueAxis.dim === 'radius') {
71715                    var radiusSpan = valueAxis.dataToRadius(value) - valueAxisStart;
71716                    var angle = baseAxis.dataToAngle(baseValue);
71717
71718                    if (Math.abs(radiusSpan) < barMinHeight) {
71719                        radiusSpan = (radiusSpan < 0 ? -1 : 1) * barMinHeight;
71720                    }
71721
71722                    r0 = baseCoord;
71723                    r = baseCoord + radiusSpan;
71724                    startAngle = angle - columnOffset;
71725                    endAngle = startAngle - columnWidth;
71726
71727                    stacked && (lastStackCoords[stackId][baseValue][sign] = r);
71728                }
71729                // tangential sector
71730                else {
71731                    // angleAxis must be clamped.
71732                    var angleSpan = valueAxis.dataToAngle(value, true) - valueAxisStart;
71733                    var radius = baseAxis.dataToRadius(baseValue);
71734
71735                    if (Math.abs(angleSpan) < barMinAngle) {
71736                        angleSpan = (angleSpan < 0 ? -1 : 1) * barMinAngle;
71737                    }
71738
71739                    r0 = radius + columnOffset;
71740                    r = r0 + columnWidth;
71741                    startAngle = baseCoord;
71742                    endAngle = baseCoord + angleSpan;
71743
71744                    // if the previous stack is at the end of the ring,
71745                    // add a round to differentiate it from origin
71746                    // var extent = angleAxis.getExtent();
71747                    // var stackCoord = angle;
71748                    // if (stackCoord === extent[0] && value > 0) {
71749                    //     stackCoord = extent[1];
71750                    // }
71751                    // else if (stackCoord === extent[1] && value < 0) {
71752                    //     stackCoord = extent[0];
71753                    // }
71754                    stacked && (lastStackCoords[stackId][baseValue][sign] = endAngle);
71755                }
71756
71757                data.setItemLayout(idx, {
71758                    cx: cx,
71759                    cy: cy,
71760                    r0: r0,
71761                    r: r,
71762                    // Consider that positive angle is anti-clockwise,
71763                    // while positive radian of sector is clockwise
71764                    startAngle: -startAngle * Math.PI / 180,
71765                    endAngle: -endAngle * Math.PI / 180
71766                });
71767
71768            }
71769
71770        }, this);
71771
71772    }
71773
71774    /**
71775     * Calculate bar width and offset for radial bar charts
71776     */
71777    function calRadialBar(barSeries, api) {
71778        // Columns info on each category axis. Key is polar name
71779        var columnsMap = {};
71780
71781        each$1(barSeries, function (seriesModel, idx) {
71782            var data = seriesModel.getData();
71783            var polar = seriesModel.coordinateSystem;
71784
71785            var baseAxis = polar.getBaseAxis();
71786
71787            var axisExtent = baseAxis.getExtent();
71788            var bandWidth = baseAxis.type === 'category'
71789                ? baseAxis.getBandWidth()
71790                : (Math.abs(axisExtent[1] - axisExtent[0]) / data.count());
71791
71792            var columnsOnAxis = columnsMap[getAxisKey$1(baseAxis)] || {
71793                bandWidth: bandWidth,
71794                remainedWidth: bandWidth,
71795                autoWidthCount: 0,
71796                categoryGap: '20%',
71797                gap: '30%',
71798                stacks: {}
71799            };
71800            var stacks = columnsOnAxis.stacks;
71801            columnsMap[getAxisKey$1(baseAxis)] = columnsOnAxis;
71802
71803            var stackId = getSeriesStackId$1(seriesModel);
71804
71805            if (!stacks[stackId]) {
71806                columnsOnAxis.autoWidthCount++;
71807            }
71808            stacks[stackId] = stacks[stackId] || {
71809                width: 0,
71810                maxWidth: 0
71811            };
71812
71813            var barWidth = parsePercent$1(
71814                seriesModel.get('barWidth'),
71815                bandWidth
71816            );
71817            var barMaxWidth = parsePercent$1(
71818                seriesModel.get('barMaxWidth'),
71819                bandWidth
71820            );
71821            var barGap = seriesModel.get('barGap');
71822            var barCategoryGap = seriesModel.get('barCategoryGap');
71823
71824            if (barWidth && !stacks[stackId].width) {
71825                barWidth = Math.min(columnsOnAxis.remainedWidth, barWidth);
71826                stacks[stackId].width = barWidth;
71827                columnsOnAxis.remainedWidth -= barWidth;
71828            }
71829
71830            barMaxWidth && (stacks[stackId].maxWidth = barMaxWidth);
71831            (barGap != null) && (columnsOnAxis.gap = barGap);
71832            (barCategoryGap != null) && (columnsOnAxis.categoryGap = barCategoryGap);
71833        });
71834
71835
71836        var result = {};
71837
71838        each$1(columnsMap, function (columnsOnAxis, coordSysName) {
71839
71840            result[coordSysName] = {};
71841
71842            var stacks = columnsOnAxis.stacks;
71843            var bandWidth = columnsOnAxis.bandWidth;
71844            var categoryGap = parsePercent$1(columnsOnAxis.categoryGap, bandWidth);
71845            var barGapPercent = parsePercent$1(columnsOnAxis.gap, 1);
71846
71847            var remainedWidth = columnsOnAxis.remainedWidth;
71848            var autoWidthCount = columnsOnAxis.autoWidthCount;
71849            var autoWidth = (remainedWidth - categoryGap)
71850                / (autoWidthCount + (autoWidthCount - 1) * barGapPercent);
71851            autoWidth = Math.max(autoWidth, 0);
71852
71853            // Find if any auto calculated bar exceeded maxBarWidth
71854            each$1(stacks, function (column, stack) {
71855                var maxWidth = column.maxWidth;
71856                if (maxWidth && maxWidth < autoWidth) {
71857                    maxWidth = Math.min(maxWidth, remainedWidth);
71858                    if (column.width) {
71859                        maxWidth = Math.min(maxWidth, column.width);
71860                    }
71861                    remainedWidth -= maxWidth;
71862                    column.width = maxWidth;
71863                    autoWidthCount--;
71864                }
71865            });
71866
71867            // Recalculate width again
71868            autoWidth = (remainedWidth - categoryGap)
71869                / (autoWidthCount + (autoWidthCount - 1) * barGapPercent);
71870            autoWidth = Math.max(autoWidth, 0);
71871
71872            var widthSum = 0;
71873            var lastColumn;
71874            each$1(stacks, function (column, idx) {
71875                if (!column.width) {
71876                    column.width = autoWidth;
71877                }
71878                lastColumn = column;
71879                widthSum += column.width * (1 + barGapPercent);
71880            });
71881            if (lastColumn) {
71882                widthSum -= lastColumn.width * barGapPercent;
71883            }
71884
71885            var offset = -widthSum / 2;
71886            each$1(stacks, function (column, stackId) {
71887                result[coordSysName][stackId] = result[coordSysName][stackId] || {
71888                    offset: offset,
71889                    width: column.width
71890                };
71891
71892                offset += column.width * (1 + barGapPercent);
71893            });
71894        });
71895
71896        return result;
71897    }
71898
71899    /*
71900* Licensed to the Apache Software Foundation (ASF) under one
71901* or more contributor license agreements.  See the NOTICE file
71902* distributed with this work for additional information
71903* regarding copyright ownership.  The ASF licenses this file
71904* to you under the Apache License, Version 2.0 (the
71905* "License");
71905 you may not use this file except in compliance
71906* with the License.  You may obtain a copy of the License at
71907*
71908*   http://www.apache.org/licenses/LICENSE-2.0
71909*
71910* Unless required by applicable law or agreed to in writing,
71911* software distributed under the License is distributed on an
71912* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
71913* KIND, either express or implied.  See the License for the
71914* specific language governing permissions and limitations
71915* under the License.
71916*/
71917
71918    function RadiusAxis(scale, radiusExtent) {
71919
71920        Axis.call(this, 'radius', scale, radiusExtent);
71921
71922        /**
71923         * Axis type
71924         *  - 'category'
71925         *  - 'value'
71926         *  - 'time'
71927         *  - 'log'
71928         * @type {string}
71929         */
71930        this.type = 'category';
71931    }
71932
71933    RadiusAxis.prototype = {
71934
71935        constructor: RadiusAxis,
71936
71937        /**
71938         * @override
71939         */
71940        pointToData: function (point, clamp) {
71941            return this.polar.pointToData(point, clamp)[this.dim === 'radius' ? 0 : 1];
71942        },
71943
71944        dataToRadius: Axis.prototype.dataToCoord,
71945
71946        radiusToData: Axis.prototype.coordToData
71947    };
71948
71949    inherits(RadiusAxis, Axis);
71950
71951    /*
71952* Licensed to the Apache Software Foundation (ASF) under one
71953* or more contributor license agreements.  See the NOTICE file
71954* distributed with this work for additional information
71955* regarding copyright ownership.  The ASF licenses this file
71956* to you under the Apache License, Version 2.0 (the
71957* "License"); you may not use this file except in compliance
71958* with the License.  You may obtain a copy of the License at
71959*
71960*   http://www.apache.org/licenses/LICENSE-2.0
71961*
71962* Unless required by applicable law or agreed to in writing,
71963* software distributed under the License is distributed on an
71964* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
71965* KIND, either express or implied.  See the License for the
71966* specific language governing permissions and limitations
71967* under the License.
71968*/
71969
71970    function AngleAxis(scale, angleExtent) {
71971
71972        angleExtent = angleExtent || [0, 360];
71973
71974        Axis.call(this, 'angle', scale, angleExtent);
71975
71976        /**
71977         * Axis type
71978         *  - 'category'
71979         *  - 'value'
71980         *  - 'time'
71981         *  - 'log'
71982         * @type {string}
71983         */
71984        this.type = 'category';
71985    }
71986
71987    AngleAxis.prototype = {
71988
71989        constructor: AngleAxis,
71990
71991        /**
71992         * @override
71993         */
71994        pointToData: function (point, clamp) {
71995            return this.polar.pointToData(point, clamp)[this.dim === 'radius' ? 0 : 1];
71996        },
71997
71998        dataToAngle: Axis.prototype.dataToCoord,
71999
72000        angleToData: Axis.prototype.coordToData
72001    };
72002
72003    inherits(AngleAxis, Axis);
72004
72005    /*
72006* Licensed to the Apache Software Foundation (ASF) under one
72007* or more contributor license agreements.  See the NOTICE file
72008* distributed with this work for additional information
72009* regarding copyright ownership.  The ASF licenses this file
72010* to you under the Apache License, Version 2.0 (the
72011* "License"); you may not use this file except in compliance
72012* with the License.  You may obtain a copy of the License at
72013*
72014*   http://www.apache.org/licenses/LICENSE-2.0
72015*
72016* Unless required by applicable law or agreed to in writing,
72017* software distributed under the License is distributed on an
72018* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
72019* KIND, either express or implied.  See the License for the
72020* specific language governing permissions and limitations
72021* under the License.
72022*/
72023
72024    /**
72025     * @module echarts/coord/polar/Polar
72026     */
72027
72028    /**
72029     * @alias {module:echarts/coord/polar/Polar}
72030     * @constructor
72031     * @param {string} name
72032     */
72033    var Polar = function (name) {
72034
72035        /**
72036         * @type {string}
72037         */
72038        this.name = name || '';
72039
72040        /**
72041         * x of polar center
72042         * @type {number}
72043         */
72044        this.cx = 0;
72045
72046        /**
72047         * y of polar center
72048         * @type {number}
72049         */
72050        this.cy = 0;
72051
72052        /**
72053         * @type {module:echarts/coord/polar/RadiusAxis}
72054         * @private
72055         */
72056        this._radiusAxis = new RadiusAxis();
72057
72058        /**
72059         * @type {module:echarts/coord/polar/AngleAxis}
72060         * @private
72061         */
72062        this._angleAxis = new AngleAxis();
72063
72064        this._radiusAxis.polar = this._angleAxis.polar = this;
72065    };
72066
72067    Polar.prototype = {
72068
72069        type: 'polar',
72070
72071        axisPointerEnabled: true,
72072
72073        constructor: Polar,
72074
72075        /**
72076         * @param {Array.<string>}
72077         * @readOnly
72078         */
72079        dimensions: ['radius', 'angle'],
72080
72081        /**
72082         * @type {module:echarts/coord/PolarModel}
72083         */
72084        model: null,
72085
72086        /**
72087         * If contain coord
72088         * @param {Array.<number>} point
72089         * @return {boolean}
72090         */
72091        containPoint: function (point) {
72092            var coord = this.pointToCoord(point);
72093            return this._radiusAxis.contain(coord[0])
72094                && this._angleAxis.contain(coord[1]);
72095        },
72096
72097        /**
72098         * If contain data
72099         * @param {Array.<number>} data
72100         * @return {boolean}
72101         */
72102        containData: function (data) {
72103            return this._radiusAxis.containData(data[0])
72104                && this._angleAxis.containData(data[1]);
72105        },
72106
72107        /**
72108         * @param {string} dim
72109         * @return {module:echarts/coord/polar/AngleAxis|module:echarts/coord/polar/RadiusAxis}
72110         */
72111        getAxis: function (dim) {
72112            return this['_' + dim + 'Axis'];
72113        },
72114
72115        /**
72116         * @return {Array.<module:echarts/coord/Axis>}
72117         */
72118        getAxes: function () {
72119            return [this._radiusAxis, this._angleAxis];
72120        },
72121
72122        /**
72123         * Get axes by type of scale
72124         * @param {string} scaleType
72125         * @return {module:echarts/coord/polar/AngleAxis|module:echarts/coord/polar/RadiusAxis}
72126         */
72127        getAxesByScale: function (scaleType) {
72128            var axes = [];
72129            var angleAxis = this._angleAxis;
72130            var radiusAxis = this._radiusAxis;
72131            angleAxis.scale.type === scaleType && axes.push(angleAxis);
72132            radiusAxis.scale.type === scaleType && axes.push(radiusAxis);
72133
72134            return axes;
72135        },
72136
72137        /**
72138         * @return {module:echarts/coord/polar/AngleAxis}
72139         */
72140        getAngleAxis: function () {
72141            return this._angleAxis;
72142        },
72143
72144        /**
72145         * @return {module:echarts/coord/polar/RadiusAxis}
72146         */
72147        getRadiusAxis: function () {
72148            return this._radiusAxis;
72149        },
72150
72151        /**
72152         * @param {module:echarts/coord/polar/Axis}
72153         * @return {module:echarts/coord/polar/Axis}
72154         */
72155        getOtherAxis: function (axis) {
72156            var angleAxis = this._angleAxis;
72157            return axis === angleAxis ? this._radiusAxis : angleAxis;
72158        },
72159
72160        /**
72161         * Base axis will be used on stacking.
72162         *
72163         * @return {module:echarts/coord/polar/Axis}
72164         */
72165        getBaseAxis: function () {
72166            return this.getAxesByScale('ordinal')[0]
72167                || this.getAxesByScale('time')[0]
72168                || this.getAngleAxis();
72169        },
72170
72171        /**
72172         * @param {string} [dim] 'radius' or 'angle' or 'auto' or null/undefined
72173         * @return {Object} {baseAxes: [], otherAxes: []}
72174         */
72175        getTooltipAxes: function (dim) {
72176            var baseAxis = (dim != null && dim !== 'auto')
72177                ? this.getAxis(dim) : this.getBaseAxis();
72178            return {
72179                baseAxes: [baseAxis],
72180                otherAxes: [this.getOtherAxis(baseAxis)]
72181            };
72182        },
72183
72184        /**
72185         * Convert a single data item to (x, y) point.
72186         * Parameter data is an array which the first element is radius and the second is angle
72187         * @param {Array.<number>} data
72188         * @param {boolean} [clamp=false]
72189         * @return {Array.<number>}
72190         */
72191        dataToPoint: function (data, clamp) {
72192            return this.coordToPoint([
72193                this._radiusAxis.dataToRadius(data[0], clamp),
72194                this._angleAxis.dataToAngle(data[1], clamp)
72195            ]);
72196        },
72197
72198        /**
72199         * Convert a (x, y) point to data
72200         * @param {Array.<number>} point
72201         * @param {boolean} [clamp=false]
72202         * @return {Array.<number>}
72203         */
72204        pointToData: function (point, clamp) {
72205            var coord = this.pointToCoord(point);
72206            return [
72207                this._radiusAxis.radiusToData(coord[0], clamp),
72208                this._angleAxis.angleToData(coord[1], clamp)
72209            ];
72210        },
72211
72212        /**
72213         * Convert a (x, y) point to (radius, angle) coord
72214         * @param {Array.<number>} point
72215         * @return {Array.<number>}
72216         */
72217        pointToCoord: function (point) {
72218            var dx = point[0] - this.cx;
72219            var dy = point[1] - this.cy;
72220            var angleAxis = this.getAngleAxis();
72221            var extent = angleAxis.getExtent();
72222            var minAngle = Math.min(extent[0], extent[1]);
72223            var maxAngle = Math.max(extent[0], extent[1]);
72224            // Fix fixed extent in polarCreator
72225            // FIXME
72226            angleAxis.inverse
72227                ? (minAngle = maxAngle - 360)
72228                : (maxAngle = minAngle + 360);
72229
72230            var radius = Math.sqrt(dx * dx + dy * dy);
72231            dx /= radius;
72232            dy /= radius;
72233
72234            var radian = Math.atan2(-dy, dx) / Math.PI * 180;
72235
72236            // move to angleExtent
72237            var dir = radian < minAngle ? 1 : -1;
72238            while (radian < minAngle || radian > maxAngle) {
72239                radian += dir * 360;
72240            }
72241
72242            return [radius, radian];
72243        },
72244
72245        /**
72246         * Convert a (radius, angle) coord to (x, y) point
72247         * @param {Array.<number>} coord
72248         * @return {Array.<number>}
72249         */
72250        coordToPoint: function (coord) {
72251            var radius = coord[0];
72252            var radian = coord[1] / 180 * Math.PI;
72253            var x = Math.cos(radian) * radius + this.cx;
72254            // Inverse the y
72255            var y = -Math.sin(radian) * radius + this.cy;
72256
72257            return [x, y];
72258        }
72259
72260    };
72261
72262    /*
72263* Licensed to the Apache Software Foundation (ASF) under one
72264* or more contributor license agreements.  See the NOTICE file
72265* distributed with this work for additional information
72266* regarding copyright ownership.  The ASF licenses this file
72267* to you under the Apache License, Version 2.0 (the
72268* "License");
72268 you may not use this file except in compliance
72269* with the License.  You may obtain a copy of the License at
72270*
72271*   http://www.apache.org/licenses/LICENSE-2.0
72272*
72273* Unless required by applicable law or agreed to in writing,
72274* software distributed under the License is distributed on an
72275* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
72276* KIND, either express or implied.  See the License for the
72277* specific language governing permissions and limitations
72278* under the License.
72279*/
72280
72281    var PolarAxisModel = ComponentModel.extend({
72282
72283        type: 'polarAxis',
72284
72285        /**
72286         * @type {module:echarts/coord/polar/AngleAxis|module:echarts/coord/polar/RadiusAxis}
72287         */
72288        axis: null,
72289
72290        /**
72291         * @override
72292         */
72293        getCoordSysModel: function () {
72294            return this.ecModel.queryComponents({
72295                mainType: 'polar',
72296                index: this.option.polarIndex,
72297                id: this.option.polarId
72298            })[0];
72299        }
72300
72301    });
72302
72303    merge(PolarAxisModel.prototype, axisModelCommonMixin);
72304
72305    var polarAxisDefaultExtendedOption = {
72306        angle: {
72307            // polarIndex: 0,
72308            // polarId: '',
72309
72310            startAngle: 90,
72311
72312            clockwise: true,
72313
72314            splitNumber: 12,
72315
72316            axisLabel: {
72317                rotate: false
72318            }
72319        },
72320        radius: {
72321            // polarIndex: 0,
72322            // polarId: '',
72323
72324            splitNumber: 5
72325        }
72326    };
72327
72328    function getAxisType$3(axisDim, option) {
72329        // Default axis with data is category axis
72330        return option.type || (option.data ? 'category' : 'value');
72331    }
72332
72333    axisModelCreator('angle', PolarAxisModel, getAxisType$3, polarAxisDefaultExtendedOption.angle);
72334    axisModelCreator('radius', PolarAxisModel, getAxisType$3, polarAxisDefaultExtendedOption.radius);
72335
72336    /*
72337* Licensed to the Apache Software Foundation (ASF) under one
72338* or more contributor license agreements.  See the NOTICE file
72339* distributed with this work for additional information
72340* regarding copyright ownership.  The ASF licenses this file
72341* to you under the Apache License, Version 2.0 (the
72342* "License"); you may not use this file except in compliance
72343* with the License.  You may obtain a copy of the License at
72344*
72345*   http://www.apache.org/licenses/LICENSE-2.0
72346*
72347* Unless required by applicable law or agreed to in writing,
72348* software distributed under the License is distributed on an
72349* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
72350* KIND, either express or implied.  See the License for the
72351* specific language governing permissions and limitations
72352* under the License.
72353*/
72354
72355    extendComponentModel({
72356
72357        type: 'polar',
72358
72359        dependencies: ['polarAxis', 'angleAxis'],
72360
72361        /**
72362         * @type {module:echarts/coord/polar/Polar}
72363         */
72364        coordinateSystem: null,
72365
72366        /**
72367         * @param {string} axisType
72368         * @return {module:echarts/coord/polar/AxisModel}
72369         */
72370        findAxisModel: function (axisType) {
72371            var foundAxisModel;
72372            var ecModel = this.ecModel;
72373
72374            ecModel.eachComponent(axisType, function (axisModel) {
72375                if (axisModel.getCoordSysModel() === this) {
72376                    foundAxisModel = axisModel;
72377                }
72378            }, this);
72379            return foundAxisModel;
72380        },
72381
72382        defaultOption: {
72383
72384            zlevel: 0,
72385
72386            z: 0,
72387
72388            center: ['50%', '50%'],
72389
72390            radius: '80%'
72391        }
72392    });
72393
72394    /*
72395* Licensed to the Apache Software Foundation (ASF) under one
72396* or more contributor license agreements.  See the NOTICE file
72397* distributed with this work for additional information
72398* regarding copyright ownership.  The ASF licenses this file
72399* to you under the Apache License, Version 2.0 (the
72400* "License"); you may not use this file except in compliance
72401* with the License.  You may obtain a copy of the License at
72402*
72403*   http://www.apache.org/licenses/LICENSE-2.0
72404*
72405* Unless required by applicable law or agreed to in writing,
72406* software distributed under the License is distributed on an
72407* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
72408* KIND, either express or implied.  See the License for the
72409* specific language governing permissions and limitations
72410* under the License.
72411*/
72412
72413// TODO Axis scale
72414
72415    /**
72416     * Resize method bound to the polar
72417     * @param {module:echarts/coord/polar/PolarModel} polarModel
72418     * @param {module:echarts/ExtensionAPI} api
72419     */
72420    function resizePolar(polar, polarModel, api) {
72421        var center = polarModel.get('center');
72422        var width = api.getWidth();
72423        var height = api.getHeight();
72424
72425        polar.cx = parsePercent$1(center[0], width);
72426        polar.cy = parsePercent$1(center[1], height);
72427
72428        var radiusAxis = polar.getRadiusAxis();
72429        var size = Math.min(width, height) / 2;
72430        var radius = parsePercent$1(polarModel.get('radius'), size);
72431        radiusAxis.inverse
72432            ? radiusAxis.setExtent(radius, 0)
72433            : radiusAxis.setExtent(0, radius);
72434    }
72435
72436    /**
72437     * Update polar
72438     */
72439    function updatePolarScale(ecModel, api) {
72440        var polar = this;
72441        var angleAxis = polar.getAngleAxis();
72442        var radiusAxis = polar.getRadiusAxis();
72443        // Reset scale
72444        angleAxis.scale.setExtent(Infinity, -Infinity);
72445        radiusAxis.scale.setExtent(Infinity, -Infinity);
72446
72447        ecModel.eachSeries(function (seriesModel) {
72448            if (seriesModel.coordinateSystem === polar) {
72449                var data = seriesModel.getData();
72450                each$1(data.mapDimension('radius', true), function (dim) {
72451                    radiusAxis.scale.unionExtentFromData(
72452                        data, getStackedDimension(data, dim)
72453                    );
72454                });
72455                each$1(data.mapDimension('angle', true), function (dim) {
72456                    angleAxis.scale.unionExtentFromData(
72457                        data, getStackedDimension(data, dim)
72458                    );
72459                });
72460            }
72461        });
72462
72463        niceScaleExtent(angleAxis.scale, angleAxis.model);
72464        niceScaleExtent(radiusAxis.scale, radiusAxis.model);
72465
72466        // Fix extent of category angle axis
72467        if (angleAxis.type === 'category' && !angleAxis.onBand) {
72468            var extent = angleAxis.getExtent();
72469            var diff = 360 / angleAxis.scale.count();
72470            angleAxis.inverse ? (extent[1] += diff) : (extent[1] -= diff);
72471            angleAxis.setExtent(extent[0], extent[1]);
72472        }
72473    }
72474
72475    /**
72476     * Set common axis properties
72477     * @param {module:echarts/coord/polar/AngleAxis|module:echarts/coord/polar/RadiusAxis}
72478     * @param {module:echarts/coord/polar/AxisModel}
72479     * @inner
72480     */
72481    function setAxis(axis, axisModel) {
72482        axis.type = axisModel.get('type');
72483        axis.scale = createScaleByModel(axisModel);
72484        axis.onBand = axisModel.get('boundaryGap') && axis.type === 'category';
72485        axis.inverse = axisModel.get('inverse');
72486
72487        if (axisModel.mainType === 'angleAxis') {
72488            axis.inverse ^= axisModel.get('clockwise');
72489            var startAngle = axisModel.get('startAngle');
72490            axis.setExtent(startAngle, startAngle + (axis.inverse ? -360 : 360));
72491        }
72492
72493        // Inject axis instance
72494        axisModel.axis = axis;
72495        axis.model = axisModel;
72496    }
72497
72498
72499    var polarCreator = {
72500
72501        dimensions: Polar.prototype.dimensions,
72502
72503        create: function (ecModel, api) {
72504            var polarList = [];
72505            ecModel.eachComponent('polar', function (polarModel, idx) {
72506                var polar = new Polar(idx);
72507                // Inject resize and update method
72508                polar.update = updatePolarScale;
72509
72510                var radiusAxis = polar.getRadiusAxis();
72511                var angleAxis = polar.getAngleAxis();
72512
72513                var radiusAxisModel = polarModel.findAxisModel('radiusAxis');
72514                var angleAxisModel = polarModel.findAxisModel('angleAxis');
72515
72516                setAxis(radiusAxis, radiusAxisModel);
72517                setAxis(angleAxis, angleAxisModel);
72518
72519                resizePolar(polar, polarModel, api);
72520
72521                polarList.push(polar);
72522
72523                polarModel.coordinateSystem = polar;
72524                polar.model = polarModel;
72525            });
72526            // Inject coordinateSystem to series
72527            ecModel.eachSeries(function (seriesModel) {
72528                if (seriesModel.get('coordinateSystem') === 'polar') {
72529                    var polarModel = ecModel.queryComponents({
72530                        mainType: 'polar',
72531                        index: seriesModel.get('polarIndex'),
72532                        id: seriesModel.get('polarId')
72533                    })[0];
72534
72535                    if (__DEV__) {
72536                        if (!polarModel) {
72537                            throw new Error(
72538                                'Polar "' + retrieve(
72539                                seriesModel.get('polarIndex'),
72540                                seriesModel.get('polarId'),
72541                                0
72542                                ) + '" not found'
72543                            );
72544                        }
72545                    }
72546                    seriesModel.coordinateSystem = polarModel.coordinateSystem;
72547                }
72548            });
72549
72550            return polarList;
72551        }
72552    };
72553
72554    CoordinateSystemManager.register('polar', polarCreator);
72555
72556    /*
72557* Licensed to the Apache Software Foundation (ASF) under one
72558* or more contributor license agreements.  See the NOTICE file
72559* distributed with this work for additional information
72560* regarding copyright ownership.  The ASF licenses this file
72561* to you under the Apache License, Version 2.0 (the
72562* "License");
72562 you may not use this file except in compliance
72563* with the License.  You may obtain a copy of the License at
72564*
72565*   http://www.apache.org/licenses/LICENSE-2.0
72566*
72567* Unless required by applicable law or agreed to in writing,
72568* software distributed under the License is distributed on an
72569* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
72570* KIND, either express or implied.  See the License for the
72571* specific language governing permissions and limitations
72572* under the License.
72573*/
72574
72575    var elementList$1 = ['axisLine', 'axisLabel', 'axisTick', 'splitLine', 'splitArea'];
72576
72577    function getAxisLineShape(polar, rExtent, angle) {
72578        rExtent[1] > rExtent[0] && (rExtent = rExtent.slice().reverse());
72579        var start = polar.coordToPoint([rExtent[0], angle]);
72580        var end = polar.coordToPoint([rExtent[1], angle]);
72581
72582        return {
72583            x1: start[0],
72584            y1: start[1],
72585            x2: end[0],
72586            y2: end[1]
72587        };
72588    }
72589
72590    function getRadiusIdx(polar) {
72591        var radiusAxis = polar.getRadiusAxis();
72592        return radiusAxis.inverse ? 0 : 1;
72593    }
72594
72595// Remove the last tick which will overlap the first tick
72596    function fixAngleOverlap(list) {
72597        var firstItem = list[0];
72598        var lastItem = list[list.length - 1];
72599        if (firstItem
72600            && lastItem
72601            && Math.abs(Math.abs(firstItem.coord - lastItem.coord) - 360) < 1e-4
72602        ) {
72603            list.pop();
72604        }
72605    }
72606
72607    AxisView.extend({
72608
72609        type: 'angleAxis',
72610
72611        axisPointerClass: 'PolarAxisPointer',
72612
72613        render: function (angleAxisModel, ecModel) {
72614            this.group.removeAll();
72615            if (!angleAxisModel.get('show')) {
72616                return;
72617            }
72618
72619            var angleAxis = angleAxisModel.axis;
72620            var polar = angleAxis.polar;
72621            var radiusExtent = polar.getRadiusAxis().getExtent();
72622
72623            var ticksAngles = angleAxis.getTicksCoords();
72624            var labels = map(angleAxis.getViewLabels(), function (labelItem) {
72625                var labelItem = clone(labelItem);
72626                labelItem.coord = angleAxis.dataToCoord(labelItem.tickValue);
72627                return labelItem;
72628            });
72629
72630            fixAngleOverlap(labels);
72631            fixAngleOverlap(ticksAngles);
72632
72633            each$1(elementList$1, function (name) {
72634                if (angleAxisModel.get(name +'.show')
72635                    && (!angleAxis.scale.isBlank() || name === 'axisLine')
72636                ) {
72637                    this['_' + name](angleAxisModel, polar, ticksAngles, radiusExtent, labels);
72638                }
72639            }, this);
72640        },
72641
72642        /**
72643         * @private
72644         */
72645        _axisLine: function (angleAxisModel, polar, ticksAngles, radiusExtent) {
72646            var lineStyleModel = angleAxisModel.getModel('axisLine.lineStyle');
72647
72648            var circle = new Circle({
72649                shape: {
72650                    cx: polar.cx,
72651                    cy: polar.cy,
72652                    r: radiusExtent[getRadiusIdx(polar)]
72653                },
72654                style: lineStyleModel.getLineStyle(),
72655                z2: 1,
72656                silent: true
72657            });
72658            circle.style.fill = null;
72659
72660            this.group.add(circle);
72661        },
72662
72663        /**
72664         * @private
72665         */
72666        _axisTick: function (angleAxisModel, polar, ticksAngles, radiusExtent) {
72667            var tickModel = angleAxisModel.getModel('axisTick');
72668
72669            var tickLen = (tickModel.get('inside') ? -1 : 1) * tickModel.get('length');
72670            var radius = radiusExtent[getRadiusIdx(polar)];
72671
72672            var lines = map(ticksAngles, function (tickAngleItem) {
72673                return new Line({
72674                    shape: getAxisLineShape(polar, [radius, radius + tickLen], tickAngleItem.coord)
72675                });
72676            });
72677            this.group.add(mergePath(
72678                lines, {
72679                    style: defaults(
72680                        tickModel.getModel('lineStyle').getLineStyle(),
72681                        {
72682                            stroke: angleAxisModel.get('axisLine.lineStyle.color')
72683                        }
72684                    )
72685                }
72686            ));
72687        },
72688
72689        /**
72690         * @private
72691         */
72692        _axisLabel: function (angleAxisModel, polar, ticksAngles, radiusExtent, labels) {
72693            var rawCategoryData = angleAxisModel.getCategories(true);
72694
72695            var commonLabelModel = angleAxisModel.getModel('axisLabel');
72696
72697            var labelMargin = commonLabelModel.get('margin');
72698
72699            // Use length of ticksAngles because it may remove the last tick to avoid overlapping
72700            each$1(labels, function (labelItem, idx) {
72701                var labelModel = commonLabelModel;
72702                var tickValue = labelItem.tickValue;
72703
72704                var r = radiusExtent[getRadiusIdx(polar)];
72705                var p = polar.coordToPoint([r + labelMargin, labelItem.coord]);
72706                var cx = polar.cx;
72707                var cy = polar.cy;
72708
72709                var labelTextAlign = Math.abs(p[0] - cx) / r < 0.3
72710                    ? 'center' : (p[0] > cx ? 'left' : 'right');
72711                var labelTextVerticalAlign = Math.abs(p[1] - cy) / r < 0.3
72712                    ? 'middle' : (p[1] > cy ? 'top' : 'bottom');
72713
72714                if (rawCategoryData && rawCategoryData[tickValue] && rawCategoryData[tickValue].textStyle) {
72715                    labelModel = new Model(rawCategoryData[tickValue].textStyle, commonLabelModel, commonLabelModel.ecModel);
72716                }
72717
72718                var textEl = new Text({silent: true});
72719                this.group.add(textEl);
72720                setTextStyle(textEl.style, labelModel, {
72721                    x: p[0],
72722                    y: p[1],
72723                    textFill: labelModel.getTextColor() || angleAxisModel.get('axisLine.lineStyle.color'),
72724                    text: labelItem.formattedLabel,
72725                    textAlign: labelTextAlign,
72726                    textVerticalAlign: labelTextVerticalAlign
72727                });
72728            }, this);
72729        },
72730
72731        /**
72732         * @private
72733         */
72734        _splitLine: function (angleAxisModel, polar, ticksAngles, radiusExtent) {
72735            var splitLineModel = angleAxisModel.getModel('splitLine');
72736            var lineStyleModel = splitLineModel.getModel('lineStyle');
72737            var lineColors = lineStyleModel.get('color');
72738            var lineCount = 0;
72739
72740            lineColors = lineColors instanceof Array ? lineColors : [lineColors];
72741
72742            var splitLines = [];
72743
72744            for (var i = 0; i < ticksAngles.length; i++) {
72745                var colorIndex = (lineCount++) % lineColors.length;
72746                splitLines[colorIndex] = splitLines[colorIndex] || [];
72747                splitLines[colorIndex].push(new Line({
72748                    shape: getAxisLineShape(polar, radiusExtent, ticksAngles[i].coord)
72749                }));
72750            }
72751
72752            // Simple optimization
72753            // Batching the lines if color are the same
72754            for (var i = 0; i < splitLines.length; i++) {
72755                this.group.add(mergePath(splitLines[i], {
72756                    style: defaults({
72757                        stroke: lineColors[i % lineColors.length]
72758                    }, lineStyleModel.getLineStyle()),
72759                    silent: true,
72760                    z: angleAxisModel.get('z')
72761                }));
72762            }
72763        },
72764
72765        /**
72766         * @private
72767         */
72768        _splitArea: function (angleAxisModel, polar, ticksAngles, radiusExtent) {
72769            if (!ticksAngles.length) {
72770                return;
72771            }
72772
72773            var splitAreaModel = angleAxisModel.getModel('splitArea');
72774            var areaStyleModel = splitAreaModel.getModel('areaStyle');
72775            var areaColors = areaStyleModel.get('color');
72776            var lineCount = 0;
72777
72778            areaColors = areaColors instanceof Array ? areaColors : [areaColors];
72779
72780            var splitAreas = [];
72781
72782            var RADIAN = Math.PI / 180;
72783            var prevAngle = -ticksAngles[0].coord * RADIAN;
72784            var r0 = Math.min(radiusExtent[0], radiusExtent[1]);
72785            var r1 = Math.max(radiusExtent[0], radiusExtent[1]);
72786
72787            var clockwise = angleAxisModel.get('clockwise');
72788
72789            for (var i = 1; i < ticksAngles.length; i++) {
72790                var colorIndex = (lineCount++) % areaColors.length;
72791                splitAreas[colorIndex] = splitAreas[colorIndex] || [];
72792                splitAreas[colorIndex].push(new Sector({
72793                    shape: {
72794                        cx: polar.cx,
72795                        cy: polar.cy,
72796                        r0: r0,
72797                        r: r1,
72798                        startAngle: prevAngle,
72799                        endAngle: -ticksAngles[i].coord * RADIAN,
72800                        clockwise: clockwise
72801                    },
72802                    silent: true
72803                }));
72804                prevAngle = -ticksAngles[i].coord * RADIAN;
72805            }
72806
72807            // Simple optimization
72808            // Batching the lines if color are the same
72809            for (var i = 0; i < splitAreas.length; i++) {
72810                this.group.add(mergePath(splitAreas[i], {
72811                    style: defaults({
72812                        fill: areaColors[i % areaColors.length]
72813                    }, areaStyleModel.getAreaStyle()),
72814                    silent: true
72815                }));
72816            }
72817        }
72818    });
72819
72820    /*
72821* Licensed to the Apache Software Foundation (ASF) under one
72822* or more contributor license agreements.  See the NOTICE file
72823* distributed with this work for additional information
72824* regarding copyright ownership.  The ASF licenses this file
72825* to you under the Apache License, Version 2.0 (the
72826* "License"); you may not use this file except in compliance
72827* with the License.  You may obtain a copy of the License at
72828*
72829*   http://www.apache.org/licenses/LICENSE-2.0
72830*
72831* Unless required by applicable law or agreed to in writing,
72832* software distributed under the License is distributed on an
72833* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
72834* KIND, either express or implied.  See the License for the
72835* specific language governing permissions and limitations
72836* under the License.
72837*/
72838
72839    /*
72840* Licensed to the Apache Software Foundation (ASF) under one
72841* or more contributor license agreements.  See the NOTICE file
72842* distributed with this work for additional information
72843* regarding copyright ownership.  The ASF licenses this file
72844* to you under the Apache License, Version 2.0 (the
72845* "License"); you may not use this file except in compliance
72846* with the License.  You may obtain a copy of the License at
72847*
72848*   http://www.apache.org/licenses/LICENSE-2.0
72849*
72850* Unless required by applicable law or agreed to in writing,
72851* software distributed under the License is distributed on an
72852* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
72853* KIND, either express or implied.  See the License for the
72854* specific language governing permissions and limitations
72855* under the License.
72856*/
72857
72858    var axisBuilderAttrs$3 = [
72859        'axisLine', 'axisTickLabel', 'axisName'
72860    ];
72861    var selfBuilderAttrs$1 = [
72862        'splitLine', 'splitArea'
72863    ];
72864
72865    AxisView.extend({
72866
72867        type: 'radiusAxis',
72868
72869        axisPointerClass: 'PolarAxisPointer',
72870
72871        render: function (radiusAxisModel, ecModel) {
72872            this.group.removeAll();
72873            if (!radiusAxisModel.get('show')) {
72874                return;
72875            }
72876            var radiusAxis = radiusAxisModel.axis;
72877            var polar = radiusAxis.polar;
72878            var angleAxis = polar.getAngleAxis();
72879            var ticksCoords = radiusAxis.getTicksCoords();
72880            var axisAngle = angleAxis.getExtent()[0];
72881            var radiusExtent = radiusAxis.getExtent();
72882
72883            var layout = layoutAxis(polar, radiusAxisModel, axisAngle);
72884            var axisBuilder = new AxisBuilder(radiusAxisModel, layout);
72885            each$1(axisBuilderAttrs$3, axisBuilder.add, axisBuilder);
72886            this.group.add(axisBuilder.getGroup());
72887
72888            each$1(selfBuilderAttrs$1, function (name) {
72889                if (radiusAxisModel.get(name +'.show') && !radiusAxis.scale.isBlank()) {
72890                    this['_' + name](radiusAxisModel, polar, axisAngle, radiusExtent, ticksCoords);
72891                }
72892            }, this);
72893        },
72894
72895        /**
72896         * @private
72897         */
72898        _splitLine: function (radiusAxisModel, polar, axisAngle, radiusExtent, ticksCoords) {
72899            var splitLineModel = radiusAxisModel.getModel('splitLine');
72900            var lineStyleModel = splitLineModel.getModel('lineStyle');
72901            var lineColors = lineStyleModel.get('color');
72902            var lineCount = 0;
72903
72904            lineColors = lineColors instanceof Array ? lineColors : [lineColors];
72905
72906            var splitLines = [];
72907
72908            for (var i = 0; i < ticksCoords.length; i++) {
72909                var colorIndex = (lineCount++) % lineColors.length;
72910                splitLines[colorIndex] = splitLines[colorIndex] || [];
72911                splitLines[colorIndex].push(new Circle({
72912                    shape: {
72913                        cx: polar.cx,
72914                        cy: polar.cy,
72915                        r: ticksCoords[i].coord
72916                    },
72917                    silent: true
72918                }));
72919            }
72920
72921            // Simple optimization
72922            // Batching the lines if color are the same
72923            for (var i = 0; i < splitLines.length; i++) {
72924                this.group.add(mergePath(splitLines[i], {
72925                    style: defaults({
72926                        stroke: lineColors[i % lineColors.length],
72927                        fill: null
72928                    }, lineStyleModel.getLineStyle()),
72929                    silent: true
72930                }));
72931            }
72932        },
72933
72934        /**
72935         * @private
72936         */
72937        _splitArea: function (radiusAxisModel, polar, axisAngle, radiusExtent, ticksCoords) {
72938            if (!ticksCoords.length) {
72939                return;
72940            }
72941
72942            var splitAreaModel = radiusAxisModel.getModel('splitArea');
72943            var areaStyleModel = splitAreaModel.getModel('areaStyle');
72944            var areaColors = areaStyleModel.get('color');
72945            var lineCount = 0;
72946
72947            areaColors = areaColors instanceof Array ? areaColors : [areaColors];
72948
72949            var splitAreas = [];
72950
72951            var prevRadius = ticksCoords[0].coord;
72952            for (var i = 1; i < ticksCoords.length; i++) {
72953                var colorIndex = (lineCount++) % areaColors.length;
72954                splitAreas[colorIndex] = splitAreas[colorIndex] || [];
72955                splitAreas[colorIndex].push(new Sector({
72956                    shape: {
72957                        cx: polar.cx,
72958                        cy: polar.cy,
72959                        r0: prevRadius,
72960                        r: ticksCoords[i].coord,
72961                        startAngle: 0,
72962                        endAngle: Math.PI * 2
72963                    },
72964                    silent: true
72965                }));
72966                prevRadius = ticksCoords[i].coord;
72967            }
72968
72969            // Simple optimization
72970            // Batching the lines if color are the same
72971            for (var i = 0; i < splitAreas.length; i++) {
72972                this.group.add(mergePath(splitAreas[i], {
72973                    style: defaults({
72974                        fill: areaColors[i % areaColors.length]
72975                    }, areaStyleModel.getAreaStyle()),
72976                    silent: true
72977                }));
72978            }
72979        }
72980    });
72981
72982    /**
72983     * @inner
72984     */
72985    function layoutAxis(polar, radiusAxisModel, axisAngle) {
72986        return {
72987            position: [polar.cx, polar.cy],
72988            rotation: axisAngle / 180 * Math.PI,
72989            labelDirection: -1,
72990            tickDirection: -1,
72991            nameDirection: 1,
72992            labelRotate: radiusAxisModel.getModel('axisLabel').get('rotate'),
72993            // Over splitLine and splitArea
72994            z2: 1
72995        };
72996    }
72997
72998    /*
72999* Licensed to the Apache Software Foundation (ASF) under one
73000* or more contributor license agreements.  See the NOTICE file
73001* distributed with this work for additional information
73002* regarding copyright ownership.  The ASF licenses this file
73003* to you under the Apache License, Version 2.0 (the
73004* "License"); you may not use this file except in compliance
73005* with the License.  You may obtain a copy of the License at
73006*
73007*   http://www.apache.org/licenses/LICENSE-2.0
73008*
73009* Unless required by applicable law or agreed to in writing,
73010* software distributed under the License is distributed on an
73011* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
73012* KIND, either express or implied.  See the License for the
73013* specific language governing permissions and limitations
73014* under the License.
73015*/
73016
73017    /*
73018* Licensed to the Apache Software Foundation (ASF) under one
73019* or more contributor license agreements.  See the NOTICE file
73020* distributed with this work for additional information
73021* regarding copyright ownership.  The ASF licenses this file
73022* to you under the Apache License, Version 2.0 (the
73023* "License"); you may not use this file except in compliance
73024* with the License.  You may obtain a copy of the License at
73025*
73026*   http://www.apache.org/licenses/LICENSE-2.0
73027*
73028* Unless required by applicable law or agreed to in writing,
73029* software distributed under the License is distributed on an
73030* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
73031* KIND, either express or implied.  See the License for the
73032* specific language governing permissions and limitations
73033* under the License.
73034*/
73035
73036    var PolarAxisPointer = BaseAxisPointer.extend({
73037
73038        /**
73039         * @override
73040         */
73041        makeElOption: function (elOption, value, axisModel, axisPointerModel, api) {
73042            var axis = axisModel.axis;
73043
73044            if (axis.dim === 'angle') {
73045                this.animationThreshold = Math.PI / 18;
73046            }
73047
73048            var polar = axis.polar;
73049            var otherAxis = polar.getOtherAxis(axis);
73050            var otherExtent = otherAxis.getExtent();
73051
73052            var coordValue;
73053            coordValue = axis['dataTo' + capitalFirst(axis.dim)](value);
73054
73055            var axisPointerType = axisPointerModel.get('type');
73056            if (axisPointerType && axisPointerType !== 'none') {
73057                var elStyle = buildElStyle(axisPointerModel);
73058                var pointerOption = pointerShapeBuilder$2[axisPointerType](
73059                    axis, polar, coordValue, otherExtent, elStyle
73060                );
73061                pointerOption.style = elStyle;
73062                elOption.graphicKey = pointerOption.type;
73063                elOption.pointer = pointerOption;
73064            }
73065
73066            var labelMargin = axisPointerModel.get('label.margin');
73067            var labelPos = getLabelPosition(value, axisModel, axisPointerModel, polar, labelMargin);
73068            buildLabelElOption(elOption, axisModel, axisPointerModel, api, labelPos);
73069        }
73070
73071        // Do not support handle, utill any user requires it.
73072
73073    });
73074
73075    function getLabelPosition(value, axisModel, axisPointerModel, polar, labelMargin) {
73076        var axis = axisModel.axis;
73077        var coord = axis.dataToCoord(value);
73078        var axisAngle = polar.getAngleAxis().getExtent()[0];
73079        axisAngle = axisAngle / 180 * Math.PI;
73080        var radiusExtent = polar.getRadiusAxis().getExtent();
73081        var position;
73082        var align;
73083        var verticalAlign;
73084
73085        if (axis.dim === 'radius') {
73086            var transform = create$1();
73087            rotate(transform, transform, axisAngle);
73088            translate(transform, transform, [polar.cx, polar.cy]);
73089            position = applyTransform$1([coord, -labelMargin], transform);
73090
73091            var labelRotation = axisModel.getModel('axisLabel').get('rotate') || 0;
73092            var labelLayout = AxisBuilder.innerTextLayout(
73093                axisAngle, labelRotation * Math.PI / 180, -1
73094            );
73095            align = labelLayout.textAlign;
73096            verticalAlign = labelLayout.textVerticalAlign;
73097        }
73098        else { // angle axis
73099            var r = radiusExtent[1];
73100            position = polar.coordToPoint([r + labelMargin, coord]);
73101            var cx = polar.cx;
73102            var cy = polar.cy;
73103            align = Math.abs(position[0] - cx) / r < 0.3
73104                ? 'center' : (position[0] > cx ? 'left' : 'right');
73105            verticalAlign = Math.abs(position[1] - cy) / r < 0.3
73106                ? 'middle' : (position[1] > cy ? 'top' : 'bottom');
73107        }
73108
73109        return {
73110            position: position,
73111            align: align,
73112            verticalAlign: verticalAlign
73113        };
73114    }
73115
73116
73117    var pointerShapeBuilder$2 = {
73118
73119        line: function (axis, polar, coordValue, otherExtent, elStyle) {
73120            return axis.dim === 'angle'
73121                ? {
73122                    type: 'Line',
73123                    shape: makeLineShape(
73124                        polar.coordToPoint([otherExtent[0], coordValue]),
73125                        polar.coordToPoint([otherExtent[1], coordValue])
73126                    )
73127                }
73128                : {
73129                    type: 'Circle',
73130                    shape: {
73131                        cx: polar.cx,
73132                        cy: polar.cy,
73133                        r: coordValue
73134                    }
73135                };
73136        },
73137
73138        shadow: function (axis, polar, coordValue, otherExtent, elStyle) {
73139            var bandWidth = Math.max(1, axis.getBandWidth());
73140            var radian = Math.PI / 180;
73141
73142            return axis.dim === 'angle'
73143                ? {
73144                    type: 'Sector',
73145                    shape: makeSectorShape(
73146                        polar.cx, polar.cy,
73147                        otherExtent[0], otherExtent[1],
73148                        // In ECharts y is negative if angle is positive
73149                        (-coordValue - bandWidth / 2) * radian,
73150                        (-coordValue + bandWidth / 2) * radian
73151                    )
73152                }
73153                : {
73154                    type: 'Sector',
73155                    shape: makeSectorShape(
73156                        polar.cx, polar.cy,
73157                        coordValue - bandWidth / 2,
73158                        coordValue + bandWidth / 2,
73159                        0, Math.PI * 2
73160                    )
73161                };
73162        }
73163    };
73164
73165    AxisView.registerAxisPointerClass('PolarAxisPointer', PolarAxisPointer);
73166
73167    /*
73168* Licensed to the Apache Software Foundation (ASF) under one
73169* or more contributor license agreements.  See the NOTICE file
73170* distributed with this work for additional information
73171* regarding copyright ownership.  The ASF licenses this file
73172* to you under the Apache License, Version 2.0 (the
73173* "License"); you may not use this file except in compliance
73174* with the License.  You may obtain a copy of the License at
73175*
73176*   http://www.apache.org/licenses/LICENSE-2.0
73177*
73178* Unless required by applicable law or agreed to in writing,
73179* software distributed under the License is distributed on an
73180* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
73181* KIND, either express or implied.  See the License for the
73182* specific language governing permissions and limitations
73183* under the License.
73184*/
73185
73186// For reducing size of echarts.min, barLayoutPolar is required by polar.
73187    registerLayout(curry(barLayoutPolar, 'bar'));
73188
73189// Polar view
73190    extendComponentView({
73191        type: 'polar'
73192    });
73193
73194    /*
73195* Licensed to the Apache Software Foundation (ASF) under one
73196* or more contributor license agreements.  See the NOTICE file
73197* distributed with this work for additional information
73198* regarding copyright ownership.  The ASF licenses this file
73199* to you under the Apache License, Version 2.0 (the
73200* "License"); you may not use this file except in compliance
73201* with the License.  You may obtain a copy of the License at
73202*
73203*   http://www.apache.org/licenses/LICENSE-2.0
73204*
73205* Unless required by applicable law or agreed to in writing,
73206* software distributed under the License is distributed on an
73207* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
73208* KIND, either express or implied.  See the License for the
73209* specific language governing permissions and limitations
73210* under the License.
73211*/
73212
73213    var GeoModel = ComponentModel.extend({
73214
73215        type: 'geo',
73216
73217        /**
73218         * @type {module:echarts/coord/geo/Geo}
73219         */
73220        coordinateSystem: null,
73221
73222        layoutMode: 'box',
73223
73224        init: function (option) {
73225            ComponentModel.prototype.init.apply(this, arguments);
73226
73227            // Default label emphasis `show`
73228            defaultEmphasis(option, 'label', ['show']);
73229        },
73230
73231        optionUpdated: function () {
73232            var option = this.option;
73233            var self = this;
73234
73235            option.regions = geoCreator.getFilledRegions(option.regions, option.map, option.nameMap);
73236
73237            this._optionModelMap = reduce(option.regions || [], function (optionModelMap, regionOpt) {
73238                if (regionOpt.name) {
73239                    optionModelMap.set(regionOpt.name, new Model(regionOpt, self));
73240                }
73241                return optionModelMap;
73242            }, createHashMap());
73243
73244            this.updateSelectedMap(option.regions);
73245        },
73246
73247        defaultOption: {
73248
73249            zlevel: 0,
73250
73251            z: 0,
73252
73253            show: true,
73254
73255            left: 'center',
73256
73257            top: 'center',
73258
73259
73260            // width:,
73261            // height:,
73262            // right
73263            // bottom
73264
73265            // Aspect is width / height. Inited to be geoJson bbox aspect
73266            // This parameter is used for scale this aspect
73267            aspectScale: 0.75,
73268
73269            ///// Layout with center and size
73270            // If you wan't to put map in a fixed size box with right aspect ratio
73271            // This two properties may more conveninet
73272            // layoutCenter: [50%, 50%]
73273            // layoutSize: 100
73274
73275
73276            silent: false,
73277
73278            // Map type
73279            map: '',
73280
73281            // Define left-top, right-bottom coords to control view
73282            // For example, [ [180, 90], [-180, -90] ]
73283            boundingCoords: null,
73284
73285            // Default on center of map
73286            center: null,
73287
73288            zoom: 1,
73289
73290            scaleLimit: null,
73291
73292            // selectedMode: false
73293
73294            label: {
73295                show: false,
73296                color: '#000'
73297            },
73298
73299            itemStyle: {
73300                // color: 各异,
73301                borderWidth: 0.5,
73302                borderColor: '#444',
73303                color: '#eee'
73304            },
73305
73306            emphasis: {
73307                label: {
73308                    show: true,
73309                    color: 'rgb(100,0,0)'
73310                },
73311                itemStyle: {
73312                    color: 'rgba(255,215,0,0.8)'
73313                }
73314            },
73315
73316            regions: []
73317        },
73318
73319        /**
73320         * Get model of region
73321         * @param  {string} name
73322         * @return {module:echarts/model/Model}
73323         */
73324        getRegionModel: function (name) {
73325            return this._optionModelMap.get(name) || new Model(null, this, this.ecModel);
73326        },
73327
73328        /**
73329         * Format label
73330         * @param {string} name Region name
73331         * @param {string} [status='normal'] 'normal' or 'emphasis'
73332         * @return {string}
73333         */
73334        getFormattedLabel: function (name, status) {
73335            var regionModel = this.getRegionModel(name);
73336            var formatter = regionModel.get('label.' + status + '.formatter');
73337            var params = {
73338                name: name
73339            };
73340            if (typeof formatter === 'function') {
73341                params.status = status;
73342                return formatter(params);
73343            }
73344            else if (typeof formatter === 'string') {
73345                return formatter.replace('{a}', name != null ? name : '');
73346            }
73347        },
73348
73349        setZoom: function (zoom) {
73350            this.option.zoom = zoom;
73351        },
73352
73353        setCenter: function (center) {
73354            this.option.center = center;
73355        }
73356    });
73357
73358    mixin(GeoModel, selectableMixin);
73359
73360    /*
73361* Licensed to the Apache Software Foundation (ASF) under one
73362* or more contributor license agreements.  See the NOTICE file
73363* distributed with this work for additional information
73364* regarding copyright ownership.  The ASF licenses this file
73365* to you under the Apache License, Version 2.0 (the
73366* "License");
73366 you may not use this file except in compliance
73367* with the License.  You may obtain a copy of the License at
73368*
73369*   http://www.apache.org/licenses/LICENSE-2.0
73370*
73371* Unless required by applicable law or agreed to in writing,
73372* software distributed under the License is distributed on an
73373* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
73374* KIND, either express or implied.  See the License for the
73375* specific language governing permissions and limitations
73376* under the License.
73377*/
73378
73379    extendComponentView({
73380
73381        type: 'geo',
73382
73383        init: function (ecModel, api) {
73384            var mapDraw = new MapDraw(api, true);
73385            this._mapDraw = mapDraw;
73386
73387            this.group.add(mapDraw.group);
73388        },
73389
73390        render: function (geoModel, ecModel, api, payload) {
73391            // Not render if it is an toggleSelect action from self
73392            if (payload && payload.type === 'geoToggleSelect'
73393                && payload.from === this.uid
73394            ) {
73395                return;
73396            }
73397
73398            var mapDraw = this._mapDraw;
73399            if (geoModel.get('show')) {
73400                mapDraw.draw(geoModel, ecModel, api, this, payload);
73401            }
73402            else {
73403                this._mapDraw.group.removeAll();
73404            }
73405
73406            this.group.silent = geoModel.get('silent');
73407        },
73408
73409        dispose: function () {
73410            this._mapDraw && this._mapDraw.remove();
73411        }
73412
73413    });
73414
73415    /*
73416* Licensed to the Apache Software Foundation (ASF) under one
73417* or more contributor license agreements.  See the NOTICE file
73418* distributed with this work for additional information
73419* regarding copyright ownership.  The ASF licenses this file
73420* to you under the Apache License, Version 2.0 (the
73421* "License"); you may not use this file except in compliance
73422* with the License.  You may obtain a copy of the License at
73423*
73424*   http://www.apache.org/licenses/LICENSE-2.0
73425*
73426* Unless required by applicable law or agreed to in writing,
73427* software distributed under the License is distributed on an
73428* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
73429* KIND, either express or implied.  See the License for the
73430* specific language governing permissions and limitations
73431* under the License.
73432*/
73433
73434    function makeAction(method, actionInfo) {
73435        actionInfo.update = 'updateView';
73436        registerAction(actionInfo, function (payload, ecModel) {
73437            var selected = {};
73438
73439            ecModel.eachComponent(
73440                { mainType: 'geo', query: payload},
73441                function (geoModel) {
73442                    geoModel[method](payload.name);
73443                    var geo = geoModel.coordinateSystem;
73444                    each$1(geo.regions, function (region) {
73445                        selected[region.name] = geoModel.isSelected(region.name) || false;
73446                    });
73447                }
73448            );
73449
73450            return {
73451                selected: selected,
73452                name: payload.name
73453            };
73454        });
73455    }
73456
73457    makeAction('toggleSelected', {
73458        type: 'geoToggleSelect',
73459        event: 'geoselectchanged'
73460    });
73461    makeAction('select', {
73462        type: 'geoSelect',
73463        event: 'geoselected'
73464    });
73465    makeAction('unSelect', {
73466        type: 'geoUnSelect',
73467        event: 'geounselected'
73468    });
73469
73470    /*
73471* Licensed to the Apache Software Foundation (ASF) under one
73472* or more contributor license agreements.  See the NOTICE file
73473* distributed with this work for additional information
73474* regarding copyright ownership.  The ASF licenses this file
73475* to you under the Apache License, Version 2.0 (the
73476* "License"); you may not use this file except in compliance
73477* with the License.  You may obtain a copy of the License at
73478*
73479*   http://www.apache.org/licenses/LICENSE-2.0
73480*
73481* Unless required by applicable law or agreed to in writing,
73482* software distributed under the License is distributed on an
73483* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
73484* KIND, either express or implied.  See the License for the
73485* specific language governing permissions and limitations
73486* under the License.
73487*/
73488
73489    var DEFAULT_TOOLBOX_BTNS = ['rect', 'polygon', 'keep', 'clear'];
73490
73491    var preprocessor$1 = function (option, isNew) {
73492        var brushComponents = option && option.brush;
73493        if (!isArray(brushComponents)) {
73494            brushComponents = brushComponents ? [brushComponents] : [];
73495        }
73496
73497        if (!brushComponents.length) {
73498            return;
73499        }
73500
73501        var brushComponentSpecifiedBtns = [];
73502
73503        each$1(brushComponents, function (brushOpt) {
73504            var tbs = brushOpt.hasOwnProperty('toolbox')
73505                ? brushOpt.toolbox : [];
73506
73507            if (tbs instanceof Array) {
73508                brushComponentSpecifiedBtns = brushComponentSpecifiedBtns.concat(tbs);
73509            }
73510        });
73511
73512        var toolbox = option && option.toolbox;
73513
73514        if (isArray(toolbox)) {
73515            toolbox = toolbox[0];
73516        }
73517        if (!toolbox) {
73518            toolbox = {feature: {}};
73519            option.toolbox = [toolbox];
73520        }
73521
73522        var toolboxFeature = (toolbox.feature || (toolbox.feature = {}));
73523        var toolboxBrush = toolboxFeature.brush || (toolboxFeature.brush = {});
73524        var brushTypes = toolboxBrush.type || (toolboxBrush.type = []);
73525
73526        brushTypes.push.apply(brushTypes, brushComponentSpecifiedBtns);
73527
73528        removeDuplicate(brushTypes);
73529
73530        if (isNew && !brushTypes.length) {
73531            brushTypes.push.apply(brushTypes, DEFAULT_TOOLBOX_BTNS);
73532        }
73533    };
73534
73535    function removeDuplicate(arr) {
73536        var map$$1 = {};
73537        each$1(arr, function (val) {
73538            map$$1[val] = 1;
73539        });
73540        arr.length = 0;
73541        each$1(map$$1, function (flag, val) {
73542            arr.push(val);
73543        });
73544    }
73545
73546    /*
73547* Licensed to the Apache Software Foundation (ASF) under one
73548* or more contributor license agreements.  See the NOTICE file
73549* distributed with this work for additional information
73550* regarding copyright ownership.  The ASF licenses this file
73551* to you under the Apache License, Version 2.0 (the
73552* "License");
73552 you may not use this file except in compliance
73553* with the License.  You may obtain a copy of the License at
73554*
73555*   http://www.apache.org/licenses/LICENSE-2.0
73556*
73557* Unless required by applicable law or agreed to in writing,
73558* software distributed under the License is distributed on an
73559* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
73560* KIND, either express or implied.  See the License for the
73561* specific language governing permissions and limitations
73562* under the License.
73563*/
73564
73565    /**
73566     * @file Visual solution, for consistent option specification.
73567     */
73568
73569    var each$19 = each$1;
73570
73571    function hasKeys(obj) {
73572        if (obj) {
73573            for (var name in obj){
73574                if (obj.hasOwnProperty(name)) {
73575                    return true;
73576                }
73577            }
73578        }
73579    }
73580
73581    /**
73582     * @param {Object} option
73583     * @param {Array.<string>} stateList
73584     * @param {Function} [supplementVisualOption]
73585     * @return {Object} visualMappings <state, <visualType, module:echarts/visual/VisualMapping>>
73586     */
73587    function createVisualMappings(option, stateList, supplementVisualOption) {
73588        var visualMappings = {};
73589
73590        each$19(stateList, function (state) {
73591            var mappings = visualMappings[state] = createMappings();
73592
73593            each$19(option[state], function (visualData, visualType) {
73594                if (!VisualMapping.isValidType(visualType)) {
73595                    return;
73596                }
73597                var mappingOption = {
73598                    type: visualType,
73599                    visual: visualData
73600                };
73601                supplementVisualOption && supplementVisualOption(mappingOption, state);
73602                mappings[visualType] = new VisualMapping(mappingOption);
73603
73604                // Prepare a alpha for opacity, for some case that opacity
73605                // is not supported, such as rendering using gradient color.
73606                if (visualType === 'opacity') {
73607                    mappingOption = clone(mappingOption);
73608                    mappingOption.type = 'colorAlpha';
73609                    mappings.__hidden.__alphaForOpacity = new VisualMapping(mappingOption);
73610                }
73611            });
73612        });
73613
73614        return visualMappings;
73615
73616        function createMappings() {
73617            var Creater = function () {};
73618            // Make sure hidden fields will not be visited by
73619            // object iteration (with hasOwnProperty checking).
73620            Creater.prototype.__hidden = Creater.prototype;
73621            var obj = new Creater();
73622            return obj;
73623        }
73624    }
73625
73626    /**
73627     * @param {Object} thisOption
73628     * @param {Object} newOption
73629     * @param {Array.<string>} keys
73630     */
73631    function replaceVisualOption(thisOption, newOption, keys) {
73632        // Visual attributes merge is not supported, otherwise it
73633        // brings overcomplicated merge logic. See #2853. So if
73634        // newOption has anyone of these keys, all of these keys
73635        // will be reset. Otherwise, all keys remain.
73636        var has;
73637        each$1(keys, function (key) {
73638            if (newOption.hasOwnProperty(key) && hasKeys(newOption[key])) {
73639                has = true;
73640            }
73641        });
73642        has && each$1(keys, function (key) {
73643            if (newOption.hasOwnProperty(key) && hasKeys(newOption[key])) {
73644                thisOption[key] = clone(newOption[key]);
73645            }
73646            else {
73647                delete thisOption[key];
73648            }
73649        });
73650    }
73651
73652    /**
73653     * @param {Array.<string>} stateList
73654     * @param {Object} visualMappings <state, Object.<visualType, module:echarts/visual/VisualMapping>>
73655     * @param {module:echarts/data/List} list
73656     * @param {Function} getValueState param: valueOrIndex, return: state.
73657     * @param {object} [scope] Scope for getValueState
73658     * @param {string} [dimension] Concrete dimension, if used.
73659     */
73660// ???! handle brush?
73661    function applyVisual(stateList, visualMappings, data, getValueState, scope, dimension) {
73662        var visualTypesMap = {};
73663        each$1(stateList, function (state) {
73664            var visualTypes = VisualMapping.prepareVisualTypes(visualMappings[state]);
73665            visualTypesMap[state] = visualTypes;
73666        });
73667
73668        var dataIndex;
73669
73670        function getVisual(key) {
73671            return data.getItemVisual(dataIndex, key);
73672        }
73673
73674        function setVisual(key, value) {
73675            data.setItemVisual(dataIndex, key, value);
73676        }
73677
73678        if (dimension == null) {
73679            data.each(eachItem);
73680        }
73681        else {
73682            data.each([dimension], eachItem);
73683        }
73684
73685        function eachItem(valueOrIndex, index) {
73686            dataIndex = dimension == null ? valueOrIndex : index;
73687
73688            var rawDataItem = data.getRawDataItem(dataIndex);
73689            // Consider performance
73690            if (rawDataItem && rawDataItem.visualMap === false) {
73691                return;
73692            }
73693
73694            var valueState = getValueState.call(scope, valueOrIndex);
73695            var mappings = visualMappings[valueState];
73696            var visualTypes = visualTypesMap[valueState];
73697
73698            for (var i = 0, len = visualTypes.length; i < len; i++) {
73699                var type = visualTypes[i];
73700                mappings[type] && mappings[type].applyVisual(
73701                    valueOrIndex, getVisual, setVisual
73702                );
73703            }
73704        }
73705    }
73706
73707    /**
73708     * @param {module:echarts/data/List} data
73709     * @param {Array.<string>} stateList
73710     * @param {Object} visualMappings <state, Object.<visualType, module:echarts/visual/VisualMapping>>
73711     * @param {Function} getValueState param: valueOrIndex, return: state.
73712     * @param {number} [dim] dimension or dimension index.
73713     */
73714    function incrementalApplyVisual(stateList, visualMappings, getValueState, dim) {
73715        var visualTypesMap = {};
73716        each$1(stateList, function (state) {
73717            var visualTypes = VisualMapping.prepareVisualTypes(visualMappings[state]);
73718            visualTypesMap[state] = visualTypes;
73719        });
73720
73721        function progress(params, data) {
73722            if (dim != null) {
73723                dim = data.getDimension(dim);
73724            }
73725
73726            function getVisual(key) {
73727                return data.getItemVisual(dataIndex, key);
73728            }
73729
73730            function setVisual(key, value) {
73731                data.setItemVisual(dataIndex, key, value);
73732            }
73733
73734            var dataIndex;
73735            while ((dataIndex = params.next()) != null) {
73736                var rawDataItem = data.getRawDataItem(dataIndex);
73737
73738                // Consider performance
73739                if (rawDataItem && rawDataItem.visualMap === false) {
73740                    return;
73741                }
73742
73743                var value = dim != null
73744                    ? data.get(dim, dataIndex, true)
73745                    : dataIndex;
73746
73747                var valueState = getValueState(value);
73748                var mappings = visualMappings[valueState];
73749                var visualTypes = visualTypesMap[valueState];
73750
73751                for (var i = 0, len = visualTypes.length; i < len; i++) {
73752                    var type = visualTypes[i];
73753                    mappings[type] && mappings[type].applyVisual(value, getVisual, setVisual);
73754                }
73755            }
73756        }
73757
73758        return {progress: progress};
73759    }
73760
73761    /*
73762* Licensed to the Apache Software Foundation (ASF) under one
73763* or more contributor license agreements.  See the NOTICE file
73764* distributed with this work for additional information
73765* regarding copyright ownership.  The ASF licenses this file
73766* to you under the Apache License, Version 2.0 (the
73767* "License"); you may not use this file except in compliance
73768* with the License.  You may obtain a copy of the License at
73769*
73770*   http://www.apache.org/licenses/LICENSE-2.0
73771*
73772* Unless required by applicable law or agreed to in writing,
73773* software distributed under the License is distributed on an
73774* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
73775* KIND, either express or implied.  See the License for the
73776* specific language governing permissions and limitations
73777* under the License.
73778*/
73779
73780// Key of the first level is brushType: `line`, `rect`, `polygon`.
73781// Key of the second level is chart element type: `point`, `rect`.
73782// See moudule:echarts/component/helper/BrushController
73783// function param:
73784//      {Object} itemLayout fetch from data.getItemLayout(dataIndex)
73785//      {Object} selectors {point: selector, rect: selector, ...}
73786//      {Object} area {range: [[], [], ..], boudingRect}
73787// function return:
73788//      {boolean} Whether in the given brush.
73789    var selector = {
73790        lineX: getLineSelectors(0),
73791        lineY: getLineSelectors(1),
73792        rect: {
73793            point: function (itemLayout, selectors, area) {
73794                return itemLayout && area.boundingRect.contain(itemLayout[0], itemLayout[1]);
73795            },
73796            rect: function (itemLayout, selectors, area) {
73797                return itemLayout && area.boundingRect.intersect(itemLayout);
73798            }
73799        },
73800        polygon: {
73801            point: function (itemLayout, selectors, area) {
73802                return itemLayout
73803                    && area.boundingRect.contain(itemLayout[0], itemLayout[1])
73804                    && contain$1(area.range, itemLayout[0], itemLayout[1]);
73805            },
73806            rect: function (itemLayout, selectors, area) {
73807                var points = area.range;
73808
73809                if (!itemLayout || points.length <= 1) {
73810                    return false;
73811                }
73812
73813                var x = itemLayout.x;
73814                var y = itemLayout.y;
73815                var width = itemLayout.width;
73816                var height = itemLayout.height;
73817                var p = points[0];
73818
73819                if (contain$1(points, x, y)
73820                    || contain$1(points, x + width, y)
73821                    || contain$1(points, x, y + height)
73822                    || contain$1(points, x + width, y + height)
73823                    || BoundingRect.create(itemLayout).contain(p[0], p[1])
73824                    || lineIntersectPolygon(x, y, x + width, y, points)
73825                    || lineIntersectPolygon(x, y, x, y + height, points)
73826                    || lineIntersectPolygon(x + width, y, x + width, y + height, points)
73827                    || lineIntersectPolygon(x, y + height, x + width, y + height, points)
73828                ) {
73829                    return true;
73830                }
73831            }
73832        }
73833    };
73834
73835    function getLineSelectors(xyIndex) {
73836        var xy = ['x', 'y'];
73837        var wh = ['width', 'height'];
73838
73839        return {
73840            point: function (itemLayout, selectors, area) {
73841                if (itemLayout) {
73842                    var range = area.range;
73843                    var p = itemLayout[xyIndex];
73844                    return inLineRange(p, range);
73845                }
73846            },
73847            rect: function (itemLayout, selectors, area) {
73848                if (itemLayout) {
73849                    var range = area.range;
73850                    var layoutRange = [
73851                        itemLayout[xy[xyIndex]],
73852                        itemLayout[xy[xyIndex]] + itemLayout[wh[xyIndex]]
73853                    ];
73854                    layoutRange[1] < layoutRange[0] && layoutRange.reverse();
73855                    return inLineRange(layoutRange[0], range)
73856                        || inLineRange(layoutRange[1], range)
73857                        || inLineRange(range[0], layoutRange)
73858                        || inLineRange(range[1], layoutRange);
73859                }
73860            }
73861        };
73862    }
73863
73864    function inLineRange(p, range) {
73865        return range[0] <= p && p <= range[1];
73866    }
73867
73868    function lineIntersectPolygon(lx, ly, l2x, l2y, points) {
73869        for (var i = 0, p2 = points[points.length - 1]; i < points.length; i++) {
73870            var p = points[i];
73871            if (lineIntersect(lx, ly, l2x, l2y, p[0], p[1], p2[0], p2[1])) {
73872                return true;
73873            }
73874            p2 = p;
73875        }
73876    }
73877
73878// Code from <http://blog.csdn.net/rickliuxiao/article/details/6259322> with some fix.
73879// See <https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection>
73880    function lineIntersect(a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y) {
73881        var delta = determinant(a2x - a1x, b1x - b2x, a2y - a1y, b1y - b2y);
73882        if (nearZero(delta)) { // parallel
73883            return false;
73884        }
73885        var namenda = determinant(b1x - a1x, b1x - b2x, b1y - a1y, b1y - b2y) / delta;
73886        if (namenda < 0 || namenda > 1) {
73887            return false;
73888        }
73889        var miu = determinant(a2x - a1x, b1x - a1x, a2y - a1y, b1y - a1y) / delta;
73890        if (miu < 0 || miu > 1) {
73891            return false;
73892        }
73893        return true;
73894    }
73895
73896    function nearZero(val) {
73897        return val <= (1e-6) && val >= -(1e-6);
73898    }
73899
73900    function determinant(v1, v2, v3, v4) {
73901        return v1 * v4 - v2 * v3;
73902    }
73903
73904    /*
73905* Licensed to the Apache Software Foundation (ASF) under one
73906* or more contributor license agreements.  See the NOTICE file
73907* distributed with this work for additional information
73908* regarding copyright ownership.  The ASF licenses this file
73909* to you under the Apache License, Version 2.0 (the
73910* "License");
73910 you may not use this file except in compliance
73911* with the License.  You may obtain a copy of the License at
73912*
73913*   http://www.apache.org/licenses/LICENSE-2.0
73914*
73915* Unless required by applicable law or agreed to in writing,
73916* software distributed under the License is distributed on an
73917* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
73918* KIND, either express or implied.  See the License for the
73919* specific language governing permissions and limitations
73920* under the License.
73921*/
73922
73923    var each$20 = each$1;
73924    var indexOf$1 = indexOf;
73925    var curry$5 = curry;
73926
73927    var COORD_CONVERTS = ['dataToPoint', 'pointToData'];
73928
73929// FIXME
73930// how to genarialize to more coordinate systems.
73931    var INCLUDE_FINDER_MAIN_TYPES = [
73932        'grid', 'xAxis', 'yAxis', 'geo', 'graph',
73933        'polar', 'radiusAxis', 'angleAxis', 'bmap'
73934    ];
73935
73936    /**
73937     * [option in constructor]:
73938     * {
73939 *     Index/Id/Name of geo, xAxis, yAxis, grid: See util/model#parseFinder.
73940 * }
73941     *
73942     *
73943     * [targetInfo]:
73944     *
73945     * There can be multiple axes in a single targetInfo. Consider the case
73946     * of `grid` component, a targetInfo represents a grid which contains one or more
73947     * cartesian and one or more axes. And consider the case of parallel system,
73948     * which has multiple axes in a coordinate system.
73949     * Can be {
73950 *     panelId: ...,
73951 *     coordSys: <a representitive cartesian in grid (first cartesian by default)>,
73952 *     coordSyses: all cartesians.
73953 *     gridModel: <grid component>
73954 *     xAxes: correspond to coordSyses on index
73955 *     yAxes: correspond to coordSyses on index
73956 * }
73957     * or {
73958 *     panelId: ...,
73959 *     coordSys: <geo coord sys>
73960 *     coordSyses: [<geo coord sys>]
73961 *     geoModel: <geo component>
73962 * }
73963     *
73964     *
73965     * [panelOpt]:
73966     *
73967     * Make from targetInfo. Input to BrushController.
73968     * {
73969 *     panelId: ...,
73970 *     rect: ...
73971 * }
73972     *
73973     *
73974     * [area]:
73975     *
73976     * Generated by BrushController or user input.
73977     * {
73978 *     panelId: Used to locate coordInfo directly. If user inpput, no panelId.
73979 *     brushType: determine how to convert to/from coord('rect' or 'polygon' or 'lineX/Y').
73980 *     Index/Id/Name of geo, xAxis, yAxis, grid: See util/model#parseFinder.
73981 *     range: pixel range.
73982 *     coordRange: representitive coord range (the first one of coordRanges).
73983 *     coordRanges: <Array> coord ranges, used in multiple cartesian in one grid.
73984 * }
73985     */
73986
73987    /**
73988     * @param {Object} option contains Index/Id/Name of xAxis/yAxis/geo/grid
73989     *        Each can be {number|Array.<number>}. like: {xAxisIndex: [3, 4]}
73990     * @param {module:echarts/model/Global} ecModel
73991     * @param {Object} [opt]
73992     * @param {Array.<string>} [opt.include] include coordinate system types.
73993     */
73994    function BrushTargetManager(option, ecModel, opt) {
73995        /**
73996         * @private
73997         * @type {Array.<Object>}
73998         */
73999        var targetInfoList = this._targetInfoList = [];
74000        var info = {};
74001        var foundCpts = parseFinder$1(ecModel, option);
74002
74003        each$20(targetInfoBuilders, function (builder, type) {
74004            if (!opt || !opt.include || indexOf$1(opt.include, type) >= 0) {
74005                builder(foundCpts, targetInfoList, info);
74006            }
74007        });
74008    }
74009
74010    var proto$2 = BrushTargetManager.prototype;
74011
74012    proto$2.setOutputRanges = function (areas, ecModel) {
74013        this.matchOutputRanges(areas, ecModel, function (area, coordRange, coordSys) {
74014            (area.coordRanges || (area.coordRanges = [])).push(coordRange);
74015            // area.coordRange is the first of area.coordRanges
74016            if (!area.coordRange) {
74017                area.coordRange = coordRange;
74018                // In 'category' axis, coord to pixel is not reversible, so we can not
74019                // rebuild range by coordRange accrately, which may bring trouble when
74020                // brushing only one item. So we use __rangeOffset to rebuilding range
74021                // by coordRange. And this it only used in brush component so it is no
74022                // need to be adapted to coordRanges.
74023                var result = coordConvert[area.brushType](0, coordSys, coordRange);
74024                area.__rangeOffset = {
74025                    offset: diffProcessor[area.brushType](result.values, area.range, [1, 1]),
74026                    xyMinMax: result.xyMinMax
74027                };
74028            }
74029        });
74030    };
74031
74032    proto$2.matchOutputRanges = function (areas, ecModel, cb) {
74033        each$20(areas, function (area) {
74034            var targetInfo = this.findTargetInfo(area, ecModel);
74035
74036            if (targetInfo && targetInfo !== true) {
74037                each$1(
74038                    targetInfo.coordSyses,
74039                    function (coordSys) {
74040                        var result = coordConvert[area.brushType](1, coordSys, area.range);
74041                        cb(area, result.values, coordSys, ecModel);
74042                    }
74043                );
74044            }
74045        }, this);
74046    };
74047
74048    proto$2.setInputRanges = function (areas, ecModel) {
74049        each$20(areas, function (area) {
74050            var targetInfo = this.findTargetInfo(area, ecModel);
74051
74052            if (__DEV__) {
74053                assert$1(
74054                    !targetInfo || targetInfo === true || area.coordRange,
vendor: 6,165 bytes, lines 74055-74205
74055                    'coordRange must be specified when coord index specified.'
74056                );
74057                assert$1(
74058                    !targetInfo || targetInfo !== true || area.range,
74059                    'range must be specified in global brush.'
74060                );
74061            }
74062
74063            area.range = area.range || [];
74064
74065            // convert coordRange to global range and set panelId.
74066            if (targetInfo && targetInfo !== true) {
74067                area.panelId = targetInfo.panelId;
74068                // (1) area.range shoule always be calculate from coordRange but does
74069                // not keep its original value, for the sake of the dataZoom scenario,
74070                // where area.coordRange remains unchanged but area.range may be changed.
74071                // (2) Only support converting one coordRange to pixel range in brush
74072                // component. So do not consider `coordRanges`.
74073                // (3) About __rangeOffset, see comment above.
74074                var result = coordConvert[area.brushType](0, targetInfo.coordSys, area.coordRange);
74075                var rangeOffset = area.__rangeOffset;
74076                area.range = rangeOffset
74077                    ? diffProcessor[area.brushType](
74078                        result.values,
74079                        rangeOffset.offset,
74080                        getScales(result.xyMinMax, rangeOffset.xyMinMax)
74081                    )
74082                    : result.values;
74083            }
74084        }, this);
74085    };
74086
74087    proto$2.makePanelOpts = function (api, getDefaultBrushType) {
74088        return map(this._targetInfoList, function (targetInfo) {
74089            var rect = targetInfo.getPanelRect();
74090            return {
74091                panelId: targetInfo.panelId,
74092                defaultBrushType: getDefaultBrushType && getDefaultBrushType(targetInfo),
74093                clipPath: makeRectPanelClipPath(rect),
74094                isTargetByCursor: makeRectIsTargetByCursor(
74095                    rect, api, targetInfo.coordSysModel
74096                ),
74097                getLinearBrushOtherExtent: makeLinearBrushOtherExtent(rect)
74098            };
74099        });
74100    };
74101
74102    proto$2.controlSeries = function (area, seriesModel, ecModel) {
74103        // Check whether area is bound in coord, and series do not belong to that coord.
74104        // If do not do this check, some brush (like lineX) will controll all axes.
74105        var targetInfo = this.findTargetInfo(area, ecModel);
74106        return targetInfo === true || (
74107            targetInfo && indexOf$1(targetInfo.coordSyses, seriesModel.coordinateSystem) >= 0
74108        );
74109    };
74110
74111    /**
74112     * If return Object, a coord found.
74113     * If reutrn true, global found.
74114     * Otherwise nothing found.
74115     *
74116     * @param {Object} area
74117     * @param {Array} targetInfoList
74118     * @return {Object|boolean}
74119     */
74120    proto$2.findTargetInfo = function (area, ecModel) {
74121        var targetInfoList = this._targetInfoList;
74122        var foundCpts = parseFinder$1(ecModel, area);
74123
74124        for (var i = 0; i < targetInfoList.length; i++) {
74125            var targetInfo = targetInfoList[i];
74126            var areaPanelId = area.panelId;
74127            if (areaPanelId) {
74128                if (targetInfo.panelId === areaPanelId) {
74129                    return targetInfo;
74130                }
74131            }
74132            else {
74133                for (var i = 0; i < targetInfoMatchers.length; i++) {
74134                    if (targetInfoMatchers[i](foundCpts, targetInfo)) {
74135                        return targetInfo;
74136                    }
74137                }
74138            }
74139        }
74140
74141        return true;
74142    };
74143
74144    function formatMinMax(minMax) {
74145        minMax[0] > minMax[1] && minMax.reverse();
74146        return minMax;
74147    }
74148
74149    function parseFinder$1(ecModel, option) {
74150        return parseFinder(
74151            ecModel, option, {includeMainTypes: INCLUDE_FINDER_MAIN_TYPES}
74152        );
74153    }
74154
74155    var targetInfoBuilders = {
74156
74157        grid: function (foundCpts, targetInfoList) {
74158            var xAxisModels = foundCpts.xAxisModels;
74159            var yAxisModels = foundCpts.yAxisModels;
74160            var gridModels = foundCpts.gridModels;
74161            // Remove duplicated.
74162            var gridModelMap = createHashMap();
74163            var xAxesHas = {};
74164            var yAxesHas = {};
74165
74166            if (!xAxisModels && !yAxisModels && !gridModels) {
74167                return;
74168            }
74169
74170            each$20(xAxisModels, function (axisModel) {
74171                var gridModel = axisModel.axis.grid.model;
74172                gridModelMap.set(gridModel.id, gridModel);
74173                xAxesHas[gridModel.id] = true;
74174            });
74175            each$20(yAxisModels, function (axisModel) {
74176                var gridModel = axisModel.axis.grid.model;
74177                gridModelMap.set(gridModel.id, gridModel);
74178                yAxesHas[gridModel.id] = true;
74179            });
74180            each$20(gridModels, function (gridModel) {
74181                gridModelMap.set(gridModel.id, gridModel);
74182                xAxesHas[gridModel.id] = true;
74183                yAxesHas[gridModel.id] = true;
74184            });
74185
74186            gridModelMap.each(function (gridModel) {
74187                var grid = gridModel.coordinateSystem;
74188                var cartesians = [];
74189
74190                each$20(grid.getCartesians(), function (cartesian, index) {
74191                    if (indexOf$1(xAxisModels, cartesian.getAxis('x').model) >= 0
74192                        || indexOf$1(yAxisModels, cartesian.getAxis('y').model) >= 0
74193                    ) {
74194                        cartesians.push(cartesian);
74195                    }
74196                });
74197                targetInfoList.push({
74198                    panelId: 'grid--' + gridModel.id,
74199                    gridModel: gridModel,
74200                    coordSysModel: gridModel,
74201                    // Use the first one as the representitive coordSys.
74202                    coordSys: cartesians[0],
74203                    coordSyses: cartesians,
74204                    getPanelRect: panelRectBuilder.grid,
74205                    xAxisDeclared: xAxesHas[gridModel.id],
74206                    yAxisDeclared: yAxesHas[gridModel.id]
74207                });
74208            });
74209        },
74210
74211        geo: function (foundCpts, targetInfoList) {
74212            each$20(foundCpts.geoModels, function (geoModel) {
74213                var coordSys = geoModel.coordinateSystem;
74214                targetInfoList.push({
74215                    panelId: 'geo--' + geoModel.id,
74216                    geoModel: geoModel,
74217                    coordSysModel: geoModel,
74218                    coordSys: coordSys,
74219                    coordSyses: [coordSys],
74220                    getPanelRect: panelRectBuilder.geo
74221                });
74222            });
74223        }
74224    };
74225
74226    var targetInfoMatchers = [
74227
74228        // grid
74229        function (foundCpts, targetInfo) {
74230            var xAxisModel = foundCpts.xAxisModel;
74231            var yAxisModel = foundCpts.yAxisModel;
74232            var gridModel = foundCpts.gridModel;
74233
74234            !gridModel && xAxisModel && (gridModel = xAxisModel.axis.grid.model);
74235            !gridModel && yAxisModel && (gridModel = yAxisModel.axis.grid.model);
74236
74237            return gridModel && gridModel === targetInfo.gridModel;
74238        },
74239
74240        // geo
74241        function (foundCpts, targetInfo) {
74242            var geoModel = foundCpts.geoModel;
74243            return geoModel && geoModel === targetInfo.geoModel;
74244        }
74245    ];
74246
74247    var panelRectBuilder = {
74248
74249        grid: function () {
74250            // grid is not Transformable.
74251            return this.coordSys.grid.getRect().clone();
74252        },
74253
74254        geo: function () {
74255            var coordSys = this.coordSys;
74256            var rect = coordSys.getBoundingRect().clone();
74257            // geo roam and zoom transform
74258            rect.applyTransform(getTransform(coordSys));
74259            return rect;
74260        }
74261    };
74262
74263    var coordConvert = {
74264
74265        lineX: curry$5(axisConvert, 0),
74266
74267        lineY: curry$5(axisConvert, 1),
74268
74269        rect: function (to, coordSys, rangeOrCoordRange) {
74270            var xminymin = coordSys[COORD_CONVERTS[to]]([rangeOrCoordRange[0][0], rangeOrCoordRange[1][0]]);
74271            var xmaxymax = coordSys[COORD_CONVERTS[to]]([rangeOrCoordRange[0][1], rangeOrCoordRange[1][1]]);
74272            var values = [
74273                formatMinMax([xminymin[0], xmaxymax[0]]),
74274                formatMinMax([xminymin[1], xmaxymax[1]])
74275            ];
74276            return {values: values, xyMinMax: values};
74277        },
74278
74279        polygon: function (to, coordSys, rangeOrCoordRange) {
74280            var xyMinMax = [[Infinity, -Infinity], [Infinity, -Infinity]];
74281            var values = map(rangeOrCoordRange, function (item) {
74282                var p = coordSys[COORD_CONVERTS[to]](item);
74283                xyMinMax[0][0] = Math.min(xyMinMax[0][0], p[0]);
74284                xyMinMax[1][0] = Math.min(xyMinMax[1][0], p[1]);
74285                xyMinMax[0][1] = Math.max(xyMinMax[0][1], p[0]);
74286                xyMinMax[1][1] = Math.max(xyMinMax[1][1], p[1]);
74287                return p;
74288            });
74289            return {values: values, xyMinMax: xyMinMax};
74290        }
74291    };
74292
74293    function axisConvert(axisNameIndex, to, coordSys, rangeOrCoordRange) {
74294        if (__DEV__) {
74295            assert$1(
74296                coordSys.type === 'cartesian2d',
74297                'lineX/lineY brush is available only in cartesian2d.'
74298            );
74299        }
74300
74301        var axis = coordSys.getAxis(['x', 'y'][axisNameIndex]);
74302        var values = formatMinMax(map([0, 1], function (i) {
74303            return to
74304                ? axis.coordToData(axis.toLocalCoord(rangeOrCoordRange[i]))
74305                : axis.toGlobalCoord(axis.dataToCoord(rangeOrCoordRange[i]));
74306        }));
74307        var xyMinMax = [];
74308        xyMinMax[axisNameIndex] = values;
74309        xyMinMax[1 - axisNameIndex] = [NaN, NaN];
74310
74311        return {values: values, xyMinMax: xyMinMax};
74312    }
74313
74314    var diffProcessor = {
74315        lineX: curry$5(axisDiffProcessor, 0),
74316
74317        lineY: curry$5(axisDiffProcessor, 1),
74318
74319        rect: function (values, refer, scales) {
74320            return [
74321                [values[0][0] - scales[0] * refer[0][0], values[0][1] - scales[0] * refer[0][1]],
74322                [values[1][0] - scales[1] * refer[1][0], values[1][1] - scales[1] * refer[1][1]]
74323            ];
74324        },
74325
74326        polygon: function (values, refer, scales) {
74327            return map(values, function (item, idx) {
74328                return [item[0] - scales[0] * refer[idx][0], item[1] - scales[1] * refer[idx][1]];
74329            });
74330        }
74331    };
74332
74333    function axisDiffProcessor(axisNameIndex, values, refer, scales) {
74334        return [
74335            values[0] - scales[axisNameIndex] * refer[0],
74336            values[1] - scales[axisNameIndex] * refer[1]
74337        ];
74338    }
74339
74340// We have to process scale caused by dataZoom manually,
74341// although it might be not accurate.
74342    function getScales(xyMinMaxCurr, xyMinMaxOrigin) {
74343        var sizeCurr = getSize(xyMinMaxCurr);
74344        var sizeOrigin = getSize(xyMinMaxOrigin);
74345        var scales = [sizeCurr[0] / sizeOrigin[0], sizeCurr[1] / sizeOrigin[1]];
74346        isNaN(scales[0]) && (scales[0] = 1);
74347        isNaN(scales[1]) && (scales[1] = 1);
74348        return scales;
74349    }
74350
74351    function getSize(xyMinMax) {
74352        return xyMinMax
74353            ? [xyMinMax[0][1] - xyMinMax[0][0], xyMinMax[1][1] - xyMinMax[1][0]]
74354            : [NaN, NaN];
74355    }
74356
74357    /*
74358* Licensed to the Apache Software Foundation (ASF) under one
74359* or more contributor license agreements.  See the NOTICE file
74360* distributed with this work for additional information
74361* regarding copyright ownership.  The ASF licenses this file
74362* to you under the Apache License, Version 2.0 (the
74363* "License"); you may not use this file except in compliance
74364* with the License.  You may obtain a copy of the License at
74365*
74366*   http://www.apache.org/licenses/LICENSE-2.0
74367*
74368* Unless required by applicable law or agreed to in writing,
74369* software distributed under the License is distributed on an
74370* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
74371* KIND, either express or implied.  See the License for the
74372* specific language governing permissions and limitations
74373* under the License.
74374*/
74375
74376    var STATE_LIST = ['inBrush', 'outOfBrush'];
74377    var DISPATCH_METHOD = '__ecBrushSelect';
74378    var DISPATCH_FLAG = '__ecInBrushSelectEvent';
74379    var PRIORITY_BRUSH = PRIORITY.VISUAL.BRUSH;
74380
74381    /**
74382     * Layout for visual, the priority higher than other layout, and before brush visual.
74383     */
74384    registerLayout(PRIORITY_BRUSH, function (ecModel, api, payload) {
74385        ecModel.eachComponent({mainType: 'brush'}, function (brushModel) {
74386
74387            payload && payload.type === 'takeGlobalCursor' && brushModel.setBrushOption(
74388                payload.key === 'brush' ? payload.brushOption : {brushType: false}
74389            );
74390
74391            var brushTargetManager = brushModel.brushTargetManager = new BrushTargetManager(brushModel.option, ecModel);
74392
74393            brushTargetManager.setInputRanges(brushModel.areas, ecModel);
74394        });
74395    });
74396
74397    /**
74398     * Register the visual encoding if this modules required.
74399     */
74400    registerVisual(PRIORITY_BRUSH, function (ecModel, api, payload) {
74401
74402        var brushSelected = [];
74403        var throttleType;
74404        var throttleDelay;
74405
74406        ecModel.eachComponent({mainType: 'brush'}, function (brushModel, brushIndex) {
74407
74408            var thisBrushSelected = {
74409                brushId: brushModel.id,
74410                brushIndex: brushIndex,
74411                brushName: brushModel.name,
74412                areas: clone(brushModel.areas),
74413                selected: []
74414            };
74415            // Every brush component exists in event params, convenient
74416            // for user to find by index.
74417            brushSelected.push(thisBrushSelected);
74418
74419            var brushOption = brushModel.option;
74420            var brushLink = brushOption.brushLink;
74421            var linkedSeriesMap = [];
74422            var selectedDataIndexForLink = [];
74423            var rangeInfoBySeries = [];
74424            var hasBrushExists = 0;
74425
74426            if (!brushIndex) { // Only the first throttle setting works.
74427                throttleType = brushOption.throttleType;
74428                throttleDelay = brushOption.throttleDelay;
74429            }
74430
74431            // Add boundingRect and selectors to range.
74432            var areas = map(brushModel.areas, function (area) {
74433                return bindSelector(
74434                    defaults(
74435                        {boundingRect: boundingRectBuilders[area.brushType](area)},
74436                        area
74437                    )
74438                );
74439            });
74440
74441            var visualMappings = createVisualMappings(
74442                brushModel.option, STATE_LIST, function (mappingOption) {
74443                    mappingOption.mappingMethod = 'fixed';
74444                }
74445            );
74446
74447            isArray(brushLink) && each$1(brushLink, function (seriesIndex) {
74448                linkedSeriesMap[seriesIndex] = 1;
74449            });
74450
74451            function linkOthers(seriesIndex) {
74452                return brushLink === 'all' || linkedSeriesMap[seriesIndex];
74453            }
74454
74455            // If no supported brush or no brush on the series,
74456            // all visuals should be in original state.
74457            function brushed(rangeInfoList) {
74458                return !!rangeInfoList.length;
74459            }
74460
74461            /**
74462             * Logic for each series: (If the logic has to be modified one day, do it carefully!)
74463             *
74464             * ( brushed ┬ && ┬hasBrushExist ┬ && linkOthers  ) =>
74464 StepA: ┬record, ┬ StepB: ┬visualByRecord.
74465             *   !brushed┘    ├hasBrushExist ┤                            └nothing,┘        ├visualByRecord.
74466             *                └!hasBrushExist┘                                              └nothing.
74467             * ( !brushed  && ┬hasBrushExist ┬ && linkOthers  ) => StepA:  nothing,  StepB: ┬visualByRecord.
74468             *                └!hasBrushExist┘                                              └nothing.
74469             * ( brushed ┬ &&                     !linkOthers ) => StepA:  nothing,  StepB: ┬visualByCheck.
74470             *   !brushed┘                                                                  └nothing.
74471             * ( !brushed  &&                     !linkOthers ) => StepA:  nothing,  StepB:  nothing.
74472             */
74473
74474            // Step A
74475            ecModel.eachSeries(function (seriesModel, seriesIndex) {
74476                var rangeInfoList = rangeInfoBySeries[seriesIndex] = [];
74477
74478                seriesModel.subType === 'parallel'
74479                    ? stepAParallel(seriesModel, seriesIndex, rangeInfoList)
74480                    : stepAOthers(seriesModel, seriesIndex, rangeInfoList);
74481            });
74482
74483            function stepAParallel(seriesModel, seriesIndex) {
74484                var coordSys = seriesModel.coordinateSystem;
74485                hasBrushExists |= coordSys.hasAxisBrushed();
74486
74487                linkOthers(seriesIndex) && coordSys.eachActiveState(
74488                    seriesModel.getData(),
74489                    function (activeState, dataIndex) {
74490                        activeState === 'active' && (selectedDataIndexForLink[dataIndex] = 1);
74491                    }
74492                );
74493            }
74494
74495            function stepAOthers(seriesModel, seriesIndex, rangeInfoList) {
74496                var selectorsByBrushType = getSelectorsByBrushType(seriesModel);
74497                if (!selectorsByBrushType || brushModelNotControll(brushModel, seriesIndex)) {
74498                    return;
74499                }
74500
74501                each$1(areas, function (area) {
74502                    selectorsByBrushType[area.brushType]
74503                    && brushModel.brushTargetManager.controlSeries(area, seriesModel, ecModel)
74504                    && rangeInfoList.push(area);
74505                    hasBrushExists |= brushed(rangeInfoList);
74506                });
74507
74508                if (linkOthers(seriesIndex) && brushed(rangeInfoList)) {
74509                    var data = seriesModel.getData();
74510                    data.each(function (dataIndex) {
74511                        if (checkInRange(selectorsByBrushType, rangeInfoList, data, dataIndex)) {
74512                            selectedDataIndexForLink[dataIndex] = 1;
74513                        }
74514                    });
74515                }
74516            }
74517
74518            // Step B
74519            ecModel.eachSeries(function (seriesModel, seriesIndex) {
74520                var seriesBrushSelected = {
74521                    seriesId: seriesModel.id,
74522                    seriesIndex: seriesIndex,
74523                    seriesName: seriesModel.name,
74524                    dataIndex: []
74525                };
74526                // Every series exists in event params, convenient
74527                // for user to find series by seriesIndex.
74528                thisBrushSelected.selected.push(seriesBrushSelected);
74529
74530                var selectorsByBrushType = getSelectorsByBrushType(seriesModel);
74531                var rangeInfoList = rangeInfoBySeries[seriesIndex];
74532
74533                var data = seriesModel.getData();
74534                var getValueState = linkOthers(seriesIndex)
74535                    ? function (dataIndex) {
74536                        return selectedDataIndexForLink[dataIndex]
74537                            ? (seriesBrushSelected.dataIndex.push(data.getRawIndex(dataIndex)), 'inBrush')
74538                            : 'outOfBrush';
74539                    }
74540                    : function (dataIndex) {
74541                        return checkInRange(selectorsByBrushType, rangeInfoList, data, dataIndex)
74542                            ? (seriesBrushSelected.dataIndex.push(data.getRawIndex(dataIndex)), 'inBrush')
74543                            : 'outOfBrush';
74544                    };
74545
74546                // If no supported brush or no brush, all visuals are in original state.
74547                (linkOthers(seriesIndex) ? hasBrushExists : brushed(rangeInfoList))
74548                && applyVisual(
74549                    STATE_LIST, visualMappings, data, getValueState
74550                );
74551            });
74552
74553        });
74554
74555        dispatchAction(api, throttleType, throttleDelay, brushSelected, payload);
74556    });
74557
74558    function dispatchAction(api, throttleType, throttleDelay, brushSelected, payload) {
74559        // This event will not be triggered when `setOpion`, otherwise dead lock may
74560        // triggered when do `setOption` in event listener, which we do not find
74561        // satisfactory way to solve yet. Some considered resolutions:
74562        // (a) Diff with prevoius selected data ant only trigger event when changed.
74563        // But store previous data and diff precisely (i.e., not only by dataIndex, but
74564        // also detect value changes in selected data) might bring complexity or fragility.
74565        // (b) Use spectial param like `silent` to suppress event triggering.
74566        // But such kind of volatile param may be weird in `setOption`.
74567        if (!payload) {
74568            return;
74569        }
74570
74571        var zr = api.getZr();
74572        if (zr[DISPATCH_FLAG]) {
74573            return;
74574        }
74575
74576        if (!zr[DISPATCH_METHOD]) {
74577            zr[DISPATCH_METHOD] = doDispatch;
74578        }
74579
74580        var fn = createOrUpdate(zr, DISPATCH_METHOD, throttleDelay, throttleType);
74581
74582        fn(api, brushSelected);
74583    }
74584
74585    function doDispatch(api, brushSelected) {
74586        if (!api.isDisposed()) {
74587            var zr = api.getZr();
74588            zr[DISPATCH_FLAG] = true;
74589            api.dispatchAction({
74590                type: 'brushSelect',
74591                batch: brushSelected
74592            });
74593            zr[DISPATCH_FLAG] = false;
74594        }
74595    }
74596
74597    function checkInRange(selectorsByBrushType, rangeInfoList, data, dataIndex) {
74598        for (var i = 0, len = rangeInfoList.length; i < len; i++) {
74599            var area = rangeInfoList[i];
74600            if (selectorsByBrushType[area.brushType](
74601                    dataIndex, data, area.selectors, area
74602                )) {
74603                return true;
74604            }
74605        }
74606    }
74607
74608    function getSelectorsByBrushType(seriesModel) {
74609        var brushSelector = seriesModel.brushSelector;
74610        if (isString(brushSelector)) {
74611            var sels = [];
74612            each$1(selector, function (selectorsByElementType, brushType) {
74613                sels[brushType] = function (dataIndex, data, selectors, area) {
74614                    var itemLayout = data.getItemLayout(dataIndex);
74615                    return selectorsByElementType[brushSelector](itemLayout, selectors, area);
74616                };
74617            });
74618            return sels;
74619        }
74620        else if (isFunction$1(brushSelector)) {
74621            var bSelector = {};
74622            each$1(selector, function (sel, brushType) {
74623                bSelector[brushType] = brushSelector;
74624            });
74625            return bSelector;
74626        }
74627        return brushSelector;
74628    }
74629
74630    function brushModelNotControll(brushModel, seriesIndex) {
74631        var seriesIndices = brushModel.option.seriesIndex;
74632        return seriesIndices != null
74633            && seriesIndices !== 'all'
74634            && (
74635                isArray(seriesIndices)
74636                    ? indexOf(seriesIndices, seriesIndex) < 0
74637                    : seriesIndex !== seriesIndices
74638            );
74639    }
74640
74641    function bindSelector(area) {
74642        var selectors = area.selectors = {};
74643        each$1(selector[area.brushType], function (selFn, elType) {
74644            // Do not use function binding or curry for performance.
74645            selectors[elType] = function (itemLayout) {
74646                return selFn(itemLayout, selectors, area);
74647            };
74648        });
74649        return area;
74650    }
74651
74652    var boundingRectBuilders = {
74653
74654        lineX: noop,
74655
74656        lineY: noop,
74657
74658        rect: function (area) {
74659            return getBoundingRectFromMinMax(area.range);
74660        },
74661
74662        polygon: function (area) {
74663            var minMax;
74664            var range = area.range;
74665
74666            for (var i = 0, len = range.length; i < len; i++) {
74667                minMax = minMax || [[Infinity, -Infinity], [Infinity, -Infinity]];
74668                var rg = range[i];
74669                rg[0] < minMax[0][0] && (minMax[0][0] = rg[0]);
74670                rg[0] > minMax[0][1] && (minMax[0][1] = rg[0]);
74671                rg[1] < minMax[1][0] && (minMax[1][0] = rg[1]);
74672                rg[1] > minMax[1][1] && (minMax[1][1] = rg[1]);
74673            }
74674
74675            return minMax && getBoundingRectFromMinMax(minMax);
74676        }
74677    };
74678
74679    function getBoundingRectFromMinMax(minMax) {
74680        return new BoundingRect(
74681            minMax[0][0],
74682            minMax[1][0],
74683            minMax[0][1] - minMax[0][0],
74684            minMax[1][1] - minMax[1][0]
74685        );
74686    }
74687
74688    /*
74689* Licensed to the Apache Software Foundation (ASF) under one
74690* or more contributor license agreements.  See the NOTICE file
74691* distributed with this work for additional information
74692* regarding copyright ownership.  The ASF licenses this file
74693* to you under the Apache License, Version 2.0 (the
74694* "License"); you may not use this file except in compliance
74695* with the License.  You may obtain a copy of the License at
74696*
74697*   http://www.apache.org/licenses/LICENSE-2.0
74698*
74699* Unless required by applicable law or agreed to in writing,
74700* software distributed under the License is distributed on an
74701* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
74702* KIND, either express or implied.  See the License for the
74703* specific language governing permissions and limitations
74704* under the License.
74705*/
74706
74707    var DEFAULT_OUT_OF_BRUSH_COLOR = ['#ddd'];
74708
74709    var BrushModel = extendComponentModel({
74710
74711        type: 'brush',
74712
74713        dependencies: ['geo', 'grid', 'xAxis', 'yAxis', 'parallel', 'series'],
74714
74715        /**
74716         * @protected
74717         */
74718        defaultOption: {
74719            // inBrush: null,
74720            // outOfBrush: null,
74721            toolbox: null,          // Default value see preprocessor.
74722            brushLink: null,        // Series indices array, broadcast using dataIndex.
74723                                    // or 'all', which means all series. 'none' or null means no series.
74724            seriesIndex: 'all',     // seriesIndex array, specify series controlled by this brush component.
74725            geoIndex: null,         //
74726            xAxisIndex: null,
74727            yAxisIndex: null,
74728
74729            brushType: 'rect',      // Default brushType, see BrushController.
74730            brushMode: 'single',    // Default brushMode, 'single' or 'multiple'
74731            transformable: true,    // Default transformable.
74732            brushStyle: {           // Default brushStyle
74733                borderWidth: 1,
74734                color: 'rgba(120,140,180,0.3)',
74735                borderColor: 'rgba(120,140,180,0.8)'
74736            },
74737
74738            throttleType: 'fixRate',// Throttle in brushSelected event. 'fixRate' or 'debounce'.
74739                                    // If null, no throttle. Valid only in the first brush component
74740            throttleDelay: 0,       // Unit: ms, 0 means every event will be triggered.
74741
74742            // FIXME
74743            // 试验效果
74744            removeOnClick: true,
74745
74746            z: 10000
74747        },
74748
74749        /**
74750         * @readOnly
74751         * @type {Array.<Object>}
74752         */
74753        areas: [],
74754
74755        /**
74756         * Current activated brush type.
74757         * If null, brush is inactived.
74758         * see module:echarts/component/helper/BrushController
74759         * @readOnly
74760         * @type {string}
74761         */
74762        brushType: null,
74763
74764        /**
74765         * Current brush opt.
74766         * see module:echarts/component/helper/BrushController
74767         * @readOnly
74768         * @type {Object}
74769         */
74770        brushOption: {},
74771
74772        /**
74773         * @readOnly
74774         * @type {Array.<Object>}
74775         */
74776        coordInfoList: [],
74777
74778        optionUpdated: function (newOption, isInit) {
74779            var thisOption = this.option;
74780
74781            !isInit && replaceVisualOption(
74782                thisOption, newOption, ['inBrush', 'outOfBrush']
74783            );
74784
74785            var inBrush = thisOption.inBrush = thisOption.inBrush || {};
74786            // Always give default visual, consider setOption at the second time.
74787            thisOption.outOfBrush = thisOption.outOfBrush || {color: DEFAULT_OUT_OF_BRUSH_COLOR};
74788
74789            if (!inBrush.hasOwnProperty('liftZ')) {
74790                // Bigger than the highlight z lift, otherwise it will
74791                // be effected by the highlight z when brush.
74792                inBrush.liftZ = 5;
74793            }
74794        },
74795
74796        /**
74797         * If ranges is null/undefined, range state remain.
74798         *
74799         * @param {Array.<Object>} [ranges]
74800         */
74801        setAreas: function (areas) {
74802            if (__DEV__) {
74803                assert$1(isArray(areas));
74804                each$1(areas, function (area) {
74805                    assert$1(area.brushType, 'Illegal areas');
74806                });
74807            }
74808
74809            // If ranges is null/undefined, range state remain.
74810            // This helps user to dispatchAction({type: 'brush'}) with no areas
74811            // set but just want to get the current brush select info from a `brush` event.
74812            if (!areas) {
74813                return;
74814            }
74815
74816            this.areas = map(areas, function (area) {
74817                return generateBrushOption(this.option, area);
74818            }, this);
74819        },
74820
74821        /**
74822         * see module:echarts/component/helper/BrushController
74823         * @param {Object} brushOption
74824         */
74825        setBrushOption: function (brushOption) {
74826            this.brushOption = generateBrushOption(this.option, brushOption);
74827            this.brushType = this.brushOption.brushType;
74828        }
74829
74830    });
74831
74832    function generateBrushOption(option, brushOption) {
74833        return merge(
74834            {
74835                brushType: option.brushType,
74836                brushMode: option.brushMode,
74837                transformable: option.transformable,
74838                brushStyle: new Model(option.brushStyle).getItemStyle(),
74839                removeOnClick: option.removeOnClick,
74840                z: option.z
74841            },
74842            brushOption,
74843            true
74844        );
74845    }
74846
74847    /*
74848* Licensed to the Apache Software Foundation (ASF) under one
74849* or more contributor license agreements.  See the NOTICE file
74850* distributed with this work for additional information
74851* regarding copyright ownership.  The ASF licenses this file
74852* to you under the Apache License, Version 2.0 (the
74853* "License"); you may not use this file except in compliance
74854* with the License.  You may obtain a copy of the License at
74855*
74856*   http://www.apache.org/licenses/LICENSE-2.0
74857*
74858* Unless required by applicable law or agreed to in writing,
74859* software distributed under the License is distributed on an
74860* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
74861* KIND, either express or implied.  See the License for the
74862* specific language governing permissions and limitations
74863* under the License.
74864*/
74865
74866    extendComponentView({
74867
74868        type: 'brush',
74869
74870        init: function (ecModel, api) {
74871
74872            /**
74873             * @readOnly
74874             * @type {module:echarts/model/Global}
74875             */
74876            this.ecModel = ecModel;
74877
74878            /**
74879             * @readOnly
74880             * @type {module:echarts/ExtensionAPI}
74881             */
74882            this.api = api;
74883
74884            /**
74885             * @readOnly
74886             * @type {module:echarts/component/brush/BrushModel}
74887             */
74888            this.model;
74889
74890            /**
74891             * @private
74892             * @type {module:echarts/component/helper/BrushController}
74893             */
74894            (this._brushController = new BrushController(api.getZr()))
74895                .on('brush', bind(this._onBrush, this))
74896                .mount();
74897        },
74898
74899        /**
74900         * @override
74901         */
74902        render: function (brushModel) {
74903            this.model = brushModel;
74904            return updateController.apply(this, arguments);
74905        },
74906
74907        /**
74908         * @override
74909         */
74910        updateTransform: updateController,
74911
74912        /**
74913         * @override
74914         */
74915        updateView: updateController,
74916
74917        // /**
74918        //  * @override
74919        //  */
74920        // updateLayout: updateController,
74921
74922        // /**
74923        //  * @override
74924        //  */
74925        // updateVisual: updateController,
74926
74927        /**
74928         * @override
74929         */
74930        dispose: function () {
74931            this._brushController.dispose();
74932        },
74933
74934        /**
74935         * @private
74936         */
74937        _onBrush: function (areas, opt) {
74938            var modelId = this.model.id;
74939
74940            this.model.brushTargetManager.setOutputRanges(areas, this.ecModel);
74941
74942            // Action is not dispatched on drag end, because the drag end
74943            // emits the same params with the last drag move event, and
74944            // may have some delay when using touch pad, which makes
74945            // animation not smooth (when using debounce).
74946            (!opt.isEnd || opt.removeOnClick) && this.api.dispatchAction({
74947                type: 'brush',
74948                brushId: modelId,
74949                areas: clone(areas),
74950                $from: modelId
74951            });
74952        }
74953
74954    });
74955
74956    function updateController(brushModel, ecModel, api, payload) {
74957        // Do not update controller when drawing.
74958        (!payload || payload.$from !== brushModel.id) && this._brushController
74959            .setPanels(brushModel.brushTargetManager.makePanelOpts(api))
74960            .enableBrush(brushModel.brushOption)
74961            .updateCovers(brushModel.areas.slice());
74962    }
74963
74964    /*
74965* Licensed to the Apache Software Foundation (ASF) under one
74966* or more contributor license agreements.  See the NOTICE file
74967* distributed with this work for additional information
74968* regarding copyright ownership.  The ASF licenses this file
74969* to you under the Apache License, Version 2.0 (the
74970* "License"); you may not use this file except in compliance
74971* with the License.  You may obtain a copy of the License at
74972*
74973*   http://www.apache.org/licenses/LICENSE-2.0
74974*
74975* Unless required by applicable law or agreed to in writing,
74976* software distributed under the License is distributed on an
74977* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
74978* KIND, either express or implied.  See the License for the
74979* specific language governing permissions and limitations
74980* under the License.
74981*/
74982
74983    /**
74984     * payload: {
74985 *      brushIndex: number, or,
74986 *      brushId: string, or,
74987 *      brushName: string,
74988 *      globalRanges: Array
74989 * }
74990     */
74991    registerAction(
74992        {type: 'brush', event: 'brush' /*, update: 'updateView' */},
74993        function (payload, ecModel) {
74994            ecModel.eachComponent({mainType: 'brush', query: payload}, function (brushModel) {
74995                brushModel.setAreas(payload.areas);
74996            });
74997        }
74998    );
74999
75000    /**
75001     * payload: {
75002 *      brushComponents: [
75003 *          {
75004 *              brushId,
75005 *              brushIndex,
75006 *              brushName,
75007 *              series: [
75008 *                  {
75009 *                      seriesId,
75010 *                      seriesIndex,
75011 *                      seriesName,
75012 *                      rawIndices: [21, 34, ...]
75013 *                  },
75014 *                  ...
75015 *              ]
75016 *          },
75017 *          ...
75018 *      ]
75019 * }
75020     */
75021    registerAction(
75022        {type: 'brushSelect', event: 'brushSelected', update: 'none'},
75023        function () {}
75024    );
75025
75026    /*
75027* Licensed to the Apache Software Foundation (ASF) under one
75028* or more contributor license agreements.  See the NOTICE file
75029* distributed with this work for additional information
75030* regarding copyright ownership.  The ASF licenses this file
75031* to you under the Apache License, Version 2.0 (the
75032* "License");
75032 you may not use this file except in compliance
75033* with the License.  You may obtain a copy of the License at
75034*
75035*   http://www.apache.org/licenses/LICENSE-2.0
75036*
75037* Unless required by applicable law or agreed to in writing,
75038* software distributed under the License is distributed on an
75039* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
75040* KIND, either express or implied.  See the License for the
75041* specific language governing permissions and limitations
75042* under the License.
75043*/
75044
75045
75046    var features = {};
75047
75048    function register$1(name, ctor) {
75049        features[name] = ctor;
75050    }
75051
75052    function get$1(name) {
75053        return features[name];
75054    }
75055
75056    /*
75057* Licensed to the Apache Software Foundation (ASF) under one
75058* or more contributor license agreements.  See the NOTICE file
75059* distributed with this work for additional information
75060* regarding copyright ownership.  The ASF licenses this file
75061* to you under the Apache License, Version 2.0 (the
75062* "License"); you may not use this file except in compliance
75063* with the License.  You may obtain a copy of the License at
75064*
75065*   http://www.apache.org/licenses/LICENSE-2.0
75066*
75067* Unless required by applicable law or agreed to in writing,
75068* software distributed under the License is distributed on an
75069* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
75070* KIND, either express or implied.  See the License for the
75071* specific language governing permissions and limitations
75072* under the License.
75073*/
75074
75075    var brushLang = lang.toolbox.brush;
75076
75077    function Brush(model, ecModel, api) {
75078        this.model = model;
75079        this.ecModel = ecModel;
75080        this.api = api;
75081
75082        /**
75083         * @private
75084         * @type {string}
75085         */
75086        this._brushType;
75087
75088        /**
75089         * @private
75090         * @type {string}
75091         */
75092        this._brushMode;
75093    }
75094
75095    Brush.defaultOption = {
75096        show: true,
75097        type: ['rect', 'polygon', 'lineX', 'lineY', 'keep', 'clear'],
75098        icon: {
75099            rect: 'M7.3,34.7 M0.4,10V-0.2h9.8 M89.6,10V-0.2h-9.8 M0.4,60v10.2h9.8 M89.6,60v10.2h-9.8 M12.3,22.4V10.5h13.1 M33.6,10.5h7.8 M49.1,10.5h7.8 M77.5,22.4V10.5h-13 M12.3,31.1v8.2 M77.7,31.1v8.2 M12.3,47.6v11.9h13.1 M33.6,59.5h7.6 M49.1,59.5 h7.7 M77.5,47.6v11.9h-13', // jshint ignore:line
75100            polygon: 'M55.2,34.9c1.7,0,3.1,1.4,3.1,3.1s-1.4,3.1-3.1,3.1 s-3.1-1.4-3.1-3.1S53.5,34.9,55.2,34.9z M50.4,51c1.7,0,3.1,1.4,3.1,3.1c0,1.7-1.4,3.1-3.1,3.1c-1.7,0-3.1-1.4-3.1-3.1 C47.3,52.4,48.7,51,50.4,51z M55.6,37.1l1.5-7.8 M60.1,13.5l1.6-8.7l-7.8,4 M59,19l-1,5.3 M24,16.1l6.4,4.9l6.4-3.3 M48.5,11.6 l-5.9,3.1 M19.1,12.8L9.7,5.1l1.1,7.7 M13.4,29.8l1,7.3l6.6,1.6 M11.6,18.4l1,6.1 M32.8,41.9 M26.6,40.4 M27.3,40.2l6.1,1.6 M49.9,52.1l-5.6-7.6l-4.9-1.2', // jshint ignore:line
75101            lineX: 'M15.2,30 M19.7,15.6V1.9H29 M34.8,1.9H40.4 M55.3,15.6V1.9H45.9 M19.7,44.4V58.1H29 M34.8,58.1H40.4 M55.3,44.4 V58.1H45.9 M12.5,20.3l-9.4,9.6l9.6,9.8 M3.1,29.9h16.5 M62.5,20.3l9.4,9.6L62.3,39.7 M71.9,29.9H55.4', // jshint ignore:line
75102            lineY: 'M38.8,7.7 M52.7,12h13.2v9 M65.9,26.6V32 M52.7,46.3h13.2v-9 M24.9,12H11.8v9 M11.8,26.6V32 M24.9,46.3H11.8v-9 M48.2,5.1l-9.3-9l-9.4,9.2 M38.9-3.9V12 M48.2,53.3l-9.3,9l-9.4-9.2 M38.9,62.3V46.4', // jshint ignore:line
75103            keep: 'M4,10.5V1h10.3 M20.7,1h6.1 M33,1h6.1 M55.4,10.5V1H45.2 M4,17.3v6.6 M55.6,17.3v6.6 M4,30.5V40h10.3 M20.7,40 h6.1 M33,40h6.1 M55.4,30.5V40H45.2 M21,18.9h62.9v48.6H21V18.9z', // jshint ignore:line
75104            clear: 'M22,14.7l30.9,31 M52.9,14.7L22,45.7 M4.7,16.8V4.2h13.1 M26,4.2h7.8 M41.6,4.2h7.8 M70.3,16.8V4.2H57.2 M4.7,25.9v8.6 M70.3,25.9v8.6 M4.7,43.2v12.6h13.1 M26,55.8h7.8 M41.6,55.8h7.8 M70.3,43.2v12.6H57.2' // jshint ignore:line
75105        },
75106        // `rect`, `polygon`, `lineX`, `lineY`, `keep`, `clear`
75107        title: clone(brushLang.title)
75108    };
75109
75110    var proto$3 = Brush.prototype;
75111
75112// proto.updateLayout = function (featureModel, ecModel, api) {
75113    proto$3.render =
75114        proto$3.updateView = function (featureModel, ecModel, api) {
75115            var brushType;
75116            var brushMode;
75117            var isBrushed;
75118
75119            ecModel.eachComponent({mainType: 'brush'}, function (brushModel) {
75120                brushType = brushModel.brushType;
75121                brushMode = brushModel.brushOption.brushMode || 'single';
75122                isBrushed |= brushModel.areas.length;
75123            });
75124            this._brushType = brushType;
75125            this._brushMode = brushMode;
75126
75127            each$1(featureModel.get('type', true), function (type) {
75128                featureModel.setIconStatus(
75129                    type,
75130                    (
75131                        type === 'keep'
75132                            ? brushMode === 'multiple'
75133                            : type === 'clear'
75134                            ? isBrushed
75135                            : type === brushType
75136                    ) ? 'emphasis' : 'normal'
75137                );
75138            });
75139        };
75140
75141    proto$3.getIcons = function () {
75142        var model = this.model;
75143        var availableIcons = model.get('icon', true);
75144        var icons = {};
75145        each$1(model.get('type', true), function (type) {
75146            if (availableIcons[type]) {
75147                icons[type] = availableIcons[type];
75148            }
75149        });
75150        return icons;
75151    };
75152
75153    proto$3.onclick = function (ecModel, api, type) {
75154        var brushType = this._brushType;
75155        var brushMode = this._brushMode;
75156
75157        if (type === 'clear') {
75158            // Trigger parallel action firstly
75159            api.dispatchAction({
75160                type: 'axisAreaSelect',
75161                intervals: []
75162            });
75163
75164            api.dispatchAction({
75165                type: 'brush',
75166                command: 'clear',
75167                // Clear all areas of all brush components.
75168                areas: []
75169            });
75170        }
75171        else {
75172            api.dispatchAction({
75173                type: 'takeGlobalCursor',
75174                key: 'brush',
75175                brushOption: {
75176                    brushType: type === 'keep'
75177                        ? brushType
75178                        : (brushType === type ? false : type),
75179                    brushMode: type === 'keep'
75180                        ? (brushMode === 'multiple' ? 'single' : 'multiple')
75181                        : brushMode
75182                }
75183            });
75184        }
75185    };
75186
75187    register$1('brush', Brush);
75188
75189    /*
75190* Licensed to the Apache Software Foundation (ASF) under one
75191* or more contributor license agreements.  See the NOTICE file
75192* distributed with this work for additional information
75193* regarding copyright ownership.  The ASF licenses this file
75194* to you under the Apache License, Version 2.0 (the
75195* "License"); you may not use this file except in compliance
75196* with the License.  You may obtain a copy of the License at
75197*
75198*   http://www.apache.org/licenses/LICENSE-2.0
75199*
75200* Unless required by applicable law or agreed to in writing,
75201* software distributed under the License is distributed on an
75202* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
75203* KIND, either express or implied.  See the License for the
75204* specific language governing permissions and limitations
75205* under the License.
75206*/
75207
75208    /**
75209     * Brush component entry
75210     */
75211
75212    registerPreprocessor(preprocessor$1);
75213
75214    /*
75215* Licensed to the Apache Software Foundation (ASF) under one
75216* or more contributor license agreements.  See the NOTICE file
75217* distributed with this work for additional information
75218* regarding copyright ownership.  The ASF licenses this file
75219* to you under the Apache License, Version 2.0 (the
75220* "License"); you may not use this file except in compliance
75221* with the License.  You may obtain a copy of the License at
75222*
75223*   http://www.apache.org/licenses/LICENSE-2.0
75224*
75225* Unless required by applicable law or agreed to in writing,
75226* software distributed under the License is distributed on an
75227* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
75228* KIND, either express or implied.  See the License for the
75229* specific language governing permissions and limitations
75230* under the License.
75231*/
75232
75233// (24*60*60*1000)
75234    var PROXIMATE_ONE_DAY = 86400000;
75235
75236    /**
75237     * Calendar
75238     *
75239     * @constructor
75240     *
75241     * @param {Object} calendarModel calendarModel
75242     * @param {Object} ecModel       ecModel
75243     * @param {Object} api           api
75244     */
75245    function Calendar(calendarModel, ecModel, api) {
75246        this._model = calendarModel;
75247    }
75248
75249    Calendar.prototype = {
75250
75251        constructor: Calendar,
75252
75253        type: 'calendar',
75254
75255        dimensions: ['time', 'value'],
75256
75257        // Required in createListFromData
75258        getDimensionsInfo: function () {
75259            return [{name: 'time', type: 'time'}, 'value'];
75260        },
75261
75262        getRangeInfo: function () {
75263            return this._rangeInfo;
75264        },
75265
75266        getModel: function () {
75267            return this._model;
75268        },
75269
75270        getRect: function () {
75271            return this._rect;
75272        },
75273
75274        getCellWidth: function () {
75275            return this._sw;
75276        },
75277
75278        getCellHeight: function () {
75279            return this._sh;
75280        },
75281
75282        getOrient: function () {
75283            return this._orient;
75284        },
75285
75286        /**
75287         * getFirstDayOfWeek
75288         *
75289         * @example
75290         *     0 : start at Sunday
75291         *     1 : start at Monday
75292         *
75293         * @return {number}
75294         */
75295        getFirstDayOfWeek: function () {
75296            return this._firstDayOfWeek;
75297        },
75298
75299        /**
75300         * get date info
75301         *
75302         * @param  {string|number} date date
75303         * @return {Object}
75304         * {
75305     *      y: string, local full year, eg., '1940',
75306     *      m: string, local month, from '01' ot '12',
75307     *      d: string, local date, from '01' to '31' (if exists),
75308     *      day: It is not date.getDay(). It is the location of the cell in a week, from 0 to 6,
75309     *      time: timestamp,
75310     *      formatedDate: string, yyyy-MM-dd,
75311     *      date: original date object.
75312     * }
75313         */
75314        getDateInfo: function (date) {
75315
75316            date = parseDate(date);
75317
75318            var y = date.getFullYear();
75319
75320            var m = date.getMonth() + 1;
75321            m = m < 10 ? '0' + m : m;
75322
75323            var d = date.getDate();
75324            d = d < 10 ? '0' + d : d;
75325
75326            var day = date.getDay();
75327
75328            day = Math.abs((day + 7 - this.getFirstDayOfWeek()) % 7);
75329
75330            return {
75331                y: y,
75332                m: m,
75333                d: d,
75334                day: day,
75335                time: date.getTime(),
75336                formatedDate: y + '-' + m + '-' + d,
75337                date: date
75338            };
75339        },
75340
75341        getNextNDay: function (date, n) {
75342            n = n || 0;
75343            if (n === 0) {
75344                return this.getDateInfo(date);
75345            }
75346
75347            date = new Date(this.getDateInfo(date).time);
75348            date.setDate(date.getDate() + n);
75349
75350            return this.getDateInfo(date);
75351        },
75352
75353        update: function (ecModel, api) {
75354
75355            this._firstDayOfWeek = +this._model.getModel('dayLabel').get('firstDay');
75356            this._orient = this._model.get('orient');
75357            this._lineWidth = this._model.getModel('itemStyle').getItemStyle().lineWidth || 0;
75358
75359
75360            this._rangeInfo = this._getRangeInfo(this._initRangeOption());
75361            var weeks = this._rangeInfo.weeks || 1;
75362            var whNames = ['width', 'height'];
75363            var cellSize = this._model.get('cellSize').slice();
75364            var layoutParams = this._model.getBoxLayoutParams();
75365            var cellNumbers = this._orient === 'horizontal' ? [weeks, 7] : [7, weeks];
75366
75367            each$1([0, 1], function (idx) {
75368                if (cellSizeSpecified(cellSize, idx)) {
75369                    layoutParams[whNames[idx]] = cellSize[idx] * cellNumbers[idx];
75370                }
75371            });
75372
75373            var whGlobal = {
75374                width: api.getWidth(),
75375                height: api.getHeight()
75376            };
75377            var calendarRect = this._rect = getLayoutRect(layoutParams, whGlobal);
75378
75379            each$1([0, 1], function (idx) {
75380                if (!cellSizeSpecified(cellSize, idx)) {
75381                    cellSize[idx] = calendarRect[whNames[idx]] / cellNumbers[idx];
75382                }
75383            });
75384
75385            function cellSizeSpecified(cellSize, idx) {
75386                return cellSize[idx] != null && cellSize[idx] !== 'auto';
75387            }
75388
75389            this._sw = cellSize[0];
75390            this._sh = cellSize[1];
75391        },
75392
75393
75394        /**
75395         * Convert a time data(time, value) item to (x, y) point.
75396         *
75397         * @override
75398         * @param  {Array|number} data data
75399         * @param  {boolean} [clamp=true] out of range
75400         * @return {Array} point
75401         */
75402        dataToPoint: function (data, clamp) {
75403            isArray(data) && (data = data[0]);
75404            clamp == null && (clamp = true);
75405
75406            var dayInfo = this.getDateInfo(data);
75407            var range = this._rangeInfo;
75408            var date = dayInfo.formatedDate;
75409
75410            // if not in range return [NaN, NaN]
75411            if (clamp && !(
75412                    dayInfo.time >= range.start.time
75413                    && dayInfo.time < range.end.time + PROXIMATE_ONE_DAY
75414                )) {
75415                return [NaN, NaN];
75416            }
75417
75418            var week = dayInfo.day;
75419            var nthWeek = this._getRangeInfo([range.start.time, date]).nthWeek;
75420
75421            if (this._orient === 'vertical') {
75422                return [
75423                    this._rect.x + week * this._sw + this._sw / 2,
75424                    this._rect.y + nthWeek * this._sh + this._sh / 2
75425                ];
75426
75427            }
75428
75429            return [
75430                this._rect.x + nthWeek * this._sw + this._sw / 2,
75431                this._rect.y + week * this._sh + this._sh / 2
75432            ];
75433
75434        },
75435
75436        /**
75437         * Convert a (x, y) point to time data
75438         *
75439         * @override
75440         * @param  {string} point point
75441         * @return {string}       data
75442         */
75443        pointToData: function (point) {
75444
75445            var date = this.pointToDate(point);
75446
75447            return date && date.time;
75448        },
75449
75450        /**
75451         * Convert a time date item to (x, y) four point.
75452         *
75453         * @param  {Array} data  date[0] is date
75454         * @param  {boolean} [clamp=true]  out of range
75455         * @return {Object}       point
75456         */
75457        dataToRect: function (data, clamp) {
75458            var point = this.dataToPoint(data, clamp);
75459
75460            return {
75461                contentShape: {
75462                    x: point[0] - (this._sw - this._lineWidth) / 2,
75463                    y: point[1] - (this._sh - this._lineWidth) / 2,
75464                    width: this._sw - this._lineWidth,
75465                    height: this._sh - this._lineWidth
75466                },
75467
75468                center: point,
75469
75470                tl: [
75471                    point[0] - this._sw / 2,
75472                    point[1] - this._sh / 2
75473                ],
75474
75475                tr: [
75476                    point[0] + this._sw / 2,
75477                    point[1] - this._sh / 2
75478                ],
75479
75480                br: [
75481                    point[0] + this._sw / 2,
75482                    point[1] + this._sh / 2
75483                ],
75484
75485                bl: [
75486                    point[0] - this._sw / 2,
75487                    point[1] + this._sh / 2
75488                ]
75489
75490            };
75491        },
75492
75493        /**
75494         * Convert a (x, y) point to time date
75495         *
75496         * @param  {Array} point point
75497         * @return {Object}       date
75498         */
75499        pointToDate: function (point) {
75500            var nthX = Math.floor((point[0] - this._rect.x) / this._sw) + 1;
75501            var nthY = Math.floor((point[1] - this._rect.y) / this._sh) + 1;
75502            var range = this._rangeInfo.range;
75503
75504            if (this._orient === 'vertical') {
75505                return this._getDateByWeeksAndDay(nthY, nthX - 1, range);
75506            }
75507
75508            return this._getDateByWeeksAndDay(nthX, nthY - 1, range);
75509        },
75510
75511        /**
75512         * @inheritDoc
75513         */
75514        convertToPixel: curry(doConvert$2, 'dataToPoint'),
75515
75516        /**
75517         * @inheritDoc
75518         */
75519        convertFromPixel: curry(doConvert$2, 'pointToData'),
75520
75521        /**
75522         * initRange
75523         *
75524         * @private
75525         * @return {Array} [start, end]
75526         */
75527        _initRangeOption: function () {
75528            var range = this._model.get('range');
75529
75530            var rg = range;
75531
75532            if (isArray(rg) && rg.length === 1) {
75533                rg = rg[0];
75534            }
75535
75536            if (/^\d{4}$/.test(rg)) {
75537                range = [rg + '-01-01', rg + '-12-31'];
75538            }
75539
75540            if (/^\d{4}[\/|-]\d{1,2}$/.test(rg)) {
75541
75542                var start = this.getDateInfo(rg);
75543                var firstDay = start.date;
75544                firstDay.setMonth(firstDay.getMonth() + 1);
75545
75546                var end = this.getNextNDay(firstDay, -1);
75547                range = [start.formatedDate, end.formatedDate];
75548            }
75549
75550            if (/^\d{4}[\/|-]\d{1,2}[\/|-]\d{1,2}$/.test(rg)) {
75551                range = [rg, rg];
75552            }
75553
75554            var tmp = this._getRangeInfo(range);
75555
75556            if (tmp.start.time > tmp.end.time) {
75557                range.reverse();
75558            }
75559
75560            return range;
75561        },
75562
75563        /**
75564         * range info
75565         *
75566         * @private
75567         * @param  {Array} range range ['2017-01-01', '2017-07-08']
75568         *  If range[0] > range[1], they will not be reversed.
75569         * @return {Object}       obj
75570         */
75571        _getRangeInfo: function (range) {
75572            range = [
75573                this.getDateInfo(range[0]),
75574                this.getDateInfo(range[1])
75575            ];
75576
75577            var reversed;
75578            if (range[0].time > range[1].time) {
75579                reversed = true;
75580                range.reverse();
75581            }
75582
75583            var allDay = Math.floor(range[1].time / PROXIMATE_ONE_DAY)
75584                - Math.floor(range[0].time / PROXIMATE_ONE_DAY) + 1;
75585
75586            // Consider case:
75587            // Firstly set system timezone as "Time Zone: America/Toronto",
75588            // ```
75589            // var first = new Date(1478412000000 - 3600 * 1000 * 2.5);
75590            // var second = new Date(1478412000000);
75591            // var allDays = Math.floor(second / ONE_DAY) - Math.floor(first / ONE_DAY) + 1;
75592            // ```
75593            // will get wrong result because of DST. So we should fix it.
75594            var date = new Date(range[0].time);
75595            var startDateNum = date.getDate();
75596            var endDateNum = range[1].date.getDate();
75597            date.setDate(startDateNum + allDay - 1);
75598            // The bias can not over a month, so just compare date.
75599            if (date.getDate() !== endDateNum) {
75600                var sign = date.getTime() - range[1].time > 0 ? 1 : -1;
75601                while (date.getDate() !== endDateNum && (date.getTime() - range[1].time) * sign > 0) {
75602                    allDay -= sign;
75603                    date.setDate(startDateNum + allDay - 1);
75604                }
75605            }
75606
75607            var weeks = Math.floor((allDay + range[0].day + 6) / 7);
75608            var nthWeek = reversed ? -weeks + 1: weeks - 1;
75609
75610            reversed && range.reverse();
75611
75612            return {
75613                range: [range[0].formatedDate, range[1].formatedDate],
75614                start: range[0],
75615                end: range[1],
75616                allDay: allDay,
75617                weeks: weeks,
75618                // From 0.
75619                nthWeek: nthWeek,
75620                fweek: range[0].day,
75621                lweek: range[1].day
75622            };
75623        },
75624
75625        /**
75626         * get date by nthWeeks and week day in range
75627         *
75628         * @private
75629         * @param  {number} nthWeek the week
75630         * @param  {number} day   the week day
75631         * @param  {Array} range [d1, d2]
75632         * @return {Object}
75633         */
75634        _getDateByWeeksAndDay: function (nthWeek, day, range) {
75635            var rangeInfo = this._getRangeInfo(range);
75636
75637            if (nthWeek > rangeInfo.weeks
75638                || (nthWeek === 0 && day < rangeInfo.fweek)
75639                || (nthWeek === rangeInfo.weeks && day > rangeInfo.lweek)
75640            ) {
75641                return false;
75642            }
75643
75644            var nthDay = (nthWeek - 1) * 7 - rangeInfo.fweek + day;
75645            var date = new Date(rangeInfo.start.time);
75646            date.setDate(rangeInfo.start.d + nthDay);
75647
75648            return this.getDateInfo(date);
75649        }
75650    };
75651
75652    Calendar.dimensions =  Calendar.prototype.dimensions;
75653
75654    Calendar.getDimensionsInfo =  Calendar.prototype.getDimensionsInfo;
75655
75656    Calendar.create = function (ecModel, api) {
75657        var calendarList = [];
75658
75659        ecModel.eachComponent('calendar', function (calendarModel) {
75660            var calendar = new Calendar(calendarModel, ecModel, api);
75661            calendarList.push(calendar);
75662            calendarModel.coordinateSystem = calendar;
75663        });
75664
75665        ecModel.eachSeries(function (calendarSeries) {
75666            if (calendarSeries.get('coordinateSystem') === 'calendar') {
75667                // Inject coordinate system
75668                calendarSeries.coordinateSystem = calendarList[calendarSeries.get('calendarIndex') || 0];
75669            }
75670        });
75671        return calendarList;
75672    };
75673
75674    function doConvert$2(methodName, ecModel, finder, value) {
75675        var calendarModel = finder.calendarModel;
75676        var seriesModel = finder.seriesModel;
75677
75678        var coordSys = calendarModel
75679            ? calendarModel.coordinateSystem
75680            : seriesModel
75681                ? seriesModel.coordinateSystem
75682                : null;
75683
75684        return coordSys === this ? coordSys[methodName](value) : null;
75685    }
75686
75687    CoordinateSystemManager.register('calendar', Calendar);
75688
75689    /*
75690* Licensed to the Apache Software Foundation (ASF) under one
75691* or more contributor license agreements.  See the NOTICE file
75692* distributed with this work for additional information
75693* regarding copyright ownership.  The ASF licenses this file
75694* to you under the Apache License, Version 2.0 (the
75695* "License"); you may not use this file except in compliance
75696* with the License.  You may obtain a copy of the License at
75697*
75698*   http://www.apache.org/licenses/LICENSE-2.0
75699*
75700* Unless required by applicable law or agreed to in writing,
75701* software distributed under the License is distributed on an
75702* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
75703* KIND, either express or implied.  See the License for the
75704* specific language governing permissions and limitations
75705* under the License.
75706*/
75707
75708    var CalendarModel = ComponentModel.extend({
75709
75710        type: 'calendar',
75711
75712        /**
75713         * @type {module:echarts/coord/calendar/Calendar}
75714         */
75715        coordinateSystem: null,
75716
75717        defaultOption: {
75718            zlevel: 0,
75719            z: 2,
75720            left: 80,
75721            top: 60,
75722
75723            cellSize: 20,
75724
75725            // horizontal vertical
75726            orient: 'horizontal',
75727
75728            // month separate line style
75729            splitLine: {
75730                show: true,
75731                lineStyle: {
75732                    color: '#000',
75733                    width: 1,
75734                    type: 'solid'
75735                }
75736            },
75737
75738            // rect style  temporarily unused emphasis
75739            itemStyle: {
75740                color: '#fff',
75741                borderWidth: 1,
75742                borderColor: '#ccc'
75743            },
75744
75745            // week text style
75746            dayLabel: {
75747                show: true,
75748
75749                // a week first day
75750                firstDay: 0,
75751
75752                // start end
75753                position: 'start',
75754                margin: '50%', // 50% of cellSize
75755                nameMap: 'en',
75756                color: '#000'
75757            },
75758
75759            // month text style
75760            monthLabel: {
75761                show: true,
75762
75763                // start end
75764                position: 'start',
75765                margin: 5,
75766
75767                // center or left
75768                align: 'center',
75769
75770                // cn en []
75771                nameMap: 'en',
75772                formatter: null,
75773                color: '#000'
75774            },
75775
75776            // year text style
75777            yearLabel: {
75778                show: true,
75779
75780                // top bottom left right
75781                position: null,
75782                margin: 30,
75783                formatter: null,
75784                color: '#ccc',
75785                fontFamily: 'sans-serif',
75786                fontWeight: 'bolder',
75787                fontSize: 20
75788            }
75789        },
75790
75791        /**
75792         * @override
75793         */
75794        init: function (option, parentModel, ecModel, extraOpt) {
75795            var inputPositionParams = getLayoutParams(option);
75796
75797            CalendarModel.superApply(this, 'init', arguments);
75798
75799            mergeAndNormalizeLayoutParams$1(option, inputPositionParams);
75800        },
75801
75802        /**
75803         * @override
75804         */
75805        mergeOption: function (option, extraOpt) {
75806            CalendarModel.superApply(this, 'mergeOption', arguments);
75807
75808            mergeAndNormalizeLayoutParams$1(this.option, option);
75809        }
75810    });
75811
75812    function mergeAndNormalizeLayoutParams$1(target, raw) {
75813        // Normalize cellSize
75814        var cellSize = target.cellSize;
75815
75816        if (!isArray(cellSize)) {
75817            cellSize = target.cellSize = [cellSize, cellSize];
75818        }
75819        else if (cellSize.length === 1) {
75820            cellSize[1] = cellSize[0];
75821        }
75822
75823        var ignoreSize = map([0, 1], function (hvIdx) {
75824            // If user have set `width` or both `left` and `right`, cellSize
75825            // will be automatically set to 'auto', otherwise the default
75826            // setting of cellSize will make `width` setting not work.
75827            if (sizeCalculable(raw, hvIdx)) {
75828                cellSize[hvIdx] = 'auto';
75829            }
75830            return cellSize[hvIdx] != null && cellSize[hvIdx] !== 'auto';
75831        });
75832
75833        mergeLayoutParam(target, raw, {
75834            type: 'box', ignoreSize: ignoreSize
75835        });
75836    }
75837
75838    /*
75839* Licensed to the Apache Software Foundation (ASF) under one
75840* or more contributor license agreements.  See the NOTICE file
75841* distributed with this work for additional information
75842* regarding copyright ownership.  The ASF licenses this file
75843* to you under the Apache License, Version 2.0 (the
75844* "License"); you may not use this file except in compliance
75845* with the License.  You may obtain a copy of the License at
75846*
75847*   http://www.apache.org/licenses/LICENSE-2.0
75848*
75849* Unless required by applicable law or agreed to in writing,
75850* software distributed under the License is distributed on an
75851* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
75852* KIND, either express or implied.  See the License for the
75853* specific language governing permissions and limitations
75854* under the License.
75855*/
75856
75857    var MONTH_TEXT = {
75858        EN: [
75859            'Jan', 'Feb', 'Mar',
75860            'Apr', 'May', 'Jun',
75861            'Jul', 'Aug', 'Sep',
75862            'Oct', 'Nov', 'Dec'
75863        ],
75864        CN: [
75865            '一月', '二月', '三月',
75866            '四月', '五月', '六月',
75867            '七月', '八月', '九月',
75868            '十月', '十一月', '十二月'
75869        ]
75870    };
75871
75872    var WEEK_TEXT = {
75873        EN: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
75874        CN: ['日', '一', '二', '三', '四', '五', '六']
75875    };
75876
75877    extendComponentView({
75878
75879        type: 'calendar',
75880
75881        /**
75882         * top/left line points
75883         *  @private
75884         */
75885        _tlpoints: null,
75886
75887        /**
75888         * bottom/right line points
75889         *  @private
75890         */
75891        _blpoints: null,
75892
75893        /**
75894         * first day of month
75895         *  @private
75896         */
75897        _firstDayOfMonth: null,
75898
75899        /**
75900         * first day point of month
75901         *  @private
75902         */
75903        _firstDayPoints: null,
75904
75905        render: function (calendarModel, ecModel, api) {
75906
75907            var group = this.group;
75908
75909            group.removeAll();
75910
75911            var coordSys = calendarModel.coordinateSystem;
75912
75913            // range info
75914            var rangeData = coordSys.getRangeInfo();
75915            var orient = coordSys.getOrient();
75916
75917            this._renderDayRect(calendarModel, rangeData, group);
75918
75919            // _renderLines must be called prior to following function
75920            this._renderLines(calendarModel, rangeData, orient, group);
75921
75922            this._renderYearText(calendarModel, rangeData, orient, group);
75923
75924            this._renderMonthText(calendarModel, orient, group);
75925
75926            this._renderWeekText(calendarModel, rangeData, orient, group);
75927        },
75928
75929        // render day rect
75930        _renderDayRect: function (calendarModel, rangeData, group) {
75931            var coordSys = calendarModel.coordinateSystem;
75932            var itemRectStyleModel = calendarModel.getModel('itemStyle').getItemStyle();
75933            var sw = coordSys.getCellWidth();
vendor: 12,492 bytes, lines 75934-76316
75934            var sh = coordSys.getCellHeight();
75935
75936            for (var i = rangeData.start.time;
75937                 i <= rangeData.end.time;
75938                 i = coordSys.getNextNDay(i, 1).time
75939            ) {
75940
75941                var point = coordSys.dataToRect([i], false).tl;
75942
75943                // every rect
75944                var rect = new Rect({
75945                    shape: {
75946                        x: point[0],
75947                        y: point[1],
75948                        width: sw,
75949                        height: sh
75950                    },
75951                    cursor: 'default',
75952                    style: itemRectStyleModel
75953                });
75954
75955                group.add(rect);
75956            }
75957
75958        },
75959
75960        // render separate line
75961        _renderLines: function (calendarModel, rangeData, orient, group) {
75962
75963            var self = this;
75964
75965            var coordSys = calendarModel.coordinateSystem;
75966
75967            var lineStyleModel = calendarModel.getModel('splitLine.lineStyle').getLineStyle();
75968            var show = calendarModel.get('splitLine.show');
75969
75970            var lineWidth = lineStyleModel.lineWidth;
75971
75972            this._tlpoints = [];
75973            this._blpoints = [];
75974            this._firstDayOfMonth = [];
75975            this._firstDayPoints = [];
75976
75977
75978            var firstDay = rangeData.start;
75979
75980            for (var i = 0; firstDay.time <= rangeData.end.time; i++) {
75981                addPoints(firstDay.formatedDate);
75982
75983                if (i === 0) {
75984                    firstDay = coordSys.getDateInfo(rangeData.start.y + '-' + rangeData.start.m);
75985                }
75986
75987                var date = firstDay.date;
75988                date.setMonth(date.getMonth() + 1);
75989                firstDay = coordSys.getDateInfo(date);
75990            }
75991
75992            addPoints(coordSys.getNextNDay(rangeData.end.time, 1).formatedDate);
75993
75994            function addPoints(date) {
75995
75996                self._firstDayOfMonth.push(coordSys.getDateInfo(date));
75997                self._firstDayPoints.push(coordSys.dataToRect([date], false).tl);
75998
75999                var points = self._getLinePointsOfOneWeek(calendarModel, date, orient);
76000
76001                self._tlpoints.push(points[0]);
76002                self._blpoints.push(points[points.length - 1]);
76003
76004                show && self._drawSplitline(points, lineStyleModel, group);
76005            }
76006
76007
76008            // render top/left line
76009            show && this._drawSplitline(self._getEdgesPoints(self._tlpoints, lineWidth, orient), lineStyleModel, group);
76010
76011            // render bottom/right line
76012            show && this._drawSplitline(self._getEdgesPoints(self._blpoints, lineWidth, orient), lineStyleModel, group);
76013
76014        },
76015
76016        // get points at both ends
76017        _getEdgesPoints: function (points, lineWidth, orient) {
76018            var rs = [points[0].slice(), points[points.length - 1].slice()];
76019            var idx = orient === 'horizontal' ? 0 : 1;
76020
76021            // both ends of the line are extend half lineWidth
76022            rs[0][idx] = rs[0][idx] - lineWidth / 2;
76023            rs[1][idx] = rs[1][idx] + lineWidth / 2;
76024
76025            return rs;
76026        },
76027
76028        // render split line
76029        _drawSplitline: function (points, lineStyleModel, group) {
76030
76031            var poyline = new Polyline({
76032                z2: 20,
76033                shape: {
76034                    points: points
76035                },
76036                style: lineStyleModel
76037            });
76038
76039            group.add(poyline);
76040        },
76041
76042        // render month line of one week points
76043        _getLinePointsOfOneWeek: function (calendarModel, date, orient) {
76044
76045            var coordSys = calendarModel.coordinateSystem;
76046            date = coordSys.getDateInfo(date);
76047
76048            var points = [];
76049
76050            for (var i = 0; i < 7; i++) {
76051
76052                var tmpD = coordSys.getNextNDay(date.time, i);
76053                var point = coordSys.dataToRect([tmpD.time], false);
76054
76055                points[2 * tmpD.day] = point.tl;
76056                points[2 * tmpD.day + 1] = point[orient === 'horizontal' ? 'bl' : 'tr'];
76057            }
76058
76059            return points;
76060
76061        },
76062
76063        _formatterLabel: function (formatter, params) {
76064
76065            if (typeof formatter === 'string' && formatter) {
76066                return formatTplSimple(formatter, params);
76067            }
76068
76069            if (typeof formatter === 'function') {
76070                return formatter(params);
76071            }
76072
76073            return params.nameMap;
76074
76075        },
76076
76077        _yearTextPositionControl: function (textEl, point, orient, position, margin) {
76078
76079            point = point.slice();
76080            var aligns = ['center', 'bottom'];
76081
76082            if (position === 'bottom') {
76083                point[1] += margin;
76084                aligns = ['center', 'top'];
76085            }
76086            else if (position === 'left') {
76087                point[0] -= margin;
76088            }
76089            else if (position === 'right') {
76090                point[0] += margin;
76091                aligns = ['center', 'top'];
76092            }
76093            else { // top
76094                point[1] -= margin;
76095            }
76096
76097            var rotate = 0;
76098            if (position === 'left' || position === 'right') {
76099                rotate = Math.PI / 2;
76100            }
76101
76102            return {
76103                rotation: rotate,
76104                position: point,
76105                style: {
76106                    textAlign: aligns[0],
76107                    textVerticalAlign: aligns[1]
76108                }
76109            };
76110        },
76111
76112        // render year
76113        _renderYearText: function (calendarModel, rangeData, orient, group) {
76114            var yearLabel = calendarModel.getModel('yearLabel');
76115
76116            if (!yearLabel.get('show')) {
76117                return;
76118            }
76119
76120            var margin = yearLabel.get('margin');
76121            var pos = yearLabel.get('position');
76122
76123            if (!pos) {
76124                pos = orient !== 'horizontal' ? 'top' : 'left';
76125            }
76126
76127            var points = [this._tlpoints[this._tlpoints.length - 1], this._blpoints[0]];
76128            var xc = (points[0][0] + points[1][0]) / 2;
76129            var yc = (points[0][1] + points[1][1]) / 2;
76130
76131            var idx = orient === 'horizontal' ? 0 : 1;
76132
76133            var posPoints = {
76134                top: [xc, points[idx][1]],
76135                bottom: [xc, points[1 - idx][1]],
76136                left: [points[1 - idx][0], yc],
76137                right: [points[idx][0], yc]
76138            };
76139
76140            var name = rangeData.start.y;
76141
76142            if (+rangeData.end.y > +rangeData.start.y) {
76143                name = name + '-' + rangeData.end.y;
76144            }
76145
76146            var formatter = yearLabel.get('formatter');
76147
76148            var params = {
76149                start: rangeData.start.y,
76150                end: rangeData.end.y,
76151                nameMap: name
76152            };
76153
76154            var content = this._formatterLabel(formatter, params);
76155
76156            var yearText = new Text({z2: 30});
76157            setTextStyle(yearText.style, yearLabel, {text: content}),
76158                yearText.attr(this._yearTextPositionControl(yearText, posPoints[pos], orient, pos, margin));
76159
76160            group.add(yearText);
76161        },
76162
76163        _monthTextPositionControl: function (point, isCenter, orient, position, margin) {
76164            var align = 'left';
76165            var vAlign = 'top';
76166            var x = point[0];
76167            var y = point[1];
76168
76169            if (orient === 'horizontal') {
76170                y = y + margin;
76171
76172                if (isCenter) {
76173                    align = 'center';
76174                }
76175
76176                if (position === 'start') {
76177                    vAlign = 'bottom';
76178                }
76179            }
76180            else {
76181                x = x + margin;
76182
76183                if (isCenter) {
76184                    vAlign = 'middle';
76185                }
76186
76187                if (position === 'start') {
76188                    align = 'right';
76189                }
76190            }
76191
76192            return {
76193                x: x,
76194                y: y,
76195                textAlign: align,
76196                textVerticalAlign: vAlign
76197            };
76198        },
76199
76200        // render month and year text
76201        _renderMonthText: function (calendarModel, orient, group) {
76202            var monthLabel = calendarModel.getModel('monthLabel');
76203
76204            if (!monthLabel.get('show')) {
76205                return;
76206            }
76207
76208            var nameMap = monthLabel.get('nameMap');
76209            var margin = monthLabel.get('margin');
76210            var pos = monthLabel.get('position');
76211            var align = monthLabel.get('align');
76212
76213            var termPoints = [this._tlpoints, this._blpoints];
76214
76215            if (isString(nameMap)) {
76216                nameMap = MONTH_TEXT[nameMap.toUpperCase()] || [];
76217            }
76218
76219            var idx = pos === 'start' ? 0 : 1;
76220            var axis = orient === 'horizontal' ? 0 : 1;
76221            margin = pos === 'start' ? -margin : margin;
76222            var isCenter = (align === 'center');
76223
76224            for (var i = 0; i < termPoints[idx].length - 1; i++) {
76225
76226                var tmp = termPoints[idx][i].slice();
76227                var firstDay = this._firstDayOfMonth[i];
76228
76229                if (isCenter) {
76230                    var firstDayPoints = this._firstDayPoints[i];
76231                    tmp[axis] = (firstDayPoints[axis] + termPoints[0][i + 1][axis]) / 2;
76232                }
76233
76234                var formatter = monthLabel.get('formatter');
76235                var name = nameMap[+firstDay.m - 1];
76236                var params = {
76237                    yyyy: firstDay.y,
76238                    yy: (firstDay.y + '').slice(2),
76239                    MM: firstDay.m,
76240                    M: +firstDay.m,
76241                    nameMap: name
76242                };
76243
76244                var content = this._formatterLabel(formatter, params);
76245
76246                var monthText = new Text({z2: 30});
76247                extend(
76248                    setTextStyle(monthText.style, monthLabel, {text: content}),
76249                    this._monthTextPositionControl(tmp, isCenter, orient, pos, margin)
76250                );
76251
76252                group.add(monthText);
76253            }
76254        },
76255
76256        _weekTextPositionControl: function (point, orient, position, margin, cellSize) {
76257            var align = 'center';
76258            var vAlign = 'middle';
76259            var x = point[0];
76260            var y = point[1];
76261            var isStart = position === 'start';
76262
76263            if (orient === 'horizontal') {
76264                x = x + margin + (isStart ? 1 : -1) * cellSize[0] / 2;
76265                align = isStart ? 'right' : 'left';
76266            }
76267            else {
76268                y = y + margin + (isStart ? 1 : -1) * cellSize[1] / 2;
76269                vAlign = isStart ? 'bottom' : 'top';
76270            }
76271
76272            return {
76273                x: x,
76274                y: y,
76275                textAlign: align,
76276                textVerticalAlign: vAlign
76277            };
76278        },
76279
76280        // render weeks
76281        _renderWeekText: function (calendarModel, rangeData, orient, group) {
76282            var dayLabel = calendarModel.getModel('dayLabel');
76283
76284            if (!dayLabel.get('show')) {
76285                return;
76286            }
76287
76288            var coordSys = calendarModel.coordinateSystem;
76289            var pos = dayLabel.get('position');
76290            var nameMap = dayLabel.get('nameMap');
76291            var margin = dayLabel.get('margin');
76292            var firstDayOfWeek = coordSys.getFirstDayOfWeek();
76293
76294            if (isString(nameMap)) {
76295                nameMap = WEEK_TEXT[nameMap.toUpperCase()] || [];
76296            }
76297
76298            var start = coordSys.getNextNDay(
76299                rangeData.end.time, (7 - rangeData.lweek)
76300            ).time;
76301
76302            var cellSize = [coordSys.getCellWidth(), coordSys.getCellHeight()];
76303            margin = parsePercent$1(margin, cellSize[orient === 'horizontal' ? 0 : 1]);
76304
76305            if (pos === 'start') {
76306                start = coordSys.getNextNDay(
76307                    rangeData.start.time, -(7 + rangeData.fweek)
76308                ).time;
76309                margin = -margin;
76310            }
76311
76312            for (var i = 0; i < 7; i++) {
76313
76314                var tmpD = coordSys.getNextNDay(start, i);
76315                var point = coordSys.dataToRect([tmpD.time], false).center;
76316                var day = i;
76317                day = Math.abs((i + firstDayOfWeek) % 7);
76318                var weekText = new Text({z2: 30});
76319
76320                extend(
76321                    setTextStyle(weekText.style, dayLabel, {text: nameMap[day]}),
76322                    this._weekTextPositionControl(point, orient, pos, margin, cellSize)
76323                );
76324                group.add(weekText);
76325            }
76326        }
76327    });
76328
76329    /*
76330* Licensed to the Apache Software Foundation (ASF) under one
76331* or more contributor license agreements.  See the NOTICE file
76332* distributed with this work for additional information
76333* regarding copyright ownership.  The ASF licenses this file
76334* to you under the Apache License, Version 2.0 (the
76335* "License"); you may not use this file except in compliance
76336* with the License.  You may obtain a copy of the License at
76337*
76338*   http://www.apache.org/licenses/LICENSE-2.0
76339*
76340* Unless required by applicable law or agreed to in writing,
76341* software distributed under the License is distributed on an
76342* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
76343* KIND, either express or implied.  See the License for the
76344* specific language governing permissions and limitations
76345* under the License.
76346*/
76347
76348    /**
76349     * @file calendar.js
76350     * @author dxh
76351     */
76352
76353    /*
76354* Licensed to the Apache Software Foundation (ASF) under one
76355* or more contributor license agreements.  See the NOTICE file
76356* distributed with this work for additional information
76357* regarding copyright ownership.  The ASF licenses this file
76358* to you under the Apache License, Version 2.0 (the
76359* "License"); you may not use this file except in compliance
76360* with the License.  You may obtain a copy of the License at
76361*
76362*   http://www.apache.org/licenses/LICENSE-2.0
76363*
76364* Unless required by applicable law or agreed to in writing,
76365* software distributed under the License is distributed on an
76366* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
76367* KIND, either express or implied.  See the License for the
76368* specific language governing permissions and limitations
76369* under the License.
76370*/
76371
76372// Model
76373    extendComponentModel({
76374
76375        type: 'title',
76376
76377        layoutMode: {type: 'box', ignoreSize: true},
76378
76379        defaultOption: {
76380            // 一级层å
76381            zlevel: 0,
76382            // 二级层å
76383            z: 6,
76384            show: true,
76385
76386            text: '',
76387            // 超链接跳转
76388            // link: null,
76389            // 仅支持self | blank
76390            target: 'blank',
76391            subtext: '',
76392
76393            // 超链接跳转
76394            // sublink: null,
76395            // 仅支持self | blank
76396            subtarget: 'blank',
76397
76398            // 'center' ¦ 'left' ¦ 'right'
76399            // ¦ {number}(xåæ ‡ï¼Œå•ä½px)
76400            left: 0,
76401            // 'top' ¦ 'bottom' ¦ 'center'
76402            // ¦ {number}(yåæ ‡ï¼Œå•ä½px)
76403            top: 0,
76404
76405            // 水平对齐
76406            // 'auto' | 'left' | 'right' | 'center'
76407            // é»˜è®¤æ ¹æ® left 的位置判断是左对齐还是右对齐
76408            // textAlign: null
76409            //
76410            // 垂直对齐
76411            // 'auto' | 'top' | 'bottom' | 'middle'
76412            // é»˜è®¤æ ¹æ® top 位置判断是上对齐还是下对齐
76413            // textBaseline: null
76414
76415            backgroundColor: 'rgba(0,0,0,0)',
76416
76417            // æ ‡é¢˜è¾¹æ¡†é¢œè‰²
76418            borderColor: '#ccc',
76419
76420            // æ ‡é¢˜è¾¹æ¡†çº¿å®½ï¼Œå•ä½px,默认为0ï¼ˆæ— è¾¹æ¡†ï¼‰
76421            borderWidth: 0,
76422
76423            // æ ‡é¢˜å†…è¾¹è·ï¼Œå•ä½px,默认各方向内边距为5,
76424            // 接受数组分别设定上右下左边距,同css
76425            padding: 5,
76426
76427            // 主副æ ‡é¢˜çºµå‘间隔,单位px,默èÂ
76427®Â¤Ã¤Â¸Âº10,
76428            itemGap: 10,
76429            textStyle: {
76430                fontSize: 18,
76431                fontWeight: 'bolder',
76432                color: '#333'
76433            },
76434            subtextStyle: {
76435                color: '#aaa'
76436            }
76437        }
76438    });
76439
76440// View
76441    extendComponentView({
76442
76443        type: 'title',
76444
76445        render: function (titleModel, ecModel, api) {
76446            this.group.removeAll();
76447
76448            if (!titleModel.get('show')) {
76449                return;
76450            }
76451
76452            var group = this.group;
76453
76454            var textStyleModel = titleModel.getModel('textStyle');
76455            var subtextStyleModel = titleModel.getModel('subtextStyle');
76456
76457            var textAlign = titleModel.get('textAlign');
76458            var textBaseline = titleModel.get('textBaseline');
76459
76460            var textEl = new Text({
76461                style: setTextStyle({}, textStyleModel, {
76462                    text: titleModel.get('text'),
76463                    textFill: textStyleModel.getTextColor()
76464                }, {disableBox: true}),
76465                z2: 10
76466            });
76467
76468            var textRect = textEl.getBoundingRect();
76469
76470            var subText = titleModel.get('subtext');
76471            var subTextEl = new Text({
76472                style: setTextStyle({}, subtextStyleModel, {
76473                    text: subText,
76474                    textFill: subtextStyleModel.getTextColor(),
76475                    y: textRect.height + titleModel.get('itemGap'),
76476                    textVerticalAlign: 'top'
76477                }, {disableBox: true}),
76478                z2: 10
76479            });
76480
76481            var link = titleModel.get('link');
76482            var sublink = titleModel.get('sublink');
76483
76484            textEl.silent = !link;
76485            subTextEl.silent = !sublink;
76486
76487            if (link) {
76488                textEl.on('click', function () {
76489                    window.open(link, '_' + titleModel.get('target'));
76490                });
76491            }
76492            if (sublink) {
76493                subTextEl.on('click', function () {
76494                    window.open(sublink, '_' + titleModel.get('subtarget'));
76495                });
76496            }
76497
76498            group.add(textEl);
76499            subText && group.add(subTextEl);
76500            // If no subText, but add subTextEl, there will be an empty line.
76501
76502            var groupRect = group.getBoundingRect();
76503            var layoutOption = titleModel.getBoxLayoutParams();
76504            layoutOption.width = groupRect.width;
76505            layoutOption.height = groupRect.height;
76506            var layoutRect = getLayoutRect(
76507                layoutOption, {
76508                    width: api.getWidth(),
76509                    height: api.getHeight()
76510                }, titleModel.get('padding')
76511            );
76512            // Adjust text align based on position
76513            if (!textAlign) {
76514                // Align left if title is on the left. center and right is same
76515                textAlign = titleModel.get('left') || titleModel.get('r
76515ight');
76516                if (textAlign === 'middle') {
76517                    textAlign = 'center';
76518                }
76519                // Adjust layout by text align
76520                if (textAlign === 'right') {
76521                    layoutRect.x += layoutRect.width;
76522                }
76523                else if (textAlign === 'center') {
76524                    layoutRect.x += layoutRect.width / 2;
76525                }
76526            }
76527            if (!textBaseline) {
76528                textBaseline = titleModel.get('top') || titleModel.get('bottom');
76529                if (textBaseline === 'center') {
76530                    textBaseline = 'middle';
76531                }
76532                if (textBaseline === 'bottom') {
76533                    layoutRect.y += layoutRect.height;
76534                }
76535                else if (textBaseline === 'middle') {
76536                    layoutRect.y += layoutRect.height / 2;
76537                }
76538
76539                textBaseline = textBaseline || 'top';
76540            }
76541
76542            group.attr('position', [layoutRect.x, layoutRect.y]);
76543            var alignStyle = {
76544                textAlign: textAlign,
76545                textVerticalAlign: textBaseline
76546            };
76547            textEl.setStyle(alignStyle);
76548            subTextEl.setStyle(alignStyle);
76549
76550            // Render background
76551            // Get groupRect again because textAlign has been changed
76552            groupRect = group.getBoundingRect();
76553            var padding = layoutRect.margin;
76554            var style = titleModel.getItemStyle(['color', 'opacity']);
76555            style.fill = titleModel.get('backgroundColor');
76556            var rect = new Rect({
76557                shape: {
76558                    x: groupRect.x - padding[3],
76559                    y: groupRect.y - padding[0],
76560                    width: groupRect.width + padding[1] + padding[3],
76561                    height: groupRect.height + padding[0] + padding[2],
76562                    r: titleModel.get('borderRadius')
76563                },
76564                style: style,
76565                silent: true
76566            });
76567            subPixelOptimizeRect(rect);
76568
76569            group.add(rect);
76570        }
76571    });
76572
76573    /*
76574* Licensed to the Apache Software Foundation (ASF) under one
76575* or more contributor license agreements.  See the NOTICE file
76576* distributed with this work for additional information
76577* regarding copyright ownership.  The ASF licenses this file
76578* to you under the Apache License, Version 2.0 (the
76579* "License"); you may not use this file except in compliance
76580* with the License.  You may obtain a copy of the License at
76581*
76582*   http://www.apache.org/licenses/LICENSE-2.0
76583*
76584* Unless required by applicable law or agreed to in writing,
76585* software distributed under the License is distributed on an
76586* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
76587* KIND, either express or implied.  See the License for the
76588* specific language governing permissions and limitations
76589* under the License.
76590*/
76591
76592    ComponentModel.registerSubTypeDefaulter('dataZoom', function () {
76593        // Default 'slider' when no type specified.
76594        return 'slider';
76595    });
76596
76597    /*
76598* Licensed to the Apache Software Foundation (ASF) under one
76599* or more contributor license agreements.  See the NOTICE file
76600* distributed with this work for additional information
76601* regarding copyright ownership.  The ASF licenses this file
76602* to you under the Apache License, Version 2.0 (the
76603* "License"); you may not use this file except in compliance
76604* with the License.  You may obtain a copy of the License at
76605*
76606*   http://www.apache.org/licenses/LICENSE-2.0
76607*
76608* Unless required by applicable law or agreed to in writing,
76609* software distributed under the License is distributed on an
76610* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
76611* KIND, either express or implied.  See the License for the
76612* specific language governing permissions and limitations
76613* under the License.
76614*/
76615
76616    var AXIS_DIMS = ['x', 'y', 'z', 'radius', 'angle', 'single'];
76617// Supported coords.
76618    var COORDS = ['cartesian2d', 'polar', 'singleAxis'];
76619
76620    /**
76621     * @param {string} coordType
76622     * @return {boolean}
76623     */
76624    function isCoordSupported(coordType) {
76625        return indexOf(COORDS, coordType) >= 0;
76626    }
76627
76628    /**
76629     * Create "each" method to iterate names.
76630     *
76631     * @pubilc
76632     * @param  {Array.<string>} names
76633     * @param  {Array.<string>=} attrs
76634     * @return {Function}
76635     */
76636    function createNameEach(names, attrs) {
76637        names = names.slice();
76638        var capitalNames = map(names, capitalFirst);
76639        attrs = (attrs || []).slice();
76640        var capitalAttrs = map(attrs, capitalFirst);
76641
76642        return function (callback, context) {
76643            each$1(names, function (name, index) {
76644                var nameObj = {name: name, capital: capitalNames[index]};
76645
76646                for (var j = 0; j < attrs.length; j++) {
76647                    nameObj[attrs[j]] = name + capitalAttrs[j];
76648                }
76649
76650                callback.call(context, nameObj);
76651            });
76652        };
76653    }
76654
76655    /**
76656     * Iterate each dimension name.
76657     *
76658     * @public
76659     * @param {Function} callback The parameter is like:
76660     *                            {
76661 *                                name: 'angle',
76662 *                                capital: 'Angle',
76663 *                                axis: 'angleAxis',
76664 *                                axisIndex: 'angleAixs',
76665 *                                index: 'angleIndex'
76666 *                            }
76667     * @param {Object} context
76668     */
76669    var eachAxisDim$1 = createNameEach(AXIS_DIMS, ['axisIndex', 'axis', 'index', 'id']);
76670
76671    /**
76672     * If tow dataZoomModels has the same axis controlled, we say that they are 'linked'.
76673     * dataZoomModels and 'links' make up one or more graphics.
76674     * This function finds the graphic where the source dataZoomModel is in.
76675     *
76676     * @public
76677     * @param {Function} forEachNode Node iterator.
76678     * @param {Function} forEachEdgeType edgeType iterator
76679     * @param {Function} edgeIdGetter Giving node and edgeType, return an array of edge id.
76680     * @return {Function} Input: sourceNode, Output: Like {nodes: [], dims: {}}
76681     */
76682    function createLinkedNodesFinder(forEachNode, forEachEdgeType, edgeIdGetter) {
76683
76684        return function (sourceNode) {
76685            var result = {
76686                nodes: [],
76687                records: {} // key: edgeType.name, value: Object (key: edge id, value: boolean).
76688            };
76689
76690            forEachEdgeType(function (edgeType) {
76691                result.records[edgeType.name] = {};
76692            });
76693
76694            if (!sourceNode) {
76695                return result;
76696            }
76697
76698            absorb(sourceNode, result);
76699
76700            var existsLink;
76701            do {
76702                existsLink = false;
76703                forEachNode(processSingleNode);
76704            }
76705            while (existsLink);
76706
76707            function processSingleNode(node) {
76708                if (!isNodeAbsorded(node, result) && isLinked(node, result)) {
76709                    absorb(node, result);
76710                    existsLink = true;
76711                }
76712            }
76713
76714            return result;
76715        };
76716
76717        function isNodeAbsorded(node, result) {
76718            return indexOf(result.nodes, node) >= 0;
76719        }
76720
76721        function isLinked(node, result) {
76722            var hasLink = false;
76723            forEachEdgeType(function (edgeType) {
76724                each$1(edgeIdGetter(node, edgeType) || [], function (edgeId) {
76725                    result.records[edgeType.name][edgeId] && (hasLink = true);
76726                });
76727            });
76728            return hasLink;
76729        }
76730
76731        function absorb(node, result) {
76732            result.nodes.push(node);
76733            forEachEdgeType(function (edgeType) {
76734                each$1(edgeIdGetter(node, edgeType) || [], function (edgeId) {
76735                    result.records[edgeType.name][edgeId] = true;
76736                });
76737            });
76738        }
76739    }
76740
76741    /*
76742* Licensed to the Apache Software Foundation (ASF) under one
76743* or more contributor license agreements.  See the NOTICE file
76744* distributed with this work for additional information
76745* regarding copyright ownership.  The ASF licenses this file
76746* to you under the Apache License, Version 2.0 (the
76747* "License"); you may not use this file except in compliance
76748* with the License.  You may obtain a copy of the License at
76749*
76750*   http://www.apache.org/licenses/LICENSE-2.0
76751*
76752* Unless required by applicable law or agreed to in writing,
76753* software distributed under the License is distributed on an
76754* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
76755* KIND, either express or implied.  See the License for the
76756* specific language governing permissions and limitations
76757* under the License.
76758*/
76759
76760    var each$22 = each$1;
76761    var asc$1 = asc;
76762
76763    /**
76764     * Operate single axis.
76765     * One axis can only operated by one axis operator.
76766     * Different dataZoomModels may be defined to operate the same axis.
76767     * (i.e. 'inside' data zoom and 'slider' data zoom components)
76768     * So dataZoomModels share one axisProxy in that case.
76769     *
76770     * @class
76771     */
76772    var AxisProxy = function (dimName, axisIndex, dataZoomModel, ecModel) {
76773
76774        /**
76775         * @private
76776         * @type {string}
76777         */
76778        this._dimName = dimName;
76779
76780        /**
76781         * @private
76782         */
76783        this._axisIndex = axisIndex;
76784
76785        /**
76786         * @private
76787         * @type {Array.<number>}
76788         */
76789        this._valueWindow;
76790
76791        /**
76792         * @private
76793         * @type {Array.<number>}
76794         */
76795        this._percentWindow;
76796
76797        /**
76798         * @private
76799         * @type {Array.<number>}
76800         */
76801        this._dataExtent;
76802
76803        /**
76804         * {minSpan, maxSpan, minValueSpan, maxValueSpan}
76805         * @private
76806         * @type {Object}
76807         */
76808        this._minMaxSpan;
76809
76810        /**
76811         * @readOnly
76812         * @type {module: echarts/model/Global}
76813         */
76814        this.ecModel = ecModel;
76815
76816        /**
76817         * @private
76818         * @type {module: echarts/component/dataZoom/DataZoomModel}
76819         */
76820        this._dataZoomModel = dataZoomModel;
76821
76822        // /**
76823        //  * @readOnly
76824        //  * @private
76825        //  */
76826        // this.hasSeriesStacked;
76827    };
76828
76829    AxisProxy.prototype = {
76830
76831        constructor: AxisProxy,
76832
76833        /**
76834         * Whether the axisProxy is hosted by dataZoomModel.
76835         *
76836         * @public
76837         * @param {module: echarts/component/dataZoom/DataZoomModel} dataZoomModel
76838         * @return {boolean}
76839         */
76840        hostedBy: function (dataZoomModel) {
76841            return this._dataZoomModel === dataZoomModel;
76842        },
76843
76844        /**
76845         * @return {Array.<number>} Value can only be NaN or finite value.
76846         */
76847        getDataValueWindow: function () {
76848            return this._valueWindow.slice();
76849        },
76850
76851        /**
76852         * @return {Array.<number>}
76853         */
76854        getDataPercentWindow: function () {
76855            return this._percentWindow.slice();
76856        },
76857
76858        /**
76859         * @public
76860         * @param {number} axisIndex
76861         * @return {Array} seriesModels
76862         */
76863        getTargetSeriesModels: function () {
76864            var seriesModels = [];
76865            var ecModel = this.ecModel;
76866
76867            ecModel.eachSeries(function (seriesModel) {
76868                if (isCoordSupported(seriesModel.get('coordinateSystem'))) {
76869                    var dimName = this._dimName;
76870                    var axisModel = ecModel.queryComponents({
76871                        mainType: dimName + 'Axis',
76872                        index: seriesModel.get(dimName + 'AxisIndex'),
76873                        id: seriesModel.get(dimName + 'AxisId')
76874                    })[0];
76875                    if (this._axisIndex === (axisModel && axisModel.componentIndex)) {
76876                        seriesModels.push(seriesModel);
76877                    }
76878                }
76879            }, this);
76880
76881            return seriesModels;
76882        },
76883
76884        getAxisModel: function () {
76885            return this.ecModel.getComponent(this._dimName + 'Axis', this._axisIndex);
76886        },
76887
76888        getOtherAxisModel: function () {
76889            var axisDim = this._dimName;
76890            var ecModel = this.ecModel;
76891            var axisModel = this.getAxisModel();
76892            var isCartesian = axisDim === 'x' || axisDim === 'y';
76893            var otherAxisDim;
76894            var coordSysIndexName;
76895            if (isCartesian) {
76896                coordSysIndexName = 'gridIndex';
76897                otherAxisDim = axisDim === 'x' ? 'y' : 'x';
76898            }
76899            else {
76900                coordSysIndexName = 'polarIndex';
76901                otherAxisDim = axisDim === 'angle' ? 'radius' : 'angle';
76902            }
76903            var foundOtherAxisModel;
76904            ecModel.eachComponent(otherAxisDim + 'Axis', function (otherAxisModel) {
76905                if ((otherAxisModel.get(coordSysIndexName) || 0)
76906                    === (axisModel.get(coordSysIndexName) || 0)
76907                ) {
76908                    foundOtherAxisModel = otherAxisModel;
76909                }
76910            });
76911            return foundOtherAxisModel;
76912        },
76913
76914        getMinMaxSpan: function () {
76915            return clone(this._minMaxSpan);
76916        },
76917
76918        /**
76919         * Only calculate by given range and this._dataExtent, do not change anything.
76920         *
76921         * @param {Object} opt
76922         * @param {number} [opt.start]
76923         * @param {number} [opt.end]
76924         * @param {number} [opt.startValue]
76925         * @param {number} [opt.endValue]
76926         */
76927        calculateDataWindow: function (opt) {
76928            var dataExtent = this._dataExtent;
76929            var axisModel = this.getAxisModel();
76930            var scale = axisModel.axis.scale;
76931            var rangePropMode = this._dataZoomModel.getRangePropMode();
76932            var percentExtent = [0, 100];
76933            var percentWindow = [
76934                opt.start,
76935                opt.end
76936            ];
76937            var valueWindow = [];
76938
76939            each$22(['startValue', 'endValue'], function (prop) {
76940                valueWindow.push(opt[prop] != null ? scale.parse(opt[prop]) : null);
76941            });
76942
76943            // Normalize bound.
76944            each$22([0, 1], function (idx) {
76945                var boundValue = valueWindow[idx];
76946                var boundPercent = percentWindow[idx];
76947
76948                // Notice: dataZoom is based either on `percentProp` ('start', 'end') or
76949                // on `valueProp` ('startValue', 'endValue'). The former one is suitable
76950                // for cases that a dataZoom component controls multiple axes with different
76951                // unit or extent, and the latter one is suitable for accurate zoom by pixel
76952                // (e.g., in dataZoomSelect). `valueProp` can be calculated from `percentProp`,
76953                // but it is awkward that `percentProp` can not be obtained from `valueProp`
76954                // accurately (because all of values that are overflow the `dataExtent` will
76955                // be calculated to percent '100%'). So we have to use
76956                // `dataZoom.getRangePropMode()` to mark which prop is used.
76957                // `rangePropMode` is updated only when setOption or dispatchAction, otherwise
76958                // it remains its original value.
76959
76960                if (rangePropMode[idx] === 'percent') {
76961                    if (boundPercent == null) {
76962                        boundPercent = percentExtent[idx];
76963                    }
76964                    // Use scale.parse to math round for category or time axis.
76965                    boundValue = scale.parse(linearMap(
76966                        boundPercent, percentExtent, dataExtent, true
76967                    ));
76968                }
76969                else {
76970                    // Calculating `percent` from `value` may be not accurate, because
76971                    // This calculation can not be inversed, because all of values that
76972                    // are overflow the `dataExtent` will be calculated to percent '100%'
76973                    boundPercent = linearMap(
76974                        boundValue, dataExtent, percentExtent, true
76975                    );
76976                }
76977
76978                // valueWindow[idx] = round(boundValue);
76979                // percentWindow[idx] = round(boundPercent);
76980                valueWindow[idx] = boundValue;
76981                percentWindow[idx] = boundPercent;
76982            });
76983
76984            return {
76985                valueWindow: asc$1(valueWindow),
76986                percentWindow: asc$1(percentWindow)
76987            };
76988        },
76989
76990        /**
76991         * Notice: reset should not be called before series.restoreData() called,
76992         * so it is recommanded to be called in "process stage" but not "model init
76993         * stage".
76994         *
76995         * @param {module: echarts/component/dataZoom/DataZoomModel} dataZoomModel
76996         */
76997        reset: function (dataZoomModel) {
76998            if (dataZoomModel !== this._dataZoomModel) {
76999                return;
77000            }
77001
77002            var targetSeries = this.getTargetSeriesModels();
77003            // Culculate data window and data extent, and record them.
77004            this._dataExtent = calculateDataExtent(this, this._dimName, targetSeries);
77005
77006            // this.hasSeriesStacked = false;
77007            // each(targetSeries, function (series) {
77008            // var data = series.getData();
77009            // var dataDim = data.mapDimension(this._dimName);
77010            // var stackedDimension = data.getCalculationInfo('stackedDimension');
77011            // if (stackedDimension && stackedDimension === dataDim) {
77012            // this.hasSeriesStacked = true;
77013            // }
77014            // }, this);
77015
77016            var dataWindow = this.calculateDataWindow(dataZoomModel.option);
77017
77018            this._valueWindow = dataWindow.valueWindow;
77019            this._percentWindow = dataWindow.percentWindow;
77020
77021            setMinMaxSpan(this);
77022
77023            // Update axis setting then.
77024            setAxisModel(this);
77025        },
77026
77027        /**
77028         * @param {module: echarts/component/dataZoom/DataZoomModel} dataZoomModel
77029         */
77030        restore: function (dataZoomModel) {
77031            if (dataZoomModel !== this._dataZoomModel) {
77032                return;
77033            }
77034
77035            this._valueWindow = this._percentWindow = null;
77036            setAxisModel(this, true);
77037        },
77038
77039        /**
77040         * @param {module: echarts/component/dataZoom/DataZoomModel} dataZoomModel
77041         */
77042        filterData: function (dataZoomModel, api) {
77043            if (dataZoomModel !== this._dataZoomModel) {
77044                return;
77045            }
77046
77047            var axisDim = this._dimName;
77048            var seriesModels = this.getTargetSeriesModels();
77049            var filterMode = dataZoomModel.get('filterMode');
77050            var valueWindow = this._valueWindow;
77051
77052            if (filterMode === 'none') {
77053                return;
77054            }
77055
77056            // FIXME
77057            // Toolbox may has dataZoom injected. And if there are stacked bar chart
77058            // with NaN data, NaN will be filtered and stack will be wrong.
77059            // So we need to force the mode to be set empty.
77060            // In fect, it is not a big deal that do not support filterMode-'filter'
77061            // when using toolbox#dataZoom, utill tooltip#dataZoom support "single axis
77062            // selection" some day, which might need "adapt to data extent on the
77063            // otherAxis", which is disabled by filterMode-'empty'.
77064            // But currently, stack has been fixed to based on value but not index,
77065            // so this is not an issue any more.
77066            // var otherAxisModel = this.getOtherAxisModel();
77067            // if (dataZoomModel.get('$fromToolbox')
77068            //     && otherAxisModel
77069            //     && otherAxisModel.hasSeriesStacked
77070            // ) {
77071            //     filterMode = 'empty';
77072            // }
77073
77074            // TODO
77075            // filterMode 'weakFilter' and 'empty' is not optimized for huge data yet.
77076
77077            // Process series data
77078            each$22(seriesModels, function (seriesModel) {
77079                var seriesData = seriesModel.getData();
77080                var dataDims = seriesData.mapDimension(axisDim, true);
77081
77082                if (filterMode === 'weakFilter') {
77083                    seriesData.filterSelf(function (dataIndex) {
77084                        var leftOut;
77085                        var rightOut;
77086                        var hasValue;
77087                        for (var i = 0; i < dataDims.length; i++) {
77088                            var value = seriesData.get(dataDims[i], dataIndex);
77089                            var thisHasValue = !isNaN(value);
77090                            var thisLeftOut = value < valueWindow[0];
77091                            var thisRightOut = value > valueWindow[1];
77092                            if (thisHasValue && !thisLeftOut && !thisRightOut) {
77093                                return true;
77094                            }
77095                            thisHasValue && (hasValue = true);
77096                            thisLeftOut && (leftOut = true);
77097                            thisRightOut && (rightOut = true);
77098                        }
77099                        // If both left out and right out, do not filter.
77100                        return hasValue && leftOut && rightOut;
77101                    });
77102                }
77103                else {
77104                    each$22(dataDims, function (dim) {
77105                        if (filterMode === 'empty') {
77106                            seriesModel.setData(
77107                                seriesData.map(dim, function (value) {
77108                                    return !isInWindow(value) ? NaN : value;
77109                                })
77110                            );
77111                        }
77112                        else {
77113                            var range = {};
77114                            range[dim] = valueWindow;
77115
77116                            // console.time('select');
77117                            seriesData.selectRange(range);
77118                            // console.timeEnd('select');
77119                        }
77120                    });
77121                }
77122
77123                each$22(dataDims, function (dim) {
77124                    seriesData.setApproximateExtent(valueWindow, dim);
77125                });
77126            });
77127
77128            function isInWindow(value) {
77129                return value >= valueWindow[0] && value <= valueWindow[1];
77130            }
77131        }
77132    };
77133
77134    function calculateDataExtent(axisProxy, axisDim, seriesModels) {
77135        var dataExtent = [Infinity, -Infinity];
77136
77137        each$22(seriesModels, function (seriesModel) {
77138            var seriesData = seriesModel.getData();
77139            if (seriesData) {
77140                each$22(seriesData.mapDimension(axisDim, true), function (dim) {
77141                    var seriesExtent = seriesData.getApproximateExtent(dim);
77142                    seriesExtent[0] < dataExtent[0] && (dataExtent[0] = seriesExtent[0]);
77143                    seriesExtent[1] > dataExtent[1] && (dataExtent[1] = seriesExtent[1]);
77144                });
77145            }
77146        });
77147
77148        if (dataExtent[1] < dataExtent[0]) {
77149            dataExtent = [NaN, NaN];
77150        }
77151
77152        // It is important to get "consistent" extent when more then one axes is
77153        // controlled by a `dataZoom`, otherwise those axes will not be synchronized
77154        // when zooming. But it is difficult to know what is "consistent", considering
77155        // axes have different type or even different meanings (For example, two
77156        // time axes are used to compare data of the same date in different years).
77157        // So basically dataZoom just obtains extent by series.data (in category axis
77158        // extent can be obtained from axis.data).
77159        // Nevertheless, user can set min/max/scale on axes to make extent of axes
77160        // consistent.
77161        fixExtentByAxis(axisProxy, dataExtent);
77162
77163        return dataExtent;
77164    }
77165
77166    function fixExtentByAxis(axisProxy, dataExtent) {
77167        var axisModel = axisProxy.getAxisModel();
77168        var min = axisModel.getMin(true);
77169
77170        // For category axis, if min/max/scale are not set, extent is determined
77171        // by axis.data by default.
77172        var isCategoryAxis = axisModel.get('type') === 'category';
77173        var axisDataLen = isCategoryAxis && axisModel.getCategories().length;
77174
77175        if (min != null && min !== 'dataMin' && typeof min !== 'function') {
77176            dataExtent[0] = min;
77177        }
77178        else if (isCategoryAxis) {
77179            dataExtent[0] = axisDataLen > 0 ? 0 : NaN;
77180        }
77181
77182        var max = axisModel.getMax(true);
77183        if (max != null && max !== 'dataMax' && typeof max !== 'function') {
77184            dataExtent[1] = max;
77185        }
77186        else if (isCategoryAxis) {
77187            dataExtent[1] = axisDataLen > 0 ? axisDataLen - 1 : NaN;
77188        }
77189
77190        if (!axisModel.get('scale', true)) {
77191            dataExtent[0] > 0 && (dataExtent[0] = 0);
77192            dataExtent[1] < 0 && (dataExtent[1] = 0);
77193        }
77194
77195        // For value axis, if min/max/scale are not set, we just use the extent obtained
77196        // by series data, which may be a little different from the extent calculated by
77197        // `axisHelper.getScaleExtent`. But the different just affects the experience a
77198        // little when zooming. So it will not be fixed until some users require it strongly.
77199
77200        return dataExtent;
77201    }
77202
77203    function setAxisModel(axisProxy, isRestore) {
77204        var axisModel = axisProxy.getAxisModel();
77205
77206        var percentWindow = axisProxy._percentWindow;
77207        var valueWindow = axisProxy._valueWindow;
77208
77209        if (!percentWindow) {
77210            return;
77211        }
77212
77213        // [0, 500]: arbitrary value, guess axis extent.
77214        var precision = getPixelPrecision(valueWindow, [0, 500]);
77215        precision = Math.min(precision, 20);
77216        // isRestore or isFull
77217        var useOrigin = isRestore || (percentWindow[0] === 0 && percentWindow[1] === 100);
77218
77219        axisModel.setRange(
77220            useOrigin ? null : +valueWindow[0].toFixed(precision),
77221            useOrigin ? null : +valueWindow[1].toFixed(precision)
77222        );
77223    }
77224
77225    function setMinMaxSpan(axisProxy) {
77226        var minMaxSpan = axisProxy._minMaxSpan = {};
77227        var dataZoomModel = axisProxy._dataZoomModel;
77228
77229        each$22(['min', 'max'], function (minMax) {
77230            minMaxSpan[minMax + 'Span'] = dataZoomModel.get(minMax + 'Span');
77231
77232            // minValueSpan and maxValueSpan has higher priority than minSpan and maxSpan
77233            var valueSpan = dataZoomModel.get(minMax + 'ValueSpan');
77234
77235            if (valueSpan != null) {
77236                minMaxSpan[minMax + 'ValueSpan'] = valueSpan;
77237                valueSpan = axisProxy.getAxisModel().axis.scale.parse(valueSpan);
77238
77239                if (valueSpan != null) {
77240                    var dataExtent = axisProxy._dataExtent;
77241                    minMaxSpan[minMax + 'Span'] = linearMap(
77242                        dataExtent[0] + valueSpan, dataExtent, [0, 100], true
77243                    );
77244                }
77245            }
77246        });
77247    }
77248
77249    /*
77250* Licensed to the Apache Software Foundation (ASF) under one
77251* or more contributor license agreements.  See the NOTICE file
77252* distributed with this work for additional information
77253* regarding copyright ownership.  The ASF licenses this file
77254* to you under the Apache License, Version 2.0 (the
77255* "License");
77255 you may not use this file except in compliance
77256* with the License.  You may obtain a copy of the License at
77257*
77258*   http://www.apache.org/licenses/LICENSE-2.0
77259*
77260* Unless required by applicable law or agreed to in writing,
77261* software distributed under the License is distributed on an
77262* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
77263* KIND, either express or implied.  See the License for the
77264* specific language governing permissions and limitations
77265* under the License.
77266*/
77267
77268    var each$21 = each$1;
77269    var eachAxisDim = eachAxisDim$1;
77270
77271    var DataZoomModel = extendComponentModel({
77272
77273        type: 'dataZoom',
77274
77275        dependencies: [
77276            'xAxis', 'yAxis', 'zAxis', 'radiusAxis', 'angleAxis', 'singleAxis', 'series'
77277        ],
77278
77279        /**
77280         * @protected
77281         */
77282        defaultOption: {
77283            zlevel: 0,
77284            z: 4,                   // Higher than normal component (z: 2).
77285            orient: null,           // Default auto by axisIndex. Possible value: 'horizontal', 'vertical'.
77286            xAxisIndex: null,       // Default the first horizontal category axis.
77287            yAxisIndex: null,       // Default the first vertical category axis.
77288
77289            filterMode: 'filter',   // Possible values: 'filter' or 'empty' or 'weakFilter'.
77290                                    // 'filter': data items which are out of window will be removed. This option is
77291                                    //          applicable when filtering outliers. For each data item, it will be
77292                                    //          filtered if one of the relevant dimensions is out of the window.
77293                                    // 'weakFilter': data items which are out of window will be removed. This option
77294                                    //          is applicable when filtering outliers. For each data item, it will be
77295                                    //          filtered only if all  of the relevant dimensions are out of the same
77296                                    //          side of the window.
77297                                    // 'empty': data items which are out of window will be set to empty.
77298                                    //          This option is applicable when user should not neglect
77299                                    //          that there are some data items out of window.
77300                                    // 'none': Do not filter.
77301                                    // Taking line chart as an example, line will be broken in
77302                                    // the filtered points when filterModel is set to 'empty', but
77303                                    // be connected when set to 'filter'.
77304
77305            throttle: null,         // Dispatch action by the fixed rate, avoid frequency.
77306                                    // default 100. Do not throttle when use null/undefined.
77307                                    // If animation === true and animationDurationUpdate > 0,
77308                                    // default value is 100, otherwise 20.
77309            start: 0,               // Start percent. 0 ~ 100
77310            end: 100,               // End percent. 0 ~ 100
77311            startValue: null,       // Start value. If startValue specified, start is ignored.
77312            endValue: null,         // End value. If endValue specified, end is ignored.
77313            minSpan: null,          // 0 ~ 100
77314            maxSpan: null,          // 0 ~ 100
77315            minValueSpan: null,     // The range of dataZoom can not be smaller than that.
77316            maxValueSpan: null,     // The range of dataZoom can not be larger than that.
77317            rangeMode: null         // Array, can be 'value' or 'percent'.
77318        },
77319
77320        /**
77321         * @override
77322         */
77323        init: function (option, parentModel, ecModel) {
77324
77325            /**
77326             * key like x_0, y_1
77327             * @private
77328             * @type {Object}
77329             */
77330            this._dataIntervalByAxis = {};
77331
77332            /**
77333             * @private
77334             */
77335            this._dataInfo = {};
77336
77337            /**
77338             * key like x_0, y_1
77339             * @private
77340             */
77341            this._axisProxies = {};
77342
77343            /**
77344             * @readOnly
77345             */
77346            this.textStyleModel;
77347
77348            /**
77349             * @private
77350             */
77351            this._autoThrottle = true;
77352
77353            /**
77354             * 'percent' or 'value'
77355             * @private
77356             */
77357            this._rangePropMode = ['percent', 'percent'];
77358
77359            var rawOption = retrieveRaw(option);
77360
77361            this.mergeDefaultAndTheme(option, ecModel);
77362
77363            this.doInit(rawOption);
77364        },
77365
77366        /**
77367         * @override
77368         */
77369        mergeOption: function (newOption) {
77370            var rawOption = retrieveRaw(newOption);
77371
77372            //FIX #2591
77373            merge(this.option, newOption, true);
77374
77375            this.doInit(rawOption);
77376        },
77377
77378        /**
77379         * @protected
77380         */
77381        doInit: function (rawOption) {
77382            var thisOption = this.option;
77383
77384            // Disable realtime view update if canvas is not supported.
77385            if (!env$1.canvasSupported) {
77386                thisOption.realtime = false;
77387            }
77388
77389            this._setDefaultThrottle(rawOption);
77390
77391            updateRangeUse(this, rawOption);
77392
77393            each$21([['start', 'startValue'], ['end', 'endValue']], function (names, index) {
77394                // start/end has higher priority over startValue/endValue if they
77395                // both set, but we should make chart.setOption({endValue: 1000})
77396                // effective, rather than chart.setOption({endValue: 1000, end: null}).
77397                if (this._rangePropMode[index] === 'value') {
77398                    thisOption[names[0]] = null;
77399                }
77400                // Otherwise do nothing and use the merge result.
77401            }, this);
77402
77403            this.textStyleModel = this.getModel('textStyle');
77404
77405            this._resetTarget();
77406
77407            this._giveAxisProxies();
77408        },
77409
77410        /**
77411         * @private
77412         */
77413        _giveAxisProxies: function () {
77414            var axisProxies = this._axisProxies;
77415
77416            this.eachTargetAxis(function (dimNames, axisIndex, dataZoomModel, ecModel) {
77417                var axisModel = this.dependentModels[dimNames.axis][axisIndex];
77418
77419                // If exists, share axisProxy with other dataZoomModels.
77420                var axisProxy = axisModel.__dzAxisProxy || (
77421                    // Use the first dataZoomModel as the main model of axisProxy.
77422                    axisModel.__dzAxisProxy = new AxisProxy(
77423                        dimNames.name, axisIndex, this, ecModel
77424                    )
77425                );
77426                // FIXME
77427                // dispose __dzAxisProxy
77428
77429                axisProxies[dimNames.name + '_' + axisIndex] = axisProxy;
77430            }, this);
77431        },
77432
77433        /**
77434         * @private
77435         */
77436        _resetTarget: function () {
77437            var thisOption = this.option;
77438
77439            var autoMode = this._judgeAutoMode();
77440
77441            eachAxisDim(function (dimNames) {
77442                var axisIndexName = dimNames.axisIndex;
77443                thisOption[axisIndexName] = normalizeToArray(
77444                    thisOption[axisIndexName]
77445                );
77446            }, this);
77447
77448            if (autoMode === 'axisIndex') {
77449                this._autoSetAxisIndex();
77450            }
77451            else if (autoMode === 'orient') {
77452                this._autoSetOrient();
77453            }
77454        },
77455
77456        /**
77457         * @private
77458         */
77459        _judgeAutoMode: function () {
77460            // Auto set only works for setOption at the first time.
77461            // The following is user's reponsibility. So using merged
77462            // option is OK.
77463            var thisOption = this.option;
77464
77465            var hasIndexSpecified = false;
77466            eachAxisDim(function (dimNames) {
77467                // When user set axisIndex as a empty array, we think that user specify axisIndex
77468                // but do not want use auto mode. Because empty array may be encountered when
77469                // some error occured.
77470                if (thisOption[dimNames.axisIndex] != null) {
77471                    hasIndexSpecified = true;
77472                }
77473            }, this);
77474
77475            var orient = thisOption.orient;
77476
77477            if (orient == null && hasIndexSpecified) {
77478                return 'orient';
77479            }
77480            else if (!hasIndexSpecified) {
77481                if (orient == null) {
77482                    thisOption.orient = 'horizontal';
77483                }
77484                return 'axisIndex';
77485            }
77486        },
77487
77488        /**
77489         * @private
77490         */
77491        _autoSetAxisIndex: function () {
77492            var autoAxisIndex = true;
77493            var orient = this.get('orient', true);
77494            var thisOption = this.option;
77495            var dependentModels = this.dependentModels;
77496
77497            if (autoAxisIndex) {
77498                // Find axis that parallel to dataZoom as default.
77499                var dimName = orient === 'vertical' ? 'y' : 'x';
77500
77501                if (dependentModels[dimName + 'Axis'].length) {
77502                    thisOption[dimName + 'AxisIndex'] = [0];
77503                    autoAxisIndex = false;
77504                }
77505                else {
77506                    each$21(dependentModels.singleAxis, function (singleAxisModel) {
77507                        if (autoAxisIndex && singleAxisModel.get('orient', true) === orient) {
77508                            thisOption.singleAxisIndex = [singleAxisModel.componentIndex];
77509                            autoAxisIndex = false;
77510                        }
77511                    });
77512                }
77513            }
77514
77515            if (autoAxisIndex) {
77516                // Find the first category axis as default. (consider polar)
77517                eachAxisDim(function (dimNames) {
77518                    if (!autoAxisIndex) {
77519                        return;
77520                    }
77521                    var axisIndices = [];
77522                    var axisModels = this.dependentModels[dimNames.axis];
77523                    if (axisModels.length && !axisIndices.length) {
77524                        for (var i = 0, len = axisModels.length; i < len; i++) {
77525                            if (axisModels[i].get('type') === 'category') {
77526                                axisIndices.push(i);
77527                            }
77528                        }
77529                    }
77530                    thisOption[dimNames.axisIndex] = axisIndices;
77531                    if (axisIndices.length) {
77532                        autoAxisIndex = false;
77533                    }
77534                }, this);
77535            }
77536
77537            if (autoAxisIndex) {
77538                // FIXME
77539                // 这里是兼容ec2的写法(没指定xAxisIndex和yAxisIndex时把scatter和双数值轴折柱纳入dataZoom控制),
77540                // 但是实际是否需要Grid.js#getScaleByOption来判断(考虑time,log等axis type)?
77541
77542                // If both dataZoom.xAxisIndex and dataZoom.yAxisIndex is not specified,
77543                // dataZoom component auto adopts series that reference to
77544                // both xAxis and yAxis which type is 'value'.
77545                this.ecModel.eachSeries(function (seriesModel) {
77546                    if (this._isSeriesHasAllAxesTypeOf(seriesModel, 'value')) {
77547                        eachAxisDim(function (dimNames) {
77548                            var axisIndices = thisOption[dimNames.axisIndex];
77549
77550                            var axisIndex = seriesModel.get(dimNames.axisIndex);
77551                            var axisId = seriesModel.get(dimNames.axisId);
77552
77553                            var axisModel = seriesModel.ecModel.queryComponents({
77554                                mainType: dimNames.axis,
77555                                index: axisIndex,
77556                                id: axisId
77557                            })[0];
77558
77559                            if (__DEV__) {
77560                                if (!axisModel) {
77561                                    throw new Error(
77562                                        dimNames.axis + ' "' + retrieve(
77563                                        axisIndex,
77564                                        axisId,
77565                                        0
77566                                        ) + '" not found'
77567                                    );
77568                                }
77569                            }
77570                            axisIndex = axisModel.componentIndex;
77571
77572                            if (indexOf(axisIndices, axisIndex) < 0) {
77573                                axisIndices.push(axisIndex);
77574                            }
77575                        });
77576                    }
77577                }, this);
77578            }
77579        },
77580
77581        /**
77582         * @private
77583         */
77584        _autoSetOrient: function () {
77585            var dim;
77586
77587            // Find the first axis
77588            this.eachTargetAxis(function (dimNames) {
77589                !dim && (dim = dimNames.name);
77590            }, this);
77591
77592            this.option.orient = dim === 'y' ? 'vertical' : 'horizontal';
77593        },
77594
77595        /**
77596         * @private
77597         */
77598        _isSeriesHasAllAxesTypeOf: function (seriesModel, axisType) {
77599            // FIXME
77600            // 需要series的xAxisIndex和yAxisIndex都首先自动设置上。
77601            // 例如series.type === scatter时。
77602
77603            var is = true;
77604            eachAxisDim(function (dimNames) {
77605                var seriesAxisIndex = seriesModel.get(dimNames.axisIndex);
77606                var axisModel = this.dependentModels[dimNames.axis][seriesAxisIndex];
77607
77608                if (!axisModel || axisModel.get('type') !== axisType) {
77609                    is = false;
77610                }
77611            }, this);
77612            return is;
77613        },
77614
77615        /**
77616         * @private
77617         */
77618        _setDefaultThrottle: function (rawOption) {
77619            // When first time user set throttle, auto throttle ends.
77620            if (rawOption.hasOwnProperty('throttle')) {
77621                this._autoThrottle = false;
77622            }
77623            if (this._autoThrottle) {
77624                var globalOption = this.ecModel.option;
77625                this.option.throttle =
77626                    (globalOption.animation && globalOption.animationDurationUpdate > 0)
77627                        ? 100 : 20;
77628            }
77629        },
77630
77631        /**
77632         * @public
77633         */
77634        getFirstTargetAxisModel: function () {
77635            var firstAxisModel;
77636            eachAxisDim(function (dimNames) {
77637                if (firstAxisModel == null) {
77638                    var indices = this.get(dimNames.axisIndex);
77639                    if (indices.length) {
77640                        firstAxisModel = this.dependentModels[dimNames.axis][indices[0]];
77641                    }
77642                }
77643            }, this);
77644
77645            return firstAxisModel;
77646        },
77647
77648        /**
77649         * @public
77650         * @param {Function} callback param: axisModel, dimNames, axisIndex, dataZoomModel, ecModel
77651         */
77652        eachTargetAxis: function (callback, context) {
77653            var ecModel = this.ecModel;
77654            eachAxisDim(function (dimNames) {
77655                each$21(
77656                    this.get(dimNames.axisIndex),
77657                    function (axisIndex) {
77658                        callback.call(context, dimNames, axisIndex, this, ecModel);
77659                    },
77660                    this
77661                );
77662            }, this);
77663        },
77664
77665        /**
77666         * @param {string} dimName
77667         * @param {number} axisIndex
77668         * @return {module:echarts/component/dataZoom/AxisProxy} If not found, return null/undefined.
77669         */
77670        getAxisProxy: function (dimName, axisIndex) {
77671            return this._axisProxies[dimName + '_' + axisIndex];
77672        },
77673
77674        /**
77675         * @param {string} dimName
77676         * @param {number} axisIndex
77677         * @return {module:echarts/model/Model} If not found, return null/undefined.
77678         */
77679        getAxisModel: function (dimName, axisIndex) {
77680            var axisProxy = this.getAxisProxy(dimName, axisIndex);
77681            return axisProxy && axisProxy.getAxisModel();
77682        },
77683
77684        /**
77685         * If not specified, set to undefined.
77686         *
77687         * @public
77688         * @param {Object} opt
77689         * @param {number} [opt.start]
77690         * @param {number} [opt.end]
77691         * @param {number} [opt.startValue]
77692         * @param {number} [opt.endValue]
77693         * @param {boolean} [ignoreUpdateRangeUsg=false]
77694         */
77695        setRawRange: function (opt, ignoreUpdateRangeUsg) {
77696            var option = this.option;
77697            each$21([['start', 'startValue'], ['end', 'endValue']], function (names) {
77698                // If only one of 'start' and 'startValue' is not null/undefined, the other
77699                // should be cleared, which enable clear the option.
77700                // If both of them are not set, keep option with the original value, which
77701                // enable use only set start but not set end when calling `dispatchAction`.
77702                // The same as 'end' and 'endValue'.
77703                if (opt[names[0]] != null || opt[names[1]] != null) {
77704                    option[names[0]] = opt[names[0]];
77705                    option[names[1]] = opt[names[1]];
77706                }
77707            }, this);
77708
77709            !ignoreUpdateRangeUsg && updateRangeUse(this, opt);
77710        },
77711
77712        /**
77713         * @public
77714         * @return {Array.<number>} [startPercent, endPercent]
77715         */
77716        getPercentRange: function () {
77717            var axisProxy = this.findRepresentativeAxisProxy();
77718            if (axisProxy) {
77719                return axisProxy.getDataPercentWindow();
77720            }
77721        },
77722
77723        /**
77724         * @public
77725         * For example, chart.getModel().getComponent('dataZoom').getValueRange('y', 0);
77726         *
77727         * @param {string} [axisDimName]
77728         * @param {number} [axisIndex]
77729         * @return {Array.<number>} [startValue, endValue] value can only be '-' or finite number.
77730         */
77731        getValueRange: function (axisDimName, axisIndex) {
77732            if (axisDimName == null && axisIndex == null) {
77733                var axisProxy = this.findRepresentativeAxisProxy();
77734                if (axisProxy) {
77735                    return axisProxy.getDataValueWindow();
77736                }
77737            }
77738            else {
77739                return this.getAxisProxy(axisDimName, axisIndex).getDataValueWindow();
77740            }
77741        },
77742
77743        /**
77744         * @public
77745         * @param {module:echarts/model/Model} [axisModel] If axisModel given, find axisProxy
77746         *      corresponding to the axisModel
77747         * @return {module:echarts/component/dataZoom/AxisProxy}
77748         */
77749        findRepresentativeAxisProxy: function (axisModel) {
77750            if (axisModel) {
77751                return axisModel.__dzAxisProxy;
77752            }
77753
77754            // Find the first hosted axisProxy
77755            var axisProxies = this._axisProxies;
77756            for (var key in axisProxies) {
77757                if (axisProxies.hasOwnProperty(key) && axisProxies[key].hostedBy(this)) {
77758                    return axisProxies[key];
77759                }
77760            }
77761
77762            // If no hosted axis find not hosted axisProxy.
77763            // Consider this case: dataZoomModel1 and dataZoomModel2 control the same axis,
77764            // and the option.start or option.end settings are different. The percentRange
77765            // should follow axisProxy.
77766            // (We encounter this problem in toolbox data zoom.)
77767            for (var key in axisProxies) {
77768                if (axisProxies.hasOwnProperty(key) && !axisProxies[key].hostedBy(this)) {
77769                    return axisProxies[key];
77770                }
77771            }
77772        },
77773
77774        /**
77775         * @return {Array.<string>}
77776         */
77777        getRangePropMode: function () {
77778            return this._rangePropMode.slice();
77779        }
77780
77781    });
77782
77783    function retrieveRaw(option) {
77784        var ret = {};
77785        each$21(
77786            ['start', 'end', 'startValue', 'endValue', 'throttle'],
77787            function (name) {
77788                option.hasOwnProperty(name) && (ret[name] = option[name]);
77789            }
77790        );
77791        return ret;
77792    }
77793
77794    function updateRangeUse(dataZoomModel, rawOption) {
77795        var rangePropMode = dataZoomModel._rangePropMode;
77796        var rangeModeInOption = dataZoomModel.get('rangeMode');
77797
77798        each$21([['start', 'startValue'], ['end', 'endValue']], function (names, index) {
77799            var percentSpecified = rawOption[names[0]] != null;
77800            var valueSpecified = rawOption[names[1]] != null;
77801            if (percentSpecified && !valueSpecified) {
77802                rangePropMode[index] = 'percent';
77803            }
77804            else if (!percentSpecified && valueSpecified) {
77805                rangePropMode[index] = 'value';
77806            }
77807            else if (rangeModeInOption) {
77808                rangePropMode[index] = rangeModeInOption[index];
77809            }
77810            else if (percentSpecified) { // percentSpecified && valueSpecified
77811                rangePropMode[index] = 'percent';
77812            }
77813            // else remain its original setting.
77814        });
77815    }
77816
77817    /*
77818* Licensed to the Apache Software Foundation (ASF) under one
77819* or more contributor license agreements.  See the NOTICE file
77820* distributed with this work for additional information
77821* regarding copyright ownership.  The ASF licenses this file
77822* to you under the Apache License, Version 2.0 (the
77823* "License"); you may not use this file except in compliance
77824* with the License.  You may obtain a copy of the License at
77825*
77826*   http://www.apache.org/licenses/LICENSE-2.0
77827*
77828* Unless required by applicable law or agreed to in writing,
77829* software distributed under the License is distributed on an
77830* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
77831* KIND, either express or implied.  See the License for the
77832* specific language governing permissions and limitations
77833* under the License.
77834*/
77835
77836    var DataZoomView = Component.extend({
77837
77838        type: 'dataZoom',
77839
77840        render: function (dataZoomModel, ecModel, api, payload) {
77841            this.dataZoomModel = dataZoomModel;
77842            this.ecModel = ecModel;
77843            this.api = api;
77844        },
77845
77846        /**
77847         * Find the first target coordinate system.
77848         *
77849         * @protected
77850         * @return {Object} {
77851     *                   grid: [
77852     *                       {model: coord0, axisModels: [axis1, axis3], coordIndex: 1},
77853     *                       {model: coord1, axisModels: [axis0, axis2], coordIndex: 0},
77854     *                       ...
77855     *                   ],  // cartesians must not be null/undefined.
77856     *                   polar: [
77857     *                       {model: coord0, axisModels: [axis4], coordIndex: 0},
77858     *                       ...
77859     *                   ],  // polars must not be null/undefined.
77860     *                   singleAxis: [
77861     *                       {model: coord0, axisModels: [], coordIndex: 0}
77862     *                   ]
77863     */
77864        getTargetCoordInfo: function () {
77865            var dataZoomModel = this.dataZoomModel;
77866            var ecModel = this.ecModel;
77867            var coordSysLists = {};
77868
77869            dataZoomModel.eachTargetAxis(function (dimNames, axisIndex) {
77870                var axisModel = ecModel.getComponent(dimNames.axis, axisIndex);
77871                if (axisModel) {
77872                    var coordModel = axisModel.getCoordSysModel();
77873                    coordModel && save(
77874                        coordModel,
77875                        axisModel,
77876                        coordSysLists[coordModel.mainType] || (coordSysLists[coordModel.mainType] = []),
77877                        coordModel.componentIndex
77878                    );
77879                }
77880            }, this);
77881
77882            function save(coordModel, axisModel, store, coordIndex) {
77883                var item;
77884                for (var i = 0; i < store.length; i++) {
77885                    if (store[i].model === coordModel) {
77886                        item = store[i];
77887                        break;
77888                    }
77889                }
77890                if (!item) {
77891                    store.push(item = {
77892                        model: coordModel, axisModels: [], coordIndex: coordIndex
77893                    });
77894                }
77895                item.axisModels.push(axisModel);
77896            }
77897
77898            return coordSysLists;
77899        }
77900
77901    });
77902
77903    /*
77904* Licensed to the Apache Software Foundation (ASF) under one
77905* or more contributor license agreements.  See the NOTICE file
77906* distributed with this work for additional information
77907* regarding copyright ownership.  The ASF licenses this file
77908* to you under the Apache License, Version 2.0 (the
77909* "License"); you may not use this file except in compliance
77910* with the License.  You may obtain a copy of the License at
77911*
77912*   http://www.apache.org/licenses/LICENSE-2.0
77913*
77914* Unless required by applicable law or agreed to in writing,
77915* software distributed under the License is distributed on an
77916* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
77917* KIND, either express or implied.  See the License for the
77918* specific language governing permissions and limitations
77919* under the License.
77920*/
77921
77922    var SliderZoomModel = DataZoomModel.extend({
77923
77924        type: 'dataZoom.slider',
77925
77926        layoutMode: 'box',
77927
77928        /**
77929         * @protected
77930         */
77931        defaultOption: {
77932            show: true,
77933
77934            // ph => placeholder. Using placehoder here because
77935            // deault value can only be drived in view stage.
77936            right: 'ph',  // Default align to grid rect.
77937            top: 'ph',    // Default align to grid rect.
77938            width: 'ph',  // Default align to grid rect.
77939            height: 'ph', // Default align to grid rect.
77940            left: null,   // Default align to grid rect.
77941            bottom: null, // Default align to grid rect.
77942
77943            backgroundColor: 'rgba(47,69,84,0)',    // Background of slider zoom component.
77944            // dataBackgroundColor: '#ddd',         // Background coor of data shadow and border of box,
77945            // highest priority, remain for compatibility of
77946            // previous version, but not recommended any more.
77947            dataBackground: {
77948                lineStyle: {
77949                    color: '#2f4554',
77950                    width: 0.5,
77951                    opacity: 0.3
77952                },
77953                areaStyle: {
77954                    color: 'rgba(47,69,84,0.3)',
77955                    opacity: 0.3
77956                }
77957            },
77958            borderColor: '#ddd',                    // border color of the box. For compatibility,
77959                                                    // if dataBackgroundColor is set, borderColor
77960                                                    // is ignored.
77961
77962            fillerColor: 'rgba(167,183,204,0.4)',     // Color of selected area.
77963            // handleColor: 'rgba(89,170,216,0.95)',     // Color of handle.
77964            // handleIcon: 'path://M4.9,17.8c0-1.4,4.5-10.5,5.5-12.4c0-0.1,0.6-1.1,0.9-1.1c0.4,0,0.9,1,0.9,1.1c1.1,2.2,5.4,11,5.4,12.4v17.8c0,1.5-0.
779646,2.1-1.3,2.1H6.1c-0.7,0-1.3-0.6-1.3-2.1V17.8z',
77965            handleIcon: 'M8.2,13.6V3.9H6.3v9.7H3.1v14.9h3.3v9.7h1.8v-9.7h3.3V13.6H8.2z M9.7,24.4H4.8v-1.4h4.9V24.4z M9.7,19.1H4.8v-1.4h4.9V19.1z',
77966            // Percent of the slider height
77967            handleSize: '100%',
77968
77969            handleStyle: {
77970                color: '#a7b7cc'
77971            },
77972
77973            labelPrecision: null,
77974            labelFormatter: null,
77975            showDetail: true,
77976            showDataShadow: 'auto',                 // Default auto decision.
77977            realtime: true,
77978            zoomLock: false,                        // Whether disable zoom.
77979            textStyle: {
77980                color: '#333'
77981            }
77982        }
77983
77984    });
77985
77986    /*
77987* Licensed to the Apache Software Foundation (ASF) under one
77988* or more contributor license agreements.  See the NOTICE file
77989* distributed with this work for additional information
77990* regarding copyright ownership.  The ASF licenses this file
77991* to you under the Apache License, Version 2.0 (the
77992* "License"); you may not use this file except in compliance
77993* with the License.  You may obtain a copy of the License at
77994*
77995*   http://www.apache.org/licenses/LICENSE-2.0
77996*
77997* Unless required by applicable law or agreed to in writing,
77998* software distributed under the License is distributed on an
77999* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
78000* KIND, either express or implied.  See the License for the
78001* specific language governing permissions and limitations
78002* under the License.
78003*/
78004
78005    var Rect$2 = Rect;
78006    var linearMap$1 = linearMap;
78007    var asc$2 = asc;
78008    var bind$4 = bind;
78009    var each$23 = each$1;
78010
78011// Constants
78012    var DEFAULT_LOCATION_EDGE_GAP = 7;
78013    var DEFAULT_FRAME_BORDER_WIDTH = 1;
78014    var DEFAULT_FILLER_SIZE = 30;
78015    var HORIZONTAL = 'horizontal';
78016    var VERTICAL = 'vertical';
78017    var LABEL_GAP = 5;
78018    var SHOW_DATA_SHADOW_SERIES_TYPE = ['line', 'bar', 'candlestick', 'scatter'];
78019
78020    var SliderZoomView = DataZoomView.extend({
78021
78022        type: 'dataZoom.slider',
78023
78024        init: function (ecModel, api) {
78025
78026            /**
78027             * @private
78028             * @type {Object}
78029             */
78030            this._displayables = {};
78031
78032            /**
78033             * @private
78034             * @type {string}
78035             */
78036            this._orient;
78037
78038            /**
78039             * [0, 100]
78040             * @private
78041             */
78042            this._range;
78043
78044            /**
78045             * [coord of the first handle, coord of the second handle]
78046             * @private
78047             */
78048            this._handleEnds;
78049
78050            /**
78051             * [length, thick]
78052             * @private
78053             * @type {Array.<number>}
78054             */
78055            this._size;
78056
78057            /**
78058             * @private
78059             * @type {number}
78060             */
78061            this._handleWidth;
78062
78063            /**
78064             * @private
78065             * @type {number}
78066             */
78067            this._handleHeight;
78068
78069            /**
78070             * @private
78071             */
78072            this._location;
78073
78074            /**
78075             * @private
78076             */
78077            this._dragging;
78078
78079            /**
78080             * @private
78081             */
78082            this._dataShadowInfo;
78083
78084            this.api = api;
78085        },
78086
78087        /**
78088         * @override
78089         */
78090        render: function (dataZoomModel, ecModel, api, payload) {
78091            SliderZoomView.superApply(this, 'render', arguments);
78092
78093            createOrUpdate(
78094                this,
78095                '_dispatchZoomAction',
78096                this.dataZoomModel.get('throttle'),
78097                'fixRate'
78098            );
78099
78100            this._orient = dataZoomModel.get('orient');
78101
78102            if (this.dataZoomModel.get('show') === false) {
78103                this.group.removeAll();
78104                return;
78105            }
78106
78107            // Notice: this._resetInterval() should not be executed when payload.type
78108            // is 'dataZoom', origin this._range should be maintained, otherwise 'pan'
78109            // or 'zoom' info will be missed because of 'throttle' of this.dispatchAction,
78110            if (!payload || payload.type !== 'dataZoom' || payload.from !== this.uid) {
78111                this._buildView();
78112            }
78113
78114            this._updateView();
78115        },
78116
78117        /**
78118         * @override
78119         */
78120        remove: function () {
78121            SliderZoomView.superApply(this, 'remove', arguments);
78122            clear(this, '_dispatchZoomAction');
78123        },
78124
78125        /**
78126         * @override
78127         */
78128        dispose: function () {
78129            SliderZoomView.superApply(this, 'dispose', arguments);
78130            clear(this, '_dispatchZoomAction');
78131        },
78132
78133        _buildView: function () {
78134            var thisGroup = this.group;
78135
78136            thisGroup.removeAll();
78137
78138            this._resetLocation();
78139            this._resetInterval();
78140
78141            var barGroup = this._displayables.barGroup = new Group();
78142
78143            this._renderBackground();
78144
78145            this._renderHandle();
78146
78147            this._renderDataShadow();
78148
78149            thisGroup.add(barGroup);
78150
78151            this._positionGroup();
78152        },
78153
78154        /**
78155         * @private
78156         */
78157        _resetLocation: function () {
78158            var dataZoomModel = this.dataZoomModel;
78159            var api = this.api;
78160
78161            // If some of x/y/width/height are not specified,
78162            // auto-adapt according to target grid.
78163            var coordRect = this._findCoordRect();
78164            var ecSize = {width: api.getWidth(), height: api.getHeight()};
78165            // Default align by coordinate system rect.
78166            var positionInfo = this._orient === HORIZONTAL
78167                ? {
78168                    // Why using 'right', because right should be used in vertical,
78169                    // and it is better to be consistent for dealing with position param merge.
78170                    right: ecSize.width - coordRect.x - coordRect.width,
78171                    top: (ecSize.height - DEFAULT_FILLER_SIZE - DEFAULT_LOCATION_EDGE_GAP),
78172                    width: coordRect.width,
78173                    height: DEFAULT_FILLER_SIZE
78174                }
78175                : { // vertical
78176                    right: DEFAULT_LOCATION_EDGE_GAP,
78177                    top: coordRect.y,
78178                    width: DEFAULT_FILLER_SIZE,
78179                    height: coordRect.height
78180                };
78181
78182            // Do not write back to option and replace value 'ph', because
78183            // the 'ph' value should be recalculated when resize.
78184            var layoutParams = getLayoutParams(dataZoomModel.option);
78185
78186            // Replace the placeholder value.
78187            each$1(['right', 'top', 'width', 'height'], function (name) {
78188                if (layoutParams[name] === 'ph') {
78189                    layoutParams[name] = positionInfo[name];
78190                }
78191            });
78192
78193            var layoutRect = getLayoutRect(
78194                layoutParams,
78195                ecSize,
78196                dataZoomModel.padding
78197            );
78198
78199            this._location = {x: layoutRect.x, y: layoutRect.y};
78200            this._size = [layoutRect.width, layoutRect.height];
78201            this._orient === VERTICAL && this._size.reverse();
78202        },
78203
78204        /**
78205         * @private
78206         */
78207        _positionGroup: function () {
78208            var thisGroup = this.group;
78209            var location = this._location;
78210            var orient = this._orient;
78211
78212            // Just use the first axis to determine mapping.
78213            var targetAxisModel = this.dataZoomModel.getFirstTargetAxisModel();
78214            var inverse = targetAxisModel && targetAxisModel.get('inverse');
78215
78216            var barGroup = this._displayables.barGroup;
78217            var otherAxisInverse = (this._dataShadowInfo || {}).otherAxisInverse;
78218
78219            // Transform barGroup.
78220            barGroup.attr(
78221                (orient === HORIZONTAL && !inverse)
78222                    ? {scale: otherAxisInverse ? [1, 1] : [1, -1]}
78223                    : (orient === HORIZONTAL && inverse)
78224                    ? {scale: otherAxisInverse ? [-1, 1] : [-1, -1]}
78225                    : (orient === VERTICAL && !inverse)
78226                        ? {scale: otherAxisInverse ? [1, -1] : [1, 1], rotation: Math.PI / 2}
78227                        // Dont use Math.PI, considering shadow direction.
78228                        : {scale: otherAxisInverse ? [-1, -1] : [-1, 1], rotation: Math.PI / 2}
78229            );
78230
78231            // Position barGroup
78232            var rect = thisGroup.getBoundingRect([barGroup]);
78233            thisGroup.attr('position', [location.x - rect.x, location.y - rect.y]);
78234        },
78235
78236        /**
78237         * @private
78238         */
78239        _getViewExtent: function () {
78240            return [0, this._size[0]];
78241        },
78242
78243        _renderBackground: function () {
78244            var dataZoomModel = this.dataZoomModel;
78245            var size = this._size;
78246            var barGroup = this._displayables.barGroup;
78247
78248            barGroup.add(new Rect$2({
78249                silent: true,
78250                shape: {
78251                    x: 0, y: 0, width: size[0], height: size[1]
78252                },
78253                style: {
78254                    fill: dataZoomModel.get('backgroundColor')
78255                },
78256                z2: -40
78257            }));
78258
78259            // Click panel, over shadow, below handles.
78260            barGroup.add(new Rect$2({
78261                shape: {
78262                    x: 0, y: 0, width: size[0], height: size[1]
78263                },
78264                style: {
78265                    fill: 'transparent'
78266                },
78267                z2: 0,
78268                onclick: bind(this._onClickPanelClick, this)
78269            }));
78270        },
78271
78272        _renderDataShadow: function () {
78273            var info = this._dataShadowInfo = this._prepareDataShadowInfo();
78274
78275            if (!info) {
78276                return;
78277            }
78278
78279            var size = this._size;
78280            var seriesModel = info.series;
78281            var data = seriesModel.getRawData();
78282
78283            var otherDim = seriesModel.getShadowDim
78284                ? seriesModel.getShadowDim() // @see candlestick
78285                : info.otherDim;
78286
78287            if (otherDim == null) {
78288                return;
78289            }
78290
78291            var otherDataExtent = data.getDataExtent(otherDim);
78292            // Nice extent.
78293            var otherOffset = (otherDataExtent[1] - otherDataExtent[0]) * 0.3;
78294            otherDataExtent = [
78295                otherDataExtent[0] - otherOffset,
78296                otherDataExtent[1] + otherOffset
78297            ];
78298            var otherShadowExtent = [0, size[1]];
78299
78300            var thisShadowExtent = [0, size[0]];
78301
78302            var areaPoints = [[size[0], 0], [0, 0]];
78303            var linePoints = [];
78304            var step = thisShadowExtent[1] / (data.count() - 1);
78305            var thisCoord = 0;
78306
78307            // Optimize for large data shadow
78308            var stride = Math.round(data.count() / size[0]);
78309            var lastIsEmpty;
78310            data.each([otherDim], function (value, index) {
78311                if (stride > 0 && (index % stride)) {
78312                    thisCoord += step;
78313                    return;
78314                }
78315
78316                // FIXME
78317                // Should consider axis.min/axis.max when drawing dataShadow.
78318
78319                // FIXME
78320                // 应该使用统一的空判断?还是在list里进行空判断?
78321                var isEmpty = value == null || isNaN(value) || value === '';
78322                // See #4235.
78323                var otherCoord = isEmpty
78324                    ? 0 : linearMap$1(value, otherDataExtent, otherShadowExtent, true);
78325
78326                // Attempt to draw data shadow precisely when there are empty value.
78327                if (isEmpty && !lastIsEmpty && index) {
78328                    areaPoints.push([areaPoints[areaPoints.length - 1][0], 0]);
78329                    linePoints.push([linePoints[linePoints.length - 1][0], 0]);
78330                }
78331                else if (!isEmpty && lastIsEmpty) {
78332                    areaPoints.push([thisCoord, 0]);
78333                    linePoints.push([thisCoord, 0]);
78334                }
78335
78336                areaPoints.push([thisCoord, otherCoord]);
78337                linePoints.push([thisCoord, otherCoord]);
78338
78339                thisCoord += step;
78340                lastIsEmpty = isEmpty;
78341            });
78342
78343            var dataZoomModel = this.dataZoomModel;
78344            // var dataBackgroundModel = dataZoomModel.getModel('dataBackground');
78345            this._displayables.barGroup.add(new Polygon({
78346                shape: {points: areaPoints},
78347                style: defaults(
78348                    {fill: dataZoomModel.get('dataBackgroundColor')},
78349                    dataZoomModel.getModel('dataBackground.areaStyle').getAreaStyle()
78350                ),
78351                silent: true,
78352                z2: -20
78353            }));
78354            this._displayables.barGroup.add(new Polyline({
78355                shape: {points: linePoints},
78356                style: dataZoomModel.getModel('dataBackground.lineStyle').getLineStyle(),
78357                silent: true,
78358                z2: -19
78359            }));
78360        },
78361
78362        _prepareDataShadowInfo: function () {
78363            var dataZoomModel = this.dataZoomModel;
78364            var showDataShadow = dataZoomModel.get('showDataShadow');
78365
78366            if (showDataShadow === false) {
78367                return;
78368            }
78369
78370            // Find a representative series.
78371            var result;
78372            var ecModel = this.ecModel;
78373
78374            dataZoomModel.eachTargetAxis(function (dimNames, axisIndex) {
78375                var seriesModels = dataZoomModel
78376                    .getAxisProxy(dimNames.name, axisIndex)
78377                    .getTargetSeriesModels();
78378
78379                each$1(seriesModels, function (seriesModel) {
78380                    if (result) {
78381                        return;
78382                    }
78383
78384                    if (showDataShadow !== true && indexOf(
78385                            SHOW_DATA_SHADOW_SERIES_TYPE, seriesModel.get('type')
78386                        ) < 0
78387                    ) {
78388                        return;
78389                    }
78390
78391                    var thisAxis = ecModel.getComponent(dimNames.axis, axisIndex).axis;
78392                    var otherDim = getOtherDim(dimNames.name);
78393                    var otherAxisInverse;
78394                    var coordSys = seriesModel.coordinateSystem;
78395
78396                    if (otherDim != null && coordSys.getOtherAxis) {
78397                        otherAxisInverse = coordSys.getOtherAxis(thisAxis).inverse;
78398                    }
78399
78400                    otherDim = seriesModel.getData().mapDimension(otherDim);
78401
78402                    result = {
78403                        thisAxis: thisAxis,
78404                        series: seriesModel,
78405                        thisDim: dimNames.name,
78406                        otherDim: otherDim,
78407                        otherAxisInverse: otherAxisInverse
78408                    };
78409
78410                }, this);
78411
78412            }, this);
78413
78414            return result;
78415        },
78416
78417        _renderHandle: function () {
78418            var displaybles = this._displayables;
78419            var handles = displaybles.handles = [];
78420            var handleLabels = displaybles.handleLabels = [];
78421            var barGroup = this._displayables.barGroup;
78422            var size = this._size;
78423            var dataZoomModel = this.dataZoomModel;
78424
78425            barGroup.add(displaybles.filler = new Rect$2({
78426                draggable: true,
78427                cursor: getCursor(this._orient),
78428                drift: bind$4(this._onDragMove, this, 'all'),
78429                onmousemove: function (e) {
78430                    // Fot mobile devicem, prevent screen slider on the button.
78431                    stop(e.event);
78432                },
78433                ondragstart: bind$4(this._showDataInfo, this, true),
78434                ondragend: bind$4(this._onDragEnd, this),
78435                onmouseover: bind$4(this._showDataInfo, this, true),
78436                onmouseout: bind$4(this._showDataInfo, this, false),
78437                style: {
78438                    fill: dataZoomModel.get('fillerColor'),
78439                    textPosition : 'inside'
78440                }
78441            }));
78442
78443            // Frame border.
78444            barGroup.add(new Rect$2(subPixelOptimizeRect({
78445                silent: true,
78446                shape: {
78447                    x: 0,
78448                    y: 0,
78449                    width: size[0],
78450                    height: size[1]
78451                },
78452                style: {
78453                    stroke: dataZoomModel.get('dataBackgroundColor')
78454                    || dataZoomModel.get('borderColor'),
78455                    lineWidth: DEFAULT_FRAME_BORDER_WIDTH,
78456                    fill: 'rgba(0,0,0,0)'
78457                }
78458            })));
78459
78460            each$23([0, 1], function (handleIndex) {
78461                var path = createIcon(
78462                    dataZoomModel.get('handleIcon'),
78463                    {
78464                        cursor: getCursor(this._orient),
78465                        draggable: true,
78466                        drift: bind$4(this._onDragMove, this, handleIndex),
78467                        onmousemove: function (e) {
78468                            // Fot mobile devicem, prevent screen slider on the button.
78469                            stop(e.event);
78470                        },
78471                        ondragend: bind$4(this._onDragEnd, this),
78472                        onmouseover: bind$4(this._showDataInfo, this, true),
78473                        onmouseout: bind$4(this._showDataInfo, this, false)
78474                    },
78475                    {x: -1, y: 0, width: 2, height: 2}
78476                );
78477
78478                var bRect = path.getBoundingRect();
78479                this._handleHeight = parsePercent$1(dataZoomModel.get('handleSize'), this._size[1]);
78480                this._handleWidth = bRect.width / bRect.height * this._handleHeight;
78481
78482                path.setStyle(dataZoomModel.getModel('handleStyle').getItemStyle());
78483                var handleColor = dataZoomModel.get('handleColor');
78484                // Compatitable with previous version
78485                if (handleColor != null) {
78486                    path.style.fill = handleColor;
78487                }
78488
78489                barGroup.add(handles[handleIndex] = path);
78490
78491                var textStyleModel = dataZoomModel.textStyleModel;
78492
78493                this.group.add(
78494                    handleLabels[handleIndex] = new Text({
78495                        silent: true,
78496                        invisible: true,
78497                        style: {
78498                            x: 0, y: 0, text: '',
78499                            textVerticalAlign: 'middle',
78500                            textAlign: 'center',
78501                            textFill: textStyleModel.getTextColor(),
78502                            textFont: textStyleModel.getFont()
78503                        },
78504                        z2: 10
78505                    }));
78506
78507            }, this);
78508        },
78509
78510        /**
78511         * @private
78512         */
78513        _resetInterval: function () {
78514            var range = this._range = this.dataZoomModel.getPercentRange();
78515            var viewExtent = this._getViewExtent();
78516
78517            this._handleEnds = [
78518                linearMap$1(range[0], [0, 100], viewExtent, true),
78519                linearMap$1(range[1], [0, 100], viewExtent, true)
78520            ];
78521        },
78522
78523        /**
78524         * @private
78525         * @param {(number|string)} handleIndex 0 or 1 or 'all'
78526         * @param {number} delta
78527         * @return {boolean} changed
78528         */
78529        _updateInterval: function (handleIndex, delta) {
78530            var dataZoomModel = this.dataZoomModel;
78531            var handleEnds = this._handleEnds;
78532            var viewExtend = this._getViewExtent();
78533            var minMaxSpan = dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan();
78534            var percentExtent = [0, 100];
78535
78536            sliderMove(
78537                delta,
78538                handleEnds,
78539                viewExtend,
78540                dataZoomModel.get('zoomLock') ? 'all' : handleIndex,
78541                minMaxSpan.minSpan != null
78542                    ? linearMap$1(minMaxSpan.minSpan, percentExtent, viewExtend, true) : null,
78543                minMaxSpan.maxSpan != null
78544                    ? linearMap$1(minMaxSpan.maxSpan, percentExtent, viewExtend, true) : null
78545            );
78546
78547            var lastRange = this._range;
78548            var range = this._range = asc$2([
78549                linearMap$1(handleEnds[0], viewExtend, percentExtent, true),
78550                linearMap$1(handleEnds[1], viewExtend, percentExtent, true)
78551            ]);
78552
78553            return !lastRange || lastRange[0] !== range[0] || lastRange[1] !== range[1];
78554        },
78555
78556        /**
78557         * @private
78558         */
78559        _updateView: function (nonRealtime) {
78560            var displaybles = this._displayables;
78561            var handleEnds = this._handleEnds;
78562            var handleInterval = asc$2(handleEnds.slice());
78563            var size = this._size;
78564
78565            each$23([0, 1], function (handleIndex) {
78566                // Handles
78567                var handle = displaybles.handles[handleIndex];
78568                var handleHeight = this._handleHeight;
78569                handle.attr({
78570                    scale: [handleHeight / 2, handleHeight / 2],
78571                    position: [handleEnds[handleIndex], size[1] / 2 - handleHeight / 2]
78572                });
78573            }, this);
78574
78575            // Filler
78576            displaybles.filler.setShape({
78577                x: handleInterval[0],
78578                y: 0,
78579                width: handleInterval[1] - handleInterval[0],
78580                height: size[1]
78581            });
78582
78583            this._updateDataInfo(nonRealtime);
78584        },
78585
78586        /**
78587         * @private
78588         */
78589        _updateDataInfo: function (nonRealtime) {
78590            var dataZoomModel = this.dataZoomModel;
78591            var displaybles = this._displayables;
78592            var handleLabels = displaybles.handleLabels;
78593            var orient = this._orient;
78594            var labelTexts = ['', ''];
78595
78596            // FIXME
78597            // date型,支持formatter,autoformatter(ec2 date.getAutoFormatter)
78598            if (dataZoomModel.get('showDetail')) {
78599                var axisProxy = dataZoomModel.findRepresentativeAxisProxy();
78600
78601                if (axisProxy) {
78602                    var axis = axisProxy.getAxisModel().axis;
78603                    var range = this._range;
78604
78605                    var dataInterval = nonRealtime
78606                        // See #4434, data and axis are not processed and reset yet in non-realtime mode.
78607                        ? axisProxy.calculateDataWindow({
78608                            start: range[0], end: range[1]
78609                        }).valueWindow
78610                        : axisProxy.getDataValueWindow();
78611
78612                    labelTexts = [
78613                        this._formatLabel(dataInterval[0], axis),
78614                        this._formatLabel(dataInterval[1], axis)
78615                    ];
78616                }
78617            }
78618
78619            var orderedHandleEnds = asc$2(this._handleEnds.slice());
78620
78621            setLabel.call(this, 0);
78622            setLabel.call(this, 1);
78623
78624            function setLabel(handleIndex) {
78625                // Label
78626                // Text should not transform by barGroup.
78627                // Ignore handlers transform
78628                var barTransform = getTransform(
78629                    displaybles.handles[handleIndex].parent, this.group
78630                );
78631                var direction = transformDirection(
78632                    handleIndex === 0 ? 'right' : 'left', barTransform
78633                );
78634                var offset = this._handleWidth / 2 + LABEL_GAP;
78635                var textPoint = applyTransform$1(
78636                    [
78637                        orderedHandleEnds[handleIndex] + (handleIndex === 0 ? -offset : offset),
78638                        this._size[1] / 2
78639                    ],
78640                    barTransform
78641                );
78642                handleLabels[handleIndex].setStyle({
78643                    x: textPoint[0],
78644                    y: textPoint[1],
78645                    textVerticalAlign: orient === HORIZONTAL ? 'middle' : direction,
78646                    textAlign: orient === HORIZONTAL ? direction : 'center',
78647                    text: labelTexts[handleIndex]
78648                });
78649            }
78650        },
78651
78652        /**
78653         * @private
78654         */
78655        _formatLabel: function (value, axis) {
78656            var dataZoomModel = this.dataZoomModel;
78657            var labelFormatter = dataZoomModel.get('labelFormatter');
78658
78659            var labelPrecision = dataZoomModel.get('labelPrecision');
78660            if (labelPrecision == null || labelPrecision === 'auto') {
78661                labelPrecision = axis.getPixelPrecision();
78662            }
78663
78664            var valueStr = (value == null || isNaN(value))
78665                ? ''
78666                // FIXME Glue code
78667                : (axis.type === 'category' || axis.type === 'time')
78668                    ? axis.scale.getLabel(Math.round(value))
78669                    // param of toFixed should less then 20.
78670                    : value.toFixed(Math.min(labelPrecision, 20));
78671
78672            return isFunction$1(labelFormatter)
78673                ? labelFormatter(value, valueStr)
78674                : isString(labelFormatter)
78675                    ? labelFormatter.replace('{value}', valueStr)
78676                    : valueStr;
78677        },
78678
78679        /**
78680         * @private
78681         * @param {boolean} showOrHide true: show, false: hide
78682         */
78683        _showDataInfo: function (showOrHide) {
78684            // Always show when drgging.
78685            showOrHide = this._dragging || showOrHide;
78686
78687            var handleLabels = this._displayables.handleLabels;
78688            handleLabels[0].attr('invisible', !showOrHide);
78689            handleLabels[1].attr('invisible', !showOrHide);
78690        },
78691
78692        _onDragMove: function (handleIndex, dx, dy) {
78693            this._dragging = true;
78694
78695            // Transform dx, dy to bar coordination.
78696            var barTransform = this._displayables.barGroup.getLocalTransform();
78697            var vertex = applyTransform$1([dx, dy], barTransform, true);
78698
78699            var changed = this._updateInterval(handleIndex, vertex[0]);
78700
78701            var realtime = this.dataZoomModel.get('realtime');
78702
78703            this._updateView(!realtime);
78704
78705            // Avoid dispatch dataZoom repeatly but range not changed,
78706            // which cause bad visual effect when progressive enabled.
78707            changed && realtime && this._dispatchZoomAction();
78708        },
78709
78710        _onDragEnd: function () {
78711            this._dragging = false;
78712            this._showDataInfo(false);
78713
78714            // While in realtime mode and stream mode, dispatch action when
78715            // drag end will cause the whole view rerender, which is unnecessary.
78716            var realtime = this.dataZoomModel.get('realtime');
78717            !realtime && this._dispatchZoomAction();
78718        },
78719
78720        _onClickPanelClick: function (e) {
78721            var size = this._size;
78722            var localPoint = this._displayables.barGroup.transformCoordToLocal(e.offsetX, e.offsetY);
78723
78724            if (localPoint[0] < 0 || localPoint[0] > size[0]
78725                || localPoint[1] < 0 || localPoint[1] > size[1]
78726            ) {
78727                return;
78728            }
78729
78730            var handleEnds = this._handleEnds;
78731            var center = (handleEnds[0] + handleEnds[1]) / 2;
78732
78733            var changed = this._updateInterval('all', localPoint[0] - center);
78734            this._updateView();
78735            changed && this._dispatchZoomAction();
78736        },
78737
78738        /**
78739         * This action will be throttled.
78740         * @private
78741         */
78742        _dispatchZoomAction: function () {
78743            var range = this._range;
78744
78745            this.api.dispatchAction({
78746                type: 'dataZoom',
78747                from: this.uid,
78748                dataZoomId: this.dataZoomModel.id,
78749                start: range[0],
78750                end: range[1]
78751            });
78752        },
78753
78754        /**
78755         * @private
78756         */
78757        _findCoordRect: function () {
78758            // Find the grid coresponding to the first axis referred by dataZoom.
78759            var rect;
78760            each$23(this.getTargetCoordInfo(), function (coordInfoList) {
78761                if (!rect && coordInfoList.length) {
78762                    var coordSys = coordInfoList[0].model.coordinateSystem;
78763                    rect = coordSys.getRect && coordSys.getRect();
78764                }
78765            });
78766            if (!rect) {
78767                var width = this.api.getWidth();
78768                var height = this.api.getHeight();
78769                rect = {
78770                    x: width * 0.2,
78771                    y: height * 0.2,
78772                    width: width * 0.6,
78773                    height: height * 0.6
78774                };
78775            }
78776
78777            return rect;
78778        }
78779
78780    });
78781
78782    function getOtherDim(thisDim) {
78783        // FIXME
78784        // 这个逻辑和getOtherAxis里一致,但是写在这里是否不好
78785        var map$$1 = {x: 'y', y: 'x', radius: 'angle', angle: 'radius'};
78786        return map$$1[thisDim];
78787    }
78788
78789    function getCursor(orient) {
78790        return orient === 'vertical' ? 'ns-resize' : 'ew-resize';
78791    }
78792
78793    /*
78794* Licensed to the Apache Software Foundation (ASF) under one
78795* or more contributor license agreements.  See the NOTICE file
78796* distributed with this work for additional information
78797* regarding copyright ownership.  The ASF licenses this file
78798* to you under the Apache License, Version 2.0 (the
78799* "License"); you may not use this file except in compliance
78800* with the License.  You may obtain a copy of the License at
78801*
78802*   http://www.apache.org/licenses/LICENSE-2.0
78803*
78804* Unless required by applicable law or agreed to in writing,
78805* software distributed under the License is distributed on an
78806* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
78807* KIND, either express or implied.  See the License for the
78808* specific language governing permissions and limitations
78809* under the License.
78810*/
78811
78812    DataZoomModel.extend({
78813
78814        type: 'dataZoom.inside',
78815
78816        /**
78817         * @protected
78818         */
78819        defaultOption: {
78820            disabled: false,   // Whether disable this inside zoom.
78821            zoomLock: false,   // Whether disable zoom but only pan.
78822            zoomOnMouseWheel: true, // Can be: true / false / 'shift' / 'ctrl' / 'alt'.
78823            moveOnMouseMove: true,   // Can be: true / false / 'shift' / 'ctrl' / 'alt'.
78824            preventDefaultMouseMove: true
78825        }
78826    });
78827
78828    /*
78829* Licensed to the Apache Software Foundation (ASF) under one
78830* or more contributor license agreements.  See the NOTICE file
78831* distributed with this work for additional information
78832* regarding copyright ownership.  The ASF licenses this file
78833* to you under the Apache License, Version 2.0 (the
78834* "License"); you may not use this file except in compliance
78835* with the License.  You may obtain a copy of the License at
78836*
78837*   http://www.apache.org/licenses/LICENSE-2.0
78838*
78839* Unless required by applicable law or agreed to in writing,
78840* software distributed under the License is distributed on an
78841* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
78842* KIND, either express or implied.  See the License for the
78843* specific language governing permissions and limitations
78844* under the License.
78845*/
78846
78847// Only create one roam controller for each coordinate system.
78848// one roam controller might be refered by two inside data zoom
78849// components (for example, one for x and one for y). When user
78850// pan or zoom, only dispatch one action for those data zoom
78851// components.
78852
78853    var curry$6 = curry;
78854
78855    var ATTR$1 = '\0_ec_dataZoom_roams';
78856
78857
78858    /**
78859     * @public
78860     * @param {module:echarts/ExtensionAPI} api
78861     * @param {Object} dataZoomInfo
78862     * @param {string} dataZoomInfo.coordId
78863     * @param {Function} dataZoomInfo.containsPoint
78864     * @param {Array.<string>} dataZoomInfo.allCoordIds
78865     * @param {string} dataZoomInfo.dataZoomId
78866     * @param {number} dataZoomInfo.throttleRate
78867     * @param {Function} dataZoomInfo.panGetRange
78868     * @param {Function} dataZoomInfo.zoomGetRange
78869     * @param {boolean} [dataZoomInfo.zoomLock]
78870     * @param {boolean} [dataZoomInfo.disabled]
78871     */
78872    function register$2(api, dataZoomInfo) {
78873        var store = giveStore(api);
78874        var theDataZoomId = dataZoomInfo.dataZoomId;
78875        var theCoordId = dataZoomInfo.coordId;
78876
78877        // Do clean when a dataZoom changes its target coordnate system.
78878        // Avoid memory leak, dispose all not-used-registered.
78879        each$1(store, function (record, coordId) {
78880            var dataZoomInfos = record.dataZoomInfos;
78881            if (dataZoomInfos[theDataZoomId]
78882                && indexOf(dataZoomInfo.allCoordIds, theCoordId) < 0
78883            ) {
78884                delete dataZoomInfos[theDataZoomId];
78885                record.count--;
78886            }
78887        });
78888
78889        cleanStore(store);
78890
78891        var record = store[theCoordId];
78892        // Create if needed.
78893        if (!record) {
78894            record = store[theCoordId] = {
78895                coordId: theCoordId,
78896                dataZoomInfos: {},
78897                count: 0
78898            };
78899            record.controller = createController(api, record);
78900            record.dispatchAction = curry(dispatchAction$1, api);
78901        }
78902
78903        // Update reference of dataZoom.
78904        !(record.dataZoomInfos[theDataZoomId]) && record.count++;
78905        record.dataZoomInfos[theDataZoomId] = dataZoomInfo;
78906
78907        var controllerParams = mergeControllerParams(record.dataZoomInfos);
78908        record.controller.enable(controllerParams.controlType, controllerParams.opt);
78909
78910        // Consider resize, area should be always updated.
78911        record.controller.setPointerChecker(dataZoomInfo.containsPoint);
78912
78913        // Update throttle.
78914        createOrUpdate(
78915            record,
78916            'dispatchAction',
78917            dataZoomInfo.throttleRate,
78918            'fixRate'
78919        );
78920    }
78921
78922    /**
78923     * @public
78924     * @param {module:echarts/ExtensionAPI} api
78925     * @param {string} dataZoomId
78926     */
78927    function unregister$1(api, dataZoomId) {
78928        var store = giveStore(api);
78929
78930        each$1(store, function (record) {
78931            record.controller.dispose();
78932            var dataZoomInfos = record.dataZoomInfos;
78933            if (dataZoomInfos[dataZoomId]) {
78934                delete dataZoomInfos[dataZoomId];
78935                record.count--;
78936            }
78937        });
78938
78939        cleanStore(store);
78940    }
78941
78942    /**
78943     * @public
78944     */
78945    function generateCoordId(coordModel) {
78946        return coordModel.type + '\0_' + coordModel.id;
78947    }
78948
78949    /**
78950     * Key: coordId, value: {dataZoomInfos: [], count, controller}
78951     * @type {Array.<Object>}
78952     */
78953    function giveStore(api) {
78954        // Mount store on zrender instance, so that we do not
78955        // need to worry about dispose.
78956        var zr = api.getZr();
78957        return zr[ATTR$1] || (zr[ATTR$1] = {});
78958    }
78959
78960    function createController(api, newRecord) {
78961        var controller = new RoamController(api.getZr());
78962        controller.on('pan', curry$6(onPan, newRecord));
78963        controller.on('zoom', curry$6(onZoom, newRecord));
78964
78965        return controller;
78966    }
78967
78968    function cleanStore(store) {
78969        each$1(store, function (record, coordId) {
78970            if (!record.count) {
78971                record.controller.dispose();
78972                delete store[coordId];
78973            }
78974        });
78975    }
78976
78977    function onPan(record, dx, dy, oldX, oldY, newX, newY) {
78978        wrapAndDispatch(record, function (info) {
78979            return info.panGetRange(record.controller, dx, dy, oldX, oldY, newX, newY);
78980        });
78981    }
78982
78983    function onZoom(record, scale, mouseX, mouseY) {
78984        wrapAndDispatch(record, function (info) {
78985            return info.zoomGetRange(record.controller, scale, mouseX, mouseY);
78986        });
78987    }
78988
78989    function wrapAndDispatch(record, getRange) {
78990        var batch = [];
78991
78992        each$1(record.dataZoomInfos, function (info) {
78993            var range = getRange(info);
78994            !info.disabled && range && batch.push({
78995                dataZoomId: info.dataZoomId,
78996                start: range[0],
78997                end: range[1]
78998            });
78999        });
79000
79001        batch.length && record.dispatchAction(batch);
79002    }
79003
79004    /**
79005     * This action will be throttled.
79006     */
79007    function dispatchAction$1(api, batch) {
79008        api.dispatchAction({
79009            type: 'dataZoom',
79010            batch: batch
79011        });
79012    }
79013
79014    /**
79015     * Merge roamController settings when multiple dataZooms share one roamController.
79016     */
79017    function mergeControllerParams(dataZoomInfos) {
79018        var controlType;
79019        var opt = {};
79020        // DO NOT use reserved word (true, false, undefined) as key literally. Even if encapsulated
79021        // as string, it is probably revert to reserved word by compress tool. See #7411.
79022        var prefix = 'type_';
79023        var typePriority = {
79024            'type_true': 2,
79025            'type_move': 1,
79026            'type_false': 0,
79027            'type_undefined': -1
79028        };
79029        each$1(dataZoomInfos, function (dataZoomInfo) {
79030            var oneType = dataZoomInfo.disabled ? false : dataZoomInfo.zoomLock ? 'move' : true;
79031            if (typePriority[prefix + oneType] > typePriority[prefix + controlType]) {
79032                controlType = oneType;
79033            }
79034            // Do not support that different 'shift'/'ctrl'/'alt' setting used in one coord sys.
79035            extend(opt, dataZoomInfo.roamControllerOpt);
79036        });
79037
79038        return {
79039            controlType: controlType,
79040            opt: opt
79041        };
79042    }
79043
79044    /*
79045* Licensed to the Apache Software Foundation (ASF) under one
79046* or more contributor license agreements.  See the NOTICE file
79047* distributed with this work for additional information
79048* regarding copyright ownership.  The ASF licenses this file
79049* to you under the Apache License, Version 2.0 (the
79050* "License"); you may not use this file except in compliance
79051* with the License.  You may obtain a copy of the License at
79052*
79053*   http://www.apache.org/licenses/LICENSE-2.0
79054*
79055* Unless required by applicable law or agreed to in writing,
79056* software distributed under the License is distributed on an
79057* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
79058* KIND, either express or implied.  See the License for the
79059* specific language governing permissions and limitations
79060* under the License.
79061*/
79062
79063    var bind$5 = bind;
79064
79065    var InsideZoomView = DataZoomView.extend({
79066
79067        type: 'dataZoom.inside',
79068
79069        /**
79070         * @override
79071         */
79072        init: function (ecModel, api) {
79073            /**
79074             * 'throttle' is used in this.dispatchAction, so we save range
79075             * to avoid missing some 'pan' info.
79076             * @private
79077             * @type {Array.<number>}
79078             */
79079            this._range;
79080        },
79081
79082        /**
79083         * @override
79084         */
79085        render: function (dataZoomModel, ecModel, api, payload) {
79086            InsideZoomView.superApply(this, 'render', arguments);
79087
79088            // Hance the `throttle` util ensures to preserve command order,
79089            // here simply updating range all the time will not cause missing
79090            // any of the the roam change.
79091            this._range = dataZoomModel.getPercentRange();
79092
79093            // Reset controllers.
79094            each$1(this.getTargetCoordInfo(), function (coordInfoList, coordSysName) {
79095
79096                var allCoordIds = map(coordInfoList, function (coordInfo) {
79097                    return generateCoordId(coordInfo.model);
79098                });
79099
79100                each$1(coordInfoList, function (coordInfo) {
79101                    var coordModel = coordInfo.model;
79102                    var dataZoomOption = dataZoomModel.option;
79103
79104                    register$2(
79105                        api,
79106                        {
79107                            coordId: generateCoordId(coordModel),
79108                            allCoordIds: allCoordIds,
79109                            containsPoint: function (e, x, y) {
79110                                return coordModel.coordinateSystem.containPoint([x, y]);
79111                            },
79112                            dataZoomId: dataZoomModel.id,
79113                            throttleRate: dataZoomModel.get('throttle', true),
79114                            panGetRange: bind$5(this._onPan, this, coordInfo, coordSysName),
79115                            zoomGetRange: bind$5(this._onZoom, this, coordInfo, coordSysName),
79116                            zoomLock: dataZoomOption.zoomLock,
79117                            disabled: dataZoomOption.disabled,
79118                            roamControllerOpt: {
79119                                zoomOnMouseWheel: dataZoomOption.zoomOnMouseWheel,
79120                                moveOnMouseMove: dataZoomOption.moveOnMouseMove,
79121                                preventDefaultMouseMove: dataZoomOption.preventDefaultMouseMove
79122                            }
79123                        }
79124                    );
79125                }, this);
79126
79127            }, this);
79128        },
79129
79130        /**
79131         * @override
79132         */
79133        dispose: function () {
79134            unregister$1(this.api, this.dataZoomModel.id);
79135            InsideZoomView.superApply(this, 'dispose', arguments);
79136            this._range = null;
79137        },
79138
79139        /**
79140         * @private
79141         */
79142        _onPan: function (coordInfo, coordSysName, controller, dx, dy, oldX, oldY, newX, newY) {
79143            var lastRange = this._range;
79144            var range = lastRange.slice();
79145
79146            // Calculate transform by the first axis.
79147            var axisModel = coordInfo.axisModels[0];
79148            if (!axisModel) {
79149                return;
79150            }
79151
79152            var directionInfo = getDirectionInfo[coordSysName](
79153                [oldX, oldY], [newX, newY], axisModel, controller, coordInfo
79154            );
79155
79156            var percentDelta = directionInfo.signal
79157                * (range[1] - range[0])
79158                * directionInfo.pixel / directionInfo.pixelLength;
79159
79160            sliderMove(percentDelta, range, [0, 100], 'all');
79161
79162            this._range = range;
79163
79164            if (lastRange[0] !== range[0] || lastRange[1] !== range[1]) {
79165                return range;
79166            }
79167        },
79168
79169        /**
79170         * @private
79171         */
79172        _onZoom: function (coordInfo, coordSysName, controller, scale, mouseX, mouseY) {
79173            var lastRange = this._range;
79174            var range = lastRange.slice();
79175
79176            // Calculate transform by the first axis.
79177            var axisModel = coordInfo.axisModels[0];
79178            if (!axisModel) {
79179                return;
79180            }
79181
79182            var directionInfo = getDirectionInfo[coordSysName](
79183                null, [mouseX, mouseY], axisModel, controller, coordInfo
79184            );
79185            var percentPoint = (
79186                directionInfo.signal > 0
79187                    ? (directionInfo.pixelStart + directionInfo.pixelLength - directionInfo.pixel)
79188                    : (directionInfo.pixel - directionInfo.pixelStart)
79189            ) / directionInfo.pixelLength * (range[1] - range[0]) + range[0];
79190
79191            scale = Math.max(1 / scale, 0);
79192            range[0] = (range[0] - percentPoint) * scale + percentPoint;
79193            range[1] = (range[1] - percentPoint) * scale + percentPoint;
79194
79195            // Restrict range.
79196            var minMaxSpan = this.dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan();
79197
79198            sliderMove(0, range, [0, 100], 0, minMaxSpan.minSpan, minMaxSpan.maxSpan);
79199
79200            this._range = range;
79201
79202            if (lastRange[0] !== range[0] || lastRange[1] !== range[1]) {
79203                return range;
79204            }
79205        }
79206
79207    });
vendor: 1,494 bytes, lines 79208-79245
79208
79209    var getDirectionInfo = {
79210
79211        grid: function (oldPoint, newPoint, axisModel, controller, coordInfo) {
79212            var axis = axisModel.axis;
79213            var ret = {};
79214            var rect = coordInfo.model.coordinateSystem.getRect();
79215            oldPoint = oldPoint || [0, 0];
79216
79217            if (axis.dim === 'x') {
79218                ret.pixel = newPoint[0] - oldPoint[0];
79219                ret.pixelLength = rect.width;
79220                ret.pixelStart = rect.x;
79221                ret.signal = axis.inverse ? 1 : -1;
79222            }
79223            else { // axis.dim === 'y'
79224                ret.pixel = newPoint[1] - oldPoint[1];
79225                ret.pixelLength = rect.height;
79226                ret.pixelStart = rect.y;
79227                ret.signal = axis.inverse ? -1 : 1;
79228            }
79229
79230            return ret;
79231        },
79232
79233        polar: function (oldPoint, newPoint, axisModel, controller, coordInfo) {
79234            var axis = axisModel.axis;
79235            var ret = {};
79236            var polar = coordInfo.model.coordinateSystem;
79237            var radiusExtent = polar.getRadiusAxis().getExtent();
79238            var angleExtent = polar.getAngleAxis().getExtent();
79239
79240            oldPoint = oldPoint ? polar.pointToCoord(oldPoint) : [0, 0];
79241            newPoint = polar.pointToCoord(newPoint);
79242
79243            if (axisModel.mainType === 'radiusAxis') {
79244                ret.pixel = newPoint[0] - oldPoint[0];
79245                // ret.pixelLength = Math.abs(radiusExtent[1] - radiu
79245sExtent[0]);
79246                // ret.pixelStart = Math.min(radiusExtent[0], radiusExtent[1]);
79247                ret.pixelLength = radiusExtent[1] - radiusExtent[0];
79248                ret.pixelStart = radiusExtent[0];
79249                ret.signal = axis.inverse ? 1 : -1;
79250            }
79251            else { // 'angleAxis'
79252                ret.pixel = newPoint[1] - oldPoint[1];
79253                // ret.pixelLength = Math.abs(angleExtent[1] - angleExtent[0]);
79254                // ret.pixelStart = Math.min(angleExtent[0], angleExtent[1]);
79255                ret.pixelLength = angleExtent[1] - angleExtent[0];
79256                ret.pixelStart = angleExtent[0];
79257                ret.signal = axis.inverse ? -1 : 1;
79258            }
79259
79260            return ret;
79261        },
79262
79263        singleAxis: function (oldPoint, newPoint, axisModel, controller, coordInfo) {
79264            var axis = axisModel.axis;
79265            var rect = coordInfo.model.coordinateSystem.getRect();
79266            var ret = {};
79267
79268            oldPoint = oldPoint || [0, 0];
79269
79270            if (axis.orient === 'horizontal') {
79271                ret.pixel = newPoint[0] - oldPoint[0];
79272                ret.pixelLength = rect.width;
79273                ret.pixelStart = rect.x;
79274                ret.signal = axis.inverse ? 1 : -1;
79275            }
79276            else { // 'vertical'
79277                ret.pixel = newPoint[1] - oldPoint[1];
79278                ret.pixelLength = rect.height;
79279                ret.pixelStart = rect.y;
79280                ret.signal = axis.inverse ? -1 : 1;
79281            }
79282
79283            return ret;
79284        }
79285    };
79286
79287    /*
79288* Licensed to the Apache Software Foundation (ASF) under one
79289* or more contributor license agreements.  See the NOTICE file
79290* distributed with this work for additional information
79291* regarding copyright ownership.  The ASF licenses this file
79292* to you under the Apache License, Version 2.0 (the
79293* "License"); you may not use this file except in compliance
79294* with the License.  You may obtain a copy of the License at
79295*
79296*   http://www.apache.org/licenses/LICENSE-2.0
79297*
79298* Unless required by applicable law or agreed to in writing,
79299* software distributed under the License is distributed on an
79300* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
79301* KIND, either express or implied.  See the License for the
79302* specific language governing permissions and limitations
79303* under the License.
79304*/
79305
79306    registerProcessor({
79307
79308        // `dataZoomProcessor` will only be performed in needed series. Consider if
79309        // there is a line series and a pie series, it is better not to update the
79310        // line series if only pie series is needed to be updated.
79311        getTargetSeries: function (ecModel) {
79312            var seriesModelMap = createHashMap();
79313
79314            ecModel.eachComponent('dataZoom', function (dataZoomModel) {
79315                dataZoomModel.eachTargetAxis(function (dimNames, axisIndex, dataZoomModel) {
79316                    var axisProxy = dataZoomModel.getAxisProxy(dimNames.name, axisIndex);
79317                    each$1(axisProxy.getTargetSeriesModels(), function (seriesModel) {
79318                        seriesModelMap.set(seriesModel.uid, seriesModel);
79319                    });
79320                });
79321            });
79322
79323            return seriesModelMap;
79324        },
79325
79326        modifyOutputEnd: true,
79327
79328        // Consider appendData, where filter should be performed. Because data process is
79329        // in block mode currently, it is not need to worry about that the overallProgress
79330        // execute every frame.
79331        overallReset: function (ecModel, api) {
79332
79333            ecModel.eachComponent('dataZoom', function (dataZoomModel) {
79334                // We calculate window and reset axis here but not in model
79335                // init stage and not after action dispatch handler, because
79336                // reset should be called after seriesData.restoreData.
79337                dataZoomModel.eachTargetAxis(function (dimNames, axisIndex, dataZoomModel) {
79338                    dataZoomModel.getAxisProxy(dimNames.name, axisIndex).reset(dataZoomModel, api);
79339                });
79340
79341                // Caution: data zoom filtering is order sensitive when using
79342                // percent range and no min/max/scale set on axis.
79343                // For example, we have dataZoom definition:
79344                // [
79345                //      {xAxisIndex: 0, start: 30, end: 70},
79346                //      {yAxisIndex: 0, start: 20, end: 80}
79347                // ]
79348                // In this case, [20, 80] of y-dataZoom should be based on data
79349                // that have filtered by x-dataZoom using range of [30, 70],
79350                // but should not be based on full raw data. Thus sliding
79351                // x-dataZoom will change both ranges of xAxis and yAxis,
79352                // while sliding y-dataZoom will only change the range of yAxis.
79353                // So we should filter x-axis after reset x-axis immediately,
79354                // and then reset y-axis and filter y-axis.
79355                dataZoomModel.eachTargetAxis(function (dimNames, axisIndex, dataZoomModel) {
79356                    dataZoomModel.getAxisProxy(dimNames.name, axisIndex).filterData(dataZoomModel, api);
79357                });
79358            });
79359
79360            ecModel.eachComponent('dataZoom', function (dataZoomModel) {
79361                // Fullfill all of the range props so that user
79362                // is able to get them from chart.getOption().
79363                var axisProxy = dataZoomModel.findRepresentativeAxisProxy();
79364                var percentRange = axisProxy.getDataPercentWindow();
79365                var valueRange = axisProxy.getDataValueWindow();
79366
79367                dataZoomModel.setRawRange({
79368                    start: percentRange[0],
79369                    end: percentRange[1],
79370                    startValue: valueRange[0],
79371                    endValue: valueRange[1]
79372                }, true);
79373            });
79374        }
79375    });
79376
79377    /*
79378* Licensed to the Apache Software Foundation (ASF) under one
79379* or more contributor license agreements.  See the NOTICE file
79380* distributed with this work for additional information
79381* regarding copyright ownership.  The ASF licenses this file
79382* to you under the Apache License, Version 2.0 (the
79383* "License"); you may not use this file except in compliance
79384* with the License.  You may obtain a copy of the License at
79385*
79386*   http://www.apache.org/licenses/LICENSE-2.0
79387*
79388* Unless required by applicable law or agreed to in writing,
79389* software distributed under the License is distributed on an
79390* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
79391* KIND, either express or implied.  See the License for the
79392* specific language governing permissions and limitations
79393* under the License.
79394*/
79395
79396    registerAction('dataZoom', function (payload, ecModel) {
79397
79398        var linkedNodesFinder = createLinkedNodesFinder(
79399            bind(ecModel.eachComponent, ecModel, 'dataZoom'),
79400            eachAxisDim$1,
79401            function (model, dimNames) {
79402                return model.get(dimNames.axisIndex);
79403            }
79404        );
79405
79406        var effectedModels = [];
79407
79408        ecModel.eachComponent(
79409            {mainType: 'dataZoom', query: payload},
79410            function (model, index) {
79411                effectedModels.push.apply(
79412                    effectedModels, linkedNodesFinder(model).nodes
79413                );
79414            }
79415        );
79416
79417        each$1(effectedModels, function (dataZoomModel, index) {
79418            dataZoomModel.setRawRange({
79419                start: payload.start,
79420                end: payload.end,
79421                startValue: payload.startValue,
79422                endValue: payload.endValue
79423            });
79424        });
79425
79426    });
79427
79428    /*
79429* Licensed to the Apache Software Foundation (ASF) under one
79430* or more contributor license agreements.  See the NOTICE file
79431* distributed with this work for additional information
79432* regarding copyright ownership.  The ASF licenses this file
79433* to you under the Apache License, Version 2.0 (the
79434* "License"); you may not use this file except in compliance
79435* with the License.  You may obtain a copy of the License at
79436*
79437*   http://www.apache.org/licenses/LICENSE-2.0
79438*
79439* Unless required by applicable law or agreed to in writing,
79440* software distributed under the License is distributed on an
79441* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
79442* KIND, either express or implied.  See the License for the
79443* specific language governing permissions and limitations
79444* under the License.
79445*/
79446
79447    /**
79448     * DataZoom component entry
79449     */
79450
79451    /*
79452* Licensed to the Apache Software Foundation (ASF) under one
79453* or more contributor license agreements.  See the NOTICE file
79454* distributed with this work for additional information
79455* regarding copyright ownership.  The ASF licenses this file
79456* to you under the Apache License, Version 2.0 (the
79457* "License"); you may not use this file except in compliance
79458* with the License.  You may obtain a copy of the License at
79459*
79460*   http://www.apache.org/licenses/LICENSE-2.0
79461*
79462* Unless required by applicable law or agreed to in writing,
79463* software distributed under the License is distributed on an
79464* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
79465* KIND, either express or implied.  See the License for the
79466* specific language governing permissions and limitations
79467* under the License.
79468*/
79469
79470    var each$24 = each$1;
79471
79472    var preprocessor$2 = function (option) {
79473        var visualMap = option && option.visualMap;
79474
79475        if (!isArray(visualMap)) {
79476            visualMap = visualMap ? [visualMap] : [];
79477        }
79478
79479        each$24(visualMap, function (opt) {
79480            if (!opt) {
79481                return;
79482            }
79483
79484            // rename splitList to pieces
79485            if (has$1(opt, 'splitList') && !has$1(opt, 'pieces')) {
79486                opt.pieces = opt.splitList;
79487                delete opt.splitList;
79488            }
79489
79490            var pieces = opt.pieces;
79491            if (pieces && isArray(pieces)) {
79492                each$24(pieces, function (piece) {
79493                    if (isObject$1(piece)) {
79494                        if (has$1(piece, 'start') && !has$1(piece, 'min')) {
79495                            piece.min = piece.start;
79496                        }
79497                        if (has$1(piece, 'end') && !has$1(piece, 'max')) {
79498                            piece.max = piece.end;
79499                        }
79500                    }
79501                });
79502            }
79503        });
79504    };
79505
79506    function has$1(obj, name) {
79507        return obj && obj.hasOwnProperty && obj.hasOwnProperty(name);
79508    }
79509
79510    /*
79511* Licensed to the Apache Software Foundation (ASF) under one
79512* or more contributor license agreements.  See the NOTICE file
79513* distributed with this work for additional information
79514* regarding copyright ownership.  The ASF licenses this file
79515* to you under the Apache License, Version 2.0 (the
79516* "License");
79516 you may not use this file except in compliance
79517* with the License.  You may obtain a copy of the License at
79518*
79519*   http://www.apache.org/licenses/LICENSE-2.0
79520*
79521* Unless required by applicable law or agreed to in writing,
79522* software distributed under the License is distributed on an
79523* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
79524* KIND, either express or implied.  See the License for the
79525* specific language governing permissions and limitations
79526* under the License.
79527*/
79528
79529    ComponentModel.registerSubTypeDefaulter('visualMap', function (option) {
79530        // Compatible with ec2, when splitNumber === 0, continuous visualMap will be used.
79531        return (
79532            !option.categories
79533            && (
79534                !(
79535                    option.pieces
79536                        ? option.pieces.length > 0
79537                        : option.splitNumber > 0
79538                )
79539                || option.calculable
79540            )
79541        )
79542            ? 'continuous' : 'piecewise';
79543    });
79544
79545    /*
79546* Licensed to the Apache Software Foundation (ASF) under one
79547* or more contributor license agreements.  See the NOTICE file
79548* distributed with this work for additional information
79549* regarding copyright ownership.  The ASF licenses this file
79550* to you under the Apache License, Version 2.0 (the
79551* "License"); you may not use this file except in compliance
79552* with the License.  You may obtain a copy of the License at
79553*
79554*   http://www.apache.org/licenses/LICENSE-2.0
79555*
79556* Unless required by applicable law or agreed to in writing,
79557* software distributed under the License is distributed on an
79558* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
79559* KIND, either express or implied.  See the License for the
79560* specific language governing permissions and limitations
79561* under the License.
79562*/
79563
79564    var VISUAL_PRIORITY = PRIORITY.VISUAL.COMPONENT;
79565
79566    registerVisual(VISUAL_PRIORITY, {
79567        createOnAllSeries: true,
79568        reset: function (seriesModel, ecModel) {
79569            var resetDefines = [];
79570            ecModel.eachComponent('visualMap', function (visualMapModel) {
79571                var pipelineContext = seriesModel.pipelineContext;
79572                if (!visualMapModel.isTargetSeries(seriesModel)
79573                    || (pipelineContext && pipelineContext.large)
79574                ) {
79575                    return;
79576                }
79577
79578                resetDefines.push(incrementalApplyVisual(
79579                    visualMapModel.stateList,
79580                    visualMapModel.targetVisuals,
79581                    bind(visualMapModel.getValueState, visualMapModel),
79582                    visualMapModel.getDataDimension(seriesModel.getData())
79583                ));
79584            });
79585
79586            return resetDefines;
79587        }
79588    });
79589
79590// Only support color.
79591    registerVisual(VISUAL_PRIORITY, {
79592        createOnAllSeries: true,
79593        reset: function (seriesModel, ecModel) {
79594            var data = seriesModel.getData();
79595            var visualMetaList = [];
79596
79597            ecModel.eachComponent('visualMap', function (visualMapModel) {
79598                if (visualMapModel.isTargetSeries(seriesModel)) {
79599                    var visualMeta = visualMapModel.getVisualMeta(
79600                        bind(getColorVisual, null, seriesModel, visualMapModel)
79601                    ) || {stops: [], outerColors: []};
79602
79603                    var concreteDim = visualMapModel.getDataDimension(data);
79604                    var dimInfo = data.getDimensionInfo(concreteDim);
79605                    if (dimInfo != null) {
79606                        // visualMeta.dimension should be dimension index, but not concrete dimension.
79607                        visualMeta.dimension = dimInfo.index;
79608                        visualMetaList.push(visualMeta);
79609                    }
79610                }
79611            });
79612
79613            // console.log(JSON.stringify(visualMetaList.map(a => a.stops)));
79614            seriesModel.getData().setVisual('visualMeta', visualMetaList);
79615        }
79616    });
79617
79618// FIXME
79619// performance and export for heatmap?
79620// value can be Infinity or -Infinity
79621    function getColorVisual(seriesModel, visualMapModel, value, valueState) {
79622        var mappings = visualMapModel.targetVisuals[valueState];
79623        var visualTypes = VisualMapping.prepareVisualTypes(mappings);
79624        var resultVisual = {
79625            color: seriesModel.getData().getVisual('color') // default color.
79626        };
79627
79628        for (var i = 0, len = visualTypes.length; i < len; i++) {
79629            var type = visualTypes[i];
79630            var mapping = mappings[
79631                type === 'opacity' ? '__alphaForOpacity' : type
79632                ];
79633            mapping && mapping.applyVisual(value, getVisual, setVisual);
79634        }
79635
79636        return resultVisual.color;
79637
79638        function getVisual(key) {
79639            return resultVisual[key];
79640        }
79641
79642        function setVisual(key, value) {
79643            resultVisual[key] = value;
79644        }
79645    }
79646
79647    /*
79648* Licensed to the Apache Software Foundation (ASF) under one
79649* or more contributor license agreements.  See the NOTICE file
79650* distributed with this work for additional information
79651* regarding copyright ownership.  The ASF licenses this file
79652* to you under the Apache License, Version 2.0 (the
79653* "License"); you may not use this file except in compliance
79654* with the License.  You may obtain a copy of the License at
79655*
79656*   http://www.apache.org/licenses/LICENSE-2.0
79657*
79658* Unless required by applicable law or agreed to in writing,
79659* software distributed under the License is distributed on an
79660* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
79661* KIND, either express or implied.  See the License for the
79662* specific language governing permissions and limitations
79663* under the License.
79664*/
79665
79666    /**
79667     * @file Visual mapping.
79668     */
79669
79670    var visualDefault = {
79671
79672        /**
79673         * @public
79674         */
79675        get: function (visualType, key, isCategory) {
79676            var value = clone(
79677                (defaultOption$3[visualType] || {})[key]
79678            );
79679
79680            return isCategory
79681                ? (isArray(value) ? value[value.length - 1] : value)
79682                : value;
79683        }
79684
79685    };
79686
79687    var defaultOption$3 = {
79688
79689        color: {
79690            active: ['#006edd', '#e0ffff'],
79691            inactive: ['rgba(0,0,0,0)']
79692        },
79693
79694        colorHue: {
79695            active: [0, 360],
79696            inactive: [0, 0]
79697        },
79698
79699        colorSaturation: {
79700            active: [0.3, 1],
79701            inactive: [0, 0]
79702        },
79703
79704        colorLightness: {
79705            active: [0.9, 0.5],
79706            inactive: [0, 0]
79707        },
79708
79709        colorAlpha: {
79710            active: [0.3, 1],
79711            inactive: [0, 0]
79712        },
79713
79714        opacity: {
79715            active: [0.3, 1],
79716            inactive: [0, 0]
79717        },
79718
79719        symbol: {
79720            active: ['circle', 'roundRect', 'diamond'],
79721            inactive: ['none']
79722        },
79723
79724        symbolSize: {
79725            active: [10, 50],
79726            inactive: [0, 0]
79727        }
79728    };
79729
79730    /*
79731* Licensed to the Apache Software Foundation (ASF) under one
79732* or more contributor license agreements.  See the NOTICE file
79733* distributed with this work for additional information
79734* regarding copyright ownership.  The ASF licenses this file
79735* to you under the Apache License, Version 2.0 (the
79736* "License"); you may not use this file except in compliance
79737* with the License.  You may obtain a copy of the License at
79738*
79739*   http://www.apache.org/licenses/LICENSE-2.0
79740*
79741* Unless required by applicable law or agreed to in writing,
79742* software distributed under the License is distributed on an
79743* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
79744* KIND, either express or implied.  See the License for the
79745* specific language governing permissions and limitations
79746* under the License.
79747*/
79748
79749    var mapVisual$2 = VisualMapping.mapVisual;
79750    var eachVisual = VisualMapping.eachVisual;
79751    var isArray$3 = isArray;
79752    var each$25 = each$1;
79753    var asc$3 = asc;
79754    var linearMap$2 = linearMap;
79755    var noop$2 = noop;
79756
79757    var VisualMapModel = extendComponentModel({
79758
79759        type: 'visualMap',
79760
79761        dependencies: ['series'],
79762
79763        /**
79764         * @readOnly
79765         * @type {Array.<string>}
79766         */
79767        stateList: ['inRange', 'outOfRange'],
79768
79769        /**
79770         * @readOnly
79771         * @type {Array.<string>}
79772         */
79773        replacableOptionKeys: [
79774            'inRange', 'outOfRange', 'target', 'controller', 'color'
79775        ],
79776
79777        /**
79778         * [lowerBound, upperBound]
79779         *
79780         * @readOnly
79781         * @type {Array.<number>}
79782         */
79783        dataBound: [-Infinity, Infinity],
79784
79785        /**
79786         * @readOnly
79787         * @type {string|Object}
79788         */
79789        layoutMode: {type: 'box', ignoreSize: true},
79790
79791        /**
79792         * @protected
79793         */
79794        defaultOption: {
79795            show: true,
79796
79797            zlevel: 0,
79798            z: 4,
79799
79800            seriesIndex: 'all',     // 'all' or null/undefined: all series.
79801                                    // A number or an array of number: the specified series.
79802
79803            // set min: 0, max: 200, only for campatible with ec2.
79804            // In fact min max should not have default value.
79805            min: 0,                 // min value, must specified if pieces is not specified.
79806            max: 200,               // max value, must specified if pieces is not specified.
79807
79808            dimension: null,
79809            inRange: null,          // 'color', 'colorHue', 'colorSaturation', 'colorLightness', 'colorAlpha',
79810                                    // 'symbol', 'symbolSize'
79811            outOfRange: null,       // 'color', 'colorHue', 'colorSaturation',
79812                                    // 'colorLightness', 'colorAlpha',
79813                                    // 'symbol', 'symbolSize'
79814
79815            left: 0,                // 'center' ¦ 'left' ¦ 'right' ¦ {number} (px)
79816            right: null,            // The same as left.
79817            top: null,              // 'top' ¦ 'bottom' ¦ 'center' ¦ {number} (px)
79818            bottom: 0,              // The same as top.
79819
79820            itemWidth: null,
79821            itemHeight: null,
79822            inverse: false,
79823            orient: 'vertical',        // 'horizontal' ¦ 'vertical'
79824
79825            backgroundColor: 'rgba(0,0,0,0)',
79826            borderColor: '#ccc',       // 值域边框颜色
79827            contentColor: '#5793f3',
79828            inactiveColor: '#aaa',
79829            borderWidth: 0,            // 值域边框线宽,单位px,默èÂ
79829®Â¤Ã¤Â¸Âº0(æ— 边框)
79830            padding: 5,                // 值域内边距,单位px,默认各方向内边距为5,
79831            // 接受数组分别设定上右下左边距,同css
79832            textGap: 10,               //
79833            precision: 0,              // 小数精度,默认为0ï¼Œæ— å°æ•°ç‚¹
79834            color: null,               //颜色(deprecated,兼容ec2,顺序同pieces,不同于inRange/outOfRange)
79835
79836            formatter: null,
79837            text: null,                // 文本,如['高', '低'],兼容ec2,text[0]对应高值,text[1]对应低值
79838            textStyle: {
79839                color: '#333'          // 值域文字颜色
79840            }
79841        },
79842
79843        /**
79844         * @protected
79845         */
79846        init: function (option, parentModel, ecModel) {
79847
79848            /**
79849             * @private
79850             * @type {Array.<number>}
79851             */
79852            this._dataExtent;
79853
79854            /**
79855             * @readOnly
79856             */
79857            this.targetVisuals = {};
79858
79859            /**
79860             * @readOnly
79861             */
79862            this.controllerVisuals = {};
79863
79864            /**
79865             * @readOnly
79866             */
79867            this.textStyleModel;
79868
79869            /**
79870             * [width, height]
79871             * @readOnly
79872             * @type {Array.<number>}
79873             */
79874            this.itemSize;
79875
79876            this.mergeDefaultAndTheme(option, ecModel);
79877        },
79878
79879        /**
79880         * @protected
79881         */
79882        optionUpdated: function (newOption, isInit) {
79883            var thisOption = this.option;
79884
79885            // FIXME
79886            // necessary?
79887            // Disable realtime view update if canvas is not supported.
79888            if (!env$1.canvasSupported) {
79889                thisOption.realtime = false;
79890            }
79891
79892            !isInit && replaceVisualOption(
79893                thisOption, newOption, this.replacableOptionKeys
79894            );
79895
79896            this.textStyleModel = this.getModel('textStyle');
79897
79898            this.resetItemSize();
79899
79900            this.completeVisualOption();
79901        },
79902
79903        /**
79904         * @protected
79905         */
79906        resetVisual: function (supplementVisualOption) {
79907            var stateList = this.stateList;
79908            supplementVisualOption = bind(supplementVisualOption, this);
79909
79910            this.controllerVisuals = createVisualMappings(
79911                this.option.controller, stateList, supplementVisualOption
79912            );
79913            this.targetVisuals = createVisualMappings(
79914                this.option.target, stateList, supplementVisualOption
79915            );
79916        },
79917
79918        /**
79919         * @protected
79920         * @return {Array.<number>} An array of series indices.
79921         */
79922        getTargetSeriesIndices: function () {
79923            var optionSeriesIndex = this.option.seriesIndex;
79924            var seriesIndices = [];
79925
79926            if (optionSeriesIndex == null || optionSeriesIndex === 'all') {
79927                this.ecModel.eachSeries(function (seriesModel, index) {
79928                    seriesIndices.push(index);
79929                });
79930            }
79931            else {
79932                seriesIndices = normalizeToArray(optionSeriesIndex);
79933            }
79934
79935            return seriesIndices;
79936        },
79937
79938        /**
79939         * @public
79940         */
79941        eachTargetSeries: function (callback, context) {
79942            each$1(this.getTargetSeriesIndices(), function (seriesIndex) {
79943                callback.call(context, this.ecModel.getSeriesByIndex(seriesIndex));
79944            }, this);
79945        },
79946
79947        /**
79948         * @pubilc
79949         */
79950        isTargetSeries: function (seriesModel) {
79951            var is = false;
79952            this.eachTargetSeries(function (model) {
79953                model === seriesModel && (is = true);
79954            });
79955            return is;
79956        },
79957
79958        /**
79959         * @example
79960         * this.formatValueText(someVal); // format single numeric value to text.
79961         * this.formatValueText(someVal, true); // format single category value to text.
79962         * this.formatValueText([min, max]); // format numeric min-max to text.
79963         * this.formatValueText([this.dataBound[0], max]); // using data lower bound.
79964         * this.formatValueText([min, this.dataBound[1]]); // using data upper bound.
79965         *
79966         * @param {number|Array.<number>} value Real value, or this.dataBound[0 or 1].
79967         * @param {boolean} [isCategory=false] Only available when value is number.
79968         * @param {Array.<string>} edgeSymbols Open-close symbol when value is interval.
79969         * @return {string}
79970         * @protected
79971         */
79972        formatValueText: function(value, isCategory, edgeSymbols) {
79973            var option = this.option;
79974            var precision = option.precision;
79975            var dataBound = this.dataBound;
79976            var formatter = option.formatter;
79977            var isMinMax;
79978            var textValue;
79979            edgeSymbols = edgeSymbols || ['<', '>'];
79980
79981            if (isArray(value)) {
79982                value = value.slice();
79983                isMinMax = true;
79984            }
79985
79986            textValue = isCategory
79987                ? value
79988                : (isMinMax
79989                        ? [toFixed(value[0]), toFixed(value[1])]
79990                        : toFixed(value)
79991                );
79992
79993            if (isString(formatter)) {
79994                return formatter
79995                    .replace('{value}', isMinMax ? textValue[0] : textValue)
79996                    .replace('{value2}', isMinMax ? textValue[1] : textValue);
79997            }
79998            else if (isFunction$1(formatter)) {
79999                return isMinMax
80000                    ? formatter(value[0], value[1])
80001                    : formatter(value);
80002            }
80003
80004            if (isMinMax) {
80005                if (value[0] === dataBound[0]) {
80006                    return edgeSymbols[0] + ' ' + textValue[1];
80007                }
80008                else if (value[1] === dataBound[1]) {
80009                    return edgeSymbols[1] + ' ' + textValue[0];
80010                }
80011                else {
80012                    return textValue[0] + ' - ' + textValue[1];
80013                }
80014            }
80015            else { // Format single value (includes category case).
80016                return textValue;
80017            }
80018
80019            function toFixed(val) {
80020                return val === dataBound[0]
80021                    ? 'min'
80022                    : val === dataBound[1]
80023                        ? 'max'
80024                        : (+val).toFixed(Math.min(precision, 20));
80025            }
80026        },
80027
80028        /**
80029         * @protected
80030         */
80031        resetExtent: function () {
80032            var thisOption = this.option;
80033
80034            // Can not calculate data extent by data here.
80035            // Because series and data may be modified in processing stage.
80036            // So we do not support the feature "auto min/max".
80037
80038            var extent = asc$3([thisOption.min, thisOption.max]);
80039
80040            this._dataExtent = extent;
80041        },
80042
80043        /**
80044         * @public
80045         * @param {module:echarts/data/List} list
80046         * @return {string} Concrete dimention. If return null/undefined,
80047         *                  no dimension used.
80048         */
80049        getDataDimension: function (list) {
80050            var optDim = this.option.dimension;
80051            var listDimensions = list.dimensions;
80052            if (optDim == null && !listDimensions.length) {
80053                return;
80054            }
80055
80056            if (optDim != null) {
80057                return list.getDimension(optDim);
80058            }
80059
80060            var dimNames = list.dimensions;
80061            for (var i = dimNames.length - 1; i >= 0; i--) {
80062                var dimName = dimNames[i];
80063                var dimInfo = list.getDimensionInfo(dimName);
80064                if (!dimInfo.isCalculationCoord) {
80065                    return dimName;
80066                }
80067            }
80068        },
80069
80070        /**
80071         * @public
80072         * @override
80073         */
80074        getExtent: function () {
80075            return this._dataExtent.slice();
80076        },
80077
80078        /**
80079         * @protected
80080         */
80081        completeVisualOption: function () {
80082            var ecModel = this.ecModel;
80083            var thisOption = this.option;
80084            var base = {inRange: thisOption.inRange, outOfRange: thisOption.outOfRange};
80085
80086            var target = thisOption.target || (thisOption.target = {});
80087            var controller = thisOption.controller || (thisOption.controller = {});
80088
80089            merge(target, base); // Do not override
80090            merge(controller, base); // Do not override
80091
80092            var isCategory = this.isCategory();
80093
80094            completeSingle.call(this, target);
80095            completeSingle.call(this, controller);
80096            completeInactive.call(this, target, 'inRange', 'outOfRange');
80097            // completeInactive.call(this, target, 'outOfRange', 'inRange');
80098            completeController.call(this, controller);
80099
80100            function completeSingle(base) {
80101                // Compatible with ec2 dataRange.color.
80102                // The mapping order of dataRange.color is: [high value, ..., low value]
80103                // whereas inRange.color and outOfRange.color is [low value, ..., high value]
80104                // Notice: ec2 has no inverse.
80105                if (isArray$3(thisOption.color)
80106                    // If there has been inRange: {symbol: ...}, adding color is a mistake.
80107                    // So adding color only when no inRange defined.
80108                    && !base.inRange
80109                ) {
80110                    base.inRange = {color: thisOption.color.slice().reverse()};
80111                }
80112
80113                // Compatible with previous logic, always give a defautl color, otherwise
80114                // simple config with no inRange and outOfRange will not work.
80115                // Originally we use visualMap.color as the default color, but setOption at
80116                // the second time the default color will be erased. So we change to use
80117                // constant DEFAULT_COLOR.
80118                // If user do not want the defualt color, set inRange: {color: null}.
80119                base.inRange = base.inRange || {color: ecModel.get('gradientColor')};
80120
80121                // If using shortcut like: {inRange: 'symbol'}, complete default value.
80122                each$25(this.stateList, function (state) {
80123                    var visualType = base[state];
80124
80125                    if (isString(visualType)) {
80126                        var defa = visualDefault.get(visualType, 'active', isCategory);
80127                        if (defa) {
80128                            base[state] = {};
80129                            base[state][visualType] = defa;
80130                        }
80131                        else {
80132                            // Mark as not specified.
80133                            delete base[state];
80134                        }
80135                    }
80136                }, this);
80137            }
80138
80139            function completeInactive(base, stateExist, stateAbsent) {
80140                var optExist = base[stateExist];
80141                var optAbsent = base[stateAbsent];
80142
80143                if (optExist && !optAbsent) {
80144                    optAbsent = base[stateAbsent] = {};
80145                    each$25(optExist, function (visualData, visualType) {
80146                        if (!VisualMapping.isValidType(visualType)) {
80147                            return;
80148                        }
80149
80150                        var defa = visualDefault.get(visualType, 'inactive', isCategory);
80151
80152                        if (defa != null) {
80153                            optAbsent[visualType] = defa;
80154
80155                            // Compatibable with ec2:
80156                            // Only inactive color to rgba(0,0,0,0) can not
80157                            // make label transparent, so use opacity also.
80158                            if (visualType === 'color'
80159                                && !optAbsent.hasOwnProperty('opacity')
80160                                && !optAbsent.hasOwnProperty('colorAlpha')
80161                            ) {
80162                                optAbsent.opacity = [0, 0];
80163                            }
80164                        }
80165                    });
80166                }
80167            }
80168
80169            function completeController(controller) {
80170                var symbolExists = (controller.inRange || {}).symbol
80171                    || (controller.outOfRange || {}).symbol;
80172                var symbolSizeExists = (controller.inRange || {}).symbolSize
80173                    || (controller.outOfRange || {}).symbolSize;
80174                var inactiveColor = this.get('inactiveColor');
80175
80176                each$25(this.stateList, function (state) {
80177
80178                    var itemSize = this.itemSize;
80179                    var visuals = controller[state];
80180
80181                    // Set inactive color for controller if no other color
80182                    // attr (like colorAlpha) specified.
80183                    if (!visuals) {
80184                        visuals = controller[state] = {
80185                            color: isCategory ? inactiveColor : [inactiveColor]
80186                        };
80187                    }
80188
80189                    // Consistent symbol and symbolSize if not specified.
80190                    if (visuals.symbol == null) {
80191                        visuals.symbol = symbolExists
80192                            && clone(symbolExists)
80193                            || (isCategory ? 'roundRect' : ['roundRect']);
80194                    }
80195                    if (visuals.symbolSize == null) {
80196                        visuals.symbolSize = symbolSizeExists
80197                            && clone(symbolSizeExists)
80198                            || (isCategory ? itemSize[0] : [itemSize[0], itemSize[0]]);
80199                    }
80200
80201                    // Filter square and none.
80202                    visuals.symbol = mapVisual$2(visuals.symbol, function (symbol) {
80203                        return (symbol === 'none' || symbol === 'square') ? 'roundRect' : symbol;
80204                    });
80205
80206                    // Normalize symbolSize
80207                    var symbolSize = visuals.symbolSize;
80208
80209                    if (symbolSize != null) {
80210                        var max = -Infinity;
80211                        // symbolSize can be object when categories defined.
80212                        eachVisual(symbolSize, function (value) {
80213                            value > max && (max = value);
80214                        });
80215                        visuals.symbolSize = mapVisual$2(symbolSize, function (value) {
80216                            return linearMap$2(value, [0, max], [0, itemSize[0]], true);
80217                        });
80218                    }
80219
80220                }, this);
80221            }
80222        },
80223
80224        /**
80225         * @protected
80226         */
80227        resetItemSize: function () {
80228            this.itemSize = [
80229                parseFloat(this.get('itemWidth')),
80230                parseFloat(this.get('itemHeight'))
80231            ];
80232        },
80233
80234        /**
80235         * @public
80236         */
80237        isCategory: function () {
80238            return !!this.option.categories;
80239        },
80240
80241        /**
80242         * @public
80243         * @abstract
80244         */
80245        setSelected: noop$2,
80246
80247        /**
80248         * @public
80249         * @abstract
80250         * @param {*|module:echarts/data/List} valueOrData
80251         * @param {number} dataIndex
80252         * @return {string} state See this.stateList
80253         */
80254        getValueState: noop$2,
80255
80256        /**
80257         * FIXME
80258         * Do not publish to thirt-part-dev temporarily
80259         * util the interface is stable. (Should it return
80260         * a function but not visual meta?)
80261         *
80262         * @pubilc
80263         * @abstract
80264         * @param {Function} getColorVisual
80265         *        params: value, valueState
80266         *        return: color
80267         * @return {Object} visualMeta
80268         *        should includes {stops, outerColors}
80269         *        outerColor means [colorBeyondMinValue, colorBeyondMaxValue]
80270         */
80271        getVisualMeta: noop$2
80272
80273    });
80274
80275    /*
80276* Licensed to the Apache Software Foundation (ASF) under one
80277* or more contributor license agreements.  See the NOTICE file
80278* distributed with this work for additional information
80279* regarding copyright ownership.  The ASF licenses this file
80280* to you under the Apache License, Version 2.0 (the
80281* "License"); you may not use this file except in compliance
80282* with the License.  You may obtain a copy of the License at
80283*
80284*   http://www.apache.org/licenses/LICENSE-2.0
80285*
80286* Unless required by applicable law or agreed to in writing,
80287* software distributed under the License is distributed on an
80288* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
80289* KIND, either express or implied.  See the License for the
80290* specific language governing permissions and limitations
80291* under the License.
80292*/
80293
80294// Constant
80295    var DEFAULT_BAR_BOUND = [20, 140];
80296
80297    var ContinuousModel = VisualMapModel.extend({
80298
80299        type: 'visualMap.continuous',
80300
80301        /**
80302         * @protected
80303         */
80304        defaultOption: {
80305            align: 'auto',          // 'auto', 'left', 'right', 'top', 'bottom'
80306            calculable: false,      // This prop effect default component type determine,
80307                                    // See echarts/component/visualMap/typeDefaulter.
80308            range: null,            // selected range. In default case `range` is [min, max]
80309                                    // and can auto change along with modification of min max,
80310                                    // util use specifid a range.
80311            realtime: true,         // Whether realtime update.
80312            itemHeight: null,       // The length of the range control edge.
80313            itemWidth: null,        // The length of the other side.
80314            hoverLink: true,        // Enable hover highlight.
80315            hoverLinkDataSize: null,// The size of hovered data.
80316            hoverLinkOnHandle: null // Whether trigger hoverLink when hover handle.
80317                                    // If not specified, follow the value of `realtime`.
80318        },
80319
80320        /**
80321         * @override
80322         */
80323        optionUpdated: function (newOption, isInit) {
80324            ContinuousModel.superApply(this, 'optionUpdated', arguments);
80325
80326            this.resetExtent();
80327
80328            this.resetVisual(function (mappingOption) {
80329                mappingOption.mappingMethod = 'linear';
80330                mappingOption.dataExtent = this.getExtent();
80331            });
80332
80333            this._resetRange();
80334        },
80335
80336        /**
80337         * @protected
80338         * @override
80339         */
80340        resetItemSize: function () {
80341            ContinuousModel.superApply(this, 'resetItemSize', arguments);
80342
80343            var itemSize = this.itemSize;
80344
80345            this._orient === 'horizontal' && itemSize.reverse();
80346
80347            (itemSize[0] == null || isNaN(itemSize[0])) && (itemSize[0] = DEFAULT_BAR_BOUND[0]);
80348            (itemSize[1] == null || isNaN(itemSize[1])) && (itemSize[1] = DEFAULT_BAR_BOUND[1]);
80349        },
80350
80351        /**
80352         * @private
80353         */
80354        _resetRange: function () {
80355            var dataExtent = this.getExtent();
80356            var range = this.option.range;
80357
80358            if (!range || range.auto) {
80359                // `range` should always be array (so we dont use other
80360                // value like 'auto') for user-friend. (consider getOption).
80361                dataExtent.auto = 1;
80362                this.option.range = dataExtent;
80363            }
80364            else if (isArray(range)) {
80365                if (range[0] > range[1]) {
80366                    range.reverse();
80367                }
80368                range[0] = Math.max(range[0], dataExtent[0]);
80369                range[1] = Math.min(range[1], dataExtent[1]);
80370            }
80371        },
80372
80373        /**
80374         * @protected
80375         * @override
80376         */
80377        completeVisualOption: function () {
80378            VisualMapModel.prototype.completeVisualOption.apply(this, arguments);
80379
80380            each$1(this.stateList, function (state) {
80381                var symbolSize = this.option.controller[state].symbolSize;
80382                if (symbolSize && symbolSize[0] !== symbolSize[1]) {
80383                    symbolSize[0] = 0; // For good looking.
80384                }
80385            }, this);
80386        },
80387
80388        /**
80389         * @override
80390         */
80391        setSelected: function (selected) {
80392            this.option.range = selected.slice();
80393            this._resetRange();
80394        },
80395
80396        /**
80397         * @public
80398         */
80399        getSelected: function () {
80400            var dataExtent = this.getExtent();
80401
80402            var dataInterval = asc(
80403                (this.get('range') || []).slice()
80404            );
80405
80406            // Clamp
80407            dataInterval[0] > dataExtent[1] && (dataInterval[0] = dataExtent[1]);
80408            dataInterval[1] > dataExtent[1] && (dataInterval[1] = dataExtent[1]);
80409            dataInterval[0] < dataExtent[0] && (dataInterval[0] = dataExtent[0]);
80410            dataInterval[1] < dataExtent[0] && (dataInterval[1] = dataExtent[0]);
80411
80412            return dataInterval;
80413        },
80414
80415        /**
80416         * @override
80417         */
80418        getValueState: function (value) {
80419            var range = this.option.range;
80420            var dataExtent = this.getExtent();
80421
80422            // When range[0] === dataExtent[0], any value larger than dataExtent[0] maps to 'inRange'.
80423            // range[1] is processed likewise.
80424            return (
80425                (range[0] <= dataExtent[0] || range[0] <= value)
80426                && (range[1] >= dataExtent[1] || value <= range[1])
80427            ) ? 'inRange' : 'outOfRange';
80428        },
80429
80430        /**
80431         * @params {Array.<number>} range target value: range[0] <= value && value <= range[1]
80432         * @return {Array.<Object>} [{seriesId, dataIndices: <Array.<number>>}, ...]
80433         */
80434        findTargetDataIndices: function (range) {
80435            var result = [];
80436
80437            this.eachTargetSeries(function (seriesModel) {
80438                var dataIndices = [];
80439                var data = seriesModel.getData();
80440
80441                data.each(this.getDataDimension(data), function (value, dataIndex) {
80442                    range[0] <= value && value <= range[1] && dataIndices.push(dataIndex);
80443                }, this);
80444
80445                result.push({seriesId: seriesModel.id, dataIndex: dataIndices});
80446            }, this);
80447
80448            return result;
80449        },
80450
80451        /**
80452         * @implement
80453         */
vendor: 4,856 bytes, lines 80454-80596
80454        getVisualMeta: function (getColorVisual) {
80455            var oVals = getColorStopValues(this, 'outOfRange', this.getExtent());
80456            var iVals = getColorStopValues(this, 'inRange', this.option.range.slice());
80457            var stops = [];
80458
80459            function setStop(value, valueState) {
80460                stops.push({
80461                    value: value,
80462                    color: getColorVisual(value, valueState)
80463                });
80464            }
80465
80466            // Format to: outOfRange -- inRange -- outOfRange.
80467            var iIdx = 0;
80468            var oIdx = 0;
80469            var iLen = iVals.length;
80470            var oLen = oVals.length;
80471
80472            for (; oIdx < oLen && (!iVals.length || oVals[oIdx] <= iVals[0]); oIdx++) {
80473                // If oVal[oIdx] === iVals[iIdx], oVal[oIdx] should be ignored.
80474                if (oVals[oIdx] < iVals[iIdx]) {
80475                    setStop(oVals[oIdx], 'outOfRange');
80476                }
80477            }
80478            for (var first = 1; iIdx < iLen; iIdx++, first = 0) {
80479                // If range is full, value beyond min, max will be clamped.
80480                // make a singularity
80481                first && stops.length && setStop(iVals[iIdx], 'outOfRange');
80482                setStop(iVals[iIdx], 'inRange');
80483            }
80484            for (var first = 1; oIdx < oLen; oIdx++) {
80485                if (!iVals.length || iVals[iVals.length - 1] < oVals[oIdx]) {
80486                    // make a singularity
80487                    if (first) {
80488                        stops.length && setStop(stops[stops.length - 1].value, 'outOfRange');
80489                        first = 0;
80490                    }
80491                    setStop(oVals[oIdx], 'outOfRange');
80492                }
80493            }
80494
80495            var stopsLen = stops.length;
80496
80497            return {
80498                stops: stops,
80499                outerColors: [
80500                    stopsLen ? stops[0].color : 'transparent',
80501                    stopsLen ? stops[stopsLen - 1].color : 'transparent'
80502                ]
80503            };
80504        }
80505
80506    });
80507
80508    function getColorStopValues(visualMapModel, valueState, dataExtent) {
80509        if (dataExtent[0] === dataExtent[1]) {
80510            return dataExtent.slice();
80511        }
80512
80513        // When using colorHue mapping, it is not linear color any more.
80514        // Moreover, canvas gradient seems not to be accurate linear.
80515        // FIXME
80516        // Should be arbitrary value 100? or based on pixel size?
80517        var count = 200;
80518        var step = (dataExtent[1] - dataExtent[0]) / count;
80519
80520        var value = dataExtent[0];
80521        var stopValues = [];
80522        for (var i = 0; i <= count && value < dataExtent[1]; i++) {
80523            stopValues.push(value);
80524            value += step;
80525        }
80526        stopValues.push(dataExtent[1]);
80527
80528        return stopValues;
80529    }
80530
80531    /*
80532* Licensed to the Apache Software Foundation (ASF) under one
80533* or more contributor license agreements.  See the NOTICE file
80534* distributed with this work for additional information
80535* regarding copyright ownership.  The ASF licenses this file
80536* to you under the Apache License, Version 2.0 (the
80537* "License"); you may not use this file except in compliance
80538* with the License.  You may obtain a copy of the License at
80539*
80540*   http://www.apache.org/licenses/LICENSE-2.0
80541*
80542* Unless required by applicable law or agreed to in writing,
80543* software distributed under the License is distributed on an
80544* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
80545* KIND, either express or implied.  See the License for the
80546* specific language governing permissions and limitations
80547* under the License.
80548*/
80549
80550    var VisualMapView = extendComponentView({
80551
80552        type: 'visualMap',
80553
80554        /**
80555         * @readOnly
80556         * @type {Object}
80557         */
80558        autoPositionValues: {left: 1, right: 1, top: 1, bottom: 1},
80559
80560        init: function (ecModel, api) {
80561            /**
80562             * @readOnly
80563             * @type {module:echarts/model/Global}
80564             */
80565            this.ecModel = ecModel;
80566
80567            /**
80568             * @readOnly
80569             * @type {module:echarts/ExtensionAPI}
80570             */
80571            this.api = api;
80572
80573            /**
80574             * @readOnly
80575             * @type {module:echarts/component/visualMap/visualMapModel}
80576             */
80577            this.visualMapModel;
80578        },
80579
80580        /**
80581         * @protected
80582         */
80583        render: function (visualMapModel, ecModel, api, payload) {
80584            this.visualMapModel = visualMapModel;
80585
80586            if (visualMapModel.get('show') === false) {
80587                this.group.removeAll();
80588                return;
80589            }
80590
80591            this.doRender.apply(this, arguments);
80592        },
80593
80594        /**
80595         * @protected
80596         */
80597        renderBackground: function (group) {
80598            var visualMapModel = this.visualMapModel;
80599            var padding = normalizeCssArray$1(visualMapModel.get('padding') || 0);
80600            var rect = group.getBoundingRect();
80601
80602            group.add(new Rect({
80603                z2: -1, // Lay background rect on the lowest layer.
80604                silent: true,
80605                shape: {
80606                    x: rect.x - padding[3],
80607                    y: rect.y - padding[0],
80608                    width: rect.width + padding[3] + padding[1],
80609                    height: rect.height + padding[0] + padding[2]
80610                },
80611                style: {
80612                    fill: visualMapModel.get('backgroundColor'),
80613                    stroke: visualMapModel.get('borderColor'),
80614                    lineWidth: visualMapModel.get('borderWidth')
80615                }
80616            }));
80617        },
80618
80619        /**
80620         * @protected
80621         * @param {number} targetValue can be Infinity or -Infinity
80622         * @param {string=} visualCluster Only can be 'color' 'opacity' 'symbol' 'symbolSize'
80623         * @param {Object} [opts]
80624         * @param {string=} [opts.forceState] Specify state, instead of using getValueState method.
80625         * @param {string=} [opts.convertOpacityToAlpha=false] For color gradient in controller widget.
80626         * @return {*} Visual value.
80627         */
80628        getControllerVisual: function (targetValue, visualCluster, opts) {
80629            opts = opts || {};
80630
80631            var forceState = opts.forceState;
80632            var visualMapModel = this.visualMapModel;
80633            var visualObj = {};
80634
80635            // Default values.
80636            if (visualCluster === 'symbol') {
80637                visualObj.symbol = visualMapModel.get('itemSymbol');
80638            }
80639            if (visualCluster === 'color') {
80640                var defaultColor = visualMapModel.get('contentColor');
80641                visualObj.color = defaultColor;
80642            }
80643
80644            function getter(key) {
80645                return visualObj[key];
80646            }
80647
80648            function setter(key, value) {
80649                visualObj[key] = value;
80650            }
80651
80652            var mappings = visualMapModel.controllerVisuals[
80653            forceState || visualMapModel.getValueState(targetValue)
80654                ];
80655            var visualTypes = VisualMapping.prepareVisualTypes(mappings);
80656
80657            each$1(visualTypes, function (type) {
80658                var visualMapping = mappings[type];
80659                if (opts.convertOpacityToAlpha && type === 'opacity') {
80660                    type = 'colorAlpha';
80661                    visualMapping = mappings.__alphaForOpacity;
80662                }
80663                if (VisualMapping.dependsOn(type, visualCluster)) {
80664                    visualMapping && visualMapping.applyVisual(
80665                        targetValue, getter, setter
80666                    );
80667                }
80668            });
80669
80670            return visualObj[visualCluster];
80671        },
80672
80673        /**
80674         * @protected
80675         */
80676        positionGroup: function (group) {
80677            var model = this.visualMapModel;
80678            var api = this.api;
80679
80680            positionElement(
80681                group,
80682                model.getBoxLayoutParams(),
80683                {width: api.getWidth(), height: api.getHeight()}
80684            );
80685        },
80686
80687        /**
80688         * @protected
80689         * @abstract
80690         */
80691        doRender: noop
80692
80693    });
80694
80695    /*
80696* Licensed to the Apache Software Foundation (ASF) under one
80697* or more contributor license agreements.  See the NOTICE file
80698* distributed with this work for additional information
80699* regarding copyright ownership.  The ASF licenses this file
80700* to you under the Apache License, Version 2.0 (the
80701* "License"); you may not use this file except in compliance
80702* with the License.  You may obtain a copy of the License at
80703*
80704*   http://www.apache.org/licenses/LICENSE-2.0
80705*
80706* Unless required by applicable law or agreed to in writing,
80707* software distributed under the License is distributed on an
80708* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
80709* KIND, either express or implied.  See the License for the
80710* specific language governing permissions and limitations
80711* under the License.
80712*/
80713
80714    /**
80715     * @param {module:echarts/component/visualMap/VisualMapModel} visualMapModel\
80716     * @param {module:echarts/ExtensionAPI} api
80717     * @param {Array.<number>} itemSize always [short, long]
80718     * @return {string} 'left' or 'right' or 'top' or 'bottom'
80719     */
80720    function getItemAlign(visualMapModel, api, itemSize) {
80721        var modelOption = visualMapModel.option;
80722        var itemAlign = modelOption.align;
80723
80724        if (itemAlign != null && itemAlign !== 'auto') {
80725            return itemAlign;
80726        }
80727
80728        // Auto decision align.
80729        var ecSize = {width: api.getWidth(), height: api.getHeight()};
80730        var realIndex = modelOption.orient === 'horizontal' ? 1 : 0;
80731
80732        var paramsSet = [
80733            ['left', 'right', 'width'],
80734            ['top', 'bottom', 'height']
80735        ];
80736        var reals = paramsSet[realIndex];
80737        var fakeValue = [0, null, 10];
80738
80739        var layoutInput = {};
80740        for (var i = 0; i < 3; i++) {
80741            layoutInput[paramsSet[1 - realIndex][i]] = fakeValue[i];
80742            layoutInput[reals[i]] = i === 2 ? itemSize[0] : modelOption[reals[i]];
80743        }
80744
80745        var rParam = [['x', 'width', 3], ['y', 'height', 0]][realIndex];
80746        var rect = getLayoutRect(layoutInput, ecSize, modelOption.padding);
80747
80748        return reals[
80749            (rect.margin[rParam[2]] || 0) + rect[rParam[0]] + rect[rParam[1]] * 0.5
80750            < ecSize[rParam[1]] * 0.5 ? 0 : 1
80751            ];
80752    }
80753
80754    /**
80755     * Prepare dataIndex for outside usage, where dataIndex means rawIndex, and
80756     * dataIndexInside means filtered index.
80757     */
80758    function convertDataIndex(batch) {
80759        each$1(batch || [], function (batchItem) {
80760            if (batch.dataIndex != null) {
80761                batch.dataIndexInside = batch.dataIndex;
80762                batch.dataIndex = null;
80763            }
80764        });
80765        return batch;
80766    }
80767
80768    /*
80769* Licensed to the Apache Software Foundation (ASF) under one
80770* or more contributor license agreements.  See the NOTICE file
80771* distributed with this work for additional information
80772* regarding copyright ownership.  The ASF licenses this file
80773* to you under the Apache License, Version 2.0 (the
80774* "License");
80774 you may not use this file except in compliance
80775* with the License.  You may obtain a copy of the License at
80776*
80777*   http://www.apache.org/licenses/LICENSE-2.0
80778*
80779* Unless required by applicable law or agreed to in writing,
80780* software distributed under the License is distributed on an
80781* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
80782* KIND, either express or implied.  See the License for the
80783* specific language governing permissions and limitations
80784* under the License.
80785*/
80786
80787    var linearMap$3 = linearMap;
80788    var each$26 = each$1;
80789    var mathMin$7 = Math.min;
80790    var mathMax$7 = Math.max;
80791
80792// Arbitrary value
80793    var HOVER_LINK_SIZE = 12;
80794    var HOVER_LINK_OUT = 6;
80795
80796// Notice:
80797// Any "interval" should be by the order of [low, high].
80798// "handle0" (handleIndex === 0) maps to
80799// low data value: this._dataInterval[0] and has low coord.
80800// "handle1" (handleIndex === 1) maps to
80801// high data value: this._dataInterval[1] and has high coord.
80802// The logic of transform is implemented in this._createBarGroup.
80803
80804    var ContinuousView = VisualMapView.extend({
80805
80806        type: 'visualMap.continuous',
80807
80808        /**
80809         * @override
80810         */
80811        init: function () {
80812
80813            ContinuousView.superApply(this, 'init', arguments);
80814
80815            /**
80816             * @private
80817             */
80818            this._shapes = {};
80819
80820            /**
80821             * @private
80822             */
80823            this._dataInterval = [];
80824
80825            /**
80826             * @private
80827             */
80828            this._handleEnds = [];
80829
80830            /**
80831             * @private
80832             */
80833            this._orient;
80834
80835            /**
80836             * @private
80837             */
80838            this._useHandle;
80839
80840            /**
80841             * @private
80842             */
80843            this._hoverLinkDataIndices = [];
80844
80845            /**
80846             * @private
80847             */
80848            this._dragging;
80849
80850            /**
80851             * @private
80852             */
80853            this._hovering;
80854        },
80855
80856        /**
80857         * @protected
80858         * @override
80859         */
80860        doRender: function (visualMapModel, ecModel, api, payload) {
80861            if (!payload || payload.type !== 'selectDataRange' || payload.from !== this.uid) {
80862                this._buildView();
80863            }
80864        },
80865
80866        /**
80867         * @private
80868         */
80869        _buildView: function () {
80870            this.group.removeAll();
80871
80872            var visualMapModel = this.visualMapModel;
80873            var thisGroup = this.group;
80874
80875            this._orient = visualMapModel.get('orient');
80876            this._useHandle = visualMapModel.get('calculable');
80877
80878            this._resetInterval();
80879
80880            this._renderBar(thisGroup);
80881
80882            var dataRangeText = visualMapModel.get('text');
80883            this._renderEndsText(thisGroup, dataRangeText, 0);
80884            this._renderEndsText(thisGroup, dataRangeText, 1);
80885
80886            // Do this for background size calculation.
80887            this._updateView(true);
80888
80889            // After updating view, inner shapes is built completely,
80890            // and then background can be rendered.
80891            this.renderBackground(thisGroup);
80892
80893            // Real update view
80894            this._updateView();
80895
80896            this._enableHoverLinkToSeries();
80897            this._enableHoverLinkFromSeries();
80898
80899            this.positionGroup(thisGroup);
80900        },
80901
80902        /**
80903         * @private
80904         */
80905        _renderEndsText: function (group, dataRangeText, endsIndex) {
80906            if (!dataRangeText) {
80907                return;
80908            }
80909
80910            // Compatible with ec2, text[0] map to high value, text[1] map low value.
80911            var text = dataRangeText[1 - endsIndex];
80912            text = text != null ? text + '' : '';
80913
80914            var visualMapModel = this.visualMapModel;
80915            var textGap = visualMapModel.get('textGap');
80916            var itemSize = visualMapModel.itemSize;
80917
80918            var barGroup = this._shapes.barGroup;
80919            var position = this._applyTransform(
80920                [
80921                    itemSize[0] / 2,
80922                    endsIndex === 0 ? -textGap : itemSize[1] + textGap
80923                ],
80924                barGroup
80925            );
80926            var align = this._applyTransform(
80927                endsIndex === 0 ? 'bottom' : 'top',
80928                barGroup
80929            );
80930            var orient = this._orient;
80931            var textStyleModel = this.visualMapModel.textStyleModel;
80932
80933            this.group.add(new Text({
80934                style: {
80935                    x: position[0],
80936                    y: position[1],
80937                    textVerticalAlign: orient === 'horizontal' ? 'middle' : align,
80938                    textAlign: orient === 'horizontal' ? align : 'center',
80939                    text: text,
80940                    textFont: textStyleModel.getFont(),
80941                    textFill: textStyleModel.getTextColor()
80942                }
80943            }));
80944        },
80945
80946        /**
80947         * @private
80948         */
80949        _renderBar: function (targetGroup) {
80950            var visualMapModel = this.visualMapModel;
80951            var shapes = this._shapes;
80952            var itemSize = visualMapModel.itemSize;
80953            var orient = this._orient;
80954            var useHandle = this._useHandle;
80955            var itemAlign = getItemAlign(visualMapModel, this.api, itemSize);
80956            var barGroup = shapes.barGroup = this._createBarGroup(itemAlign);
80957
80958            // Bar
80959            barGroup.add(shapes.outOfRange = createPolygon());
80960            barGroup.add(shapes.inRange = createPolygon(
80961                null,
80962                useHandle ? getCursor$1(this._orient) : null,
80963                bind(this._dragHandle, this, 'all', false),
80964                bind(this._dragHandle, this, 'all', true)
80965            ));
80966
80967            var textRect = visualMapModel.textStyleModel.getTextRect
80967('国');
80968            var textSize = mathMax$7(textRect.width, textRect.height);
80969
80970            // Handle
80971            if (useHandle) {
80972                shapes.handleThumbs = [];
80973                shapes.handleLabels = [];
80974                shapes.handleLabelPoints = [];
80975
80976                this._createHandle(barGroup, 0, itemSize, textSize, orient, itemAlign);
80977                this._createHandle(barGroup, 1, itemSize, textSize, orient, itemAlign);
80978            }
80979
80980            this._createIndicator(barGroup, itemSize, textSize, orient);
80981
80982            targetGroup.add(barGroup);
80983        },
80984
80985        /**
80986         * @private
80987         */
80988        _createHandle: function (barGroup, handleIndex, itemSize, textSize, orient) {
80989            var onDrift = bind(this._dragHandle, this, handleIndex, false);
80990            var onDragEnd = bind(this._dragHandle, this, handleIndex, true);
80991            var handleThumb = createPolygon(
80992                createHandlePoints(handleIndex, textSize),
80993                getCursor$1(this._orient),
80994                onDrift,
80995                onDragEnd
80996            );
80997            handleThumb.position[0] = itemSize[0];
80998            barGroup.add(handleThumb);
80999
81000            // Text is always horizontal layout but should not be effected by
81001            // transform (orient/inverse). So label is built separately but not
81002            // use zrender/graphic/helper/RectText, and is located based on view
81003            // group (according to handleLabelPoint) but not barGroup.
81004            var textStyleModel = this.visualMapModel.textStyleModel;
81005            var handleLabel = new Text({
81006                draggable: true,
81007                drift: onDrift,
81008                onmousemove: function (e) {
81009                    // Fot mobile devicem, prevent screen slider on the button.
81010                    stop(e.event);
81011                },
81012                ondragend: onDragEnd,
81013                style: {
81014                    x: 0, y: 0, text: '',
81015                    textFont: textStyleModel.getFont(),
81016                    textFill: textStyleModel.getTextColor()
81017                }
81018            });
81019            this.group.add(handleLabel);
81020
81021            var handleLabelPoint = [
81022                orient === 'horizontal'
81023                    ? textSize / 2
81024                    : textSize * 1.5,
81025                orient === 'horizontal'
81026                    ? (handleIndex === 0 ? -(textSize * 1.5) : (textSize * 1.5))
81027                    : (handleIndex === 0 ? -textSize / 2 : textSize / 2)
81028            ];
81029
81030            var shapes = this._shapes;
81031            shapes.handleThumbs[handleIndex] = handleThumb;
81032            shapes.handleLabelPoints[handleIndex] = handleLabelPoint;
81033            shapes.handleLabels[handleIndex] = handleLabel;
81034        },
81035
81036        /**
81037         * @private
81038         */
81039        _createIndicator: function (barGroup, itemSize, textSize, orient) {
81040            var indicator = createPolygon([[0, 0]], 'move');
81041            indicator.position[0] = itemSize[0];
81042            indicator.attr({invisible: true, silent: true});
81043            barGroup.add(indicator);
81044
81045            var textStyleModel = this.visualMapModel.textStyleModel;
81046            var indicatorLabel = new Text({
81047                silent: true,
81048                invisible: true,
81049                style: {
81050                    x: 0, y: 0, text: '',
81051                    textFont: textStyleModel.getFont(),
81052                    textFill: textStyleModel.getTextColor()
81053                }
81054            });
81055            this.group.add(indicatorLabel);
81056
81057            var indicatorLabelPoint = [
81058                orient === 'horizontal' ? textSize / 2 : HOVER_LINK_OUT + 3,
81059                0
81060            ];
81061
81062            var shapes = this._shapes;
81063            shapes.indicator = indicator;
81064            shapes.indicatorLabel = indicatorLabel;
81065            shapes.indicatorLabelPoint = indicatorLabelPoint;
81066        },
81067
81068        /**
81069         * @private
81070         */
81071        _dragHandle: function (handleIndex, isEnd, dx, dy) {
81072            if (!this._useHandle) {
81073                return;
81074            }
81075
81076            this._dragging = !isEnd;
81077
81078            if (!isEnd) {
81079                // Transform dx, dy to bar coordination.
81080                var vertex = this._applyTransform([dx, dy], this._shapes.barGroup, true);
81081                this._updateInterval(handleIndex, vertex[1]);
81082
81083                // Considering realtime, update view should be executed
81084                // before dispatch action.
81085                this._updateView();
81086            }
81087
81088            // dragEnd do not dispatch action when realtime.
81089            if (isEnd === !this.visualMapModel.get('realtime')) { // jshint ignore:line
81090                this.api.dispatchAction({
81091                    type: 'selectDataRange',
81092                    from: this.uid,
81093                    visualMapId: this.visualMapModel.id,
81094                    selected: this._dataInterval.slice()
81095                });
81096            }
81097
81098            if (isEnd) {
81099                !this._hovering && this._clearHoverLinkToSeries();
81100            }
81101            else if (useHoverLinkOnHandle(this.visualMapModel)) {
81102                this._doHoverLinkToSeries(this._handleEnds[handleIndex], false);
81103            }
81104        },
81105
81106        /**
81107         * @private
81108         */
81109        _resetInterval: function () {
81110            var visualMapModel = this.visualMapModel;
81111
81112            var dataInterval = this._dataInterval = visualMapModel.getSelected();
81113            var dataExtent = visualMapModel.getExtent();
81114            var sizeExtent = [0, visualMapModel.itemSize[1]];
81115
81116            this._handleEnds = [
81117                linearMap$3(dataInterval[0], dataExtent, sizeExtent, true),
81118                linearMap$3(dataInterval[1], dataExtent, sizeExtent, true)
81119            ];
81120        },
81121
81122        /**
81123         * @private
81124         * @param {(number|string)} handleIndex 0 or 1 or 'all'
81125         * @param {number} dx
81126         * @param {number} dy
81127         */
81128        _updateInterval: function (handleIndex, delta) {
81129            delta = delta || 0;
81130            var visualMapModel = this.visualMapModel;
81131            var handleEnds = this._handleEnds;
81132            var sizeExtent = [0, visualMapModel.itemSize[1]];
81133
81134            sliderMove(
81135                delta,
81136                handleEnds,
81137                sizeExtent,
81138                handleIndex,
81139                // cross is forbiden
81140                0
81141            );
81142
81143            var dataExtent = visualMapModel.getExtent();
81144            // Update data interval.
81145            this._dataInterval = [
81146                linearMap$3(handleEnds[0], sizeExtent, dataExtent, true),
81147                linearMap$3(handleEnds[1], sizeExtent, dataExtent, true)
81148            ];
81149        },
81150
81151        /**
81152         * @private
81153         */
81154        _updateView: function (forSketch) {
81155            var visualMapModel = this.visualMapModel;
81156            var dataExtent = visualMapModel.getExtent();
81157            var shapes = this._shapes;
81158
81159            var outOfRangeHandleEnds = [0, visualMapModel.itemSize[1]];
81160            var inRangeHandleEnds = forSketch ? outOfRangeHandleEnds : this._handleEnds;
81161
81162            var visualInRange = this._createBarVisual(
81163                this._dataInterval, dataExtent, inRangeHandleEnds, 'inRange'
81164            );
81165            var visualOutOfRange = this._createBarVisual(
81166                dataExtent, dataExtent, outOfRangeHandleEnds, 'outOfRange'
81167            );
81168
81169            shapes.inRange
81170                .setStyle({
81171                    fill: visualInRange.barColor,
81172                    opacity: visualInRange.opacity
81173                })
81174                .setShape('points', visualInRange.barPoints);
81175            shapes.outOfRange
81176                .setStyle({
81177                    fill: visualOutOfRange.barColor,
81178                    opacity: visualOutOfRange.opacity
81179                })
81180                .setShape('points', visualOutOfRange.barPoints);
81181
81182            this._updateHandle(inRangeHandleEnds, visualInRange);
81183        },
81184
81185        /**
81186         * @private
81187         */
81188        _createBarVisual: function (dataInterval, dataExtent, handleEnds, forceState) {
81189            var opts = {
81190                forceState: forceState,
81191                convertOpacityToAlpha: true
81192            };
81193            var colorStops = this._makeColorGradient(dataInterval, opts);
81194
81195            var symbolSizes = [
81196                this.getControllerVisual(dataInterval[0], 'symbolSize', opts),
81197                this.getControllerVisual(dataInterval[1], 'symbolSize', opts)
81198            ];
81199            var barPoints = this._createBarPoints(handleEnds, symbolSizes);
81200
81201            return {
81202                barColor: new LinearGradient(0, 0, 0, 1, colorStops),
81203                barPoints: barPoints,
81204                handlesColor: [
81205                    colorStops[0].color,
81206                    colorStops[colorStops.length - 1].color
81207                ]
81208            };
81209        },
81210
81211        /**
81212         * @private
81213         */
81214        _makeColorGradient: function (dataInterval, opts) {
81215            // Considering colorHue, which is not linear, so we have to sample
81216            // to calculate gradient color stops, but not only caculate head
81217            // and tail.
81218            var sampleNumber = 100; // Arbitrary value.
81219            var colorStops = [];
81220            var step = (dataInterval[1] - dataInterval[0]) / sampleNumber;
81221
81222            colorStops.push({
81223                color: this.getControllerVisual(dataInterval[0], 'color', opts),
81224                offset: 0
81225            });
81226
81227            for (var i = 1; i < sampleNumber; i++) {
81228                var currValue = dataInterval[0] + step * i;
81229                if (currValue > dataInterval[1]) {
81230                    break;
81231                }
81232                colorStops.push({
81233                    color: this.getControllerVisual(currValue, 'color', opts),
81234                    offset: i / sampleNumber
81235                });
81236            }
81237
81238            colorStops.push({
81239                color: this.getControllerVisual(dataInterval[1], 'color', opts),
81240                offset: 1
81241            });
81242
81243            return colorStops;
81244        },
81245
81246        /**
81247         * @private
81248         */
81249        _createBarPoints: function (handleEnds, symbolSizes) {
81250            var itemSize = this.visualMapModel.itemSize;
81251
81252            return [
81253                [itemSize[0] - symbolSizes[0], handleEnds[0]],
81254                [itemSize[0], handleEnds[0]],
81255                [itemSize[0], handleEnds[1]],
81256                [itemSize[0] - symbolSizes[1], handleEnds[1]]
81257            ];
81258        },
81259
81260        /**
81261         * @private
81262         */
81263        _createBarGroup: function (itemAlign) {
81264            var orient = this._orient;
81265            var inverse = this.visualMapModel.get('inverse');
81266
81267            return new Group(
81268                (orient === 'horizontal' && !inverse)
81269                    ? {scale: itemAlign === 'bottom' ? [1, 1] : [-1, 1], rotation: Math.PI / 2}
81270                    : (orient === 'horizontal' && inverse)
81271                    ? {scale: itemAlign === 'bottom' ? [-1, 1] : [1, 1], rotation: -Math.PI / 2}
81272                    : (orient === 'vertical' && !inverse)
81273                        ? {scale: itemAlign === 'left' ? [1, -1] : [-1, -1]}
81274                        : {scale: itemAlign === 'left' ? [1, 1] : [-1, 1]}
81275            );
81276        },
81277
81278        /**
81279         * @private
81280         */
81281        _updateHandle: function (handleEnds, visualInRange) {
81282            if (!this._useHandle) {
81283                return;
81284            }
81285
81286            var shapes = this._shapes;
81287            var visualMapModel = this.visualMapModel;
81288            var handleThumbs = shapes.handleThumbs;
81289            var handleLabels = shapes.handleLabels;
81290
81291            each$26([0, 1], function (handleIndex) {
81292                var handleThumb = handleThumbs[handleIndex];
81293                handleThumb.setStyle('fill', visualInRange.handlesColor[handleIndex]);
81294                handleThumb.position[1] = handleEnds[handleIndex];
81295
81296                // Update handle label position.
81297                var textPoint = applyTransform$1(
81298                    shapes.handleLabelPoints[handleIndex],
81299                    getTransform(handleThumb, this.group)
81300                );
81301                handleLabels[handleIndex].setStyle({
81302                    x: textPoint[0],
81303                    y: textPoint[1],
81304                    text: visualMapModel.formatValueText(this._dataInterval[handleIndex]),
81305                    textVerticalAlign: 'middle',
81306                    textAlign: this._applyTransform(
81307                        this._orient === 'horizontal'
81308                            ? (handleIndex === 0 ? 'bottom' : 'top')
81309                            : 'left',
81310                        shapes.barGroup
81311                    )
81312                });
81313            }, this);
81314        },
81315
81316        /**
81317         * @private
81318         * @param {number} cursorValue
81319         * @param {number} textValue
81320         * @param {string} [rangeSymbol]
81321         * @param {number} [halfHoverLinkSize]
81322         */
81323        _showIndicator: function (cursorValue, textValue, rangeSymbol, halfHoverLinkSize) {
81324            var visualMapModel = this.visualMapModel;
81325            var dataExtent = visualMapModel.getExtent();
81326            var itemSize = visualMapModel.itemSize;
81327            var sizeExtent = [0, itemSize[1]];
81328            var pos = linearMap$3(cursorValue, dataExtent, sizeExtent, true);
81329
81330            var shapes = this._shapes;
81331            var indicator = shapes.indicator;
81332            if (!indicator) {
81333                return;
81334            }
81335
81336            indicator.position[1] = pos;
81337            indicator.attr('invisible', false);
81338            indicator.setShape('points', createIndicatorPoints(
81339                !!rangeSymbol, halfHoverLinkSize, pos, itemSize[1]
81340            ));
81341
81342            var opts = {convertOpacityToAlpha: true};
81343            var color = this.getControllerVisual(cursorValue, 'color', opts);
81344            indicator.setStyle('fill', color);
81345
81346            // Update handle label position.
81347            var textPoint = applyTransform$1(
81348                shapes.indicatorLabelPoint,
81349                getTransform(indicator, this.group)
81350            );
81351
81352            var indicatorLabel = shapes.indicatorLabel;
81353            indicatorLabel.attr('invisible', false);
81354            var align = this._applyTransform('left', shapes.barGroup);
81355            var orient = this._orient;
81356            indicatorLabel.setStyle({
81357                text: (rangeSymbol ? rangeSymbol : '') + visualMapModel.formatValueText(textValue),
81358                textVerticalAlign: orient === 'horizontal' ? align : 'middle',
81359                textAlign: orient === 'horizontal' ? 'center' : align,
81360                x: textPoint[0],
81361                y: textPoint[1]
81362            });
81363        },
81364
81365        /**
81366         * @private
81367         */
81368        _enableHoverLinkToSeries: function () {
81369            var self = this;
81370            this._shapes.barGroup
81371
81372                .on('mousemove', function (e) {
81373                    self._hovering = true;
81374
81375                    if (!self._dragging) {
81376                        var itemSize = self.visualMapModel.itemSize;
81377                        var pos = self._applyTransform(
81378                            [e.offsetX, e.offsetY], self._shapes.barGroup, true, true
81379                        );
81380                        // For hover link show when hover handle, which might be
81381                        // below or upper than sizeExtent.
81382                        pos[1] = mathMin$7(mathMax$7(0, pos[1]), itemSize[1]);
81383                        self._doHoverLinkToSeries(
81384                            pos[1],
81385                            0 <= pos[0] && pos[0] <= itemSize[0]
81386                        );
81387                    }
81388                })
81389
81390                .on('mouseout', function () {
81391                    // When mouse is out of handle, hoverLink still need
81392                    // to be displayed when realtime is set as false.
81393                    self._hovering = false;
81394                    !self._dragging && self._clearHoverLinkToSeries();
81395                });
81396        },
81397
81398        /**
81399         * @private
81400         */
81401        _enableHoverLinkFromSeries: function () {
81402            var zr = this.api.getZr();
81403
81404            if (this.visualMapModel.option.hoverLink) {
81405                zr.on('mouseover', this._hoverLinkFromSeriesMouseOver, this);
81406                zr.on('mouseout', this._hideIndicator, this);
81407            }
81408            else {
81409                this._clearHoverLinkFromSeries();
81410            }
81411        },
81412
81413        /**
81414         * @private
81415         */
81416        _doHoverLinkToSeries: function (cursorPos, hoverOnBar) {
81417            var visualMapModel = this.visualMapModel;
81418            var itemSize = visualMapModel.itemSize;
81419
81420            if (!visualMapModel.option.hoverLink) {
81421                return;
81422            }
81423
81424            var sizeExtent = [0, itemSize[1]];
81425            var dataExtent = visualMapModel.getExtent();
81426
81427            // For hover link show when hover handle, which might be below or upper than sizeExtent.
81428            cursorPos = mathMin$7(mathMax$7(sizeExtent[0], cursorPos), sizeExtent[1]);
81429
81430            var halfHoverLinkSize = getHalfHoverLinkSize(visualMapModel, dataExtent, sizeExtent);
81431            var hoverRange = [cursorPos - halfHoverLinkSize, cursorPos + halfHoverLinkSize];
81432            var cursorValue = linearMap$3(cursorPos, sizeExtent, dataExtent, true);
81433            var valueRange = [
81434                linearMap$3(hoverRange[0], sizeExtent, dataExtent, true),
81435                linearMap$3(hoverRange[1], sizeExtent, dataExtent, true)
81436            ];
81437            // Consider data range is out of visualMap range, see test/visualMap-continuous.html,
81438            // where china and india has very large population.
81439            hoverRange[0] < sizeExtent[0] && (valueRange[0] = -Infinity);
81440            hoverRange[1] > sizeExtent[1] && (valueRange[1] = Infinity);
81441
81442            // Do not show indicator when mouse is over handle,
81443            // otherwise labels overlap, especially when dragging.
81444            if (hoverOnBar) {
81445                if (valueRange[0] === -Infinity) {
81446                    this._showIndicator(cursorValue, valueRange[1], '< ', halfHoverLinkSize);
81447                }
81448                else if (valueRange[1] === Infinity) {
81449                    this._showIndicator(cursorValue, valueRange[0], '> ', halfHoverLinkSize);
81450                }
81451                else {
81452                    this._showIndicator(cursorValue, cursorValue, '≈ ', halfHoverLinkSize);
81453                }
81454            }
81455
81456            // When realtime is set as false, handles, which are in barGroup,
81457            // also trigger hoverLink, which help user to realize where they
81458            // focus on when dragging. (see test/heatmap-large.html)
81459            // When realtime is set as true, highlight will not show when hover
81460            // handle, because the label on handle, which displays a exact value
81461            // but not range, might mislead users.
81462            var oldBatch = this._hoverLinkDataIndices;
81463            var newBatch = [];
81464            if (hoverOnBar || useHoverLinkOnHandle(visualMapModel)) {
81465                newBatch = this._hoverLinkDataIndices = visualMapModel.findTargetDataIndices(valueRange);
81466            }
81467
81468            var resultBatches = compressBatches(oldBatch, newBatch);
81469
81470            this._dispatchHighDown('downplay', convertDataIndex(resultBatches[0]));
81471            this._dispatchHighDown('highlight', convertDataIndex(resultBatches[1]));
81472        },
81473
81474        /**
81475         * @private
81476         */
81477        _hoverLinkFromSeriesMouseOver: function (e) {
81478            var el = e.target;
81479            var visualMapModel = this.visualMapModel;
81480
81481            if (!el || el.dataIndex == null) {
81482                return;
81483            }
81484
81485            var dataModel = this.ecModel.getSeriesByIndex(el.seriesIndex);
81486
81487            if (!visualMapModel.isTargetSeries(dataModel)) {
81488                return;
81489            }
81490
81491            var data = dataModel.getData(el.dataType);
81492            var value = data.get(visualMapModel.getDataDimension(data), el.dataIndex, true);
81493
81494            if (!isNaN(value)) {
81495                this._showIndicator(value, value);
81496            }
81497        },
81498
81499        /**
81500         * @private
81501         */
81502        _hideIndicator: function () {
81503            var shapes = this._shapes;
81504            shapes.indicator && shapes.indicator.attr('invisible', true);
81505            shapes.indicatorLabel && shapes.indicatorLabel.attr('invisible', true);
81506        },
81507
81508        /**
81509         * @private
81510         */
81511        _clearHoverLinkToSeries: function () {
81512            this._hideIndicator();
81513
81514            var indices = this._hoverLinkDataIndices;
81515            this._dispatchHighDown('downplay', convertDataIndex(indices));
81516
81517            indices.length = 0;
81518        },
81519
81520        /**
81521         * @private
81522         */
81523        _clearHoverLinkFromSeries: function () {
81524            this._hideIndicator();
81525
81526            var zr = this.api.getZr();
81527            zr.off('mouseover', this._hoverLinkFromSeriesMouseOver);
81528            zr.off('mouseout', this._hideIndicator);
81529        },
81530
81531        /**
81532         * @private
81533         */
81534        _applyTransform: function (vertex, element, inverse, global) {
81535            var transform = getTransform(element, global ? null : this.group);
81536
81537            return graphic[
81538                isArray(vertex) ? 'applyTransform' : 'transformDirection'
81539                ](vertex, transform, inverse);
81540        },
81541
81542        /**
81543         * @private
81544         */
81545        _dispatchHighDown: function (type, batch) {
81546            batch && batch.length && this.api.dispatchAction({
81547                type: type,
81548                batch: batch
81549            });
81550        },
81551
81552        /**
81553         * @override
81554         */
81555        dispose: function () {
81556            this._clearHoverLinkFromSeries();
81557            this._clearHoverLinkToSeries();
81558        },
81559
81560        /**
81561         * @override
81562         */
81563        remove: function () {
81564            this._clearHoverLinkFromSeries();
81565            this._clearHoverLinkToSeries();
81566        }
81567
81568    });
81569
81570    function createPolygon(points, cursor, onDrift, onDragEnd) {
81571        return new Polygon({
81572            shape: {points: points},
81573            draggable: !!onDrift,
81574            cursor: cursor,
81575            drift: onDrift,
81576            onmousemove: function (e) {
81577                // Fot mobile devicem, prevent screen slider on the button.
81578                stop(e.event);
81579            },
81580            ondragend: onDragEnd
81581        });
81582    }
81583
81584    function createHandlePoints(handleIndex, textSize) {
81585        return handleIndex === 0
81586            ? [[0, 0], [textSize, 0], [textSize, -textSize]]
81587            : [[0, 0], [textSize, 0], [textSize, textSize]];
81588    }
81589
81590    function createIndicatorPoints(isRange, halfHoverLinkSize, pos, extentMax) {
81591        return isRange
81592            ? [ // indicate range
81593                [0, -mathMin$7(halfHoverLinkSize, mathMax$7(pos, 0))],
81594                [HOVER_LINK_OUT, 0],
81595                [0, mathMin$7(halfHoverLinkSize, mathMax$7(extentMax - pos, 0))]
81596            ]
81597            : [ // indicate single value
81598                [0, 0], [5, -5], [5, 5]
81599            ];
81600    }
81601
81602    function getHalfHoverLinkSize(visualMapModel, dataExtent, sizeExtent) {
81603        var halfHoverLinkSize = HOVER_LINK_SIZE / 2;
81604        var hoverLinkDataSize = visualMapModel.get('hoverLinkDataSize');
81605        if (hoverLinkDataSize) {
81606            halfHoverLinkSize = linearMap$3(hoverLinkDataSize, dataExtent, sizeExtent, true) / 2;
81607        }
81608        return halfHoverLinkSize;
81609    }
81610
81611    function useHoverLinkOnHandle(visualMapModel) {
81612        var hoverLinkOnHandle = visualMapModel.get('hoverLinkOnHandle');
81613        return !!(hoverLinkOnHandle == null ? visualMapModel.get('realtime') : hoverLinkOnHandle);
81614    }
81615
81616    function getCursor$1(orient) {
81617        return orient === 'vertical' ? 'ns-resize' : 'ew-resize';
81618    }
81619
81620    /*
81621* Licensed to the Apache Software Foundation (ASF) under one
81622* or more contributor license agreements.  See the NOTICE file
81623* distributed with this work for additional information
81624* regarding copyright ownership.  The ASF licenses this file
81625* to you under the Apache License, Version 2.0 (the
81626* "License"); you may not use this file except in compliance
81627* with the License.  You may obtain a copy of the License at
81628*
81629*   http://www.apache.org/licenses/LICENSE-2.0
81630*
81631* Unless required by applicable law or agreed to in writing,
81632* software distributed under the License is distributed on an
81633* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
81634* KIND, either express or implied.  See the License for the
81635* specific language governing permissions and limitations
81636* under the License.
81637*/
81638
81639    var actionInfo$2 = {
81640        type: 'selectDataRange',
81641        event: 'dataRangeSelected',
81642        // FIXME use updateView appears wrong
81643        update: 'update'
81644    };
81645
81646    registerAction(actionInfo$2, function (payload, ecModel) {
81647
81648        ecModel.eachComponent({mainType: 'visualMap', query: payload}, function (model) {
81649            model.setSelected(payload.selected);
81650        });
81651
81652    });
81653
81654    /*
81655* Licensed to the Apache Software Foundation (ASF) under one
81656* or more contributor license agreements.  See the NOTICE file
81657* distributed with this work for additional information
81658* regarding copyright ownership.  The ASF licenses this file
81659* to you under the Apache License, Version 2.0 (the
81660* "License"); you may not use this file except in compliance
81661* with the License.  You may obtain a copy of the License at
81662*
81663*   http://www.apache.org/licenses/LICENSE-2.0
81664*
81665* Unless required by applicable law or agreed to in writing,
81666* software distributed under the License is distributed on an
81667* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
81668* KIND, either express or implied.  See the License for the
81669* specific language governing permissions and limitations
81670* under the License.
81671*/
81672
81673    /**
81674     * DataZoom component entry
81675     */
81676
81677    registerPreprocessor(preprocessor$2);
81678
81679    /*
81680* Licensed to the Apache Software Foundation (ASF) under one
81681* or more contributor license agreements.  See the NOTICE file
81682* distributed with this work for additional information
81683* regarding copyright ownership.  The ASF licenses this file
81684* to you under the Apache License, Version 2.0 (the
81685* "License");
81685 you may not use this file except in compliance
81686* with the License.  You may obtain a copy of the License at
81687*
81688*   http://www.apache.org/licenses/LICENSE-2.0
81689*
81690* Unless required by applicable law or agreed to in writing,
81691* software distributed under the License is distributed on an
81692* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
81693* KIND, either express or implied.  See the License for the
81694* specific language governing permissions and limitations
81695* under the License.
81696*/
81697
81698    var PiecewiseModel = VisualMapModel.extend({
81699
81700        type: 'visualMap.piecewise',
81701
81702        /**
81703         * Order Rule:
81704         *
81705         * option.categories / option.pieces / option.text / option.selected:
81706         *     If !option.inverse,
81707         *     Order when vertical: ['top', ..., 'bottom'].
81708         *     Order when horizontal: ['left', ..., 'right'].
81709         *     If option.inverse, the meaning of
81710         *     the order should be reversed.
81711         *
81712         * this._pieceList:
81713         *     The order is always [low, ..., high].
81714         *
81715         * Mapping from location to low-high:
81716         *     If !option.inverse
81717         *     When vertical, top is high.
81718         *     When horizontal, right is high.
81719         *     If option.inverse, reverse.
81720         */
81721
81722        /**
81723         * @protected
81724         */
81725        defaultOption: {
81726            selected: null,             // Object. If not specified, means selected.
81727                                        // When pieces and splitNumber: {'0': true, '5': true}
81728                                        // When categories: {'cate1': false, 'cate3': true}
81729                                        // When selected === false, means all unselected.
81730
81731            minOpen: false,             // Whether include values that smaller than `min`.
81732            maxOpen: false,             // Whether include values that bigger than `max`.
81733
81734            align: 'auto',              // 'auto', 'left', 'right'
81735            itemWidth: 20,              // When put the controller vertically, it is the length of
81736                                        // horizontal side of each item. Otherwise, vertical side.
81737            itemHeight: 14,             // When put the controller vertically, it is the length of
81738                                        // vertical side of each item. Otherwise, horizontal side.
81739            itemSymbol: 'roundRect',
81740            pieceList: null,            // Each item is Object, with some of those attrs:
81741                                        // {min, max, lt, gt, lte, gte, value,
81742                                        // color, colorSaturation, colorAlpha, opacity,
81743                                        // symbol, symbolSize}, which customize the range or visual
81744                                        // coding of the certain piece. Besides, see "Order Rule".
81745            categories: null,           // category names, like: ['some1', 'some2', 'some3'].
81746                                        // Attr min/max are ignored when categories set. See "Order Rule"
81747            splitNumber: 5,             // If set to 5, auto split five pieces equally.
81748                                        // If set to 0 and component type not set, component type will be
81749                                        // determined as "continuous". (It is less reasonable but for ec2
81750                                        // compatibility, see echarts/component/visualMap/typeDefaulter)
81751            selectedMode: 'multiple',   // Can be 'multiple' or 'single'.
81752            itemGap: 10,                // The gap between two items, in px.
81753            hoverLink: true,            // Enable hover highlight.
81754
81755            showLabel: null             // By default, when text is used, label will hide (the logic
81756                                        // is remained for compatibility reason)
81757        },
81758
81759        /**
81760         * @override
81761         */
81762        optionUpdated: function (newOption, isInit) {
81763            PiecewiseModel.superApply(this, 'optionUpdated', arguments);
81764
81765            /**
81766             * The order is always [low, ..., high].
81767             * [{text: string, interval: Array.<number>}, ...]
81768             * @private
81769             * @type {Array.<Object>}
81770             */
81771            this._pieceList = [];
81772
81773            this.resetExtent();
81774
81775            /**
81776             * 'pieces', 'categories', 'splitNumber'
81777             * @type {string}
81778             */
81779            var mode = this._mode = this._determineMode();
81780
81781            resetMethods[this._mode].call(this);
81782
81783            this._resetSelected(newOption, isInit);
81784
81785            var categories = this.option.categories;
81786
81787            this.resetVisual(function (mappingOption, state) {
81788                if (mode === 'categories') {
81789                    mappingOption.mappingMethod = 'category';
81790                    mappingOption.categories = clone(categories);
81791                }
81792                else {
81793                    mappingOption.dataExtent = this.getExtent();
81794                    mappingOption.mappingMethod = 'piecewise';
81795                    mappingOption.pieceList = map(this._pieceList, function (piece) {
81796                        var piece = clone(piece);
81797                        if (state !== 'inRange') {
81798                            // FIXME
81799                            // outOfRange do not support special visual in pieces.
81800                            piece.visual = null;
81801                        }
81802                        return piece;
81803                    });
81804                }
81805            });
81806        },
81807
81808        /**
81809         * @protected
81810         * @override
81811         */
81812        completeVisualOption: function () {
81813            // Consider this case:
81814            // visualMap: {
81815            //      pieces: [{symbol: 'circle', lt: 0}, {symbol: 'rect', gte: 0}]
81816            // }
81817            // where no inRange/outOfRange set but only pieces. So we should make
81818            // default inRange/outOfRange for this case, otherwise visual
81818s that only
81819            // appear in `pieces` will not be taken into account in visual encoding.
81820
81821            var option = this.option;
81822            var visualTypesInPieces = {};
81823            var visualTypes = VisualMapping.listVisualTypes();
81824            var isCategory = this.isCategory();
81825
81826            each$1(option.pieces, function (piece) {
81827                each$1(visualTypes, function (visualType) {
81828                    if (piece.hasOwnProperty(visualType)) {
81829                        visualTypesInPieces[visualType] = 1;
81830                    }
81831                });
81832            });
81833
81834            each$1(visualTypesInPieces, function (v, visualType) {
81835                var exists = 0;
81836                each$1(this.stateList, function (state) {
81837                    exists |= has(option, state, visualType)
81838                        || has(option.target, state, visualType);
81839                }, this);
81840
81841                !exists && each$1(this.stateList, function (state) {
81842                    (option[state] || (option[state] = {}))[visualType] = visualDefault.get(
81843                        visualType, state === 'inRange' ? 'active' : 'inactive', isCategory
81844                    );
81845                });
81846            }, this);
81847
81848            function has(obj, state, visualType) {
81849                return obj && obj[state] && (
81850                    isObject$1(obj[state])
81851                        ? obj[state].hasOwnProperty(visualType)
81852                        : obj[state] === visualType // e.g., inRange: 'symbol'
81853                );
81854            }
81855
81856            VisualMapModel.prototype.completeVisualOption.apply(this, arguments);
81857        },
81858
81859        _resetSelected: function (newOption, isInit) {
81860            var thisOption = this.option;
81861            var pieceList = this._pieceList;
81862
81863            // Selected do not merge but all override.
81864            var selected = (isInit ? thisOption : newOption).selected || {};
81865            thisOption.selected = selected;
81866
81867            // Consider 'not specified' means true.
81868            each$1(pieceList, function (piece, index) {
81869                var key = this.getSelectedMapKey(piece);
81870                if (!selected.hasOwnProperty(key)) {
81871                    selected[key] = true;
81872                }
81873            }, this);
81874
81875            if (thisOption.selectedMode === 'single') {
81876                // Ensure there is only one selected.
81877                var hasSel = false;
81878
81879                each$1(pieceList, function (piece, index) {
81880                    var key = this.getSelectedMapKey(piece);
81881                    if (selected[key]) {
81882                        hasSel
81883                            ? (selected[key] = false)
81884                            : (hasSel = true);
81885                    }
81886                }, this);
81887            }
81888            // thisOption.selectedMode === 'multiple', default: all selected.
81889        },
81890
81891        /**
81892         * @public
81893         */
81894        getSelectedMapKey: function (piece) {
81895            return this._mode === 'categories'
81896                ? piece.value + '' : piece.index + '';
81897        },
81898
81899        /**
81900         * @public
81901         */
81902        getPieceList: function () {
81903            return this._pieceList;
81904        },
81905
81906        /**
81907         * @private
81908         * @return {string}
81909         */
81910        _determineMode: function () {
81911            var option = this.option;
81912
81913            return option.pieces && option.pieces.length > 0
81914                ? 'pieces'
81915                : this.option.categories
81916                    ? 'categories'
81917                    : 'splitNumber';
81918        },
81919
81920        /**
81921         * @public
81922         * @override
81923         */
81924        setSelected: function (selected) {
81925            this.option.selected = clone(selected);
81926        },
81927
81928        /**
81929         * @public
81930         * @override
81931         */
81932        getValueState: function (value) {
81933            var index = VisualMapping.findPieceIndex(value, this._pieceList);
81934
81935            return index != null
81936                ? (this.option.selected[this.getSelectedMapKey(this._pieceList[index])]
81937                        ? 'inRange' : 'outOfRange'
81938                )
81939                : 'outOfRange';
81940        },
81941
81942        /**
81943         * @public
81944         * @params {number} pieceIndex piece index in visualMapModel.getPieceList()
81945         * @return {Array.<Object>} [{seriesId, dataIndices: <Array.<number>>}, ...]
81946         */
81947        findTargetDataIndices: function (pieceIndex) {
81948            var result = [];
81949
81950            this.eachTargetSeries(function (seriesModel) {
81951                var dataIndices = [];
81952                var data = seriesModel.getData();
81953
81954                data.each(this.getDataDimension(data), function (value, dataIndex) {
81955                    // Should always base on model pieceList, because it is order sensitive.
81956                    var pIdx = VisualMapping.findPieceIndex(value, this._pieceList);
81957                    pIdx === pieceIndex && dataIndices.push(dataIndex);
81958                }, this);
81959
81960                result.push({seriesId: seriesModel.id, dataIndex: dataIndices});
81961            }, this);
81962
81963            return result;
81964        },
81965
81966        /**
81967         * @private
81968         * @param {Object} piece piece.value or piece.interval is required.
81969         * @return {number} Can be Infinity or -Infinity
81970         */
81971        getRepresentValue: function (piece) {
81972            var representValue;
81973            if (this.isCategory()) {
81974                representValue = piece.value;
81975            }
81976            else {
81977                if (piece.value != null) {
81978                    representValue = piece.value;
81979                }
81980                else {
81981                    var pieceInterval = piece.interval || [];
81982                    representValue = (pieceInterval[0] === -Infinity && pieceInterval[1] === Infinity)
81983                        ? 0
81984                        : (pieceInterval[0] + pieceInterval[1]) / 2;
81985                }
81986            }
81987            return representValue;
81988        },
81989
81990        getVisualMeta: function (getColorVisual) {
81991            // Do not support category. (category axis is ordinal, numerical)
81992            if (this.isCategory()) {
81993                return;
81994            }
81995
81996            var stops = [];
81997            var outerColors = [];
81998            var visualMapModel = this;
81999
82000            function setStop(interval, valueState) {
82001                var representValue = visualMapModel.getRepresentValue({interval: interval});
82002                if (!valueState) {
82003                    valueState = visualMapModel.getValueState(representValue);
82004                }
82005                var color = getColorVisual(representValue, valueState);
82006                if (interval[0] === -Infinity) {
82007                    outerColors[0] = color;
82008                }
82009                else if (interval[1] === Infinity) {
82010                    outerColors[1] = color;
82011                }
82012                else {
82013                    stops.push(
82014                        {value: interval[0], color: color},
82015                        {value: interval[1], color: color}
82016                    );
82017                }
82018            }
82019
82020            // Suplement
82021            var pieceList = this._pieceList.slice();
82022            if (!pieceList.length) {
82023                pieceList.push({interval: [-Infinity, Infinity]});
82024            }
82025            else {
82026                var edge = pieceList[0].interval[0];
82027                edge !== -Infinity && pieceList.unshift({interval: [-Infinity, edge]});
82028                edge = pieceList[pieceList.length - 1].interval[1];
82029                edge !== Infinity && pieceList.push({interval: [edge, Infinity]});
82030            }
82031
82032            var curr = -Infinity;
82033            each$1(pieceList, function (piece) {
82034                var interval = piece.interval;
82035                if (interval) {
82036                    // Fulfill gap.
82037                    interval[0] > curr && setStop([curr, interval[0]], 'outOfRange');
82038                    setStop(interval.slice());
82039                    curr = interval[1];
82040                }
82041            }, this);
82042
82043            return {stops: stops, outerColors: outerColors};
82044        }
82045
82046    });
82047
82048    /**
82049     * Key is this._mode
82050     * @type {Object}
82051     * @this {module:echarts/component/viusalMap/PiecewiseMode}
82052     */
82053    var resetMethods = {
82054
82055        splitNumber: function () {
82056            var thisOption = this.option;
82057            var pieceList = this._pieceList;
82058            var precision = Math.min(thisOption.precision, 20);
82059            var dataExtent = this.getExtent();
82060            var splitNumber = thisOption.splitNumber;
82061            splitNumber = Math.max(parseInt(splitNumber, 10), 1);
82062            thisOption.splitNumber = splitNumber;
82063
82064            var splitStep = (dataExtent[1] - dataExtent[0]) / splitNumber;
82065            // Precision auto-adaption
82066            while (+splitStep.toFixed(precision) !== splitStep && precision < 5) {
82067                precision++;
82068            }
82069            thisOption.precision = precision;
82070            splitStep = +splitStep.toFixed(precision);
82071
82072            var index = 0;
82073
82074            if (thisOption.minOpen) {
82075                pieceList.push({
82076                    index: index++,
82077                    interval: [-Infinity, dataExtent[0]],
82078                    close: [0, 0]
82079                });
82080            }
82081
82082            for (
82083                var curr = dataExtent[0], len = index + splitNumber;
82084                index < len;
82085                curr += splitStep
82086            ) {
82087                var max = index === splitNumber - 1 ? dataExtent[1] : (curr + splitStep);
82088
82089                pieceList.push({
82090                    index: index++,
82091                    interval: [curr, max],
82092                    close: [1, 1]
82093                });
82094            }
82095
82096            if (thisOption.maxOpen) {
82097                pieceList.push({
82098                    index: index++,
82099                    interval: [dataExtent[1], Infinity],
82100                    close: [0, 0]
82101                });
82102            }
82103
82104            reformIntervals(pieceList);
82105
82106            each$1(pieceList, function (piece) {
82107                piece.text = this.formatValueText(piece.interval);
82108            }, this);
82109        },
82110
82111        categories: function () {
82112            var thisOption = this.option;
82113            each$1(thisOption.categories, function (cate) {
82114                // FIXME category模式也使用pieceList,但在visualMapping中不是使用pieceList。
82115                // 是否改一致。
82116                this._pieceList.push({
82117                    text: this.formatValueText(cate, true),
82118                    value: cate
82119                });
82120            }, this);
82121
82122            // See "Order Rule".
vendor: 5,184 bytes, lines 82123-82254
82123            normalizeReverse(thisOption, this._pieceList);
82124        },
82125
82126        pieces: function () {
82127            var thisOption = this.option;
82128            var pieceList = this._pieceList;
82129
82130            each$1(thisOption.pieces, function (pieceListItem, index) {
82131
82132                if (!isObject$1(pieceListItem)) {
82133                    pieceListItem = {value: pieceListItem};
82134                }
82135
82136                var item = {text: '', index: index};
82137
82138                if (pieceListItem.label != null) {
82139                    item.text = pieceListItem.label;
82140                }
82141
82142                if (pieceListItem.hasOwnProperty('value')) {
82143                    var value = item.value = pieceListItem.value;
82144                    item.interval = [value, value];
82145                    item.close = [1, 1];
82146                }
82147                else {
82148                    // `min` `max` is legacy option.
82149                    // `lt` `gt` `lte` `gte` is recommanded.
82150                    var interval = item.interval = [];
82151                    var close = item.close = [0, 0];
82152
82153                    var closeList = [1, 0, 1];
82154                    var infinityList = [-Infinity, Infinity];
82155
82156                    var useMinMax = [];
82157                    for (var lg = 0; lg < 2; lg++) {
82158                        var names = [['gte', 'gt', 'min'], ['lte', 'lt', 'max']][lg];
82159                        for (var i = 0; i < 3 && interval[lg] == null; i++) {
82160                            interval[lg] = pieceListItem[names[i]];
82161                            close[lg] = closeList[i];
82162                            useMinMax[lg] = i === 2;
82163                        }
82164                        interval[lg] == null && (interval[lg] = infinityList[lg]);
82165                    }
82166                    useMinMax[0] && interval[1] === Infinity && (close[0] = 0);
82167                    useMinMax[1] && interval[0] === -Infinity && (close[1] = 0);
82168
82169                    if (__DEV__) {
82170                        if (interval[0] > interval[1]) {
82171                            console.warn(
82172                                'Piece ' + index + 'is illegal: ' + interval
82173                                + ' lower bound should not greater then uppper bound.'
82174                            );
82175                        }
82176                    }
82177
82178                    if (interval[0] === interval[1] && close[0] && close[1]) {
82179                        // Consider: [{min: 5, max: 5, visual: {...}}, {min: 0, max: 5}],
82180                        // we use value to lift the priority when min === max
82181                        item.value = interval[0];
82182                    }
82183                }
82184
82185                item.visual = VisualMapping.retrieveVisuals(pieceListItem);
82186
82187                pieceList.push(item);
82188
82189            }, this);
82190
82191            // See "Order Rule".
82192            normalizeReverse(thisOption, pieceList);
82193            // Only pieces
82194            reformIntervals(pieceList);
82195
82196            each$1(pieceList, function (piece) {
82197                var close = piece.close;
82198                var edgeSymbols = [['<', '≤'][close[1]], ['>', '≥'][close[0]]];
82199                piece.text = piece.text || this.formatValueText(
82200                    piece.value != null ? piece.value : piece.interval,
82201                    false,
82202                    edgeSymbols
82203                );
82204            }, this);
82205        }
82206    };
82207
82208    function normalizeReverse(thisOption, pieceList) {
82209        var inverse = thisOption.inverse;
82210        if (thisOption.orient === 'vertical' ? !inverse : inverse) {
82211            pieceList.reverse();
82212        }
82213    }
82214
82215    /*
82216* Licensed to the Apache Software Foundation (ASF) under one
82217* or more contributor license agreements.  See the NOTICE file
82218* distributed with this work for additional information
82219* regarding copyright ownership.  The ASF licenses this file
82220* to you under the Apache License, Version 2.0 (the
82221* "License"); you may not use this file except in compliance
82222* with the License.  You may obtain a copy of the License at
82223*
82224*   http://www.apache.org/licenses/LICENSE-2.0
82225*
82226* Unless required by applicable law or agreed to in writing,
82227* software distributed under the License is distributed on an
82228* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
82229* KIND, either express or implied.  See the License for the
82230* specific language governing permissions and limitations
82231* under the License.
82232*/
82233
82234    var PiecewiseVisualMapView = VisualMapView.extend({
82235
82236        type: 'visualMap.piecewise',
82237
82238        /**
82239         * @protected
82240         * @override
82241         */
82242        doRender: function () {
82243            var thisGroup = this.group;
82244
82245            thisGroup.removeAll();
82246
82247            var visualMapModel = this.visualMapModel;
82248            var textGap = visualMapModel.get('textGap');
82249            var textStyleModel = visualMapModel.textStyleModel;
82250            var textFont = textStyleModel.getFont();
82251            var textFill = textStyleModel.getTextColor();
82252            var itemAlign = this._getItemAlign();
82253            var itemSize = visualMapModel.itemSize;
82254            var viewData = this._getViewData();
82255            var endsText = viewData.endsText;
82256            var showLabel = retrieve(visualMapModel.get('showLabel', true), !endsText);
82257
82258            endsText && this._renderEndsText(
82259                thisGroup, endsText[0], itemSize, showLabel, itemAlign
82260            );
82261
82262            each$1(viewData.viewPieceList, renderItem, this);
82263
82264            endsText && this._renderEndsText(
82265                thisGroup, endsText[1], itemSize, showLabel, itemAlign
82266            );
82267
82268            box(
82269                visualMapModel.get('orient'), thisGroup, visualMapModel.get('itemGap')
82270            );
82271
82272            this.renderBackground(thisGroup);
82273
82274            this.positionGroup(thisGroup);
82275
82276            function renderItem(item) {
82277                var piece = item.piece;
82278
82279                var itemGroup = new Group();
82280                itemGroup.onclick = bind(this._onItemClick, this, piece);
82281
82282                this._enableHoverLink(itemGroup, item.indexInModelPieceList);
82283
82284                var representValue = visualMapModel.getRepresentValue(piece);
82285
82286                this._createItemSymbol(
82287                    itemGroup, representValue, [0, 0, itemSize[0], itemSize[1]]
82288                );
82289
82290                if (showLabel) {
82291                    var visualState = this.visualMapModel.getValueState(representValue);
82292
82293                    itemGroup.add(new Text({
82294                        style: {
82295                            x: itemAlign === 'right' ? -textGap : itemSize[0] + textGap,
82296                            y: itemSize[1] / 2,
82297                            text: piece.text,
82298                            textVerticalAlign: 'middle',
82299                            textAlign: itemAlign,
82300                            textFont: textFont,
82301                            textFill: textFill,
82302                            opacity: visualState === 'outOfRange' ? 0.5 : 1
82303                        }
82304                    }));
82305                }
82306
82307                thisGroup.add(itemGroup);
82308            }
82309        },
82310
82311        /**
82312         * @private
82313         */
82314        _enableHoverLink: function (itemGroup, pieceIndex) {
82315            itemGroup
82316                .on('mouseover', bind(onHoverLink, this, 'highlight'))
82317                .on('mouseout', bind(onHoverLink, this, 'downplay'));
82318
82319            function onHoverLink(method) {
82320                var visualMapModel = this.visualMapModel;
82321
82322                visualMapModel.option.hoverLink && this.api.dispatchAction({
82323                    type: method,
82324                    batch: convertDataIndex(
82325                        visualMapModel.findTargetDataIndices(pieceIndex)
82326                    )
82327                });
82328            }
82329        },
82330
82331        /**
82332         * @private
82333         */
82334        _getItemAlign: function () {
82335            var visualMapModel = this.visualMapModel;
82336            var modelOption = visualMapModel.option;
82337
82338            if (modelOption.orient === 'vertical') {
82339                return getItemAlign(
82340                    visualMapModel, this.api, visualMapModel.itemSize
82341                );
82342            }
82343            else { // horizontal, most case left unless specifying right.
82344                var align = modelOption.align;
82345                if (!align || align === 'auto') {
82346                    align = 'left';
82347                }
82348                return align;
82349            }
82350        },
82351
82352        /**
82353         * @private
82354         */
82355        _renderEndsText: function (group, text, itemSize, showLabel, itemAlign) {
82356            if (!text) {
82357                return;
82358            }
82359
82360            var itemGroup = new Group();
82361            var textStyleModel = this.visualMapModel.textStyleModel;
82362
82363            itemGroup.add(new Text({
82364                style: {
82365                    x: showLabel ? (itemAlign === 'right' ? itemSize[0] : 0) : itemSize[0] / 2,
82366                    y: itemSize[1] / 2,
82367                    textVerticalAlign: 'middle',
82368                    textAlign: showLabel ? itemAlign : 'center',
82369                    text: text,
82370                    textFont: textStyleModel.getFont(),
82371                    textFill: textStyleModel.getTextColor()
82372                }
82373            }));
82374
82375            group.add(itemGroup);
82376        },
82377
82378        /**
82379         * @private
82380         * @return {Object} {peiceList, endsText} The order is the same as screen pixel order.
82381         */
82382        _getViewData: function () {
82383            var visualMapModel = this.visualMapModel;
82384
82385            var viewPieceList = map(visualMapModel.getPieceList(), function (piece, index) {
82386                return {piece: piece, indexInModelPieceList: index};
82387            });
82388            var endsText = visualMapModel.get('text');
82389
82390            // Consider orient and inverse.
82391            var orient = visualMapModel.get('orient');
82392            var inverse = visualMapModel.get('inverse');
82393
82394            // Order of model pieceList is always [low, ..., high]
82395            if (orient === 'horizontal' ? inverse : !inverse) {
82396                viewPieceList.reverse();
82397            }
82398            // Origin order of endsText is [high, low]
82399            else if (endsText) {
82400                endsText = endsText.slice().reverse();
82401            }
82402
82403            return {viewPieceList: viewPieceList, endsText: endsText};
82404        },
82405
82406        /**
82407         * @private
82408         */
82409        _createItemSymbol: function (group, representValue, shapeParam) {
82410            group.add(createSymbol(
82411                this.getControllerVisual(representValue, 'symbol'),
82412                shapeParam[0], shapeParam[1], shapeParam[2], shapeParam[3],
82413                this.getControllerVisual(representValue, 'color')
82414            ));
82415        },
82416
82417        /**
82418         * @private
82419         */
82420        _onItemClick: function (piece) {
82421            var visualMapModel = this.visualMapModel;
82422            var option = visualMapModel.option;
82423            var selected = clone(option.selected);
82424            var newKey = visualMapModel.getSelectedMapKey(piece);
82425
82426            if (option.selectedMode === 'single') {
82427                selected[newKey] = true;
82428                each$1(selected, function (o, key) {
82429                    selected[key] = key === newKey;
82430                });
82431            }
82432            else {
82433                selected[newKey] = !selected[newKey];
82434            }
82435
82436            this.api.dispatchAction({
82437                type: 'selectDataRange',
82438                from: this.uid,
82439                visualMapId: this.visualMapModel.id,
82440                selected: selected
82441            });
82442        }
82443    });
82444
82445    /*
82446* Licensed to the Apache Software Foundation (ASF) under one
82447* or more contributor license agreements.  See the NOTICE file
82448* distributed with this work for additional information
82449* regarding copyright ownership.  The ASF licenses this file
82450* to you under the Apache License, Version 2.0 (the
82451* "License"); you may not use this file except in compliance
82452* with the License.  You may obtain a copy of the License at
82453*
82454*   http://www.apache.org/licenses/LICENSE-2.0
82455*
82456* Unless required by applicable law or agreed to in writing,
82457* software distributed under the License is distributed on an
82458* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
82459* KIND, either express or implied.  See the License for the
82460* specific language governing permissions and limitations
82461* under the License.
82462*/
82463
82464    /**
82465     * DataZoom component entry
82466     */
82467
82468    registerPreprocessor(preprocessor$2);
82469
82470    /*
82471* Licensed to the Apache Software Foundation (ASF) under one
82472* or more contributor license agreements.  See the NOTICE file
82473* distributed with this work for additional information
82474* regarding copyright ownership.  The ASF licenses this file
82475* to you under the Apache License, Version 2.0 (the
82476* "License"); you may not use this file except in compliance
82477* with the License.  You may obtain a copy of the License at
82478*
82479*   http://www.apache.org/licenses/LICENSE-2.0
82480*
82481* Unless required by applicable law or agreed to in writing,
82482* software distributed under the License is distributed on an
82483* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
82484* KIND, either express or implied.  See the License for the
82485* specific language governing permissions and limitations
82486* under the License.
82487*/
82488
82489    /**
82490     * visualMap component entry
82491     */
82492
82493    /*
82494* Licensed to the Apache Software Foundation (ASF) under one
82495* or more contributor license agreements.  See the NOTICE file
82496* distributed with this work for additional information
82497* regarding copyright ownership.  The ASF licenses this file
82498* to you under the Apache License, Version 2.0 (the
82499* "License"); you may not use this file except in compliance
82500* with the License.  You may obtain a copy of the License at
82501*
82502*   http://www.apache.org/licenses/LICENSE-2.0
82503*
82504* Unless required by applicable law or agreed to in writing,
82505* software distributed under the License is distributed on an
82506* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
82507* KIND, either express or implied.  See the License for the
82508* specific language governing permissions and limitations
82509* under the License.
82510*/
82511
82512    var addCommas$1 = addCommas;
82513    var encodeHTML$1 = encodeHTML;
82514
82515    function fillLabel(opt) {
82516        defaultEmphasis(opt, 'label', ['show']);
82517    }
82518    var MarkerModel = extendComponentModel({
82519
82520        type: 'marker',
82521
82522        dependencies: ['series', 'grid', 'polar', 'geo'],
82523
82524        /**
82525         * @overrite
82526         */
82527        init: function (option, parentModel, ecModel, extraOpt) {
82528
82529            if (__DEV__) {
82530                if (this.type === 'marker') {
82531                    throw new Error('Marker component is abstract component. Use markLine, markPoint, markArea instead.');
82532                }
82533            }
82534            this.mergeDefaultAndTheme(option, ecModel);
82535            this.mergeOption(option, ecModel, extraOpt.createdBySelf, true);
82536        },
82537
82538        /**
82539         * @return {boolean}
82540         */
82541        isAnimationEnabled: function () {
82542            if (env$1.node) {
82543                return false;
82544            }
82545
82546            var hostSeries = this.__hostSeries;
82547            return this.getShallow('animation') && hostSeries && hostSeries.isAnimationEnabled();
82548        },
82549
82550        mergeOption: function (newOpt, ecModel, createdBySelf, isInit) {
82551            var MarkerModel = this.constructor;
82552            var modelPropName = this.mainType + 'Model';
82553            if (!createdBySelf) {
82554                ecModel.eachSeries(function (seriesModel) {
82555
82556                    var markerOpt = seriesModel.get(this.mainType, true);
82557
82558                    var markerModel = seriesModel[modelPropName];
82559                    if (!markerOpt || !markerOpt.data) {
82560                        seriesModel[modelPropName] = null;
82561                        return;
82562                    }
82563                    if (!markerModel) {
82564                        if (isInit) {
82565                            // Default label emphasis `position` and `show`
82566                            fillLabel(markerOpt);
82567                        }
82568                        each$1(markerOpt.data, function (item) {
82569                            // FIXME Overwrite fillLabel method ?
82570                            if (item instanceof Array) {
82571                                fillLabel(item[0]);
82572                                fillLabel(item[1]);
82573                            }
82574                            else {
82575                                fillLabel(item);
82576                            }
82577                        });
82578
82579                        markerModel = new MarkerModel(
82580                            markerOpt, this, ecModel
82581                        );
82582
82583                        extend(markerModel, {
82584                            mainType: this.mainType,
82585                            // Use the same series index and name
82586                            seriesIndex: seriesModel.seriesIndex,
82587                            name: seriesModel.name,
82588                            createdBySelf: true
82589                        });
82590
82591                        markerModel.__hostSeries = seriesModel;
82592                    }
82593                    else {
82594                        markerModel.mergeOption(markerOpt, ecModel, true);
82595                    }
82596                    seriesModel[modelPropName] = markerModel;
82597                }, this);
82598            }
82599        },
82600
82601        formatTooltip: function (dataIndex) {
82602            var data = this.getData();
82603            var value = this.getRawValue(dataIndex);
82604            var formattedValue = isArray(value)
82605                ? map(value, addCommas$1).join(', ') : addCommas$1(value);
82606            var name = data.getName(dataIndex);
82607            var html = encodeHTML$1(this.name);
82608            if (value != null || name) {
82609                html += '<br />';
82610            }
82611            if (name) {
82612                html += encodeHTML$1(name);
82613                if (value != null) {
82614                    html += ' : ';
82615                }
82616            }
82617            if (value != null) {
82618                html += encodeHTML$1(formattedValue);
82619            }
82620            return html;
82621        },
82622
82623        getData: function () {
82624            return this._data;
82625        },
82626
82627        setData: function (data) {
82628            this._data = data;
82629        }
82630    });
82631
82632    mixin(MarkerModel, dataFormatMixin);
82633
82634    /*
82635* Licensed to the Apache Software Foundation (ASF) under one
82636* or more contributor license agreements.  See the NOTICE file
82637* distributed with this work for additional information
82638* regarding copyright ownership.  The ASF licenses this file
82639* to you under the Apache License, Version 2.0 (the
82640* "License"); you may not use this file except in compliance
82641* with the License.  You may obtain a copy of the License at
82642*
82643*   http://www.apache.org/licenses/LICENSE-2.0
82644*
82645* Unless required by applicable law or agreed to in writing,
82646* software distributed under the License is distributed on an
82647* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
82648* KIND, either express or implied.  See the License for the
82649* specific language governing permissions and limitations
82650* under the License.
82651*/
82652
82653    MarkerModel.extend({
82654
82655        type: 'markPoint',
82656
82657        defaultOption: {
82658            zlevel: 0,
82659            z: 5,
82660            symbol: 'pin',
82661            symbolSize: 50,
82662            //symbolRotate: 0,
82663            //symbolOffset: [0, 0]
82664            tooltip: {
82665                trigger: 'item'
82666            },
82667            label: {
82668                show: true,
82669                position: 'inside'
82670            },
82671            itemStyle: {
82672                borderWidth: 2
82673            },
82674            emphasis: {
82675                label: {
82676                    show: true
82677                }
82678            }
82679        }
82680    });
82681
82682    /*
82683* Licensed to the Apache Software Foundation (ASF) under one
82684* or more contributor license agreements.  See the NOTICE file
82685* distributed with this work for additional information
82686* regarding copyright ownership.  The ASF licenses this file
82687* to you under the Apache License, Version 2.0 (the
82688* "License"); you may not use this file except in compliance
82689* with the License.  You may obtain a copy of the License at
82690*
82691*   http://www.apache.org/licenses/LICENSE-2.0
82692*
82693* Unless required by applicable law or agreed to in writing,
82694* software distributed under the License is distributed on an
82695* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
82696* KIND, either express or implied.  See the License for the
82697* specific language governing permissions and limitations
82698* under the License.
82699*/
82700
82701    var indexOf$2 = indexOf;
82702
82703    function hasXOrY(item) {
82704        return !(isNaN(parseFloat(item.x)) && isNaN(parseFloat(item.y)));
82705    }
82706
82707    function hasXAndY(item) {
82708        return !isNaN(parseFloat(item.x)) && !isNaN(parseFloat(item.y));
82709    }
82710
82711// Make it simple, do not visit all stacked value to count precision.
82712// function getPrecision(data, valueAxisDim, dataIndex) {
82713//     var precision = -1;
82714//     var stackedDim = data.mapDimension(valueAxisDim);
82715//     do {
82716//         precision = Math.max(
82717//             numberUtil.getPrecision(data.get(stackedDim, dataIndex)),
82718//             precision
82719//         );
82720//         var stackedOnSeries = data.getCalculationInfo('stackedOnSeries');
82721//         if (stackedOnSeries) {
82722//             var byValue = data.get(data.getCalculationInfo('stackedByDimension'), dataIndex);
82723//             data = stackedOnSeries.getData();
82724//             dataIndex = data.indexOf(data.getCalculationInfo('stackedByDimension'), byValue);
82725//             stackedDim = data.getCalculationInfo('stackedDimension');
82726//         }
82727//         else {
82728//             data = null;
82729//         }
82730//     } while (data);
82731
82732//     return precision;
82733// }
82734
82735    function markerTypeCalculatorWithExtent(
82736        mlType, data, otherDataDim, targetDataDim, otherCoordIndex, targetCoordIndex
82737    ) {
82738        var coordArr = [];
82739
82740        var stacked = isDimensionStacked(data, targetDataDim /*, otherDataDim*/);
82741        var calcDataDim = stacked
82742            ? data.getCalculationInfo('stackResultDimension')
82743            : targetDataDim;
82744
82745        var value = numCalculate(data, calcDataDim, mlType);
82746
82747        var dataIndex = data.indicesOfNearest(calcDataDim, value)[0];
82748        coordArr[otherCoordIndex] = data.get(otherDataDim, dataIndex);
82749        coordArr[targetCoordIndex] = data.get(targetDataDim, dataIndex);
82750
82751        // Make it simple, do not visit all stacked value to count precision.
82752        var precision = getPrecision(data.get(targetDataDim, dataIndex));
82753        precision = Math.min(precision, 20);
82754        if (precision >= 0) {
82755            coordArr[targetCoordIndex] = +coordArr[targetCoordIndex].toFixed(precision);
82756        }
82757
82758        return coordArr;
82759    }
82760
82761    var curry$7 = curry;
82762// TODO Specified percent
82763    var markerTypeCalculator = {
82764        /**
82765         * @method
82766         * @param {module:echarts/data/List} data
82767         * @param {string} baseAxisDim
82768         * @param {string} valueAxisDim
82769         */
82770        min: curry$7(markerTypeCalculatorWithExtent, 'min'),
82771        /**
82772         * @method
82773         * @param {module:echarts/data/List} data
82774         * @param {string} baseAxisDim
82775         * @param {string} valueAxisDim
82776         */
82777        max: curry$7(markerTypeCalculatorWithExtent, 'max'),
82778
82779        /**
82780         * @method
82781         * @param {module:echarts/data/List} data
82782         * @param {string} baseAxisDim
82783         * @param {string} valueAxisDim
82784         */
82785        average: curry$7(markerTypeCalculatorWithExtent, 'average')
82786    };
82787
82788    /**
82789     * Transform markPoint data item to format used in List by do the following
82790     * 1. Calculate statistic like `max`, `min`, `average`
82791     * 2. Convert `item.xAxis`, `item.yAxis` to `item.coord` array
82792     * @param  {module:echarts/model/Series} seriesModel
82793     * @param  {module:echarts/coord/*} [coordSys]
82794     * @param  {Object} item
82795     * @return {Object}
82796     */
82797    function dataTransform(seriesModel, item) {
82798        var data = seriesModel.getData();
82799        var coordSys = seriesModel.coordinateSystem;
82800
82801        // 1. If not specify the position with pixel directly
82802        // 2. If `coord` is not a data array. Which uses `xAxis`,
82803        // `yAxis` to specify the coord on each dimension
82804
82805        // parseFloat first because item.x and item.y can be percent string like '20%'
82806        if (item && !hasXAndY(item) && !isArray(item.coord) && coordSys) {
82807            var dims = coordSys.dimensions;
82808            var axisInfo = getAxisInfo$1(item, data, coordSys, seriesModel);
82809
82810            // Clone the option
82811            // Transform the properties xAxis, yAxis, radiusAxis, angleAxis, geoCoord to value
82812            item = clone(item);
82813
82814            if (item.type
82815                && markerTypeCalculator[item.type]
82816                && axisInfo.baseAxis && axisInfo.valueAxis
82817            ) {
82818                var otherCoordIndex = indexOf$2(dims, axisInfo.baseAxis.dim);
82819                var targetCoordIndex = indexOf$2(dims, axisInfo.valueAxis.dim);
82820
82821                item.coord = markerTypeCalculator[item.type](
82822                    data, axisInfo.baseDataDim, axisInfo.valueDataDim,
82823                    otherCoordIndex, targetCoordIndex
82824                );
82825                // Force to use the value of calculated value.
82826                item.value = item.coord[targetCoordIndex];
82827            }
82828            else {
82829                // FIXME Only has one of xAxis and yAxis.
82830                var coord = [
82831                    item.xAxis != null ? item.xAxis : item.radiusAxis,
82832                    item.yAxis != null ? item.yAxis : item.angleAxis
82833                ];
82834                // Each coord support max, min, average
82835                for (var i = 0; i < 2; i++) {
82836                    if (markerTypeCalculator[coord[i]]) {
82837                        coord[i] = numCalculate(data, data.mapDimension(dims[i]), coord[i]);
82838                    }
82839                }
82840                item.coord = coord;
82841            }
82842        }
82843        return item;
82844    }
82845
82846    function getAxisInfo$1(item, data, coordSys, seriesModel) {
82847        var ret = {};
82848
82849        if (item.valueIndex != null || item.valueDim != null) {
82850            ret.valueDataDim = item.valueIndex != null
82851                ? data.getDimension(item.valueIndex) : item.valueDim;
82852            ret.valueAxis = coordSys.getAxis(dataDimToCoordDim(seriesModel, ret.valueDataDim));
82853            ret.baseAxis = coordSys.getOtherAxis(ret.valueAxis);
82854            ret.baseDataDim = data.mapDimension(ret.baseAxis.dim);
82855        }
82856        else {
82857            ret.baseAxis = seriesModel.getBaseAxis();
82858            ret.valueAxis = coordSys.getOtherAxis(ret.baseAxis);
82859            ret.baseDataDim = data.mapDimension(ret.baseAxis.dim);
82860            ret.valueDataDim = data.mapDimension(ret.valueAxis.dim);
82861        }
82862
82863        return ret;
82864    }
82865
82866    function dataDimToCoordDim(seriesModel, dataDim) {
82867        var data = seriesModel.getData();
82868        var dimensions = data.dimensions;
82869        dataDim = data.getDimension(dataDim);
82870        for (var i = 0; i < dimensions.length; i++) {
82871            var dimItem = data.getDimensionInfo(dimensions[i]);
82872            if (dimItem.name === dataDim) {
82873                return dimItem.coordDim;
82874            }
82875        }
82876    }
82877
82878    /**
82879     * Filter data which is out of coordinateSystem range
82880     * [dataFilter description]
82881     * @param  {module:echarts/coord/*} [coordSys]
82882     * @param  {Object} item
82883     * @return {boolean}
82884     */
82885    function dataFilter$1(coordSys, item) {
82886        // Alwalys return true if there is no coordSys
82887        return (coordSys && coordSys.containData && item.coord && !hasXOrY(item))
82888            ? coordSys.containData(item.coord) : true;
82889    }
82890
82891    function dimValueGetter(item, dimName, dataIndex, dimIndex) {
82892        // x, y, radius, angle
82893        if (dimIndex < 2) {
82894            return item.coord && item.coord[dimIndex];
82895        }
82896        return item.value;
82897    }
82898
82899    function numCalculate(data, valueDataDim, type) {
82900        if (type === 'average') {
82901            var sum = 0;
82902            var count = 0;
82903            data.each(valueDataDim, function (val, idx) {
82904                if (!isNaN(val)) {
82905                    sum += val;
82906                    count++;
82907                }
82908            });
82909            return sum / count;
82910        }
82911        else if (type === 'median') {
82912            return data.getMedian(valueDataDim);
82913        }
82914        else {
82915            // max & min
82916            return data.getDataExtent(valueDataDim, true)[type === 'max' ? 1 : 0];
82917        }
82918    }
82919
82920    /*
82921* Licensed to the Apache Software Foundation (ASF) under one
82922* or more contributor license agreements.  See the NOTICE file
82923* distributed with this work for additional information
82924* regarding copyright ownership.  The ASF licenses this file
82925* to you under the Apache License, Version 2.0 (the
82926* "License");
82926 you may not use this file except in compliance
82927* with the License.  You may obtain a copy of the License at
82928*
82929*   http://www.apache.org/licenses/LICENSE-2.0
82930*
82931* Unless required by applicable law or agreed to in writing,
82932* software distributed under the License is distributed on an
82933* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
82934* KIND, either express or implied.  See the License for the
82935* specific language governing permissions and limitations
82936* under the License.
82937*/
82938
82939    var MarkerView = extendComponentView({
82940
82941        type: 'marker',
82942
82943        init: function () {
82944            /**
82945             * Markline grouped by series
82946             * @private
82947             * @type {module:zrender/core/util.HashMap}
82948             */
82949            this.markerGroupMap = createHashMap();
82950        },
82951
82952        render: function (markerModel, ecModel, api) {
82953            var markerGroupMap = this.markerGroupMap;
82954            markerGroupMap.each(function (item) {
82955                item.__keep = false;
82956            });
82957
82958            var markerModelKey = this.type + 'Model';
82959            ecModel.eachSeries(function (seriesModel) {
82960                var markerModel = seriesModel[markerModelKey];
82961                markerModel && this.renderSeries(seriesModel, markerModel, ecModel, api);
82962            }, this);
82963
82964            markerGroupMap.each(function (item) {
82965                !item.__keep && this.group.remove(item.group);
82966            }, this);
82967        },
82968
82969        renderSeries: function () {}
82970    });
82971
82972    /*
82973* Licensed to the Apache Software Foundation (ASF) under one
82974* or more contributor license agreements.  See the NOTICE file
82975* distributed with this work for additional information
82976* regarding copyright ownership.  The ASF licenses this file
82977* to you under the Apache License, Version 2.0 (the
82978* "License"); you may not use this file except in compliance
82979* with the License.  You may obtain a copy of the License at
82980*
82981*   http://www.apache.org/licenses/LICENSE-2.0
82982*
82983* Unless required by applicable law or agreed to in writing,
82984* software distributed under the License is distributed on an
82985* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
82986* KIND, either express or implied.  See the License for the
82987* specific language governing permissions and limitations
82988* under the License.
82989*/
82990
82991    function updateMarkerLayout(mpData, seriesModel, api) {
82992        var coordSys = seriesModel.coordinateSystem;
82993        mpData.each(function (idx) {
82994            var itemModel = mpData.getItemModel(idx);
82995            var point;
82996            var xPx = parsePercent$1(itemModel.get('x'), api.getWidth());
82997            var yPx = parsePercent$1(itemModel.get('y'), api.getHeight());
82998            if (!isNaN(xPx) && !isNaN(yPx)) {
82999                point = [xPx, yPx];
83000            }
83001            // Chart like bar may have there own marker positioning logic
83002            else if (seriesModel.getMarkerPosition) {
83003                // Use the getMarkerPoisition
83004                point = seriesModel.getMarkerPosition(
83005                    mpData.getValues(mpData.dimensions, idx)
83006                );
83007            }
83008            else if (coordSys) {
83009                var x = mpData.get(coordSys.dimensions[0], idx);
83010                var y = mpData.get(coordSys.dimensions[1], idx);
83011                point = coordSys.dataToPoint([x, y]);
83012
83013            }
83014
83015            // Use x, y if has any
83016            if (!isNaN(xPx)) {
83017                point[0] = xPx;
83018            }
83019            if (!isNaN(yPx)) {
83020                point[1] = yPx;
83021            }
83022
83023            mpData.setItemLayout(idx, point);
83024        });
83025    }
83026
83027    MarkerView.extend({
83028
83029        type: 'markPoint',
83030
83031        // updateLayout: function (markPointModel, ecModel, api) {
83032        //     ecModel.eachSeries(function (seriesModel) {
83033        //         var mpModel = seriesModel.markPointModel;
83034        //         if (mpModel) {
83035        //             updateMarkerLayout(mpModel.getData(), seriesModel, api);
83036        //             this.markerGroupMap.get(seriesModel.id).updateLayout(mpModel);
83037        //         }
83038        //     }, this);
83039        // },
83040
83041        updateTransform: function (markPointModel, ecModel, api) {
83042            ecModel.eachSeries(function (seriesModel) {
83043                var mpModel = seriesModel.markPointModel;
83044                if (mpModel) {
83045                    updateMarkerLayout(mpModel.getData(), seriesModel, api);
83046                    this.markerGroupMap.get(seriesModel.id).updateLayout(mpModel);
83047                }
83048            }, this);
83049        },
83050
83051        renderSeries: function (seriesModel, mpModel, ecModel, api) {
83052            var coordSys = seriesModel.coordinateSystem;
83053            var seriesId = seriesModel.id;
83054            var seriesData = seriesModel.getData();
83055
83056            var symbolDrawMap = this.markerGroupMap;
83057            var symbolDraw = symbolDrawMap.get(seriesId)
83058                || symbolDrawMap.set(seriesId, new SymbolDraw());
83059
83060            var mpData = createList$1(coordSys, seriesModel, mpModel);
83061
83062            // FIXME
83063            mpModel.setData(mpData);
83064
83065            updateMarkerLayout(mpModel.getData(), seriesModel, api);
83066
83067            mpData.each(function (idx) {
83068                var itemModel = mpData.getItemModel(idx);
83069                var symbolSize = itemModel.getShallow('symbolSize');
83070                if (typeof symbolSize === 'function') {
83071                    // FIXME 这里不兼容 ECharts 2.x,2.x 貌似参数是整个数据?
83072                    symbolSize = symbolSize(
83073                        mpModel.getRawValue(idx), mpModel.getDataParams(idx)
83074                    );
83075                }
83076                mpData.setItemVisual(idx, {
83077                    symbolSize: symbolSize,
83078                    color: itemModel.get('itemStyle.color')
83079                    || seriesData.getVisual('color'),
83080                    symbol: itemModel.getShallow('symbol')
83081                });
83082            });
83083
83084            // TODO Text are wrong
83085            symbolDraw.updateData(mpData);
83086            this.group.add(symbolDraw.group);
83087
83088            // Set host model for tooltip
83089            // FIXME
83090            mpData.eachItemGraphicEl(function (el) {
83091                el.traverse(function (child) {
83092                    child.dataModel = mpModel;
83093                });
83094            });
83095
83096            symbolDraw.__keep = true;
83097
83098            symbolDraw.group.silent = mpModel.get('silent') || seriesModel.get('silent');
83099        }
83100    });
83101
83102    /**
83103     * @inner
83104     * @param {module:echarts/coord/*} [coordSys]
83105     * @param {module:echarts/model/Series} seriesModel
83106     * @param {module:echarts/model/Model} mpModel
83107     */
83108    function createList$1(coordSys, seriesModel, mpModel) {
83109        var coordDimsInfos;
83110        if (coordSys) {
83111            coordDimsInfos = map(coordSys && coordSys.dimensions, function (coordDim) {
83112                var info = seriesModel.getData().getDimensionInfo(
83113                    seriesModel.getData().mapDimension(coordDim)
83114                ) || {};
83115                // In map series data don't have lng and lat dimension. Fallback to same with coordSys
83116                return defaults({name: coordDim}, info);
83117            });
83118        }
83119        else {
83120            coordDimsInfos =[{
83121                name: 'value',
83122                type: 'float'
83123            }];
83124        }
83125
83126        var mpData = new List(coordDimsInfos, mpModel);
83127        var dataOpt = map(mpModel.get('data'), curry(
83128            dataTransform, seriesModel
83129        ));
83130        if (coordSys) {
83131            dataOpt = filter(
83132                dataOpt, curry(dataFilter$1, coordSys)
83133            );
83134        }
83135
83136        mpData.initData(dataOpt, null,
83137            coordSys ? dimValueGetter : function (item) {
83138                return item.value;
83139            }
83140        );
83141
83142        return mpData;
83143    }
83144
83145    /*
83146* Licensed to the Apache Software Foundation (ASF) under one
83147* or more contributor license agreements.  See the NOTICE file
83148* distributed with this work for additional information
83149* regarding copyright ownership.  The ASF licenses this file
83150* to you under the Apache License, Version 2.0 (the
83151* "License"); you may not use this file except in compliance
83152* with the License.  You may obtain a copy of the License at
83153*
83154*   http://www.apache.org/licenses/LICENSE-2.0
83155*
83156* Unless required by applicable law or agreed to in writing,
83157* software distributed under the License is distributed on an
83158* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
83159* KIND, either express or implied.  See the License for the
83160* specific language governing permissions and limitations
83161* under the License.
83162*/
83163
83164// HINT Markpoint can't be used too much
83165    registerPreprocessor(function (opt) {
83166        // Make sure markPoint component is enabled
83167        opt.markPoint = opt.markPoint || {};
83168    });
83169
83170    /*
83171* Licensed to the Apache Software Foundation (ASF) under one
83172* or more contributor license agreements.  See the NOTICE file
83173* distributed with this work for additional information
83174* regarding copyright ownership.  The ASF licenses this file
83175* to you under the Apache License, Version 2.0 (the
83176* "License"); you may not use this file except in compliance
83177* with the License.  You may obtain a copy of the License at
83178*
83179*   http://www.apache.org/licenses/LICENSE-2.0
83180*
83181* Unless required by applicable law or agreed to in writing,
83182* software distributed under the License is distributed on an
83183* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
83184* KIND, either express or implied.  See the License for the
83185* specific language governing permissions and limitations
83186* under the License.
83187*/
83188
83189    MarkerModel.extend({
83190
83191        type: 'markLine',
83192
83193        defaultOption: {
83194            zlevel: 0,
83195            z: 5,
83196
83197            symbol: ['circle', 'arrow'],
83198            symbolSize: [8, 16],
83199
83200            //symbolRotate: 0,
83201
83202            precision: 2,
83203            tooltip: {
83204                trigger: 'item'
83205            },
83206            label: {
83207                show: true,
83208                position: 'end'
83209            },
83210            lineStyle: {
83211                type: 'dashed'
83212            },
83213            emphasis: {
83214                label: {
83215                    show: true
83216                },
83217                lineStyle: {
83218                    width: 3
83219                }
83220            },
83221            animationEasing: 'linear'
83222        }
83223    });
83224
83225    /*
83226* Licensed to the Apache Software Foundation (ASF) under one
83227* or more contributor license agreements.  See the NOTICE file
83228* distributed with this work for additional information
83229* regarding copyright ownership.  The ASF licenses this file
83230* to you under the Apache License, Version 2.0 (the
83231* "License");
83231 you may not use this file except in compliance
83232* with the License.  You may obtain a copy of the License at
83233*
83234*   http://www.apache.org/licenses/LICENSE-2.0
83235*
83236* Unless required by applicable law or agreed to in writing,
83237* software distributed under the License is distributed on an
83238* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
83239* KIND, either express or implied.  See the License for the
83240* specific language governing permissions and limitations
83241* under the License.
83242*/
83243
83244    var markLineTransform = function (seriesModel, coordSys, mlModel, item) {
83245        var data = seriesModel.getData();
83246        // Special type markLine like 'min', 'max', 'average', 'median'
83247        var mlType = item.type;
83248
83249        if (!isArray(item)
83250            && (
83251                mlType === 'min' || mlType === 'max' || mlType === 'average' || mlType === 'median'
83252                // In case
83253                // data: [{
83254                //   yAxis: 10
83255                // }]
83256                || (item.xAxis != null || item.yAxis != null)
83257            )
83258        ) {
83259            var valueAxis;
83260            var valueDataDim;
83261            var value;
83262
83263            if (item.yAxis != null || item.xAxis != null) {
83264                valueDataDim = item.yAxis != null ? 'y' : 'x';
83265                valueAxis = coordSys.getAxis(valueDataDim);
83266
83267                value = retrieve(item.yAxis, item.xAxis);
83268            }
83269            else {
83270                var axisInfo = getAxisInfo$1(item, data, coordSys, seriesModel);
83271                valueDataDim = axisInfo.valueDataDim;
83272                valueAxis = axisInfo.valueAxis;
83273                value = numCalculate(data, valueDataDim, mlType);
83274            }
83275            var valueIndex = valueDataDim === 'x' ? 0 : 1;
83276            var baseIndex = 1 - valueIndex;
83277
83278            var mlFrom = clone(item);
83279            var mlTo = {};
83280
83281            mlFrom.type = null;
83282
83283            mlFrom.coord = [];
83284            mlTo.coord = [];
83285            mlFrom.coord[baseIndex] = -Infinity;
83286            mlTo.coord[baseIndex] = Infinity;
83287
83288            var precision = mlModel.get('precision');
83289            if (precision >= 0 && typeof value === 'number') {
83290                value = +value.toFixed(Math.min(precision, 20));
83291            }
83292
83293            mlFrom.coord[valueIndex] = mlTo.coord[valueIndex] = value;
83294
83295            item = [mlFrom, mlTo, { // Extra option for tooltip and label
83296                type: mlType,
83297                valueIndex: item.valueIndex,
83298                // Force to use the value of calculated value.
83299                value: value
83300            }];
83301        }
83302
83303        item = [
83304            dataTransform(seriesModel, item[0]),
83305            dataTransform(seriesModel, item[1]),
83306            extend({}, item[2])
83307        ];
83308
83309        // Avoid line data type is extended by from(to) data type
83310        item[2].type = item[2].type || '';
83311
83312        // Merge from option and to option into line option
83313        merge(item[2], item[0]);
83314        merge(item[2], item[1]);
83315
83316        return item;
83317    };
83318
83319    function isInifinity(val) {
83320        return !isNaN(val) && !isFinite(val);
83321    }
83322
83323// If a markLine has one dim
83324    function ifMarkLineHasOnlyDim(dimIndex, fromCoord, toCoord, coordSys) {
83325        var otherDimIndex = 1 - dimIndex;
83326        var dimName = coordSys.dimensions[dimIndex];
83327        return isInifinity(fromCoord[otherDimIndex]) && isInifinity(toCoord[otherDimIndex])
83328            && fromCoord[dimIndex] === toCoord[dimIndex] && coordSys.getAxis(dimName).containData(fromCoord[dimIndex]);
83329    }
83330
83331    function markLineFilter(coordSys, item) {
83332        if (coordSys.type === 'cartesian2d') {
83333            var fromCoord = item[0].coord;
83334            var toCoord = item[1].coord;
83335            // In case
83336            // {
83337            //  markLine: {
83338            //    data: [{ yAxis: 2 }]
83339            //  }
83340            // }
83341            if (
83342                fromCoord && toCoord &&
83343                (ifMarkLineHasOnlyDim(1, fromCoord, toCoord, coordSys)
83344                    || ifMarkLineHasOnlyDim(0, fromCoord, toCoord, coordSys))
83345            ) {
83346                return true;
83347            }
83348        }
83349        return dataFilter$1(coordSys, item[0])
83350            && dataFilter$1(coordSys, item[1]);
83351    }
83352
83353    function updateSingleMarkerEndLayout(
83354        data, idx, isFrom, seriesModel, api
83355    ) {
83356        var coordSys = seriesModel.coordinateSystem;
83357        var itemModel = data.getItemModel(idx);
83358
83359        var point;
83360        var xPx = parsePercent$1(itemModel.get('x'), api.getWidth());
83361        var yPx = parsePercent$1(itemModel.get('y'), api.getHeight());
83362        if (!isNaN(xPx) && !isNaN(yPx)) {
83363            point = [xPx, yPx];
83364        }
83365        else {
83366            // Chart like bar may have there own marker positioning logic
83367            if (seriesModel.getMarkerPosition) {
83368                // Use the getMarkerPoisition
83369                point = seriesModel.getMarkerPosition(
83370                    data.getValues(data.dimensions, idx)
83371                );
83372            }
83373            else {
83374                var dims = coordSys.dimensions;
83375                var x = data.get(dims[0], idx);
83376                var y = data.get(dims[1], idx);
83377                point = coordSys.dataToPoint([x, y]);
83378            }
83379            // Expand line to the edge of grid if value on one axis is Inifnity
83380            // In case
83381            //  markLine: {
83382            //    data: [{
83383            //      yAxis: 2
83384            //      // or
83385            //      type: 'average'
83386            //    }]
83387            //  }
83388            if (coordSys.type === 'cartesian2d') {
83389                var xAxis = coordSys.getAxis('x');
83390                var yAxis = coordSys.getAxis('y');
83391                var dims = coordSys.dimensions;
83392                if (isInifinity(data.get(dims[0], idx))) {
83393                    point[0] = xAxis.toGlobalCoord(xAxis.getExtent()[isFrom ? 0 : 1]);
83394                }
83395                else if (isInifinity(data.get(dims[1], idx))) {
83396                    point[1] = yAxis.toGlobalCoord(yAxis.getExtent()[isFrom ? 0 : 1]);
83397                }
83398            }
83399
83400            // Use x, y if has any
83401            if (!isNaN(xPx)) {
83402                point[0] = xPx;
83403            }
83404            if (!isNaN(yPx)) {
83405                point[1] = yPx;
83406            }
83407        }
83408
83409        data.setItemLayout(idx, point);
83410    }
83411
83412    MarkerView.extend({
83413
83414        type: 'markLine',
83415
83416        // updateLayout: function (markLineModel, ecModel, api) {
83417        //     ecModel.eachSeries(function (seriesModel) {
83418        //         var mlModel = seriesModel.markLineModel;
83419        //         if (mlModel) {
83420        //             var mlData = mlModel.getData();
83421        //             var fromData = mlModel.__from;
83422        //             var toData = mlModel.__to;
83423        //             // Update visual and layout of from symbol and to symbol
83424        //             fromData.each(function (idx) {
83425        //                 updateSingleMarkerEndLayout(fromData, idx, true, seriesModel, api);
83426        //                 updateSingleMarkerEndLayout(toData, idx, false, seriesModel, api);
83427        //             });
83428        //             // Update layout of line
83429        //             mlData.each(function (idx) {
83430        //                 mlData.setItemLayout(idx, [
83431        //                     fromData.getItemLayout(idx),
83432        //                     toData.getItemLayout(idx)
83433        //                 ]);
83434        //             });
83435
83436        //             this.markerGroupMap.get(seriesModel.id).updateLayout();
83437
83438        //         }
83439        //     }, this);
83440        // },
83441
83442        updateTransform: function (markLineModel, ecModel, api) {
83443            ecModel.eachSeries(function (seriesModel) {
83444                var mlModel = seriesModel.markLineModel;
83445                if (mlModel) {
83446                    var mlData = mlModel.getData();
83447                    var fromData = mlModel.__from;
83448                    var toData = mlModel.__to;
83449                    // Update visual and layout of from symbol and to symbol
83450                    fromData.each(function (idx) {
83451                        updateSingleMarkerEndLayout(fromData, idx, true, seriesModel, api);
83452                        updateSingleMarkerEndLayout(toData, idx, false, seriesModel, api);
83453                    });
83454                    // Update layout of line
83455                    mlData.each(function (idx) {
83456                        mlData.setItemLayout(idx, [
83457                            fromData.getItemLayout(idx),
83458                            toData.getItemLayout(idx)
83459                        ]);
83460                    });
83461
83462                    this.markerGroupMap.get(seriesModel.id).updateLayout();
83463
83464                }
83465            }, this);
83466        },
83467
83468        renderSeries: function (seriesModel, mlModel, ecModel, api) {
83469            var coordSys = seriesModel.coordinateSystem;
83470            var seriesId = seriesModel.id;
83471            var seriesData = seriesModel.getData();
83472
83473            var lineDrawMap = this.markerGroupMap;
83474            var lineDraw = lineDrawMap.get(seriesId)
83475                || lineDrawMap.set(seriesId, new LineDraw());
83476            this.group.add(lineDraw.group);
83477
83478            var mlData = createList$2(coordSys, seriesModel, mlModel);
83479
83480            var fromData = mlData.from;
83481            var toData = mlData.to;
83482            var lineData = mlData.line;
83483
83484            mlModel.__from = fromData;
83485            mlModel.__to = toData;
83486            // Line data for tooltip and formatter
83487            mlModel.setData(lineData);
83488
83489            var symbolType = mlModel.get('symbol');
83490            var symbolSize = mlModel.get('symbolSize');
83491            if (!isArray(symbolType)) {
83492                symbolType = [symbolType, symbolType];
83493            }
83494            if (typeof symbolSize === 'number') {
83495                symbolSize = [symbolSize, symbolSize];
83496            }
83497
83498            // Update visual and layout of from symbol and to symbol
83499            mlData.from.each(function (idx) {
83500                updateDataVisualAndLayout(fromData, idx, true);
83501                updateDataVisualAndLayout(toData, idx, false);
83502            });
83503
83504            // Update visual and layout of line
83505            lineData.each(function (idx) {
83506                var lineColor = lineData.getItemModel(idx).get('lineStyle.color');
83507                lineData.setItemVisual(idx, {
83508                    color: lineColor || fromData.getItemVisual(idx, 'color')
83509                });
83510                lineData.setItemLayout(idx, [
83511                    fromData.getItemLayout(idx),
83512                    toData.getItemLayout(idx)
83513                ]);
83514
83515                lineData.setItemVisual(idx, {
83516                    'fromSymbolSize': fromData.getItemVisual(idx, 'symbolSize'),
83517                    'fromSymbol': fromData.getItemVisual(idx, 'symbol'),
83518                    'toSymbolSize': toData.getItemVisual(idx, 'symbolSize'),
83519                    'toSymbol': toData.getItemVisual(idx, 'symbol')
83520                });
83521            });
83522
83523            lineDraw.updateData(lineData);
83524
83525            // Set host model for tooltip
83526            // FIXME
83527            mlData.line.eachItemGraphicEl(function (el, idx) {
83528                el.traverse(function (child) {
83529                    child.dataModel = mlModel;
83530                });
83531            });
83532
83533            function updateDataVisualAndLayout(data, idx, isFrom) {
83534                var itemModel = data.getItemModel(idx);
83535
83536                updateSingleMarkerEndLayout(
83537                    data, idx, isFrom, seriesModel, api
83538                );
83539
83540                data.setItemVisual(idx, {
83541                    symbolSize: itemModel.get('symbolSize') || symbolSize[isFrom ? 0 : 1],
83542                    symbol: itemModel.get('symbol', true) || symbolType[isFrom ? 0 : 1],
83543                    color: itemModel.get('itemStyle.color') || seriesData.getVisual('color')
83544                });
83545            }
83546
83547            lineDraw.__keep = true;
83548
83549            lineDraw.group.silent = mlModel.get('silent') || seriesModel.get('silent');
83550        }
83551    });
83552
83553    /**
83554     * @inner
83555     * @param {module:echarts/coord/*} coordSys
83556     * @param {module:echarts/model/Series} seriesModel
83557     * @param {module:echarts/model/Model} mpModel
83558     */
83559    function createList$2(coordSys, seriesModel, mlModel) {
83560
83561        var coordDimsInfos;
83562        if (coordSys) {
83563            coordDimsInfos = map(coordSys && coordSys.dimensions, function (coordDim) {
83564                var info = seriesModel.getData().getDimensionInfo(
83565                    seriesModel.getData().mapDimension(coordDim)
83566                ) || {};
83567                // In map series data don't have lng and lat dimension. Fallback to same with coordSys
83568                return defaults({name: coordDim}, info);
83569            });
83570        }
83571        else {
83572            coordDimsInfos =[{
83573                name: 'value',
83574                type: 'float'
83575            }];
83576        }
83577
83578        var fromData = new List(coordDimsInfos, mlModel);
83579        var toData = new List(coordDimsInfos, mlModel);
83580        // No dimensions
83581        var lineData = new List([], mlModel);
83582
83583        var optData = map(mlModel.get('data'), curry(
83584            markLineTransform, seriesModel, coordSys, mlModel
83585        ));
83586        if (coordSys) {
83587            optData = filter(
83588                optData, curry(markLineFilter, coordSys)
83589            );
83590        }
83591        var dimValueGetter$$1 = coordSys ? dimValueGetter : function (item) {
83592            return item.value;
83593        };
83594        fromData.initData(
83595            map(optData, function (item) { return item[0]; }),
83596            null, dimValueGetter$$1
83597        );
83598        toData.initData(
83599            map(optData, function (item) { return item[1]; }),
83600            null, dimValueGetter$$1
83601        );
83602        lineData.initData(
83603            map(optData, function (item) { return item[2]; })
83604        );
83605        lineData.hasItemOption = true;
83606
83607        return {
83608            from: fromData,
83609            to: toData,
83610            line: lineData
83611        };
83612    }
83613
83614    /*
83615* Licensed to the Apache Software Foundation (ASF) under one
83616* or more contributor license agreements.  See the NOTICE file
83617* distributed with this work for additional information
83618* regarding copyright ownership.  The ASF licenses this file
83619* to you under the Apache License, Version 2.0 (the
83620* "License"); you may not use this file except in compliance
83621* with the License.  You may obtain a copy of the License at
83622*
83623*   http://www.apache.org/licenses/LICENSE-2.0
83624*
83625* Unless required by applicable law or agreed to in writing,
83626* software distributed under the License is distributed on an
83627* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
83628* KIND, either express or implied.  See the License for the
83629* specific language governing permissions and limitations
83630* under the License.
83631*/
83632
83633    registerPreprocessor(function (opt) {
83634        // Make sure markLine component is enabled
83635        opt.markLine = opt.markLine || {};
83636    });
83637
83638    /*
83639* Licensed to the Apache Software Foundation (ASF) under one
83640* or more contributor license agreements.  See the NOTICE file
83641* distributed with this work for additional information
83642* regarding copyright ownership.  The ASF licenses this file
83643* to you under the Apache License, Version 2.0 (the
83644* "License"); you may not use this file except in compliance
83645* with the License.  You may obtain a copy of the License at
83646*
83647*   http://www.apache.org/licenses/LICENSE-2.0
83648*
83649* Unless required by applicable law or agreed to in writing,
83650* software distributed under the License is distributed on an
83651* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
83652* KIND, either express or implied.  See the License for the
83653* specific language governing permissions and limitations
83654* under the License.
83655*/
83656
83657    MarkerModel.extend({
83658
83659        type: 'markArea',
83660
83661        defaultOption: {
83662            zlevel: 0,
83663            // PENDING
83664            z: 1,
83665            tooltip: {
83666                trigger: 'item'
83667            },
83668            // markArea should fixed on the coordinate system
83669            animation: false,
83670            label: {
83671                show: true,
83672                position: 'top'
83673            },
83674            itemStyle: {
83675                // color and borderColor default to use color from series
83676                // color: 'auto'
83677                // borderColor: 'auto'
83678                borderWidth: 0
83679            },
83680
83681            emphasis: {
83682                label: {
83683                    show: true,
83684                    position: 'top'
83685                }
83686            }
83687        }
83688    });
83689
83690    /*
83691* Licensed to the Apache Software Foundation (ASF) under one
83692* or more contributor license agreements.  See the NOTICE file
83693* distributed with this work for additional information
83694* regarding copyright ownership.  The ASF licenses this file
83695* to you under the Apache License, Version 2.0 (the
83696* "License"); you may not use this file except in compliance
83697* with the License.  You may obtain a copy of the License at
83698*
83699*   http://www.apache.org/licenses/LICENSE-2.0
83700*
83701* Unless required by applicable law or agreed to in writing,
83702* software distributed under the License is distributed on an
83703* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
83704* KIND, either express or implied.  See the License for the
83705* specific language governing permissions and limitations
83706* under the License.
83707*/
83708
83709// TODO Better on polar
83710
83711    var markAreaTransform = function (seriesModel, coordSys, maModel, item) {
83712        var lt = dataTransform(seriesModel, item[0]);
83713        var rb = dataTransform(seriesModel, item[1]);
83714        var retrieve$$1 = retrieve;
83715
83716        // FIXME make sure lt is less than rb
83717        var ltCoord = lt.coord;
83718        var rbCoord = rb.coord;
83719        ltCoord[0] = retrieve$$1(ltCoord[0], -Infinity);
83720        ltCoord[1] = retrieve$$1(ltCoord[1], -Infinity);
83721
83722        rbCoord[0] = retrieve$$1(rbCoord[0], Infinity);
83723        rbCoord[1] = retrieve$$1(rbCoord[1], Infinity);
83724
83725        // Merge option into one
83726        var result = mergeAll([{}, lt, rb]);
83727
83728        result.coord = [
83729            lt.coord, rb.coord
83730        ];
83731        result.x0 = lt.x;
83732        result.y0 = lt.y;
83733        result.x1 = rb.x;
83734        result.y1 = rb.y;
83735        return result;
83736    };
83737
83738    function isInifinity$1(val) {
83739        return !isNaN(val) && !isFinite(val);
83740    }
83741
83742// If a markArea has one dim
83743    function ifMarkLineHasOnlyDim$1(dimIndex, fromCoord, toCoord, coordSys) {
83744        var otherDimIndex = 1 - dimIndex;
83745        return isInifinity$1(fromCoord[otherDimIndex]) && isInifinity$1(toCoord[otherDimIndex]);
83746    }
83747
83748    function markAreaFilter(coordSys, item) {
83749        var fromCoord = item.coord[0];
83750        var toCoord = item.coord[1];
83751        if (coordSys.type === 'cartesian2d') {
83752            // In case
83753            // {
83754            //  markArea: {
83755            //    data: [{ yAxis: 2 }]
83756            //  }
83757            // }
83758            if (
83759                fromCoord && toCoord &&
83760                (ifMarkLineHasOnlyDim$1(1, fromCoord, toCoord, coordSys)
83761                    || ifMarkLineHasOnlyDim$1(0, fromCoord, toCoord, coordSys))
83762            ) {
83763                return true;
83764            }
83765        }
83766        return dataFilter$1(coordSys, {
83767                coord: fromCoord,
83768                x: item.x0,
83769                y: item.y0
83770            })
83771            || dataFilter$1(coordSys, {
83772                coord: toCoord,
83773                x: item.x1,
83774                y: item.y1
83775            });
83776    }
83777
83778// dims can be ['x0', 'y0'], ['x1', 'y1'], ['x0', 'y1'], ['x1', 'y0']
83779    function getSingleMarkerEndPoint(data, idx, dims, seriesModel, api) {
83780        var coordSys = seriesModel.coordinateSystem;
83781        var itemModel = data.getItemModel(idx);
83782
83783        var point;
83784        var xPx = parsePercent$1(itemModel.get(dims[0]), api.getWidth());
83785        var yPx = parsePercent$1(itemModel.get(dims[1]), api.getHeight());
83786        if (!isNaN(xPx) && !isNaN(yPx)) {
83787            point = [xPx, yPx];
83788        }
83789        else {
83790            // Chart like bar may have there own marker positioning logic
83791            if (seriesModel.getMarkerPosition) {
83792                // Use the getMarkerPoisition
83793                point = seriesModel.getMarkerPosition(
83794                    data.getValues(dims, idx)
83795                );
83796            }
83797            else {
83798                var x = data.get(dims[0], idx);
83799                var y = data.get(dims[1], idx);
83800                var pt = [x, y];
83801                coordSys.clampData && coordSys.clampData(pt, pt);
83802                point = coordSys.dataToPoint(pt, true);
83803            }
83804            if (coordSys.type === 'cartesian2d') {
83805                var xAxis = coordSys.getAxis('x');
83806                var yAxis = coordSys.getAxis('y');
83807                var x = data.get(dims[0], idx);
83808                var y = data.get(dims[1], idx);
83809                if (isInifinity$1(x)) {
83810                    point[0] = xAxis.toGlobalCoord(xAxis.getExtent()[dims[0] === 'x0' ? 0 : 1]);
83811                }
83812                else if (isInifinity$1(y)) {
83813                    point[1] = yAxis.toGlobalCoord(yAxis.getExtent()[dims[1] === 'y0' ? 0 : 1]);
83814                }
83815            }
83816
83817            // Use x, y if has any
83818            if (!isNaN(xPx)) {
83819                point[0] = xPx;
83820            }
83821            if (!isNaN(yPx)) {
83822                point[1] = yPx;
83823            }
83824        }
83825
83826        return point;
83827    }
83828
83829    var dimPermutations = [['x0', 'y0'], ['x1', 'y0'], ['x1', 'y1'], ['x0', 'y1']];
83830
83831    MarkerView.extend({
83832
83833        type: 'markArea',
83834
83835        // updateLayout: function (markAreaModel, ecModel, api) {
83836        //     ecModel.eachSeries(function (seriesModel) {
83837        //         var maModel = seriesModel.markAreaModel;
83838        //         if (maModel) {
83839        //             var areaData = maModel.getData();
83840        //             areaData.each(function (idx) {
83841        //                 var points = zrUtil.map(dimPermutations, function (dim) {
83842        //                     return getSingleMarkerEndPoint(areaData, idx, dim, seriesModel, api);
83843        //                 });
83844        //                 // Layout
83845        //                 areaData.setItemLayout(idx, points);
83846        //                 var el = areaData.getItemGraphicEl(idx);
83847        //                 el.setShape('points', points);
83848        //             });
83849        //         }
83850        //     }, this);
83851        // },
83852
83853        updateTransform: function (markAreaModel, ecModel, api) {
83854            ecModel.eachSeries(function (seriesModel) {
83855                var maModel = seriesModel.markAreaModel;
83856                if (maModel) {
83857                    var areaData = maModel.getData();
83858                    areaData.each(function (idx) {
83859                        var points = map(dimPermutations, function (dim) {
83860                            return getSingleMarkerEndPoint(areaData, idx, dim, seriesModel, api);
83861                        });
83862                        // Layout
83863                        areaData.setItemLayout(idx, points);
83864                        var el = areaData.getItemGraphicEl(idx);
83865                        el.setShape('points', points);
83866                    });
83867                }
83868            }, this);
83869        },
83870
83871        renderSeries: function (seriesModel, maModel, ecModel, api) {
83872            var coordSys = seriesModel.coordinateSystem;
83873            var seriesId = seriesModel.id;
83874            var seriesData = seriesModel.getData();
83875
83876            var areaGroupMap = this.markerGroupMap;
83877            var polygonGroup = areaGroupMap.get(seriesId)
83878                || areaGroupMap.set(seriesId, {group: new Group()});
83879
83880            this.group.add(polygonGroup.group);
83881            polygonGroup.__keep = true;
83882
83883            var areaData = createList$3(coordSys, seriesModel, maModel);
83884
83885            // Line data for tooltip and formatter
83886            maModel.setData(areaData);
83887
83888            // Update visual and layout of line
83889            areaData.each(function (idx) {
83890                // Layout
83891                areaData.setItemLayout(idx, map(dimPermutations, function (dim) {
83892                    return getSingleMarkerEndPoint(areaData, idx, dim, seriesModel, api);
83893                }));
83894
83895                // Visual
83896                areaData.setItemVisual(idx, {
83897                    color: seriesData.getVisual('color')
83898                });
83899            });
83900
83901
83902            areaData.diff(polygonGroup.__data)
83903                .add(function (idx) {
83904                    var polygon = new Polygon({
83905                        shape: {
83906                            points: areaData.getItemLayout(idx)
83907                        }
83908                    });
83909                    areaData.setItemGraphicEl(idx, polygon);
83910                    polygonGroup.group.add(polygon);
83911                })
83912                .update(function (newIdx, oldIdx) {
83913                    var polygon = polygonGroup.__data.getItemGraphicEl(oldIdx);
83914                    updateProps(polygon, {
83915                        shape: {
83916                            points: areaData.getItemLayout(newIdx)
83917                        }
83918                    }, maModel, newIdx);
83919                    polygonGroup.group.add(polygon);
83920                    areaData.setItemGraphicEl(newIdx, polygon);
83921                })
83922                .remove(function (idx) {
83923                    var polygon = polygonGroup.__data.getItemGraphicEl(idx);
83924                    polygonGroup.group.remove(polygon);
83925                })
83926                .execute();
83927
83928            areaData.eachItemGraphicEl(function (polygon, idx) {
83929                var itemModel = areaData.getItemModel(idx);
83930                var labelModel = itemModel.getModel('label');
83931                var labelHoverModel = itemModel.getModel('emphasis.label');
83932                var color = areaData.getItemVisual(idx, 'color');
83933                polygon.useStyle(
83934                    defaults(
83935                        itemModel.getModel('itemStyle').getItemStyle(),
83936                        {
83937                            fill: modifyAlpha(color, 0.4),
83938                            stroke: color
83939                        }
83940                    )
83941                );
83942
83943                polygon.hoverStyle = itemModel.getModel('emphasis.itemStyle').getItemStyle();
83944
83945                setLabelStyle(
83946                    polygon.style, polygon.hoverStyle, labelModel, labelHoverModel,
83947                    {
83948                        labelFetcher: maModel,
83949                        labelDataIndex: idx,
83950                        defaultText: areaData.getName(idx) || '',
83951                        isRectText: true,
83952                        autoColor: color
83953                    }
83954                );
83955
83956                setHoverStyle(polygon, {});
83957
83958                polygon.dataModel = maModel;
83959            });
83960
83961            polygonGroup.__data = areaData;
83962
83963            polygonGroup.group.silent = maModel.get('silent') || seriesModel.get('silent');
83964        }
83965    });
83966
83967    /**
83968     * @inner
83969     * @param {module:echarts/coord/*} coordSys
83970     * @param {module:echarts/model/Series} seriesModel
83971     * @param {module:echarts/model/Model} mpModel
83972     */
83973    function createList$3(coordSys, seriesModel, maModel) {
83974
83975        var coordDimsInfos;
83976        var areaData;
83977        var dims = ['x0', 'y0', 'x1', 'y1'];
83978        if (coordSys) {
83979            coordDimsInfos = map(coordSys && coordSys.dimensions, function (coordDim) {
83980                var data = seriesModel.getData();
83981                var info = data.getDimensionInfo(
83982                    data.mapDimension(coordDim)
83983                ) || {};
83984                // In map series data don't have lng and lat dimension. Fallback to same with coordSys
83985                return defaults({name: coordDim}, info);
83986            });
83987            areaData = new List(map(dims, function (dim, idx) {
83988                return {
83989                    name: dim,
83990                    type: coordDimsInfos[idx % 2].type
83991                };
83992            }), maModel);
83993        }
83994        else {
83995            coordDimsInfos =[{
83996                name: 'value',
83997                type: 'float'
83998            }];
83999            areaData = new List(coordDimsInfos, maModel);
84000        }
84001
84002        var optData = map(maModel.get('data'), curry(
84003            markAreaTransform, seriesModel, coordSys, maModel
84004        ));
84005        if (coordSys) {
84006            optData = filter(
84007                optData, curry(markAreaFilter, coordSys)
84008            );
84009        }
84010
84011        var dimValueGetter$$1 = coordSys ? function (item, dimName, dataIndex, dimIndex) {
84012            return item.coord[Math.floor(dimIndex / 2)][dimIndex % 2];
84013        } : function (item) {
84014            return item.value;
84015        };
84016        areaData.initData(optData, null, dimValueGetter$$1);
84017        areaData.hasItemOption = true;
84018        return areaData;
84019    }
84020
84021    /*
84022* Licensed to the Apache Software Foundation (ASF) under one
84023* or more contributor license agreements.  See the NOTICE file
84024* distributed with this work for additional information
84025* regarding copyright ownership.  The ASF licenses this file
84026* to you under the Apache License, Version 2.0 (the
84027* "License"); you may not use this file except in compliance
84028* with the License.  You may obtain a copy of the License at
84029*
84030*   http://www.apache.org/licenses/LICENSE-2.0
84031*
84032* Unless required by applicable law or agreed to in writing,
84033* software distributed under the License is distributed on an
84034* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
84035* KIND, either express or implied.  See the License for the
84036* specific language governing permissions and limitations
84037* under the License.
84038*/
84039
84040    registerPreprocessor(function (opt) {
84041        // Make sure markArea component is enabled
84042        opt.markArea = opt.markArea || {};
84043    });
84044
84045    /*
84046* Licensed to the Apache Software Foundation (ASF) under one
84047* or more contributor license agreements.  See the NOTICE file
84048* distributed with this work for additional information
84049* regarding copyright ownership.  The ASF licenses this file
84050* to you under the Apache License, Version 2.0 (the
84051* "License"); you may not use this file except in compliance
84052* with the License.  You may obtain a copy of the License at
84053*
84054*   http://www.apache.org/licenses/LICENSE-2.0
84055*
84056* Unless required by applicable law or agreed to in writing,
84057* software distributed under the License is distributed on an
84058* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
84059* KIND, either express or implied.  See the License for the
84060* specific language governing permissions and limitations
84061* under the License.
84062*/
84063
84064    var preprocessor$3 = function (option) {
84065        var timelineOpt = option && option.timeline;
84066
84067        if (!isArray(timelineOpt)) {
84068            timelineOpt = timelineOpt ? [timelineOpt] : [];
84069        }
84070
84071        each$1(timelineOpt, function (opt) {
84072            if (!opt) {
84073                return;
84074            }
84075
84076            compatibleEC2(opt);
84077        });
84078    };
84079
84080    function compatibleEC2(opt) {
84081        var type = opt.type;
84082
84083        var ec2Types = {'number': 'value', 'time': 'time'};
84084
84085        // Compatible with ec2
84086        if (ec2Types[type]) {
84087            opt.axisType = ec2Types[type];
84088            delete opt.type;
84089        }
84090
84091        transferItem(opt);
84092
84093        if (has$2(opt, 'controlPosition')) {
84094            var controlStyle = opt.controlStyle || (opt.controlStyle = {});
84095            if (!has$2(controlStyle, 'position')) {
84096                controlStyle.position = opt.controlPosition;
84097            }
84098            if (controlStyle.position === 'none' && !has$2(controlStyle, 'show')) {
84099                controlStyle.show = false;
84100                delete controlStyle.position;
84101            }
84102            delete opt.controlPosition;
84103        }
84104
84105        each$1(opt.data || [], function (dataItem) {
84106            if (isObject$1(dataItem) && !isArray(dataItem)) {
84107                if (!has$2(dataItem, 'value') && has$2(dataItem, 'name')) {
84108                    // In ec2, using name as value.
84109                    dataItem.value = dataItem.name;
84110                }
84111                transferItem(dataItem);
84112            }
84113        });
84114    }
84115
84116    function transferItem(opt) {
84117        var itemStyle = opt.itemStyle || (opt.itemStyle = {});
84118
84119        var itemStyleEmphasis = itemStyle.emphasis || (itemStyle.emphasis = {});
84120
84121        // Transfer label out
84122        var label = opt.label || (opt.label || {});
84123        var labelNormal = label.normal || (label.normal = {});
84124        var excludeLabelAttr = {normal: 1, emphasis: 1};
84125
84126        each$1(label, function (value, name) {
84127            if (!excludeLabelAttr[name] && !has$2(labelNormal, name)) {
84128                labelNormal[name] = value;
84129            }
84130        });
84131
84132        if (itemStyleEmphasis.label && !has$2(label, 'emphasis')) {
84133            label.emphasis = itemStyleEmphasis.label;
84134            delete itemStyleEmphasis.label;
84135        }
84136    }
84137
84138    function has$2(obj, attr) {
84139        return obj.hasOwnProperty(attr);
84140    }
84141
84142    /*
84143* Licensed to the Apache Software Foundation (ASF) under one
84144* or more contributor license agreements.  See the NOTICE file
84145* distributed with this work for additional information
84146* regarding copyright ownership.  The ASF licenses this file
84147* to you under the Apache License, Version 2.0 (the
84148* "License"); you may not use this file except in compliance
84149* with the License.  You may obtain a copy of the License at
84150*
84151*   http://www.apache.org/licenses/LICENSE-2.0
84152*
84153* Unless required by applicable law or agreed to in writing,
84154* software distributed under the License is distributed on an
84155* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
84156* KIND, either express or implied.  See the License for the
84157* specific language governing permissions and limitations
84158* under the License.
84159*/
84160
84161    ComponentModel.registerSubTypeDefaulter('timeline', function () {
84162        // Only slider now.
84163        return 'slider';
84164    });
84165
84166    /*
84167* Licensed to the Apache Software Foundation (ASF) under one
84168* or more contributor license agreements.  See the NOTICE file
84169* distributed with this work for additional information
84170* regarding copyright ownership.  The ASF licenses this file
84171* to you under the Apache License, Version 2.0 (the
84172* "License"); you may not use this file except in compliance
84173* with the License.  You may obtain a copy of the License at
84174*
84175*   http://www.apache.org/licenses/LICENSE-2.0
84176*
84177* Unless required by applicable law or agreed to in writing,
84178* software distributed under the License is distributed on an
84179* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
84180* KIND, either express or implied.  See the License for the
84181* specific language governing permissions and limitations
84182* under the License.
84183*/
84184
84185    registerAction(
84186
84187        {type: 'timelineChange', event: 'timelineChanged', update: 'prepareAndUpdate'},
84188
84189        function (payload, ecModel) {
84190
84191            var timelineModel = ecModel.getComponent('timeline');
84192            if (timelineModel && payload.currentIndex != null) {
84193                timelineModel.setCurrentIndex(payload.currentIndex);
84194
84195                if (!timelineModel.get('loop', true) && timelineModel.isIndexMax()) {
84196                    timelineModel.setPlayState(false);
84197                }
84198            }
84199
84200            // Set normalized currentIndex to payload.
84201            ecModel.resetOption('timeline');
84202
84203            return defaults({
84204                currentIndex: timelineModel.option.currentIndex
84205            }, payload);
84206        }
84207    );
84208
84209    registerAction(
84210
84211        {type: 'timelinePlayChange', event: 'timelinePlayChanged', update: 'update'},
84212
84213        function (payload, ecModel) {
84214            var timelineModel = ecModel.getComponent('timeline');
84215            if (timelineModel && payload.playState != null) {
84216                timelineModel.setPlayState(payload.playState);
84217            }
84218        }
84219    );
84220
84221    /*
84222* Licensed to the Apache Software Foundation (ASF) under one
84223* or more contributor license agreements.  See the NOTICE file
84224* distributed with this work for additional information
84225* regarding copyright ownership.  The ASF licenses this file
84226* to you under the Apache License, Version 2.0 (the
84227* "License"); you may not use this file except in compliance
84228* with the License.  You may obtain a copy of the License at
84229*
84230*   http://www.apache.org/licenses/LICENSE-2.0
84231*
84232* Unless required by applicable law or agreed to in writing,
84233* software distributed under the License is distributed on an
84234* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
84235* KIND, either express or implied.  See the License for the
84236* specific language governing permissions and limitations
84237* under the License.
84238*/
84239
84240    var TimelineModel = ComponentModel.extend({
84241
84242        type: 'timeline',
84243
84244        layoutMode: 'box',
84245
84246        /**
84247         * @protected
84248         */
84249        defaultOption: {
84250
84251            zlevel: 0,                  // 一级层å
84252            z: 4,                       // 二级层å
84253            show: true,
84254
84255            axisType: 'time',  // 模式是时间类型,支持 value, category
84256
84257            realtime: true,
84258
84259            left: '20%',
84260            top: null,
84261            right: '20%',
84262            bottom: 0,
84263            width: null,
84264            height: 40,
84265            padding: 5,
84266
84267            controlPosition: 'left',           // 'left' 'right' 'top' 'bottom' 'none'
84268            autoPlay: false,
84269            rewind: false,                     // 反向播放
84270            loop: true,
84271            playInterval: 2000,                // 播放时间间隔,单位ms
84272
84273            currentIndex: 0,
84274
84275            itemStyle: {},
84276            label: {
84277                color: '#000'
84278            },
84279
84280            data: []
84281        },
84282
84283        /**
84284         * @override
84285         */
84286        init: function (option, parentModel, ecModel) {
84287
84288            /**
84289             * @private
84290             * @type {module:echarts/data/List}
84291             */
84292            this._data;
84293
84294            /**
84295             * @private
84296             * @type {Array.<string>}
84297             */
84298            this._names;
84299
84300            this.mergeDefaultAndTheme(option, ecModel);
84301            this._initData();
84302        },
84303
84304        /**
84305         * @override
84306         */
84307        mergeOption: function (option) {
84308            TimelineModel.superApply(this, 'mergeOption', arguments);
84309            this._initData();
84310        },
84311
84312        /**
84313         * @param {number} [currentIndex]
84314         */
84315        setCurrentIndex: function (currentIndex) {
84316            if (currentIndex == null) {
84317                currentIndex = this.option.currentIndex;
84318            }
84319            var count = this._data.count();
84320
84321            if (this.option.loop) {
84322                currentIndex = (currentIndex % count + count) % count;
84323            }
84324            else {
84325                currentIndex >= count && (currentIndex = count - 1);
84326                currentIndex < 0 && (currentIndex = 0);
84327            }
84328
84329            this.option.currentIndex = currentIndex;
84330        },
84331
84332        /**
84333         * @return {number} currentIndex
84334         */
84335        getCurrentIndex: function () {
84336            return this.option.currentIndex;
84337        },
84338
84339        /**
84340         * @return {boolean}
84341         */
84342        isIndexMax: function () {
84343            return this.getCurrentIndex() >= this._data.count() - 1;
84344        },
84345
84346        /**
84347         * @param {boolean} state true: play, false: stop
84348         */
84349        setPlayState: function (state) {
84350            this.option.autoPlay = !!state;
84351        },
84352
84353        /**
84354         * @return {boolean} true: play, false: stop
84355         */
84356        getPlayState: function () {
84357            return !!this.option.autoPlay;
84358        },
84359
84360        /**
84361         * @private
84362         */
84363        _initData: function () {
84364            var thisOption = this.option;
84365            var dataArr = thisOption.data || [];
84366            var axisType = thisOption.axisType;
84367            var names = this._names = [];
84368
84369            if (axisType === 'category') {
84370                var idxArr = [];
84371                each$1(dataArr, function (item, index) {
84372                    var value = getDataItemValue(item);
84373                    var newItem;
84374
84375                    if (isObject$1(item)) {
84376                        newItem = clone(item);
84377                        newItem.value = index;
84378                    }
84379                    else {
84380                        newItem = index;
84381                    }
84382
84383                    idxArr.push(newItem);
84384
84385                    if (!isString(value) && (value == null || isNaN(value))) {
84386                        value = '';
84387                    }
84388
84389                    names.push(value + '');
84390                });
84391                dataArr = idxArr;
84392            }
84393
84394            var dimType = ({category: 'ordinal', time: 'time'})[axisType] || 'number';
84395
84396            var data = this._data = new List([{name: 'value', type: dimType}], this);
84397
84398            data.initData(dataArr, names);
84399        },
84400
84401        getData: function () {
84402            return this._data;
84403        },
84404
84405        /**
84406         * @public
84407         * @return {Array.<string>} categoreis
84408         */
84409        getCategories: function () {
84410            if (this.get('axisType') === 'category') {
84411                return this._names.slice();
84412            }
84413        }
84414
84415    });
84416
84417    /*
84418* Licensed to the Apache Software Foundation (ASF) under one
84419* or more contributor license agreements.  See the NOTICE file
84420* distributed with this work for additional information
84421* regarding copyright ownership.  The ASF licenses this file
84422* to you under the Apache License, Version 2.0 (the
84423* "License"); you may not use this file except in compliance
84424* with the License.  You may obtain a copy of the License at
84425*
84426*   http://www.apache.org/licenses/LICENSE-2.0
84427*
84428* Unless required by applicable law or agreed to in writing,
84429* software distributed under the License is distributed on an
84430* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
84431* KIND, either express or implied.  See the License for the
84432* specific language governing permissions and limitations
84433* under the License.
84434*/
84435
84436    var SliderTimelineModel = TimelineModel.extend({
84437
84438        type: 'timeline.slider',
84439
84440        /**
84441         * @protected
84442         */
84443        defaultOption: {
84444
84445            backgroundColor: 'rgba(0,0,0,0)',   // 时间轴背景颜色
84446            borderColor: '#ccc',               // 时间轴边框颜色
84447            borderWidth: 0,                    // 时间轴边框线宽,单位px,默认为0ï¼ˆæ— è¾¹æ¡†ï¼‰
84448
84449            orient: 'horizontal',              // 'vertical'
84450            inverse: false,
84451
84452            tooltip: {                          // boolean or Object
84453                trigger: 'item'                 // data item may also have tootip attr.
84454            },
84455
84456            symbol: 'emptyCircle',
84457            symbolSize: 10,
84458
84459            lineStyle: {
84460                show: true,
84461                width: 2,
84462                color: '#304654'
84463            },
84464            label: {                            // æ–‡æœ¬æ ‡ç­¾
84465                position: 'auto',           // auto left right top bottom
84466                                            // When using number, label position is not
84467                                            // restricted by viewRect.
84468                                            // positive: right/bottom, negative: left/top
84469                show: true,
84470                interval: 'auto',
84471                rotate: 0,
84472                // formatter: null,
84473                // å…¶ä½™å±žæ€§é»˜è®¤ä½¿ç”¨å…¨å±€æ–‡æœ¬æ ·å¼ï¼Œè¯¦è§TEXTSTYLE
84474                color: '#304654'
84475            },
84476            itemStyle: {
84477                color: '#304654',
84478                borderWidth: 1
84479            },
84480
84481            checkpointStyle: {
84482                symbol: 'circle',
84483                symbolSize: 13,
84484                color: '#c23531',
84485                borderWidth: 5,
84486                borderColor: 'rgba(194,53,49, 0.5)',
84487                animation: true,
84488                animationDuration: 300,
84489                animationEasing: 'quinticInOut'
84490            },
84491
84492            controlStyle: {
84493                show: true,
84494                showPlayBtn: true,
84495                showPrevBtn: true,
84496                showNextBtn: true,
84497                itemSize: 22,
84498                itemGap: 12,
84499                position: 'left',  // 'left' 'right' 'top' 'bottom'
84500                playIcon: 'path://M31.6,53C17.5,53,6,41.5,6,27.4S1
845007.5,1.8,31.6,1.8C45.7,1.8,57.2,13.3,57.2,27.4S45.7,53,31.6,53z M31.6,3.3 C18.4,3.3,7.5,14.1,7.5,27.4c0,13.3,10.8,24.1,24.1,24.1C44.9,51.5,55.7,40.7,55.7,27.4C55.7,14.1,44.9,3.3,31.6,3.3z M24.9,21.3 c0-2.2,1.6-3.1,3.5-2l10.5,6.1c1.899,1.1,1.899,2.9,0,4l-10.5,6.1c-1.9,1.1-3.5,0.2-3.5-2V21.3z', // jshint ignore:line
84501                stopIcon: 'path://M30.9,53.2C16.8,53.2,5.3,41.7,5.3,27.6S16.8,2,30.9,2C45,2,56.4,13.5,56.4,27.6S45,53.2,30.9,53.2z M30.9,3.5C17.6,3.5,6.8,14.4,6.8,27.6c0,13.3,10.8,24.1,24.101,24.1C44.2,51.7,55,40.9,55,27.6C54.9,14.4,44.1,3.5,30.9,3.5z M36.9,35.8c0,0.601-0.4,1-0.9,1h-1.3c-0.5,0-0.9-0.399-0.9-1V19.5c0-0.6,0.4-1,0.9-1H36c0.5,0,0.9,0.4,0.9,1V35.8z M27.8,35.8 c0,0.601-0.4,1-0.9,1h-1.3c-0.5,0-0.9-0.399-0.9-1V19.5c0-0.6,0.4-1,0.9-1H27c0.5,0,0.9,0.4,0.9,1L27.8,35.8L27.8,35.8z', // jshint ignore:line
84502                nextIcon: 'path://M18.6,50.8l22.5-22.5c0.2-0.2,0.3-0.4,0.3-0.7c0-0.3-0.1-0.5-0.3-0.7L18.7,4.4c-0.1-0.1-0.2-0.3-0.2-0.5 c0-0.4,0.3-0.8,0.8-0.8c0.2,0,0.5,0.1,0.6,0.3l23.5,23.5l0,0c0.2,0.2,0.3,0.4,0.3,0.7c0,0.3-0.1,0.5-0.3,0.7l-0.1,0.1L19.7,52 c-0.1,0.1-0.3,0.2-0.5,0.2c-0.4,0-0.8-0.3-0.8-0.8C18.4,51.2,18.5,51,18.6,50.8z', // jshint ignore:line
84503                prevIcon: 'path://M43,52.8L20.4,30.3c-0.2-0.2-0.3-0.4-0.3-0.7c0-0.3,0.1-0.5,0.3-0.7L42.9,6.4c0.1-0.1,0.2-0.3,0.2-0.5 c0-0.4-0.3-0.8-0.8-0.8c-0.2,0-0.5,0.1-0.6,0.3L18.3,28.8l0,0c-0.2,0.2-0.3,0.4-0.3,0.7c0,0.3,0.1,0.5,0.3,0.7l0.1,0.1L41.9,54 c0.1,0.1,0.3,0.2,0.5,0.2c0.4,0,0.8-0.3,0.8-0.8C43.2,53.2,43.1,53,43,52.8z', // jshint ignore:line
84504
84505                color: '#304654',
84506                borderColor: '#304654',
84507                borderWidth: 1
84508            },
84509
84510            emphasis: {
84511                label: {
84512                    show: true,
84513                    // å…¶ä½™å±žæ€§é»˜è®¤ä½¿ç”¨å…¨å±€æ–‡æœ¬æ ·å¼ï¼Œè¯¦è§TEXTSTYLE
84514                    color: '#c23531'
84515                },
84516
84517                itemStyle: {
84518                    color: '#c23531'
84519                },
84520
84521                controlStyle: {
84522                    color: '#c23531',
84523                    borderColor: '#c23531',
84524                    borderWidth: 2
84525                }
84526            },
84527            data: []
84528        }
84529
84530    });
84531
84532    mixin(SliderTimelineModel, dataFormatMixin);
84533
84534    /*
84535* Licensed to the Apache Software Foundation (ASF) under one
84536* or more contributor license agreements.  See the NOTICE file
84537* distributed with this work for additional information
84538* regarding copyright ownership.  The ASF licenses this file
84539* to you under the Apache License, Version 2.0 (the
84540* "License"); you may not use this file except in compliance
84541* with the License.  You may obtain a copy of the License at
84542*
84543*   http://www.apache.org/licenses/LICENSE-2.0
84544*
84545* Unless required by applicable law or agreed to in writing,
84546* software distributed under the License is distributed on an
84547* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
84548* KIND, either express or implied.  See the License for the
84549* specific language governing permissions and limitations
84550* under the License.
84551*/
84552
84553    var TimelineView = Component.extend({
84554        type: 'timeline'
84555    });
84556
84557    /*
84558* Licensed to the Apache Software Foundation (ASF) under one
84559* or more contributor license agreements.  See the NOTICE file
84560* distributed with this work for additional information
84561* regarding copyright ownership.  The ASF licenses this file
84562* to you under the Apache License, Version 2.0 (the
84563* "License"); you may not use this file except in compliance
84564* with the License.  You may obtain a copy of the License at
84565*
84566*   http://www.apache.org/licenses/LICENSE-2.0
84567*
84568* Unless required by applicable law or agreed to in writing,
84569* software distributed under the License is distributed on an
84570* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
84571* KIND, either express or implied.  See the License for the
84572* specific language governing permissions and limitations
84573* under the License.
84574*/
84575
84576    /**
84577     * Extend axis 2d
84578     * @constructor module:echarts/coord/cartesian/Axis2D
84579     * @extends {module:echarts/coord/cartesian/Axis}
84580     * @param {string} dim
84581     * @param {*} scale
84582     * @param {Array.<number>} coordExtent
84583     * @param {string} axisType
84584     * @param {string} position
84585     */
84586    var TimelineAxis = function (dim, scale, coordExtent, axisType) {
84587
84588        Axis.call(this, dim, scale, coordExtent);
84589
84590        /**
84591         * Axis type
84592         *  - 'category'
84593         *  - 'value'
84594         *  - 'time'
84595         *  - 'log'
84596         * @type {string}
84597         */
84598        this.type = axisType || 'value';
84599
84600        /**
84601         * Axis model
84602         * @param {module:echarts/component/TimelineModel}
84603         */
84604        this.model = null;
84605    };
84606
84607    TimelineAxis.prototype = {
84608
84609        constructor: TimelineAxis,
84610
84611        /**
84612         * @override
84613         */
84614        getLabelModel: function () {
84615            return this.model.getModel('label');
84616        },
84617
84618        /**
84619         * @override
84620         */
84621        isHorizontal: function () {
84622            return this.model.get('orient') === 'horizontal';
84623        }
84624
84625    };
84626
84627    inherits(TimelineAxis, Axis);
84628
84629    /*
84630* Licensed to the Apache Software Foundation (ASF) under one
84631* or more contributor license agreements.  See the NOTICE file
84632* distributed with this work for additional information
84633* regarding copyright ownership.  The ASF licenses this file
84634* to you under the Apache License, Version 2.0 (the
84635* "License");
84635 you may not use this file except in compliance
84636* with the License.  You may obtain a copy of the License at
84637*
84638*   http://www.apache.org/licenses/LICENSE-2.0
84639*
84640* Unless required by applicable law or agreed to in writing,
84641* software distributed under the License is distributed on an
84642* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
84643* KIND, either express or implied.  See the License for the
84644* specific language governing permissions and limitations
84645* under the License.
84646*/
84647
84648    var bind$6 = bind;
84649    var each$27 = each$1;
84650
84651    var PI$4 = Math.PI;
84652
84653    TimelineView.extend({
84654
84655        type: 'timeline.slider',
84656
84657        init: function (ecModel, api) {
84658
84659            this.api = api;
84660
84661            /**
84662             * @private
84663             * @type {module:echarts/component/timeline/TimelineAxis}
84664             */
84665            this._axis;
84666
84667            /**
84668             * @private
84669             * @type {module:zrender/core/BoundingRect}
84670             */
84671            this._viewRect;
84672
84673            /**
84674             * @type {number}
84675             */
84676            this._timer;
84677
84678            /**
84679             * @type {module:zrender/Element}
84680             */
84681            this._currentPointer;
84682
84683            /**
84684             * @type {module:zrender/container/Group}
84685             */
84686            this._mainGroup;
84687
84688            /**
84689             * @type {module:zrender/container/Group}
84690             */
84691            this._labelGroup;
84692        },
84693
84694        /**
84695         * @override
84696         */
84697        render: function (timelineModel, ecModel, api, payload) {
84698            this.model = timelineModel;
84699            this.api = api;
84700            this.ecModel = ecModel;
84701
84702            this.group.removeAll();
84703
84704            if (timelineModel.get('show', true)) {
84705
84706                var layoutInfo = this._layout(timelineModel, api);
84707                var mainGroup = this._createGroup('mainGroup');
84708                var labelGroup = this._createGroup('labelGroup');
84709
84710                /**
84711                 * @private
84712                 * @type {module:echarts/component/timeline/TimelineAxis}
84713                 */
84714                var axis = this._axis = this._createAxis(layoutInfo, timelineModel);
84715
84716                timelineModel.formatTooltip = function (dataIndex) {
84717                    return encodeHTML(axis.scale.getLabel(dataIndex));
84718                };
84719
84720                each$27(
84721                    ['AxisLine', 'AxisTick', 'Control', 'CurrentPointer'],
84722                    function (name) {
84723                        this['_render' + name](layoutInfo, mainGroup, axis, timelineModel);
84724                    },
84725                    this
84726                );
84727
84728                this._renderAxisLabel(layoutInfo, labelGroup, axis, timelineModel);
84729                this._position(layoutInfo, timelineModel);
84730            }
84731
84732            this._doPlayStop();
84733        },
84734
84735        /**
84736         * @override
84737         */
84738        remove: function () {
84739            this._clearTimer();
84740            this.group.removeAll();
84741        },
84742
84743        /**
84744         * @override
84745         */
84746        dispose: function () {
84747            this._clearTimer();
84748        },
84749
84750        _layout: function (timelineModel, api) {
84751            var labelPosOpt = timelineModel.get('label.position');
84752            var orient = timelineModel.get('orient');
84753            var viewRect = getViewRect$4(timelineModel, api);
84754            // Auto label offset.
84755            if (labelPosOpt == null || labelPosOpt === 'auto') {
84756                labelPosOpt = orient === 'horizontal'
84757                    ? ((viewRect.y + viewRect.height / 2) < api.getHeight() / 2 ? '-' : '+')
84758                    : ((viewRect.x + viewRect.width / 2) < api.getWidth() / 2 ? '+' : '-');
84759            }
84760            else if (isNaN(labelPosOpt)) {
84761                labelPosOpt = ({
84762                    horizontal: {top: '-', bottom: '+'},
84763                    vertical: {left: '-', right: '+'}
84764                })[orient][labelPosOpt];
84765            }
84766
84767            var labelAlignMap = {
84768                horizontal: 'center',
84769                vertical: (labelPosOpt >= 0 || labelPosOpt === '+') ? 'left' : 'right'
84770            };
84771
84772            var labelBaselineMap = {
84773                horizontal: (labelPosOpt >= 0 || labelPosOpt === '+') ? 'top' : 'bottom',
84774                vertical: 'middle'
84775            };
84776            var rotationMap = {
84777                horizontal: 0,
84778                vertical: PI$4 / 2
84779            };
84780
84781            // Position
84782            var mainLength = orient === 'vertical' ? viewRect.height : viewRect.width;
84783
84784            var controlModel = timelineModel.getModel('controlStyle');
84785            var showControl = controlModel.get('show', true);
84786            var controlSize = showControl ? controlModel.get('itemSize') : 0;
84787            var controlGap = showControl ? controlModel.get('itemGap') : 0;
84788            var sizePlusGap = controlSize + controlGap;
84789
84790            // Special label rotate.
84791            var labelRotation = timelineModel.get('label.rotate') || 0;
84792            labelRotation = labelRotation * PI$4 / 180; // To radian.
84793
84794            var playPosition;
84795            var prevBtnPosition;
84796            var nextBtnPosition;
84797            var axisExtent;
84798            var controlPosition = controlModel.get('position', true);
84799            var showPlayBtn = showControl && controlModel.get('showPlayBtn', true);
84800            var showPrevBtn = showControl && controlModel.get('showPrevBtn', true);
84801            var showNextBtn = showControl && controlModel.get('showNextBtn', true);
84802            var xLeft = 0;
84803            var xRight = mainLength;
84804
84805            // position[0] means left, position[1] means middle.
84806            if (controlPosition === 'left' || controlPosition === 'bottom') {
84807                showPlayBtn && (playPosition = [0, 0], xLeft += sizePlusGap);
84808                showPrevBtn && (prevBtnPosition = [xLeft, 0], xLeft += sizePlusGap);
84809                showNextBtn && (nextBtnPosition = [xRight - controlSize, 0], xRight -= sizePlusGap);
84810            }
84811            else { // 'top' 'right'
84812                showPlayBtn && (playPosition = [xRight - controlSize, 0], xRight -= sizePlusGap);
84813                showPrevBtn && (prevBtnPosition = [0, 0], xLeft += sizePlusGap);
84814                showNextBtn && (nextBtnPosition = [xRight - controlSize, 0], xRight -= sizePlusGap);
84815            }
84816            axisExtent = [xLeft, xRight];
84817
84818            if (timelineModel.get('inverse')) {
84819                axisExtent.reverse();
84820            }
84821
84822            return {
84823                viewRect: viewRect,
84824                mainLength: mainLength,
84825                orient: orient,
84826
84827                rotation: rotationMap[orient],
84828                labelRotation: labelRotation,
84829                labelPosOpt: labelPosOpt,
84830                labelAlign: timelineModel.get('label.align') || labelAlignMap[orient],
84831                labelBaseline: timelineModel.get('label.verticalAlign')
84832                || timelineModel.get('label.baseline')
84833                || labelBaselineMap[orient],
84834
84835                // Based on mainGroup.
84836                playPosition: playPosition,
84837                prevBtnPosition: prevBtnPosition,
84838                nextBtnPosition: nextBtnPosition,
84839                axisExtent: axisExtent,
84840
84841                controlSize: controlSize,
84842                controlGap: controlGap
84843            };
84844        },
84845
84846        _position: function (layoutInfo, timelineModel) {
84847            // Position is be called finally, because bounding rect is needed for
84848            // adapt content to fill viewRect (auto adapt offset).
84849
84850            // Timeline may be not all in the viewRect when 'offset' is specified
84851            // as a number, because it is more appropriate that label aligns at
84852            // 'offset' but not the other edge defined by viewRect.
84853
84854            var mainGroup = this._mainGroup;
84855            var labelGroup = this._labelGroup;
84856
84857            var viewRect = layoutInfo.viewRect;
84858            if (layoutInfo.orient === 'vertical') {
84859                // transform to horizontal, inverse rotate by left-top point.
84860                var m = create$1();
84861                var rotateOriginX = viewRect.x;
84862                var rotateOriginY = viewRect.y + viewRect.height;
84863                translate(m, m, [-rotateOriginX, -rotateOriginY]);
84864                rotate(m, m, -PI$4 / 2);
84865                translate(m, m, [rotateOriginX, rotateOriginY]);
84866                viewRect = viewRect.clone();
84867                viewRect.applyTransform(m);
84868            }
84869
84870            var viewBound = getBound(viewRect);
84871            var mainBound = getBound(mainGroup.getBoundingRect());
84872            var labelBound = getBound(labelGroup.getBoundingRect());
84873
84874            var mainPosition = mainGroup.position;
84875            var labelsPosition = labelGroup.position;
84876
84877            labelsPosition[0] = mainPosition[0] = viewBound[0][0];
84878
84879            var labelPosOpt = layoutInfo.labelPosOpt;
84880
84881            if (isNaN(labelPosOpt)) { // '+' or '-'
84882                var mainBoundIdx = labelPosOpt === '+' ? 0 : 1;
84883                toBound(mainPosition, mainBound, viewBound, 1, mainBoundIdx);
84884                toBound(labelsPosition, labelBound, viewBound, 1, 1 - mainBoundIdx);
84885            }
84886            else {
84887                var mainBoundIdx = labelPosOpt >= 0 ? 0 : 1;
84888                toBound(mainPosition, mainBound, viewBound, 1, mainBoundIdx);
84889                labelsPosition[1] = mainPosition[1] + labelPosOpt;
84890            }
84891
84892            mainGroup.attr('position', mainPosition);
84893            labelGroup.attr('position', labelsPosition);
84894            mainGroup.rotation = labelGroup.rotation = layoutInfo.rotation;
84895
84896            setOrigin(mainGroup);
84897            setOrigin(labelGroup);
84898
84899            function setOrigin(targetGroup) {
84900                var pos = targetGroup.position;
84901                targetGroup.origin = [
84902                    viewBound[0][0] - pos[0],
84903                    viewBound[1][0] - pos[1]
84904                ];
84905            }
84906
84907            function getBound(rect) {
84908                // [[xmin, xmax], [ymin, ymax]]
84909                return [
84910                    [rect.x, rect.x + rect.width],
84911                    [rect.y, rect.y + rect.height]
84912                ];
84913            }
84914
84915            function toBound(fromPos, from, to, dimIdx, boundIdx) {
84916                fromPos[dimIdx] += to[dimIdx][boundIdx] - from[dimIdx][boundIdx];
84917            }
84918        },
84919
84920        _createAxis: function (layoutInfo, timelineModel) {
84921            var data = timelineModel.getData();
84922            var axisType = timelineModel.get('axisType');
84923
84924            var scale = createScaleByModel(timelineModel, axisType);
84925
84926            // Customize scale. The `tickValue` is `dataIndex`.
84927            scale.getTicks = function () {
84928                return data.mapArray(['value'], function (value) {
84929                    return value;
84930                });
84931            };
84932
84933            var dataExtent = data.getDataExtent('value');
84934            scale.setExtent(dataExtent[0], dataExtent[1]);
84935            scale.niceTicks();
84936
84937            var axis = new TimelineAxis('value', scale, layoutInfo.axisExtent, axisType);
84938            axis.model = timelineModel;
84939
84940            return axis;
84941        },
84942
84943        _createGroup: function (name) {
84944            var newGroup = this['_' + name] = new Group();
84945            this.group.add(newGroup);
84946            return newGroup;
84947        },
84948
84949        _renderAxisLine: function (layoutInfo, group, axis, timelineModel) {
84950            var axisExtent = axis.getExtent();
84951
84952            if (!timelineModel.get('lineStyle.show')) {
84953                return;
84954            }
84955
84956            group.add(new Line({
84957                shape: {
84958                    x1: axisExtent[0], y1: 0,
84959                    x2: axisExtent[1], y2: 0
84960                },
84961                style: extend(
84962                    {lineCap: 'round'},
84963                    timelineModel.getModel('lineStyle').getLineStyle()
84964                ),
84965                silent: true,
84966                z2: 1
84967            }));
84968        },
84969
84970        /**
84971         * @private
84972         */
84973        _renderAxisTick: function (layoutInfo, group, axis, timelineModel) {
84974            var data = timelineModel.getData();
84975            // Show all ticks, despite ignoring strategy.
84976            var ticks = axis.scale.getTicks();
84977
84978            // The value is dataIndex, see the costomized scale.
84979            each$27(ticks, function (value) {
84980                var tickCoord = axis.dataToCoord(value);
84981                var itemModel = data.getItemModel(value);
84982                var itemStyleModel = itemModel.getModel('itemStyle');
84983                var hoverStyleModel = itemModel.getModel('emphasis.itemStyle');
84984                var symbolOpt = {
84985                    position: [tickCoord, 0],
84986                    onclick: bind$6(this._changeTimeline, this, value)
84987                };
84988                var el = giveSymbol(itemModel, itemStyleModel, group, symbolOpt);
84989                setHoverStyle(el, hoverStyleModel.getItemStyle());
84990
84991                if (itemModel.get('tooltip')) {
84992                    el.dataIndex = value;
84993                    el.dataModel = timelineModel;
84994                }
84995                else {
84996                    el.dataIndex = el.dataModel = null;
84997                }
84998
84999            }, this);
85000        },
85001
85002        /**
85003         * @private
85004         */
85005        _renderAxisLabel: function (layoutInfo, group, axis, timelineModel) {
85006            var labelModel = axis.getLabelModel();
85007
85008            if (!labelModel.get('show')) {
85009                return;
85010            }
85011
85012            var data = timelineModel.getData();
85013            var labels = axis.getViewLabels();
85014
85015            each$27(labels, function (labelItem) {
85016                // The tickValue is dataIndex, see the costomized scale.
85017                var dataIndex = labelItem.tickValue;
85018
85019                var itemModel = data.getItemModel(dataIndex);
85020                var normalLabelModel = itemModel.getModel('label');
85021                var hoverLabelModel = itemModel.getModel('emphasis.label');
85022                var tickCoord = axis.dataToCoord(labelItem.tickValue);
85023                var textEl = new Text({
85024                    position: [tickCoord, 0],
85025                    rotation: layoutInfo.labelRotation - layoutInfo.rotation,
85026                    onclick: bind$6(this._changeTimeline, this, dataIndex),
85027                    silent: false
85028                });
85029                setTextStyle(textEl.style, normalLabelModel, {
85030                    text: labelItem.formattedLabel,
85031                    textAlign: layoutInfo.labelAlign,
85032                    textVerticalAlign: layoutInfo.labelBaseline
85033                });
85034
85035                group.add(textEl);
85036                setHoverStyle(
85037                    textEl, setTextStyle({}, hoverLabelModel)
85038                );
85039
85040            }, this);
85041        },
85042
85043        /**
85044         * @private
85045         */
85046        _renderControl: function (layoutInfo, group, axis, timelineModel) {
85047            var controlSize = layoutInfo.controlSize;
85048            var rotation = layoutInfo.rotation;
85049
85050            var itemStyle = timelineModel.getModel('controlStyle').getItemStyle();
85051            var hoverStyle = timelineModel.getModel('emphasis.controlStyle').getItemStyle();
85052            var rect = [0, -controlSize / 2, controlSize, controlSize];
85053            var playState = timelineModel.getPlayState();
85054            var inverse = timelineModel.get('inverse', true);
85055
85056            makeBtn(
85057                layoutInfo.nextBtnPosition,
85058                'controlStyle.nextIcon',
85059                bind$6(this._changeTimeline, this, inverse ? '-' : '+')
85060            );
85061            makeBtn(
85062                layoutInfo.prevBtnPosition,
85063                'controlStyle.prevIcon',
85064                bind$6(this._changeTimeline, this, inverse ? '+' : '-')
85065            );
85066            makeBtn(
85067                layoutInfo.playPosition,
85068                'controlStyle.' + (playState ? 'stopIcon' : 'playIcon'),
85069                bind$6(this._handlePlayClick, this, !playState),
85070                true
85071            );
85072
85073            function makeBtn(position, iconPath, onclick, willRotate) {
85074                if (!position) {
85075                    return;
85076                }
85077                var opt = {
85078                    position: position,
85079                    origin: [controlSize / 2, 0],
85080                    rotation: willRotate ? -rotation : 0,
85081                    rectHover: true,
85082                    style: itemStyle,
85083                    onclick: onclick
85084                };
85085                var btn = makeIcon(timelineModel, iconPath, rect, opt);
85086                group.add(btn);
85087                setHoverStyle(btn, hoverStyle);
85088            }
85089        },
85090
85091        _renderCurrentPointer: function (layoutInfo, group, axis, timelineModel) {
85092            var data = timelineModel.getData();
85093            var currentIndex = timelineModel.getCurrentIndex();
85094            var pointerModel = data.getItemModel(currentIndex).getModel('checkpointStyle');
85095            var me = this;
85096
85097            var callback = {
85098                onCreate: function (pointer) {
85099                    pointer.draggable = true;
85100                    pointer.drift = bind$6(me._handlePointerDrag, me);
85101                    pointer.ondragend = bind$6(me._handlePointerDragend, me);
85102                    pointerMoveTo(pointer, currentIndex, axis, timelineModel, true);
85103                },
85104                onUpdate: function (pointer) {
85105                    pointerMoveTo(pointer, currentIndex, axis, timelineModel);
85106                }
85107            };
85108
85109            // Reuse when exists, for animation and drag.
85110            this._currentPointer = giveSymbol(
85111                pointerModel, pointerModel, this._mainGroup, {}, this._currentPointer, callback
85112            );
85113        },
85114
85115        _handlePlayClick: function (nextState) {
85116            this._clearTimer();
85117            this.api.dispatchAction({
85118                type: 'timelinePlayChange',
85119                playState: nextState,
85120                from: this.uid
85121            });
85122        },
85123
85124        _handlePointerDrag: function (dx, dy, e) {
85125            this._clearTimer();
85126            this._pointerChangeTimeline([e.offsetX, e.offsetY]);
85127        },
85128
85129        _handlePointerDragend: function (e) {
85130            this._pointerChangeTimeline([e.offsetX, e.offsetY], true);
85131        },
85132
85133        _pointerChangeTimeline: function (mousePos, trigger) {
85134            var toCoord = this._toAxisCoord(mousePos)[0];
85135
85136            var axis = this._axis;
85137            var axisExtent = asc(axis.getExtent().slice());
85138
85139            toCoord > axisExtent[1] && (toCoord = axisExtent[1]);
85140            toCoord < axisExtent[0] && (toCoord = axisExtent[0]);
85141
85142            this._currentPointer.position[0] = toCoord;
85143            this._currentPointer.dirty();
85144
85145            var targetDataIndex = this._findNearestTick(toCoord);
85146            var timelineModel = this.model;
85147
85148            if (trigger || (
85149                    targetDataIndex !== timelineModel.getCurrentIndex()
85150                    && timelineModel.get('realtime')
85151                )) {
85152                this._changeTimeline(targetDataIndex);
85153            }
85154        },
85155
85156        _doPlayStop: function () {
85157            this._clearTimer();
85158
85159            if (this.model.getPlayState()) {
85160                this._timer = setTimeout(
85161                    bind$6(handleFrame, this),
85162                    this.model.get('playInterval')
85163                );
85164            }
85165
85166            function handleFrame() {
85167                // Do not cache
85168                var timelineModel = this.model;
85169                this._changeTimeline(
85170                    timelineModel.getCurrentIndex()
85171                    + (timelineModel.get('rewind', true) ? -1 : 1)
85172                );
85173            }
85174        },
85175
85176        _toAxisCoord: function (vertex) {
85177            var trans = this._mainGroup.getLocalTransform();
85178            return applyTransform$1(vertex, trans, true);
85179        },
85180
85181        _findNearestTick: function (axisCoord) {
85182            var data = this.model.getData();
85183            var dist = Infinity;
85184            var targetDataIndex;
85185            var axis = this._axis;
85186
85187            data.each(['value'], function (value, dataIndex) {
85188                var coord = axis.dataToCoord(value);
85189                var d = Math.abs(coord - axisCoord);
85190                if (d < dist) {
85191                    dist = d;
85192                    targetDataIndex = dataIndex;
85193                }
85194            });
85195
85196            return targetDataIndex;
85197        },
85198
85199        _clearTimer: function () {
85200            if (this._timer) {
85201                clearTimeout(this._timer);
85202                this._timer = null;
85203            }
85204        },
85205
85206        _changeTimeline: function (nextIndex) {
85207            var currentIndex = this.model.getCurrentIndex();
85208
85209            if (nextIndex === '+') {
85210                nextIndex = currentIndex + 1;
85211            }
85212            else if (nextIndex === '-') {
85213                nextIndex = currentIndex - 1;
85214            }
85215
85216            this.api.dispatchAction({
85217                type: 'timelineChange',
85218                currentIndex: nextIndex,
85219                from: this.uid
85220            });
85221        }
85222
85223    });
85224
85225    function getViewRect$4(model, api) {
85226        return getLayoutRect(
85227            model.getBoxLayoutParams(),
85228            {
85229                width: api.getWidth(),
85230                height: api.getHeight()
85231            },
85232            model.get('padding')
85233        );
85234    }
85235
85236    function makeIcon(timelineModel, objPath, rect, opts) {
85237        var icon = makePath(
85238            timelineModel.get(objPath).replace(/^path:\/\//, ''),
85239            clone(opts || {}),
85240            new BoundingRect(rect[0], rect[1], rect[2], rect[3]),
85241            'center'
85242        );
85243
85244        return icon;
85245    }
85246
85247    /**
85248     * Create symbol or update symbol
85249     * opt: basic position and event handlers
85250     */
85251    function giveSymbol(hostModel, itemStyleModel, group, opt, symbol, callback) {
85252        var color = itemStyleModel.get('color');
85253
85254        if (!symbol) {
85255            var symbolType = hostModel.get('symbol');
85256            symbol = createSymbol(symbolType, -1, -1, 2, 2, color);
85257            symbol.setStyle('strokeNoScale', true);
85258            group.add(symbol);
85259            callback && callback.onCreate(symbol);
85260        }
85261        else {
85262            symbol.setColor(color);
85263            group.add(symbol); // Group may be new, also need to add.
85264            callback && callback.onUpdate(symbol);
85265        }
85266
85267        // Style
85268        var itemStyle = itemStyleModel.getItemStyle(['color', 'symbol', 'symbolSize']);
85269        symbol.setStyle(itemStyle);
85270
85271        // Transform and events.
85272        opt = merge({
85273            rectHover: true,
85274            z2: 100
85275        }, opt, true);
85276
85277        var symbolSize = hostModel.get('symbolSize');
85278        symbolSize = symbolSize instanceof Array
85279            ? symbolSize.slice()
85280            : [+symbolSize, +symbolSize];
85281        symbolSize[0] /= 2;
85282        symbolSize[1] /= 2;
85283        opt.scale = symbolSize;
85284
85285        var symbolOffset = hostModel.get('symbolOffset');
85286        if (symbolOffset) {
85287            var pos = opt.position = opt.position || [0, 0];
85288            pos[0] += parsePercent$1(symbolOffset[0], symbolSize[0]);
85289            pos[1] += parsePercent$1(symbolOffset[1], symbolSize[1]);
85290        }
85291
85292        var symbolRotate = hostModel.get('symbolRotate');
85293        opt.rotation = (symbolRotate || 0) * Math.PI / 180 || 0;
85294
85295        symbol.attr(opt);
85296
85297        // FIXME
85298        // (1) When symbol.style.strokeNoScale is true and updateTransform is not performed,
85299        // getBoundingRect will return wrong result.
85300        // (This is supposed to be resolved in zrender, but it is a little difficult to
85301        // leverage performance and auto updateTransform)
85302        // (2) All of ancesters of symbol do not scale, so we can just updateTransform symbol.
85303        symbol.updateTransform();
85304
85305        return symbol;
85306    }
85307
85308    function pointerMoveTo(pointer, dataIndex, axis, timelineModel, noAnimation) {
85309        if (pointer.dragging) {
85310            return;
85311        }
85312
85313        var pointerModel = timelineModel.getModel('checkpointStyle');
85314        var toCoord = axis.dataToCoord(timelineModel.getData().get(['value'], dataIndex));
85315
85316        if (noAnimation || !pointerModel.get('animation', true)) {
85317            pointer.attr({position: [toCoord, 0]});
85318        }
85319        else {
85320            pointer.stopAnimation(true);
85321            pointer.animateTo(
85322                {position: [toCoord, 0]},
85323                pointerModel.get('animationDuration', true),
85324                pointerModel.get('animationEasing', true)
85325            );
85326        }
85327    }
85328
85329    /*
85330* Licensed to the Apache Software Foundation (ASF) under one
85331* or more contributor license agreements.  See the NOTICE file
85332* distributed with this work for additional information
85333* regarding copyright ownership.  The ASF licenses this file
85334* to you under the Apache License, Version 2.0 (the
85335* "License"); you may not use this file except in compliance
85336* with the License.  You may obtain a copy of the License at
85337*
85338*   http://www.apache.org/licenses/LICENSE-2.0
85339*
85340* Unless required by applicable law or agreed to in writing,
85341* software distributed under the License is distributed on an
85342* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
85343* KIND, either express or implied.  See the License for the
85344* specific language governing permissions and limitations
85345* under the License.
85346*/
85347
85348    /**
85349     * DataZoom component entry
85350     */
85351
85352    registerPreprocessor(preprocessor$3);
85353
85354    /*
85355* Licensed to the Apache Software Foundation (ASF) under one
85356* or more contributor license agreements.  See the NOTICE file
85357* distributed with this work for additional information
85358* regarding copyright ownership.  The ASF licenses this file
85359* to you under the Apache License, Version 2.0 (the
85360* "License"); you may not use this file except in compliance
85361* with the License.  You may obtain a copy of the License at
85362*
85363*   http://www.apache.org/licenses/LICENSE-2.0
85364*
85365* Unless required by applicable law or agreed to in writing,
85366* software distributed under the License is distributed on an
85367* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
85368* KIND, either express or implied.  See the License for the
85369* specific language governing permissions and limitations
85370* under the License.
85371*/
85372
85373    var ToolboxModel = extendComponentModel({
85374
85375        type: 'toolbox',
85376
85377        layoutMode: {
85378            type: 'box',
85379            ignoreSize: true
85380        },
85381
85382        optionUpdated: function () {
85383            ToolboxModel.superApply(this, 'optionUpdated', arguments);
85384
85385            each$1(this.option.feature, function (featureOpt, featureName) {
85386                var Feature = get$1(featureName);
85387                Feature && merge(featureOpt, Feature.defaultOption);
85388            });
85389        },
85390
85391        defaultOption: {
85392
85393            show: true,
85394
85395            z: 6,
85396
85397            zlevel: 0,
85398
85399            orient: 'horizontal',
85400
85401            left: 'right',
85402
85403            top: 'top',
85404
85405            // right
85406            // bottom
85407
85408            backgroundColor: 'transparent',
85409
85410            borderColor: '#ccc',
85411
85412            borderRadius: 0,
85413
85414            borderWidth: 0,
85415
85416            padding: 5,
85417
85418            itemSize: 15,
85419
85420            itemGap: 8,
85421
85422            showTitle: true,
85423
85424            iconStyle: {
85425                borderColor: '#666',
85426                color: 'none'
85427            },
85428            emphasis: {
85429                iconStyle: {
85430                    borderColor: '#3E98C5'
85431                }
85432            }
85433            // textStyle: {},
85434
85435            // feature
85436        }
85437    });
85438
85439    /*
85440* Licensed to the Apache Software Foundation (ASF) under one
85441* or more contributor license agreements.  See the NOTICE file
85442* distributed with this work for additional information
85443* regarding copyright ownership.  The ASF licenses this file
85444* to you under the Apache License, Version 2.0 (the
85445* "License"); you may not use this file except in compliance
85446* with the License.  You may obtain a copy of the License at
85447*
85448*   http://www.apache.org/licenses/LICENSE-2.0
85449*
85450* Unless required by applicable law or agreed to in writing,
85451* software distributed under the License is distributed on an
85452* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
85453* KIND, either express or implied.  See the License for the
85454* specific language governing permissions and limitations
85455* under the License.
85456*/
85457
85458    extendComponentView({
85459
85460        type: 'toolbox',
85461
85462        render: function (toolboxModel, ecModel, api, payload) {
85463            var group = this.group;
85464            group.removeAll();
85465
85466            if (!toolboxModel.get('show')) {
85467                return;
85468            }
85469
85470            var itemSize = +toolboxModel.get('itemSize');
85471            var featureOpts = toolboxModel.get('feature') || {};
85472            var features = this._features || (this._features = {});
85473
85474            var featureNames = [];
85475            each$1(featureOpts, function (opt, name) {
85476                featureNames.push(name);
85477            });
85478
85479            (new DataDiffer(this._featureNames || [], featureNames))
85480                .add(processFeature)
85481                .update(processFeature)
85482                .remove(curry(processFeature, null))
85483                .execute();
85484
85485            // Keep for diff.
85486            this._featureNames = featureNames;
85487
85488            function processFeature(newIndex, oldIndex) {
85489                var featureName = featureNames[newIndex];
85490                var oldName = featureNames[oldIndex];
85491                var featureOpt = featureOpts[featureName];
85492                var featureModel = new Model(featureOpt, toolboxModel, toolboxModel.ecModel);
85493                var feature;
85494
85495                if (featureName && !oldName) { // Create
85496                    if (isUserFeatureName(featureName)) {
85497                        feature = {
85498                            model: featureModel,
85499                            onclick: featureModel.option.onclick,
85500                            featureName: featureName
85501                        };
85502                    }
85503                    else {
85504                        var Feature = get$1(featureName);
85505                        if (!Feature) {
85506                            return;
85507                        }
85508                        feature = new Feature(featureModel, ecModel, api);
85509                    }
85510                    features[featureName] = feature;
85511                }
85512                else {
85513                    feature = features[oldName];
85514                    // If feature does not exsit.
85515                    if (!feature) {
85516                        return;
85517                    }
85518                    feature.model = featureModel;
85519                    feature.ecModel = ecModel;
85520                    feature.api = api;
85521                }
85522
85523                if (!featureName && oldName) {
85524                    feature.dispose && feature.dispose(ecModel, api);
85525                    return;
85526                }
85527
85528                if (!featureModel.get('show') || feature.unusable) {
85529                    feature.remove && feature.remove(ecModel, api);
85530                    return;
85531                }
85532
85533                createIconPaths(featureModel, feature, featureName);
85534
85535                featureModel.setIconStatus = function (iconName, status) {
85536                    var option = this.option;
85537                    var iconPaths = this.iconPaths;
85538                    option.iconStatus = option.iconStatus || {};
85539                    option.iconStatus[iconName] = status;
85540                    // FIXME
85541                    iconPaths[iconName] && iconPaths[iconName].trigger(status);
85542                };
85543
85544                if (feature.render) {
85545                    feature.render(featureModel, ecModel, api, payload);
85546                }
85547            }
85548
85549            function createIconPaths(featureModel, feature, featureName) {
85550                var iconStyleModel = featureModel.getModel('iconStyle');
85551                var iconStyleEmphasisModel = featureModel.getModel('emphasis.iconStyle');
85552
85553                // If one feature has mutiple icon. they are orginaized as
85554                // {
85555                //     icon: {
85556                //         foo: '',
85557                //         bar: ''
85558                //     },
85559                //     title: {
85560                //         foo: '',
85561                //         bar: ''
85562                //     }
85563                // }
85564                var icons = feature.getIcons ? feature.getIcons() : featureModel.get('icon');
85565                var titles = featureModel.get('title') || {};
85566                if (typeof icons === 'string') {
85567                    var icon = icons;
85568                    var title = titles;
85569                    icons = {};
85570                    titles = {};
85571                    icons[featureName] = icon;
85572                    titles[featureName] = title;
85573                }
85574                var iconPaths = featureModel.iconPaths = {};
85575                each$1(icons, function (iconStr, iconName) {
85576                    var path = createIcon(
85577                        iconStr,
85578                        {},
85579                        {
85580                            x: -itemSize / 2,
85581                            y: -itemSize / 2,
85582                            width: itemSize,
85583                            height: itemSize
85584                        }
85585                    );
85586                    path.setStyle(iconStyleModel.getItemStyle());
85587                    path.hoverStyle = iconStyleEmphasisModel.getItemStyle();
85588
85589                    setHoverStyle(path);
85590
85591                    if (toolboxModel.get('showTitle')) {
85592                        path.__title = titles[iconName];
85593                        path.on('mouseover', function () {
85594                            // Should not reuse above hoverStyle, which might be modified.
85595                            var hoverStyle = iconStyleEmphasisModel.getItemStyle();
85596                            path.setStyle({
85597                                text: titles[iconName],
85598                                textPosition: hoverStyle.textPosition || 'bottom',
85599                                textFill: hoverStyle.fill || hoverStyle.stroke || '#000',
85600                                textAlign: hoverStyle.textAlign || 'center'
85601                            });
85602                        })
85603                            .on('mouseout', function () {
85604                                path.setStyle({
85605                                    textFill: null
85606                                });
85607                            });
85608                    }
85609                    path.trigger(featureModel.get('iconStatus.' + iconName) || 'normal');
85610
85611                    group.add(path);
85612                    path.on('click', bind(
85613                        feature.onclick, feature, ecModel, api, iconName
85614                    ));
85615
85616                    iconPaths[iconName] = path;
85617                });
85618            }
85619
85620            layout$3(group, toolboxModel, api);
85621            // Render background after group is layout
85622            // FIXME
85623            group.add(makeBackground(group.getBoundingRect(), toolboxModel));
85624
85625            // Adjust icon title positions to avoid them out of screen
85626            group.eachChild(function (icon) {
85627                var titleText = icon.__title;
85628                var hoverStyle = icon.hoverStyle;
85629                // May be background element
85630                if (hoverStyle && titleText) {
85631                    var rect = getBoundingRect(
85632                        titleText, makeFont(hoverStyle)
85633                    );
85634                    var offsetX = icon.position[0] + group.position[0];
85635                    var offsetY = icon.position[1] + group.position[1] + itemSize;
85636
85637                    var needPutOnTop = false;
85638                    if (offsetY + rect.height > api.getHeight()) {
85639                        hoverStyle.textPosition = 'top';
85640                        needPutOnTop = true;
85641                    }
85642                    var topOffset = needPutOnTop ? (-5 - rect.height) : (itemSize + 8);
85643                    if (offsetX + rect.width /  2 > api.getWidth()) {
85644                        hoverStyle.textPosition = ['100%', topOffset];
85645                        hoverStyle.textAlign = 'right';
85646                    }
85647                    else if (offsetX - rect.width / 2 < 0) {
85648                        hoverStyle.textPosition = [0, topOffset];
85649                        hoverStyle.textAlign = 'left';
85650                    }
85651                }
85652            });
85653        },
85654
85655        updateView: function (toolboxModel, ecModel, api, payload) {
85656            each$1(this._features, function (feature) {
85657                feature.updateView && feature.updateView(feature.model, ecModel, api, payload);
85658            });
85659        },
85660
85661        // updateLayout: function (toolboxModel, ecModel, api, payload) {
85662        //     zrUtil.each(this._features, function (feature) {
85663        //         feature.updateLayout && feature.updateLayout(feature.model, ecModel, api, payload);
85664        //     });
85665        // },
85666
85667        remove: function (ecModel, api) {
85668            each$1(this._features, function (feature) {
85669                feature.remove && feature.remove(ecModel, api);
85670            });
85671            this.group.removeAll();
85672        },
85673
85674        dispose: function (ecModel, api) {
85675            each$1(this._features, function (feature) {
85676                feature.dispose && feature.dispose(ecModel, api);
85677            });
85678        }
85679    });
85680
85681    function isUserFeatureName(featureName) {
85682        return featureName.indexOf('my') === 0;
85683    }
85684
85685    /*
85686* Licensed to the Apache Software Foundation (ASF) under one
85687* or more contributor license agreements.  See the NOTICE file
85688* distributed with this work for additional information
85689* regarding copyright ownership.  The ASF licenses this file
85690* to you under the Apache License, Version 2.0 (the
85691* "License"); you may not use this file except in compliance
85692* with the License.  You may obtain a copy of the License at
85693*
85694*   http://www.apache.org/licenses/LICENSE-2.0
85695*
85696* Unless required by applicable law or agreed to in writing,
85697* software distributed under the License is distributed on an
85698* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
85699* KIND, either express or implied.  See the License for the
85700* specific language governing permissions and limitations
85701* under the License.
85702*/
85703
85704    var saveAsImageLang = lang.toolbox.saveAsImage;
85705
85706    function SaveAsImage(model) {
85707        this.model = model;
85708    }
85709
85710    SaveAsImage.defaultOption = {
85711        show: true,
85712        icon: 'M4.7,22.9L29.3,45.5L54.7,23.4M4.6,43.6L4.6,58L53.8,58L53.8,43.6M29.2,45.1L29.2,0',
85713        title: saveAsImageLang.title,
85714        type: 'png',
85715        // Default use option.backgroundColor
85716        // backgroundColor: '#fff',
85717        name: '',
85718        excludeComponents: ['toolbox'],
85719        pixelRatio: 1,
85720        lang: saveAsImageLang.lang.slice()
85721    };
85722
85723    SaveAsImage.prototype.unusable = !env$1.canvasSupported;
85724
85725    var proto$4 = SaveAsImage.prototype;
85726
85727    proto$4.onclick = function (ecModel, api) {
85728        var model = this.model;
85729        var title = model.get('name') || ecModel.get('title.0.text') || 'echarts';
85730        var $a = document.createElement('a');
85731        var type = model.get('type', true) || 'png';
85732        $a.download = title + '.' + type;
85733        $a.target = '_blank';
85734        var url = api.getConnectedDataURL({
85735            type: type,
85736            backgroundColor: model.get('backgroundColor', true)
85737            || ecModel.get('backgroundColor') || '#fff',
85738            excludeComponents: model.get('excludeComponents'),
85739            pixelRatio: model.get('pixelRatio')
85740        });
85741        $a.href = url;
85742        // Chrome and Firefox
85743        if (typeof MouseEvent === 'function' && !env$1.browser.ie && !env$1.browser.edge) {
85744            var evt = new MouseEvent('click', {
85745                view: window,
85746                bubbles: true,
85747                cancelable: false
85748            });
85749            $a.dispatchEvent(evt);
85750        }
85751        // IE
85752        else {
85753            if (window.navigator.msSaveOrOpenBlob) {
85754                var bstr = atob(url.split(',')[1]);
85755                var n = bstr.length;
85756                var u8arr = new Uint8Array(n);
85757                while(n--) {
85758                    u8arr[n] = bstr.charCodeAt(n);
85759                }
85760                var blob = new Blob([u8arr]);
85761                window.navigator.msSaveOrOpenBlob(blob, title + '.' + type);
85762            }
85763            else {
85764                var lang$$1 = model.get('lang');
85765                var html = '' +
85766                    '<body style="margin:0;">' +
85767                    '<img src="' + url + '" style="max-width:100%;" title="' + ((lang$$1 && lang$$1[0]) || '') + '" />' +
85768                    '</body>';
85769                var tab = window.open();
85770                tab.document.write(html);
85771            }
85772        }
85773    };
85774
85775    register$1(
85776        'saveAsImage', SaveAsImage
85777    );
85778
85779    /*
85780* Licensed to the Apache Software Foundation (ASF) under one
85781* or more contributor license agreements.  See the NOTICE file
85782* distributed with this work for additional information
85783* regarding copyright ownership.  The ASF licenses this file
85784* to you under the Apache License, Version 2.0 (the
85785* "License"); you may not use this file except in compliance
85786* with the License.  You may obtain a copy of the License at
85787*
85788*   http://www.apache.org/licenses/LICENSE-2.0
85789*
85790* Unless required by applicable law or agreed to in writing,
85791* software distributed under the License is distributed on an
85792* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
85793* KIND, either express or implied.  See the License for the
85794* specific language governing permissions and limitations
85795* under the License.
85796*/
85797
85798    var magicTypeLang = lang.toolbox.magicType;
85799
85800    function MagicType(model) {
85801        this.model = model;
85802    }
85803
85804    MagicType.defaultOption = {
85805        show: true,
85806        type: [],
85807        // Icon group
85808        icon: {
85809            line: 'M4.1,28.9h7.1l9.3-22l7.4,38l9.7-19.7l3,12.8h14.9M4.1,58h51.4',
85810            bar: 'M6.7,22.9h10V48h-10V22.9zM24.9,13h10v35h-10V13zM43.2,2h10v46h-10V2zM3.1,58h53.7',
85811            stack: 'M8.2,38.4l-8.4,4.1l30.6,15.3L60,42.5l-8.1-4.1l-21.5,11L8.2,38.4z M51.9,30l-8.1,4.2l-13.4,6.9l-13.9-6.9L8.2,30l-8.4,4.2l8.4,4.2l22.2,11l21.5-11l8.1-4.2L51.9,30z M51.9,21.7l-8.1,4.2L35.7,30l-5.3,2.8L24.9,30l-8.4-4.1l-8.3-4.2l-8.4,4.2L8.2,30l8.3,4.2l13.9,6.9l13.4-6.9l8.1-4.2l8.1-4.1L51.9,21.7zM30.4,2.2L-0.2,17.5l8.4,4.1l8.3,4.2l8.4,4.2l5.5,2.7l5.3-2.7l8.1-4.2l8.1-4.2l8.1-4.1L30.4,2.2z', // jshint ignore:line
85812            tiled: 'M2.3,2.2h22.8V25H2.3V2.2z M35,2.2h22.8V25H35V2.2zM2.3,35h22.8v22.8H2.3V35z M35,35h22.8v22.8H35V35z'
85813        },
85814        // `line`, `bar`, `stack`, `tiled`
85815        title: clone(magicTypeLang.title),
85816        option: {},
85817        seriesIndex: {}
85818    };
85819
85820    var proto$5 = MagicType.prototype;
85821
85822    proto$5.getIcons = function () {
85823        var model = this.model;
85824        var availableIcons = model.get('icon');
85825        var icons = {};
85826        each$1(model.get('type'), function (type) {
85827            if (availableIcons[type]) {
85828                icons[type] = availableIcons[type];
85829            }
85830        });
85831        return icons;
85832    };
85833
85834    var seriesOptGenreator = {
85835        'line': function (seriesType, seriesId, seriesModel, model) {
85836            if (seriesType === 'bar') {
85837                return merge({
85838                    id: seriesId,
85839                    type: 'line',
85840                    // Preserve data related option
85841                    data: seriesModel.get('data'),
85842                    stack: seriesModel.get('stack'),
85843                    markPoint: seriesModel.get('markPoint'),
85844                    markLine: seriesModel.get('markLine')
85845                }, model.get('option.line') || {}, true);
85846            }
85847        },
85848        'bar': function (seriesType, seriesId, seriesModel, model) {
85849            if (seriesType === 'line') {
85850                return merge({
85851                    id: seriesId,
85852                    type: 'bar',
85853                    // Preserve data related option
85854                    data: seriesModel.get('data'),
85855                    stack: seriesModel.get('stack'),
85856                    markPoint: seriesModel.get('markPoint'),
85857                    markLine: seriesModel.get('markLine')
85858                }, model.get('option.bar') || {}, true);
85859            }
85860        },
85861        'stack': function (seriesType, seriesId, seriesModel, model) {
85862            if (seriesType === 'line' || seriesType === 'bar') {
85863                return merge({
85864                    id: seriesId,
85865                    stack: '__ec_magicType_stack__'
85866                }, model.get('option.stack') || {}, true);
85867            }
85868        },
85869        'tiled': function (seriesType, seriesId, seriesModel, model) {
85870            if (seriesType === 'line' || seriesType === 'bar') {
85871                return merge({
85872                    id: seriesId,
85873                    stack: ''
85874                }, model.get('option.tiled') || {}, true);
85875            }
85876        }
85877    };
85878
85879    var radioTypes = [
85880        ['line', 'bar'],
85881        ['stack', 'tiled']
85882    ];
85883
85884    proto$5.onclick = function (ecModel, api, type) {
85885        var model = this.model;
85886        var seriesIndex = model.get('seriesIndex.' + type);
85887        // Not supported magicType
85888        if (!seriesOptGenreator[type]) {
85889            return;
85890        }
85891        var newOption = {
85892            series: []
85893        };
85894        var generateNewSeriesTypes = function (seriesModel) {
85895            var seriesType = seriesModel.subType;
85896            var seriesId = seriesModel.id;
85897            var newSeriesOpt = seriesOptGenreator[type](
85898                seriesType, seriesId, seriesModel, model
85899            );
85900            if (newSeriesOpt) {
85901                // PENDING If merge original option?
85902                defaults(newSeriesOpt, seriesModel.option);
85903                newOption.series.push(newSeriesOpt);
85904            }
85905            // Modify boundaryGap
85906            var coordSys = seriesModel.coordinateSystem;
85907            if (coordSys && coordSys.type === 'cartesian2d' && (type === 'line' || type === 'bar')) {
85908                var categoryAxis = coordSys.getAxesByScale('ordinal')[0];
85909                if (categoryAxis) {
85910                    var axisDim = categoryAxis.dim;
85911                    var axisType = axisDim + 'Axis';
85912                    var axisModel = ecModel.queryComponents({
85913                        mainType: axisType,
85914                        index: seriesModel.get(name + 'Index'),
85915                        id: seriesModel.get(name + 'Id')
85916                    })[0];
85917                    var axisIndex = axisModel.componentIndex;
85918
85919                    newOption[axisType] = newOption[axisType] || [];
85920                    for (var i = 0; i <= axisIndex; i++) {
85921                        newOption[axisType][axisIndex] = newOption[axisType][axisIndex] || {};
85922                    }
85923                    newOption[axisType][axisIndex].boundaryGap = type === 'bar' ? true : false;
85924                }
85925            }
85926        };
85927
85928        each$1(radioTypes, function (radio) {
85929            if (indexOf(radio, type) >= 0) {
85930                each$1(radio, function (item) {
85931                    model.setIconStatus(item, 'normal');
85932                });
85933            }
85934        });
85935
85936        model.setIconStatus(type, 'emphasis');
85937
85938        ecModel.eachComponent(
85939            {
85940                mainType: 'series',
85941                query: seriesIndex == null ? null : {
85942                    seriesIndex: seriesIndex
85943                }
85944            }, generateNewSeriesTypes
85945        );
85946        api.dispatchAction({
85947            type: 'changeMagicType',
85948            currentType: type,
85949            newOption: newOption
85950        });
85951    };
85952
85953    registerAction({
85954        type: 'changeMagicType',
85955        event: 'magicTypeChanged',
85956        update: 'prepareAndUpdate'
85957    }, function (payload, ecModel) {
85958        ecModel.mergeOption(payload.newOption);
85959    });
85960
85961    register$1('magicType', MagicType);
85962
85963    /*
85964* Licensed to the Apache Software Foundation (ASF) under one
85965* or more contributor license agreements.  See the NOTICE file
85966* distributed with this work for additional information
85967* regarding copyright ownership.  The ASF licenses this file
85968* to you under the Apache License, Version 2.0 (the
85969* "License"); you may not use this file except in compliance
85970* with the License.  You may obtain a copy of the License at
85971*
85972*   http://www.apache.org/licenses/LICENSE-2.0
85973*
85974* Unless required by applicable law or agreed to in writing,
85975* software distributed under the License is distributed on an
85976* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
85977* KIND, either express or implied.  See the License for the
85978* specific language governing permissions and limitations
85979* under the License.
85980*/
85981
85982    var dataViewLang = lang.toolbox.dataView;
85983
85984    var BLOCK_SPLITER = new Array(60).join('-');
85985    var ITEM_SPLITER = '\t';
85986    /**
85987     * Group series into two types
85988     *  1. on category axis, like line, bar
85989     *  2. others, like scatter, pie
85990     * @param {module:echarts/model/Global} ecModel
85991     * @return {Object}
85992     * @inner
85993     */
85994    function groupSeries(ecModel) {
85995        var seriesGroupByCategoryAxis = {};
85996        var otherSeries = [];
85997        var meta = [];
85998        ecModel.eachRawSeries(function (seriesModel) {
85999            var coordSys = seriesModel.coordinateSystem;
86000
86001            if (coordSys && (coordSys.type === 'cartesian2d' || coordSys.type === 'polar')) {
86002                var baseAxis = coordSys.getBaseAxis();
86003                if (baseAxis.type === 'category') {
86004                    var key = baseAxis.dim + '_' + baseAxis.index;
86005                    if (!seriesGroupByCategoryAxis[key]) {
86006                        seriesGroupByCategoryAxis[key] = {
86007                            categoryAxis: baseAxis,
86008                            valueAxis: coordSys.getOtherAxis(baseAxis),
86009                            series: []
86010                        };
86011                        meta.push({
86012                            axisDim: baseAxis.dim,
86013                            axisIndex: baseAxis.index
86014                        });
86015                    }
86016                    seriesGroupByCategoryAxis[key].series.push(seriesM
86016odel);
86017                }
86018                else {
86019                    otherSeries.push(seriesModel);
86020                }
86021            }
86022            else {
86023                otherSeries.push(seriesModel);
86024            }
86025        });
86026
86027        return {
86028            seriesGroupByCategoryAxis: seriesGroupByCategoryAxis,
86029            other: otherSeries,
86030            meta: meta
86031        };
86032    }
86033
86034    /**
86035     * Assemble content of series on cateogory axis
86036     * @param {Array.<module:echarts/model/Series>} series
86037     * @return {string}
86038     * @inner
86039     */
86040    function assembleSeriesWithCategoryAxis(series) {
86041        var tables = [];
86042        each$1(series, function (group, key) {
86043            var categoryAxis = group.categoryAxis;
86044            var valueAxis = group.valueAxis;
86045            var valueAxisDim = valueAxis.dim;
86046
86047            var headers = [' '].concat(map(group.series, function (series) {
86048                return series.name;
86049            }));
86050            var columns = [categoryAxis.model.getCategories()];
86051            each$1(group.series, function (series) {
86052                columns.push(series.getRawData().mapArray(valueAxisDim, function (val) {
86053                    return val;
86054                }));
86055            });
86056            // Assemble table content
86057            var lines = [headers.join(ITEM_SPLITER)];
86058            for (var i = 0; i < columns[0].length; i++) {
86059                var items = [];
86060                for (var j = 0; j < columns.length; j++) {
86061                    items.push(columns[j][i]);
86062                }
86063                lines.push(items.join(ITEM_SPLITER));
86064            }
86065            tables.push(lines.join('\n'));
86066        });
86067        return tables.join('\n\n' +  BLOCK_SPLITER + '\n\n');
86068    }
86069
86070    /**
86071     * Assemble content of other series
86072     * @param {Array.<module:echarts/model/Series>} series
86073     * @return {string}
86074     * @inner
86075     */
86076    function assembleOtherSeries(series) {
86077        return map(series, function (series) {
86078            var data = series.getRawData();
86079            var lines = [series.name];
86080            var vals = [];
86081            data.each(data.dimensions, function () {
86082                var argLen = arguments.length;
86083                var dataIndex = arguments[argLen - 1];
86084                var name = data.getName(dataIndex);
86085                for (var i = 0; i < argLen - 1; i++) {
86086                    vals[i] = arguments[i];
86087                }
86088                lines.push((name ? (name + ITEM_SPLITER) : '') + vals.join(ITEM_SPLITER));
86089            });
86090            return lines.join('\n');
86091        }).join('\n\n' + BLOCK_SPLITER + '\n\n');
86092    }
86093
86094    /**
86095     * @param {module:echarts/model/Global}
86096     * @return {Object}
86097     * @inner
86098     */
86099    function getContentFromModel(ecModel) {
86100
86101        var result = groupSeries(ecModel);
86102
86103        return {
86104            value: filter([
86105                assembleSeriesWithCategoryAxis(result.seriesGroupByCategoryAxis),
86106                assembleOtherSeries(result.other)
86107            ], function (str) {
86108                return str.replace(/[\n\t\s]/g, '');
86109            }).join('\n\n' + BLOCK_SPLITER + '\n\n'),
86110
86111            meta: result.meta
86112        };
86113    }
86114
86115
86116    function trim$1(str) {
86117        return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
86118    }
86119    /**
86120     * If a block is tsv format
86121     */
86122    function isTSVFormat(block) {
86123        // Simple method to find out if a block is tsv format
86124        var firstLine = block.slice(0, block.indexOf('\n'));
86125        if (firstLine.indexOf(ITEM_SPLITER) >= 0) {
86126            return true;
86127        }
86128    }
86129
86130    var itemSplitRegex = new RegExp('[' + ITEM_SPLITER + ']+', 'g');
86131    /**
86132     * @param {string} tsv
86133     * @return {Object}
86134     */
86135    function parseTSVContents(tsv) {
86136        var tsvLines = tsv.split(/\n+/g);
86137        var headers = trim$1(tsvLines.shift()).split(itemSplitRegex);
86138
86139        var categories = [];
86140        var series = map(headers, function (header) {
86141            return {
86142                name: header,
86143                data: []
86144            };
86145        });
86146        for (var i = 0; i < tsvLines.length; i++) {
86147            var items = trim$1(tsvLines[i]).split(itemSplitRegex);
86148            categories.push(items.shift());
86149            for (var j = 0; j < items.length; j++) {
86150                series[j] && (series[j].data[i] = items[j]);
86151            }
86152        }
86153        return {
86154            series: series,
86155            categories: categories
86156        };
86157    }
86158
86159    /**
86160     * @param {string} str
86161     * @return {Array.<Object>}
86162     * @inner
86163     */
86164    function parseListContents(str) {
86165        var lines = str.split(/\n+/g);
86166        var seriesName = trim$1(lines.shift());
86167
86168        var data = [];
86169        for (var i = 0; i < lines.length; i++) {
86170            var items = trim$1(lines[i]).split(itemSplitRegex);
86171            var name = '';
86172            var value;
86173            var hasName = false;
86174            if (isNaN(items[0])) { // First item is name
86175                hasName = true;
86176                name = items[0];
86177                items = items.slice(1);
86178                data[i] = {
86179                    name: name,
86180                    value: []
86181                };
86182                value = data[i].value;
86183            }
86184            else {
86185                value = data[i] = [];
86186            }
86187            for (var j = 0; j < items.length; j++) {
86188                value.push(+items[j]);
86189            }
86190            if (value.length === 1) {
86191                hasName ? (data[i].value = value[0]) : (data[i] = value[0]);
86192            }
86193        }
86194
86195        return {
86196            name: seriesName,
86197            data: data
86198        };
86199    }
86200
86201    /**
86202     * @param {string} str
86203     * @param {Array.<Object>} blockMetaList
86204     * @return {Object}
86205     * @inner
86206     */
86207    function parseContents(str, blockMetaList) {
86208        var blocks = str.split(new RegExp('\n*' + BLOCK_SPLITER + '\n*', 'g'));
86209        var newOption = {
86210            series: []
86211        };
86212        each$1(blocks, function (block, idx) {
86213            if (isTSVFormat(block)) {
86214                var result = parseTSVContents(block);
86215                var blockMeta = blockMetaList[idx];
86216                var axisKey = blockMeta.axisDim + 'Axis';
86217
86218                if (blockMeta) {
86219                    newOption[axisKey] = newOption[axisKey] || [];
86220                    newOption[axisKey][blockMeta.axisIndex] = {
86221                        data: result.categories
86222                    };
86223                    newOption.series = newOption.series.concat(result.series);
86224                }
86225            }
86226            else {
86227                var result = parseListContents(block);
86228                newOption.series.push(result);
86229            }
86230        });
86231        return newOption;
86232    }
86233
86234    /**
86235     * @alias {module:echarts/component/toolbox/feature/DataView}
86236     * @constructor
86237     * @param {module:echarts/model/Model} model
86238     */
86239    function DataView(model) {
86240
86241        this._dom = null;
86242
86243        this.model = model;
86244    }
86245
86246    DataView.defaultOption = {
86247        show: true,
86248        readOnly: false,
86249        optionToContent: null,
86250        contentToOption: null,
86251
86252        icon: 'M17.5,17.3H33 M17.5,17.3H33 M45.4,29.5h-28 M11.5,2v56H51V14.8L38.4,2H11.5z M38.4,2.2v12.7H51 M45.4,41.7h-28',
86253        title: clone(dataViewLang.title),
86254        lang: clone(dataViewLang.lang),
86255        backgroundColor: '#fff',
86256        textColor: '#000',
86257        textareaColor: '#fff',
86258        textareaBorderColor: '#333',
86259        buttonColor: '#c23531',
86260        buttonTextColor: '#fff'
86261    };
86262
86263    DataView.prototype.onclick = function (ecModel, api) {
86264        var container = api.getDom();
86265        var model = this.model;
86266        if (this._dom) {
86267            container.removeChild(this._dom);
86268        }
86269        var root = document.createElement('div');
86270        root.style.cssText = 'position:absolute;left:5px;top:5px;bottom:5px;right:5px;';
86271        root.style.backgroundColor = model.get('backgroundColor') || '#fff';
86272
86273        // Create elements
86274        var header = document.createElement('h4');
86275        var lang$$1 = model.get('lang') || [];
86276        header.innerHTML = lang$$1[0] || model.get('title');
86277        header.style.cssText = 'margin: 10px 20px;';
86278        header.style.color = model.get('textColor');
86279
86280        var viewMain = document.createElement('div');
86281        var textarea = document.createElement('textarea');
86282        viewMain.style.cssText = 'display:block;width:100%;overflow:auto;';
86283
86284        var optionToContent = model.get('optionToContent');
86285        var contentToOption = model.get('contentToOption');
86286        var result = getContentFromModel(ecModel);
86287        if (typeof optionToContent === 'function') {
86288            var htmlOrDom = optionToContent(api.getOption());
86289            if (typeof htmlOrDom === 'string') {
86290                viewMain.innerHTML = htmlOrDom;
86291            }
86292            else if (isDom(htmlOrDom)) {
86293                viewMain.appendChild(htmlOrDom);
86294            }
86295        }
86296        else {
86297            // Use default textarea
86298            viewMain.appendChild(textarea);
86299            textarea.readOnly = model.get('readOnly');
86300            textarea.style.cssText = 'width:100%;height:100%;font-family:monospace;font-size:14px;line-height:1.6rem;';
86301            textarea.style.color = model.get('textColor');
86302            textarea.style.borderColor = model.get('textareaBorderColor');
86303            textarea.style.backgroundColor = model.get('textareaColor');
86304            textarea.value = result.value;
86305        }
86306
86307        var blockMetaList = result.meta;
86308
86309        var buttonContainer = document.createElement('div');
86310        buttonContainer.style.cssText = 'position:absolute;bottom:0;left:0;right:0;';
86311
86312        var buttonStyle = 'float:right;margin-right:20px;border:none;'
86313            + 'cursor:pointer;padding:2px 5px;font-size:12px;border-radius:3px';
86314        var closeButton = document.createElement('div');
86315        var refreshButton = document.createElement('div');
86316
86317        buttonStyle += ';background-color:' + model.get('buttonColor');
86318        buttonStyle += ';color:' + model.get('buttonTextColor');
86319
86320        var self = this;
86321
86322        function close() {
86323            container.removeChild(root);
86324            self._dom = null;
86325        }
86326        addEventListener(closeButton, 'click', close);
86327
86328        addEventListener(refreshButton, 'click', function () {
86329            var newOption;
86330            try {
86331                if (typeof contentToOption === 'function') {
86332                    newOption = contentToOption(viewMain, api.getOption());
86333                }
86334                else {
86335                    newOption = parseContents(textarea.value, blockMetaList);
86336                }
86337            }
86338            catch (e) {
86339                close();
86340                throw new Error('Data view format error ' + e);
86341            }
86342            if (newOption) {
86343                api.dispatchAction({
86344                    type: 'changeDataView',
86345                    newOption: newOption
86346                });
86347            }
86348
86349            close();
86350        });
86351
86352        closeButton.innerHTML = lang$$1[1];
86353        refreshButton.innerHTML = lang$$1[2];
86354        refreshButton.style.cssText = buttonStyle;
86355        closeButton.style.cssText = buttonStyle;
86356
86357        !model.get('readOnly') && buttonContainer.appendChild(refreshButton);
86358        buttonContainer.appendChild(closeButton);
86359
86360        // http://stackoverflow.com/questions/6637341/use-tab-to-indent-in-textarea
86361        addEventListener(textarea, 'keydown', function (e) {
86362            if ((e.keyCode || e.which) === 9) {
86363                // get caret position/selection
86364                var val = this.value;
86365                var start = this.selectionStart;
86366                var end = this.selectionEnd;
86367
86368                // set textarea value to: text before caret + tab + text after caret
86369                this.value = val.substring(0, start) + ITEM_SPLITER + val.substring(end);
86370
86371                // put caret at right position again
86372                this.selectionStart = this.selectionEnd = start + 1;
86373
86374                // prevent the focus lose
86375                stop(e);
86376            }
86377        });
86378
86379        root.appendChild(header);
86380        root.appendChild(viewMain);
86381        root.appendChild(buttonContainer);
86382
86383        viewMain.style.height = (container.clientHeight - 80) + 'px';
86384
86385        container.appendChild(root);
86386        this._dom = root;
86387    };
86388
86389    DataView.prototype.remove = function (ecModel, api) {
86390        this._dom && api.getDom().removeChild(this._dom);
86391    };
86392
86393    DataView.prototype.dispose = function (ecModel, api) {
86394        this.remove(ecModel, api);
86395    };
86396
86397    /**
86398     * @inner
86399     */
86400    function tryMergeDataOption(newData, originalData) {
86401        return map(newData, function (newVal, idx) {
86402            var original = originalData && originalData[idx];
86403            if (isObject$1(original) && !isArray(original)) {
86404                if (isObject$1(newVal) && !isArray(newVal)) {
86405                    newVal = newVal.value;
86406                }
86407                // Original data has option
86408                return defaults({
86409                    value: newVal
86410                }, original);
86411            }
86412            else {
86413                return newVal;
86414            }
86415        });
86416    }
86417
86418    register$1('dataView', DataView);
86419
86420    registerAction({
86421        type: 'changeDataView',
86422        event: 'dataViewChanged',
86423        update: 'prepareAndUpdate'
86424    }, function (payload, ecModel) {
86425        var newSeriesOptList = [];
86426        each$1(payload.newOption.series, function (seriesOpt) {
86427            var seriesModel = ecModel.getSeriesByName(seriesOpt.name)[0];
vendor: 4,168 bytes, lines 86428-86553
86428            if (!seriesModel) {
86429                // New created series
86430                // Geuss the series type
86431                newSeriesOptList.push(extend({
86432                    // Default is scatter
86433                    type: 'scatter'
86434                }, seriesOpt));
86435            }
86436            else {
86437                var originalData = seriesModel.get('data');
86438                newSeriesOptList.push({
86439                    name: seriesOpt.name,
86440                    data: tryMergeDataOption(seriesOpt.data, originalData)
86441                });
86442            }
86443        });
86444
86445        ecModel.mergeOption(defaults({
86446            series: newSeriesOptList
86447        }, payload.newOption));
86448    });
86449
86450    /*
86451* Licensed to the Apache Software Foundation (ASF) under one
86452* or more contributor license agreements.  See the NOTICE file
86453* distributed with this work for additional information
86454* regarding copyright ownership.  The ASF licenses this file
86455* to you under the Apache License, Version 2.0 (the
86456* "License"); you may not use this file except in compliance
86457* with the License.  You may obtain a copy of the License at
86458*
86459*   http://www.apache.org/licenses/LICENSE-2.0
86460*
86461* Unless required by applicable law or agreed to in writing,
86462* software distributed under the License is distributed on an
86463* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
86464* KIND, either express or implied.  See the License for the
86465* specific language governing permissions and limitations
86466* under the License.
86467*/
86468
86469    var each$29 = each$1;
86470
86471    var ATTR$2 = '\0_ec_hist_store';
86472
86473    /**
86474     * @param {module:echarts/model/Global} ecModel
86475     * @param {Object} newSnapshot {dataZoomId, batch: [payloadInfo, ...]}
86476     */
86477    function push(ecModel, newSnapshot) {
86478        var store = giveStore$1(ecModel);
86479
86480        // If previous dataZoom can not be found,
86481        // complete an range with current range.
86482        each$29(newSnapshot, function (batchItem, dataZoomId) {
86483            var i = store.length - 1;
86484            for (; i >= 0; i--) {
86485                var snapshot = store[i];
86486                if (snapshot[dataZoomId]) {
86487                    break;
86488                }
86489            }
86490            if (i < 0) {
86491                // No origin range set, create one by current range.
86492                var dataZoomModel = ecModel.queryComponents(
86493                    {mainType: 'dataZoom', subType: 'select', id: dataZoomId}
86494                )[0];
86495                if (dataZoomModel) {
86496                    var percentRange = dataZoomModel.getPercentRange();
86497                    store[0][dataZoomId] = {
86498                        dataZoomId: dataZoomId,
86499                        start: percentRange[0],
86500                        end: percentRange[1]
86501                    };
86502                }
86503            }
86504        });
86505
86506        store.push(newSnapshot);
86507    }
86508
86509    /**
86510     * @param {module:echarts/model/Global} ecModel
86511     * @return {Object} snapshot
86512     */
86513    function pop(ecModel) {
86514        var store = giveStore$1(ecModel);
86515        var head = store[store.length - 1];
86516        store.length > 1 && store.pop();
86517
86518        // Find top for all dataZoom.
86519        var snapshot = {};
86520        each$29(head, function (batchItem, dataZoomId) {
86521            for (var i = store.length - 1; i >= 0; i--) {
86522                var batchItem = store[i][dataZoomId];
86523                if (batchItem) {
86524                    snapshot[dataZoomId] = batchItem;
86525                    break;
86526                }
86527            }
86528        });
86529
86530        return snapshot;
86531    }
86532
86533    /**
86534     * @param {module:echarts/model/Global} ecModel
86535     */
86536    function clear$1(ecModel) {
86537        ecModel[ATTR$2] = null;
86538    }
86539
86540    /**
86541     * @param {module:echarts/model/Global} ecModel
86542     * @return {number} records. always >= 1.
86543     */
86544    function count(ecModel) {
86545        return giveStore$1(ecModel).length;
86546    }
86547
86548    /**
86549     * [{key: dataZoomId, value: {dataZoomId, range}}, ...]
86550     * History length of each dataZoom may be different.
86551     * this._history[0] is used to store origin range.
86552     * @type {Array.<Object>}
86553     */
86554    function giveStore$1(ecModel) {
86555        var store = ecModel[ATTR$2];
86556        if (!store) {
86557            store = ecModel[ATTR$2] = [{}];
86558        }
86559        return store;
86560    }
86561
86562    /*
86563* Licensed to the Apache Software Foundation (ASF) under one
86564* or more contributor license agreements.  See the NOTICE file
86565* distributed with this work for additional information
86566* regarding copyright ownership.  The ASF licenses this file
86567* to you under the Apache License, Version 2.0 (the
86568* "License"); you may not use this file except in compliance
86569* with the License.  You may obtain a copy of the License at
86570*
86571*   http://www.apache.org/licenses/LICENSE-2.0
86572*
86573* Unless required by applicable law or agreed to in writing,
86574* software distributed under the License is distributed on an
86575* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
86576* KIND, either express or implied.  See the License for the
86577* specific language governing permissions and limitations
86578* under the License.
86579*/
86580
86581    DataZoomModel.extend({
86582        type: 'dataZoom.select'
86583    });
86584
86585    /*
86586* Licensed to the Apache Software Foundation (ASF) under one
86587* or more contributor license agreements.  See the NOTICE file
86588* distributed with this work for additional information
86589* regarding copyright ownership.  The ASF licenses this file
86590* to you under the Apache License, Version 2.0 (the
86591* "License"); you may not use this file except in compliance
86592* with the License.  You may obtain a copy of the License at
86593*
86594*   http://www.apache.org/licenses/LICENSE-2.0
86595*
86596* Unless required by applicable law or agreed to in writing,
86597* software distributed under the License is distributed on an
86598* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
86599* KIND, either express or implied.  See the License for the
86600* specific language governing permissions and limitations
86601* under the License.
86602*/
86603
86604    DataZoomView.extend({
86605        type: 'dataZoom.select'
86606    });
86607
86608    /*
86609* Licensed to the Apache Software Foundation (ASF) under one
86610* or more contributor license agreements.  See the NOTICE file
86611* distributed with this work for additional information
86612* regarding copyright ownership.  The ASF licenses this file
86613* to you under the Apache License, Version 2.0 (the
86614* "License"); you may not use this file except in compliance
86615* with the License.  You may obtain a copy of the License at
86616*
86617*   http://www.apache.org/licenses/LICENSE-2.0
86618*
86619* Unless required by applicable law or agreed to in writing,
86620* software distributed under the License is distributed on an
86621* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
86622* KIND, either express or implied.  See the License for the
86623* specific language governing permissions and limitations
86624* under the License.
86625*/
86626
86627    /**
86628     * DataZoom component entry
86629     */
86630
86631    /*
86632* Licensed to the Apache Software Foundation (ASF) under one
86633* or more contributor license agreements.  See the NOTICE file
86634* distributed with this work for additional information
86635* regarding copyright ownership.  The ASF licenses this file
86636* to you under the Apache License, Version 2.0 (the
86637* "License"); you may not use this file except in compliance
86638* with the License.  You may obtain a copy of the License at
86639*
86640*   http://www.apache.org/licenses/LICENSE-2.0
86641*
86642* Unless required by applicable law or agreed to in writing,
86643* software distributed under the License is distributed on an
86644* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
86645* KIND, either express or implied.  See the License for the
86646* specific language governing permissions and limitations
86647* under the License.
86648*/
86649
86650// Use dataZoomSelect
86651    var dataZoomLang = lang.toolbox.dataZoom;
86652    var each$28 = each$1;
86653
86654// Spectial component id start with \0ec\0, see echarts/model/Global.js~hasInnerId
86655    var DATA_ZOOM_ID_BASE = '\0_ec_\0toolbox-dataZoom_';
86656
86657    function DataZoom(model, ecModel, api) {
86658
86659        /**
86660         * @private
86661         * @type {module:echarts/component/helper/BrushController}
86662         */
86663        (this._brushController = new BrushController(api.getZr()))
86664            .on('brush', bind(this._onBrush, this))
86665            .mount();
86666
86667        /**
86668         * @private
86669         * @type {boolean}
86670         */
86671        this._isZoomActive;
86672    }
86673
86674    DataZoom.defaultOption = {
86675        show: true,
86676        // Icon group
86677        icon: {
86678            zoom: 'M0,13.5h26.9 M13.5,26.9V0 M32.1,13.5H58V58H13.5 V32.1',
86679            back: 'M22,1.4L9.9,13.5l12.3,12.3 M10.3,13.5H54.9v44.6 H10.3v-26'
86680        },
86681        // `zoom`, `back`
86682        title: clone(dataZoomLang.title)
86683    };
86684
86685    var proto$6 = DataZoom.prototype;
86686
86687    proto$6.render = function (featureModel, ecModel, api, payload) {
86688        this.model = featureModel;
86689        this.ecModel = ecModel;
86690        this.api = api;
86691
86692        updateZoomBtnStatus(featureModel, ecModel, this, payload, api);
86693        updateBackBtnStatus(featureModel, ecModel);
86694    };
86695
86696    proto$6.onclick = function (ecModel, api, type) {
86697        handlers$1[type].call(this);
86698    };
86699
86700    proto$6.remove = function (ecModel, api) {
86701        this._brushController.unmount();
86702    };
86703
86704    proto$6.dispose = function (ecModel, api) {
86705        this._brushController.dispose();
86706    };
86707
86708    /**
86709     * @private
86710     */
86711    var handlers$1 = {
86712
86713        zoom: function () {
86714            var nextActive = !this._isZoomActive;
86715
86716            this.api.dispatchAction({
86717                type: 'takeGlobalCursor',
86718                key: 'dataZoomSelect',
86719                dataZoomSelectActive: nextActive
86720            });
86721        },
86722
86723        back: function () {
86724            this._dispatchZoomAction(pop(this.ecModel));
86725        }
86726    };
86727
86728    /**
86729     * @private
86730     */
86731    proto$6._onBrush = function (areas, opt) {
86732        if (!opt.isEnd || !areas.length) {
86733            return;
86734        }
86735        var snapshot = {};
86736        var ecModel = this.ecModel;
86737
86738        this._brushController.updateCovers([]); // remove cover
86739
86740        var brushTargetManager = new BrushTargetManager(
86741            retrieveAxisSetting(this.model.option), ecModel, {include: ['grid']}
86742        );
86743        brushTargetManager.matchOutputRanges(areas, ecModel, function (area, coordRange, coordSys) {
86744            if (coordSys.type !== 'cartesian2d') {
86745                return;
86746            }
86747
86748            var brushType = area.brushType;
86749            if (brushType === 'rect') {
86750                setBatch('x', coordSys, coordRange[0]);
86751                setBatch('y', coordSys, coordRange[1]);
86752            }
86753            else {
86754                setBatch(({lineX: 'x', lineY: 'y'})[brushType], coordSys, coordRange);
86755            }
86756        });
86757
86758        push(ecModel, snapshot);
86759
86760        this._dispatchZoomAction(snapshot);
86761
86762        function setBatch(dimName, coordSys, minMax) {
86763            var axis = coordSys.getAxis(dimName);
86764            var axisModel = axis.model;
86765            var dataZoomModel = findDataZoom(dimName, axisModel, ecModel);
86766
86767            // Restrict range.
86768            var minMaxSpan = dataZoomModel.findRepresentativeAxisProxy(axisModel).getMinMaxSpan();
86769            if (minMaxSpan.minValueSpan != null || minMaxSpan.maxValueSpan != null) {
86770                minMax = sliderMove(
86771                    0, minMax.slice(), axis.scale.getExtent(), 0,
86772                    minMaxSpan.minValueSpan, minMaxSpan.maxValueSpan
86773                );
86774            }
86775
86776            dataZoomModel && (snapshot[dataZoomModel.id] = {
86777                dataZoomId: dataZoomModel.id,
86778                startValue: minMax[0],
86779                endValue: minMax[1]
86780            });
86781        }
86782
86783        function findDataZoom(dimName, axisModel, ecModel) {
86784            var found;
86785            ecModel.eachComponent({mainType: 'dataZoom', subType: 'select'}, function (dzModel) {
86786                var has = dzModel.getAxisModel(dimName, axisModel.componentIndex);
86787                has && (found = dzModel);
86788            });
86789            return found;
86790        }
86791    };
86792
86793    /**
86794     * @private
86795     */
86796    proto$6._dispatchZoomAction = function (snapshot) {
86797        var batch = [];
86798
86799        // Convert from hash map to array.
86800        each$28(snapshot, function (batchItem, dataZoomId) {
86801            batch.push(clone(batchItem));
86802        });
86803
86804        batch.length && this.api.dispatchAction({
86805            type: 'dataZoom',
86806            from: this.uid,
86807            batch: batch
86808        });
86809    };
86810
86811    function retrieveAxisSetting(option) {
86812        var setting = {};
86813        // Compatible with previous setting: null => all axis, false => no axis.
86814        each$1(['xAxisIndex', 'yAxisIndex'], function (name) {
86815            setting[name] = option[name];
86816            setting[name] == null && (setting[name] = 'all');
86817            (setting[name] === false || setting[name] === 'none') && (setting[name] = []);
86818        });
86819        return setting;
86820    }
86821
86822    function updateBackBtnStatus(featureModel, ecModel) {
86823        featureModel.setIconStatus(
86824            'back',
86825            count(ecModel) > 1 ? 'emphasis' : 'normal'
86826        );
86827    }
86828
86829    function updateZoomBtnStatus(featureModel, ecModel, view, payload, api) {
86830        var zoomActive = view._isZoomActive;
86831
86832        if (payload && payload.type === 'takeGlobalCursor') {
86833            zoomActive = payload.key === 'dataZoomSelect'
86834                ? payload.dataZoomSelectActive : false;
86835        }
86836
86837        view._isZoomActive = zoomActive;
86838
86839        featureModel.setIconStatus('zoom', zoomActive ? 'emphasis' : 'normal');
86840
86841        var brushTargetManager = new BrushTargetManager(
86842            retrieveAxisSetting(featureModel.option), ecModel, {include: ['grid']}
86843        );
86844
86845        view._brushController
86846            .setPanels(brushTargetManager.makePanelOpts(api, function (targetInfo) {
86847                return (targetInfo.xAxisDeclared && !targetInfo.yAxisDeclared)
86848                    ? 'lineX'
86849                    : (!targetInfo.xAxisDeclared && targetInfo.yAxisDeclared)
86850                        ? 'lineY'
86851                        : 'rect';
86852            }))
86853            .enableBrush(
86854                zoomActive
86855                    ? {
86856                        brushType: 'auto',
86857                        brushStyle: {
86858                            // FIXME user customized?
86859                            lineWidth: 0,
86860                            fill: 'rgba(0,0,0,0.2)'
86861                        }
86862                    }
86863                    : false
86864            );
86865    }
86866
86867
86868    register$1('dataZoom', DataZoom);
86869
86870
86871// Create special dataZoom option for select
86872// FIXME consider the case of merge option, where axes options are not exists.
86873    registerPreprocessor(function (option) {
86874        if (!option) {
86875            return;
86876        }
86877
86878        var dataZoomOpts = option.dataZoom || (option.dataZoom = []);
86879        if (!isArray(dataZoomOpts)) {
86880            option.dataZoom = dataZoomOpts = [dataZoomOpts];
86881        }
86882
86883        var toolboxOpt = option.toolbox;
86884        if (toolboxOpt) {
86885            // Assume there is only one toolbox
86886            if (isArray(toolboxOpt)) {
86887                toolboxOpt = toolboxOpt[0];
86888            }
86889
86890            if (toolboxOpt && toolboxOpt.feature) {
86891                var dataZoomOpt = toolboxOpt.feature.dataZoom;
86892                // FIXME: If add dataZoom when setOption in merge mode,
86893                // no axis info to be added. See `test/dataZoom-extreme.html`
86894                addForAxis('xAxis', dataZoomOpt);
86895                addForAxis('yAxis', dataZoomOpt);
86896            }
86897        }
86898
86899        function addForAxis(axisName, dataZoomOpt) {
86900            if (!dataZoomOpt) {
86901                return;
86902            }
86903
86904            // Try not to modify model, because it is not merged yet.
86905            var axisIndicesName = axisName + 'Index';
86906            var givenAxisIndices = dataZoomOpt[axisIndicesName];
86907            if (givenAxisIndices != null
86908                && givenAxisIndices != 'all'
86909                && !isArray(givenAxisIndices)
86910            ) {
86911                givenAxisIndices = (givenAxisIndices === false || givenAxisIndices === 'none') ? [] : [givenAxisIndices];
86912            }
86913
86914            forEachComponent(axisName, function (axisOpt, axisIndex) {
86915                if (givenAxisIndices != null
86916                    && givenAxisIndices != 'all'
86917                    && indexOf(givenAxisIndices, axisIndex) === -1
86918                ) {
86919                    return;
86920                }
86921                var newOpt = {
86922                    type: 'select',
86923                    $fromToolbox: true,
86924                    // Id for merge mapping.
86925                    id: DATA_ZOOM_ID_BASE + axisName + axisIndex
86926                };
86927                // FIXME
86928                // Only support one axis now.
86929                newOpt[axisIndicesName] = axisIndex;
86930                dataZoomOpts.push(newOpt);
86931            });
86932        }
86933
86934        function forEachComponent(mainType, cb) {
86935            var opts = option[mainType];
86936            if (!isArray(opts)) {
86937                opts = opts ? [opts] : [];
86938            }
86939            each$28(opts, cb);
86940        }
86941    });
86942
86943    /*
86944* Licensed to the Apache Software Foundation (ASF) under one
86945* or more contributor license agreements.  See the NOTICE file
86946* distributed with this work for additional information
86947* regarding copyright ownership.  The ASF licenses this file
86948* to you under the Apache License, Version 2.0 (the
86949* "License"); you may not use this file except in compliance
86950* with the License.  You may obtain a copy of the License at
86951*
86952*   http://www.apache.org/licenses/LICENSE-2.0
86953*
86954* Unless required by applicable law or agreed to in writing,
86955* software distributed under the License is distributed on an
86956* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
86957* KIND, either express or implied.  See the License for the
86958* specific language governing permissions and limitations
86959* under the License.
86960*/
86961
86962    var restoreLang = lang.toolbox.restore;
86963
86964    function Restore(model) {
86965        this.model = model;
86966    }
86967
86968    Restore.defaultOption = {
86969        show: true,
86970        icon: 'M3.8,33.4 M47,18.9h9.8V8.7 M56.3,20.1 C52.1,9,40.5,0.6,26.8,2.1C12.6,3.7,1.6,16.2,2.1,30.6 M13,41.1H3.1v10.2 M3.7,39.9c4.2,11.1,15.8,19.5,29.5,18 c14.2-1.6,25.2-14.1,24.7-28.5',
86971        title: restoreLang.title
86972    };
86973
86974    var proto$7 = Restore.prototype;
86975
86976    proto$7.onclick = function (ecModel, api, type) {
86977        clear$1(ecModel);
86978
86979        api.dispatchAction({
86980            type: 'restore',
86981            from: this.uid
86982        });
86983    };
86984
86985    register$1('restore', Restore);
86986
86987    registerAction(
86988        {type: 'restore', event: 'restore', update: 'prepareAndUpdate'},
86989        function (payload, ecModel) {
86990            ecModel.resetOption('recreate');
86991        }
86992    );
86993
86994    /*
86995* Licensed to the Apache Software Foundation (ASF) under one
86996* or more contributor license agreements.  See the NOTICE file
86997* distributed with this work for additional information
86998* regarding copyright ownership.  The ASF licenses this file
86999* to you under the Apache License, Version 2.0 (the
87000* "License"); you may not use this file except in compliance
87001* with the License.  You may obtain a copy of the License at
87002*
87003*   http://www.apache.org/licenses/LICENSE-2.0
87004*
87005* Unless required by applicable law or agreed to in writing,
87006* software distributed under the License is distributed on an
87007* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
87008* KIND, either express or implied.  See the License for the
87009* specific language governing permissions and limitations
87010* under the License.
87011*/
87012
87013    var urn = 'urn:schemas-microsoft-com:vml';
87014    var win = typeof window === 'undefined' ? null : window;
87015
87016    var vmlInited = false;
87017
87018    var doc = win && win.document;
87019
87020    function createNode(tagName) {
87021        return doCreateNode(tagName);
87022    }
87023
87024// Avoid assign to an exported variable, for transforming to cjs.
87025    var doCreateNode;
87026
87027    if (doc && !env$1.canvasSupported) {
87028        try {
87029            !doc.namespaces.zrvml && doc.namespaces.add('zrvml', urn);
87030            doCreateNode = function (tagName) {
87031                return doc.createElement('<zrvml:' + tagName + ' class="zrvml">');
87032            };
87033        }
87034        catch (e) {
87035            doCreateNode = function (tagName) {
87036                return doc.createElement('<' + tagName + ' xmlns="' + urn + '" class="zrvml">');
87037            };
87038        }
87039    }
87040
87041// From raphael
87042    function initVML() {
87043        if (vmlInited || !doc) {
87044            return;
87045        }
87046        vmlInited = true;
87047
87048        var styleSheets = doc.styleSheets;
87049        if (styleSheets.length < 31) {
87050            doc.createStyleSheet().addRule('.zrvml', 'behavior:url(#default#VML)');
87051        }
87052        else {
87053            // http://msdn.microsoft.com/en-us/library/ms531194%28VS.85%29.aspx
87054            styleSheets[0].addRule('.zrvml', 'behavior:url(#default#VML)');
87055        }
87056    }
87057
87058// http://www.w3.org/TR/NOTE-VML
87059// TODO Use proxy like svg instead of overwrite brush methods
87060
87061    var CMD$3 = PathProxy.CMD;
87062    var round$3 = Math.round;
87063    var sqrt = Math.sqrt;
87064    var abs$1 = Math.abs;
87065    var cos = Math.cos;
87066    var sin = Math.sin;
87067    var mathMax$8 = Math.max;
87068
87069    if (!env$1.canvasSupported) {
87070
87071        var comma = ',';
87072        var imageTransformPrefix = 'progid:DXImageTransform.Microsoft';
87073
87074        var Z = 21600;
87075        var Z2 = Z / 2;
87076
87077        var ZLEVEL_BASE = 100000;
87078        var Z_BASE$1 = 1000;
87079
87080        var initRootElStyle = function (el) {
87081            el.style.cssText = 'position:absolute;left:0;top:0;width:1px;height:1px;';
87082            el.coordsize = Z + ','  + Z;
87083            el.coordorigin = '0,0';
87084        };
87085
87086        var encodeHtmlAttribute = function (s) {
87087            return String(s).replace(/&/g, '&amp;').replace(/"/g, '&quot;');
87088        };
87089
87090        var rgb2Str = function (r, g, b) {
87091            return 'rgb(' + [r, g, b].join(',') + ')';
87092        };
87093
87094        var append = function (parent, child) {
87095            if (child && parent && child.parentNode !== parent) {
87096                parent.appendChild(child);
87097            }
87098        };
87099
87100        var remove = function (parent, child) {
87101            if (child && parent && child.parentNode === parent) {
87102                parent.removeChild(child);
87103            }
87104        };
87105
87106        var getZIndex = function (zlevel, z, z2) {
87107            // z 的取值范围为 [0, 1000]
87108            return (parseFloat(zlevel) || 0) * ZLEVEL_BASE + (parseFloat(z) || 0) * Z_BASE$1 + z2;
87109        };
87110
87111        var parsePercent$3 = function (value, maxValue) {
87112            if (typeof value === 'string') {
87113                if (value.lastIndexOf('%') >= 0) {
87114                    return parseFloat(value) / 100 * maxValue;
87115                }
87116                return parseFloat(value);
87117            }
87118            return value;
87119        };
87120
87121        /***************************************************
87122         * PATH
87123         **************************************************/
87124
87125        var setColorAndOpacity = function (el, color, opacity) {
87126            var colorArr = parse(color);
87127            opacity = +opacity;
87128            if (isNaN(opacity)) {
87129                opacity = 1;
87130            }
87131            if (colorArr) {
87132                el.color = rgb2Str(colorArr[0], colorArr[1], colorArr[2]);
87133                el.opacity = opacity * colorArr[3];
87134            }
87135        };
87136
87137        var getColorAndAlpha = function (color) {
87138            var colorArr = parse(color);
87139            return [
87140                rgb2Str(colorArr[0], colorArr[1], colorArr[2]),
87141                colorArr[3]
87142            ];
87143        };
87144
87145        var updateFillNode = function (el, style, zrEl) {
87146            // TODO pattern
87147            var fill = style.fill;
87148            if (fill != null) {
87149                // Modified from excanvas
87150                if (fill instanceof Gradient) {
87151                    var gradientType;
87152                    var angle = 0;
87153                    var focus = [0, 0];
87154                    // additional offset
87155                    var shift = 0;
87156                    // scale factor for offset
87157                    var expansion = 1;
87158                    var rect = zrEl.getBoundingRect();
87159                    var rectWidth = rect.width;
87160                    var rectHeight = rect.height;
87161                    if (fill.type === 'linear') {
87162                        gradientType = 'gradient';
87163                        var transform = zrEl.transform;
87164                        var p0 = [fill.x * rectWidth, fill.y * rectHeight];
87165                        var p1 = [fill.x2 * rectWidth, fill.y2 * rectHeight];
87166                        if (transform) {
87167                            applyTransform(p0, p0, transform);
87168                            applyTransform(p1, p1, transform);
87169                        }
87170                        var dx = p1[0] - p0[0];
87171                        var dy = p1[1] - p0[1];
87172                        angle = Math.atan2(dx, dy) * 180 / Math.PI;
87173                        // The angle should be a non-negative number.
87174                        if (angle < 0) {
87175                            angle += 360;
87176                        }
87177
87178                        // Very small angles produce an unexpected result because they are
87179                        // converted to a scientific notation string.
87180                        if (angle < 1e-6) {
87181                            angle = 0;
87182                        }
87183                    }
87184                    else {
87185                        gradientType = 'gradientradial';
87186                        var p0 = [fill.x * rectWidth, fill.y * rectHeight];
87187                        var transform = zrEl.transform;
87188                        var scale$$1 = zrEl.scale;
87189                        var width = rectWidth;
87190                        var height = rectHeight;
87191                        focus = [
87192                            // Percent in bounding rect
87193                            (p0[0] - rect.x) / width,
87194                            (p0[1] - rect.y) / height
87195                        ];
87196                        if (transform) {
87197                            applyTransform(p0, p0, transform);
87198                        }
87199
87200                        width /= scale$$1[0] * Z;
87201                        height /= scale$$1[1] * Z;
87202                        var dimension = mathMax$8(width, height);
87203                        shift = 2 * 0 / dimension;
87204                        expansion = 2 * fill.r / dimension - shift;
87205                    }
87206
87207                    // We need to sort the color stops in ascending order by offset,
87208                    // otherwise IE won't interpret it correctly.
87209                    var stops = fill.colorStops.slice();
87210                    stops.sort(function(cs1, cs2) {
87211                        return cs1.offset - cs2.offset;
87212                    });
87213
87214                    var length$$1 = stops.length;
87215                    // Color and alpha list of first and last stop
87216                    var colorAndAlphaList = [];
87217                    var colors = [];
87218                    for (var i = 0; i < length$$1; i++) {
87219                        var stop = stops[i];
87220                        var colorAndAlpha = getColorAndAlpha(stop.color);
87221                        colors.push(stop.offset * expansion + shift + ' ' + colorAndAlpha[0]);
87222                        if (i === 0 || i === length$$1 - 1) {
87223                            colorAndAlphaList.push(colorAndAlpha);
87224                        }
87225                    }
87226
87227                    if (length$$1 >= 2) {
87228                        var color1 = colorAndAlphaList[0][0];
87229                        var color2 = colorAndAlphaList[1][0];
87230                        var opacity1 = colorAndAlphaList[0][1] * style.opacity;
87231                        var opacity2 = colorAndAlphaList[1][1] * style.opacity;
87232
87233                        el.type = gradientType;
87234                        el.method = 'none';
87235                        el.focus = '100%';
87236                        el.angle = angle;
87237                        el.color = color1;
87238                        el.color2 = color2;
87239                        el.colors = colors.join(',');
87240                        // When colors attribute is used, the meanings of opacity and o:opacity2
87241                        // are reversed.
87242                        el.opacity = opacity2;
87243                        // FIXME g_o_:opacity ?
87244                        el.opacity2 = opacity1;
87245                    }
87246                    if (gradientType === 'radial') {
87247                        el.focusposition = focus.join(',');
87248                    }
87249                }
87250                else {
87251                    // FIXME Change from Gradient fill to color fill
87252                    setColorAndOpacity(el, fill, style.opacity);
87253                }
87254            }
87255        };
87256
87257        var updateStrokeNode = function (el, style) {
87258            // if (style.lineJoin != null) {
87259            //     el.joinstyle = style.lineJoin;
87260            // }
87261            // if (style.miterLimit != null) {
87262            //     el.miterlimit = style.miterLimit * Z;
87263            // }
87264            // if (style.lineCap != null) {
87265            //     el.endcap = style.lineCap;
87266            // }
87267            if (style.lineDash != null) {
87268                el.dashstyle = style.lineDash.join(' ');
87269            }
87270            if (style.stroke != null && !(style.stroke instanceof Gradient)) {
87271                setColorAndOpacity(el, style.stroke, style.opacity);
87272            }
87273        };
87274
87275        var updateFillAndStroke = function (vmlEl, type, style, zrEl) {
87276            var isFill = type == 'fill';
87277            var el = vmlEl.getElementsByTagName(type)[0];
87278            // Stroke must have lineWidth
87279            if (style[type] != null && style[type] !== 'none' && (isFill || (!isFill && style.lineWidth))) {
87280                vmlEl[isFill ? 'filled' : 'stroked'] = 'true';
87281                // FIXME Remove before updating, or set `colors` will throw error
87282                if (style[type] instanceof Gradient) {
87283                    remove(vmlEl, el);
87284                }
87285                if (!el) {
87286                    el = createNode(type);
87287                }
87288
87289                isFill ? updateFillNode(el, style, zrEl) : updateStrokeNode(el, style);
87290                append(vmlEl, el);
87291            }
87292            else {
87293                vmlEl[isFill ? 'filled' : 'stroked'] = 'false';
87294                remove(vmlEl, el);
87295            }
87296        };
87297
87298        var points$3 = [[], [], []];
87299        var pathDataToString = function (path, m) {
87300            var M = CMD$3.M;
87301            var C = CMD$3.C;
87302            var L = CMD$3.L;
87303            var A = CMD$3.A;
87304            var Q = CMD$3.Q;
87305
87306            var str = [];
87307            var nPoint;
87308            var cmdStr;
87309            var cmd;
87310            var i;
87311            var xi;
87312            var yi;
87313            var data = path.data;
87314            var dataLength = path.len();
87315            for (i = 0; i < dataLength;) {
87316                cmd = data[i++];
87317                cmdStr = '';
87318                nPoint = 0;
87319                switch (cmd) {
87320                    case M:
87321                        cmdStr = ' m ';
87322                        nPoint = 1;
87323                        xi = data[i++];
87324                        yi = data[i++];
87325                        points$3[0][0] = xi;
87326                        points$3[0][1] = yi;
87327                        break;
87328                    case L:
87329                        cmdStr = ' l ';
87330                        nPoint = 1;
87331                        xi = data[i++];
87332                        yi = data[i++];
87333                        points$3[0][0] = xi;
87334                        points$3[0][1] = yi;
87335                        break;
87336                    case Q:
87337                    case C:
87338                        cmdStr = ' c ';
87339                        nPoint = 3;
87340                        var x1 = data[i++];
87341                        var y1 = data[i++];
87342                        var x2 = data[i++];
87343                        var y2 = data[i++];
87344                        var x3;
87345                        var y3;
87346                        if (cmd === Q) {
87347                            // Convert quadratic to cubic using degree elevation
87348                            x3 = x2;
87349                            y3 = y2;
87350                            x2 = (x2 + 2 * x1) / 3;
87351                            y2 = (y2 + 2 * y1) / 3;
87352                            x1 = (xi + 2 * x1) / 3;
87353                            y1 = (yi + 2 * y1) / 3;
87354                        }
87355                        else {
87356                            x3 = data[i++];
87357                            y3 = data[i++];
87358                        }
87359                        points$3[0][0] = x1;
87360                        points$3[0][1] = y1;
87361                        points$3[1][0] = x2;
87362                        points$3[1][1] = y2;
87363                        points$3[2][0] = x3;
87364                        points$3[2][1] = y3;
87365
87366                        xi = x3;
87367                        yi = y3;
87368                        break;
87369                    case A:
87370                        var x = 0;
87371                        var y = 0;
87372                        var sx = 1;
87373                        var sy = 1;
87374                        var angle = 0;
87375                        if (m) {
87376                            // Extract SRT from matrix
87377                            x = m[4];
87378                            y = m[5];
87379                            sx = sqrt(m[0] * m[0] + m[1] * m[1]);
87380                            sy = sqrt(m[2] * m[2] + m[3] * m[3]);
87381                            angle = Math.atan2(-m[1] / sy, m[0] / sx);
87382                        }
87383
87384                        var cx = data[i++];
87385                        var cy = data[i++];
87386                        var rx = data[i++];
87387                        var ry = data[i++];
87388                        var startAngle = data[i++] + angle;
87389                        var endAngle = data[i++] + startAngle + angle;
87390                        // FIXME
87391                        // var psi = data[i++];
87392                        i++;
87393                        var clockwise = data[i++];
87394
87395                        var x0 = cx + cos(startAngle) * rx;
87396                        var y0 = cy + sin(startAngle) * ry;
87397
87398                        var x1 = cx + cos(endAngle) * rx;
87399                        var y1 = cy + sin(endAngle) * ry;
87400
87401                        var type = clockwise ? ' wa ' : ' at ';
87402                        if (Math.abs(x0 - x1) < 1e-4) {
87403                            // IE won't render arches drawn counter clockwise if x0 == x1.
87404                            if (Math.abs(endAngle - startAngle) > 1e-2) {
87405                                // Offset x0 by 1/80 of a pixel. Use something
87406                                // that can be represented in binary
87407                                if (clockwise) {
87408                                    x0 += 270 / Z;
87409                                }
87410                            }
87411                            else {
87412                                // Avoid case draw full circle
87413                                if (Math.abs(y0 - cy) < 1e-4) {
87414                                    if ((clockwise && x0 < cx) || (!clockwise && x0 > cx)) {
87415                                        y1 -= 270 / Z;
87416                                    }
87417                                    else {
87418                                        y1 += 270 / Z;
87419                                    }
87420                                }
87421                                else if ((clockwise && y0 < cy) || (!clockwise && y0 > cy)) {
87422                                    x1 += 270 / Z;
87423                                }
87424                                else {
87425                                    x1 -= 270 / Z;
87426                                }
87427                            }
87428                        }
87429                        str.push(
87430                            type,
87431                            round$3(((cx - rx) * sx + x) * Z - Z2), comma,
87432                            round$3(((cy - ry) * sy + y) * Z - Z2), comma,
87433                            round$3(((cx + rx) * sx + x) * Z - Z2), comma,
87434                            round$3(((cy + ry) * sy + y) * Z - Z2), comma,
87435                            round$3((x0 * sx + x) * Z - Z2), comma,
87436                            round$3((y0 * sy + y) * Z - Z2), comma,
87437                            round$3((x1 * sx + x) * Z - Z2), comma,
87438                            round$3((y1 * sy + y) * Z - Z2)
87439                        );
87440
87441                        xi = x1;
87442                        yi = y1;
87443                        break;
87444                    case CMD$3.R:
87445                        var p0 = points$3[0];
87446                        var p1 = points$3[1];
87447                        // x0, y0
87448                        p0[0] = data[i++];
87449                        p0[1] = data[i++];
87450                        // x1, y1
87451                        p1[0] = p0[0] + data[i++];
87452                        p1[1] = p0[1] + data[i++];
87453
87454                        if (m) {
87455                            applyTransform(p0, p0, m);
87456                            applyTransform(p1, p1, m);
87457                        }
87458
87459                        p0[0] = round$3(p0[0] * Z - Z2);
87460                        p1[0] = round$3(p1[0] * Z - Z2);
87461                        p0[1] = round$3(p0[1] * Z - Z2);
87462                        p1[1] = round$3(p1[1] * Z - Z2);
87463                        str.push(
87464                            // x0, y0
87465                            ' m ', p0[0], comma, p0[1],
87466                            // x1, y0
87467                            ' l ', p1[0], comma, p0[1],
87468                            // x1, y1
87469                            ' l ', p1[0], comma, p1[1],
87470                            // x0, y1
87471                            ' l ', p0[0], comma, p1[1]
87472                        );
87473                        break;
87474                    case CMD$3.Z:
87475                        // FIXME Update xi, yi
87476                        str.push(' x ');
87477                }
87478
87479                if (nPoint > 0) {
87480                    str.push(cmdStr);
87481                    for (var k = 0; k < nPoint; k++) {
87482                        var p = points$3[k];
87483
87484                        m && applyTransform(p, p, m);
87485                        // 不 round 会非常慢
87486                        str.push(
87487                            round$3(p[0] * Z - Z2), comma, round$3(p[1] * Z - Z2),
87488                            k < nPoint - 1 ? comma : ''
87489                        );
87490                    }
87491                }
87492            }
87493
87494            return str.join('');
87495        };
87496
87497        // Rewrite the original path method
87498        Path.prototype.brushVML = function (vmlRoot) {
87499            var style = this.style;
87500
87501            var vmlEl = this._vmlEl;
87502            if (!vmlEl) {
87503                vmlEl = createNode('shape');
87504                initRootElStyle(vmlEl);
87505
87506                this._vmlEl = vmlEl;
87507            }
87508
87509            updateFillAndStroke(vmlEl, 'fill', style, this);
87510            updateFillAndStroke(vmlEl, 'stroke', style, this);
87511
87512            var m = this.transform;
87513            var needTransform = m != null;
87514            var strokeEl = vmlEl.getElementsByTagName('stroke')[0];
87515            if (strokeEl) {
87516                var lineWidth = style.lineWidth;
87517                // Get the line scale.
87518                // Determinant of this.m_ means how much the area is enlarged by the
87519                // transformation. So its square root can be used as a scale factor
87520                // for width.
87521                if (needTransform && !style.strokeNoScale) {
87522                    var det = m[0] * m[3] - m[1] * m[2];
87523                    lineWidth *= sqrt(abs$1(det));
87524                }
87525                strokeEl.weight = lineWidth + 'px';
87526            }
87527
87528            var path = this.path || (this.path = new PathProxy());
87529            if (this.__dirtyPath) {
87530                path.beginPath();
87531                this.buildPath(path, this.shape);
87532                path.toStatic();
87533                this.__dirtyPath = false;
87534            }
87535
87536            vmlEl.path = pathDataToString(path, this.transform);
87537
87538            vmlEl.style.zIndex = getZIndex(this.zlevel, this.z, this.z2);
87539
87540            // Append to root
87541            append(vmlRoot, vmlEl);
87542
87543            // Text
87544            if (style.text != null) {
87545                this.drawRectText(vmlRoot, this.getBoundingRect());
87546            }
87547            else {
87548                this.removeRectText(vmlRoot);
87549            }
87550        };
87551
87552        Path.prototype.onRemove = function (vmlRoot) {
87553            remove(vmlRoot, this._vmlEl);
87554            this.removeRectText(vmlRoot);
87555        };
87556
87557        Path.prototype.onAdd = function (vmlRoot) {
87558            append(vmlRoot, this._vmlEl);
87559            this.appendRectText(vmlRoot);
87560        };
87561
87562        /***************************************************
87563         * IMAGE
87564         **************************************************/
87565        var isImage = function (img) {
87566            // FIXME img instanceof Image 如果 img 是一个字符串的时候,IE8 下会报错
87567            return (typeof img === 'object') && img.tagName && img.tagName.toUpperCase() === 'IMG';
87568            // return img instanceof Image;
87569        };
87570
87571        // Rewrite the original path method
87572        ZImage.prototype.brushVML = function (vmlRoot) {
87573            var style = this.style;
87574            var image = style.image;
87575
87576            // Image original width, height
87577            var ow;
87578            var oh;
87579
87580            if (isImage(image)) {
87581                var src = image.src;
87582                if (src === this._imageSrc) {
87583                    ow = this._imageWidth;
87584                    oh = this._imageHeight;
87585                }
87586                else {
87587                    var imageRuntimeStyle = image.runtimeStyle;
87588                    var oldRuntimeWidth = imageRuntimeStyle.width;
87589                    var oldRuntimeHeight = imageRuntimeStyle.height;
87590                    imageRuntimeStyle.width = 'auto';
87591                    imageRuntimeStyle.height = 'auto';
87592
87593                    // get the original size
87594                    ow = image.width;
87595                    oh = image.height;
87596
87597                    // and remove overides
87598                    imageRuntimeStyle.width = oldRuntimeWidth;
87599                    imageRuntimeStyle.height = oldRuntimeHeight;
87600
87601                    // Caching image original width, height and src
87602                    this._imageSrc = src;
87603                    this._imageWidth = ow;
87604                    this._imageHeight = oh;
87605                }
87606                image = src;
87607            }
87608            else {
87609                if (image === this._imageSrc) {
87610                    ow = this._imageWidth;
87611                    oh = this._imageHeight;
87612                }
87613            }
87614            if (!image) {
87615                return;
87616            }
87617
87618            var x = style.x || 0;
87619            var y = style.y || 0;
87620
87621            var dw = style.width;
87622            var dh = style.height;
87623
87624            var sw = style.sWidth;
87625            var sh = style.sHeight;
87626            var sx = style.sx || 0;
87627            var sy = style.sy || 0;
87628
87629            var hasCrop = sw && sh;
87630
87631            var vmlEl = this._vmlEl;
87632            if (!vmlEl) {
87633                // FIXME 使用 group 在 left, top 都不是 0 çš„æ—¶å€™å°±æ— æ³•æ˜¾ç¤ºäº†ã€‚
87634                // vmlEl = vmlCore.createNode('group');
87635                vmlEl = doc.createElement('div');
87636                initRootElStyle(vmlEl);
87637
87638                this._vmlEl = vmlEl;
87639            }
87640
87641            var vmlElStyle = vmlEl.style;
87642            var hasRotation = false;
87643            var m;
87644            var scaleX = 1;
87645            var scaleY = 1;
87646            if (this.transform) {
87647                m = this.transform;
87648                scaleX = sqrt(m[0] * m[0] + m[1] * m[1]);
87649                scaleY = sqrt(m[2] * m[2] + m[3] * m[3]);
87650
87651                hasRotation = m[1] || m[2];
87652            }
87653            if (hasRotation) {
87654                // If filters are necessary (rotation exists), create them
87655                // filters are bog-slow, so only create them if abbsolutely necessary
87656                // The following check doesn't account for skews (which don't exist
87657                // in the canvas spec (yet) anyway.
87658                // From excanvas
87659                var p0 = [x, y];
87660                var p1 = [x + dw, y];
87661                var p2 = [x, y + dh];
87662                var p3 = [x + dw, y + dh];
87663                applyTransform(p0, p0, m);
87664                applyTransform(p1, p1, m);
87665                applyTransform(p2, p2, m);
87666                applyTransform(p3, p3, m);
87667
87668                var maxX = mathMax$8(p0[0], p1[0], p2[0], p3[0]);
87669                var maxY = mathMax$8(p0[1], p1[1], p2[1], p3[1]);
87670
87671                var transformFilter = [];
87672                transformFilter.push('M11=', m[0] / scaleX, comma,
87673                    'M12=', m[2] / scaleY, comma,
87674                    'M21=', m[1] / scaleX, comma,
87675                    'M22=', m[3] / scaleY, comma,
87676                    'Dx=', round$3(x * scaleX + m[4]), comma,
87677                    'Dy=', round$3(y * scaleY + m[5]));
87678
87679                vmlElStyle.padding = '0 ' + round$3(maxX) + 'px ' + round$3(maxY) + 'px 0';
87680                // FIXME DXImageTransform 在 IE11 的兼容模式下不起作用
87681                vmlElStyle.filter = imageTransformPrefix + '.Matrix('
87682                    + transformFilter.join('') + ', SizingMethod=clip)';
87683
87684            }
87685            else {
87686                if (m) {
87687                    x = x * scaleX + m[4];
87688                    y = y * scaleY + m[5];
87689                }
87690                vmlElStyle.filter = '';
87691                vmlElStyle.left = round$3(x) + 'px';
87692                vmlElStyle.top = round$3(y) + 'px';
87693            }
87694
87695            var imageEl = this._imageEl;
87696            var cropEl = this._cropEl;
87697
87698            if (!imageEl) {
87699                imageEl = doc.createElement('div');
87700                this._imageEl = imageEl;
87701            }
87702            var imageELStyle = imageEl.style;
87703            if (hasCrop) {
87704                // Needs know image original width and height
87705                if (! (ow && oh)) {
87706                    var tmpImage = new Image();
87707                    var self = this;
87708                    tmpImage.onload = function () {
87709                        tmpImage.onload = null;
87710                        ow = tmpImage.width;
87711                        oh = tmpImage.height;
87712                        // Adjust image width and height to fit the ratio destinationSize / sourceSize
87713                        imageELStyle.width = round$3(scaleX * ow * dw / sw) + 'px';
87714                        imageELStyle.height = round$3(scaleY * oh * dh / sh) + 'px';
87715
87716                        // Caching image original width, height and src
87717                        self._imageWidth = ow;
87718                        self._imageHeight = oh;
87719                        self._imageSrc = image;
87720                    };
87721                    tmpImage.src = image;
87722                }
87723                else {
87724                    imageELStyle.width = round$3(scaleX * ow * dw / sw) + 'px';
87725                    imageELStyle.height = round$3(scaleY * oh * dh / sh) + 'px';
87726                }
87727
87728                if (! cropEl) {
87729                    cropEl = doc.createElement('div');
87730                    cropEl.style.overflow = 'hidden';
87731                    this._cropEl = cropEl;
87732                }
87733                var cropElStyle = cropEl.style;
87734                cropElStyle.width = round$3((dw + sx * dw / sw) * 
87734scaleX);
87735                cropElStyle.height = round$3((dh + sy * dh / sh) * scaleY);
87736                cropElStyle.filter = imageTransformPrefix + '.Matrix(Dx='
87737                    + (-sx * dw / sw * scaleX) + ',Dy=' + (-sy * dh / sh * scaleY) + ')';
87738
87739                if (! cropEl.parentNode) {
87740                    vmlEl.appendChild(cropEl);
87741                }
87742                if (imageEl.parentNode != cropEl) {
87743                    cropEl.appendChild(imageEl);
87744                }
87745            }
87746            else {
87747                imageELStyle.width = round$3(scaleX * dw) + 'px';
87748                imageELStyle.height = round$3(scaleY * dh) + 'px';
87749
87750                vmlEl.appendChild(imageEl);
87751
87752                if (cropEl && cropEl.parentNode) {
87753                    vmlEl.removeChild(cropEl);
87754                    this._cropEl = null;
87755                }
87756            }
87757
87758            var filterStr = '';
87759            var alpha = style.opacity;
87760            if (alpha < 1) {
87761                filterStr += '.Alpha(opacity=' + round$3(alpha * 100) + ') ';
87762            }
87763            filterStr += imageTransformPrefix + '.AlphaImageLoader(src=' + image + ', SizingMethod=scale)';
87764
87765            imageELStyle.filter = filterStr;
87766
87767            vmlEl.style.zIndex = getZIndex(this.zlevel, this.z, this.z2);
87768
87769            // Append to root
87770            append(vmlRoot, vmlEl);
87771
87772            // Text
87773            if (style.text != null) {
87774                this.drawRectText(vmlRoot, this.getBoundingRect());
87775            }
87776        };
87777
87778        ZImage.prototype.onRemove = function (vmlRoot) {
87779            remove(vmlRoot, this._vmlEl);
87780
87781            this._vmlEl = null;
87782            this._cropEl = null;
87783            this._imageEl = null;
87784
87785            this.removeRectText(vmlRoot);
87786        };
87787
87788        ZImage.prototype.onAdd = function (vmlRoot) {
87789            append(vmlRoot, this._vmlEl);
87790            this.appendRectText(vmlRoot);
87791        };
87792
87793
87794        /***************************************************
87795         * TEXT
87796         **************************************************/
87797
87798        var DEFAULT_STYLE_NORMAL = 'normal';
87799
87800        var fontStyleCache = {};
87801        var fontStyleCacheCount = 0;
87802        var MAX_FONT_CACHE_SIZE = 100;
87803        var fontEl = document.createElement('div');
87804
87805        var getFontStyle = function (fontString) {
87806            var fontStyle = fontStyleCache[fontString];
87807            if (!fontStyle) {
87808                // Clear cache
87809                if (fontStyleCacheCount > MAX_FONT_CACHE_SIZE) {
87810                    fontStyleCacheCount = 0;
87811                    fontStyleCache = {};
87812                }
87813
87814                var style = fontEl.style;
87815                var fontFamily;
87816                try {
87817                    style.font = fontString;
87818                    fontFamily = style.fontFamily.split(',')[0];
87819                }
87820                catch (e) {
87821                }
87822
87823                fontStyle = {
87824                    style: style.fontStyle || DEFAULT_STYLE_NORMAL,
87825                    variant: style.fontVariant || DEFAULT_STYLE_NORMAL,
87826                    weight: style.fontWeight || DEFAULT_STYLE_NORMAL,
87827                    size: parseFloat(style.fontSize || 12) | 0,
87828                    family: fontFamily || 'Microsoft YaHei'
87829                };
87830
87831                fontStyleCache[fontString] = fontStyle;
87832                fontStyleCacheCount++;
87833            }
87834            return fontStyle;
87835        };
87836
87837        var textMeasureEl;
87838        // Overwrite measure text method
87839        $override$1('measureText', function (text, textFont) {
87840            var doc$$1 = doc;
87841            if (!textMeasureEl) {
87842                textMeasureEl = doc$$1.createElement('div');
87843                textMeasureEl.style.cssText = 'position:absolute;top:-20000px;left:0;'
87844                    + 'padding:0;margin:0;border:none;white-space:pre;';
87845                doc.body.appendChild(textMeasureEl);
87846            }
87847
87848            try {
87849                textMeasureEl.style.font = textFont;
87850            } catch (ex) {
87851                // Ignore failures to set to invalid font.
87852            }
87853            textMeasureEl.innerHTML = '';
87854            // Don't use innerHTML or innerText because they allow markup/whitespace.
87855            textMeasureEl.appendChild(doc$$1.createTextNode(text));
87856            return {
87857                width: textMeasureEl.offsetWidth
87858            };
87859        });
87860
87861        var tmpRect$2 = new BoundingRect();
87862
87863        var drawRectText = function (vmlRoot, rect, textRect, fromTextEl) {
87864
87865            var style = this.style;
87866
87867            // Optimize, avoid normalize every time.
87868            this.__dirty && normalizeTextStyle(style, true);
87869
87870            var text = style.text;
87871            // Convert to string
87872            text != null && (text += '');
87873            if (!text) {
87874                return;
87875            }
87876
87877            // Convert rich text to plain text. Rich text is not supported in
87878            // IE8-, but tags in rich text template will be removed.
87879            if (style.rich) {
87880                var contentBlock = parseRichText(text, style);
87881                text = [];
87882                for (var i = 0; i < contentBlock.lines.length; i++) {
87883                    var tokens = contentBlock.lines[i].tokens;
87884                    var textLine = [];
87885                    for (var j = 0; j < tokens.length; j++) {
87886                        textLine.push(tokens[j].text);
87887                    }
87888                    text.push(textLine.join(''));
87889                }
87890                text = text.join('\n');
87891            }
87892
87893            var x;
87894            var y;
87895            var align = style.textAlign;
87896            var verticalAlign = style.textVerticalAlign;
87897
87898            var fontStyle = getFontStyle(style.font);
87899            // FIXME encodeHtmlAttribute ?
87900            var font = fontStyle.style + ' ' + fontStyle.variant + ' ' + fontStyle.weight + ' '
87901                + fontStyle.size + 'px "' + fontStyle.family + '"';
87902
87903            textRect = textRect || getBoundingRect(text, font, align, verticalAlign);
87904
87905            // Transform rect to view space
87906            var m = this.transform;
87907            // Ignore transform for text in other element
87908            if (m && !fromTextEl) {
87909                tmpRect$2.copy(rect);
87910                tmpRect$2.applyTransform(m);
87911                rect = tmpRect$2;
87912            }
87913
87914            if (!fromTextEl) {
87915                var textPosition = style.textPosition;
87916                var distance$$1 = style.textDistance;
87917                // Text position represented by coord
87918                if (textPosition instanceof Array) {
87919                    x = rect.x + parsePercent$3(textPosition[0], rect.width);
87920                    y = rect.y + parsePercent$3(textPosition[1], rect.height);
87921
87922                    align = align || 'left';
87923                }
87924                else {
87925                    var res = adjustTextPositionOnRect(
87926                        textPosition, rect, distance$$1
87927                    );
87928                    x = res.x;
87929                    y = res.y;
87930
87931                    // Default align and baseline when has textPosition
87932                    align = align || res.textAlign;
87933                    verticalAlign = verticalAlign || res.textVerticalAlign;
87934                }
87935            }
87936            else {
87937                x = rect.x;
87938                y = rect.y;
87939            }
87940
87941            x = adjustTextX(x, textRect.width, align);
87942            y = adjustTextY(y, textRect.height, verticalAlign);
87943
87944            // Force baseline 'middle'
87945            y += textRect.height / 2;
87946
87947            // var fontSize = fontStyle.size;
87948            // 1.75 is an arbitrary number, as there is no info about the text baseline
87949            // switch (baseline) {
87950            // case 'hanging':
87951            // case 'top':
87952            //     y += fontSize / 1.75;
87953            //     break;
87954            //     case 'middle':
87955            //         break;
87956            //     default:
87957            //     // case null:
87958            //     // case 'alphabetic':
87959            //     // case 'ideographic':
87960            //     // case 'bottom':
87961            //         y -= fontSize / 2.25;
87962            //         break;
87963            // }
87964
87965            // switch (align) {
87966            //     case 'left':
87967            //         break;
87968            //     case 'center':
87969            //         x -= textRect.width / 2;
87970            //         break;
87971            //     case 'right':
87972            //         x -= textRect.width;
87973            //         break;
87974            // case 'end':
87975            // align = elementStyle.direction == 'ltr' ? 'right' : 'left';
87976            // break;
87977            // case 'start':
87978            // align = elementStyle.direction == 'rtl' ? 'right' : 'left';
87979            // break;
87980            // default:
87981            //     align = 'left';
87982            // }
87983
87984            var createNode$$1 = createNode;
87985
87986            var textVmlEl = this._textVmlEl;
87987            var pathEl;
87988            var textPathEl;
87989            var skewEl;
87990            if (!textVmlEl) {
87991                textVmlEl = createNode$$1('line');
87992                pathEl = createNode$$1('path');
87993                textPathEl = createNode$$1('textpath');
87994                skewEl = createNode$$1('skew');
87995
87996                // FIXME Why here is not cammel case
87997                // Align 'center' seems wrong
87998                textPathEl.style['v-text-align'] = 'left';
87999
88000                initRootElStyle(textVmlEl);
88001
88002                pathEl.textpathok = true;
88003                textPathEl.on = true;
88004
88005                textVmlEl.from = '0 0';
88006                textVmlEl.to = '1000 0.05';
88007
88008                append(textVmlEl, skewEl);
88009                append(textVmlEl, pathEl);
88010                append(textVmlEl, textPathEl);
88011
88012                this._textVmlEl = textVmlEl;
88013            }
88014            else {
88015                // 这里是在前面 appendChild 保证顺序的前提下
88016                skewEl = textVmlEl.firstChild;
88017                pathEl = skewEl.nextSibling;
88018                textPathEl = pathEl.nextSibling;
88019            }
88020
88021            var coords = [x, y];
88022            var textVmlElStyle = textVmlEl.style;
88023            // Ignore transform for text in other element
88024            if (m && fromTextEl) {
88025                applyTransform(coords, coords, m);
88026
88027                skewEl.on = true;
88028
88029                skewEl.matrix = m[0].toFixed(3) + comma + m[2].toFixed(3) + comma +
88030                    m[1].toFixed(3) + comma + m[3].toFixed(3) + ',0,0';
88031
88032                // Text position
88033                skewEl.offset = (round$3(coords[0]) || 0) + ',' + (round$3(coords[1]) || 0);
88034                // Left top point as origin
88035                skewEl.origin = '0 0';
88036
88037                textVmlElStyle.left = '0px';
88038                textVmlElStyle.top = '0px';
88039            }
88040            else {
88041                skewEl.on = false;
88042                textVmlElStyle.left = round$3(x) + 'px';
88043                textVmlElStyle.top = round$3(y) + 'px';
88044            }
88045
88046            textPathEl.string = encodeHtmlAttribute(text);
88047            // TODO
88048            try {
88049                textPathEl.style.font = font;
88050            }
88051                // Error font format
88052            catch (e) {}
88053
88054            updateFillAndStroke(textVmlEl, 'fill', {
88055                fill: style.textFill,
88056                opacity: style.opacity
88057            }, this);
88058            updateFillAndStroke(textVmlEl, 'stroke', {
88059                stroke: style.textStroke,
88060                opacity: style.opacity,
88061                lineDash: style.lineDash
88062            }, this);
88063
88064            textVmlEl.style.zIndex = getZIndex(this.zlevel, this.z, this.z2);
88065
88066            // Attached to root
88067            append(vmlRoot, textVmlEl);
88068        };
88069
88070        var removeRectText = function (vmlRoot) {
88071            remove(vmlRoot, this._textVmlEl);
88072            this._textVmlEl = null;
88073        };
88074
88075        var appendRectText = function (vmlRoot) {
88076            append(vmlRoot, this._textVmlEl);
88077        };
88078
88079        var list = [RectText, Displayable, ZImage, Path, Text];
88080
88081        // In case Displayable has been mixed in RectText
88082        for (var i$3 = 0; i$3 < list.length; i$3++) {
88083            var proto$8 = list[i$3].prototype;
88084            proto$8.drawRectText = drawRectText;
88085            proto$8.removeRectText = removeRectText;
88086            proto$8.appendRectText = appendRectText;
88087        }
88088
88089        Text.prototype.brushVML = function (vmlRoot) {
88090            var style = this.style;
88091            if (style.text != null) {
88092                this.drawRectText(vmlRoot, {
88093                    x: style.x || 0, y: style.y || 0,
88094                    width: 0, height: 0
88095                }, this.getBoundingRect(), true);
88096            }
88097            else {
88098                this.removeRectText(vmlRoot);
88099            }
88100        };
88101
88102        Text.prototype.onRemove = function (vmlRoot) {
88103            this.removeRectText(vmlRoot);
88104        };
88105
88106        Text.prototype.onAdd = function (vmlRoot) {
88107            this.appendRectText(vmlRoot);
88108        };
88109    }
88110
88111    /**
88112     * VML Painter.
88113     *
88114     * @module zrender/vml/Painter
88115     */
88116
88117    function parseInt10$1(val) {
88118        return parseInt(val, 10);
88119    }
88120
88121    /**
88122     * @alias module:zrender/vml/Painter
88123     */
88124    function VMLPainter(root, storage) {
88125
88126        initVML();
88127
88128        this.root = root;
88129
88130        this.storage = storage;
88131
88132        var vmlViewport = document.createElement('div');
88133
88134        var vmlRoot = document.createElement('div');
88135
88136        vmlViewport.style.cssText = 'display:inline-block;overflow:hidden;position:relative;width:300px;height:150px;';
88137
88138        vmlRoot.style.cssText = 'position:absolute;left:0;top:0;';
88139
88140        root.appendChild(vmlViewport);
88141
88142        this._vmlRoot = vmlRoot;
88143        this._vmlViewport = vmlViewport;
88144
88145        this.resize();
88146
88147        // Modify storage
88148        var oldDelFromStorage = storage.delFromStorage;
88149        var oldAddToStorage = storage.addToStorage;
88150        storage.delFromStorage = function (el) {
88151            oldDelFromStorage.call(storage, el);
88152
88153            if (el) {
88154                el.onRemove && el.onRemove(vmlRoot);
88155            }
88156        };
88157
88158        storage.addToStorage = function (el) {
88159            // Displayable already has a vml node
88160            el.onAdd && el.onAdd(vmlRoot);
88161
88162            oldAddToStorage.call(storage, el);
88163        };
88164
88165        this._firstPaint = true;
88166    }
88167
88168    VMLPainter.prototype = {
88169
88170        constructor: VMLPainter,
88171
88172        getType: function () {
88173            return 'vml';
88174        },
88175
88176        /**
88177         * @return {HTMLDivElement}
88178         */
88179        getViewportRoot: function () {
88180            return this._vmlViewport;
88181        },
88182
88183        getViewportRootOffset: function () {
88184            var viewportRoot = this.getViewportRoot();
88185            if (viewportRoot) {
88186                return {
88187                    offsetLeft: viewportRoot.offsetLeft || 0,
88188                    offsetTop: viewportRoot.offsetTop || 0
88189                };
88190            }
88191        },
88192
88193        /**
88194         * 刷新
88195         */
88196        refresh: function () {
88197
88198            var list = this.storage.getDisplayList(true, true);
88199
88200            this._paintList(list);
88201        },
88202
88203        _paintList: function (list) {
88204            var vmlRoot = this._vmlRoot;
88205            for (var i = 0; i < list.length; i++) {
88206                var el = list[i];
88207                if (el.invisible || el.ignore) {
88208                    if (!el.__alreadyNotVisible) {
88209                        el.onRemove(vmlRoot);
88210                    }
88211                    // Set as already invisible
88212                    el.__alreadyNotVisible = true;
88213                }
88214                else {
88215                    if (el.__alreadyNotVisible) {
88216                        el.onAdd(vmlRoot);
88217                    }
88218                    el.__alreadyNotVisible = false;
88219                    if (el.__dirty) {
88220                        el.beforeBrush && el.beforeBrush();
88221                        (el.brushVML || el.brush).call(el, vmlRoot);
88222                        el.afterBrush && el.afterBrush();
88223                    }
88224                }
88225                el.__dirty = false;
88226            }
88227
88228            if (this._firstPaint) {
88229                // Detached from document at first time
88230                // to avoid page refreshing too many times
88231
88232                // FIXME 如果每次都先 removeChild 可能会导致一些填充和描边的效果改变
88233                this._vmlViewport.appendChild(vmlRoot);
88234                this._firstPaint = false;
88235            }
88236        },
88237
88238        resize: function (width, height) {
88239            var width = width == null ? this._getWidth() : width;
88240            var height = height == null ? this._getHeight() : height;
88241
88242            if (this._width != width || this._height != height) {
88243                this._width = width;
88244                this._height = height;
88245
88246                var vmlViewportStyle = this._vmlViewport.style;
88247                vmlViewportStyle.width = width + 'px';
88248                vmlViewportStyle.height = height + 'px';
88249            }
88250        },
88251
88252        dispose: function () {
88253            this.root.innerHTML = '';
88254
88255            this._vmlRoot =
88256                this._vmlViewport =
88257                    this.storage = null;
88258        },
88259
88260        getWidth: function () {
88261            return this._width;
88262        },
88263
88264        getHeight: function () {
88265            return this._height;
88266        },
88267
88268        clear: function () {
88269            if (this._vmlViewport) {
88270                this.root.removeChild(this._vmlViewport);
88271            }
88272        },
88273
88274        _getWidth: function () {
88275            var root = this.root;
88276            var stl = root.currentStyle;
88277
88278            return ((root.clientWidth || parseInt10$1(stl.width))
88279                - parseInt10$1(stl.paddingLeft)
88280                - parseInt10$1(stl.paddingRight)) | 0;
88281        },
88282
88283        _getHeight: function () {
88284            var root = this.root;
88285            var stl = root.currentStyle;
88286
88287            return ((root.clientHeight || parseInt10$1(stl.height))
88288                - parseInt10$1(stl.paddingTop)
88289                - parseInt10$1(stl.paddingBottom)) | 0;
88290        }
88291    };
88292
88293// Not supported methods
88294    function createMethodNotSupport(method) {
88295        return function () {
88296            zrLog('In IE8.0 VML mode painter not support method "' + method + '"');
88297        };
88298    }
88299
88300// Unsupported methods
88301    each$1([
88302        'getLayer', 'insertLayer', 'eachLayer', 'eachBuiltinLayer', 'eachOtherLayer', 'getLayers',
88303        'modLayer', 'delLayer', 'clearLayer', 'toDataURL', 'pathToImage'
88304    ], function (name) {
88305        VMLPainter.prototype[name] = createMethodNotSupport(name);
88306    });
88307
88308    registerPainter('vml', VMLPainter);
88309
88310    var svgURI = 'http://www.w3.org/2000/svg';
88311
88312    function createElement(name) {
88313        return document.createElementNS(svgURI, name);
88314    }
88315
88316// TODO
88317// 1. shadow
88318// 2. Image: sx, sy, sw, sh
88319
88320    var CMD$4 = PathProxy.CMD;
88321    var arrayJoin = Array.prototype.join;
88322
88323    var NONE = 'none';
88324    var mathRound = Math.round;
88325    var mathSin$3 = Math.sin;
88326    var mathCos$3 = Math.cos;
88327    var PI$5 = Math.PI;
88328    var PI2$7 = Math.PI * 2;
88329    var degree = 180 / PI$5;
88330
88331    var EPSILON$4 = 1e-4;
88332
88333    function round4(val) {
88334        return mathRound(val * 1e4) / 1e4;
88335    }
88336
88337    function isAroundZero$1(val) {
88338        return val < EPSILON$4 && val > -EPSILON$4;
88339    }
88340
88341    function pathHasFill(style, isText) {
88342        var fill = isText ? style.textFill : style.fill;
88343        return fill != null && fill !== NONE;
88344    }
88345
88346    function pathHasStroke(style, isText) {
88347        var stroke = isText ? style.textStroke : style.stroke;
88348        return stroke != null && stroke !== NONE;
88349    }
88350
88351    function setTransform(svgEl, m) {
88352        if (m) {
88353            attr(svgEl, 'transform', 'matrix(' + arrayJoin.call(m, ',') + ')');
88354        }
88355    }
88356
88357    function attr(el, key, val) {
88358        if (!val || val.type !== 'linear' && val.type !== 'radial') {
88359            // Don't set attribute for gradient, since it need new dom nodes
88360            if (typeof val === 'string' && val.indexOf('NaN') > -1) {
88361                console.log(val);
88362            }
88363            el.setAttribute(key, val);
88364        }
88365    }
88366
88367    function attrXLink(el, key, val) {
88368        el.setAttributeNS('http://www.w3.org/1999/xlink', key, val);
88369    }
88370
88371    function bindStyle(svgEl, style, isText) {
88372        if (pathHasFill(style, isText)) {
88373            var fill = isText ? style.textFill : style.fill;
88374            fill = fill === 'transparent' ? NONE : fill;
88375
88376            /**
88377             * FIXME:
88378             * This is a temporary fix for Chrome's clipping bug
88379             * that happens when a clip-path is referring another one.
88380             * This fix should be used before Chrome's bug is fixed.
88381             * For an element that has clip-path, and fill is none,
88382             * set it to be "rgba(0, 0, 0, 0.002)" will hide the element.
88383             * Otherwise, it will show black fill color.
88384             * 0.002 is used because this won't work for alpha values smaller
88385             * than 0.002.
88386             *
88387             * See
88388             * https://bugs.chromium.org/p/chromium/issues/detail?id=659790
88389             * for more information.
88390             */
88391            if (svgEl.getAttribute('clip-path') !== 'none' && fill === NONE) {
88392                fill = 'rgba(0, 0, 0, 0.002)';
88393            }
88394
88395            attr(svgEl, 'fill', fill);
88396            attr(svgEl, 'fill-opacity', style.opacity);
88397        }
88398        else {
88399            attr(svgEl, 'fill', NONE);
88400        }
88401
88402        if (pathHasStroke(style, isText)) {
88403            var stroke = isText ? style.textStroke : style.stroke;
88404            stroke = stroke === 'transparent' ? NONE : stroke;
88405            attr(svgEl, 'stroke', stroke);
88406            var strokeWidth = isText
88407                ? style.textStrokeWidth
88408                : style.lineWidth;
88409            var strokeScale = !isText && style.strokeNoScale
88410                ? style.host.getLineScale()
88411                : 1;
88412            attr(svgEl, 'stroke-width', strokeWidth / strokeScale);
88413            // stroke then fill for text; fill then stroke for others
88414            attr(svgEl, 'paint-order', isText ? 'stroke' : 'fill');
88415            attr(svgEl, 'stroke-opacity', style.opacity);
88416            var lineDash = style.lineDash;
88417            if (lineDash) {
88418                attr(svgEl, 'stroke-dasharray', style.lineDash.join(','));
88419                attr(svgEl, 'stroke-dashoffset', mathRound(style.lineDashOffset || 0));
88420            }
88421            else {
88422                attr(svgEl, 'stroke-dasharray', '');
88423            }
88424
88425            // PENDING
88426            style.lineCap && attr(svgEl, 'stroke-linecap', style.lineCap);
88427            style.lineJoin && attr(svgEl, 'stroke-linejoin', style.lineJoin);
88428            style.miterLimit && attr(svgEl, 'stroke-miterlimit', style.miterLimit);
88429        }
88430        else {
88431            attr(svgEl, 'stroke', NONE);
88432        }
88433    }
88434
88435    /***************************************************
88436     * PATH
88437     **************************************************/
88438    function pathDataToString$1(path) {
88439        var str = [];
88440        var data = path.data;
88441        var dataLength = path.len();
88442        for (var i = 0; i < dataLength;) {
88443            var cmd = data[i++];
88444            var cmdStr = '';
88445            var nData = 0;
88446            switch (cmd) {
88447                case CMD$4.M:
88448                    cmdStr = 'M';
88449                    nData = 2;
88450                    break;
88451                case CMD$4.L:
88452                    cmdStr = 'L';
88453                    nData = 2;
88454                    break;
88455                case CMD$4.Q:
88456                    cmdStr = 'Q';
88457                    nData = 4;
88458                    break;
88459                case CMD$4.C:
88460                    cmdStr = 'C';
88461                    nData = 6;
88462                    break;
88463                case CMD$4.A:
88464                    var cx = data[i++];
88465                    var cy = data[i++];
88466                    var rx = data[i++];
88467                    var ry = data[i++];
88468                    var theta = data[i++];
88469                    var dTheta = data[i++];
88470                    var psi = data[i++];
88471                    var clockwise = data[i++];
88472
88473                    var dThetaPositive = Math.abs(dTheta);
88474                    var isCircle = isAroundZero$1(dThetaPositive - PI2$7)
88475                        && !isAroundZero$1(dThetaPositive);
88476
88477                    var large = false;
88478                    if (dThetaPositive >= PI2$7) {
88479                        large = true;
88480                    }
88481                    else if (isAroundZero$1(dThetaPositive)) {
88482                        large = false;
88483                    }
88484                    else {
88485                        large = (dTheta > -PI$5 && dTheta < 0 || dTheta > PI$5)
88486                            === !!clockwise;
88487                    }
88488
88489                    var x0 = round4(cx + rx * mathCos$3(theta));
88490                    var y0 = round4(cy + ry * mathSin$3(theta));
88491
88492                    // It will not draw if start point and end point are exactly the same
88493                    // We need to shift the end point with a small value
88494                    // FIXME A better way to draw circle ?
88495                    if (isCircle) {
88496                        if (clockwise) {
88497                            dTheta = PI2$7 - 1e-4;
88498                        }
88499                        else {
88500                            dTheta = -PI2$7 + 1e-4;
88501                        }
88502
88503                        large = true;
88504
88505                        if (i === 9) {
88506                            // Move to (x0, y0) only when CMD.A comes at the
88507                            // first position of a shape.
88508                            // For instance, when drawing a ring, CMD.A comes
88509                            // after CMD.M, so it's unnecessary to move to
88510                            // (x0, y0).
88511                            str.push('M', x0, y0);
88512                        }
88513                    }
88514
88515                    var x = round4(cx + rx * mathCos$3(theta + dTheta));
88516                    var y = round4(cy + ry * mathSin$3(theta + dTheta));
88517
88518                    // FIXME Ellipse
88519                    str.push('A', round4(rx), round4(ry),
88520                        mathRound(psi * degree), +large, +clockwise, x, y);
88521                    break;
88522                case CMD$4.Z:
88523                    cmdStr = 'Z';
88524                    break;
88525                case CMD$4.R:
88526                    var x = round4(data[i++]);
88527                    var y = round4(data[i++]);
88528                    var w = round4(data[i++]);
88529                    var h = round4(data[i++]);
88530                    str.push(
88531                        'M', x, y,
88532                        'L', x + w, y,
88533                        'L', x + w, y + h,
88534                        'L', x, y + h,
88535                        'L', x, y
88536                    );
88537                    break;
88538            }
88539            cmdStr && str.push(cmdStr);
88540            for (var j = 0; j < nData; j++) {
88541                // PENDING With scale
88542                str.push(round4(data[i++]));
88543            }
88544        }
88545        return str.join(' ');
88546    }
88547
88548    var svgPath = {};
88549    svgPath.brush = function (el) {
88550        var style = el.style;
88551
88552        var svgEl = el.__svgEl;
88553        if (!svgEl) {
88554            svgEl = createElement('path');
88555            el.__svgEl = svgEl;
88556        }
88557
88558        if (!el.path) {
88559            el.createPathProxy();
88560        }
88561        var path = el.path;
88562
88563        if (el.__dirtyPath) {
88564            path.beginPath();
88565            el.buildPath(path, el.shape);
88566            el.__dirtyPath = false;
88567
88568            var pathStr = pathDataToString$1(path);
88569            if (pathStr.indexOf('NaN') < 0) {
88570                // Ignore illegal path, which may happen such in out-of-range
88571                // data in Calendar series.
88572                attr(svgEl, 'd', pathStr);
88573            }
88574        }
88575
88576        bindStyle(svgEl, style);
88577        setTransform(svgEl, el.transform);
88578
88579        if (style.text != null) {
88580            svgTextDrawRectText(el, el.getBoundingRect());
88581        }
88582    };
88583
88584    /***************************************************
88585     * IMAGE
88586     **************************************************/
88587    var svgImage = {};
88588    svgImage.brush = function (el) {
88589        var style = el.style;
88590        var image = style.image;
88591
88592        if (image instanceof HTMLImageElement) {
88593            var src = image.src;
88594            image = src;
88595        }
88596        if (! image) {
88597            return;
88598        }
88599
88600        var x = style.x || 0;
88601        var y = style.y || 0;
88602
88603        var dw = style.width;
88604        var dh = style.height;
88605
88606        var svgEl = el.__svgEl;
88607        if (! svgEl) {
88608            svgEl = createElement('image');
88609            el.__svgEl = svgEl;
88610        }
88611
88612        if (image !== el.__imageSrc) {
88613            attrXLink(svgEl, 'href', image);
88614            // Caching image src
88615            el.__imageSrc = image;
88616        }
88617
88618        attr(svgEl, 'width', dw);
88619        attr(svgEl, 'height', dh);
88620
88621        attr(svgEl, 'x', x);
88622        attr(svgEl, 'y', y);
88623
88624        setTransform(svgEl, el.transform);
88625
88626        if (style.text != null) {
88627            svgTextDrawRectText(el, el.getBoundingRect());
88628        }
88629    };
88630
88631    /***************************************************
88632     * TEXT
88633     **************************************************/
88634    var svgText = {};
88635    var tmpRect$3 = new BoundingRect();
88636
88637    var svgTextDrawRectText = function (el, rect, textRect) {
88638        var style = el.style;
88639
88640        el.__dirty && normalizeTextStyle(style, true);
88641
88642        var text = style.text;
88643        // Convert to string
88644        if (text == null) {
88645            // Draw no text only when text is set to null, but not ''
88646            return;
88647        }
88648        else {
88649            text += '';
88650        }
88651
88652        var textSvgEl = el.__textSvgEl;
88653        if (! textSvgEl) {
88654            textSvgEl = createElement('text');
88655            el.__textSvgEl = textSvgEl;
88656        }
88657
88658        var x;
88659        var y;
88660        var textPosition = style.textPosition;
88661        var distance = style.textDistance;
88662        var align = style.textAlign || 'left';
88663
88664        if (typeof style.fontSize === 'number') {
88665            style.fontSize += 'px';
88666        }
88667        var font = style.font
88668            || [
88669                style.fontStyle || '',
88670                style.fontWeight || '',
88671                style.fontSize || '',
88672                style.fontFamily || ''
88673            ].join(' ')
88674            || DEFAULT_FONT;
88675
88676        var verticalAlign = getVerticalAlignForSvg(style.textVerticalAlign);
88677
88678        textRect = getBoundingRect(text, font, align,
88679            verticalAlign);
88680
88681        var lineHeight = textRect.lineHeight;
88682        // Text position represented by coord
88683        if (textPosition instanceof Array) {
88684            x = rect.x + textPosition[0];
88685            y = rect.y + textPosition[1];
88686        }
88687        else {
88688            var newPos = adjustTextPositionOnRect(
88689                textPosition, rect, distance
88690            );
88691            x = newPos.x;
88692            y = newPos.y;
88693            verticalAlign = getVerticalAlignForSvg(newPos.textVerticalAlign);
88694            align = newPos.textAlign;
88695        }
88696
88697        attr(textSvgEl, 'alignment-baseline', verticalAlign);
88698
88699        if (font) {
88700            textSvgEl.style.font = font;
88701        }
88702
88703        var textPadding = style.textPadding;
88704
88705        // Make baseline top
88706        attr(textSvgEl, 'x', x);
88707        attr(textSvgEl, 'y', y);
88708
88709        bindStyle(textSvgEl, style, true);
88710        if (el instanceof Text || el.style.transformText) {
88711            // Transform text with element
88712            setTransform(textSvgEl, el.transform);
88713        }
88714        else {
88715            if (el.transform) {
88716                tmpRect$3.copy(rect);
88717                tmpRect$3.applyTransform(el.transform);
88718                rect = tmpRect$3;
88719            }
88720            else {
88721                var pos = el.transformCoordToGlobal(rect.x, rect.y);
88722                rect.x = pos[0];
88723                rect.y = pos[1];
88724            }
88725
88726            // Text rotation, but no element transform
88727            var origin = style.textOrigin;
88728            if (origin === 'center') {
88729                x = textRect.width / 2 + x;
88730                y = textRect.height / 2 + y;
88731            }
88732            else if (origin) {
88733                x = origin[0] + x;
88734                y = origin[1] + y;
88735            }
88736            var rotate$$1 = -style.textRotation || 0;
88737            var transform = create$1();
88738            // Apply textRotate to element matrix
88739            rotate(transform, el.transform, rotate$$1);
88740            setTransform(textSvgEl, transform);
88741        }
88742
88743        var textLines = text.split('\n');
88744        var nTextLines = textLines.length;
88745        var textAnchor = align;
88746        // PENDING
88747        if (textAnchor === 'left')  {
88748            textAnchor = 'start';
88749            textPadding && (x += textPadding[3]);
88750        }
88751        else if (textAnchor === 'right') {
88752            textAnchor = 'end';
88753            textPadding && (x -= textPadding[1]);
88754        }
88755        else if (textAnchor === 'center') {
88756            textAnchor = 'middle';
88757            textPadding && (x += (textPadding[3] - textPadding[1]) / 2);
88758        }
88759
88760        var dy = 0;
88761        if (verticalAlign === 'baseline') {
88762            dy = -textRect.height + lineHeight;
88763            textPadding && (dy -= textPadding[2]);
88764        }
88765        else if (verticalAlign === 'middle') {
88766            dy = (-textRect.height + lineHeight) / 2;
88767            textPadding && (y += (textPadding[0] - textPadding[2]) / 2);
88768        }
88769        else {
88770            textPadding && (dy += textPadding[0]);
88771        }
88772
88773        // Font may affect position of each tspan elements
88774        if (el.__text !== text || el.__textFont !== font) {
88775            var tspanList = el.__tspanList || [];
88776            el.__tspanList = tspanList;
88777            for (var i = 0; i < nTextLines; i++) {
88778                // Using cached tspan elements
88779                var tspan = tspanList[i];
88780                if (! tspan) {
88781                    tspan = tspanList[i] = createElement('tspan');
88782                    textSvgEl.appendChild(tspan);
88783                    attr(tspan, 'alignment-baseline', verticalAlign);
88784                    attr(tspan, 'text-anchor', textAnchor);
88785                }
88786                else {
88787                    tspan.innerHTML = '';
88788                }
88789                attr(tspan, 'x', x);
88790                attr(tspan, 'y', y + i * lineHeight + dy);
88791                tspan.appendChild(document.createTextNode(textLines[i]));
88792            }
88793            // Remove unsed tspan elements
88794            for (; i < tspanList.length; i++) {
88795                textSvgEl.removeChild(tspanList[i]);
88796            }
88797            tspanList.length = nTextLines;
88798
88799            el.__text = text;
88800            el.__textFont = font;
88801        }
88802        else if (el.__tspanList.length) {
88803            // Update span x and y
88804            var len = el.__tspanList.length;
88805            for (var i = 0; i < len; ++i) {
88806                var tspan = el.__tspanList[i];
88807                if (tspan) {
88808                    attr(tspan, 'x', x);
88809                    attr(tspan, 'y', y + i * lineHeight + dy);
88810                }
88811            }
88812        }
88813    };
88814
88815    function getVerticalAlignForSvg(verticalAlign) {
88816        if (verticalAlign === 'middle') {
88817            return 'middle';
88818        }
88819        else if (verticalAlign === 'bottom') {
88820            return 'baseline';
88821        }
88822        else {
88823            return 'hanging';
88824        }
88825    }
88826
88827    svgText.drawRectText = svgTextDrawRectText;
88828
88829    svgText.brush = function (el) {
88830        var style = el.style;
88831        if (style.text != null) {
88832            // 强制设置 textPosition
88833            style.textPosition = [0, 0];
88834            svgTextDrawRectText(el, {
88835                x: style.x || 0, y: style.y || 0,
88836                width: 0, height: 0
88837            }, el.getBoundingRect());
88838        }
88839    };
88840
88841// Myers' Diff Algorithm
88842// Modified from https://github.com/kpdecker/jsdiff/blob/master/src/diff/base.js
88843
88844    function Diff() {}
88845
88846    Diff.prototype = {
88847        diff: function (oldArr, newArr, equals) {
88848            if (!equals) {
88849                equals = function (a, b) {
88850                    return a === b;
88851                };
88852            }
88853            this.equals = equals;
88854
88855            var self = this;
88856
88857            oldArr = oldArr.slice();
88858            newArr = newArr.slice();
88859            // Allow subclasses to massage the input prior to running
88860            var newLen = newArr.length;
88861            var oldLen = oldArr.length;
88862            var editLength = 1;
88863            var maxEditLength = newLen + oldLen;
88864            var bestPath = [{ newPos: -1, components: [] }];
88865
88866            // Seed editLength = 0, i.e. the content starts with the same values
88867            var oldPos = this.extractCommon(bestPath[0], newArr, oldArr, 0);
88868            if (bestPath[0].newPos + 1 >= newLen && oldPos + 1 >= oldLen) {
88869                var indices = [];
88870                for (var i = 0; i < newArr.length; i++) {
88871                    indices.push(i);
88872                }
88873                // Identity per the equality and tokenizer
88874                return [{
88875                    indices: indices, count: newArr.length
88876                }];
88877            }
88878
88879            // Main worker method. checks all permutations of a given edit length for acceptance.
88880            function execEditLength() {
88881                for (var diagonalPath = -1 * editLength; diagonalPath <= editLength; diagonalPath += 2) {
88882                    var basePath;
88883                    var addPath = bestPath[diagonalPath - 1];
88884                    var removePath = bestPath[diagonalPath + 1];
88885                    var oldPos = (removePath ? removePath.newPos : 0) - diagonalPath;
88886                    if (addPath) {
88887                        // No one else is going to attempt to use this value, clear it
88888                        bestPath[diagonalPath - 1] = undefined;
88889                    }
88890
88891                    var canAdd = addPath && addPath.newPos + 1 < newLen;
88892                    var canRemove = removePath && 0 <= oldPos && oldPos < oldLen;
88893                    if (!canAdd && !canRemove) {
88894                        // If this path is a terminal then prune
88895                        bestPath[diagonalPath] = undefined;
88896                        continue;
88897                    }
88898
88899                    // Select the diagonal that we want to branch from. We select the prior
88900                    // path whose position in the new string is the farthest from the origin
88901                    // and does not pass the bounds of the diff graph
88902                    if (!canAdd || (canRemove && addPath.newPos < removePath.newPos)) {
88903                        basePath = clonePath(removePath);
88904                        self.pushComponent(basePath.components, undefined, true);
88905                    }
88906                    else {
88907                        basePath = addPath;   // No need to clone, we've pulled it from the list
88908                        basePath.newPos++;
88909                        self.pushComponent(basePath.components, true, undefined);
88910                    }
88911
88912                    oldPos = self.extractCommon(basePath, newArr, oldArr, diagonalPath);
88913
88914                    // If we have hit the end of both strings, then we are done
88915                    if (basePath.newPos + 1 >= newLen && oldPos + 1 >= oldLen) {
88916                        return buildValues(self, basePath.components, newArr, oldArr);
88917                    }
88918                    else {
88919                        // Otherwise track this path as a potential candidate and continue.
88920                        bestPath[diagonalPath] = basePath;
88921                    }
88922                }
88923
88924                editLength++;
88925            }
88926
88927            while (editLength <= maxEditLength) {
88928                var ret = execEditLength();
88929                if (ret) {
88930                    return ret;
88931                }
88932            }
88933        },
88934
88935        pushComponent: function (components, added, removed) {
88936            var last = components[components.length - 1];
88937            if (last && last.added === added && last.removed === removed) {
88938                // We need to clone here as the component clone operation is just
88939                // as shallow array clone
88940                components[components.length - 1] = {count: last.count + 1, added: added, removed: removed };
88941            }
88942            else {
88943                components.push({count: 1, added: added, removed: removed });
88944            }
88945        },
88946        extractCommon: function (basePath, newArr, oldArr, diagonalPath) {
88947            var newLen = newArr.length;
88948            var oldLen = oldArr.length;
88949            var newPos = basePath.newPos;
88950            var oldPos = newPos - diagonalPath;
88951            var commonCount = 0;
88952
88953            while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(newArr[newPos + 1], oldArr[oldPos + 1])) {
88954                newPos++;
88955                oldPos++;
88956                commonCount++;
88957            }
88958
88959            if (commonCount) {
88960                basePath.components.push({count: commonCount});
88961            }
88962
88963            basePath.newPos = newPos;
88964            return oldPos;
88965        },
88966        tokenize: function (value) {
88967            return value.slice();
88968        },
88969        join: function (value) {
88970            return value.slice();
88971        }
88972    };
88973
88974    function buildValues(diff, components, newArr, oldArr) {
88975        var componentPos = 0;
88976        var componentLen = components.length;
88977        var newPos = 0;
88978        var oldPos = 0;
88979
88980        for (; componentPos < componentLen; componentPos++) {
88981            var component = components[componentPos];
88982            if (!component.removed) {
88983                var indices = [];
88984                for (var i = newPos; i < newPos + component.count; i++) {
88985                    indices.push(i);
88986                }
88987                component.indices = indices;
88988                newPos += component.count;
88989                // Common case
88990                if (!component.added) {
88991                    oldPos += component.count;
88992                }
88993            }
88994            else {
88995                var indices = [];
88996                for (var i = oldPos; i < oldPos + component.count; i++) {
88997                    indices.push(i);
88998                }
88999                component.indices = indices;
89000                oldPos += component.count;
89001            }
89002        }
89003
89004        return components;
89005    }
89006
89007    function clonePath(path) {
89008        return { newPos: path.newPos, components: path.components.slice(0) };
89009    }
89010
89011    var arrayDiff = new Diff();
89012
89013    var arrayDiff$1 = function (oldArr, newArr, callback) {
89014        return arrayDiff.diff(oldArr, newArr, callback);
89015    };
89016
89017    /**
89018     * @file Manages elements that can be defined in <defs> in SVG,
89019     *       e.g., gradients, clip path, etc.
89020     * @author Zhang Wenli
89021     */
89022
89023    var MARK_UNUSED = '0';
89024    var MARK_USED = '1';
89025
89026    /**
89027     * Manages elements that can be defined in <defs> in SVG,
89028     * e.g., gradients, clip path, etc.
89029     *
89030     * @class
89031     * @param {number}          zrId      zrender instance id
89032     * @param {SVGElement}      svgRoot   root of SVG document
89033     * @param {string|string[]} tagNames  possible tag names
89034     * @param {string}          markLabel label name to make if the element
89035     *                                    is used
89036     */
89037    function Definable(
89038        zrId,
89039        svgRoot,
89040        tagNames,
89041        markLabel,
89042        domName
89043    ) {
89044        this._zrId = zrId;
89045        this._svgRoot = svgRoot;
89046        this._tagNames = typeof tagNames === 'string' ? [tagNames] : tagNames;
89047        this._markLabel = markLabel;
89048        this._domName = domName || '_dom';
89049
89050        this.nextId = 0;
89051    }
89052
89053
89054    Definable.prototype.createElement = createElement;
89055
89056
89057    /**
89058     * Get the <defs> tag for svgRoot; optionally creates one if not exists.
89059     *
89060     * @param {boolean} isForceCreating if need to create when not exists
89061     * @return {SVGDefsElement} SVG <defs> element, null if it doesn't
89062     * exist and isForceCreating is false
89063     */
89064    Definable.prototype.getDefs = function (isForceCreating) {
89065        var svgRoot = this._svgRoot;
89066        var defs = this._svgRoot.getElementsByTagName('defs');
89067        if (defs.length === 0) {
89068            // Not exist
89069            if (isForceCreating) {
89070                defs = svgRoot.insertBefore(
89071                    this.createElement('defs'), // Create new tag
89072                    svgRoot.firstChild // Insert in the front of svg
89073                );
89074                if (!defs.contains) {
89075                    // IE doesn't support contains method
89076                    defs.contains = function (el) {
89077                        var children = defs.children;
89078                        if (!children) {
89079                            return false;
89080                        }
89081                        for (var i = children.length - 1; i >= 0; --i) {
89082                            if (children[i] === el) {
89083                                return true;
89084                            }
89085                        }
89086                        return false;
89087                    };
89088                }
89089                return defs;
89090            }
89091            else {
89092                return null;
89093            }
89094        }
89095        else {
89096            return defs[0];
89097        }
89098    };
89099
89100
89101    /**
89102     * Update DOM element if necessary.
89103     *
89104     * @param {Object|string} element style element. e.g., for gradient,
89105     *                                it may be '#ccc' or {type: 'linear', ...}
89106     * @param {Function|undefined} onUpdate update callback
89107     */
89108    Definable.prototype.update = function (element, onUpdate) {
89109        if (!element) {
89110            return;
89111        }
89112
89113        var defs = this.getDefs(false);
89114        if (element[this._domName] && defs.contains(element[this._domName])) {
89115            // Update DOM
89116            if (typeof onUpdate === 'function') {
89117                onUpdate(element);
89118            }
89119        }
89120        else {
89121            // No previous dom, create new
89122            var dom = this.add(element);
89123            if (dom) {
89124                element[this._domName] = dom;
89125            }
89126        }
89127    };
89128
89129
89130    /**
89131     * Add gradient dom to defs
89132     *
89133     * @param {SVGElement} dom DOM to be added to <defs>
89134     */
89135    Definable.prototype.addDom = function (dom) {
89136        var defs = this.getDefs(true);
89137        defs.appendChild(dom);
89138    };
89139
89140
89141    /**
89142     * Remove DOM of a given element.
89143     *
89144     * @param {SVGElement} element element to remove dom
89145     */
89146    Definable.prototype.removeDom = function (element) {
89147        var defs = this.getDefs(false);
89148        if (defs && element[this._domName]) {
89149            defs.removeChild(element[this._domName]);
89150            element[this._domName] = null;
89151        }
89152    };
89153
89154
89155    /**
89156     * Get DOMs of this element.
89157     *
89158     * @return {HTMLDomElement} doms of this defineable elements in <defs>
89159     */
89160    Definable.prototype.getDoms = function () {
89161        var defs = this.getDefs(false);
89162        if (!defs) {
89163            // No dom when defs is not defined
89164            return [];
89165        }
89166
89167        var doms = [];
89168        each$1(this._tagNames, function (tagName) {
89169            var tags = defs.getElementsByTagName(tagName);
89170            // Note that tags is HTMLCollection, which is array-like
89171            // rather than real array.
89172            // So `doms.concat(tags)` add tags as one object.
89173            doms = doms.concat([].slice.call(tags));
89174        });
89175
89176        return doms;
89177    };
89178
89179
89180    /**
89181     * Mark DOMs to be unused before painting, and clear unused ones at the end
89182     * of the painting.
89183     */
89184    Definable.prototype.markAllUnused = function () {
89185        var doms = this.getDoms();
89186        var that = this;
89187        each$1(doms, function (dom) {
89188            dom[that._markLabel] = MARK_UNUSED;
89189        });
89190    };
89191
89192
89193    /**
89194     * Mark a single DOM to be used.
89195     *
89196     * @param {SVGElement} dom DOM to mark
89197     */
89198    Definable.prototype.markUsed = function (dom) {
89199        if (dom) {
89200            dom[this._markLabel] = MARK_USED;
89201        }
89202    };
89203
89204
89205    /**
89206     * Remove unused DOMs defined in <defs>
89207     */
89208    Definable.prototype.removeUnused = function () {
89209        var defs = this.getDefs(false);
89210        if (!defs) {
89211            // Nothing to remove
89212            return;
89213        }
89214
89215        var doms = this.getDoms();
89216        var that = this;
89217        each$1(doms, function (dom) {
89218            if (dom[that._markLabel] !== MARK_USED) {
89219                // Remove gradient
89220                defs.removeChild(dom);
89221            }
89222        });
89223    };
89224
89225
89226    /**
89227     * Get SVG proxy.
89228     *
89229     * @param {Displayable} displayable displayable element
89230     * @return {Path|Image|Text} svg proxy of given element
89231     */
89232    Definable.prototype.getSvgProxy = function (displayable) {
89233        if (displayable instanceof Path) {
89234            return svgPath;
89235        }
89236        else if (displayable instanceof ZImage) {
89237            return svgImage;
89238        }
89239        else if (displayable instanceof Text) {
89240            return svgText;
89241        }
89242        else {
89243            return svgPath;
89244        }
89245    };
89246
89247
89248    /**
89249     * Get text SVG element.
89250     *
89251     * @param {Displayable} displayable displayable element
89252     * @return {SVGElement} SVG element of text
89253     */
89254    Definable.prototype.getTextSvgElement = function (displayable) {
89255        return displayable.__textSvgEl;
89256    };
89257
89258
89259    /**
89260     * Get SVG element.
89261     *
89262     * @param {Displayable} displayable displayable element
89263     * @return {SVGElement} SVG element
89264     */
89265    Definable.prototype.getSvgElement = function (displayable) {
89266        return displayable.__svgEl;
89267    };
89268
89269    /**
89270     * @file Manages SVG gradient elements.
89271     * @author Zhang Wenli
89272     */
89273
89274    /**
89275     * Manages SVG gradient elements.
89276     *
89277     * @class
89278     * @extends Definable
89279     * @param   {number}     zrId    zrender instance id
89280     * @param   {SVGElement} svgRoot root of SVG document
89281     */
89282    function GradientManager(zrId, svgRoot) {
89283        Definable.call(
89284            this,
89285            zrId,
89286            svgRoot,
89287            ['linearGradient', 'radialGradient'],
89288            '__gradient_in_use__'
89289        );
89290    }
89291
89292
89293    inherits(GradientManager, Definable);
89294
89295
89296    /**
89297     * Create new gradient DOM for fill or stroke if not exist,
89298     * but will not update gradient if exists.
89299     *
89300     * @param {SvgElement}  svgElement   SVG element to paint
89301     * @param {Displayable} displayable  zrender displayable element
89302     */
89303    GradientManager.prototype.addWithoutUpdate = function (
89304        svgElement,
89305        displayable
89306    ) {
89307        if (displayable && displayable.style) {
89308            var that = this;
89309            each$1(['fill', 'stroke'], function (fillOrStroke) {
89310                if (displayable.style[fillOrStroke]
89311                    && (displayable.style[fillOrStroke].type === 'linear'
89312                        || displayable.style[fillOrStroke].type === 'radial')
89313                ) {
89314                    var gradient = displayable.style[fillOrStroke];
89315                    var defs = that.getDefs(true);
89316
89317                    // Create dom in <defs> if not exists
89318                    var dom;
89319                    if (gradient._dom) {
89320                        // Gradient exists
89321                        dom = gradient._dom;
89322                        if (!defs.contains(gradient._dom)) {
89323                            // _dom is no longer in defs, recreate
89324                            that.addDom(dom);
89325                        }
89326                    }
89327                    else {
89328                        // New dom
89329                        dom = that.add(gradient);
89330                    }
89331
89332                    that.markUsed(displayable);
89333
89334                    var id = dom.getAttribute('id');
89335                    svgElement.setAttribute(fillOrStroke, 'url(#' + id + ')');
89336                }
89337            });
89338        }
89339    };
89340
89341
89342    /**
89343     * Add a new gradient tag in <defs>
89344     *
89345     * @param   {Gradient} gradient zr gradient instance
89346     * @return {SVGLinearGradientElement | SVGRadialGradientElement}
89347     *                            created DOM
89348     */
89349    GradientManager.prototype.add = function (gradient) {
89350        var dom;
89351        if (gradient.type === 'linear') {
89352            dom = this.createElement('linearGradient');
89353        }
89354        else if (gradient.type === 'radial') {
89355            dom = this.createElement('radialGradient');
89356        }
89357        else {
89358            zrLog('Illegal gradient type.');
89359            return null;
89360        }
89361
89362        // Set dom id with gradient id, since each gradient instance
89363        // will have no more than one dom element.
89364        // id may exists before for those dirty elements, in which case
89365        // id should remain the same, and other attributes should be
89366        // updated.
89367        gradient.id = gradient.id || this.nextId++;
89368        dom.setAttribute('id', 'zr' + this._zrId
89369            + '-gradient-' + gradient.id);
89370
89371        this.updateDom(gradient, dom);
89372        this.addDom(dom);
89373
89374        return dom;
89375    };
89376
89377
89378    /**
89379     * Update gradient.
89380     *
89381     * @param {Gradient} gradient zr gradient instance
89382     */
89383    GradientManager.prototype.update = function (gradient) {
89384        var that = this;
89385        Definable.prototype.update.call(this, gradient, function () {
89386            var type = gradient.type;
89387            var tagName = gradient._dom.tagName;
89388            if (type === 'linear' && tagName === 'linearGradient'
89389                || type === 'radial' && tagName === 'radialGradient'
89390            ) {
89391                // Gradient type is not changed, update gradient
89392                that.updateDom(gradient, gradient._dom);
89393            }
89394            else {
89395                // Remove and re-create if type is changed
89396                that.removeDom(gradient);
89397                that.add(gradient);
89398            }
89399        });
89400    };
89401
89402
89403    /**
89404     * Update gradient dom
89405     *
89406     * @param {Gradient} gradient zr gradient instance
89407     * @param {SVGLinearGradientElement | SVGRadialGradientElement} dom
89408     *                            DOM to update
89409     */
89410    GradientManager.prototype.updateDom = function (gradient, dom) {
89411        if (gradient.type === 'linear') {
89412            dom.setAttribute('x1', gradient.x);
89413            dom.setAttribute('y1', gradient.y);
89414            dom.setAttribute('x2', gradient.x2);
89415            dom.setAttribute('y2', gradient.y2);
89416        }
89417        else if (gradient.type === 'radial') {
89418            dom.setAttribute('cx', gradient.x);
89419            dom.setAttribute('cy', gradient.y);
89420            dom.setAttribute('r', gradient.r);
89421        }
89422        else {
89423            zrLog('Illegal gradient type.');
89424            return;
89425        }
89426
89427        if (gradient.global) {
89428            // x1, x2, y1, y2 in range of 0 to canvas width or height
89429            dom.setAttribute('gradientUnits', 'userSpaceOnUse');
89430        }
89431        else {
89432            // x1, x2, y1, y2 in range of 0 to 1
89433            dom.setAttribute('gradientUnits', 'objectBoundingBox');
89434        }
89435
89436        // Remove color stops if exists
89437        dom.innerHTML = '';
89438
89439        // Add color stops
89440        var colors = gradient.colorStops;
89441        for (var i = 0, len = colors.length; i < len; ++i) {
89442            var stop = this.createElement('stop');
89443            stop.setAttribute('offset', colors[i].offset * 100 + '%');
89444            stop.setAttribute('stop-color', colors[i].color);
89445            dom.appendChild(stop);
89446        }
89447
89448        // Store dom element in gradient, to avoid creating multiple
89449        // dom instances for the same gradient element
89450        gradient._dom = dom;
89451    };
89452
89453    /**
89454     * Mark a single gradient to be used
89455     *
89456     * @param {Displayable} displayable displayable element
89457     */
89458    GradientManager.prototype.markUsed = function (displayable) {
89459        if (displayable.style) {
89460            var gradient = displayable.style.fill;
89461            if (gradient && gradient._dom) {
89462                Definable.prototype.markUsed.call(this, gradient._dom);
89463            }
89464
89465            gradient = displayable.style.stroke;
89466            if (gradient && gradient._dom) {
89467                Definable.prototype.markUsed.call(this, gradient._dom);
89468            }
89469        }
89470    };
89471
89472    /**
89473     * @file Manages SVG clipPath elements.
89474     * @author Zhang Wenli
89475     */
89476
89477    /**
89478     * Manages SVG clipPath elements.
89479     *
89480     * @class
89481     * @extends Definable
89482     * @param   {number}     zrId    zrender instance id
89483     * @param   {SVGElement} svgRoot root of SVG document
89484     */
89485    function ClippathManager(zrId, svgRoot) {
89486        Definable.call(this, zrId, svgRoot, 'clipPath', '__clippath_in_use__');
89487    }
89488
89489
89490    inherits(ClippathManager, Definable);
89491
89492
89493    /**
89494     * Update clipPath.
89495     *
89496     * @param {Displayable} displayable displayable element
89497     */
89498    ClippathManager.prototype.update = function (displayable) {
89499        var svgEl = this.getSvgElement(displayable);
89500        if (svgEl) {
89501            this.updateDom(svgEl, displayable.__clipPaths, false);
89502        }
89503
89504        var textEl = this.getTextSvgElement(displayable);
89505        if (textEl) {
89506            // Make another clipPath for text, since it's transform
89507            // matrix is not the same with svgElement
89508            this.updateDom(textEl, displayable.__clipPaths, true);
89509        }
89510
89511        this.markUsed(displayable);
89512    };
89513
89514
89515    /**
89516     * Create an SVGElement of displayable and create a <clipPath> of its
89517     * clipPath
89518     *
89519     * @param {Displayable} parentEl  parent element
89520     * @param {ClipPath[]}  clipPaths clipPaths of parent element
89521     * @param {boolean}     isText    if parent element is Text
89522     */
89523    ClippathManager.prototype.updateDom = function (
89524        parentEl,
89525        clipPaths,
89526        isText
89527    ) {
89528        if (clipPaths && clipPaths.length > 0) {
89529            // Has clipPath, create <clipPath> with the first clipPath
89530            var defs = this.getDefs(true);
89531            var clipPath = clipPaths[0];
89532            var clipPathEl;
89533            var id;
89534
89535            var dom = isText ? '_textDom' : '_dom';
89536
89537            if (clipPath[dom]) {
89538                // Use a dom that is already in <defs>
89539                id = clipPath[dom].getAttribute('id');
89540                clipPathEl = clipPath[dom];
89541
89542                // Use a dom that is already in <defs>
89543                if (!defs.contains(clipPathEl)) {
89544                    // This happens when set old clipPath that has
89545                    // been previously removed
89546                    defs.appendChild(clipPathEl);
89547                }
89548            }
89549            else {
89550                // New <clipPath>
89551                id = 'zr' + this._zrId + '-clip-' + this.nextId;
89552                ++this.nextId;
89553                clipPathEl = this.createElement('clipPath');
89554                clipPathEl.setAttribute('id', id);
89555                defs.appendChild(clipPathEl);
89556
89557                clipPath[dom] = clipPathEl;
89558            }
89559
89560            // Build path and add to <clipPath>
89561            var svgProxy = this.getSvgProxy(clipPath);
89562            if (clipPath.transform
89563                && clipPath.parent.invTransform
89564                && !isText
89565            ) {
89566                /**
89567                 * If a clipPath has a parent with transform, the transform
89568                 * of parent should not be considered when setting transform
89569                 * of clipPath. So we need to transform back from parent's
89570                 * transform, which is done by multiplying parent's inverse
89571                 * transform.
89572                 */
89573                    // Store old transform
89574                var transform = Array.prototype.slice.call(
89575                    clipPath.transform
89576                    );
89577
89578                // Transform back from parent, and brush path
89579                mul$1(
89580                    clipPath.transform,
89581                    clipPath.parent.invTransform,
89582                    clipPath.transform
89583                );
89584                svgProxy.brush(clipPath);
89585
89586                // Set back transform of clipPath
89587                clipPath.transform = transform;
89588            }
89589            else {
89590                svgProxy.brush(clipPath);
89591            }
89592
89593            var pathEl = this.getSvgElement(clipPath);
89594
89595            clipPathEl.innerHTML = '';
89596            /**
89597             * Use `cloneNode()` here to appendChild to multiple parents,
89598             * which may happend when Text and other shapes are using the same
89599             * clipPath. Since Text will create an extra clipPath DOM due to
89600             * different transform rules.
89601             */
89602            clipPathEl.appendChild(pathEl.cloneNode());
89603
89604            parentEl.setAttribute('clip-path', 'url(#' + id + ')');
89605
89606            if (clipPaths.length > 1) {
89607                // Make the other clipPaths recursively
89608                this.updateDom(clipPathEl, clipPaths.slice(1), isText);
89609            }
89610        }
89611        else {
89612            // No clipPath
89613            if (parentEl) {
89614                parentEl.setAttribute('clip-path', 'none');
89615            }
89616        }
89617    };
89618
89619    /**
89620     * Mark a single clipPath to be used
89621     *
89622     * @param {Displayable} displayable displayable element
89623     */
89624    ClippathManager.prototype.markUsed = function (displayable) {
89625        var that = this;
89626        if (displayable.__clipPaths && displayable.__clipPaths.length > 0) {
89627            each$1(displayable.__clipPaths, function (clipPath) {
89628                if (clipPath._dom) {
89629                    Definable.prototype.markUsed.call(that, clipPath._dom);
89630                }
89631                if (clipPath._textDom) {
89632                    Definable.prototype.markUsed.call(that, clipPath._textDom);
89633                }
89634            });
89635        }
89636    };
89637
89638    /**
89639     * @file Manages SVG shadow elements.
89640     * @author Zhang Wenli
89641     */
89642
89643    /**
89644     * Manages SVG shadow elements.
89645     *
89646     * @class
89647     * @extends Definable
89648     * @param   {number}     zrId    zrender instance id
89649     * @param   {SVGElement} svgRoot root of SVG document
89650     */
89651    function ShadowManager(zrId, svgRoot) {
89652        Definable.call(
89653            this,
89654            zrId,
89655            svgRoot,
89656            ['filter'],
89657            '__filter_in_use__',
89658            '_shadowDom'
89659        );
89660    }
89661
89662
89663    inherits(ShadowManager, Definable);
89664
89665
89666    /**
89667     * Create new shadow DOM for fill or stroke if not exist,
89668     * but will not update shadow if exists.
89669     *
89670     * @param {SvgElement}  svgElement   SVG element to paint
89671     * @param {Displayable} displayable  zrender displayable element
89672     */
89673    ShadowManager.prototype.addWithoutUpdate = function (
89674        svgElement,
89675        displayable
89676    ) {
89677        if (displayable && hasShadow(displayable.style)) {
89678            var style = displayable.style;
89679
89680            // Create dom in <defs> if not exists
89681            var dom;
89682            if (style._shadowDom) {
89683                // Gradient exists
89684                dom = style._shadowDom;
89685
89686                var defs = this.getDefs(true);
89687                if (!defs.contains(style._shadowDom)) {
89688                    // _shadowDom is no longer in defs, recreate
89689                    this.addDom(dom);
89690                }
89691            }
89692            else {
89693                // New dom
89694                dom = this.add(displayable);
89695            }
89696
89697            this.markUsed(displayable);
89698
89699            var id = dom.getAttribute('id');
89700            svgElement.style.filter = 'url(#' + id + ')';
89701        }
89702    };
89703
89704
89705    /**
89706     * Add a new shadow tag in <defs>
89707     *
89708     * @param {Displayable} displayable  zrender displayable element
89709     * @return {SVGFilterElement} created DOM
89710     */
89711    ShadowManager.prototype.add = function (displayable) {
89712        var dom = this.createElement('filter');
89713        var style = displayable.style;
89714
89715        // Set dom id with shadow id, since each shadow instance
89716        // will have no more than one dom element.
89717        // id may exists before for those dirty elements, in which case
89718        // id should remain the same, and other attributes should be
89719        // updated.
89720        style._shadowDomId = style._shadowDomId || this.nextId++;
89721        dom.setAttribute('id', 'zr' + this._zrId
89722            + '-shadow-' + style._shadowDomId);
89723
89724        this.updateDom(displayable, dom);
89725        this.addDom(dom);
89726
89727        return dom;
89728    };
89729
89730
89731    /**
89732     * Update shadow.
89733     *
89734     * @param {Displayable} displayable  zrender displayable element
89735     */
89736    ShadowManager.prototype.update = function (svgElement, displayable) {
89737        var style = displayable.style;
89738        if (hasShadow(style)) {
89739            var that = this;
89740            Definable.prototype.update.call(this, displayable, function (style) {
89741                that.updateDom(displayable, style._shadowDom);
89742            });
89743        }
89744        else {
89745            // Remove shadow
89746            this.remove(svgElement, style);
89747        }
89748    };
89749
89750
89751    /**
89752     * Remove DOM and clear parent filter
89753     */
89754    ShadowManager.prototype.remove = function (svgElement, style) {
89755        if (style._shadowDomId != null) {
89756            this.removeDom(style);
89757            svgElement.style.filter = '';
89758        }
89759    };
89760
89761
89762    /**
89763     * Update shadow dom
89764     *
89765     * @param {Displayable} displayable  zrender displayable element
89766     * @param {SVGFilterElement} dom DOM to update
89767     */
89768    ShadowManager.prototype.updateDom = function (displayable, dom) {
89769        var domChild = dom.getElementsByTagName('feDropShadow');
89770        if (domChild.length === 0) {
89771            domChild = this.createElement('feDropShadow');
89772        }
89773        else {
89774            domChild = domChild[0];
89775        }
89776
89777        var style = displayable.style;
89778        var scaleX = displayable.scale ? (displayable.scale[0] || 1) : 1;
89779        var scaleY = displayable.scale ? (displayable.scale[1] || 1) : 1;
89780
89781        // TODO: textBoxShadowBlur is not supported yet
89782        var offsetX, offsetY, blur, color;
89783        if (style.shadowBlur || style.shadowOffsetX || style.shadowOffsetY) {
89784            offsetX = style.shadowOffsetX || 0;
89785            offsetY = style.shadowOffsetY || 0;
89786            blur = style.shadowBlur;
89787            color = style.shadowColor;
89788        }
89789        else if (style.textShadowBlur) {
89790            offsetX = style.textShadowOffsetX || 0;
89791            offsetY = style.textShadowOffsetY || 0;
89792            blur = style.textShadowBlur;
89793            color = style.textShadowColor;
89794        }
89795        else {
89796            // Remove shadow
89797            this.removeDom(dom, style);
89798            return;
89799        }
89800
89801        domChild.setAttribute('dx', offsetX / scaleX);
89802        domChild.setAttribute('dy', offsetY / scaleY);
89803        domChild.setAttribute('flood-color', color);
89804
89805        // Divide by two here so that it looks the same as in canvas
89806        // See: https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-shadowblur
89807        var stdDx = blur / 2 / scaleX;
89808        var stdDy = blur / 2 / scaleY;
89809        var stdDeviation = stdDx + ' ' + stdDy;
89810        domChild.setAttribute('stdDeviation', stdDeviation);
89811
89812        // Fix filter clipping problem
89813        dom.setAttribute('x', '-100%');
89814        dom.setAttribute('y', '-100%');
89815        dom.setAttribute('width', Math.ceil(blur / 2 * 200) + '%');
89816        dom.setAttribute('height', Math.ceil(blur / 2 * 200) + '%');
89817
89818        dom.appendChild(domChild);
89819
89820        // Store dom element in shadow, to avoid creating multiple
89821        // dom instances for the same shadow element
89822        style._shadowDom = dom;
89823    };
89824
89825    /**
89826     * Mark a single shadow to be used
89827     *
89828     * @param {Displayable} displayable displayable element
89829     */
89830    ShadowManager.prototype.markUsed = function (displayable) {
89831        var style = displayable.style;
89832        if (style && style._shadowDom) {
89833            Definable.prototype.markUsed.call(this, style._shadowDom);
89834        }
89835    };
89836
89837    function hasShadow(style) {
89838        // TODO: textBoxShadowBlur is not supported yet
89839        return style
89840            && (style.shadowBlur || style.shadowOffsetX || style.shadowOffsetY
89841                || style.textShadowBlur || style.textShadowOffsetX
89842                || style.textShadowOffsetY);
89843    }
89844
89845    /**
89846     * SVG Painter
89847     * @module zrender/svg/Painter
89848     */
89849
89850    function parseInt10$2(val) {
89851        return parseInt(val, 10);
89852    }
89853
89854    function getSvgProxy(el) {
89855        if (el instanceof Path) {
89856            return svgPath;
89857        }
89858        else if (el instanceof ZImage) {
89859            return svgImage;
89860        }
89861        else if (el instanceof Text) {
89862            return svgText;
89863        }
89864        else {
89865            return svgPath;
89866        }
89867    }
89868
89869    function checkParentAvailable(parent, child) {
89870        return child && parent && child.parentNode !== parent;
89871    }
89872
89873    function insertAfter(parent, child, prevSibling) {
89874        if (checkParentAvailable(parent, child) && prevSibling) {
89875            var nextSibling = prevSibling.nextSibling;
89876            nextSibling ? parent.insertBefore(child, nextSibling)
89877                : parent.appendChild(child);
89878        }
89879    }
89880
89881    function prepend(parent, child) {
89882        if (checkParentAvailable(parent, child)) {
89883            var firstChild = parent.firstChild;
89884            firstChild ? parent.insertBefore(child, firstChild)
89885                : parent.appendChild(child);
89886        }
89887    }
89888
89889    function remove$1(parent, child) {
89890        if (child && parent && child.parentNode === parent) {
89891            parent.removeChild(child);
89892        }
89893    }
89894
89895    function getTextSvgElement(displayable) {
89896        return displayable.__textSvgEl;
89897    }
89898
89899    function getSvgElement(displayable) {
89900        return displayable.__svgEl;
89901    }
89902
89903    /**
89904     * @alias module:zrender/svg/Painter
89905     * @constructor
89906     * @param {HTMLElement} root 绘图容器
89907     * @param {module:zrender/Storage} storage
89908     * @param {Object} opts
89909     */
89910    var SVGPainter = function (root, storage, opts, zrId) {
89911
89912        this.root = root;
89913        this.storage = storage;
89914        this._opts = opts = extend({}, opts || {});
89915
89916        var svgRoot = createElement('svg');
89917        svgRoot.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
89918        svgRoot.setAttribute('version', '1.1');
89919        svgRoot.setAttribute('baseProfile', 'full');
89920        svgRoot.style.cssText = 'user-select:none;position:absolute;left:0;top:0;';
89921
89922        this.gradientManager = new GradientManager(zrId, svgRoot);
89923        this.clipPathManager = new ClippathManager(zrId, svgRoot);
89924        this.shadowManager = new ShadowManager(zrId, svgRoot);
89925
89926        var viewport = document.createElement('div');
89927        viewport.style.cssText = 'overflow:hidden;position:relative';
89928
89929        this._svgRoot = svgRoot;
89930        this._viewport = viewport;
89931
89932        root.appendChild(viewport);
89933        viewport.appendChild(svgRoot);
89934
89935        this.resize(opts.width, opts.height);
89936
89937        this._visibleList = [];
89938    };
89939
89940    SVGPainter.prototype = {
89941
89942        constructor: SVGPainter,
89943
89944        getType: function () {
89945            return 'svg';
89946        },
89947
89948        getViewportRoot: function () {
89949            return this._viewport;
89950        },
89951
89952        getViewportRootOffset: function () {
89953            var viewportRoot = this.getViewportRoot();
89954            if (viewportRoot) {
89955                return {
89956                    offsetLeft: viewportRoot.offsetLeft || 0,
89957                    offsetTop: viewportRoot.offsetTop || 0
89958                };
89959            }
89960        },
89961
89962        refresh: function () {
89963
89964            var list = this.storage.getDisplayList(true);
89965
89966            this._paintList(list);
89967        },
89968
89969        setBackgroundColor: function (backgroundColor) {
89970            // TODO gradient
89971            this._viewport.style.background = backgroundColor;
89972        },
89973
89974        _paintList: function (list) {
89975            this.gradientManager.markAllUnused();
89976            this.clipPathManager.markAllUnused();
89977            this.shadowManager.markAllUnused();
89978
89979            var svgRoot = this._svgRoot;
89980            var visibleList = this._visibleList;
89981            var listLen = list.length;
89982
89983            var newVisibleList = [];
89984            var i;
89985            for (i = 0; i < listLen; i++) {
89986                var displayable = list[i];
89987                var svgProxy = getSvgProxy(displayable);
89988                var svgElement = getSvgElement(displayable)
89989                    || getTextSvgElement(displayable);
89990                if (!displayable.invisible) {
89991                    if (displayable.__dirty) {
89992                        svgProxy && svgProxy.brush(displayable);
89993
89994                        // Update clipPath
89995                        this.clipPathManager.update(displayable);
89996
89997                        // Update gradient and shadow
89998                        if (displayable.style) {
89999                            this.gradientManager
90000                                .update(displayable.style.fill);
90001                            this.gradientManager
90002                                .update(displayable.style.stroke);
90003
90004                            this.shadowManager
90005                                .update(svgElement, displayable);
90006                        }
90007
90008                        displayable.__dirty = false;
90009                    }
90010                    newVisibleList.push(displayable);
90011                }
90012            }
90013
90014            var diff = arrayDiff$1(visibleList, newVisibleList);
90015            var prevSvgElement;
90016
90017            // First do remove, in case element moved to the head and do remove
90018            // after add
90019            for (i = 0; i < diff.length; i++) {
90020                var item = diff[i];
90021                if (item.removed) {
90022                    for (var k = 0; k < item.count;
90022 k++) {
90023                        var displayable = visibleList[item.indices[k]];
90024                        var svgElement = getSvgElement(displayable);
90025                        var textSvgElement = getTextSvgElement(displayable);
90026                        remove$1(svgRoot, svgElement);
90027                        remove$1(svgRoot, textSvgElement);
90028                    }
90029                }
90030            }
90031            for (i = 0; i < diff.length; i++) {
90032                var item = diff[i];
90033                if (item.added) {
90034                    for (var k = 0; k < item.count; k++) {
90035                        var displayable = newVisibleList[item.indices[k]];
90036                        var svgElement = getSvgElement(displayable);
90037                        var textSvgElement = getTextSvgElement(displayable);
90038                        prevSvgElement
90039                            ? insertAfter(svgRoot, svgElement, prevSvgElement)
90040                            : prepend(svgRoot, svgElement);
90041                        if (svgElement) {
90042                            insertAfter(svgRoot, textSvgElement, svgElement);
90043                        }
90044                        else if (prevSvgElement) {
90045                            insertAfter(
90046                                svgRoot, textSvgElement, prevSvgElement
90047                            );
90048                        }
90049                        else {
90050                            prepend(svgRoot, textSvgElement);
90051                        }
90052                        // Insert text
90053                        insertAfter(svgRoot, textSvgElement, svgElement);
90054                        prevSvgElement = textSvgElement || svgElement
90055                            || prevSvgElement;
90056
90057                        this.gradientManager
90058                            .addWithoutUpdate(svgElement, displayable);
90059                        this.shadowManager
90060                            .addWithoutUpdate(prevSvgElement, displayable);
90061                        this.clipPathManager.markUsed(displayable);
90062                    }
90063                }
90064                else if (!item.removed) {
90065                    for (var k = 0; k < item.count; k++) {
90066                        var displayable = newVisibleList[item.indices[k]];
90067                        prevSvgElement
90068                            = svgElement
90069                            = getTextSvgElement(displayable)
90070                            || getSvgElement(displayable)
90071                            || prevSvgElement;
90072
90073                        this.gradientManager.markUsed(displayable);
90074                        this.gradientManager
90075                            .addWithoutUpdate(svgElement, displayable);
90076
90077                        this.shadowManager.markUsed(displayable);
90078                        this.shadowManager
90079                            .addWithoutUpdate(svgElement, displayable);
90080
90081                        this.clipPathManager.markUsed(displayable);
90082                    }
90083                }
90084            }
90085
90086            this.gradientManager.removeUnused();
90087            this.clipPathManager.removeUnused();
90088            this.shadowManager.removeUnused();
90089
90090            this._visibleList = newVisibleList;
90091        },
90092
90093        _getDefs: function (isForceCreating) {
90094            var svgRoot = this._svgRoot;
90095            var defs = this._svgRoot.getElementsByTagName('defs');
90096            if (defs.length === 0) {
90097                // Not exist
90098                if (isForceCreating) {
90099                    var defs = svgRoot.insertBefore(
90100                        createElement('defs'), // Create new tag
90101                        svgRoot.firstChild // Insert in the front of svg
90102                    );
90103                    if (!defs.contains) {
90104                        // IE doesn't support contains method
90105                        defs.contains = function (el) {
90106                            var children = defs.children;
90107                            if (!children) {
90108                                return false;
90109                            }
90110                            for (var i = children.length - 1; i >= 0; --i) {
90111                                if (children[i] === el) {
90112                                    return true;
90113                                }
90114                            }
90115                            return false;
90116                        };
90117                    }
90118                    return defs;
90119                }
90120                else {
90121                    return null;
90122                }
90123            }
90124            else {
90125                return defs[0];
90126            }
90127        },
90128
90129        resize: function (width, height) {
90130            var viewport = this._viewport;
90131            // FIXME Why ?
90132            viewport.style.display = 'none';
90133
90134            // Save input w/h
90135            var opts = this._opts;
90136            width != null && (opts.width = width);
90137            height != null && (opts.height = height);
90138
90139            width = this._getSize(0);
90140            height = this._getSize(1);
90141
90142            viewport.style.display = '';
90143
90144            if (this._width !== width || this._height !== height) {
90145                this._width = width;
90146                this._height = height;
90147
90148                var viewportStyle = viewport.style;
90149                viewportStyle.width = width + 'px';
90150                viewportStyle.height = height + 'px';
90151
90152                var svgRoot = this._svgRoot;
90153                // Set width by 'svgRoot.width = width' is invali
90153d
90154                svgRoot.setAttribute('width', width);
90155                svgRoot.setAttribute('height', height);
90156            }
90157        },
90158
90159        /**
90160         * 获取绘图区域宽度
90161         */
90162        getWidth: function () {
90163            return this._width;
90164        },
90165
90166        /**
90167         * 获取绘图区域高度
90168         */
90169        getHeight: function () {
90170            return this._height;
90171        },
90172
90173        _getSize: function (whIdx) {
90174            var opts = this._opts;
90175            var wh = ['width', 'height'][whIdx];
90176            var cwh = ['clientWidth', 'clientHeight'][whIdx];
90177            var plt = ['paddingLeft', 'paddingTop'][whIdx];
90178            var prb = ['paddingRight', 'paddingBottom'][whIdx];
90179
90180            if (opts[wh] != null && opts[wh] !== 'auto') {
90181                return parseFloat(opts[wh]);
90182            }
90183
90184            var root = this.root;
90185            // IE8 does not support getComputedStyle, but it use VML.
90186            var stl = document.defaultView.getComputedStyle(root);
90187
90188            return (
90189                (root[cwh] || parseInt10$2(stl[wh]) || parseInt10$2(root.style[wh]))
90190                - (parseInt10$2(stl[plt]) || 0)
90191                - (parseInt10$2(stl[prb]) || 0)
90192            ) | 0;
90193        },
90194
90195        dispose: function () {
90196            this.root.innerHTML = '';
90197
90198            this._svgRoot
90199                = this._viewport
90200                = this.storage
90201                = null;
90202        },
90203
90204        clear: function () {
90205            if (this._viewport) {
90206                this.root.removeChild(this._viewport);
90207            }
90208        },
90209
90210        pathToDataUrl: function () {
90211            this.refresh();
90212            var html = this._svgRoot.outerHTML;
90213            return 'data:image/svg+xml;charset=UTF-8,' + html;
90214        }
90215    };
90216
90217// Not supported methods
90218    function createMethodNotSupport$1(method) {
90219        return function () {
90220            zrLog('In SVG mode painter not support method "' + method + '"');
90221        };
90222    }
90223
90224// Unsuppoted methods
90225    each$1([
90226        'getLayer', 'insertLayer', 'eachLayer', 'eachBuiltinLayer',
90227        'eachOtherLayer', 'getLayers', 'modLayer', 'delLayer', 'clearLayer',
90228        'toDataURL', 'pathToImage'
90229    ], function (name) {
90230        SVGPainter.prototype[name] = createMethodNotSupport$1(name);
90231    });
90232
90233    registerPainter('svg', SVGPainter);
90234
90235    /*
90236* Licensed to the Apache Software Foundation (ASF) under one
90237* or more contributor license agreements.  See the NOTICE file
90238* distributed with this work for additional information
90239* regarding copyright ownership.  The ASF licenses this file
90240* to you under the Apache License, Version 2.0 (the
90241* "License"); you may not use this file except in compliance
90242* with the License.  You may obtain a copy of the License at
90243*
90244*   http://www.apache.org/licenses/LICENSE-2.0
90245*
90246* Unless required by applicable law or agreed to in writing,
90247* software distributed under the License is distributed on an
90248* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
90249* KIND, either express or implied.  See the License for the
90250* specific language governing permissions and limitations
90251* under the License.
90252*/
90253
90254// Import all charts and components
90255
90256    exports.version = version;
90257    exports.dependencies = dependencies;
90258    exports.PRIORITY = PRIORITY;
90259    exports.init = init;
90260    exports.connect = connect;
90261    exports.disConnect = disConnect;
90262    exports.disconnect = disconnect;
90263    exports.dispose = dispose;
90264    exports.getInstanceByDom = getInstanceByDom;
90265    exports.getInstanceById = getInstanceById;
90266    exports.registerTheme = registerTheme;
90267    exports.registerPreprocessor = registerPreprocessor;
90268    exports.registerProcessor = registerProcessor;
90269    exports.registerPostUpdate = registerPostUpdate;
90270    exports.registerAction = registerAction;
90271    exports.registerCoordinateSystem = registerCoordinateSystem;
90272    exports.getCoordinateSystemDimensions = getCoordinateSystemDimensions;
90273    exports.registerLayout = registerLayout;
90274    exports.registerVisual = registerVisual;
90275    exports.registerLoading = registerLoading;
90276    exports.extendComponentModel = extendComponentModel;
90277    exports.extendComponentView = extendComponentView;
90278    exports.extendSeriesModel = extendSeriesModel;
90279    exports.extendChartView = extendChartView;
90280    exports.setCanvasCreator = setCanvasCreator;
90281    exports.registerMap = registerMap;
90282    exports.getMap = getMap;
90283    exports.dataTool = dataTool;
90284    exports.zrender = zrender;
90285    exports.graphic = graphic;
90286    exports.number = number;
90287    exports.format = format;
90288    exports.throttle = throttle;
90289    exports.helper = helper;
90290    exports.matrix = matrix;
90291    exports.vector = vector;
90292    exports.color = color;
90293    exports.parseGeoJSON = parseGeoJson$1;
90294    exports.parseGeoJson = parseGeoJson;
90295    exports.util = ecUtil;
90296    exports.List = List;
90297    exports.Model = Model;
90298    exports.Axis = Axis;
90299    exports.env = env$1;
90300
90301})));
90302//# sourceMappingURL=echarts.js.map

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.