You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@echarts.apache.org by su...@apache.org on 2021/02/01 13:26:30 UTC

[echarts] branch release-dev updated (5bda8a1 -> 0c1ffeb)

This is an automated email from the ASF dual-hosted git repository.

sushuang pushed a change to branch release-dev
in repository https://gitbox.apache.org/repos/asf/echarts.git.


    from 5bda8a1  release: 5.0.1 (ag)
     add c9da1c9  fix(toolbox): use current device pixel ratio by default for exporting crisp and clear images.
     add d38168e  Merge pull request #14002 from apache/fix-export-dpr
     add 7435f71  doc: update repo name as TLP (#14003)
     add 49c9829  test: migrate to ecSimpleTransition and ecSimpleOptionPlayer.
     add 68621c5  Merge pull request #14054 from apache/fix/remove-test-transform
     add f9896f0  Merge pull request #14020 from apache/release-dev
     add 0e07109  Merge pull request #14062 from apache/release
     add 6c7757a  fix: remove incubating & incubator
     add 392d94d  fix(pie): deprecate series[pie].label.margin #14067
     add 758aa43  Merge branch 'master' of github.com:apache/incubator-echarts
     add 97db80a  fix: add encode html to avoid xss risk.
     add 5b4a211  Merge pull request #14087 from apache/fix/encodehtml
     add ab12807  fix(pie): pie chart avoidLabelOverlap hides label. close #13938
     add 9f8340c  Merge pull request #14108 from Nick22nd/fix-13938
     add 5fdf9cf  Fix(handleIcon): icon without 'path://' will block
     add 44fdd3f  Merge pull request #14056 from susiwen8/handlerIcon
     add 1df902b  Fix(pie): labelLine is not hidden in some case
     add 0790ee9  chore: avoid null value access
     add 67a8749  Merge pull request #14017 from susiwen8/labelline
     add 7875fc5  fix: add geo dependencies in map. #14066
     add e65b1d8  Merge pull request #14124 from apache/fix-geo-dep
     add 9fe6263  Fix: endLabel color support 'auto' 'inherit'
     add 08d55f6  chore: endlabel color is string type
     add 3bad840  Merge pull request #14000 from susiwen8/endlabel-color-inherit
     add f5dfa5d  fix(type): optimize event param types
     add 893255e  Merge pull request #14155 from apache/fix-echarts-types
     add b72fa40  Merge pull request #14160 from apache/master
     add 8a9d8f6  test: add gauge regression test case
     add 6bb9d73  Merge pull request #14164 from apache/add-gauge-regression-case
     add 11d77c1  fix: error thrown when `toolbox.feature.dataZoom: { show: false, yAxisIndex: false }`. (brought in 5.0)
     add 1693e93  Merge pull request #14175 from apache/fix/toolbox-datazoom
     new 1ea6586  fix: tweak chalk for error thrown in the recently version.
     new 0c1ffeb  release: 5.0.2

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 CONTRIBUTING.md                                 |    11 +-
 README.md                                       |     8 +-
 build/addHeader.js                              |     8 +-
 dist/echarts.common.js                          | 11349 ++++++++++--
 dist/echarts.common.js.map                      |     2 +-
 dist/echarts.common.min.js                      |     2 +-
 dist/echarts.esm.js                             | 21511 +++++++++++++++++-----
 dist/echarts.esm.js.map                         |     2 +-
 dist/echarts.esm.min.js                         |     2 +-
 dist/echarts.js                                 | 21511 +++++++++++++++++-----
 dist/echarts.js.map                             |     2 +-
 dist/echarts.min.js                             |     2 +-
 dist/echarts.simple.js                          |  7981 ++++++--
 dist/echarts.simple.js.map                      |     2 +-
 dist/echarts.simple.min.js                      |     2 +-
 dist/extension/bmap.js                          |    67 +-
 dist/extension/bmap.js.map                      |     2 +-
 dist/extension/dataTool.js                      |    68 +-
 dist/extension/dataTool.js.map                  |     2 +-
 package-lock.json                               |     8 +-
 package.json                                    |     8 +-
 src/chart/custom/install.ts                     |     7 +-
 src/chart/graph/GraphSeries.ts                  |     5 +-
 src/chart/line/LineView.ts                      |    13 +-
 src/chart/map/install.ts                        |     5 +-
 src/chart/pie/PieSeries.ts                      |     6 +-
 src/chart/pie/PieView.ts                        |     7 +-
 src/chart/pie/labelLayout.ts                    |    18 +-
 src/chart/sankey/SankeySeries.ts                |     6 +-
 src/chart/sankey/SankeyView.ts                  |     7 +-
 src/component/dataZoom/SliderZoomView.ts        |     6 +-
 src/component/toolbox/feature/DataZoom.ts       |    27 +-
 src/component/toolbox/feature/SaveAsImage.ts    |     4 +-
 src/component/tooltip/tooltipMarkup.ts          |     7 +-
 src/core/echarts.ts                             |    63 +-
 src/util/ECEventProcessor.ts                    |     4 +-
 src/util/types.ts                               |    20 +-
 src/view/Chart.ts                               |     6 +-
 src/view/Component.ts                           |     4 +-
 test/custom-shape-morphing2.html                |    13 +-
 test/custom-shape-morphing3.html                |    15 +-
 test/data-transform-aggregate.html              |    18 +-
 test/{build/mktest-tpl.html => gauge-case.html} |    63 +-
 test/lib/config.js                              |     3 +-
 test/lib/ecSimpleOptionPlayer.js                |   139 +
 test/lib/ecSimpleTransform.js                   |   362 +
 test/lib/myTransform/dist/myTransform.js        |   476 -
 test/lib/myTransform/dist/myTransform.js.map    |     1 -
 test/lib/myTransform/src/.eslintrc.yaml         |    47 -
 test/lib/myTransform/src/aggregate.ts           |   593 -
 test/lib/myTransform/src/id.ts                  |    90 -
 test/lib/myTransform/src/index.ts               |     3 -
 test/lib/transitionPlayer.js                    |   256 -
 test/line-endLabel.html                         |     6 +-
 test/pie-label.html                             |    63 +
 test/types/basic.ts                             |     2 +-
 test/types/event.ts                             |    23 +
 tsconfig.json                                   |     2 +-
 58 files changed, 50834 insertions(+), 14106 deletions(-)
 copy test/{build/mktest-tpl.html => gauge-case.html} (61%)
 create mode 100644 test/lib/ecSimpleOptionPlayer.js
 create mode 100644 test/lib/ecSimpleTransform.js
 delete mode 100644 test/lib/myTransform/dist/myTransform.js
 delete mode 100644 test/lib/myTransform/dist/myTransform.js.map
 delete mode 100644 test/lib/myTransform/src/.eslintrc.yaml
 delete mode 100644 test/lib/myTransform/src/aggregate.ts
 delete mode 100644 test/lib/myTransform/src/id.ts
 delete mode 100644 test/lib/myTransform/src/index.ts
 delete mode 100644 test/lib/transitionPlayer.js
 create mode 100644 test/types/event.ts


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org


[echarts] 02/02: release: 5.0.2

Posted by su...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

sushuang pushed a commit to branch release-dev
in repository https://gitbox.apache.org/repos/asf/echarts.git

commit 0c1ffebaf0bb36cb1f65442cb7563537083aa14f
Author: 100pah <su...@gmail.com>
AuthorDate: Mon Feb 1 21:25:21 2021 +0800

    release: 5.0.2
---
 dist/echarts.common.js         | 11349 +++++++++++++++++----
 dist/echarts.common.js.map     |     2 +-
 dist/echarts.common.min.js     |     2 +-
 dist/echarts.esm.js            | 21511 ++++++++++++++++++++++++++++++---------
 dist/echarts.esm.js.map        |     2 +-
 dist/echarts.esm.min.js        |     2 +-
 dist/echarts.js                | 21511 ++++++++++++++++++++++++++++++---------
 dist/echarts.js.map            |     2 +-
 dist/echarts.min.js            |     2 +-
 dist/echarts.simple.js         |  7981 ++++++++++++---
 dist/echarts.simple.js.map     |     2 +-
 dist/echarts.simple.min.js     |     2 +-
 dist/extension/bmap.js         |    67 +-
 dist/extension/bmap.js.map     |     2 +-
 dist/extension/dataTool.js     |    68 +-
 dist/extension/dataTool.js.map |     2 +-
 package-lock.json              |     8 +-
 package.json                   |     4 +-
 src/core/echarts.ts            |     4 +-
 19 files changed, 50037 insertions(+), 12486 deletions(-)

diff --git a/dist/echarts.common.js b/dist/echarts.common.js
index b4e40cd..2c5421e 100644
--- a/dist/echarts.common.js
+++ b/dist/echarts.common.js
@@ -6553,7 +6553,7 @@
     function registerPainter(name, Ctor) {
         painterCtors[name] = Ctor;
     }
-    var version = '5.0.3';
+    var version = '5.0.4';
 
     var zrender = /*#__PURE__*/Object.freeze({
         __proto__: null,
@@ -6570,6 +6570,14 @@
     function _trim(str) {
       return str.replace(/^\s+|\s+$/g, '');
     }
+    /**
+     * Linear mapping a value from domain to range
+     * @param  val
+     * @param  domain Domain extent domain[0] can be bigger than domain[1]
+     * @param  range  Range extent range[0] can be bigger than range[1]
+     * @param  clamp Default to be false
+     */
+
 
     function linearMap(val, domain, range, clamp) {
       var subDomain = domain[1] - domain[0];
@@ -6577,7 +6585,12 @@
 
       if (subDomain === 0) {
         return subRange === 0 ? range[0] : (range[0] + range[1]) / 2;
-      }
+      } // Avoid accuracy problem in edge, such as
+      // 146.39 - 62.83 === 83.55999999999999.
+      // See echarts/test/ut/spec/util/number.js#linearMap#accuracyError
+      // It is a little verbose for efficiency considering this method
+      // is a hotspot.
+
 
       if (clamp) {
         if (subDomain > 0) {
@@ -6605,6 +6618,11 @@
 
       return (val - domain[0]) / subDomain * subRange + range[0];
     }
+    /**
+     * Convert a percent string to absolute number.
+     * Returns NaN if percent is not a valid string or number
+     */
+
     function parsePercent$1(percent, all) {
       switch (percent) {
         case 'center':
@@ -6636,24 +6654,38 @@
     function round(x, precision, returnStr) {
       if (precision == null) {
         precision = 10;
-      }
+      } // Avoid range error
+
 
       precision = Math.min(Math.max(0, precision), 20);
       x = (+x).toFixed(precision);
       return returnStr ? x : +x;
     }
+    /**
+     * Inplacd asc sort arr.
+     * The input arr will be modified.
+     */
+
     function asc(arr) {
       arr.sort(function (a, b) {
         return a - b;
       });
       return arr;
     }
+    /**
+     * Get precision
+     */
+
     function getPrecision(val) {
       val = +val;
 
       if (isNaN(val)) {
         return 0;
-      }
+      } // It is much faster than methods converting number to string as follows
+      //      let tmp = val.toString();
+      //      return tmp.length - 1 - tmp.indexOf('.');
+      // especially when precision is low
+
 
       var e = 1;
       var count = 0;
@@ -6665,8 +6697,13 @@
 
       return count;
     }
+    /**
+     * Get precision with slow but safe method
+     */
+
     function getPrecisionSafe(val) {
-      var str = val.toString();
+      var str = val.toString(); // Consider scientific notation: '3.4e-12' '3.4e+12'
+
       var eIndex = str.indexOf('e');
 
       if (eIndex > 0) {
@@ -6677,14 +6714,31 @@
         return dotIndex < 0 ? 0 : str.length - 1 - dotIndex;
       }
     }
+    /**
+     * Minimal dicernible data precisioin according to a single pixel.
+     */
+
     function getPixelPrecision(dataExtent, pixelExtent) {
       var log = Math.log;
       var LN10 = Math.LN10;
       var dataQuantity = Math.floor(log(dataExtent[1] - dataExtent[0]) / LN10);
-      var sizeQuantity = Math.round(log(Math.abs(pixelExtent[1] - pixelExtent[0])) / LN10);
+      var sizeQuantity = Math.round(log(Math.abs(pixelExtent[1] - pixelExtent[0])) / LN10); // toFixed() digits argument must be between 0 and 20.
+
       var precision = Math.min(Math.max(-dataQuantity + sizeQuantity, 0), 20);
       return !isFinite(precision) ? 20 : precision;
     }
+    /**
+     * Get a data of given precision, assuring the sum of percentages
+     * in valueList is 1.
+     * The largest remainer method is used.
+     * https://en.wikipedia.org/wiki/Largest_remainder_method
+     *
+     * @param valueList a list of all data
+     * @param idx index of the data to be processed in valueList
+     * @param precision integer number showing digits of precision
+     * @return percent ranging from 0 to 100
+     */
+
     function getPercentWithPrecision(valueList, idx, precision) {
       if (!valueList[idx]) {
         return 0;
@@ -6704,6 +6758,7 @@
       });
       var targetSeats = digits * 100;
       var seats = map(votesPerQuota, function (votes) {
+        // Assign automatic seats.
         return Math.floor(votes);
       });
       var currentSum = reduce(seats, function (acc, val) {
@@ -6711,9 +6766,10 @@
       }, 0);
       var remainder = map(votesPerQuota, function (votes, idx) {
         return votes - seats[idx];
-      });
+      }); // Has remainding votes.
 
       while (currentSum < targetSeats) {
+        // Find next largest remainder.
         var max = Number.NEGATIVE_INFINITY;
         var maxId = null;
 
@@ -6722,7 +6778,8 @@
             max = remainder[i];
             maxId = i;
           }
-        }
+        } // Add a vote to max remainder.
+
 
         ++seats[maxId];
         remainder[maxId] = 0;
@@ -6730,52 +6787,117 @@
       }
 
       return seats[idx] / digits;
-    }
+    } // Number.MAX_SAFE_INTEGER, ie do not support.
+
     var MAX_SAFE_INTEGER = 9007199254740991;
+    /**
+     * To 0 - 2 * PI, considering negative radian.
+     */
+
     function remRadian(radian) {
       var pi2 = Math.PI * 2;
       return (radian % pi2 + pi2) % pi2;
     }
+    /**
+     * @param {type} radian
+     * @return {boolean}
+     */
+
     function isRadianAroundZero(val) {
       return val > -RADIAN_EPSILON && val < RADIAN_EPSILON;
-    }
-    var TIME_REG = /^(?:(\d{4})(?:[-\/](\d{1,2})(?:[-\/](\d{1,2})(?:[T ](\d{1,2})(?::(\d{1,2})(?::(\d{1,2})(?:[.,](\d+))?)?)?(Z|[\+\-]\d\d:?\d\d)?)?)?)?)?$/;
+    } // eslint-disable-next-line
+
+    var TIME_REG = /^(?:(\d{4})(?:[-\/](\d{1,2})(?:[-\/](\d{1,2})(?:[T ](\d{1,2})(?::(\d{1,2})(?::(\d{1,2})(?:[.,](\d+))?)?)?(Z|[\+\-]\d\d:?\d\d)?)?)?)?)?$/; // jshint ignore:line
+
+    /**
+     * @param value valid type: number | string | Date, otherwise return `new Date(NaN)`
+     *   These values can be accepted:
+     *   + An instance of Date, represent a time in its own time zone.
+     *   + Or string in a subset of ISO 8601, only including:
+     *     + only year, month, date: '2012-03', '2012-03-01', '2012-03-01 05', '2012-03-01 05:06',
+     *     + separated with T or space: '2012-03-01T12:22:33.123', '2012-03-01 12:22:33.123',
+     *     + time zone: '2012-03-01T12:22:33Z', '2012-03-01T12:22:33+8000', '2012-03-01T12:22:33-05:00',
+     *     all of which will be treated as local time if time zone is not specified
+     *     (see <https://momentjs.com/>).
+     *   + Or other string format, including (all of which will be treated as loacal time):
+     *     '2012', '2012-3-1', '2012/3/1', '2012/03/01',
+     *     '2009/6/12 2:00', '2009/6/12 2:05:08', '2009/6/12 2:05:08.123'
+     *   + a timestamp, which represent a time in UTC.
+     * @return date Never be null/undefined. If invalid, return `new Date(NaN)`.
+     */
+
     function parseDate(value) {
       if (value instanceof Date) {
         return value;
       } else if (typeof value === 'string') {
+        // Different browsers parse date in different way, so we parse it manually.
+        // Some other issues:
+        // new Date('1970-01-01') is UTC,
+        // new Date('1970/01/01') and new Date('1970-1-01') is local.
+        // See issue #3623
         var match = TIME_REG.exec(value);
 
         if (!match) {
+          // return Invalid Date.
           return new Date(NaN);
-        }
+        } // Use local time when no timezone offset specifed.
+
 
         if (!match[8]) {
+          // match[n] can only be string or undefined.
+          // But take care of '12' + 1 => '121'.
           return new Date(+match[1], +(match[2] || 1) - 1, +match[3] || 1, +match[4] || 0, +(match[5] || 0), +match[6] || 0, +match[7] || 0);
-        } else {
-          var hour = +match[4] || 0;
+        } // Timezoneoffset of Javascript Date has considered DST (Daylight Saving Time,
+        // https://tc39.github.io/ecma262/#sec-daylight-saving-time-adjustment).
+        // For example, system timezone is set as "Time Zone: America/Toronto",
+        // then these code will get different result:
+        // `new Date(1478411999999).getTimezoneOffset();  // get 240`
+        // `new Date(1478412000000).getTimezoneOffset();  // get 300`
+        // So we should not use `new Date`, but use `Date.UTC`.
+        else {
+            var hour = +match[4] || 0;
 
-          if (match[8].toUpperCase() !== 'Z') {
-            hour -= +match[8].slice(0, 3);
-          }
+            if (match[8].toUpperCase() !== 'Z') {
+              hour -= +match[8].slice(0, 3);
+            }
 
-          return new Date(Date.UTC(+match[1], +(match[2] || 1) - 1, +match[3] || 1, hour, +(match[5] || 0), +match[6] || 0, +match[7] || 0));
-        }
+            return new Date(Date.UTC(+match[1], +(match[2] || 1) - 1, +match[3] || 1, hour, +(match[5] || 0), +match[6] || 0, +match[7] || 0));
+          }
       } else if (value == null) {
         return new Date(NaN);
       }
 
       return new Date(Math.round(value));
     }
+    /**
+     * Quantity of a number. e.g. 0.1, 1, 10, 100
+     *
+     * @param val
+     * @return
+     */
+
     function quantity(val) {
       return Math.pow(10, quantityExponent(val));
     }
+    /**
+     * Exponent of the quantity of a number
+     * e.g., 1234 equals to 1.234*10^3, so quantityExponent(1234) is 3
+     *
+     * @param val non-negative value
+     * @return
+     */
+
     function quantityExponent(val) {
       if (val === 0) {
         return 0;
       }
 
       var exp = Math.floor(Math.log(val) / Math.LN10);
+      /**
+       * exp is expected to be the rounded-down result of the base-10 log of val.
+       * But due to the precision loss with Math.log(val), we need to restore it
+       * using 10^exp to make sure we can get val back from exp. #11249
+       */
 
       if (val / Math.pow(10, exp) >= 10) {
         exp++;
@@ -6783,10 +6905,23 @@
 
       return exp;
     }
+    /**
+     * find a “nice” number approximately equal to x. Round the number if round = true,
+     * take ceiling if round = false. The primary observation is that the “nicest”
+     * numbers in decimal are 1, 2, and 5, and all power-of-ten multiples of these numbers.
+     *
+     * See "Nice Numbers for Graph Labels" of Graphic Gems.
+     *
+     * @param  val Non-negative value.
+     * @param  round
+     * @return Niced number
+     */
+
     function nice(val, round) {
       var exponent = quantityExponent(val);
       var exp10 = Math.pow(10, exponent);
-      var f = val / exp10;
+      var f = val / exp10; // 1 <= f < 10
+
       var nf;
 
       if (round) {
@@ -6815,9 +6950,18 @@
         }
       }
 
-      val = nf * exp10;
+      val = nf * exp10; // Fix 3 * 0.1 === 0.30000000000000004 issue (see IEEE 754).
+      // 20 is the uppper bound of toFixed.
+
       return exponent >= -20 ? +val.toFixed(exponent < 0 ? -exponent : 0) : val;
     }
+    /**
+     * This code was copied from "d3.js"
+     * <https://github.com/d3/d3/blob/9cc9a875e636a1dcf36cc1e07bdf77e1ad6e2c74/src/arrays/quantile.js>.
+     * See the license statement at the head of this file.
+     * @param ascArr
+     */
+
     function quantile(ascArr, p) {
       var H = (ascArr.length - 1) * p + 1;
       var h = Math.floor(H);
@@ -6825,6 +6969,29 @@
       var e = H - h;
       return e ? v + e * (ascArr[h] - v) : v;
     }
+    /**
+     * Order intervals asc, and split them when overlap.
+     * expect(numberUtil.reformIntervals([
+     *     {interval: [18, 62], close: [1, 1]},
+     *     {interval: [-Infinity, -70], close: [0, 0]},
+     *     {interval: [-70, -26], close: [1, 1]},
+     *     {interval: [-26, 18], close: [1, 1]},
+     *     {interval: [62, 150], close: [1, 1]},
+     *     {interval: [106, 150], close: [1, 1]},
+     *     {interval: [150, Infinity], close: [0, 0]}
+     * ])).toEqual([
+     *     {interval: [-Infinity, -70], close: [0, 0]},
+     *     {interval: [-70, -26], close: [1, 1]},
+     *     {interval: [-26, 18], close: [0, 1]},
+     *     {interval: [18, 62], close: [0, 1]},
+     *     {interval: [62, 150], close: [0, 1]},
+     *     {interval: [150, Infinity], close: [0, 0]}
+     * ]);
+     * @param list, where `close` mean open or close
+     *        of the interval, and Infinity can be used.
+     * @return The origin list, which has been reformed.
+     */
+
     function reformIntervals(list) {
       list.sort(function (a, b) {
         return littleThan(a, b, 0) ? -1 : 1;
@@ -6859,16 +7026,52 @@
         return a.interval[lg] < b.interval[lg] || a.interval[lg] === b.interval[lg] && (a.close[lg] - b.close[lg] === (!lg ? 1 : -1) || !lg && littleThan(a, b, 1));
       }
     }
+    /**
+     * [Numberic is defined as]:
+     *     `parseFloat(val) == val`
+     * For example:
+     * numeric:
+     *     typeof number except NaN, '-123', '123', '2e3', '-2e3', '011', 'Infinity', Infinity,
+     *     and they rounded by white-spaces or line-terminal like ' -123 \n ' (see es spec)
+     * not-numeric:
+     *     null, undefined, [], {}, true, false, 'NaN', NaN, '123ab',
+     *     empty string, string with only white-spaces or line-terminal (see es spec),
+     *     0x12, '0x12', '-0x12', 012, '012', '-012',
+     *     non-string, ...
+     *
+     * @test See full test cases in `test/ut/spec/util/number.js`.
+     * @return Must be a typeof number. If not numeric, return NaN.
+     */
+
     function numericToNumber(val) {
       var valFloat = parseFloat(val);
-      return valFloat == val && (valFloat !== 0 || typeof val !== 'string' || val.indexOf('x') <= 0) ? valFloat : NaN;
+      return valFloat == val // eslint-disable-line eqeqeq
+      && (valFloat !== 0 || typeof val !== 'string' || val.indexOf('x') <= 0) // For case ' 0x0 '.
+      ? valFloat : NaN;
     }
+    /**
+     * Definition of "numeric": see `numericToNumber`.
+     */
+
     function isNumeric(val) {
       return !isNaN(numericToNumber(val));
     }
+    /**
+     * Use random base to prevent users hard code depending on
+     * this auto generated marker id.
+     * @return An positive integer.
+     */
+
     function getRandomIdBase() {
       return Math.round(Math.random() * 9);
     }
+    /**
+     * Get the greatest common dividor
+     *
+     * @param {number} a one number
+     * @param {number} b the other number
+     */
+
     function getGreatestCommonDividor(a, b) {
       if (b === 0) {
         return a;
@@ -6876,6 +7079,13 @@
 
       return getGreatestCommonDividor(b, a % b);
     }
+    /**
+     * Get the least common multiple
+     *
+     * @param {number} a one number
+     * @param {number} b the other number
+     */
+
     function getLeastCommonMultiple(a, b) {
       if (a == null) {
         return b;
@@ -6890,7 +7100,8 @@
 
     var ECHARTS_PREFIX = '[ECharts] ';
     var storedLogs = {};
-    var hasConsole = typeof console !== 'undefined' && console.warn && console.log;
+    var hasConsole = typeof console !== 'undefined' // eslint-disable-next-line
+    && console.warn && console.log;
     function warn(str) {
       if (hasConsole) {
         console.warn(ECHARTS_PREFIX + str);
@@ -6904,6 +7115,7 @@
     function deprecateLog(str) {
       if ("development" !== 'production') {
         if (storedLogs[str]) {
+          // Not display duplicate message.
           return;
         }
 
@@ -6926,11 +7138,24 @@
       }
 
       if ("development" !== 'production') {
+        /* eslint-disable no-console */
         if (typeof console !== 'undefined' && console.log) {
           console.log.apply(console, args);
         }
+        /* eslint-enable no-console */
+
       }
     }
+    /**
+     * If in __DEV__ environment, get console printable message for users hint.
+     * Parameters are separated by ' '.
+     * @usuage
+     * makePrintable('This is an error on', someVar, someObj);
+     *
+     * @param hintInfo anything about the current execution context to hint users.
+     * @throws Error
+     */
+
     function makePrintable() {
       var hintInfo = [];
 
@@ -6941,12 +7166,15 @@
       var msg = '';
 
       if ("development" !== 'production') {
+        // Fuzzy stringify for print.
+        // This code only exist in dev environment.
         var makePrintableStringIfPossible_1 = function (val) {
           return val === void 0 ? 'undefined' : val === Infinity ? 'Infinity' : val === -Infinity ? '-Infinity' : eqNaN(val) ? 'NaN' : val instanceof Date ? 'Date(' + val.toISOString() + ')' : isFunction(val) ? 'function () { ... }' : isRegExp(val) ? val + '' : null;
         };
 
         msg = map(hintInfo, function (arg) {
           if (isString(arg)) {
+            // Print without quotation mark for some statement.
             return arg;
           } else {
             var printableStr = makePrintableStringIfPossible_1(arg);
@@ -6958,7 +7186,7 @@
                 return JSON.stringify(arg, function (n, val) {
                   var printableStr = makePrintableStringIfPossible_1(val);
                   return printableStr == null ? val : printableStr;
-                });
+                }); // In most cases the info object is small, so do not line break.
               } catch (err) {
                 return '?';
               }
@@ -6971,20 +7199,50 @@
 
       return msg;
     }
+    /**
+     * @throws Error
+     */
+
     function throwError(msg) {
       throw new Error(msg);
     }
 
+    /**
+     * Make the name displayable. But we should
+     * make sure it is not duplicated with user
+     * specified name, so use '\0';
+     */
+
     var DUMMY_COMPONENT_NAME_PREFIX = 'series\0';
     var INTERNAL_COMPONENT_ID_PREFIX = '\0_ec_\0';
+    /**
+     * If value is not array, then translate it to array.
+     * @param  {*} value
+     * @return {Array} [value] or value
+     */
+
     function normalizeToArray(value) {
       return value instanceof Array ? value : value == null ? [] : [value];
     }
+    /**
+     * Sync default option between normal and emphasis like `position` and `show`
+     * In case some one will write code like
+     *     label: {
+     *          show: false,
+     *          position: 'outside',
+     *          fontSize: 18
+     *     },
+     *     emphasis: {
+     *          label: { show: true }
+     *     }
+     */
+
     function defaultEmphasis(opt, key, subOpts) {
+      // Caution: performance sensitive.
       if (opt) {
         opt[key] = opt[key] || {};
         opt.emphasis = opt.emphasis || {};
-        opt.emphasis[key] = opt.emphasis[key] || {};
+        opt.emphasis[key] = opt.emphasis[key] || {}; // Default emphasis option from normal
 
         for (var i = 0, len = subOpts.length; i < len; i++) {
           var subOptName = subOpts[i];
@@ -6995,20 +7253,69 @@
         }
       }
     }
-    var TEXT_STYLE_OPTIONS = ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily', 'rich', 'tag', 'color', 'textBorderColor', 'textBorderWidth', 'width', 'height', 'lineHeight', 'align', 'verticalAlign', 'baseline', 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY', 'textShadowColor', 'textShadowBlur', 'textShadowOffsetX', 'textShadowOffsetY', 'backgroundColor', 'borderColor', 'borderWidth', 'borderRadius', 'padding'];
+    var TEXT_STYLE_OPTIONS = ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily', 'rich', 'tag', 'color', 'textBorderColor', 'textBorderWidth', 'width', 'height', 'lineHeight', 'align', 'verticalAlign', 'baseline', 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY', 'textShadowColor', 'textShadowBlur', 'textShadowOffsetX', 'textShadowOffsetY', 'backgroundColor', 'borderColor', 'borderWidth', 'borderRadius', 'padding']; // modelUtil.LABEL_OPTIONS = modelUtil.TEXT_STYLE_OPTIONS [...]
+    //     'position', 'offset', 'rotate', 'origin', 'show', 'distance', 'formatter',
+    //     'fontStyle', 'fontWeight', 'fontSize', 'fontFamily',
+    //     // FIXME: deprecated, check and remove it.
+    //     'textStyle'
+    // ]);
+
+    /**
+     * The method do not ensure performance.
+     * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}]
+     * This helper method retieves value from data.
+     */
+
     function getDataItemValue(dataItem) {
       return isObject(dataItem) && !isArray(dataItem) && !(dataItem instanceof Date) ? dataItem.value : dataItem;
     }
+    /**
+     * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}]
+     * This helper method determine if dataItem has extra option besides value
+     */
+
     function isDataItemOption(dataItem) {
-      return isObject(dataItem) && !(dataItem instanceof Array);
+      return isObject(dataItem) && !(dataItem instanceof Array); // // markLine data can be array
+      // && !(dataItem[0] && isObject(dataItem[0]) && !(dataItem[0] instanceof Array));
     }
+    /**
+     * Mapping to existings for merge.
+     *
+     * Mode "normalMege":
+     *     The mapping result (merge result) will keep the order of the existing
+     *     component, rather than the order of new option. Because we should ensure
+     *     some specified index reference (like xAxisIndex) keep work.
+     *     And in most cases, "merge option" is used to update partial option but not
+     *     be expected to change the order.
+     *
+     * Mode "replaceMege":
+     *     (1) Only the id mapped components will be merged.
+     *     (2) Other existing components (except internal compoonets) will be removed.
+     *     (3) Other new options will be used to create new component.
+     *     (4) The index of the existing compoents will not be modified.
+     *     That means their might be "hole" after the removal.
+     *     The new components are created first at those available index.
+     *
+     * Mode "replaceAll":
+     *     This mode try to support that reproduce an echarts instance from another
+     *     echarts instance (via `getOption`) in some simple cases.
+     *     In this senario, the `result` index are exactly the consistent with the `newCmptOptions`,
+     *     which ensures the compoennt index referring (like `xAxisIndex: ?`) corrent. That is,
+     *     the "hole" in `newCmptOptions` will also be kept.
+     *     On the contrary, other modes try best to eliminate holes.
+     *     PENDING: This is an experimental mode yet.
+     *
+     * @return See the comment of <MappingResult>.
+     */
+
     function mappingToExists(existings, newCmptOptions, mode) {
       var isNormalMergeMode = mode === 'normalMerge';
       var isReplaceMergeMode = mode === 'replaceMerge';
       var isReplaceAllMode = mode === 'replaceAll';
       existings = existings || [];
       newCmptOptions = (newCmptOptions || []).slice();
-      var existingIdIdxMap = createHashMap();
+      var existingIdIdxMap = createHashMap(); // Validate id and name on user input option.
+
       each(newCmptOptions, function (cmptOption, index) {
         if (!isObject(cmptOption)) {
           newCmptOptions[index] = null;
@@ -7016,6 +7323,8 @@
         }
 
         if ("development" !== 'production') {
+          // There is some legacy case that name is set as `false`.
+          // But should work normally rather than throw error.
           if (cmptOption.id != null && !isValidIdOrName(cmptOption.id)) {
             warnInvalidateIdOrName(cmptOption.id);
           }
@@ -7041,7 +7350,9 @@
         mappingInReplaceAllMode(result, newCmptOptions);
       }
 
-      makeIdAndName(result);
+      makeIdAndName(result); // The array `result` MUST NOT contain elided items, otherwise the
+      // forEach will ommit those items and result in incorrect result.
+
       return result;
     }
 
@@ -7050,14 +7361,21 @@
 
       if (mode === 'replaceAll') {
         return result;
-      }
+      } // Do not use native `map` to in case that the array `existings`
+      // contains elided items, which will be ommited.
+
 
       for (var index = 0; index < existings.length; index++) {
-        var existing = existings[index];
+        var existing = existings[index]; // Because of replaceMerge, `existing` may be null/undefined.
 
         if (existing && existing.id != null) {
           existingIdIdxMap.set(existing.id, index);
-        }
+        } // For non-internal-componnets:
+        //     Mode "normalMerge": all existings kept.
+        //     Mode "replaceMerge": all existing removed unless mapped by id.
+        // For internal-components:
+        //     go with "replaceMerge" approach in both mode.
+
 
         result.push({
           existing: mode === 'replaceMerge' || isComponentIdInternal(existing) ? null : existing,
@@ -7071,6 +7389,7 @@
     }
 
     function mappingById(result, existings, existingIdIdxMap, newCmptOptions) {
+      // Mapping by id if specified.
       each(newCmptOptions, function (cmptOption, index) {
         if (!cmptOption || cmptOption.id == null) {
           return;
@@ -7082,7 +7401,9 @@
         if (existingIdx != null) {
           var resultItem = result[existingIdx];
           assert(!resultItem.newOption, 'Duplicated option on id "' + optionId + '".');
-          resultItem.newOption = cmptOption;
+          resultItem.newOption = cmptOption; // In both mode, if id matched, new option will be merged to
+          // the existings rather than creating new component model.
+
           resultItem.existing = existings[existingIdx];
           newCmptOptions[index] = null;
         }
@@ -7090,6 +7411,7 @@
     }
 
     function mappingByName(result, newCmptOptions) {
+      // Mapping by name if specified.
       each(newCmptOptions, function (cmptOption, index) {
         if (!cmptOption || cmptOption.name == null) {
           return;
@@ -7098,7 +7420,9 @@
         for (var i = 0; i < result.length; i++) {
           var existing = result[i].existing;
 
-          if (!result[i].newOption && existing && (existing.id == null || cmptOption.id == null) && !isComponentIdInternal(cmptOption) && !isComponentIdInternal(existing) && keyExistAndEqual('name', existing, cmptOption)) {
+          if (!result[i].newOption // Consider name: two map to one.
+          // Can not match when both ids existing but different.
+          && existing && (existing.id == null || cmptOption.id == null) && !isComponentIdInternal(cmptOption) && !isComponentIdInternal(existing) && keyExistAndEqual('name', existing, cmptOption)) {
             result[i].newOption = cmptOption;
             newCmptOptions[index] = null;
             return;
@@ -7111,12 +7435,21 @@
       each(newCmptOptions, function (cmptOption) {
         if (!cmptOption) {
           return;
-        }
+        } // Find the first place that not mapped by id and not internal component (consider the "hole").
+
 
         var resultItem;
         var nextIdx = 0;
 
-        while ((resultItem = result[nextIdx]) && (resultItem.newOption || isComponentIdInternal(resultItem.existing) || resultItem.existing && cmptOption.id != null && !keyExistAndEqual('id', cmptOption, resultItem.existing))) {
+        while ( // Be `!resultItem` only when `nextIdx >= result.length`.
+        (resultItem = result[nextIdx]) && ( // (1) Existing models that already have id should be able to mapped to. Because
+        // after mapping performed, model will always be assigned with an id if user not given.
+        // After that all models have id.
+        // (2) If new option has id, it can only set to a hole or append to the last. It should
+        // not be merged to the existings with different id. Because id should not be overwritten.
+        // (3) Name can be overwritten, because axis use name as 'show label text'.
+        resultItem.newOption || isComponentIdInternal(resultItem.existing) || // In mode "replaceMerge", here no not-mapped-non-internal-existing.
+        resultItem.existing && cmptOption.id != null && !keyExistAndEqual('id', cmptOption, resultItem.existing))) {
           nextIdx++;
         }
 
@@ -7138,6 +7471,8 @@
 
     function mappingInReplaceAllMode(result, newCmptOptions) {
       each(newCmptOptions, function (cmptOption) {
+        // The feature "reproduce" requires "hole" will also reproduced
+        // in case that compoennt index referring are broken.
         result.push({
           newOption: cmptOption,
           brandNew: true,
@@ -7146,19 +7481,35 @@
         });
       });
     }
+    /**
+     * Make id and name for mapping result (result of mappingToExists)
+     * into `keyInfo` field.
+     */
+
 
     function makeIdAndName(mapResult) {
+      // We use this id to hash component models and view instances
+      // in echarts. id can be specified by user, or auto generated.
+      // The id generation rule ensures new view instance are able
+      // to mapped to old instance when setOption are called in
+      // no-merge mode. So we generate model id by name and plus
+      // type in view id.
+      // name can be duplicated among components, which is convenient
+      // to specify multi components (like series) by one name.
+      // Ensure that each id is distinct.
       var idMap = createHashMap();
       each(mapResult, function (item) {
         var existing = item.existing;
         existing && idMap.set(existing.id, item);
       });
       each(mapResult, function (item) {
-        var opt = item.newOption;
+        var opt = item.newOption; // Force ensure id not duplicated.
+
         assert(!opt || opt.id == null || !idMap.get(opt.id) || idMap.get(opt.id) === item, 'id duplicates: ' + (opt && opt.id));
         opt && opt.id != null && idMap.set(opt.id, item);
         !item.keyInfo && (item.keyInfo = {});
-      });
+      }); // Make name and id.
+
       each(mapResult, function (item, index) {
         var existing = item.existing;
         var opt = item.newOption;
@@ -7166,15 +7517,26 @@
 
         if (!isObject(opt)) {
           return;
-        }
+        } // name can be overwitten. Consider case: axis.name = '20km'.
+        // But id generated by name will not be changed, which affect
+        // only in that case: setOption with 'not merge mode' and view
+        // instance will be recreated, which can be accepted.
 
-        keyInfo.name = opt.name != null ? makeComparableKey(opt.name) : existing ? existing.name : DUMMY_COMPONENT_NAME_PREFIX + index;
+
+        keyInfo.name = opt.name != null ? makeComparableKey(opt.name) : existing ? existing.name // Avoid diffferent series has the same name,
+        // because name may be used like in color pallet.
+        : DUMMY_COMPONENT_NAME_PREFIX + index;
 
         if (existing) {
           keyInfo.id = makeComparableKey(existing.id);
         } else if (opt.id != null) {
           keyInfo.id = makeComparableKey(opt.id);
         } else {
+          // Consider this situatoin:
+          //  optionA: [{name: 'a'}, {name: 'a'}, {..}]
+          //  optionB [{..}, {name: 'a'}, {name: 'a'}]
+          // Series with the same name between optionA and optionB
+          // should be mapped.
           var idNum = 0;
 
           do {
@@ -7188,9 +7550,14 @@
 
     function keyExistAndEqual(attr, obj1, obj2) {
       var key1 = convertOptionIdName(obj1[attr], null);
-      var key2 = convertOptionIdName(obj2[attr], null);
+      var key2 = convertOptionIdName(obj2[attr], null); // See `MappingExistingItem`. `id` and `name` trade string equals to number.
+
       return key1 != null && key2 != null && key1 === key2;
     }
+    /**
+     * @return return null if not exist.
+     */
+
 
     function makeComparableKey(val) {
       if ("development" !== 'production') {
@@ -7222,9 +7589,16 @@
     }
 
     function isNameSpecified(componentModel) {
-      var name = componentModel.name;
+      var name = componentModel.name; // Is specified when `indexOf` get -1 or > 0.
+
       return !!(name && name.indexOf(DUMMY_COMPONENT_NAME_PREFIX));
     }
+    /**
+     * @public
+     * @param {Object} cmptOption
+     * @return {boolean}
+     */
+
     function isComponentIdInternal(cmptOption) {
       return cmptOption && cmptOption.id != null && makeComparableKey(cmptOption.id).indexOf(INTERNAL_COMPONENT_ID_PREFIX) === 0;
     }
@@ -7232,6 +7606,7 @@
       return INTERNAL_COMPONENT_ID_PREFIX + idSuffix;
     }
     function setComponentTypeToKeyInfo(mappingResult, mainType, componentModelCtor) {
+      // Set mainType and complete subType.
       each(mappingResult, function (item) {
         var newOption = item.newOption;
 
@@ -7243,9 +7618,17 @@
     }
 
     function determineSubType(mainType, newCmptOption, existComponent, componentModelCtor) {
-      var subType = newCmptOption.type ? newCmptOption.type : existComponent ? existComponent.subType : componentModelCtor.determineSubType(mainType, newCmptOption);
+      var subType = newCmptOption.type ? newCmptOption.type : existComponent ? existComponent.subType // Use determineSubType only when there is no existComponent.
+      : componentModelCtor.determineSubType(mainType, newCmptOption); // tooltip, markline, markpoint may always has no subType
+
       return subType;
     }
+    /**
+     * @param payload Contains dataIndex (means rawIndex) / dataIndexInside / name
+     *                         each of which can be Array or primary type.
+     * @return dataIndex If not found, return undefined/null.
+     */
+
     function queryDataIndex(data, payload) {
       if (payload.dataIndexInside != null) {
         return payload.dataIndexInside;
@@ -7259,6 +7642,27 @@
         }) : data.indexOfName(payload.name);
       }
     }
+    /**
+     * Enable property storage to any host object.
+     * Notice: Serialization is not supported.
+     *
+     * For example:
+     * let inner = zrUitl.makeInner();
+     *
+     * function some1(hostObj) {
+     *      inner(hostObj).someProperty = 1212;
+     *      ...
+     * }
+     * function some2() {
+     *      let fields = inner(this);
+     *      fields.someProperty1 = 1212;
+     *      fields.someProperty2 = 'xx';
+     *      ...
+     * }
+     *
+     * @return {Function}
+     */
+
     function makeInner() {
       var key = '__ec_inner_' + innerUniqueIndex++;
       return function (hostObj) {
@@ -7266,6 +7670,10 @@
       };
     }
     var innerUniqueIndex = getRandomIdBase();
+    /**
+     * The same behavior as `component.getReferringComponents`.
+     */
+
     function parseFinder(ecModel, finderInput, opt) {
       var finder;
 
@@ -7281,6 +7689,7 @@
       var result = {};
       var mainTypeSpecified = false;
       each(finder, function (value, key) {
+        // Exclude 'dataIndex' and other illgal keys.
         if (key === 'dataIndex' || key === 'dataIndexInside') {
           result[key] = value;
           return;
@@ -7336,6 +7745,7 @@
       };
 
       if (!result.specified) {
+        // Use the first as default if `useDefault`.
         var firstCmpt = void 0;
         result.models = opt.useDefault && (firstCmpt = ecModel.getComponent(mainType)) ? [firstCmpt] : [];
         return result;
@@ -7345,7 +7755,9 @@
         assert(opt.enableNone, '`"none"` or `false` is not a valid value on index option.');
         result.models = [];
         return result;
-      }
+      } // `queryComponents` will return all components if
+      // both all of index/id/name are null/undefined.
+
 
       if (indexOption === 'all') {
         assert(opt.enableAll, '`"all"` is not a valid value on index option.');
@@ -7368,11 +7780,27 @@
     }
     function getTooltipRenderMode(renderModeOption) {
       if (renderModeOption === 'auto') {
+        // Using html when `document` exists, use richText otherwise
         return env.domSupported ? 'html' : 'richText';
       } else {
         return renderModeOption || 'html';
       }
     }
+    /**
+     * Interpolate raw values of a series with percent
+     *
+     * @param data         data
+     * @param labelModel   label model of the text element
+     * @param sourceValue  start value. May be null/undefined when init.
+     * @param targetValue  end value
+     * @param percent      0~1 percentage; 0 uses start value while 1 uses end value
+     * @return             interpolated values
+     *                     If `sourceValue` and `targetValue` are `number`, return `number`.
+     *                     If `sourceValue` and `targetValue` are `string`, return `string`.
+     *                     If `sourceValue` and `targetValue` are `(string | number)[]`, return `(string | number)[]`.
+     *                     Other cases do not supported.
+     */
+
     function interpolateRawValues(data, precision, sourceValue, targetValue, percent) {
       var isAutoPrecision = precision == null || precision === 'auto';
 
@@ -7392,9 +7820,10 @@
         var length_1 = Math.max(leftArr ? leftArr.length : 0, rightArr.length);
 
         for (var i = 0; i < length_1; ++i) {
-          var info = data.getDimensionInfo(i);
+          var info = data.getDimensionInfo(i); // Don't interpolate ordinal dims
 
           if (info.type === 'ordinal') {
+            // In init, there is no `sourceValue`, but should better not to get undefined result.
             interpolated[i] = (percent < 1 && leftArr ? leftArr : rightArr)[i];
           } else {
             var leftVal = leftArr && leftArr[i] ? leftArr[i] : 0;
@@ -7411,6 +7840,11 @@
     var TYPE_DELIMITER = '.';
     var IS_CONTAINER = '___EC__COMPONENT__CONTAINER___';
     var IS_EXTENDED_CLASS = '___EC__EXTENDED_CLASS___';
+    /**
+     * Notice, parseClassType('') should returns {main: '', sub: ''}
+     * @public
+     */
+
     function parseClassType(componentType) {
       var ret = {
         main: '',
@@ -7425,6 +7859,9 @@
 
       return ret;
     }
+    /**
+     * @public
+     */
 
     function checkClassType(componentType) {
       assert(/^[a-zA-Z0-9_]+([.][a-zA-Z0-9_]+)?$/.test(componentType), 'componentType "' + componentType + '" illegal');
@@ -7433,8 +7870,19 @@
     function isExtendedClass(clz) {
       return !!(clz && clz[IS_EXTENDED_CLASS]);
     }
+    /**
+     * Implements `ExtendableConstructor` for `rootClz`.
+     *
+     * @usage
+     * ```ts
+     * class Xxx {}
+     * type XxxConstructor = typeof Xxx & ExtendableConstructor
+     * enableClassExtend(Xxx as XxxConstructor);
+     * ```
+     */
+
     function enableClassExtend(rootClz, mandatoryMethods) {
-      rootClz.$constructor = rootClz;
+      rootClz.$constructor = rootClz; // FIXME: not necessary?
 
       rootClz.extend = function (proto) {
         if ("development" !== 'production') {
@@ -7445,7 +7893,13 @@
           });
         }
 
-        var superClass = this;
+        var superClass = this; // For backward compat, we both support ts class inheritance and this
+        // "extend" approach.
+        // The constructor should keep the same behavior as ts class inheritance:
+        // If this constructor/$constructor is not declared, auto invoke the super
+        // constructor.
+        // If this constructor/$constructor is declared, it is responsible for
+        // calling the super constructor.
 
         function ExtendedClass() {
           var args = [];
@@ -7456,9 +7910,11 @@
 
           if (!proto.$constructor) {
             if (!isESClass(superClass)) {
+              // Will throw error if superClass is an es6 native class.
               superClass.apply(this, arguments);
             } else {
-              var ins = createObject(ExtendedClass.prototype, new (superClass.bind.apply(superClass, __spreadArrays([void 0], args)))());
+              var ins = createObject( // @ts-ignore
+              ExtendedClass.prototype, new (superClass.bind.apply(superClass, __spreadArrays([void 0], args)))());
               return ins;
             }
           } else {
@@ -7480,11 +7936,41 @@
     function isESClass(fn) {
       return typeof fn === 'function' && /^class\s/.test(Function.prototype.toString.call(fn));
     }
+    /**
+     * A work around to both support ts extend and this extend mechanism.
+     * on sub-class.
+     * @usage
+     * ```ts
+     * class Component { ... }
+     * classUtil.enableClassExtend(Component);
+     * classUtil.enableClassManagement(Component, {registerWhenExtend: true});
+     *
+     * class Series extends Component { ... }
+     * // Without calling `markExtend`, `registerWhenExtend` will not work.
+     * Component.markExtend(Series);
+     * ```
+     */
+
 
     function mountExtend(SubClz, SupperClz) {
       SubClz.extend = SupperClz.extend;
-    }
+    } // A random offset.
+
     var classBase = Math.round(Math.random() * 10);
+    /**
+     * Implements `CheckableConstructor` for `target`.
+     * Can not use instanceof, consider different scope by
+     * cross domain or es module import in ec extensions.
+     * Mount a method "isInstance()" to Clz.
+     *
+     * @usage
+     * ```ts
+     * class Xxx {}
+     * type XxxConstructor = typeof Xxx & CheckableConstructor;
+     * enableClassCheck(Xxx as XxxConstructor)
+     * ```
+     */
+
     function enableClassCheck(target) {
       var classAttr = ['__\0is_clz', classBase++].join('_');
       target.prototype[classAttr] = true;
@@ -7496,7 +7982,12 @@
       target.isInstance = function (obj) {
         return !!(obj && obj[classAttr]);
       };
-    }
+    } // superCall should have class info, which can not be fetch from 'this'.
+    // Consider this case:
+    // class A has method f,
+    // class B inherits class A, overrides method f, f call superApply('f'),
+    // class C inherits class B, do not overrides method f,
+    // then when method of class C is called, dead loop occured.
 
     function superCall(context, methodName) {
       var args = [];
@@ -7511,15 +8002,39 @@
     function superApply(context, methodName, args) {
       return this.superClass.prototype[methodName].apply(context, args);
     }
+    /**
+     * Implements `ClassManager` for `target`
+     *
+     * @usage
+     * ```ts
+     * class Xxx {}
+     * type XxxConstructor = typeof Xxx & ClassManager
+     * enableClassManagement(Xxx as XxxConstructor);
+     * ```
+     */
+
 
     function enableClassManagement(target) {
+      /**
+       * Component model classes
+       * key: componentType,
+       * value:
+       *     componentClass, when componentType is 'xxx'
+       *     or Object.<subKey, componentClass>, when componentType is 'xxx.yy'
+       */
       var storage = {};
 
       target.registerClass = function (clz) {
+        // `type` should not be a "instance memeber".
+        // If using TS class, should better declared as `static type = 'series.pie'`.
+        // otherwise users have to mount `type` on prototype manually.
+        // For backward compat and enable instance visit type via `this.type`,
+        // we stil support fetch `type` from prototype.
         var componentFullType = clz.type || clz.prototype.type;
 
         if (componentFullType) {
-          checkClassType(componentFullType);
+          checkClassType(componentFullType); // If only static type declared, we assign it to prototype mandatorily.
+
           clz.prototype.type = componentFullType;
           var componentTypeInfo = parseClassType(componentFullType);
 
@@ -7571,9 +8086,14 @@
       };
 
       target.hasClass = function (componentType) {
+        // Just consider componentType.main.
         var componentTypeInfo = parseClassType(componentType);
         return !!storage[componentTypeInfo.main];
       };
+      /**
+       * @return Like ['aa', 'bb'], but can not be ['aa.xx']
+       */
+
 
       target.getAllClassMainTypes = function () {
         var types = [];
@@ -7582,6 +8102,10 @@
         });
         return types;
       };
+      /**
+       * If a main type is container and has sub types
+       */
+
 
       target.hasSubTypes = function (componentType) {
         var componentTypeInfo = parseClassType(componentType);
@@ -7599,9 +8123,28 @@
 
         return container;
       }
-    }
+    } // /**
+    //  * @param {string|Array.<string>} properties
+    //  */
+    // export function setReadOnly(obj, properties) {
+    // FIXME It seems broken in IE8 simulation of IE11
+    // if (!zrUtil.isArray(properties)) {
+    //     properties = properties != null ? [properties] : [];
+    // }
+    // zrUtil.each(properties, function (prop) {
+    //     let value = obj[prop];
+    //     Object.defineProperty
+    //         && Object.defineProperty(obj, prop, {
+    //             value: value, writable: false
+    //         });
+    //     zrUtil.isArray(obj[prop])
+    //         && Object.freeze
+    //         && Object.freeze(obj[prop]);
+    // });
+    // }
 
     function makeStyleMapper(properties, ignoreParent) {
+      // Normalize
       for (var i = 0; i < properties.length; i++) {
         if (!properties[i][1]) {
           properties[i][1] = properties[i][0];
@@ -7624,16 +8167,21 @@
           if (val != null) {
             style[properties[i][0]] = val;
           }
-        }
+        } // TODO Text or image?
+
 
         return style;
       };
     }
 
-    var AREA_STYLE_KEY_MAP = [['fill', 'color'], ['shadowBlur'], ['shadowOffsetX'], ['shadowOffsetY'], ['opacity'], ['shadowColor']];
+    var AREA_STYLE_KEY_MAP = [['fill', 'color'], ['shadowBlur'], ['shadowOffsetX'], ['shadowOffsetY'], ['opacity'], ['shadowColor'] // Option decal is in `DecalObject` but style.decal is in `PatternObject`.
+    // So do not transfer decal directly.
+    ];
     var getAreaStyle = makeStyleMapper(AREA_STYLE_KEY_MAP);
 
-    var AreaStyleMixin = function () {
+    var AreaStyleMixin =
+    /** @class */
+    function () {
       function AreaStyleMixin() {}
 
       AreaStyleMixin.prototype.getAreaStyle = function (excludes, includes) {
@@ -11374,7 +11922,8 @@
 
     function hasFillOrStroke(fillOrStroke) {
       return fillOrStroke != null && fillOrStroke !== 'none';
-    }
+    } // Most lifted color are duplicated.
+
 
     var liftedColorCache = new LRU(100);
 
@@ -11402,10 +11951,14 @@
     }
 
     function singleEnterEmphasis(el) {
+      // Only mark the flag.
+      // States will be applied in the echarts.ts in next frame.
       doChangeHoverState(el, 'emphasis', HOVER_STATE_EMPHASIS);
     }
 
     function singleLeaveEmphasis(el) {
+      // Only mark the flag.
+      // States will be applied in the echarts.ts in next frame.
       if (el.hoverState === HOVER_STATE_EMPHASIS) {
         doChangeHoverState(el, 'normal', HOVER_STATE_NORMAL);
       }
@@ -11472,7 +12025,8 @@
       for (var i = 0; i < el.animators.length; i++) {
         var animator = el.animators[i];
 
-        if (animator.__fromStateTransition && animator.__fromStateTransition.indexOf(toStateName) < 0 && animator.targetName === 'style') {
+        if (animator.__fromStateTransition // Dont consider the animation to emphasis state.
+        && animator.__fromStateTransition.indexOf(toStateName) < 0 && animator.targetName === 'style') {
           animator.saveFinalToTarget(fromState, props);
         }
       }
@@ -11490,28 +12044,33 @@
         var fromStroke = hasSelect ? store.selectStroke || store.normalStroke : store.normalStroke;
 
         if (hasFillOrStroke(fromFill) || hasFillOrStroke(fromStroke)) {
-          state = state || {};
+          state = state || {}; // Apply default color lift
+
           var emphasisStyle = state.style || {};
 
           if (!hasFillOrStroke(emphasisStyle.fill) && hasFillOrStroke(fromFill)) {
-            cloned = true;
+            cloned = true; // Not modify the original value.
+
             state = extend({}, state);
-            emphasisStyle = extend({}, emphasisStyle);
+            emphasisStyle = extend({}, emphasisStyle); // Already being applied 'emphasis'. DON'T lift color multiple times.
+
             emphasisStyle.fill = liftColor(fromFill);
-          } else if (!hasFillOrStroke(emphasisStyle.stroke) && hasFillOrStroke(fromStroke)) {
-            if (!cloned) {
-              state = extend({}, state);
-              emphasisStyle = extend({}, emphasisStyle);
-            }
+          } // Not highlight stroke if fill has been highlighted.
+          else if (!hasFillOrStroke(emphasisStyle.stroke) && hasFillOrStroke(fromStroke)) {
+              if (!cloned) {
+                state = extend({}, state);
+                emphasisStyle = extend({}, emphasisStyle);
+              }
 
-            emphasisStyle.stroke = liftColor(fromStroke);
-          }
+              emphasisStyle.stroke = liftColor(fromStroke);
+            }
 
           state.style = emphasisStyle;
         }
       }
 
       if (state) {
+        // TODO Share with textContent?
         if (state.z2 == null) {
           if (!cloned) {
             state = extend({}, state);
@@ -11526,7 +12085,9 @@
     }
 
     function createSelectDefaultState(el, stateName, state) {
+      // const hasSelect = indexOf(el.currentStates, stateName) >= 0;
       if (state) {
+        // TODO Share with textContent?
         if (state.z2 == null) {
           state = extend({}, state);
           var z2SelectLift = el.z2SelectLift;
@@ -11547,8 +12108,10 @@
       var blurStyle = state.style || {};
 
       if (blurStyle.opacity == null) {
+        // clone state
         state = extend({}, state);
         blurStyle = extend({
+          // Already being applied 'emphasis'. DON'T mul opacity multiple times.
           opacity: hasBlur ? currentOpacity : fromState.opacity * 0.1
         }, blurStyle);
         state.style = blurStyle;
@@ -11572,6 +12135,12 @@
 
       return state;
     }
+    /**FI
+     * Set hover style (namely "emphasis style") of element.
+     * @param el Should not be `zrender/graphic/Group`.
+     * @param focus 'self' | 'selfInSeries' | 'series'
+     */
+
 
     function setDefaultStateProxy(el) {
       el.stateProxy = elementStateProxy;
@@ -11587,10 +12156,12 @@
       }
     }
     function enterEmphasisWhenMouseOver(el, e) {
-      !shouldSilent(el, e) && !el.__highByOuter && traverseUpdateState(el, singleEnterEmphasis);
+      !shouldSilent(el, e) // "emphasis" event highlight has higher priority than mouse highlight.
+      && !el.__highByOuter && traverseUpdateState(el, singleEnterEmphasis);
     }
     function leaveEmphasisWhenMouseOut(el, e) {
-      !shouldSilent(el, e) && !el.__highByOuter && traverseUpdateState(el, singleLeaveEmphasis);
+      !shouldSilent(el, e) // "emphasis" event highlight has higher priority than mouse highlight.
+      && !el.__highByOuter && traverseUpdateState(el, singleLeaveEmphasis);
     }
     function enterEmphasis(el, highlightDigit) {
       el.__highByOuter |= 1 << (highlightDigit || 0);
@@ -11619,7 +12190,8 @@
     function allLeaveBlur(api) {
       var model = api.getModel();
       model.eachComponent(function (componentType, componentModel) {
-        var view = componentType === 'series' ? api.getViewOfSeriesModel(componentModel) : api.getViewOfComponentModel(componentModel);
+        var view = componentType === 'series' ? api.getViewOfSeriesModel(componentModel) : api.getViewOfComponentModel(componentModel); // Leave blur anyway
+
         view.group.traverse(function (child) {
           singleLeaveBlur(child);
         });
@@ -11666,9 +12238,13 @@
           coordSys = coordSys.master;
         }
 
-        var sameCoordSys = coordSys && targetCoordSys ? coordSys === targetCoordSys : sameSeries;
+        var sameCoordSys = coordSys && targetCoordSys ? coordSys === targetCoordSys : sameSeries; // If there is no coordinate system. use sameSeries instead.
 
-        if (!(blurScope === 'series' && !sameSeries || blurScope === 'coordinateSystem' && !sameCoordSys || focus === 'series' && sameSeries)) {
+        if (!( // Not blur other series if blurScope series
+        blurScope === 'series' && !sameSeries // Not blur other coordinate system if blurScope is coordinateSystem
+        || blurScope === 'coordinateSystem' && !sameCoordSys // Not blur self series if focus is series.
+        || focus === 'series' && sameSeries // TODO blurScope: coordinate system
+        )) {
           var view = api.getViewOfSeriesModel(seriesModel);
           view.group.traverse(function (child) {
             singleEnterBlur(child);
@@ -11707,13 +12283,14 @@
       var isHighlight = payload.type === HIGHLIGHT_ACTION_TYPE;
       var seriesIndex = seriesModel.seriesIndex;
       var data = seriesModel.getData(payload.dataType);
-      var dataIndex = queryDataIndex(data, payload);
+      var dataIndex = queryDataIndex(data, payload); // Pick the first one if there is multiple/none exists.
+
       dataIndex = (isArray(dataIndex) ? dataIndex[0] : dataIndex) || 0;
       var el = data.getItemGraphicEl(dataIndex);
 
       if (!el) {
         var count = data.count();
-        var current = 0;
+        var current = 0; // If data on dataIndex is NaN.
 
         while (!el && current < count) {
           el = data.getItemGraphicEl(current++);
@@ -11724,6 +12301,8 @@
         var ecData = getECData(el);
         toggleSeriesBlurState(seriesIndex, ecData.focus, ecData.blurScope, api, isHighlight);
       } else {
+        // If there is no element put on the data. Try getting it from raw option
+        // TODO Should put it on seriesModel?
         var focus_1 = seriesModel.get(['emphasis', 'focus']);
         var blurScope = seriesModel.get(['emphasis', 'blurScope']);
 
@@ -11782,6 +12361,14 @@
       });
       return ret;
     }
+    /**
+     * Enable the function that mouseover will trigger the emphasis state.
+     *
+     * NOTE:
+     * This function should be used on the element with dataIndex, seriesIndex.
+     *
+     */
+
     function enableHoverEmphasis(el, focus, blurScope) {
       setAsHighDownDispatcher(el, true);
       traverseUpdateState(el, setDefaultStateProxy);
@@ -11791,8 +12378,15 @@
       var ecData = getECData(el);
 
       if (focus != null) {
+        // TODO dataIndex may be set after this function. This check is not useful.
+        // if (ecData.dataIndex == null) {
+        //     if (__DEV__) {
+        //         console.warn('focus can only been set on element with dataIndex');
+        //     }
+        // }
+        // else {
         ecData.focus = focus;
-        ecData.blurScope = blurScope;
+        ecData.blurScope = blurScope; // }
       } else if (ecData.focus) {
         ecData.focus = null;
       }
@@ -11803,25 +12397,52 @@
       lineStyle: 'getLineStyle',
       areaStyle: 'getAreaStyle'
     };
-    function setStatesStylesFromModel(el, itemModel, styleType, getter) {
+    /**
+     * Set emphasis/blur/selected states of element.
+     */
+
+    function setStatesStylesFromModel(el, itemModel, styleType, // default itemStyle
+    getter) {
       styleType = styleType || 'itemStyle';
 
       for (var i = 0; i < OTHER_STATES.length; i++) {
         var stateName = OTHER_STATES[i];
         var model = itemModel.getModel([stateName, styleType]);
-        var state = el.ensureState(stateName);
+        var state = el.ensureState(stateName); // Let it throw error if getterType is not found.
+
         state.style = getter ? getter(model) : model[defaultStyleGetterMap[styleType]]();
       }
     }
+    /**
+     * @parame el
+     * @param el.highDownSilentOnTouch
+     *        In touch device, mouseover event will be trigger on touchstart event
+     *        (see module:zrender/dom/HandlerProxy). By this mechanism, we can
+     *        conveniently use hoverStyle when tap on touch screen without additional
+     *        code for compatibility.
+     *        But if the chart/component has select feature, which usually also use
+     *        hoverStyle, there might be conflict between 'select-highlight' and
+     *        'hover-highlight' especially when roam is enabled (see geo for example).
+     *        In this case, `highDownSilentOnTouch` should be used to disable
+     *        hover-highlight on touch device.
+     * @param asDispatcher If `false`, do not set as "highDownDispatcher".
+     */
+
     function setAsHighDownDispatcher(el, asDispatcher) {
       var disable = asDispatcher === false;
-      var extendedEl = el;
+      var extendedEl = el; // Make `highDownSilentOnTouch` and `onStateChange` only work after
+      // `setAsHighDownDispatcher` called. Avoid it is modified by user unexpectedly.
 
       if (el.highDownSilentOnTouch) {
         extendedEl.__highDownSilentOnTouch = el.highDownSilentOnTouch;
-      }
+      } // Simple optimize, since this method might be
+      // called for each elements of a group in some cases.
+
 
       if (!disable || extendedEl.__highDownDispatcher) {
+        // Emphasis, normal can be triggered manually by API or other components like hover link.
+        // el[method]('emphasis', onElementEmphasisEvent)[method]('normal', onElementNormalEvent);
+        // Also keep previous record.
         extendedEl.__highByOuter = extendedEl.__highByOuter || 0;
         extendedEl.__highDownDispatcher = !disable;
       }
@@ -11829,6 +12450,15 @@
     function isHighDownDispatcher(el) {
       return !!(el && el.__highDownDispatcher);
     }
+    /**
+     * Support hightlight/downplay record on each elements.
+     * For the case: hover highlight/downplay (legend, visualMap, ...) and
+     * user triggerred hightlight/downplay should not conflict.
+     * Only all of the highlightDigit cleared, return to normal.
+     * @param {string} highlightKey
+     * @return {number} highlightDigit
+     */
+
     function getHighlightDigit(highlightKey) {
       var highlightDigit = _highlightKeyMap[highlightKey];
 
@@ -12422,9 +13052,15 @@
         var clockwise = !!shape.clockwise;
         var startAngle = shape.startAngle;
         var endAngle = shape.endAngle;
-        var tmpAngles = [startAngle, endAngle];
-        normalizeArcAngles(tmpAngles, !clockwise);
-        var arc = mathAbs$1(tmpAngles[0] - tmpAngles[1]);
+        var arc;
+        if (startAngle === endAngle) {
+            arc = 0;
+        }
+        else {
+            var tmpAngles = [startAngle, endAngle];
+            normalizeArcAngles(tmpAngles, !clockwise);
+            arc = mathAbs$1(tmpAngles[0] - tmpAngles[1]);
+        }
         var x = shape.cx;
         var y = shape.cy;
         var cornerRadius = shape.cornerRadius || 0;
@@ -13287,21 +13923,77 @@
     var mathMax$4 = Math.max;
     var mathMin$4 = Math.min;
     var _customShapeMap = {};
+    /**
+     * Extend shape with parameters
+     */
+
     function extendShape(opts) {
       return Path.extend(opts);
     }
     var extendPathFromString = extendFromString;
+    /**
+     * Extend path
+     */
+
     function extendPath(pathData, opts) {
       return extendPathFromString(pathData, opts);
     }
+    /**
+     * Register a user defined shape.
+     * The shape class can be fetched by `getShapeClass`
+     * This method will overwrite the registered shapes, including
+     * the registered built-in shapes, if using the same `name`.
+     * The shape can be used in `custom series` and
+     * `graphic component` by declaring `{type: name}`.
+     *
+     * @param name
+     * @param ShapeClass Can be generated by `extendShape`.
+     */
+
     function registerShape(name, ShapeClass) {
       _customShapeMap[name] = ShapeClass;
     }
+    /**
+     * Find shape class registered by `registerShape`. Usually used in
+     * fetching user defined shape.
+     *
+     * [Caution]:
+     * (1) This method **MUST NOT be used inside echarts !!!**, unless it is prepared
+     * to use user registered shapes.
+     * Because the built-in shape (see `getBuiltInShape`) will be registered by
+     * `registerShape` by default. That enables users to get both built-in
+     * shapes as well as the shapes belonging to themsleves. But users can overwrite
+     * the built-in shapes by using names like 'circle', 'rect' via calling
+     * `registerShape`. So the echarts inner featrues should not fetch shapes from here
+     * in case that it is overwritten by users, except that some features, like
+     * `custom series`, `graphic component`, do it deliberately.
+     *
+     * (2) In the features like `custom series`, `graphic component`, the user input
+     * `{tpye: 'xxx'}` does not only specify shapes but also specify other graphic
+     * elements like `'group'`, `'text'`, `'image'` or event `'path'`. Those names
+     * are reserved names, that is, if some user register a shape named `'image'`,
+     * the shape will not be used. If we intending to add some more reserved names
+     * in feature, that might bring break changes (disable some existing user shape
+     * names). But that case probably rearly happen. So we dont make more mechanism
+     * to resolve this issue here.
+     *
+     * @param name
+     * @return The shape class. If not found, return nothing.
+     */
+
     function getShapeClass(name) {
       if (_customShapeMap.hasOwnProperty(name)) {
         return _customShapeMap[name];
       }
     }
+    /**
+     * Create a path element from path data string
+     * @param pathData
+     * @param opts
+     * @param rect
+     * @param layout 'center' or 'cover' default to be cover
+     */
+
     function makePath(pathData, opts, rect, layout) {
       var path = createFromString(pathData, opts);
 
@@ -13315,6 +14007,14 @@
 
       return path;
     }
+    /**
+     * Create a image element from image url
+     * @param imageUrl image url
+     * @param opts options
+     * @param rect constrain rect
+     * @param layout 'center' or 'cover'. Default to be 'cover'
+     */
+
     function makeImage(imageUrl, rect, layout) {
       var zrImg = new ZRImage({
         style: {
@@ -13336,8 +14036,16 @@
       });
       return zrImg;
     }
+    /**
+     * Get position of centered element in bounding box.
+     *
+     * @param  rect         element local bounding box
+     * @param  boundingRect constraint bounding box
+     * @return element position containing x, y, width, and height
+     */
 
     function centerGraphic(rect, boundingRect) {
+      // Set rect to center, keep width / height ratio.
       var aspect = boundingRect.width / boundingRect.height;
       var width = rect.height * aspect;
       var height;
@@ -13360,6 +14068,12 @@
     }
 
     var mergePath$1 = mergePath;
+    /**
+     * Resize a path to fit the rect
+     * @param path
+     * @param rect
+     */
+
     function resizePath(path, rect) {
       if (!path.applyTransform) {
         return;
@@ -13369,14 +14083,31 @@
       var m = pathRect.calculateTransform(rect);
       path.applyTransform(m);
     }
+    /**
+     * Sub pixel optimize line for canvas
+     */
+
     function subPixelOptimizeLine$1(param) {
       subPixelOptimizeLine(param.shape, param.shape, param.style);
       return param;
     }
+    /**
+     * Sub pixel optimize rect for canvas
+     */
+
     function subPixelOptimizeRect$1(param) {
       subPixelOptimizeRect(param.shape, param.shape, param.style);
       return param;
     }
+    /**
+     * Sub pixel optimize for canvas
+     *
+     * @param position Coordinate, such as x, y
+     * @param lineWidth Should be nonnegative integer.
+     * @param positiveOrNegative Default false (negative).
+     * @return Optimized position.
+     */
+
     var subPixelOptimize$1 = subPixelOptimize;
 
     function animateOrSetProps(animationType, el, props, animatableModel, dataIndex, cb, during) {
@@ -13397,7 +14128,9 @@
 
       var isUpdate = animationType === 'update';
       var isRemove = animationType === 'remove';
-      var animationPayload;
+      var animationPayload; // Check if there is global animation configuration from dataZoom/resize can override the config in option.
+      // If animation is enabled. Will use this animation config in payload.
+      // If animation is disabled. Just ignore it.
 
       if (animatableModel && animatableModel.ecModel) {
         var updatePayload = animatableModel.ecModel.getUpdatePayload();
@@ -13407,6 +14140,7 @@
       var animationEnabled = animatableModel && animatableModel.isAnimationEnabled();
 
       if (!isRemove) {
+        // Must stop the remove animation.
         el.stopAnimation('remove');
       }
 
@@ -13455,22 +14189,61 @@
           setToFinal: true,
           scope: animationType,
           during: during
-        }) : (el.stopAnimation(), !isFrom && el.attr(props), cb && cb());
+        }) : ( // FIXME:
+        // If `duration` is 0, only the animation on props
+        // can be stoped, other animation should be continued?
+        // But at present using duration 0 in `animateTo`, `animateFrom`
+        // might cause unexpected behavior.
+        el.stopAnimation(), // If `isFrom`, the props is the "from" props.
+        !isFrom && el.attr(props), cb && cb());
       } else {
         el.stopAnimation();
-        !isFrom && el.attr(props);
+        !isFrom && el.attr(props); // Call during once.
+
         during && during(1);
         cb && cb();
       }
     }
+    /**
+     * Update graphic element properties with or without animation according to the
+     * configuration in series.
+     *
+     * Caution: this method will stop previous animation.
+     * So do not use this method to one element twice before
+     * animation starts, unless you know what you are doing.
+     * @example
+     *     graphic.updateProps(el, {
+     *         position: [100, 100]
+     *     }, seriesModel, dataIndex, function () { console.log('Animation done!'); });
+     *     // Or
+     *     graphic.updateProps(el, {
+     *         position: [100, 100]
+     *     }, seriesModel, function () { console.log('Animation done!'); });
+     */
+
 
-    function updateProps(el, props, animatableModel, dataIndex, cb, during) {
+    function updateProps(el, props, // TODO: TYPE AnimatableModel
+    animatableModel, dataIndex, cb, during) {
       animateOrSetProps('update', el, props, animatableModel, dataIndex, cb, during);
     }
+    /**
+     * Init graphic element properties with or without animation according to the
+     * configuration in series.
+     *
+     * Caution: this method will stop previous animation.
+     * So do not use this method to one element twice before
+     * animation starts, unless you know what you are doing.
+     */
+
     function initProps(el, props, animatableModel, dataIndex, cb, during) {
       animateOrSetProps('init', el, props, animatableModel, dataIndex, cb, during);
     }
+    /**
+     * Remove graphic element
+     */
+
     function removeElement(el, props, animatableModel, dataIndex, cb, during) {
+      // Don't do remove animation twice.
       if (isElementRemoved(el)) {
         return;
       }
@@ -13491,18 +14264,26 @@
     function removeElementWithFadeOut(el, animatableModel, dataIndex) {
       function doRemove() {
         el.parent && el.parent.remove(el);
-      }
+      } // Hide label and labelLine first
+      // TODO Also use fade out animation?
+
 
       if (!el.isGroup) {
         fadeOutDisplayable(el, animatableModel, dataIndex, doRemove);
       } else {
         el.traverse(function (disp) {
           if (!disp.isGroup) {
+            // Can invoke doRemove multiple times.
             fadeOutDisplayable(disp, animatableModel, dataIndex, doRemove);
           }
         });
       }
     }
+    /**
+     * If element is removed.
+     * It can determine if element is having remove animation.
+     */
+
     function isElementRemoved(el) {
       if (!el.__zr) {
         return true;
@@ -13518,6 +14299,14 @@
 
       return false;
     }
+    /**
+     * Get transform matrix of target (param target),
+     * in coordinate of its ancestor (param ancestor)
+     *
+     * @param target
+     * @param [ancestor]
+     */
+
     function getTransform(target, ancestor) {
       var mat = identity([]);
 
@@ -13528,6 +14317,16 @@
 
       return mat;
     }
+    /**
+     * Apply transform to an vertex.
+     * @param target [x, y]
+     * @param transform Can be:
+     *      + Transform matrix: like [1, 0, 0, 1, 0, 0]
+     *      + {position, rotation, scale}, the same as `zrender/Transformable`.
+     * @param invert Whether use invert matrix.
+     * @return [x, y]
+     */
+
     function applyTransform$1(target, transform, invert$1) {
       if (transform && !isArrayLike(transform)) {
         transform = Transformable.getLocalTransform(transform);
@@ -13539,7 +14338,15 @@
 
       return applyTransform([], target, transform);
     }
+    /**
+     * @param direction 'left' 'right' 'top' 'bottom'
+     * @param transform Transform matrix: like [1, 0, 0, 1, 0, 0]
+     * @param invert Whether use invert matrix.
+     * @return Transformed direction. 'left' 'right' 'top' 'bottom'
+     */
+
     function transformDirection(direction, transform, invert) {
+      // Pick a base, ensure that transform result will not be (0, 0).
       var hBase = transform[4] === 0 || transform[5] === 0 || transform[0] === 0 ? 1 : Math.abs(2 * transform[4] / transform[0]);
       var vBase = transform[4] === 0 || transform[5] === 0 || transform[2] === 0 ? 1 : Math.abs(2 * transform[4] / transform[2]);
       var vertex = [direction === 'left' ? -hBase : direction === 'right' ? hBase : 0, direction === 'top' ? -vBase : direction === 'bottom' ? vBase : 0];
@@ -13554,6 +14361,11 @@
     function isPath(el) {
       return el.shape != null;
     }
+    /**
+     * Apply group transition animation from g1 to g2.
+     * If no animatableModel, no animation.
+     */
+
 
     function groupTransition(g1, g2, animatableModel) {
       if (!g1 || !g2) {
@@ -13598,6 +14410,8 @@
       });
     }
     function clipPointsByRect(points, rect) {
+      // FIXME: this way migth be incorrect when grpahic clipped by a corner.
+      // and when element have border.
       return map(points, function (point) {
         var x = point[0];
         x = mathMax$4(x, rect.x);
@@ -13608,11 +14422,16 @@
         return [x, y];
       });
     }
+    /**
+     * Return a new clipped rect. If rect size are negative, return undefined.
+     */
+
     function clipRectByRect(targetRect, rect) {
       var x = mathMax$4(targetRect.x, rect.x);
       var x2 = mathMin$4(targetRect.x + targetRect.width, rect.x + rect.width);
       var y = mathMax$4(targetRect.y, rect.y);
-      var y2 = mathMin$4(targetRect.y + targetRect.height, rect.y + rect.height);
+      var y2 = mathMin$4(targetRect.y + targetRect.height, rect.y + rect.height); // If the total rect is cliped, nothing, including the border,
+      // should be painted. So return undefined.
 
       if (x2 >= x && y2 >= y) {
         return {
@@ -13623,7 +14442,8 @@
         };
       }
     }
-    function createIcon(iconStr, opt, rect) {
+    function createIcon(iconStr, // Support 'image://' or 'path://' or direct svg path.
+    opt, rect) {
       var innerOpts = extend({
         rectHover: true
       }, opt);
@@ -13641,6 +14461,13 @@
         return iconStr.indexOf('image://') === 0 ? (style.image = iconStr.slice(8), defaults(style, rect), new ZRImage(innerOpts)) : makePath(iconStr.replace('path://', ''), innerOpts, rect, 'center');
       }
     }
+    /**
+     * Return `true` if the given line (line `a`) and the given polygon
+     * are intersect.
+     * Note that we do not count colinear as intersect here because no
+     * requirement for that. We could do that if required in future.
+     */
+
     function linePolygonIntersect(a1x, a1y, a2x, a2y, points) {
       for (var i = 0, p2 = points[points.length - 1]; i < points.length; i++) {
         var p = points[i];
@@ -13652,16 +14479,30 @@
         p2 = p;
       }
     }
+    /**
+     * Return `true` if the given two lines (line `a` and line `b`)
+     * are intersect.
+     * Note that we do not count colinear as intersect here because no
+     * requirement for that. We could do that if required in future.
+     */
+
     function lineLineIntersect(a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y) {
+      // let `vec_m` to be `vec_a2 - vec_a1` and `vec_n` to be `vec_b2 - vec_b1`.
       var mx = a2x - a1x;
       var my = a2y - a1y;
       var nx = b2x - b1x;
-      var ny = b2y - b1y;
+      var ny = b2y - b1y; // `vec_m` and `vec_n` are parallel iff
+      //     exising `k` such that `vec_m = k · vec_n`, equivalent to `vec_m X vec_n = 0`.
+
       var nmCrossProduct = crossProduct2d(nx, ny, mx, my);
 
       if (nearZero(nmCrossProduct)) {
         return false;
-      }
+      } // `vec_m` and `vec_n` are intersect iff
+      //     existing `p` and `q` in [0, 1] such that `vec_a1 + p * vec_m = vec_b1 + q * vec_n`,
+      //     such that `q = ((vec_a1 - vec_b1) X vec_m) / (vec_n X vec_m)`
+      //           and `p = ((vec_a1 - vec_b1) X vec_n) / (vec_n X vec_m)`.
+
 
       var b1a1x = a1x - b1x;
       var b1a1y = a1y - b1y;
@@ -13679,6 +14520,9 @@
 
       return true;
     }
+    /**
+     * Cross product of 2-dimension vector.
+     */
 
     function crossProduct2d(x1, y1, x2, y2) {
       return x1 * y2 - x2 * y1;
@@ -13686,7 +14530,9 @@
 
     function nearZero(val) {
       return val <= 1e-6 && val >= -1e-6;
-    }
+    } // Register built-in shapes. These shapes might be overwirtten
+    // by users, although we do not recommend that.
+
 
     registerShape('circle', Circle);
     registerShape('ellipse', Ellipse);
@@ -13797,7 +14643,8 @@
       return statesText;
     }
 
-    function setLabelStyle(targetEl, labelStatesModels, opt, stateSpecified) {
+    function setLabelStyle(targetEl, labelStatesModels, opt, stateSpecified // TODO specified position?
+    ) {
       opt = opt || EMPTY_OBJ;
       var isSetOnText = targetEl instanceof ZRText;
       var needsCreateText = false;
@@ -13815,10 +14662,12 @@
 
       if (needsCreateText) {
         if (!isSetOnText) {
+          // Reuse the previous
           if (!textContent) {
             textContent = new ZRText();
             targetEl.setTextContent(textContent);
-          }
+          } // Use same state proxy
+
 
           if (targetEl.stateProxy) {
             textContent.stateProxy = targetEl.stateProxy;
@@ -13832,6 +14681,7 @@
         normalStyle.text = labelStatesTexts.normal;
 
         if (!isSetOnText) {
+          // Always create new
           targetEl.setTextConfig(createTextConfig(normalModel, opt, false));
         }
 
@@ -13855,9 +14705,12 @@
               targetElEmphasisState.textConfig = createTextConfig(stateModel, opt, true);
             }
           }
-        }
+        } // PENDING: if there is many requirements that emphasis position
+        // need to be different from normal position, we might consider
+        // auto slient is those cases.
 
-        textContent.silent = !!normalModel.getShallow('silent');
+
+        textContent.silent = !!normalModel.getShallow('silent'); // Keep x and y
 
         if (textContent.style.x != null) {
           normalStyle.x = textContent.style.x;
@@ -13867,7 +14720,8 @@
           normalStyle.y = textContent.style.y;
         }
 
-        textContent.ignore = !showNormal;
+        textContent.ignore = !showNormal; // Always create new style.
+
         textContent.useStyle(normalStyle);
         textContent.dirty();
 
@@ -13878,6 +14732,7 @@
           };
         }
       } else if (textContent) {
+        // Not display rich text.
         textContent.ignore = true;
       }
 
@@ -13896,10 +14751,17 @@
 
       return statesModels;
     }
-    function createTextStyle(textStyleModel, specifiedTextStyle, opt, isNotNormal, isAttached) {
+    /**
+     * Set basic textStyle properties.
+     */
+
+    function createTextStyle(textStyleModel, specifiedTextStyle, // Fixed style in the code. Can't be set by model.
+    opt, isNotNormal, isAttached // If text is attached on an element. If so, auto color will handling in zrender.
+    ) {
       var textStyle = {};
       setTextStyleCommon(textStyle, textStyleModel, opt, isNotNormal, isAttached);
-      specifiedTextStyle && extend(textStyle, specifiedTextStyle);
+      specifiedTextStyle && extend(textStyle, specifiedTextStyle); // textStyle.host && textStyle.host.dirty && textStyle.host.dirty(false);
+
       return textStyle;
     }
     function createTextConfig(textStyleModel, opt, isNotNormal) {
@@ -13909,7 +14771,9 @@
       var labelRotate = textStyleModel.getShallow('rotate');
       var labelDistance = retrieve2(textStyleModel.getShallow('distance'), isNotNormal ? null : 5);
       var labelOffset = textStyleModel.getShallow('offset');
-      labelPosition = textStyleModel.getShallow('position') || (isNotNormal ? null : 'inside');
+      labelPosition = textStyleModel.getShallow('position') || (isNotNormal ? null : 'inside'); // 'outside' is not a valid zr textPostion value, but used
+      // in bar series, and magric type should be considered.
+
       labelPosition === 'outside' && (labelPosition = opt.defaultOutsidePosition || 'top');
 
       if (labelPosition != null) {
@@ -13927,16 +14791,41 @@
 
       if (labelDistance != null) {
         textConfig.distance = labelDistance;
-      }
+      } // fill and auto is determined by the color of path fill if it's not specified by developers.
+
 
       textConfig.outsideFill = textStyleModel.get('color') === 'inherit' ? opt.inheritColor || null : 'auto';
       return textConfig;
     }
+    /**
+     * The uniform entry of set text style, that is, retrieve style definitions
+     * from `model` and set to `textStyle` object.
+     *
+     * Never in merge mode, but in overwrite mode, that is, all of the text style
+     * properties will be set. (Consider the states of normal and emphasis and
+     * default value can be adopted, merge would make the logic too complicated
+     * to manage.)
+     */
 
     function setTextStyleCommon(textStyle, textStyleModel, opt, isNotNormal, isAttached) {
+      // Consider there will be abnormal when merge hover style to normal style if given default value.
       opt = opt || EMPTY_OBJ;
       var ecModel = textStyleModel.ecModel;
-      var globalTextStyle = ecModel && ecModel.option.textStyle;
+      var globalTextStyle = ecModel && ecModel.option.textStyle; // Consider case:
+      // {
+      //     data: [{
+      //         value: 12,
+      //         label: {
+      //             rich: {
+      //                 // no 'a' here but using parent 'a'.
+      //             }
+      //         }
+      //     }],
+      //     rich: {
+      //         a: { ... }
+      //     }
+      // }
+
       var richItemNames = getRichItemNames(textStyleModel);
       var richResult;
 
@@ -13945,7 +14834,13 @@
 
         for (var name_1 in richItemNames) {
           if (richItemNames.hasOwnProperty(name_1)) {
-            var richTextStyle = textStyleModel.getModel(['rich', name_1]);
+            // Cascade is supported in rich.
+            var richTextStyle = textStyleModel.getModel(['rich', name_1]); // In rich, never `disableBox`.
+            // FIXME: consider `label: {formatter: '{a|xx}', color: 'blue', rich: {a: {}}}`,
+            // the default color `'blue'` will not be adopted if no color declared in `rich`.
+            // That might confuses users. So probably we should put `textStyleModel` as the
+            // root ancestor of the `richTextStyle`. But that would be a break change.
+
             setTokenTextStyle(richResult[name_1] = {}, richTextStyle, globalTextStyle, opt, isNotNormal, isAttached, false, true);
           }
         }
@@ -13968,9 +14863,25 @@
       }
 
       setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isNotNormal, isAttached, true, false);
-    }
+    } // Consider case:
+    // {
+    //     data: [{
+    //         value: 12,
+    //         label: {
+    //             rich: {
+    //                 // no 'a' here but using parent 'a'.
+    //             }
+    //         }
+    //     }],
+    //     rich: {
+    //         a: { ... }
+    //     }
+    // }
+    // TODO TextStyleModel
+
 
     function getRichItemNames(textStyleModel) {
+      // Use object to remove duplicated names.
       var richItemNameMap;
 
       while (textStyleModel && textStyleModel !== textStyleModel.ecModel) {
@@ -13997,6 +14908,7 @@
     var TEXT_PROPS_BOX = ['padding', 'borderWidth', 'borderRadius', 'borderDashOffset', 'backgroundColor', 'borderColor', 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY'];
 
     function setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isNotNormal, isAttached, isBlock, inRich) {
+      // In merge mode, default value should not be given.
       globalTextStyle = !isNotNormal && globalTextStyle || EMPTY_OBJ;
       var inheritColor = opt && opt.inheritColor;
       var fillColor = textStyleModel.getShallow('color');
@@ -14032,6 +14944,8 @@
       }
 
       if (!isAttached) {
+        // Only use default global textStyle.color if text is individual.
+        // Otherwise it will use the strategy of attached text color because text may be on a path.
         fillColor = fillColor || globalTextStyle.color;
         strokeColor = strokeColor || globalTextStyle.textBorderColor;
       }
@@ -14068,13 +14982,18 @@
 
       if (opacity != null) {
         textStyle.opacity = opacity;
-      }
+      } // TODO
+
 
       if (!isNotNormal && !isAttached) {
+        // Set default finally.
         if (textStyle.fill == null && opt.inheritColor) {
           textStyle.fill = opt.inheritColor;
         }
-      }
+      } // Do not use `getFont` here, because merge should be supported, where
+      // part of these properties may be changed in emphasis style, and the
+      // others should remain their original value got from normal style.
+
 
       for (var i = 0; i < TEXT_PROPS_WITH_GLOBAL.length; i++) {
         var key = TEXT_PROPS_WITH_GLOBAL[i];
@@ -14142,7 +15061,8 @@
 
     function getFont(opt, ecModel) {
       var gTextStyleModel = ecModel && ecModel.getModel('textStyle');
-      return trim([opt.fontStyle || gTextStyleModel && gTextStyleModel.getShallow('fontStyle') || '', opt.fontWeight || gTextStyleModel && gTextStyleModel.getShallow('fontWeight') || '', (opt.fontSize || gTextStyleModel && gTextStyleModel.getShallow('fontSize') || 12) + 'px', opt.fontFamily || gTextStyleModel && gTextStyleModel.getShallow('fontFamily') || 'sans-serif'].join(' '));
+      return trim([// FIXME in node-canvas fontWeight is before fontStyle
+      opt.fontStyle || gTextStyleModel && gTextStyleModel.getShallow('fontStyle') || '', opt.fontWeight || gTextStyleModel && gTextStyleModel.getShallow('fontWeight') || '', (opt.fontSize || gTextStyleModel && gTextStyleModel.getShallow('fontSize') || 12) + 'px', opt.fontFamily || gTextStyleModel && gTextStyleModel.getShallow('fontFamily') || 'sans-serif'].join(' '));
     }
     var labelInner = makeInner();
     function setLabelValueAnimation(label, labelStatesModels, value, getDefaultText) {
@@ -14169,7 +15089,9 @@
         return;
       }
 
-      var defaultInterpolatedText = labelInnerStore.defaultInterpolatedText;
+      var defaultInterpolatedText = labelInnerStore.defaultInterpolatedText; // Consider the case that being animating, do not use the `obj.value`,
+      // Otherwise it will jump to the `obj.value` when this new animation started.
+
       var currValue = retrieve2(labelInnerStore.interpolatedValue, labelInnerStore.prevValue);
       var targetValue = labelInnerStore.value;
 
@@ -14187,16 +15109,29 @@
       (currValue == null ? initProps : updateProps)(textEl, {}, animatableModel, dataIndex, null, during);
     }
 
-    var PATH_COLOR = ['textStyle', 'color'];
+    var PATH_COLOR = ['textStyle', 'color']; // TODO Performance improvement?
+
     var tmpRichText = new ZRText();
 
-    var TextStyleMixin = function () {
+    var TextStyleMixin =
+    /** @class */
+    function () {
       function TextStyleMixin() {}
+      /**
+       * Get color property or get color from option.textStyle.color
+       */
+      // TODO Callback
+
 
       TextStyleMixin.prototype.getTextColor = function (isEmphasis) {
         var ecModel = this.ecModel;
         return this.getShallow('color') || (!isEmphasis && ecModel ? ecModel.get(PATH_COLOR) : null);
       };
+      /**
+       * Create font string from fontStyle, fontWeight, fontSize, fontFamily
+       * @return {string}
+       */
+
 
       TextStyleMixin.prototype.getFont = function () {
         return getFont({
@@ -14226,10 +15161,14 @@
       return TextStyleMixin;
     }();
 
-    var LINE_STYLE_KEY_MAP = [['lineWidth', 'width'], ['stroke', 'color'], ['opacity'], ['shadowBlur'], ['shadowOffsetX'], ['shadowOffsetY'], ['shadowColor'], ['lineDash', 'type'], ['lineDashOffset', 'dashOffset'], ['lineCap', 'cap'], ['lineJoin', 'join'], ['miterLimit']];
+    var LINE_STYLE_KEY_MAP = [['lineWidth', 'width'], ['stroke', 'color'], ['opacity'], ['shadowBlur'], ['shadowOffsetX'], ['shadowOffsetY'], ['shadowColor'], ['lineDash', 'type'], ['lineDashOffset', 'dashOffset'], ['lineCap', 'cap'], ['lineJoin', 'join'], ['miterLimit'] // Option decal is in `DecalObject` but style.decal is in `PatternObject`.
+    // So do not transfer decal directly.
+    ];
     var getLineStyle = makeStyleMapper(LINE_STYLE_KEY_MAP);
 
-    var LineStyleMixin = function () {
+    var LineStyleMixin =
+    /** @class */
+    function () {
       function LineStyleMixin() {}
 
       LineStyleMixin.prototype.getLineStyle = function (excludes) {
@@ -14239,10 +15178,14 @@
       return LineStyleMixin;
     }();
 
-    var ITEM_STYLE_KEY_MAP = [['fill', 'color'], ['stroke', 'borderColor'], ['lineWidth', 'borderWidth'], ['opacity'], ['shadowBlur'], ['shadowOffsetX'], ['shadowOffsetY'], ['shadowColor'], ['lineDash', 'borderType'], ['lineDashOffset', 'borderDashOffset'], ['lineCap', 'borderCap'], ['lineJoin', 'borderJoin'], ['miterLimit', 'borderMiterLimit']];
+    var ITEM_STYLE_KEY_MAP = [['fill', 'color'], ['stroke', 'borderColor'], ['lineWidth', 'borderWidth'], ['opacity'], ['shadowBlur'], ['shadowOffsetX'], ['shadowOffsetY'], ['shadowColor'], ['lineDash', 'borderType'], ['lineDashOffset', 'borderDashOffset'], ['lineCap', 'borderCap'], ['lineJoin', 'borderJoin'], ['miterLimit', 'borderMiterLimit'] // Option decal is in `DecalObject` but style.decal is in `PatternObject`.
+    // So do not transfer decal directly.
+    ];
     var getItemStyle = makeStyleMapper(ITEM_STYLE_KEY_MAP);
 
-    var ItemStyleMixin = function () {
+    var ItemStyleMixin =
+    /** @class */
+    function () {
       function ItemStyleMixin() {}
 
       ItemStyleMixin.prototype.getItemStyle = function (excludes, includes) {
@@ -14252,11 +15195,21 @@
       return ItemStyleMixin;
     }();
 
-    var Model = function () {
+    var Model =
+    /** @class */
+    function () {
       function Model(option, parentModel, ecModel) {
         this.parentModel = parentModel;
         this.ecModel = ecModel;
-        this.option = option;
+        this.option = option; // Simple optimization
+        // if (this.init) {
+        //     if (arguments.length <= 4) {
+        //         this.init(option, parentModel, ecModel, extraOpt);
+        //     }
+        //     else {
+        //         this.init.apply(this, arguments);
+        //     }
+        // }
       }
 
       Model.prototype.init = function (option, parentModel, ecModel) {
@@ -14266,10 +15219,17 @@
           rest[_i - 3] = arguments[_i];
         }
       };
+      /**
+       * Merge the input option to me.
+       */
+
 
       Model.prototype.mergeOption = function (option, ecModel) {
         merge(this.option, option, true);
-      };
+      }; // `path` can be 'xxx.yyy.zzz', so the return value type have to be `ModelOption`
+      // TODO: TYPE strict key check?
+      // get(path: string | string[], ignoreParent?: boolean): ModelOption;
+
 
       Model.prototype.get = function (path, ignoreParent) {
         if (path == null) {
@@ -14287,12 +15247,16 @@
           var parentModel = this.parentModel;
 
           if (parentModel) {
+            // FIXME:TS do not know how to make it works
             val = parentModel.getShallow(key);
           }
         }
 
         return val;
-      };
+      }; // `path` can be 'xxx.yyy.zzz', so the return value type have to be `Model<ModelOption>`
+      // getModel(path: string | string[], parentModel?: Model): Model;
+      // TODO 'xxx.yyy.zzz' is deprecated
+
 
       Model.prototype.getModel = function (path, parentModel) {
         var hasPath = path != null;
@@ -14301,17 +15265,65 @@
         parentModel = parentModel || this.parentModel && this.parentModel.getModel(this.resolveParentPath(pathFinal));
         return new Model(obj, parentModel, this.ecModel);
       };
+      /**
+       * Squash option stack into one.
+       * parentModel will be removed after squashed.
+       *
+       * NOTE: resolveParentPath will not be applied here for simplicity. DON'T use this function
+       * if resolveParentPath is modified.
+       *
+       * @param deepMerge If do deep merge. Default to be false.
+       */
+      // squash(
+      //     deepMerge?: boolean,
+      //     handleCallback?: (func: () => object) => object
+      // ) {
+      //     const optionStack = [];
+      //     let model: Model = this;
+      //     while (model) {
+      //         if (model.option) {
+      //             optionStack.push(model.option);
+      //         }
+      //         model = model.parentModel;
+      //     }
+      //     const newOption = {} as Opt;
+      //     let option;
+      //     while (option = optionStack.pop()) {    // Top down merge
+      //         if (isFunction(option) && handleCallback) {
+      //             option = handleCallback(option);
+      //         }
+      //         if (deepMerge) {
+      //             merge(newOption, option);
+      //         }
+      //         else {
+      //             extend(newOption, option);
+      //         }
+      //     }
+      //     // Remove parentModel
+      //     this.option = newOption;
+      //     this.parentModel = null;
+      // }
+
+      /**
+       * If model has option
+       */
+
 
       Model.prototype.isEmpty = function () {
         return this.option == null;
       };
 
-      Model.prototype.restoreData = function () {};
+      Model.prototype.restoreData = function () {}; // Pending
+
 
       Model.prototype.clone = function () {
         var Ctor = this.constructor;
         return new Ctor(clone(this.option));
-      };
+      }; // setReadOnly(properties): void {
+      // clazzUtil.setReadOnly(this, properties);
+      // }
+      // If path is null/undefined, return null/undefined.
+
 
       Model.prototype.parsePath = function (path) {
         if (typeof path === 'string') {
@@ -14319,11 +15331,15 @@
         }
 
         return path;
-      };
+      }; // Resolve path for parent. Perhaps useful when parent use a different property.
+      // Default to be a identity resolver.
+      // Can be modified to a different resolver.
+
 
       Model.prototype.resolveParentPath = function (path) {
         return path;
-      };
+      }; // FIXME:TS check whether put this method here
+
 
       Model.prototype.isAnimationEnabled = function () {
         if (!env.node && this.option) {
@@ -14343,9 +15359,11 @@
         }
 
         for (var i = 0; i < pathArr.length; i++) {
+          // Ignore empty
           if (!pathArr[i]) {
             continue;
-          }
+          } // obj could be number/string/... (like 0)
+
 
           obj = obj && typeof obj === 'object' ? obj[pathArr[i]] : null;
 
@@ -14363,6 +15381,7 @@
 
       return Model;
     }();
+
     enableClassExtend(Model);
     enableClassCheck(Model);
     mixin(Model, LineStyleMixin);
@@ -14371,9 +15390,21 @@
     mixin(Model, TextStyleMixin);
 
     var base = Math.round(Math.random() * 10);
+    /**
+     * @public
+     * @param {string} type
+     * @return {string}
+     */
+
     function getUID(type) {
+      // Considering the case of crossing js context,
+      // use Math.random to make id as unique as possible.
       return [type || '', base++].join('_');
     }
+    /**
+     * Implements `SubTypeDefaulterManager` for `target`.
+     */
+
     function enableSubTypeDefaulter(target) {
       var subTypeDefaulters = {};
 
@@ -14396,7 +15427,23 @@
         return type;
       };
     }
+    /**
+     * Implements `TopologicalTravelable<any>` for `entity`.
+     *
+     * Topological travel on Activity Network (Activity On Vertices).
+     * Dependencies is defined in Model.prototype.dependencies, like ['xAxis', 'yAxis'].
+     * If 'xAxis' or 'yAxis' is absent in componentTypeList, just ignore it in topology.
+     * If there is circle dependencey, Error will be thrown.
+     */
+
     function enableTopologicalTravel(entity, dependencyGetter) {
+      /**
+       * @param targetNameList Target Component type list.
+       *                       Can be ['aa', 'bb', 'aa.xx']
+       * @param fullNameList By which we can build dependency graph.
+       * @param callback Params: componentType, dependencies.
+       * @param context Scope of callback.
+       */
       entity.topologicalTravel = function (targetNameList, fullNameList, callback, context) {
         if (!targetNameList.length) {
           return;
@@ -14439,7 +15486,13 @@
           if (graph[succComponentType].entryCount === 0) {
             noEntryList.push(succComponentType);
           }
-        }
+        } // Consider this case: legend depends on series, and we call
+        // chart.setOption({series: [...]}), where only series is in option.
+        // If we do not have 'removeEdgeAndAdd', legendModel.mergeOption will
+        // not be called, but only sereis.mergeOption is called. Thus legend
+        // have no chance to update its local record about series (like which
+        // name of series is available in legend).
+
 
         function removeEdgeAndAdd(succComponentType) {
           targetNameSet[succComponentType] = true;
@@ -14498,6 +15551,7 @@
       }
     }
     function inheritDefaultOption(superOption, subOption) {
+      // See also `model/Component.ts#getDefaultOption`
       return merge(merge({}, superOption, true), subOption, true);
     }
 
@@ -14525,6 +15579,28 @@
      * AUTO-GENERATED FILE. DO NOT MODIFY.
      */
 
+    /*
+    * Licensed to the Apache Software Foundation (ASF) under one
+    * or more contributor license agreements.  See the NOTICE file
+    * distributed with this work for additional information
+    * regarding copyright ownership.  The ASF licenses this file
+    * to you under the Apache License, Version 2.0 (the
+    * "License"); you may not use this file except in compliance
+    * with the License.  You may obtain a copy of the License at
+    *
+    *   http://www.apache.org/licenses/LICENSE-2.0
+    *
+    * Unless required by applicable law or agreed to in writing,
+    * software distributed under the License is distributed on an
+    * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    * KIND, either express or implied.  See the License for the
+    * specific language governing permissions and limitations
+    * under the License.
+    */
+
+    /**
+     * Language: English.
+     */
     var langEN = {
       time: {
         month: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
@@ -14659,6 +15735,24 @@
      * AUTO-GENERATED FILE. DO NOT MODIFY.
      */
 
+    /*
+     * Licensed to the Apache Software Foundation (ASF) under one
+     * or more contributor license agreements.  See the NOTICE file
+     * distributed with this work for additional information
+     * regarding copyright ownership.  The ASF licenses this file
+     * to you under the Apache License, Version 2.0 (the
+     * "License"); you may not use this file except in compliance
+     * with the License.  You may obtain a copy of the License at
+     *
+     *   http://www.apache.org/licenses/LICENSE-2.0
+     *
+     * Unless required by applicable law or agreed to in writing,
+     * software distributed under the License is distributed on an
+     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+     * KIND, either express or implied.  See the License for the
+     * specific language governing permissions and limitations
+     * under the License.
+     */
     var langZH = {
       time: {
         month: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
@@ -14775,14 +15869,19 @@
     var localeStorage = {};
     var localeModels = {};
     var SYSTEM_LANG = !env.domSupported ? DEFAULT_LOCALE : function () {
-      var langStr = (document.documentElement.lang || navigator.language || navigator.browserLanguage).toUpperCase();
+      var langStr = (
+      /* eslint-disable-next-line */
+      document.documentElement.lang || navigator.language || navigator.browserLanguage).toUpperCase();
       return langStr.indexOf(LOCALE_ZH) > -1 ? LOCALE_ZH : DEFAULT_LOCALE;
     }();
     function registerLocale(locale, localeObj) {
       locale = locale.toUpperCase();
       localeModels[locale] = new Model(localeObj);
       localeStorage[locale] = localeObj;
-    }
+    } // export function getLocale(locale: string) {
+    //     return localeStorage[locale];
+    // }
+
     function createLocaleObject(locale) {
       if (isString(locale)) {
         var localeObj = localeStorage[locale.toUpperCase()] || {};
@@ -14801,7 +15900,8 @@
     }
     function getDefaultLocaleModel() {
       return localeModels[DEFAULT_LOCALE];
-    }
+    } // Default locale
+
     registerLocale(LOCALE_EN, langEN);
     registerLocale(LOCALE_ZH, langZH);
 
@@ -14851,6 +15951,7 @@
           return 'hour';
 
         default:
+          // year, minutes, second, milliseconds
           return timeUnit;
       }
     }
@@ -14867,10 +15968,13 @@
           return 'millisecond';
 
         default:
+          // Also for day, hour, minute, second
           return 'second';
       }
     }
-    function format(time, template, isUTC, lang) {
+    function format( // Note: The result based on `isUTC` are totally different, which can not be just simply
+    // substituted by the result without `isUTC`. So we make the param `isUTC` mandatory.
+    time, template, isUTC, lang) {
       var date = parseDate(time);
       var y = date[fullYearGetterName(isUTC)]();
       var M = date[monthGetterName(isUTC)]() + 1;
@@ -14894,8 +15998,10 @@
       var template = null;
 
       if (typeof formatter === 'string') {
+        // Single formatter for all units at all levels
         template = formatter;
       } else if (typeof formatter === 'function') {
+        // Callback formatter
         template = formatter(tick.value, idx, {
           level: tick.level
         });
@@ -14908,12 +16014,14 @@
           }
         }
 
-        var mergedFormatter = formatter ? formatter.inherit === false ? formatter : defaults(formatter, defaults$1) : defaults$1;
+        var mergedFormatter = formatter ? formatter.inherit === false ? formatter // Use formatter with bigger units
+        : defaults(formatter, defaults$1) : defaults$1;
         var unit = getUnitFromValue(tick.value, isUTC);
 
         if (mergedFormatter[unit]) {
           template = mergedFormatter[unit];
         } else if (mergedFormatter.inherit) {
+          // Unit formatter is not defined and should inherit from bigger units
           var targetId = timeUnits.indexOf(unit);
 
           for (var i = targetId - 1; i >= 0; --i) {
@@ -15062,6 +16170,10 @@
       return textEl.getBoundingRect();
     }
 
+    /**
+     * Add a comma each three digit.
+     */
+
     function addCommas(x) {
       if (!isNumeric(x)) {
         return isString(x) ? x : '-';
@@ -15095,6 +16207,14 @@
         return replaceMap[c];
       });
     }
+    /**
+     * Make value user readable for tooltip and label.
+     * "User readable":
+     *     Try to not print programmer-specific text like NaN, Infinity, null, undefined.
+     *     Avoid to display an empty string, which users can not recognize there is
+     *     a value and it might look like a bug.
+     */
+
     function makeValueReadable(value, valueType, useUTC) {
       var USER_READABLE_DEFUALT_TIME_PATTERN = 'yyyy-MM-dd hh:mm:ss';
 
@@ -15116,12 +16236,14 @@
           return format(date, USER_READABLE_DEFUALT_TIME_PATTERN, useUTC);
         } else if (isValueDate) {
           return '-';
-        }
+        } // In other cases, continue to try to display the value in the following code.
+
       }
 
       if (valueType === 'ordinal') {
         return isStringSafe(value) ? stringToUserReadable(value) : isNumber(value) ? isNumberUserReadable(value) ? value + '' : '-' : '-';
-      }
+      } // By default.
+
 
       var numericResult = numericToNumber(value);
       return isNumberUserReadable(numericResult) ? addCommas(numericResult) : isStringSafe(value) ? stringToUserReadable(value) : '-';
@@ -15131,6 +16253,11 @@
     var wrapVar = function (varName, seriesIdx) {
       return '{' + varName + (seriesIdx == null ? '' : seriesIdx) + '}';
     };
+    /**
+     * Template formatter
+     * @param {Array.<Object>|Object} paramsList
+     */
+
 
     function formatTpl(tpl, paramsList, encode) {
       if (!isArray(paramsList)) {
@@ -15174,8 +16301,13 @@
       }
 
       if (renderMode === 'html') {
-        return type === 'subItem' ? '<span style="display:inline-block;vertical-align:middle;margin-right:8px;margin-left:3px;' + 'border-radius:4px;width:4px;height:4px;background-color:' + encodeHTML(color) + ';' + (extraCssText || '') + '"></span>' : '<span style="display:inline-block;margin-right:4px;' + 'border-radius:10px;width:10px;height:10px;background-color:' + encodeHTML(color) + ';' + (extraCssText || '') + '"></span>';
+        return type === 'subItem' ? '<span style="display:inline-block;vertical-align:middle;margin-right:8px;margin-left:3px;' + 'border-radius:4px;width:4px;height:4px;background-color:' // Only support string
+        + encodeHTML(color) + ';' + (extraCssText || '') + '"></span>' : '<span style="display:inline-block;margin-right:4px;' + 'border-radius:10px;width:10px;height:10px;background-color:' + encodeHTML(color) + ';' + (extraCssText || '') + '"></span>';
       } else {
+        // Should better not to auto generate style name by auto-increment number here.
+        // Because this util is usually called in tooltip formatter, which is probably
+        // called repeatly when mouse move and the auto-increment number increases fast.
+        // Users can make their own style name by theirselves, make it unique and readable.
         var markerId = opt.markerId || 'markerX';
         return {
           renderMode: renderMode,
@@ -15194,6 +16326,17 @@
         };
       }
     }
+    /**
+     * @deprecated Use `time/format` instead.
+     * ISO Date format
+     * @param {string} tpl
+     * @param {number} value
+     * @param {boolean} [isUTC=false] Default in local time.
+     *           see `module:echarts/scale/Time`
+     *           and `module:echarts/util/number#parseDate`.
+     * @inner
+     */
+
     function formatTime(tpl, value, isUTC) {
       if ("development" !== 'production') {
         deprecateReplaceLog('echarts.format.formatTime', 'echarts.time.format');
@@ -15215,14 +16358,31 @@
       tpl = tpl.replace('MM', pad(M, 2)).replace('M', M).replace('yyyy', y).replace('yy', y % 100 + '').replace('dd', pad(d, 2)).replace('d', d).replace('hh', pad(h, 2)).replace('h', h).replace('mm', pad(m, 2)).replace('m', m).replace('ss', pad(s, 2)).replace('s', s).replace('SSS', pad(S, 3));
       return tpl;
     }
+    /**
+     * Capital first
+     * @param {string} str
+     * @return {string}
+     */
+
     function capitalFirst(str) {
       return str ? str.charAt(0).toUpperCase() + str.substr(1) : str;
     }
+    /**
+     * @return Never be null/undefined.
+     */
+
     function convertToColorString(color, defaultColor) {
       defaultColor = defaultColor || 'transparent';
       return isString(color) ? color : isObject(color) ? color.colorStops && (color.colorStops[0] || {}).color || defaultColor : defaultColor;
     }
+    /**
+     * open new tab
+     * @param link url
+     * @param target blank or self
+     */
+
     function windowOpen(link, target) {
+      /* global window */
       if (target === '_blank' || target === 'blank') {
         var blank = window.open();
         blank.opener = null;
@@ -15233,7 +16393,15 @@
     }
 
     var each$1 = each;
+    /**
+     * @public
+     */
+
     var LOCATION_PARAMS = ['left', 'right', 'top', 'bottom', 'width', 'height'];
+    /**
+     * @public
+     */
+
     var HV_NAMES = [['width', 'left', 'right'], ['height', 'top', 'bottom']];
 
     function boxLayout(orient, group, gap, maxWidth, maxHeight) {
@@ -15258,7 +16426,8 @@
 
         if (orient === 'horizontal') {
           var moveX = rect.width + (nextChildRect ? -nextChildRect.x + rect.x : 0);
-          nextX = x + moveX;
+          nextX = x + moveX; // Wrap when width exceeds maxWidth or meet a `newline` group
+          // FIXME compare before adding gap?
 
           if (nextX > maxWidth || child.newline) {
             x = 0;
@@ -15266,11 +16435,12 @@
             y += currentLineMaxSize + gap;
             currentLineMaxSize = rect.height;
           } else {
+            // FIXME: consider rect.y is not `0`?
             currentLineMaxSize = Math.max(currentLineMaxSize, rect.height);
           }
         } else {
           var moveY = rect.height + (nextChildRect ? -nextChildRect.y + rect.y : 0);
-          nextY = y + moveY;
+          nextY = y + moveY; // Wrap when width exceeds maxHeight or meet a `newline` group
 
           if (nextY > maxHeight || child.newline) {
             x += currentLineMaxSize + gap;
@@ -15292,10 +16462,39 @@
         orient === 'horizontal' ? x = nextX + gap : y = nextY + gap;
       });
     }
+    /**
+     * VBox or HBox layouting
+     * @param {string} orient
+     * @param {module:zrender/graphic/Group} group
+     * @param {number} gap
+     * @param {number} [width=Infinity]
+     * @param {number} [height=Infinity]
+     */
+
 
     var box = boxLayout;
+    /**
+     * VBox layouting
+     * @param {module:zrender/graphic/Group} group
+     * @param {number} gap
+     * @param {number} [width=Infinity]
+     * @param {number} [height=Infinity]
+     */
+
     var vbox = curry(boxLayout, 'vertical');
+    /**
+     * HBox layouting
+     * @param {module:zrender/graphic/Group} group
+     * @param {number} gap
+     * @param {number} [width=Infinity]
+     * @param {number} [height=Infinity]
+     */
+
     var hbox = curry(boxLayout, 'horizontal');
+    /**
+     * Parse position info.
+     */
+
     function getLayoutRect(positionInfo, containerRect, margin) {
       margin = normalizeCssArray$1(margin || 0);
       var containerWidth = containerRect.width;
@@ -15308,7 +16507,7 @@
       var height = parsePercent$1(positionInfo.height, containerHeight);
       var verticalMargin = margin[2] + margin[0];
       var horizontalMargin = margin[1] + margin[3];
-      var aspect = positionInfo.aspect;
+      var aspect = positionInfo.aspect; // If width is not specified, calculate width from left and right
 
       if (isNaN(width)) {
         width = containerWidth - right - horizontalMargin - left;
@@ -15319,13 +16518,21 @@
       }
 
       if (aspect != null) {
+        // If width and height are not given
+        // 1. Graph should not exceeds the container
+        // 2. Aspect must be keeped
+        // 3. Graph should take the space as more as possible
+        // FIXME
+        // Margin is not considered, because there is no case that both
+        // using margin and aspect so far.
         if (isNaN(width) && isNaN(height)) {
           if (aspect > containerWidth / containerHeight) {
             width = containerWidth * 0.8;
           } else {
             height = containerHeight * 0.8;
           }
-        }
+        } // Calculate width or height with given aspect
+
 
         if (isNaN(width)) {
           width = aspect * height;
@@ -15334,7 +16541,8 @@
         if (isNaN(height)) {
           height = width / aspect;
         }
-      }
+      } // If left is not specified, calculate left from right and width
+
 
       if (isNaN(left)) {
         left = containerWidth - right - width - horizontalMargin;
@@ -15342,7 +16550,8 @@
 
       if (isNaN(top)) {
         top = containerHeight - bottom - height - verticalMargin;
-      }
+      } // Align left and top
+
 
       switch (positionInfo.left || positionInfo.right) {
         case 'center':
@@ -15363,16 +16572,19 @@
         case 'bottom':
           top = containerHeight - height - verticalMargin;
           break;
-      }
+      } // If something is wrong and left, top, width, height are calculated as NaN
+
 
       left = left || 0;
       top = top || 0;
 
       if (isNaN(width)) {
+        // Width may be NaN if only one value is given except width
         width = containerWidth - horizontalMargin - left - (right || 0);
       }
 
       if (isNaN(height)) {
+        // Height may be NaN if only one value is given except height
         height = containerHeight - verticalMargin - top - (bottom || 0);
       }
 
@@ -15380,6 +16592,45 @@
       rect.margin = margin;
       return rect;
     }
+    /**
+     * Position a zr element in viewport
+     *  Group position is specified by either
+     *  {left, top}, {right, bottom}
+     *  If all properties exists, right and bottom will be igonred.
+     *
+     * Logic:
+     *     1. Scale (against origin point in parent coord)
+     *     2. Rotate (against origin point in parent coord)
+     *     3. Traslate (with el.position by this method)
+     * So this method only fixes the last step 'Traslate', which does not affect
+     * scaling and rotating.
+     *
+     * If be called repeatly with the same input el, the same result will be gotten.
+     *
+     * @param el Should have `getBoundingRect` method.
+     * @param positionInfo
+     * @param positionInfo.left
+     * @param positionInfo.top
+     * @param positionInfo.right
+     * @param positionInfo.bottom
+     * @param positionInfo.width Only for opt.boundingModel: 'raw'
+     * @param positionInfo.height Only for opt.boundingModel: 'raw'
+     * @param containerRect
+     * @param margin
+     * @param opt
+     * @param opt.hv Only horizontal or only vertical. Default to be [1, 1]
+     * @param opt.boundingMode
+     *        Specify how to calculate boundingRect when locating.
+     *        'all': Position the boundingRect that is transformed and uioned
+     *               both itself and its descendants.
+     *               This mode simplies confine the elements in the bounding
+     *               of their container (e.g., using 'right: 0').
+     *        'raw': Position the boundingRect that is not transformed and only itself.
+     *               This mode is useful when you want a element can overflow its
+     *               container. (Consider a rotated circle needs to be located in a corner.)
+     *               In this mode positionInfo.width/height can only be number.
+     */
+
     function positionElement(el, positionInfo, containerRect, margin, opt) {
       var h = !opt || !opt.hv || opt.hv[0];
       var v = !opt || !opt.hv || opt.hv[1];
@@ -15397,16 +16648,22 @@
         rect = el.getBoundingRect();
 
         if (el.needLocalTransform()) {
-          var transform = el.getLocalTransform();
+          var transform = el.getLocalTransform(); // Notice: raw rect may be inner object of el,
+          // which should not be modified.
+
           rect = rect.clone();
           rect.applyTransform(transform);
         }
-      }
+      } // The real width and height can not be specified but calculated by the given el.
+
 
       var layoutRect = getLayoutRect(defaults({
         width: rect.width,
         height: rect.height
-      }, positionInfo), containerRect, margin);
+      }, positionInfo), containerRect, margin); // Because 'tranlate' is the last step in transform
+      // (see zrender/core/Transformable#getLocalTransform),
+      // we can just only modify el.position to get final result.
+
       var dx = h ? layoutRect.x - rect.x : 0;
       var dy = v ? layoutRect.y - rect.y : 0;
 
@@ -15426,6 +16683,30 @@
         type: layoutMode
       } : null;
     }
+    /**
+     * Consider Case:
+     * When default option has {left: 0, width: 100}, and we set {right: 0}
+     * through setOption or media query, using normal zrUtil.merge will cause
+     * {right: 0} does not take effect.
+     *
+     * @example
+     * ComponentModel.extend({
+     *     init: function () {
+     *         ...
+     *         let inputPositionParams = layout.getLayoutParams(option);
+     *         this.mergeOption(inputPositionParams);
+     *     },
+     *     mergeOption: function (newOption) {
+     *         newOption && zrUtil.merge(thisOption, newOption, true);
+     *         layout.mergeLayoutParam(thisOption, newOption);
+     *     }
+     * });
+     *
+     * @param targetOption
+     * @param newOption
+     * @param opt
+     */
+
     function mergeLayoutParam(targetOption, newOption, opt) {
       var ignoreSize = opt && opt.ignoreSize;
       !isArray(ignoreSize) && (ignoreSize = [ignoreSize, ignoreSize]);
@@ -15444,12 +16725,15 @@
           merged[name] = targetOption[name];
         });
         each$1(names, function (name) {
+          // Consider case: newOption.width is null, which is
+          // set by user for removing width setting.
           hasProp(newOption, name) && (newParams[name] = merged[name] = newOption[name]);
           hasValue(newParams, name) && newValueCount++;
           hasValue(merged, name) && mergedValueCount++;
         });
 
         if (ignoreSize[hvIdx]) {
+          // Only one of left/right is premitted to exist.
           if (hasValue(newOption, names[1])) {
             merged[names[2]] = null;
           } else if (hasValue(newOption, names[2])) {
@@ -15457,24 +16741,32 @@
           }
 
           return merged;
-        }
+        } // Case: newOption: {width: ..., right: ...},
+        // or targetOption: {right: ...} and newOption: {width: ...},
+        // There is no conflict when merged only has params count
+        // little than enoughParamNumber.
+
 
         if (mergedValueCount === enoughParamNumber || !newValueCount) {
           return merged;
-        } else if (newValueCount >= enoughParamNumber) {
-          return newParams;
-        } else {
-          for (var i = 0; i < names.length; i++) {
-            var name_1 = names[i];
+        } // Case: newOption: {width: ..., right: ...},
+        // Than we can make sure user only want those two, and ignore
+        // all origin params in targetOption.
+        else if (newValueCount >= enoughParamNumber) {
+            return newParams;
+          } else {
+            // Chose another param from targetOption by priority.
+            for (var i = 0; i < names.length; i++) {
+              var name_1 = names[i];
 
-            if (!hasProp(newParams, name_1) && hasProp(targetOption, name_1)) {
-              newParams[name_1] = targetOption[name_1];
-              break;
+              if (!hasProp(newParams, name_1) && hasProp(targetOption, name_1)) {
+                newParams[name_1] = targetOption[name_1];
+                break;
+              }
             }
-          }
 
-          return newParams;
-        }
+            return newParams;
+          }
       }
 
       function hasProp(obj, name) {
@@ -15491,9 +16783,19 @@
         });
       }
     }
+    /**
+     * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object.
+     */
+
     function getLayoutParams(source) {
       return copyLayoutParams({}, source);
     }
+    /**
+     * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object.
+     * @param {Object} source
+     * @return {Object} Result contains those props.
+     */
+
     function copyLayoutParams(target, source) {
       source && target && each$1(LOCATION_PARAMS, function (name) {
         source.hasOwnProperty(name) && (target[name] = source[name]);
@@ -15503,7 +16805,9 @@
 
     var inner = makeInner();
 
-    var ComponentModel = function (_super) {
+    var ComponentModel =
+    /** @class */
+    function (_super) {
       __extends(ComponentModel, _super);
 
       function ComponentModel(option, parentModel, ecModel) {
@@ -15537,15 +16841,78 @@
           mergeLayoutParam(this.option, option, layoutMode);
         }
       };
+      /**
+       * Called immediately after `init` or `mergeOption` of this instance called.
+       */
+
 
       ComponentModel.prototype.optionUpdated = function (newCptOption, isInit) {};
+      /**
+       * [How to declare defaultOption]:
+       *
+       * (A) If using class declaration in typescript (since echarts 5):
+       * ```ts
+       * import {ComponentOption} from '../model/option';
+       * export interface XxxOption extends ComponentOption {
+       *     aaa: number
+       * }
+       * export class XxxModel extends Component {
+       *     static type = 'xxx';
+       *     static defaultOption: XxxOption = {
+       *         aaa: 123
+       *     }
+       * }
+       * Component.registerClass(XxxModel);
+       * ```
+       * ```ts
+       * import {inheritDefaultOption} from '../util/component';
+       * import {XxxModel, XxxOption} from './XxxModel';
+       * export interface XxxSubOption extends XxxOption {
+       *     bbb: number
+       * }
+       * class XxxSubModel extends XxxModel {
+       *     static defaultOption: XxxSubOption = inheritDefaultOption(XxxModel.defaultOption, {
+       *         bbb: 456
+       *     })
+       *     fn() {
+       *         let opt = this.getDefaultOption();
+       *         // opt is {aaa: 123, bbb: 456}
+       *     }
+       * }
+       * ```
+       *
+       * (B) If using class extend (previous approach in echarts 3 & 4):
+       * ```js
+       * let XxxComponent = Component.extend({
+       *     defaultOption: {
+       *         xx: 123
+       *     }
+       * })
+       * ```
+       * ```js
+       * let XxxSubComponent = XxxComponent.extend({
+       *     defaultOption: {
+       *         yy: 456
+       *     },
+       *     fn: function () {
+       *         let opt = this.getDefaultOption();
+       *         // opt is {xx: 123, yy: 456}
+       *     }
+       * })
+       * ```
+       */
+
 
       ComponentModel.prototype.getDefaultOption = function () {
-        var ctor = this.constructor;
+        var ctor = this.constructor; // If using class declaration, it is different to travel super class
+        // in legacy env and auto merge defaultOption. So if using class
+        // declaration, defaultOption should be merged manually.
 
         if (!isExtendedClass(ctor)) {
+          // When using ts class, defaultOption must be declared as static.
           return ctor.defaultOption;
-        }
+        } // FIXME: remove this approach?
+
 
         var fields = inner(this);
 
@@ -15570,6 +16937,15 @@
 
         return fields.defaultOption;
       };
+      /**
+       * Notice: always force to input param `useDefault` in case that forget to consider it.
+       * The same behavior as `modelUtil.parseFinder`.
+       *
+       * @param useDefault In many cases like series refer axis and axis refer grid,
+       *        If axis index / axis id not specified, use the first target as default.
+       *        In other cases like dataZoom refer axis, if not specified, measn no refer.
+       */
+
 
       ComponentModel.prototype.getReferringComponents = function (mainType, opt) {
         var indexKey = mainType + 'Index';
@@ -15581,6 +16957,7 @@
       };
 
       ComponentModel.prototype.getBoxLayoutParams = function () {
+        // Consider itself having box layout configs.
         var boxLayoutModel = this;
         return {
           left: boxLayoutModel.get('left'),
@@ -15614,10 +16991,11 @@
       var deps = [];
       each(ComponentModel.getClassesByMainType(componentType), function (clz) {
         deps = deps.concat(clz.dependencies || clz.prototype.dependencies || []);
-      });
+      }); // Ensure main type.
+
       deps = map(deps, function (type) {
         return parseClassType(type).main;
-      });
+      }); // Hack dataset for convenience.
 
       if (componentType !== 'dataset' && indexOf(deps, 'dataset') <= 0) {
         deps.unshift('dataset');
@@ -15650,16 +17028,47 @@
      * AUTO-GENERATED FILE. DO NOT MODIFY.
      */
 
-    var platform = '';
+    /*
+    * Licensed to the Apache Software Foundation (ASF) under one
+    * or more contributor license agreements.  See the NOTICE file
+    * distributed with this work for additional information
+    * regarding copyright ownership.  The ASF licenses this file
+    * to you under the Apache License, Version 2.0 (the
+    * "License"); you may not use this file except in compliance
+    * with the License.  You may obtain a copy of the License at
+    *
+    *   http://www.apache.org/licenses/LICENSE-2.0
+    *
+    * Unless required by applicable law or agreed to in writing,
+    * software distributed under the License is distributed on an
+    * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    * KIND, either express or implied.  See the License for the
+    * specific language governing permissions and limitations
+    * under the License.
+    */
+    var platform = ''; // Navigator not exists in node
 
     if (typeof navigator !== 'undefined') {
+      /* global navigator */
       platform = navigator.platform || '';
     }
 
     var decalColor = 'rgba(0, 0, 0, 0.2)';
     var globalDefault = {
       darkMode: 'auto',
-      color: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'],
+      // backgroundColor: 'rgba(0,0,0,0)',
+      // https://dribbble.com/shots/1065960-Infographic-Pie-chart-visualization
+      // color: ['#5793f3', '#d14a61', '#fd9c35', '#675bba', '#fec42c', '#dd4444', '#d4df5a', '#cd4870'],
+      // Light colors:
+      // color: ['#bcd3bb', '#e88f70', '#edc1a5', '#9dc5c8', '#e1e8c8', '#7b7c68', '#e5b5b5', '#f0b489', '#928ea8', '#bda29a'],
+      // color: ['#cc5664', '#9bd6ec', '#ea946e', '#8acaaa', '#f1ec64', '#ee8686', '#a48dc1', '#5da6bc', '#b9dcae'],
+      // Dark colors:
+      // color: [
+      //     '#c23531', '#2f4554', '#61a0a8', '#d48265', '#91c7ae', '#749f83',
+      //     '#ca8622', '#bda29a', '#6e7074', '#546570', '#c4ccd3'
+      // ],
+      color: [// '#51689b', '#ce5c5c', '#fbc357', '#8fbf8f', '#659d84', '#fb8e6a', '#c77288', '#786090', '#91c4c5', '#6890ba'
+      '#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'],
       gradientColor: ['#f6efa6', '#d88273', '#bf444c'],
       aria: {
         decal: {
@@ -15698,12 +17107,21 @@
           }]
         }
       },
+      // If xAxis and yAxis declared, grid is created by default.
+      // grid: {},
       textStyle: {
+        // color: '#000',
+        // decoration: 'none',
+        // PENDING
         fontFamily: platform.match(/^Win/) ? 'Microsoft YaHei' : 'sans-serif',
+        // fontFamily: 'Arial, Verdana, sans-serif',
         fontSize: 12,
         fontStyle: 'normal',
         fontWeight: 'normal'
       },
+      // http://blogs.adobe.com/webplatform/2014/02/24/using-blend-modes-in-html-canvas/
+      // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
+      // Default is source-over
       blendMode: null,
       stateAnimation: {
         duration: 300,
@@ -15715,9 +17133,16 @@
       animationEasing: 'cubicInOut',
       animationEasingUpdate: 'cubicInOut',
       animationThreshold: 2000,
+      // Configuration for progressive/incremental rendering
       progressiveThreshold: 3000,
       progressive: 400,
+      // Threshold of if use single hover layer to optimize.
+      // It is recommended that `hoverLayerThreshold` is equivalent to or less than
+      // `progressiveThreshold`, otherwise hover will cause restart of progressive,
+      // which is unexpected.
+      // see example <echarts/test/heatmap-large.html>.
       hoverLayerThreshold: 3000,
+      // See: module:echarts/scale/Time
       useUTC: false
     };
 
@@ -15734,15 +17159,37 @@
     var BE_ORDINAL = {
       Must: 1,
       Might: 2,
-      Not: 3
+      Not: 3 // Other cases
+
     };
     var innerGlobalModel = makeInner();
+    /**
+     * MUST be called before mergeOption of all series.
+     */
+
     function resetSourceDefaulter(ecModel) {
+      // `datasetMap` is used to make default encode.
       innerGlobalModel(ecModel).datasetMap = createHashMap();
     }
+    /**
+     * [The strategy of the arrengment of data dimensions for dataset]:
+     * "value way": all axes are non-category axes. So series one by one take
+     *     several (the number is coordSysDims.length) dimensions from dataset.
+     *     The result of data arrengment of data dimensions like:
+     *     | ser0_x | ser0_y | ser1_x | ser1_y | ser2_x | ser2_y |
+     * "category way": at least one axis is category axis. So the the first data
+     *     dimension is always mapped to the first category axis and shared by
+     *     all of the series. The other data dimensions are taken by series like
+     *     "value way" does.
+     *     The result of data arrengment of data dimensions like:
+     *     | ser_shared_x | ser0_y | ser1_y | ser2_y |
+     *
+     * @return encode Never be `null/undefined`.
+     */
+
     function makeSeriesEncodeForAxisCoordSys(coordDimensions, seriesModel, source) {
       var encode = {};
-      var datasetModel = querySeriesUpstreamDatasetModel(seriesModel);
+      var datasetModel = querySeriesUpstreamDatasetModel(seriesModel); // Currently only make default when using dataset, util more reqirements occur.
 
       if (!datasetModel || !coordDimensions) {
         return encode;
@@ -15771,25 +17218,34 @@
       var datasetRecord = datasetMap.get(key) || datasetMap.set(key, {
         categoryWayDim: categoryWayValueDimStart,
         valueWayDim: 0
-      });
+      }); // TODO
+      // Auto detect first time axis and do arrangement.
+
       each(coordDimensions, function (coordDimInfo, coordDimIdx) {
         var coordDimName = coordDimInfo.name;
-        var count = getDataDimCountOnCoordDim(coordDimInfo);
+        var count = getDataDimCountOnCoordDim(coordDimInfo); // In value way.
 
         if (baseCategoryDimIndex == null) {
           var start = datasetRecord.valueWayDim;
           pushDim(encode[coordDimName], start, count);
           pushDim(encodeSeriesName, start, count);
-          datasetRecord.valueWayDim += count;
-        } else if (baseCategoryDimIndex === coordDimIdx) {
-          pushDim(encode[coordDimName], 0, count);
-          pushDim(encodeItemName, 0, count);
-        } else {
-          var start = datasetRecord.categoryWayDim;
-          pushDim(encode[coordDimName], start, count);
-          pushDim(encodeSeriesName, start, count);
-          datasetRecord.categoryWayDim += count;
-        }
+          datasetRecord.valueWayDim += count; // ??? TODO give a better default series name rule?
+          // especially when encode x y specified.
+          // consider: when mutiple series share one dimension
+          // category axis, series name should better use
+          // the other dimsion name. On the other hand, use
+          // both dimensions name.
+        } // In category way, the first category axis.
+        else if (baseCategoryDimIndex === coordDimIdx) {
+            pushDim(encode[coordDimName], 0, count);
+            pushDim(encodeItemName, 0, count);
+          } // In category way, the other axis.
+          else {
+              var start = datasetRecord.categoryWayDim;
+              pushDim(encode[coordDimName], start, count);
+              pushDim(encodeSeriesName, start, count);
+              datasetRecord.categoryWayDim += count;
+            }
       });
 
       function pushDim(dimIdxArr, idxFrom, idxCount) {
@@ -15807,9 +17263,15 @@
       encodeSeriesName.length && (encode.seriesName = encodeSeriesName);
       return encode;
     }
+    /**
+     * Work for data like [{name: ..., value: ...}, ...].
+     *
+     * @return encode Never be `null/undefined`.
+     */
+
     function makeSeriesEncodeForNameBased(seriesModel, source, dimCount) {
       var encode = {};
-      var datasetModel = querySeriesUpstreamDatasetModel(seriesModel);
+      var datasetModel = querySeriesUpstreamDatasetModel(seriesModel); // Currently only make default when using dataset, util more reqirements occur.
 
       if (!datasetModel) {
         return encode;
@@ -15830,12 +17292,14 @@
       var idxResult = function () {
         var idxRes0 = {};
         var idxRes1 = {};
-        var guessRecords = [];
+        var guessRecords = []; // 5 is an experience value.
 
         for (var i = 0, len = Math.min(5, dimCount); i < len; i++) {
           var guessResult = doGuessOrdinal(source.data, sourceFormat, source.seriesLayoutBy, dimensionsDefine, source.startIndex, i);
           guessRecords.push(guessResult);
-          var isPureNumber = guessResult === BE_ORDINAL.Not;
+          var isPureNumber = guessResult === BE_ORDINAL.Not; // [Strategy of idxRes0]: find the first BE_ORDINAL.Not as the value dim,
+          // and then find a name dim with the priority:
+          // "BE_ORDINAL.Might|BE_ORDINAL.Must" > "other dim" > "the value dim itself".
 
           if (isPureNumber && idxRes0.v == null && i !== potentialNameDimIndex) {
             idxRes0.v = i;
@@ -15847,7 +17311,13 @@
 
           if (fulfilled(idxRes0) && guessRecords[idxRes0.n] !== BE_ORDINAL.Not) {
             return idxRes0;
-          }
+          } // [Strategy of idxRes1]: if idxRes0 not satisfied (that is, no BE_ORDINAL.Not),
+          // find the first BE_ORDINAL.Might as the value dim,
+          // and then find a name dim with the priority:
+          // "other dim" > "the value dim itself".
+          // That is for backward compat: number-like (e.g., `'3'`, `'55'`) can be
+          // treated as number.
+
 
           if (!isPureNumber) {
             if (guessResult === BE_ORDINAL.Might && idxRes1.v == null && i !== potentialNameDimIndex) {
@@ -15868,15 +17338,27 @@
       }();
 
       if (idxResult) {
-        encode.value = [idxResult.v];
-        var nameDimIndex = potentialNameDimIndex != null ? potentialNameDimIndex : idxResult.n;
+        encode.value = [idxResult.v]; // `potentialNameDimIndex` has highest priority.
+
+        var nameDimIndex = potentialNameDimIndex != null ? potentialNameDimIndex : idxResult.n; // By default, label use itemName in charts.
+        // So we dont set encodeLabel here.
+
         encode.itemName = [nameDimIndex];
         encode.seriesName = [nameDimIndex];
       }
 
       return encode;
     }
+    /**
+     * @return If return null/undefined, indicate that should not use datasetModel.
+     */
+
     function querySeriesUpstreamDatasetModel(seriesModel) {
+      // Caution: consider the scenario:
+      // A dataset is declared and a series is not expected to use the dataset,
+      // and at the beginning `setOption({series: { noData })` (just prepare other
+      // option but no data), then `setOption({series: {data: [...]}); In this case,
+      // the user should set an empty array to avoid that dataset is used by default.
       var thisData = seriesModel.get('data', true);
 
       if (!thisData) {
@@ -15886,7 +17368,13 @@
         }, SINGLE_REFERRING).models[0];
       }
     }
+    /**
+     * @return Always return an array event empty.
+     */
+
     function queryDatasetUpstreamDatasetModels(datasetModel) {
+      // Only these attributes declared, we by defualt reference to `datasetIndex: 0`.
+      // Otherwise, no reference.
       if (!datasetModel.get('transform', true) && !datasetModel.get('fromTransformResult', true)) {
         return [];
       }
@@ -15896,17 +17384,27 @@
         id: datasetModel.get('fromDatasetId', true)
       }, SINGLE_REFERRING).models;
     }
+    /**
+     * The rule should not be complex, otherwise user might not
+     * be able to known where the data is wrong.
+     * The code is ugly, but how to make it neat?
+     */
+
     function guessOrdinal(source, dimIndex) {
       return doGuessOrdinal(source.data, source.sourceFormat, source.seriesLayoutBy, source.dimensionsDefine, source.startIndex, dimIndex);
-    }
+    } // dimIndex may be overflow source data.
+    // return {BE_ORDINAL}
 
     function doGuessOrdinal(data, sourceFormat, seriesLayoutBy, dimensionsDefine, startIndex, dimIndex) {
-      var result;
+      var result; // Experience value.
+
       var maxLoop = 5;
 
       if (isTypedArray(data)) {
         return BE_ORDINAL.Not;
-      }
+      } // When sourceType is 'objectRows' or 'keyedColumns', dimensionsDefine
+      // always exists in source.
+
 
       var dimName;
       var dimType;
@@ -15996,7 +17494,8 @@
       }
 
       function detectValue(val) {
-        var beStr = isString(val);
+        var beStr = isString(val); // Consider usage convenience, '1', '2' will be treated as "number".
+        // `isFinit('')` get `true`.
 
         if (val != null && isFinite(val) && val !== '') {
           return beStr ? BE_ORDINAL.Might : BE_ORDINAL.Not;
@@ -16038,7 +17537,9 @@
     var innerColor = makeInner();
     var innerDecal = makeInner();
 
-    var PaletteMixin = function () {
+    var PaletteMixin =
+    /** @class */
+    function () {
       function PaletteMixin() {}
 
       PaletteMixin.prototype.getColorFromPalette = function (name, scope, requestNum) {
@@ -16060,7 +17561,7 @@
     }
 
     function getNearestPalette(palettes, requestColorNum) {
-      var paletteNum = palettes.length;
+      var paletteNum = palettes.length; // TODO palettes must be in order
 
       for (var i = 0; i < paletteNum; i++) {
         if (palettes[i].length > requestColorNum) {
@@ -16070,18 +17571,26 @@
 
       return palettes[paletteNum - 1];
     }
+    /**
+     * @param name MUST NOT be null/undefined. Otherwise call this function
+     *             twise with the same parameters will get different result.
+     * @param scope default this.
+     * @return Can be null/undefined
+     */
+
 
     function getFromPalette(that, inner, defaultPalette, layeredPalette, name, scope, requestNum) {
       scope = scope || that;
       var scopeFields = inner(scope);
       var paletteIdx = scopeFields.paletteIdx || 0;
-      var paletteNameMap = scopeFields.paletteNameMap = scopeFields.paletteNameMap || {};
+      var paletteNameMap = scopeFields.paletteNameMap = scopeFields.paletteNameMap || {}; // Use `hasOwnProperty` to avoid conflict with Object.prototype.
 
       if (paletteNameMap.hasOwnProperty(name)) {
         return paletteNameMap[name];
       }
 
-      var palette = requestNum == null || !layeredPalette ? defaultPalette : getNearestPalette(layeredPalette, requestNum);
+      var palette = requestNum == null || !layeredPalette ? defaultPalette : getNearestPalette(layeredPalette, requestNum); // In case can't find in layered color palette.
+
       palette = palette || defaultPalette;
 
       if (!palette || !palette.length) {
@@ -16103,13 +17612,18 @@
       inner(that).paletteNameMap = {};
     }
 
+    // Internal method names:
+    // -----------------------
+
     var reCreateSeriesIndices;
     var assertSeriesInitialized;
     var initBase;
     var OPTION_INNER_KEY = '\0_ec_inner';
     var OPTION_INNER_VALUE = 1;
 
-    var GlobalModel = function (_super) {
+    var GlobalModel =
+    /** @class */
+    function (_super) {
       __extends(GlobalModel, _super);
 
       function GlobalModel() {
@@ -16118,7 +17632,8 @@
 
       GlobalModel.prototype.init = function (option, parentModel, ecModel, theme, locale, optionManager) {
         theme = theme || {};
-        this.option = null;
+        this.option = null; // Mark as not initialized.
+
         this._theme = new Model(theme);
         this._locale = new Model(locale);
         this._optionManager = optionManager;
@@ -16136,6 +17651,14 @@
 
         this._resetOption(null, innerOpt);
       };
+      /**
+       * @param type null/undefined: reset all.
+       *        'recreate': force recreate all.
+       *        'timeline': only reset timeline option
+       *        'media': only reset media query option
+       * @return Whether option changed.
+       */
+
 
       GlobalModel.prototype.resetOption = function (type, opt) {
         return this._resetOption(type, normalizeSetOptionInput(opt));
@@ -16161,7 +17684,15 @@
 
         if (type === 'timeline' || type === 'media') {
           this.restoreData();
-        }
+        } // By design, if `setOption(option2)` at the second time, and `option2` is a `ECUnitOption`,
+        // it should better not have the same props with `MediaUnit['option']`.
+        // Becuase either `option2` or `MediaUnit['option']` will be always merged to "current option"
+        // rather than original "baseOption". If they both override a prop, the result might be
+        // unexpected when media state changed after `setOption` called.
+        // If we really need to modify a props in each `MediaUnit['option']`, use the full version
+        // (`{baseOption, media}`) in `setOption`.
+        // For `timeline`, the case is the same.
+
 
         if (!type || type === 'recreate' || type === 'timeline') {
           var timelineOption = optionManager.getTimelineOption(this);
@@ -16199,13 +17730,16 @@
         var newCmptTypes = [];
         var newCmptTypeMap = createHashMap();
         var replaceMergeMainTypeMap = opt && opt.replaceMergeMainTypeMap;
-        resetSourceDefaulter(this);
+        resetSourceDefaulter(this); // If no component class, merge directly.
+        // For example: color, animaiton options, etc.
+
         each(newOption, function (componentOption, mainType) {
           if (componentOption == null) {
             return;
           }
 
           if (!ComponentModel.hasClass(mainType)) {
+            // globalSettingTask.dirty();
             option[mainType] = option[mainType] == null ? clone(componentOption) : merge(option[mainType], componentOption, true);
           } else if (mainType) {
             newCmptTypes.push(mainType);
@@ -16214,6 +17748,10 @@
         });
 
         if (replaceMergeMainTypeMap) {
+          // If there is a mainType `xxx` in `replaceMerge` but not declared in option,
+          // we trade it as it is declared in option as `{xxx: []}`. Because:
+          // (1) for normal merge, `{xxx: null/undefined}` are the same meaning as `{xxx: []}`.
+          // (2) some preprocessor may convert some of `{xxx: null/undefined}` to `{xxx: []}`.
           replaceMergeMainTypeMap.each(function (val, mainTypeInReplaceMerge) {
             if (ComponentModel.hasClass(mainTypeInReplaceMerge) && !newCmptTypeMap.get(mainTypeInReplaceMerge)) {
               newCmptTypes.push(mainTypeInReplaceMerge);
@@ -16227,9 +17765,14 @@
         function visitComponent(mainType) {
           var newCmptOptionList = concatInternalOptions(this, mainType, normalizeToArray(newOption[mainType]));
           var oldCmptList = componentsMap.get(mainType);
-          var mergeMode = !oldCmptList ? 'replaceAll' : replaceMergeMainTypeMap && replaceMergeMainTypeMap.get(mainType) ? 'replaceMerge' : 'normalMerge';
-          var mappingResult = mappingToExists(oldCmptList, newCmptOptionList, mergeMode);
-          setComponentTypeToKeyInfo(mappingResult, mainType, ComponentModel);
+          var mergeMode = // `!oldCmptList` means init. See the comment in `mappingToExists`
+          !oldCmptList ? 'replaceAll' : replaceMergeMainTypeMap && replaceMergeMainTypeMap.get(mainType) ? 'replaceMerge' : 'normalMerge';
+          var mappingResult = mappingToExists(oldCmptList, newCmptOptionList, mergeMode); // Set mainType and complete subType.
+
+          setComponentTypeToKeyInfo(mappingResult, mainType, ComponentModel); // Empty it before the travel, in order to prevent `this._componentsMap`
+          // from being used in the `init`/`mergeOption`/`optionUpdated` of some
+          // components, which is probably incorrect logic.
+
           option[mainType] = null;
           componentsMap.set(mainType, null);
           componentsCount.set(mainType, 0);
@@ -16242,28 +17785,41 @@
 
             if (!newCmptOption) {
               if (componentModel) {
+                // Consider where is no new option and should be merged using {},
+                // see removeEdgeAndAdd in topologicalTravel and
+                // ComponentModel.getAllClassMainTypes.
                 componentModel.mergeOption({}, this);
                 componentModel.optionUpdated({}, false);
-              }
+              } // If no both `resultItem.exist` and `resultItem.option`,
+              // either it is in `replaceMerge` and not matched by any id,
+              // or it has been removed in previous `replaceMerge` and left a "hole" in this component index.
+
             } else {
               var ComponentModelClass = ComponentModel.getClass(mainType, resultItem.keyInfo.subType, true);
 
               if (componentModel && componentModel.constructor === ComponentModelClass) {
-                componentModel.name = resultItem.keyInfo.name;
+                componentModel.name = resultItem.keyInfo.name; // componentModel.settingTask && componentModel.settingTask.dirty();
+
                 componentModel.mergeOption(newCmptOption, this);
                 componentModel.optionUpdated(newCmptOption, false);
               } else {
+                // PENDING Global as parent ?
                 var extraOpt = extend({
                   componentIndex: index
                 }, resultItem.keyInfo);
-                componentModel = new ComponentModelClass(newCmptOption, this, this, extraOpt);
+                componentModel = new ComponentModelClass(newCmptOption, this, this, extraOpt); // Assign `keyInfo`
+
                 extend(componentModel, extraOpt);
 
                 if (resultItem.brandNew) {
                   componentModel.__requireNewView = true;
                 }
 
-                componentModel.init(newCmptOption, this, this);
+                componentModel.init(newCmptOption, this, this); // Call optionUpdated after init.
+                // newCmptOption has been used as componentModel.option
+                // and may be merged with theme and default, so pass null
+                // to avoid confusion.
+
                 componentModel.optionUpdated(null, true);
               }
             }
@@ -16273,33 +17829,43 @@
               cmptsByMainType.push(componentModel);
               cmptsCountByMainType++;
             } else {
+              // Always do assign to avoid elided item in array.
               optionsByMainType.push(void 0);
               cmptsByMainType.push(void 0);
             }
           }, this);
           option[mainType] = optionsByMainType;
           componentsMap.set(mainType, cmptsByMainType);
-          componentsCount.set(mainType, cmptsCountByMainType);
+          componentsCount.set(mainType, cmptsCountByMainType); // Backup series for filtering.
 
           if (mainType === 'series') {
             reCreateSeriesIndices(this);
           }
-        }
+        } // If no series declared, ensure `_seriesIndices` initialized.
+
 
         if (!this._seriesIndices) {
           reCreateSeriesIndices(this);
         }
       };
+      /**
+       * Get option for output (cloned option and inner info removed)
+       */
+
 
       GlobalModel.prototype.getOption = function () {
         var option = clone(this.option);
         each(option, function (optInMainType, mainType) {
           if (ComponentModel.hasClass(mainType)) {
-            var opts = normalizeToArray(optInMainType);
+            var opts = normalizeToArray(optInMainType); // Inner cmpts need to be removed.
+            // Inner cmpts might not be at last since ec5.0, but still
+            // compatible for users: if inner cmpt at last, splice the returned array.
+
             var realLen = opts.length;
             var metNonInner = false;
 
             for (var i = realLen - 1; i >= 0; i--) {
+              // Remove options with inner id.
               if (opts[i] && !isComponentIdInternal(opts[i])) {
                 metNonInner = true;
               } else {
@@ -16336,6 +17902,10 @@
       GlobalModel.prototype.getUpdatePayload = function () {
         return this._payload;
       };
+      /**
+       * @param idx If not specified, return the first one.
+       */
+
 
       GlobalModel.prototype.getComponent = function (mainType, idx) {
         var list = this._componentsMap.get(mainType);
@@ -16354,6 +17924,10 @@
           }
         }
       };
+      /**
+       * @return Never be null/undefined.
+       */
+
 
       GlobalModel.prototype.queryComponents = function (condition) {
         var mainType = condition.mainType;
@@ -16384,6 +17958,7 @@
         } else if (name != null) {
           result = queryByIdOrName('name', name, cmpts);
         } else {
+          // Return all non-empty components in that mainType
           result = filter(cmpts, function (cmpt) {
             return !!cmpt;
           });
@@ -16391,12 +17966,31 @@
 
         return filterBySubType(result, condition);
       };
+      /**
+       * The interface is different from queryComponents,
+       * which is convenient for inner usage.
+       *
+       * @usage
+       * let result = findComponents(
+       *     {mainType: 'dataZoom', query: {dataZoomId: 'abc'}}
+       * );
+       * let result = findComponents(
+       *     {mainType: 'series', subType: 'pie', query: {seriesName: 'uio'}}
+       * );
+       * let result = findComponents(
+       *     {mainType: 'series',
+       *     filter: function (model, index) {...}}
+       * );
+       * // result like [component0, componnet1, ...]
+       */
+
 
       GlobalModel.prototype.findComponents = function (condition) {
         var query = condition.query;
         var mainType = condition.mainType;
         var queryCond = getQueryCond(query);
-        var result = queryCond ? this.queryComponents(queryCond) : filter(this._componentsMap.get(mainType), function (cmpt) {
+        var result = queryCond ? this.queryComponents(queryCond) // Retrieve all non-empty components.
+        : filter(this._componentsMap.get(mainType), function (cmpt) {
           return !!cmpt;
         });
         return doFilter(filterBySubType(result, condition));
@@ -16407,6 +18001,7 @@
           var nameAttr = mainType + 'Name';
           return q && (q[indexAttr] != null || q[idAttr] != null || q[nameAttr] != null) ? {
             mainType: mainType,
+            // subType will be filtered finally.
             index: q[indexAttr],
             id: q[idAttr],
             name: q[nameAttr]
@@ -16439,6 +18034,10 @@
           }
         }
       };
+      /**
+       * Get series list before filtered by name.
+       */
+
 
       GlobalModel.prototype.getSeriesByName = function (name) {
         var nameStr = convertOptionIdName(name, null);
@@ -16446,26 +18045,48 @@
           return !!oneSeries && nameStr != null && oneSeries.name === nameStr;
         });
       };
+      /**
+       * Get series list before filtered by index.
+       */
+
 
       GlobalModel.prototype.getSeriesByIndex = function (seriesIndex) {
         return this._componentsMap.get('series')[seriesIndex];
       };
+      /**
+       * Get series list before filtered by type.
+       * FIXME: rename to getRawSeriesByType?
+       */
+
 
       GlobalModel.prototype.getSeriesByType = function (subType) {
         return filter(this._componentsMap.get('series'), function (oneSeries) {
           return !!oneSeries && oneSeries.subType === subType;
         });
       };
+      /**
+       * Get all series before filtered.
+       */
+
 
       GlobalModel.prototype.getSeries = function () {
         return filter(this._componentsMap.get('series').slice(), function (oneSeries) {
           return !!oneSeries;
         });
       };
+      /**
+       * Count series before filtered.
+       */
+
 
       GlobalModel.prototype.getSeriesCount = function () {
         return this._componentsCount.get('series');
       };
+      /**
+       * After filtering, series may be different
+       * frome raw series.
+       */
+
 
       GlobalModel.prototype.eachSeries = function (cb, context) {
         assertSeriesInitialized(this);
@@ -16475,12 +18096,24 @@
           cb.call(context, series, rawSeriesIndex);
         }, this);
       };
+      /**
+       * Iterate raw series before filtered.
+       *
+       * @param {Function} cb
+       * @param {*} context
+       */
+
 
       GlobalModel.prototype.eachRawSeries = function (cb, context) {
         each(this._componentsMap.get('series'), function (series) {
           series && cb.call(context, series, series.componentIndex);
         });
       };
+      /**
+       * After filtering, series may be different.
+       * frome raw series.
+       */
+
 
       GlobalModel.prototype.eachSeriesByType = function (subType, cb, context) {
         assertSeriesInitialized(this);
@@ -16492,6 +18125,10 @@
           }
         }, this);
       };
+      /**
+       * Iterate raw series before filtered of given type.
+       */
+
 
       GlobalModel.prototype.eachRawSeriesByType = function (subType, cb, context) {
         return each(this.getSeriesByType(subType), cb, context);
@@ -16540,12 +18177,15 @@
         reCreateSeriesIndices = function (ecModel) {
           var seriesIndices = ecModel._seriesIndices = [];
           each(ecModel._componentsMap.get('series'), function (series) {
+            // series may have been removed by `replaceMerge`.
             series && seriesIndices.push(series.componentIndex);
           });
           ecModel._seriesIndicesMap = createHashMap(seriesIndices);
         };
 
         assertSeriesInitialized = function (ecModel) {
+          // Components that use _seriesIndices should depends on series component,
+          // which make sure that their initialization is after series.
           if ("development" !== 'production') {
             if (!ecModel._seriesIndices) {
               throw new Error('Option should contains series.');
@@ -16554,19 +18194,26 @@
         };
 
         initBase = function (ecModel, baseOption) {
+          // Using OPTION_INNER_KEY to mark that this option can not be used outside,
+          // i.e. `chart.setOption(chart.getModel().option);` is forbiden.
           ecModel.option = {};
-          ecModel.option[OPTION_INNER_KEY] = OPTION_INNER_VALUE;
+          ecModel.option[OPTION_INNER_KEY] = OPTION_INNER_VALUE; // Init with series: [], in case of calling findSeries method
+          // before series initialized.
+
           ecModel._componentsMap = createHashMap({
             series: []
           });
-          ecModel._componentsCount = createHashMap();
+          ecModel._componentsCount = createHashMap(); // If user spefied `option.aria`, aria will be enable. This detection should be
+          // performed before theme and globalDefault merge.
+
           var airaOption = baseOption.aria;
 
           if (isObject(airaOption) && airaOption.enabled == null) {
             airaOption.enabled = true;
           }
 
-          mergeTheme(baseOption, ecModel._theme.option);
+          mergeTheme(baseOption, ecModel._theme.option); // TODO Needs clone when merging to the unexisted property
+
           merge(baseOption, globalDefault, false);
 
           ecModel._mergeOption(baseOption, null);
@@ -16586,11 +18233,15 @@
     }
 
     function mergeTheme(option, theme) {
+      // PENDING
+      // NOT use `colorLayer` in theme if option has `color`
       var notMergeColorLayer = option.color && !option.colorLayer;
       each(theme, function (themeItem, name) {
         if (name === 'colorLayer' && notMergeColorLayer) {
           return;
-        }
+        } // If it is component model mainType, the model handles that merge later.
+        // otherwise, merge them here.
+
 
         if (!ComponentModel.hasClass(name)) {
           if (typeof themeItem === 'object') {
@@ -16605,6 +18256,8 @@
     }
 
     function queryByIdOrName(attr, idOrName, cmpts) {
+      // Here is a break from echarts4: string and number are
+      // treated as equal.
       if (isArray(idOrName)) {
         var keyMap_1 = createHashMap();
         each(idOrName, function (idOrNameItem) {
@@ -16625,6 +18278,8 @@
     }
 
     function filterBySubType(components, condition) {
+      // Using hasOwnProperty for restrict. Consider
+      // subType is undefined in user payload.
       return condition.hasOwnProperty('subType') ? filter(components, function (cmpt) {
         return cmpt && cmpt.subType === condition.subType;
       }) : components;
@@ -16646,9 +18301,14 @@
 
     mixin(GlobalModel, PaletteMixin);
 
-    var availableMethods = ['getDom', 'getZr', 'getWidth', 'getHeight', 'getDevicePixelRatio', 'dispatchAction', 'isDisposed', 'on', 'off', 'getDataURL', 'getConnectedDataURL', 'getOption', 'getId', 'updateLabelLayout'];
+    var availableMethods = ['getDom', 'getZr', 'getWidth', 'getHeight', 'getDevicePixelRatio', 'dispatchAction', 'isDisposed', 'on', 'off', 'getDataURL', 'getConnectedDataURL', // 'getModel',
+    'getOption', // 'getViewOfComponentModel',
+    // 'getViewOfSeriesModel',
+    'getId', 'updateLabelLayout'];
 
-    var ExtensionAPI = function () {
+    var ExtensionAPI =
+    /** @class */
+    function () {
       function ExtensionAPI(ecInstance) {
         each(availableMethods, function (methodName) {
           this[methodName] = bind(ecInstance[methodName], ecInstance);
@@ -16660,7 +18320,9 @@
 
     var coordinateSystemCreators = {};
 
-    var CoordinateSystemManager = function () {
+    var CoordinateSystemManager =
+    /** @class */
+    function () {
       function CoordinateSystemManager() {
         this._coordinateSystems = [];
       }
@@ -16695,32 +18357,76 @@
       return CoordinateSystemManager;
     }();
 
-    var QUERY_REG = /^(min|max)?(.+)$/;
+    var QUERY_REG = /^(min|max)?(.+)$/; // Key: mainType
+    // type FakeComponentsMap = HashMap<(MappingExistingItem & { subType: string })[]>;
+
+    /**
+     * TERM EXPLANATIONS:
+     * See `ECOption` and `ECUnitOption` in `src/util/types.ts`.
+     */
 
-    var OptionManager = function () {
+    var OptionManager =
+    /** @class */
+    function () {
+      // timeline.notMerge is not supported in ec3. Firstly there is rearly
+      // case that notMerge is needed. Secondly supporting 'notMerge' requires
+      // rawOption cloned and backuped when timeline changed, which does no
+      // good to performance. What's more, that both timeline and setOption
+      // method supply 'notMerge' brings complex and some problems.
+      // Consider this case:
+      // (step1) chart.setOption({timeline: {notMerge: false}, ...}, false);
+      // (step2) chart.setOption({timeline: {notMerge: true}, ...}, false);
       function OptionManager(api) {
         this._timelineOptions = [];
         this._mediaList = [];
+        /**
+         * -1, means default.
+         * empty means no media.
+         */
+
         this._currentMediaIndices = [];
         this._api = api;
       }
 
       OptionManager.prototype.setOption = function (rawOption, optionPreprocessorFuncs, opt) {
         if (rawOption) {
+          // That set dat primitive is dangerous if user reuse the data when setOption again.
           each(normalizeToArray(rawOption.series), function (series) {
             series && series.data && isTypedArray(series.data) && setAsPrimitive(series.data);
           });
           each(normalizeToArray(rawOption.dataset), function (dataset) {
             dataset && dataset.source && isTypedArray(dataset.source) && setAsPrimitive(dataset.source);
           });
-        }
+        } // Caution: some series modify option data, if do not clone,
+        // it should ensure that the repeat modify correctly
+        // (create a new object when modify itself).
+
+
+        rawOption = clone(rawOption); // FIXME
+        // If some property is set in timeline options or media option but
+        // not set in baseOption, a warning should be given.
 
-        rawOption = clone(rawOption);
         var optionBackup = this._optionBackup;
         var newParsedOption = parseRawOption(rawOption, optionPreprocessorFuncs, !optionBackup);
-        this._newBaseOption = newParsedOption.baseOption;
+        this._newBaseOption = newParsedOption.baseOption; // For setOption at second time (using merge mode);
 
         if (optionBackup) {
+          // FIXME
+          // the restore merge solution is essentially incorrect.
+          // the mapping can not be 100% consistent with ecModel, which probably brings
+          // potential bug!
+          // The first merge is delayed, becuase in most cases, users do not call `setOption` twice.
+          // let fakeCmptsMap = this._fakeCmptsMap;
+          // if (!fakeCmptsMap) {
+          //     fakeCmptsMap = this._fakeCmptsMap = createHashMap();
+          //     mergeToBackupOption(fakeCmptsMap, null, optionBackup.baseOption, null);
+          // }
+          // mergeToBackupOption(
+          //     fakeCmptsMap, optionBackup.baseOption, newParsedOption.baseOption, opt
+          // );
+          // For simplicity, timeline options and media options do not support merge,
+          // that is, if you `setOption` twice and both has timeline options, the latter
+          // timeline opitons will not be merged to the formers, but just substitude them.
           if (newParsedOption.timelineOptions.length) {
             optionBackup.timelineOptions = newParsedOption.timelineOptions;
           }
@@ -16743,7 +18449,12 @@
         this._mediaList = optionBackup.mediaList;
         this._mediaDefault = optionBackup.mediaDefault;
         this._currentMediaIndices = [];
-        return clone(isRecreate ? optionBackup.baseOption : this._newBaseOption);
+        return clone(isRecreate // this._optionBackup.baseOption, which is created at the first `setOption`
+        // called, and is merged into every new option by inner method `mergeToBackupOption`
+        // each time `setOption` called, can be only used in `isRecreate`, because
+        // its reliability is under suspicion. In other cases option merge is
+        // performed by `model.mergeOption`.
+        ? optionBackup.baseOption : this._newBaseOption);
       };
 
       OptionManager.prototype.getTimelineOption = function (ecModel) {
@@ -16751,10 +18462,13 @@
         var timelineOptions = this._timelineOptions;
 
         if (timelineOptions.length) {
+          // getTimelineOption can only be called after ecModel inited,
+          // so we can get currentIndex from timelineModel.
           var timelineModel = ecModel.getComponent('timeline');
 
           if (timelineModel) {
-            option = clone(timelineOptions[timelineModel.getCurrentIndex()]);
+            option = clone( // FIXME:TS as TimelineModel or quivlant interface
+            timelineOptions[timelineModel.getCurrentIndex()]);
           }
         }
 
@@ -16769,17 +18483,21 @@
         var mediaList = this._mediaList;
         var mediaDefault = this._mediaDefault;
         var indices = [];
-        var result = [];
+        var result = []; // No media defined.
 
         if (!mediaList.length && !mediaDefault) {
           return result;
-        }
+        } // Multi media may be applied, the latter defined media has higher priority.
+
 
         for (var i = 0, len = mediaList.length; i < len; i++) {
           if (applyMediaQuery(mediaList[i].query, ecWidth, ecHeight)) {
             indices.push(i);
           }
-        }
+        } // FIXME
+        // Whether mediaDefault should force users to provide? Otherwise
+        // the change by media query can not be recorvered.
+
 
         if (!indices.length && mediaDefault) {
           indices = [-1];
@@ -16789,7 +18507,8 @@
           result = map(indices, function (index) {
             return clone(index === -1 ? mediaDefault.option : mediaList[index].option);
           });
-        }
+        } // Otherwise return nothing.
+
 
         this._currentMediaIndices = indices;
         return result;
@@ -16797,12 +18516,76 @@
 
       return OptionManager;
     }();
+    /**
+     * [RAW_OPTION_PATTERNS]
+     * (Note: "series: []" represents all other props in `ECUnitOption`)
+     *
+     * (1) No prop "baseOption" declared:
+     * Root option is used as "baseOption" (except prop "options" and "media").
+     * ```js
+     * option = {
+     *     series: [],
+     *     timeline: {},
+     *     options: [],
+     * };
+     * option = {
+     *     series: [],
+     *     media: {},
+     * };
+     * option = {
+     *     series: [],
+     *     timeline: {},
+     *     options: [],
+     *     media: {},
+     * }
+     * ```
+     *
+     * (2) Prop "baseOption" declared:
+     * If "baseOption" declared, `ECUnitOption` props can only be declared
+     * inside "baseOption" except prop "timeline" (compat ec2).
+     * ```js
+     * option = {
+     *     baseOption: {
+     *         timeline: {},
+     *         series: [],
+     *     },
+     *     options: []
+     * };
+     * option = {
+     *     baseOption: {
+     *         series: [],
+     *     },
+     *     media: []
+     * };
+     * option = {
+     *     baseOption: {
+     *         timeline: {},
+     *         series: [],
+     *     },
+     *     options: []
+     *     media: []
+     * };
+     * option = {
+     *     // ec3 compat ec2: allow (only) `timeline` declared
+     *     // outside baseOption. Keep this setting for compat.
+     *     timeline: {},
+     *     baseOption: {
+     *         series: [],
+     *     },
+     *     options: [],
+     *     media: []
+     * };
+     * ```
+     */
+
 
-    function parseRawOption(rawOption, optionPreprocessorFuncs, isNew) {
+    function parseRawOption( // `rawOption` May be modified
+    rawOption, optionPreprocessorFuncs, isNew) {
       var mediaList = [];
       var mediaDefault;
       var baseOption;
-      var declaredBaseOption = rawOption.baseOption;
+      var declaredBaseOption = rawOption.baseOption; // Compatible with ec2, [RAW_OPTION_PATTERNS] above.
+
       var timelineOnRoot = rawOption.timeline;
       var timelineOptionsOnRoot = rawOption.options;
       var mediaOnRoot = rawOption.media;
@@ -16810,23 +18593,26 @@
       var hasTimeline = !!(timelineOptionsOnRoot || timelineOnRoot || declaredBaseOption && declaredBaseOption.timeline);
 
       if (declaredBaseOption) {
-        baseOption = declaredBaseOption;
+        baseOption = declaredBaseOption; // For merge option.
 
         if (!baseOption.timeline) {
           baseOption.timeline = timelineOnRoot;
         }
-      } else {
-        if (hasTimeline || hasMedia) {
-          rawOption.options = rawOption.media = null;
-        }
+      } // For convenience, enable to use the root option as the `baseOption`:
+      // `{ ...normalOptionProps, media: [{ ... }, { ... }] }`
+      else {
+          if (hasTimeline || hasMedia) {
+            rawOption.options = rawOption.media = null;
+          }
 
-        baseOption = rawOption;
-      }
+          baseOption = rawOption;
+        }
 
       if (hasMedia) {
         if (isArray(mediaOnRoot)) {
           each(mediaOnRoot, function (singleMedia) {
             if ("development" !== 'production') {
+              // Real case of wrong config.
               if (singleMedia && !singleMedia.option && isObject(singleMedia.query) && isObject(singleMedia.query.option)) {
                 error('Illegal media option. Must be like { media: [ { query: {}, option: {} } ] }');
               }
@@ -16836,12 +18622,14 @@
               if (singleMedia.query) {
                 mediaList.push(singleMedia);
               } else if (!mediaDefault) {
+                // Use the first media default.
                 mediaDefault = singleMedia;
               }
             }
           });
         } else {
           if ("development" !== 'production') {
+            // Real case of wrong config.
             error('Illegal media option. Must be an array. Like { media: [ {...}, {...} ] }');
           }
         }
@@ -16868,12 +18656,19 @@
         mediaList: mediaList
       };
     }
+    /**
+     * @see <http://www.w3.org/TR/css3-mediaqueries/#media1>
+     * Support: width, height, aspectRatio
+     * Can use max or min as prefix.
+     */
+
 
     function applyMediaQuery(query, ecWidth, ecHeight) {
       var realMap = {
         width: ecWidth,
         height: ecHeight,
-        aspectratio: ecWidth / ecHeight
+        aspectratio: ecWidth / ecHeight // lowser case for convenientce.
+
       };
       var applicatable = true;
       each(query, function (value, attr) {
@@ -16899,11 +18694,13 @@
       } else if (operator === 'max') {
         return real <= expect;
       } else {
+        // Equals
         return real === expect;
       }
     }
 
     function indicesEquals(indices1, indices2) {
+      // indices is always order by asc and has only finite number.
       return indices1.join(',') === indices2.join(',');
     }
 
@@ -16964,8 +18761,10 @@
 
         if (normalOpt) {
           if ("development" !== 'production') {
+            // eslint-disable-next-line max-len
             deprecateLog("'normal' hierarchy in " + optType + " has been removed since 4.0. All style properties are configured in " + optType + " directly now.");
-          }
+          } // Timeline controlStyle has other properties besides normal and emphasis
+
 
           if (useExtend) {
             opt[optType].normal = opt[optType].emphasis = null;
@@ -16981,7 +18780,8 @@
           }
 
           opt.emphasis = opt.emphasis || {};
-          opt.emphasis[optType] = emphasisOpt;
+          opt.emphasis[optType] = emphasisOpt; // Also compat the case user mix the style and focus together in ec3 style
+          // for example: { itemStyle: { normal: {}, emphasis: {focus, shadowBlur} } }
 
           if (emphasisOpt.focus) {
             opt.emphasis.focus = emphasisOpt.focus;
@@ -16999,17 +18799,21 @@
       convertNormalEmphasis(opt, 'lineStyle');
       convertNormalEmphasis(opt, 'areaStyle');
       convertNormalEmphasis(opt, 'label');
-      convertNormalEmphasis(opt, 'labelLine');
-      convertNormalEmphasis(opt, 'upperLabel');
+      convertNormalEmphasis(opt, 'labelLine'); // treemap
+
+      convertNormalEmphasis(opt, 'upperLabel'); // graph
+
       convertNormalEmphasis(opt, 'edgeLabel');
     }
 
     function compatTextStyle(opt, propName) {
+      // Check whether is not object (string\null\undefined ...)
       var labelOptSingle = isObject$1(opt) && opt[propName];
       var textStyle = isObject$1(labelOptSingle) && labelOptSingle.textStyle;
 
       if (textStyle) {
         if ("development" !== 'production') {
+          // eslint-disable-next-line max-len
           deprecateLog("textStyle hierarchy in " + propName + " has been removed since 4.0. All textStyle properties are configured in " + propName + " directly now.");
         }
 
@@ -17038,13 +18842,17 @@
 
       compatEC2ItemStyle(seriesOpt);
       removeEC3NormalStatus(seriesOpt);
-      compatTextStyle(seriesOpt, 'label');
-      compatTextStyle(seriesOpt, 'upperLabel');
+      compatTextStyle(seriesOpt, 'label'); // treemap
+
+      compatTextStyle(seriesOpt, 'upperLabel'); // graph
+
       compatTextStyle(seriesOpt, 'edgeLabel');
 
       if (seriesOpt.emphasis) {
-        compatTextStyle(seriesOpt.emphasis, 'label');
-        compatTextStyle(seriesOpt.emphasis, 'upperLabel');
+        compatTextStyle(seriesOpt.emphasis, 'label'); // treemap
+
+        compatTextStyle(seriesOpt.emphasis, 'upperLabel'); // graph
+
         compatTextStyle(seriesOpt.emphasis, 'edgeLabel');
       }
 
@@ -17068,7 +18876,8 @@
         compatEC3CommonStyles(markArea);
       }
 
-      var data = seriesOpt.data;
+      var data = seriesOpt.data; // Break with ec3: if `setOption` again, there may be no `type` in option,
+      // then the backward compat based on option type will not be performed.
 
       if (seriesOpt.type === 'graph') {
         data = data || seriesOpt.nodes;
@@ -17089,7 +18898,8 @@
         for (var i = 0; i < data.length; i++) {
           compatEC3CommonStyles(data[i]);
         }
-      }
+      } // mark point data
+
 
       markPoint = seriesOpt.markPoint;
 
@@ -17099,7 +18909,8 @@
         for (var i = 0; i < mpData.length; i++) {
           compatEC3CommonStyles(mpData[i]);
         }
-      }
+      } // mark line data
+
 
       markLine = seriesOpt.markLine;
 
@@ -17114,7 +18925,8 @@
             compatEC3CommonStyles(mlData[i]);
           }
         }
-      }
+      } // Series
+
 
       if (seriesOpt.type === 'gauge') {
         compatTextStyle(seriesOpt, 'axisLabel');
@@ -17127,7 +18939,8 @@
         });
       } else if (seriesOpt.type === 'tree') {
         removeEC3NormalStatus(seriesOpt.leaves);
-      }
+      } // sunburst starts from ec4, so it does not need to compat levels.
+
     }
 
     function toArr(o) {
@@ -17162,9 +18975,10 @@
         compatTextStyle(calendarOpt, 'dayLabel');
         compatTextStyle(calendarOpt, 'monthLabel');
         compatTextStyle(calendarOpt, 'yearLabel');
-      });
+      }); // radar.name.textStyle
+
       each$2(toArr(option.radar), function (radarOpt) {
-        compatTextStyle(radarOpt, 'name');
+        compatTextStyle(radarOpt, 'name'); // Use axisName instead of name because component has name property
 
         if (radarOpt.name && radarOpt.axisName == null) {
           radarOpt.axisName = radarOpt.name;
@@ -17212,7 +19026,8 @@
         });
       });
       compatTextStyle(toObj(option.axisPointer), 'label');
-      compatTextStyle(toObj(option.tooltip).axisPointer, 'label');
+      compatTextStyle(toObj(option.tooltip).axisPointer, 'label'); // Clean logs
+      // storedLogs = {};
     }
 
     function get(opt, path) {
@@ -17338,7 +19153,8 @@
     }
 
     function globalBackwardCompat(option, isTheme) {
-      globalCompatStyle(option, isTheme);
+      globalCompatStyle(option, isTheme); // Make sure series array for model initialization.
+
       option.series = normalizeToArray(option.series);
       each(option.series, function (seriesOpt) {
         if (!isObject(seriesOpt)) {
@@ -17419,7 +19235,7 @@
           compatSunburstState(seriesOpt);
           traverseTree(seriesOpt.data, compatSunburstState);
         } else if (seriesType === 'graph' || seriesType === 'sankey') {
-          compatGraphFocus(seriesOpt);
+          compatGraphFocus(seriesOpt); // TODO nodes, edges?
         } else if (seriesType === 'map') {
           if (seriesOpt.mapType && !seriesOpt.map) {
             if ("development" !== 'production') {
@@ -17451,7 +19267,7 @@
         }
 
         compatLayoutProperties(seriesOpt);
-      });
+      }); // dataRange has changed to visualMap
 
       if (option.dataRange) {
         option.visualMap = option.dataRange;
@@ -17472,15 +19288,22 @@
       });
     }
 
+    //     data processing stage is blocked in stream.
+    //     See <module:echarts/stream/Scheduler#performDataProcessorTasks>
+    // (2) Only register once when import repeatly.
+    //     Should be executed after series filtered and before stack calculation.
+
     function dataStack(ecModel) {
       var stackInfoMap = createHashMap();
       ecModel.eachSeries(function (seriesModel) {
-        var stack = seriesModel.get('stack');
+        var stack = seriesModel.get('stack'); // Compatibal: when `stack` is set as '', do not stack.
 
         if (stack) {
           var stackInfoList = stackInfoMap.get(stack) || stackInfoMap.set(stack, []);
           var data = seriesModel.getData();
           var stackInfo = {
+            // Used for calculate axis extent automatically.
+            // TODO: Type getCalculationInfo return more specific type?
             stackResultDimension: data.getCalculationInfo('stackResultDimension'),
             stackedOverDimension: data.getCalculationInfo('stackedOverDimension'),
             stackedDimension: data.getCalculationInfo('stackedDimension'),
@@ -17488,7 +19311,7 @@
             isStackedByIndex: data.getCalculationInfo('isStackedByIndex'),
             data: data,
             seriesModel: seriesModel
-          };
+          }; // If stacked on axis that do not support data stack.
 
           if (!stackInfo.stackedDimension || !(stackInfo.isStackedByIndex || stackInfo.stackedByDimension)) {
             return;
@@ -17507,9 +19330,12 @@
         var resultNaN = [NaN, NaN];
         var dims = [targetStackInfo.stackResultDimension, targetStackInfo.stackedOverDimension];
         var targetData = targetStackInfo.data;
-        var isStackedByIndex = targetStackInfo.isStackedByIndex;
+        var isStackedByIndex = targetStackInfo.isStackedByIndex; // Should not write on raw data, because stack series model list changes
+        // depending on legend selection.
+
         var newData = targetData.map(dims, function (v0, v1, dataIndex) {
-          var sum = targetData.get(targetStackInfo.stackedDimension, dataIndex);
+          var sum = targetData.get(targetStackInfo.stackedDimension, dataIndex); // Consider `connectNulls` of line area, if value is NaN, stackedOver
+          // should also be NaN, to draw a appropriate belt area.
 
           if (isNaN(sum)) {
             return resultNaN;
@@ -17522,25 +19348,28 @@
             stackedDataRawIndex = targetData.getRawIndex(dataIndex);
           } else {
             byValue = targetData.get(targetStackInfo.stackedByDimension, dataIndex);
-          }
+          } // If stackOver is NaN, chart view will render point on value start.
+
 
           var stackedOver = NaN;
 
           for (var j = idxInStack - 1; j >= 0; j--) {
-            var stackInfo = stackInfoList[j];
+            var stackInfo = stackInfoList[j]; // Has been optimized by inverted indices on `stackedByDimension`.
 
             if (!isStackedByIndex) {
               stackedDataRawIndex = stackInfo.data.rawIndexOf(stackInfo.stackedByDimension, byValue);
             }
 
             if (stackedDataRawIndex >= 0) {
-              var val = stackInfo.data.getByRawIndex(stackInfo.stackResultDimension, stackedDataRawIndex);
+              var val = stackInfo.data.getByRawIndex(stackInfo.stackResultDimension, stackedDataRawIndex); // Considering positive stack, negative stack and empty data
 
-              if (sum >= 0 && val > 0 || sum <= 0 && val < 0) {
-                sum += val;
-                stackedOver = val;
-                break;
-              }
+              if (sum >= 0 && val > 0 || // Positive stack
+              sum <= 0 && val < 0 // Negative stack
+              ) {
+                  sum += val;
+                  stackedOver = val;
+                  break;
+                }
             }
           }
 
@@ -17548,15 +19377,20 @@
           resultVal[1] = stackedOver;
           return resultVal;
         });
-        targetData.hostModel.setData(newData);
+        targetData.hostModel.setData(newData); // Update for consequent calculation
+
         targetStackInfo.data = newData;
       });
     }
 
-    var SourceImpl = function () {
+    var SourceImpl =
+    /** @class */
+    function () {
+      // readonly frozen: boolean;
       function SourceImpl(fields) {
         this.data = fields.data || (fields.sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS ? {} : []);
-        this.sourceFormat = fields.sourceFormat || SOURCE_FORMAT_UNKNOWN;
+        this.sourceFormat = fields.sourceFormat || SOURCE_FORMAT_UNKNOWN; // Visit config
+
         this.seriesLayoutBy = fields.seriesLayoutBy || SERIES_LAYOUT_BY_COLUMN;
         this.startIndex = fields.startIndex || 0;
         this.dimensionsDefine = fields.dimensionsDefine;
@@ -17571,7 +19405,9 @@
     function isSourceInstance(val) {
       return val instanceof SourceImpl;
     }
-    function createSource(sourceData, thisMetaRawOption, sourceFormat, encodeDefine) {
+    function createSource(sourceData, thisMetaRawOption, // can be null. If not provided, auto detect it from `sourceData`.
+    sourceFormat, encodeDefine // can be null
+    ) {
       sourceFormat = sourceFormat || detectSourceFormat(sourceData);
       var seriesLayoutBy = thisMetaRawOption.seriesLayoutBy;
       var determined = determineSourceDimensions(sourceData, sourceFormat, seriesLayoutBy, thisMetaRawOption.sourceHeader, thisMetaRawOption.dimensions);
@@ -17587,12 +19423,20 @@
       });
       return source;
     }
+    /**
+     * Wrap original series data for some compatibility cases.
+     */
+
     function createSourceFromSeriesDataOption(data) {
       return new SourceImpl({
         data: data,
         sourceFormat: isTypedArray(data) ? SOURCE_FORMAT_TYPED_ARRAY : SOURCE_FORMAT_ORIGINAL
       });
     }
+    /**
+     * Clone source but excludes source data.
+     */
+
     function cloneSourceShallow(source) {
       return new SourceImpl({
         data: source.data,
@@ -17606,8 +19450,13 @@
     }
 
     function makeEncodeDefine(encodeDefine) {
+      // null means user not specify `series.encode`.
       return encodeDefine ? createHashMap(encodeDefine) : null;
     }
+    /**
+     * Note: An empty array will be detected as `SOURCE_FORMAT_ARRAY_ROWS`.
+     */
+
 
     function detectSourceFormat(data) {
       var sourceFormat = SOURCE_FORMAT_UNKNOWN;
@@ -17615,6 +19464,7 @@
       if (isTypedArray(data)) {
         sourceFormat = SOURCE_FORMAT_TYPED_ARRAY;
       } else if (isArray(data)) {
+        // FIXME Whether tolerate null in top level array?
         if (data.length === 0) {
           sourceFormat = SOURCE_FORMAT_ARRAY_ROWS;
         }
@@ -17643,10 +19493,22 @@
 
       return sourceFormat;
     }
+    /**
+     * Determine the source definitions from data standalone dimensions definitions
+     * are not specified.
+     */
 
-    function determineSourceDimensions(data, sourceFormat, seriesLayoutBy, sourceHeader, dimensionsDefine) {
+    function determineSourceDimensions(data, sourceFormat, seriesLayoutBy, sourceHeader, // standalone raw dimensions definition, like:
+    // {
+    //     dimensions: ['aa', 'bb', { name: 'cc', type: 'time' }]
+    // }
+    // in `dataset` or `series`
+    dimensionsDefine) {
       var dimensionsDetectedCount;
-      var startIndex;
+      var startIndex; // PEDING: could data be null/undefined here?
+      // currently, if `dataset.source` not specified, error thrown.
+      // if `series.data` not specified, nothing rendered without error thrown.
+      // Should test these cases.
 
       if (!data) {
         return {
@@ -17657,17 +19519,22 @@
       }
 
       if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) {
-        var dataArrayRows = data;
+        var dataArrayRows = data; // Rule: Most of the first line are string: it is header.
+        // Caution: consider a line with 5 string and 1 number,
+        // it still can not be sure it is a head, because the
+        // 5 string may be 5 values of category columns.
 
         if (sourceHeader === 'auto' || sourceHeader == null) {
           arrayRowsTravelFirst(function (val) {
+            // '-' is regarded as null/undefined.
             if (val != null && val !== '-') {
               if (isString(val)) {
                 startIndex == null && (startIndex = 1);
               } else {
                 startIndex = 0;
               }
-            }
+            } // 10 is an experience number, avoid long loop.
+
           }, seriesLayoutBy, dataArrayRows, 10);
         } else {
           startIndex = isNumber(sourceHeader) ? sourceHeader : sourceHeader ? 1 : 0;
@@ -17712,7 +19579,8 @@
       var firstIndex = 0;
       var obj;
 
-      while (firstIndex < data.length && !(obj = data[firstIndex++])) {}
+      while (firstIndex < data.length && !(obj = data[firstIndex++])) {} // jshint ignore: line
+
 
       if (obj) {
         var dimensions_1 = [];
@@ -17721,10 +19589,14 @@
         });
         return dimensions_1;
       }
-    }
+    } // Consider dimensions defined like ['A', 'price', 'B', 'price', 'C', 'price'],
+    // which is reasonable. But dimension name is duplicated.
+    // Returns undefined or an array contains only object without null/undefiend or string.
+
 
     function normalizeDimensionsOption(dimensionsDefine) {
       if (!dimensionsDefine) {
+        // The meaning of null/undefined is different from empty array.
         return;
       }
 
@@ -17732,18 +19604,26 @@
       return map(dimensionsDefine, function (rawItem, index) {
         rawItem = isObject(rawItem) ? rawItem : {
           name: rawItem
-        };
+        }; // Other fields will be discarded.
+
         var item = {
           name: rawItem.name,
           displayName: rawItem.displayName,
           type: rawItem.type
-        };
+        }; // User can set null in dimensions.
+        // We dont auto specify name, othewise a given name may
+        // cause it be refered unexpectedly.
 
         if (item.name == null) {
           return item;
-        }
+        } // Also consider number form like 2012.
 
-        item.name += '';
+
+        item.name += ''; // User may also specify displayName.
+        // displayName will always exists except user not
+        // specified or dim name is not specified or detected.
+        // (A auto generated dim name will not be used as
+        // displayName).
 
         if (item.displayName == null) {
           item.displayName = item.name;
@@ -17801,15 +19681,41 @@
      * AUTO-GENERATED FILE. DO NOT MODIFY.
      */
 
-    var _a, _b, _c;
+    /*
+    * Licensed to the Apache Software Foundation (ASF) under one
+    * or more contributor license agreements.  See the NOTICE file
+    * distributed with this work for additional information
+    * regarding copyright ownership.  The ASF licenses this file
+    * to you under the Apache License, Version 2.0 (the
+    * "License"); you may not use this file except in compliance
+    * with the License.  You may obtain a copy of the License at
+    *
+    *   http://www.apache.org/licenses/LICENSE-2.0
+    *
+    * Unless required by applicable law or agreed to in writing,
+    * software distributed under the License is distributed on an
+    * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    * KIND, either express or implied.  See the License for the
+    * specific language governing permissions and limitations
+    * under the License.
+    */
+    var _a, _b, _c; // TODO
     var providerMethods;
     var mountMethods;
+    /**
+     * If normal array used, mutable chunk size is supported.
+     * If typed array used, chunk size must be fixed.
+     */
 
-    var DefaultDataProvider = function () {
+    var DefaultDataProvider =
+    /** @class */
+    function () {
       function DefaultDataProvider(sourceParam, dimSize) {
-        var source = !isSourceInstance(sourceParam) ? createSourceFromSeriesDataOption(sourceParam) : sourceParam;
+        // let source: Source;
+        var source = !isSourceInstance(sourceParam) ? createSourceFromSeriesDataOption(sourceParam) : sourceParam; // declare source is Source;
+
         this._source = source;
-        var data = this._data = source.data;
+        var data = this._data = source.data; // Typed array. TODO IE10+?
 
         if (source.sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) {
           if ("development" !== 'production') {
@@ -17843,6 +19749,8 @@
       DefaultDataProvider.prototype.clean = function () {};
 
       DefaultDataProvider.protoInitialize = function () {
+        // PENDING: To avoid potential incompat (e.g., prototype
+        // is visited somewhere), still init them on prototype.
         var proto = DefaultDataProvider.prototype;
         proto.pure = false;
         proto.persistent = true;
@@ -17902,6 +19810,7 @@
             var arr = storage[dim];
 
             for (var i = 0; i < count; i++) {
+              // appendData with TypedArray will always do replace in provider.
               var val = data[i * dimSize + dim];
               arr[start + i] = val;
               val < min && (min = val);
@@ -17952,7 +19861,9 @@
 
             this._data = newData;
           },
+          // Clean self if data is already used.
           clean: function () {
+            // PENDING
             this._offset += this.count();
             this._data = null;
           }
@@ -18051,6 +19962,8 @@
     var rawSourceValueGetterMap = (_c = {}, _c[SOURCE_FORMAT_ARRAY_ROWS] = getRawValueSimply, _c[SOURCE_FORMAT_OBJECT_ROWS] = function (dataItem, dimIndex, dimName) {
       return dimIndex != null ? dataItem[dimName] : dataItem;
     }, _c[SOURCE_FORMAT_KEYED_COLUMNS] = getRawValueSimply, _c[SOURCE_FORMAT_ORIGINAL] = function (dataItem, dimIndex, dimName) {
+      // FIXME: In some case (markpoint in geo (geo-map.html)),
+      // dataItem is {coord: [...]}
       var value = getDataItemValue(dataItem);
       return dimIndex == null || !(value instanceof Array) ? value : value[dimIndex];
     }, _c[SOURCE_FORMAT_TYPED_ARRAY] = getRawValueSimply, _c);
@@ -18066,12 +19979,22 @@
 
     function getMethodMapKey(sourceFormat, seriesLayoutBy) {
       return sourceFormat === SOURCE_FORMAT_ARRAY_ROWS ? sourceFormat + '_' + seriesLayoutBy : sourceFormat;
-    }
-
-    function retrieveRawValue(data, dataIndex, dim) {
+    } // ??? FIXME can these logic be more neat: getRawValue, getRawDataItem,
+    // Consider persistent.
+    // Caution: why use raw value to display on label or tooltip?
+    // A reason is to avoid format. For example time value we do not know
+    // how to format is expected. More over, if stack is used, calculated
+    // value may be 0.91000000001, which have brings trouble to display.
+    // TODO: consider how to treat null/undefined/NaN when display?
+
+
+    function retrieveRawValue(data, dataIndex, dim // If dimIndex is null/undefined, return OptionDataItem.
+    // Otherwise, return OptionDataValue.
+    ) {
       if (!data) {
         return;
-      }
+      } // Consider data may be not persistent.
+
 
       var dataItem = data.getRawDataItem(dataIndex);
 
@@ -18094,8 +20017,14 @@
 
     var DIMENSION_LABEL_REG = /\{@(.+?)\}/g;
 
-    var DataFormatMixin = function () {
+    var DataFormatMixin =
+    /** @class */
+    function () {
       function DataFormatMixin() {}
+      /**
+       * Get params for formatter
+       */
+
 
       DataFormatMixin.prototype.getDataParams = function (dataIndex, dataType) {
         var data = this.getData(dataType);
@@ -18126,9 +20055,21 @@
           borderColor: borderColor,
           dimensionNames: userOutput ? userOutput.dimensionNames : null,
           encode: userOutput ? userOutput.encode : null,
+          // Param name list for mapping `a`, `b`, `c`, `d`, `e`
           $vars: ['seriesName', 'name', 'value']
         };
       };
+      /**
+       * Format label
+       * @param dataIndex
+       * @param status 'normal' by default
+       * @param dataType
+       * @param labelDimIndex Only used in some chart that
+       *        use formatter in different dimensions, like radar.
+       * @param formatter Formatter given outside.
+       * @return return null/undefined if no formatter
+       */
+
 
       DataFormatMixin.prototype.getFormattedLabel = function (dataIndex, status, dataType, labelDimIndex, formatter, extendParams) {
         status = status || 'normal';
@@ -18144,7 +20085,8 @@
         }
 
         if (!formatter) {
-          var itemModel = data.getItemModel(dataIndex);
+          var itemModel = data.getItemModel(dataIndex); // @ts-ignore
+
           formatter = itemModel.get(status === 'normal' ? ['label', 'formatter'] : [status, 'label', 'formatter']);
         }
 
@@ -18153,10 +20095,13 @@
           params.dimensionIndex = labelDimIndex;
           return formatter(params);
         } else if (typeof formatter === 'string') {
-          var str = formatTpl(formatter, params);
+          var str = formatTpl(formatter, params); // Support 'aaa{@[3]}bbb{@product}ccc'.
+          // Do not support '}' in dim name util have to.
+
           return str.replace(DIMENSION_LABEL_REG, function (origin, dimStr) {
             var len = dimStr.length;
-            var dimLoose = dimStr.charAt(0) === '[' && dimStr.charAt(len - 1) === ']' ? +dimStr.slice(1, len - 1) : dimStr;
+            var dimLoose = dimStr.charAt(0) === '[' && dimStr.charAt(len - 1) === ']' ? +dimStr.slice(1, len - 1) // Also support: '[]' => 0
+            : dimStr;
             var val = retrieveRawValue(data, dataIndex, dimLoose);
 
             if (extendParams && isArray(extendParams.interpolatedValue)) {
@@ -18171,19 +20116,47 @@
           });
         }
       };
+      /**
+       * Get raw value in option
+       */
+
 
       DataFormatMixin.prototype.getRawValue = function (idx, dataType) {
         return retrieveRawValue(this.getData(dataType), idx);
       };
+      /**
+       * Should be implemented.
+       * @param {number} dataIndex
+       * @param {boolean} [multipleSeries=false]
+       * @param {string} [dataType]
+       */
+
 
       DataFormatMixin.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) {
+        // Empty function
         return;
       };
 
       return DataFormatMixin;
     }();
-    function normalizeTooltipFormatResult(result) {
-      var markupText;
+    // but guess little chance has been used outside. Do we need to backward
+    // compat it?
+    // type TooltipFormatResultLegacyObject = {
+    //     // `html` means the markup language text, either in 'html' or 'richText'.
+    //     // The name `html` is not appropriate becuase in 'richText' it is not a HTML
+    //     // string. But still support it for backward compat.
+    //     html: string;
+    //     markers: Dictionary<ColorString>;
+    // };
+
+    /**
+     * For backward compat, normalize the return from `formatTooltip`.
+     */
+
+    function normalizeTooltipFormatResult(result // markersExisting: Dictionary<ColorString>
+    ) {
+      var markupText; // let markers: Dictionary<ColorString>;
+
       var markupFragment;
 
       if (isObject(result)) {
@@ -18193,22 +20166,37 @@
           if ("development" !== 'production') {
             console.warn('The return type of `formatTooltip` is not supported: ' + makePrintable(result));
           }
-        }
+        } // else {
+        //     markupText = (result as TooltipFormatResultLegacyObject).html;
+        //     markers = (result as TooltipFormatResultLegacyObject).markers;
+        //     if (markersExisting) {
+        //         markers = zrUtil.merge(markersExisting, markers);
+        //     }
+        // }
+
       } else {
         markupText = result;
       }
 
       return {
         markupText: markupText,
+        // markers: markers || markersExisting,
         markupFragment: markupFragment
       };
     }
 
+    /**
+     * @param {Object} define
+     * @return See the return of `createTask`.
+     */
+
     function createTask(define) {
       return new Task(define);
     }
 
-    var Task = function () {
+    var Task =
+    /** @class */
+    function () {
       function Task(define) {
         define = define || {};
         this._reset = define.reset;
@@ -18217,10 +20205,20 @@
         this._onDirty = define.onDirty;
         this._dirty = true;
       }
+      /**
+       * @param step Specified step.
+       * @param skip Skip customer perform call.
+       * @param modBy Sampling window size.
+       * @param modDataCount Sampling count.
+       * @return whether unfinished.
+       */
+
 
       Task.prototype.perform = function (performArgs) {
         var upTask = this._upstream;
-        var skip = performArgs && performArgs.skip;
+        var skip = performArgs && performArgs.skip; // TODO some refactor.
+        // Pull data. Must pull data each time, because context.data
+        // may be updated by Series.setData.
 
         if (this._dirty && upTask) {
           var context = this.context;
@@ -18235,7 +20233,9 @@
 
         if (this._plan && !skip) {
           planResult = this._plan(this.context);
-        }
+        } // Support sharding by mod, which changes the render sequence and makes the rendered graphic
+        // elements uniformed distributed when progress, especially when moving or zooming.
+
 
         var lastModBy = normalizeModBy(this._modBy);
         var lastModDataCount = this._modDataCount || 0;
@@ -18247,7 +20247,8 @@
         }
 
         function normalizeModBy(val) {
-          !(val >= 1) && (val = 1);
+          !(val >= 1) && (val = 1); // jshint ignore:line
+
           return val;
         }
 
@@ -18268,13 +20269,16 @@
           }
 
           this._dueEnd = upTask._outputDueEnd;
-        } else {
-          if ("development" !== 'production') {
-            assert(!this._progress || this._count);
-          }
+        } // DataTask or overallTask
+        else {
+            if ("development" !== 'production') {
+              assert(!this._progress || this._count);
+            }
+
+            this._dueEnd = this._count ? this._count(this.context) : Infinity;
+          } // Note: Stubs, that its host overall task let it has progress, has progress.
+        // If no progress, pass index from upstream to downstream each time plan called.
 
-          this._dueEnd = this._count ? this._count(this.context) : Infinity;
-        }
 
         if (this._progress) {
           var start = this._dueIndex;
@@ -18292,15 +20296,21 @@
             }
           }
 
-          this._dueIndex = end;
+          this._dueIndex = end; // If no `outputDueEnd`, assume that output data and
+          // input data is the same, so use `dueIndex` as `outputDueEnd`.
+
           var outputDueEnd = this._settedOutputEnd != null ? this._settedOutputEnd : end;
 
           if ("development" !== 'production') {
+            // ??? Can not rollback.
             assert(outputDueEnd >= this._outputDueEnd);
           }
 
           this._outputDueEnd = outputDueEnd;
         } else {
+          // (1) Some overall task has no progress.
+          // (2) Stubs, that its host overall task do not let it has progress, has no progress.
+          // This should always be performed so it can be passed to downstream.
           this._dueIndex = this._outputDueEnd = this._settedOutputEnd != null ? this._settedOutputEnd : this._dueEnd;
         }
 
@@ -18336,7 +20346,8 @@
           if (progress && progress.progress) {
             forceFirstProgress = progress.forceFirstProgress;
             progress = progress.progress;
-          }
+          } // To simplify no progress checking, array must has item.
+
 
           if (isArray(progress) && !progress.length) {
             progress = null;
@@ -18353,11 +20364,17 @@
       Task.prototype.unfinished = function () {
         return this._progress && this._dueIndex < this._dueEnd;
       };
+      /**
+       * @param downTask The downstream task.
+       * @return The downstream task.
+       */
+
 
       Task.prototype.pipe = function (downTask) {
         if ("development" !== 'production') {
           assert(downTask && !downTask._disposed && downTask !== this);
-        }
+        } // If already downstream, do not dirty downTask.
+
 
         if (this._downstream !== downTask || this._dirty) {
           this._downstream = downTask;
@@ -18386,6 +20403,11 @@
       };
 
       Task.prototype.setOutputEnd = function (end) {
+        // This only happend in dataTask, dataZoom, map, currently.
+        // where dataZoom do not set end each time, but only set
+        // when reset. So we should record the setted end, in case
+        // that the stub of dataZoom perform again and earse the
+        // setted end by upstream.
         this._outputDueEnd = this._settedOutputEnd = end;
       };
 
@@ -18416,31 +20438,140 @@
 
       function modNext() {
         var dataIndex = current % winCount * modBy + Math.ceil(current / winCount);
-        var result = current >= end ? null : dataIndex < modDataCount ? dataIndex : current;
+        var result = current >= end ? null : dataIndex < modDataCount ? dataIndex // If modDataCount is smaller than data.count() (consider `appendData` case),
+        // Use normal linear rendering mode.
+        : current;
         current++;
         return result;
       }
-    }();
+    }(); ///////////////////////////////////////////////////////////
+    // For stream debug (Should be commented out after used!)
+    // @usage: printTask(this, 'begin');
+    // @usage: printTask(this, null, {someExtraProp});
+    // @usage: Use `__idxInPipeline` as conditional breakpiont.
+    //
+    // window.printTask = function (task: any, prefix: string, extra: { [key: string]: unknown }): void {
+    //     window.ecTaskUID == null && (window.ecTaskUID = 0);
+    //     task.uidDebug == null && (task.uidDebug = `task_${window.ecTaskUID++}`);
+    //     task.agent && task.agent.uidDebug == null && (task.agent.uidDebug = `task_${window.ecTaskUID++}`);
+    //     let props = [];
+    //     if (task.__pipeline) {
+    //         let val = `${task.__idxInPipeline}/${task.__pipeline.tail.__idxInPipeline} ${task.agent ? '(stub)' : ''}`;
+    //         props.push({text: '__idxInPipeline/total', value: val});
+    //     } else {
+    //         let stubCount = 0;
+    //         task.agentStubMap.each(() => stubCount++);
+    //         props.push({text: 'idx', value: `overall (stubs: ${stubCount})`});
+    //     }
+    //     props.push({text: 'uid', value: task.uidDebug});
+    //     if (task.__pipeline) {
+    //         props.push({text: 'pipelineId', value: task.__pipeline.id});
+    //         task.agent && props.push(
+    //             {text: 'stubFor', value: task.agent.uidDebug}
+    //         );
+    //     }
+    //     props.push(
+    //         {text: 'dirty', value: task._dirty},
+    //         {text: 'dueIndex', value: task._dueIndex},
+    //         {text: 'dueEnd', value: task._dueEnd},
+    //         {text: 'outputDueEnd', value: task._outputDueEnd}
+    //     );
+    //     if (extra) {
+    //         Object.keys(extra).forEach(key => {
+    //             props.push({text: key, value: extra[key]});
+    //         });
+    //     }
+    //     let args = ['color: blue'];
+    //     let msg = `%c[${prefix || 'T'}] %c` + props.map(item => (
+    //         args.push('color: green', 'color: red'),
+    //         `${item.text}: %c${item.value}`
+    //     )).join('%c, ');
+    //     console.log.apply(console, [msg].concat(args));
+    //     // console.log(this);
+    // };
+    // window.printPipeline = function (task: any, prefix: string) {
+    //     const pipeline = task.__pipeline;
+    //     let currTask = pipeline.head;
+    //     while (currTask) {
+    //         window.printTask(currTask, prefix);
+    //         currTask = currTask._downstream;
+    //     }
+    // };
+    // window.showChain = function (chainHeadTask) {
+    //     var chain = [];
+    //     var task = chainHeadTask;
+    //     while (task) {
+    //         chain.push({
+    //             task: task,
+    //             up: task._upstream,
+    //             down: task._downstream,
+    //             idxInPipeline: task.__idxInPipeline
+    //         });
+    //         task = task._downstream;
+    //     }
+    //     return chain;
+    // };
+    // window.findTaskInChain = function (task, chainHeadTask) {
+    //     let chain = window.showChain(chainHeadTask);
+    //     let result = [];
+    //     for (let i = 0; i < chain.length; i++) {
+    //         let chainItem = chain[i];
+    //         if (chainItem.task === task) {
+    //             result.push(i);
+    //         }
+    //     }
+    //     return result;
+    // };
+    // window.printChainAEachInChainB = function (chainHeadTaskA, chainHeadTaskB) {
+    //     let chainA = window.showChain(chainHeadTaskA);
+    //     for (let i = 0; i < chainA.length; i++) {
+    //         console.log('chainAIdx:', i, 'inChainB:', window.findTaskInChain(chainA[i].task, chainHeadTaskB));
+    //     }
+    // };
+
+    /**
+     * Convert raw the value in to inner value in List.
+     *
+     * [Performance sensitive]
+     *
+     * [Caution]: this is the key logic of user value parser.
+     * For backward compatibiliy, do not modify it until have to!
+     */
 
-    function parseDataValue(value, opt) {
+    function parseDataValue(value, // For high performance, do not omit the second param.
+    opt) {
+      // Performance sensitive.
       var dimType = opt && opt.type;
 
       if (dimType === 'ordinal') {
+        // If given value is a category string
         var ordinalMeta = opt && opt.ordinalMeta;
         return ordinalMeta ? ordinalMeta.parseAndCollect(value) : value;
       }
 
-      if (dimType === 'time' && typeof value !== 'number' && value != null && value !== '-') {
+      if (dimType === 'time' // spead up when using timestamp
+      && typeof value !== 'number' && value != null && value !== '-') {
         value = +parseDate(value);
-      }
+      } // dimType defaults 'number'.
+      // If dimType is not ordinal and value is null or undefined or NaN or '-',
+      // parse to NaN.
+      // number-like string (like ' 123 ') can be converted to a number.
+      // where null/undefined or other string will be converted to NaN.
 
-      return value == null || value === '' ? NaN : +value;
+
+      return value == null || value === '' ? NaN // If string (like '-'), using '+' parse to NaN
+      // If object, also parse to NaN
+      : +value;
     }
     var valueParserMap = createHashMap({
       'number': function (val) {
+        // Do not use `numericToNumber` here. We have by defualt `numericToNumber`.
+        // Here the number parser can have loose rule:
+        // enable to cut suffix: "120px" => 120, "14%" => 14.
         return parseFloat(val);
       },
       'time': function (val) {
+        // return timestamp.
         return +parseDate(val);
       },
       'trim': function (val) {
@@ -18448,7 +20579,15 @@
       }
     });
 
-    var SortOrderComparator = function () {
+    var SortOrderComparator =
+    /** @class */
+    function () {
+      /**
+       * @param order by defualt: 'asc'
+       * @param incomparable by defualt: Always on the tail.
+       *        That is, if 'asc' => 'max', if 'desc' => 'min'
+       *        See the definition of "incomparable" in [SORT_COMPARISON_RULE]
+       */
       function SortOrderComparator(order, incomparable) {
         var isDesc = order === 'desc';
         this._resultLT = isDesc ? 1 : -1;
@@ -18458,9 +20597,12 @@
         }
 
         this._incomparable = incomparable === 'min' ? -Infinity : Infinity;
-      }
+      } // See [SORT_COMPARISON_RULE].
+      // Performance sensitive.
+
 
       SortOrderComparator.prototype.evaluate = function (lval, rval) {
+        // Most cases is 'number', and typeof maybe 10 times faseter than parseFloat.
         var lvalTypeof = typeof lval;
         var rvalTypeof = typeof rval;
         var lvalFloat = lvalTypeof === 'number' ? lval : numericToNumber(lval);
@@ -18495,24 +20637,47 @@
       return SortOrderComparator;
     }();
 
-    var ExternalSource = function () {
+    /**
+     * TODO: disable writable.
+     * This structure will be exposed to users.
+     */
+
+    var ExternalSource =
+    /** @class */
+    function () {
       function ExternalSource() {}
 
       ExternalSource.prototype.getRawData = function () {
+        // Only built-in transform available.
         throw new Error('not supported');
       };
 
       ExternalSource.prototype.getRawDataItem = function (dataIndex) {
+        // Only built-in transform available.
         throw new Error('not supported');
       };
 
       ExternalSource.prototype.cloneRawData = function () {
         return;
       };
+      /**
+       * @return If dimension not found, return null/undefined.
+       */
+
 
       ExternalSource.prototype.getDimensionInfo = function (dim) {
         return;
       };
+      /**
+       * dimensions defined if and only if either:
+       * (a) dataset.dimensions are declared.
+       * (b) dataset data include dimensions definitions in data (detected or via specified `sourceHeader`).
+       * If dimensions are defined, `dimensionInfoAll` is corresponding to
+       * the defined dimensions.
+       * Otherwise, `dimensionInfoAll` is determined by data columns.
+       * @return Always return an array (even empty array).
+       */
+
 
       ExternalSource.prototype.cloneAllDimensionInfo = function () {
         return;
@@ -18521,6 +20686,12 @@
       ExternalSource.prototype.count = function () {
         return;
       };
+      /**
+       * Only support by dimension index.
+       * No need to support by dimension name in transform function,
+       * becuase transform function is not case-specific, no need to use name literally.
+       */
+
 
       ExternalSource.prototype.retrieveValue = function (dataIndex, dimIndex) {
         return;
@@ -18545,12 +20716,21 @@
       var errMsg = '';
 
       if (internalSource.seriesLayoutBy !== SERIES_LAYOUT_BY_COLUMN) {
+        // For the logic simplicity in transformer, only 'culumn' is
+        // supported in data transform. Otherwise, the `dimensionsDefine`
+        // might be detected by 'row', which probably confuses users.
         if ("development" !== 'production') {
           errMsg = '`seriesLayoutBy` of upstream dataset can only be "column" in data transform.';
         }
 
         throwError(errMsg);
-      }
+      } // [MEMO]
+      // Create a new dimensions structure for exposing.
+      // Do not expose all dimension info to users directly.
+      // Becuase the dimension is probably auto detected from data and not might reliable.
+      // Should not lead the transformers to think that is relialbe and return it.
+      // See [DIMENSION_INHERIT_RULE] in `sourceManager.ts`.
+
 
       var dimensions = [];
       var dimsByName = {};
@@ -18564,9 +20744,13 @@
             name: name,
             displayName: dimDef.displayName
           };
-          dimensions.push(dimDefExt);
+          dimensions.push(dimDefExt); // Users probably not sepcify dimension name. For simplicity, data transform
+          // do not generate dimension name.
 
           if (name != null) {
+            // Dimension name should not be duplicated.
+            // For simplicity, data transform forbid name duplication, do not generate
+            // new name like module `completeDimensions.ts` did, but just tell users.
             var errMsg_1 = '';
 
             if (hasOwn(dimsByName, name)) {
@@ -18580,13 +20764,18 @@
             dimsByName[name] = dimDefExt;
           }
         });
-      } else {
-        for (var i = 0; i < internalSource.dimensionsDetectedCount || 0; i++) {
-          dimensions.push({
-            index: i
-          });
-        }
-      }
+      } // If dimension definitions are not defined and can not be detected.
+      // e.g., pure data `[[11, 22], ...]`.
+      else {
+          for (var i = 0; i < internalSource.dimensionsDetectedCount || 0; i++) {
+            // Do not generete name or anything others. The consequence process in
+            // `transform` or `series` probably have there own name generation strategry.
+            dimensions.push({
+              index: i
+            });
+          }
+        } // Implement public methods:
+
 
       var rawItemGetter = getRawSourceItemGetter(sourceFormat, SERIES_LAYOUT_BY_COLUMN);
 
@@ -18613,7 +20802,7 @@
           return;
         }
 
-        var dimDef = dimensions[dimIndex];
+        var dimDef = dimensions[dimIndex]; // When `dimIndex` is `null`, `rawValueGetter` return the whole item.
 
         if (dimDef) {
           return rawValueGetter(dataItem, dimIndex, dimDef.name);
@@ -18659,6 +20848,7 @@
         var result = [];
 
         for (var i = 0, len = data.length; i < len; i++) {
+          // Not strictly clone for performance
           result.push(data[i].slice());
         }
 
@@ -18667,6 +20857,7 @@
         var result = [];
 
         for (var i = 0, len = data.length; i < len; i++) {
+          // Not strictly clone for performance
           result.push(extend({}, data[i]));
         }
 
@@ -18677,9 +20868,11 @@
     function getDimensionInfo(dimensions, dimsByName, dim) {
       if (dim == null) {
         return;
-      }
+      } // Keep the same logic as `List::getDimension` did.
+
 
-      if (typeof dim === 'number' || !isNaN(dim) && !hasOwn(dimsByName, dim)) {
+      if (typeof dim === 'number' // If being a number-like string but not being defined a dimension name.
+      || !isNaN(dim) && !hasOwn(dimsByName, dim)) {
         return dimensions[dim];
       } else if (hasOwn(dimsByName, dim)) {
         return dimsByName[dim];
@@ -18712,7 +20905,9 @@
         }
 
         throwError(errMsg);
-      }
+      } // Namespace 'echarts:xxx' is official namespace, where the transforms should
+      // be called directly via 'xxx' rather than 'echarts:xxx'.
+
 
       var isBuiltIn = false;
 
@@ -18739,7 +20934,8 @@
 
       for (var i = 0, len = pipeLen; i < len; i++) {
         var transOption = pipedTransOption[i];
-        sourceList = applySingleDataTransform(transOption, sourceList, infoForPrint, pipeLen === 1 ? null : i);
+        sourceList = applySingleDataTransform(transOption, sourceList, infoForPrint, pipeLen === 1 ? null : i); // piped transform only support single input, except the fist one.
+        // piped transform only support single output, except the last one.
 
         if (i !== len - 1) {
           sourceList.length = Math.max(sourceList.length, 1);
@@ -18749,7 +20945,8 @@
       return sourceList;
     }
 
-    function applySingleDataTransform(transOption, upSourceList, infoForPrint, pipeIndex) {
+    function applySingleDataTransform(transOption, upSourceList, infoForPrint, // If `pipeIndex` is null/undefined, no piped transform.
+    pipeIndex) {
       var errMsg = '';
 
       if (!upSourceList.length) {
@@ -18777,7 +20974,8 @@
         }
 
         throwError(errMsg);
-      }
+      } // Prepare source
+
 
       var extUpSourceList = map(upSourceList, function (upSource) {
         return createExternalSource(upSource, externalTransform);
@@ -18829,9 +21027,38 @@
 
         var resultMetaRawOption;
         var firstUpSource = upSourceList[0];
-
-        if (firstUpSource && resultIndex === 0 && !result.dimensions) {
-          var startIndex = firstUpSource.startIndex;
+        /**
+         * Intuitively, the end users known the content of the original `dataset.source`,
+         * calucating the transform result in mind.
+         * Suppose the original `dataset.source` is:
+         * ```js
+         * [
+         *     ['product', '2012', '2013', '2014', '2015'],
+         *     ['AAA', 41.1, 30.4, 65.1, 53.3],
+         *     ['BBB', 86.5, 92.1, 85.7, 83.1],
+         *     ['CCC', 24.1, 67.2, 79.5, 86.4]
+         * ]
+         * ```
+         * The dimension info have to be detected from the source data.
+         * Some of the transformers (like filter, sort) will follow the dimension info
+         * of upstream, while others use new dimensions (like aggregate).
+         * Transformer can output a field `dimensions` to define the its own output dimensions.
+         * We also allow transformers to ignore the output `dimensions` field, and
+         * inherit the upstream dimensions definition. It can reduce the burden of handling
+         * dimensions in transformers.
+         *
+         * See also [DIMENSION_INHERIT_RULE] in `sourceManager.ts`.
+         */
+
+        if (firstUpSource && resultIndex === 0 // If transformer returns `dimensions`, it means that the transformer has different
+        // dimensions definitions. We do not inherit anything from upstream.
+        && !result.dimensions) {
+          var startIndex = firstUpSource.startIndex; // We copy the header of upstream to the result becuase:
+          // (1) The returned data always does not contain header line and can not be used
+          // as dimension-detection. In this case we can not use "detected dimensions" of
+          // upstream directly, because it might be detected based on different `seriesLayoutBy`.
+          // (2) We should support that the series read the upstream source in `seriesLayoutBy: 'row'`.
+          // So the original detected header should be add to the result, otherwise they can not be read.
 
           if (startIndex) {
             result.data = firstUpSource.data.slice(0, startIndex).concat(result.data);
@@ -18858,13 +21085,112 @@
       return sourceFormat === SOURCE_FORMAT_ARRAY_ROWS || sourceFormat === SOURCE_FORMAT_OBJECT_ROWS;
     }
 
-    var SourceManager = function () {
+    /**
+     * [REQUIREMENT_MEMO]:
+     * (0) `metaRawOption` means `dimensions`/`sourceHeader`/`seriesLayoutBy` in raw option.
+     * (1) Keep support the feature: `metaRawOption` can be specified both on `series` and
+     * `root-dataset`. Them on `series` has higher priority.
+     * (2) Do not support to set `metaRawOption` on a `non-root-dataset`, because it might
+     * confuse users: whether those props indicate how to visit the upstream source or visit
+     * the transform result source, and some transforms has nothing to do with these props,
+     * and some transforms might have multiple upstream.
+     * (3) Transforms should specify `metaRawOption` in each output, just like they can be
+     * declared in `root-dataset`.
+     * (4) At present only support visit source in `SERIES_LAYOUT_BY_COLUMN` in transforms.
+     * That is for reducing complexity in transfroms.
+     * PENDING: Whether to provide transposition transform?
+     *
+     * [IMPLEMENTAION_MEMO]:
+     * "sourceVisitConfig" are calculated from `metaRawOption` and `data`.
+     * They will not be calculated until `source` is about to be visited (to prevent from
+     * duplicate calcuation). `source` is visited only in series and input to transforms.
+     *
+     * [DIMENSION_INHERIT_RULE]:
+     * By default the dimensions are inherited from ancestors, unless a transform return
+     * a new dimensions definition.
+     * Consider the case:
+     * ```js
+     * dataset: [{
+     *     source: [ ['Product', 'Sales', 'Prise'], ['Cookies', 321, 44.21], ...]
+     * }, {
+     *     transform: { type: 'filter', ... }
+     * }]
+     * dataset: [{
+     *     dimension: ['Product', 'Sales', 'Prise'],
+     *     source: [ ['Cookies', 321, 44.21], ...]
+     * }, {
+     *     transform: { type: 'filter', ... }
+     * }]
+     * ```
+     * The two types of option should have the same behavior after transform.
+     *
+     *
+     * [SCENARIO]:
+     * (1) Provide source data directly:
+     * ```js
+     * series: {
+     *     encode: {...},
+     *     dimensions: [...]
+     *     seriesLayoutBy: 'row',
+     *     data: [[...]]
+     * }
+     * ```
+     * (2) Series refer to dataset.
+     * ```js
+     * series: [{
+     *     encode: {...}
+     *     // Ignore datasetIndex means `datasetIndex: 0`
+     *     // and the dimensions defination in dataset is used
+     * }, {
+     *     encode: {...},
+     *     seriesLayoutBy: 'column',
+     *     datasetIndex: 1
+     * }]
+     * ```
+     * (3) dataset transform
+     * ```js
+     * dataset: [{
+     *     source: [...]
+     * }, {
+     *     source: [...]
+     * }, {
+     *     // By default from 0.
+     *     transform: { type: 'filter', config: {...} }
+     * }, {
+     *     // Piped.
+     *     transform: [
+     *         { type: 'filter', config: {...} },
+     *         { type: 'sort', config: {...} }
+     *     ]
+     * }, {
+     *     id: 'regressionData',
+     *     fromDatasetIndex: 1,
+     *     // Third-party transform
+     *     transform: { type: 'ecStat:regression', config: {...} }
+     * }, {
+     *     // retrieve the extra result.
+     *     id: 'regressionFormula',
+     *     fromDatasetId: 'regressionData',
+     *     fromTransformResult: 1
+     * }]
+     * ```
+     */
+
+    var SourceManager =
+    /** @class */
+    function () {
       function SourceManager(sourceHost) {
-        this._sourceList = [];
+        // Cached source. Do not repeat calculating if not dirty.
+        this._sourceList = []; // version sign of each upstream source manager.
+
         this._upstreamSignList = [];
         this._versionSignBase = 0;
         this._sourceHost = sourceHost;
       }
+      /**
+       * Mark dirty.
+       */
+
 
       SourceManager.prototype.dirty = function () {
         this._setLocalSource([], []);
@@ -18879,12 +21205,23 @@
           this._versionSignBase = 0;
         }
       };
+      /**
+       * For detecting whether the upstream source is dirty, so that
+       * the local cached source (in `_sourceList`) should be discarded.
+       */
+
 
       SourceManager.prototype._getVersionSign = function () {
         return this._sourceHost.uid + '_' + this._versionSignBase;
       };
+      /**
+       * Always return a source instance. Otherwise throw error.
+       */
+
 
       SourceManager.prototype.prepareSource = function () {
+        // For the case that call `setOption` multiple time but no data changed,
+        // cache the result source to prevent from repeating transform.
         if (this._isDirty()) {
           this._createSource();
         }
@@ -18905,7 +21242,7 @@
           var seriesModel = sourceHost;
           var data = void 0;
           var sourceFormat = void 0;
-          var upSource = void 0;
+          var upSource = void 0; // Has upstream dataset
 
           if (hasUpstream) {
             var upSourceMgr = upSourceMgrList[0];
@@ -18914,17 +21251,22 @@
             data = upSource.data;
             sourceFormat = upSource.sourceFormat;
             upstreamSignList = [upSourceMgr._getVersionSign()];
-          } else {
-            data = seriesModel.get('data', true);
-            sourceFormat = isTypedArray(data) ? SOURCE_FORMAT_TYPED_ARRAY : SOURCE_FORMAT_ORIGINAL;
-            upstreamSignList = [];
-          }
+          } // Series data is from own.
+          else {
+              data = seriesModel.get('data', true);
+              sourceFormat = isTypedArray(data) ? SOURCE_FORMAT_TYPED_ARRAY : SOURCE_FORMAT_ORIGINAL;
+              upstreamSignList = [];
+            } // See [REQUIREMENT_MEMO], merge settings on series and parent dataset if it is root.
+
 
           var newMetaRawOption = this._getSourceMetaRawOption();
 
           var upMetaRawOption = upSource ? upSource.metaRawOption : null;
           var seriesLayoutBy = retrieve2(newMetaRawOption.seriesLayoutBy, upMetaRawOption ? upMetaRawOption.seriesLayoutBy : null);
-          var sourceHeader = retrieve2(newMetaRawOption.sourceHeader, upMetaRawOption ? upMetaRawOption.sourceHeader : null);
+          var sourceHeader = retrieve2(newMetaRawOption.sourceHeader, upMetaRawOption ? upMetaRawOption.sourceHeader : null); // Note here we should not use `upSource.dimensionsDefine`. Consider the case:
+          // `upSource.dimensionsDefine` is detected by `seriesLayoutBy: 'column'`,
+          // but series need `seriesLayoutBy: 'row'`.
+
           var dimensions = retrieve2(newMetaRawOption.dimensions, upMetaRawOption ? upMetaRawOption.dimensions : null);
           resultSourceList = [createSource(data, {
             seriesLayoutBy: seriesLayoutBy,
@@ -18932,18 +21274,20 @@
             dimensions: dimensions
           }, sourceFormat, seriesModel.get('encode', true))];
         } else {
-          var datasetModel = sourceHost;
+          var datasetModel = sourceHost; // Has upstream dataset.
 
           if (hasUpstream) {
             var result = this._applyTransform(upSourceMgrList);
 
             resultSourceList = result.sourceList;
             upstreamSignList = result.upstreamSignList;
-          } else {
-            var sourceData = datasetModel.get('source', true);
-            resultSourceList = [createSource(sourceData, this._getSourceMetaRawOption(), null, null)];
-            upstreamSignList = [];
-          }
+          } // Is root dataset.
+          else {
+              var sourceData = datasetModel.get('source', true);
+              resultSourceList = [createSource(sourceData, this._getSourceMetaRawOption(), null, // Note: dataset option does not have `encode`.
+              null)];
+              upstreamSignList = [];
+            }
         }
 
         if ("development" !== 'production') {
@@ -19013,24 +21357,40 @@
 
         if (!sourceList.length) {
           return true;
-        }
+        } // All sourceList is from the some upsteam.
+
 
         var upSourceMgrList = this._getUpstreamSourceManagers();
 
         for (var i = 0; i < upSourceMgrList.length; i++) {
           var upSrcMgr = upSourceMgrList[i];
 
-          if (upSrcMgr._isDirty() || this._upstreamSignList[i] !== upSrcMgr._getVersionSign()) {
+          if ( // Consider the case that there is ancestor diry, call it recursively.
+          // The performance is probably not an issue because usually the chain is not long.
+          upSrcMgr._isDirty() || this._upstreamSignList[i] !== upSrcMgr._getVersionSign()) {
             return true;
           }
         }
       };
+      /**
+       * @param sourceIndex By defualt 0, means "main source".
+       *                    Most cases there is only one source.
+       */
+
 
       SourceManager.prototype.getSource = function (sourceIndex) {
         return this._sourceList[sourceIndex || 0];
       };
+      /**
+       * PEDING: Is it fast enough?
+       * If no upstream, return empty array.
+       */
+
 
       SourceManager.prototype._getUpstreamSourceManagers = function () {
+        // Always get the relationship from the raw option.
+        // Do not cache the link of the dependency graph, so that
+        // no need to update them when change happen.
         var sourceHost = this._sourceHost;
 
         if (isSeries(sourceHost)) {
@@ -19053,12 +21413,13 @@
           seriesLayoutBy = sourceHost.get('seriesLayoutBy', true);
           sourceHeader = sourceHost.get('sourceHeader', true);
           dimensions = sourceHost.get('dimensions', true);
-        } else if (!this._getUpstreamSourceManagers().length) {
-          var model = sourceHost;
-          seriesLayoutBy = model.get('seriesLayoutBy', true);
-          sourceHeader = model.get('sourceHeader', true);
-          dimensions = model.get('dimensions', true);
-        }
+        } // See [REQUIREMENT_MEMO], `non-root-dataset` do not support them.
+        else if (!this._getUpstreamSourceManagers().length) {
+            var model = sourceHost;
+            seriesLayoutBy = model.get('seriesLayoutBy', true);
+            sourceHeader = model.get('sourceHeader', true);
+            dimensions = model.get('dimensions', true);
+          }
 
         return {
           seriesLayoutBy: seriesLayoutBy,
@@ -19069,12 +21430,15 @@
 
       return SourceManager;
     }();
+    // disable the transform merge, but do not disable transfrom clone from rawOption.
+
     function disableTransformOptionMerge(datasetModel) {
       var transformOption = datasetModel.option.transform;
       transformOption && setAsPrimitive(datasetModel.option.transform);
     }
 
     function isSeries(sourceHost) {
+      // Avoid circular dependency with Series.ts
       return sourceHost.mainType === 'series';
     }
 
@@ -19082,7 +21446,7 @@
       throw new Error(errMsg);
     }
 
-    var TOOLTIP_LINE_HEIGHT_CSS = 'line-height:1';
+    var TOOLTIP_LINE_HEIGHT_CSS = 'line-height:1'; // TODO: more textStyle option
 
     function getTooltipTextStyle(textStyle, renderMode) {
       var nameFontColor = textStyle.color || '#6e7079';
@@ -19093,9 +21457,12 @@
       var valueFontWeight = textStyle.fontWeight || '900';
 
       if (renderMode === 'html') {
+        // `textStyle` is probably from user input, should be encoded to reduce security risk.
         return {
-          nameStyle: "font-size:" + nameFontSize + "px;color:" + nameFontColor + ";font-weight:" + nameFontWeight,
-          valueStyle: "font-size:" + valueFontSize + "px;color:" + valueFontColor + ";font-weight:" + valueFontWeight
+          // eslint-disable-next-line max-len
+          nameStyle: "font-size:" + encodeHTML(nameFontSize + '') + "px;color:" + encodeHTML(nameFontColor) + ";font-weight:" + encodeHTML(nameFontWeight + ''),
+          // eslint-disable-next-line max-len
+          valueStyle: "font-size:" + encodeHTML(valueFontSize + '') + "px;color:" + encodeHTML(valueFontColor) + ";font-weight:" + encodeHTML(valueFontWeight + '')
         };
       } else {
         return {
@@ -19111,10 +21478,13 @@
           }
         };
       }
-    }
+    } // See `TooltipMarkupLayoutIntent['innerGapLevel']`.
+    // (value from UI design)
+
 
     var HTML_GAPS = [0, 10, 20, 30];
-    var RICH_TEXT_GAPS = ['', '\n', '\n\n', '\n\n\n'];
+    var RICH_TEXT_GAPS = ['', '\n', '\n\n', '\n\n\n']; // eslint-disable-next-line max-len
+
     function createTooltipMarkup(type, option) {
       option.type = type;
       return option;
@@ -19125,6 +21495,15 @@
     }
 
     var builderMap = {
+      /**
+       * A `section` block is like:
+       * ```
+       * header
+       * subBlock
+       * subBlock
+       * ...
+       * ```
+       */
       section: {
         planLayout: function (fragment) {
           var subBlockLen = fragment.blocks.length;
@@ -19132,10 +21511,14 @@
           var thisGapLevelBetweenSubBlocks = 0;
           each(fragment.blocks, function (subBlock) {
             getBuilder(subBlock).planLayout(subBlock);
-            var subGapLevel = subBlock.__gapLevelBetweenSubBlocks;
+            var subGapLevel = subBlock.__gapLevelBetweenSubBlocks; // If the some of the sub-blocks have some gaps (like 10px) inside, this block
+            // should use a larger gap (like 20px) to distinguish those sub-blocks.
 
             if (subGapLevel >= thisGapLevelBetweenSubBlocks) {
-              thisGapLevelBetweenSubBlocks = subGapLevel + (thisBlockHasInnerGap && (!subGapLevel || subBlock.type === 'section' && !subBlock.noHeader) ? 1 : 0);
+              thisGapLevelBetweenSubBlocks = subGapLevel + (thisBlockHasInnerGap && ( // 0 always can not be readable gap level.
+              !subGapLevel // If no header, always keep the sub gap level. Otherwise
+              // look weird in case `multipleSeries`.
+              || subBlock.type === 'section' && !subBlock.noHeader) ? 1 : 0);
             }
           });
           fragment.__gapLevelBetweenSubBlocks = thisGapLevelBetweenSubBlocks;
@@ -19159,6 +21542,13 @@
           }
         }
       },
+
+      /**
+       * A `nameValue` block is like:
+       * ```
+       * marker  name  value
+       * ```
+       */
       nameValue: {
         planLayout: function (fragment) {
           fragment.__gapLevelBetweenSubBlocks = 0;
@@ -19182,14 +21572,16 @@
           var readableValueList = noValue ? [] : isArray(value) ? map(value, function (val, idx) {
             return makeValueReadable(val, isArray(valueTypeOption) ? valueTypeOption[idx] : valueTypeOption, useUTC);
           }) : [makeValueReadable(value, isArray(valueTypeOption) ? valueTypeOption[0] : valueTypeOption, useUTC)];
-          var valueAlignRight = !noMarker || !noName;
+          var valueAlignRight = !noMarker || !noName; // It little weird if only value next to marker but far from marker.
+
           var valueCloseToMarker = !noMarker && noName;
 
           var _a = getTooltipTextStyle(toolTipTextStyle, renderMode),
               nameStyle = _a.nameStyle,
               valueStyle = _a.valueStyle;
 
-          return renderMode === 'richText' ? (noMarker ? '' : markerStr) + (noName ? '' : wrapInlineNameRichText(ctx, readableName, nameStyle)) + (noValue ? '' : wrapInlineValueRichText(ctx, readableValueList, valueAlignRight, valueCloseToMarker, valueStyle)) : wrapBlockHTML((noMarker ? '' : markerStr) + (noName ? '' : wrapInlineNameHTML(readableName, !noMarker, nameStyle)) + (noValue ? '' : wrapInlineValueHTML(readableValueList, valueAlignRight, valueCloseToMarker, valueStyle)), topMarg [...]
+          return renderMode === 'richText' ? (noMarker ? '' : markerStr) + (noName ? '' : wrapInlineNameRichText(ctx, readableName, nameStyle)) // Value has commas inside, so use ' ' as delimiter for multiple values.
+          + (noValue ? '' : wrapInlineValueRichText(ctx, readableValueList, valueAlignRight, valueCloseToMarker, valueStyle)) : wrapBlockHTML((noMarker ? '' : markerStr) + (noName ? '' : wrapInlineNameHTML(readableName, !noMarker, nameStyle)) + (noValue ? '' : wrapInlineValueHTML(readableValueList, valueAlignRight, valueCloseToMarker, valueStyle)), topMarginForOuterGap);
         }
       }
     };
@@ -19213,9 +21605,10 @@
           subBlocks.sort(function (a, b) {
             return comparator_1.evaluate(a.sortParam, b.sortParam);
           });
-        } else if (orderMode === 'seriesDesc') {
-          subBlocks.reverse();
-        }
+        } // FIXME 'seriesDesc' necessary?
+        else if (orderMode === 'seriesDesc') {
+            subBlocks.reverse();
+          }
       }
 
       var gaps = getGap(fragment);
@@ -19230,6 +21623,10 @@
 
       return ctx.renderMode === 'richText' ? subMarkupTextList.join(gaps.richText) : wrapBlockHTML(subMarkupTextList.join(''), topMarginForOuterGap);
     }
+    /**
+     * @return markupText. null/undefined means no content.
+     */
+
 
     function buildTooltipMarkup(fragment, markupStyleCreator, renderMode, orderMode, useUTC, toolTipTextStyle) {
       if (!fragment) {
@@ -19267,9 +21664,11 @@
     }
 
     function wrapInlineValueHTML(valueList, alignRight, valueCloseToMarker, style) {
+      // Do not too close to marker, considering there are multiple values separated by spaces.
       var paddingStr = valueCloseToMarker ? '10px' : '20px';
       var alignCSS = alignRight ? "float:right;margin-left:" + paddingStr : '';
-      return "<span style=\"" + alignCSS + ";" + style + "\">" + map(valueList, function (value) {
+      return "<span style=\"" + alignCSS + ";" + style + "\">" // Value has commas inside, so use '  ' as delimiter for multiple values.
+      + map(valueList, function (value) {
         return encodeHTML(value);
       }).join('&nbsp;&nbsp;') + '</span>';
     }
@@ -19284,7 +21683,8 @@
       alignRight && styles.push({
         padding: [0, 0, 0, paddingLeft],
         align: 'right'
-      });
+      }); // Value has commas inside, so use '  ' as delimiter for multiple values.
+
       return ctx.markupStyleCreator.wrapRichTextStyle(valueList.join('  '), styles);
     }
 
@@ -19295,12 +21695,23 @@
     }
     function getPaddingFromTooltipModel(model, renderMode) {
       var padding = model.get('padding');
-      return padding != null ? padding : renderMode === 'richText' ? [8, 10] : 10;
+      return padding != null ? padding // We give slightly different to look pretty.
+      : renderMode === 'richText' ? [8, 10] : 10;
     }
+    /**
+     * The major feature is generate styles for `renderMode: 'richText'`.
+     * But it also serves `renderMode: 'html'` to provide
+     * "renderMode-independent" API.
+     */
 
-    var TooltipMarkupStyleCreator = function () {
+    var TooltipMarkupStyleCreator =
+    /** @class */
+    function () {
       function TooltipMarkupStyleCreator() {
-        this.richTextStyles = {};
+        this.richTextStyles = {}; // Notice that "generate a style name" usuall happens repeatly when mouse moving and
+        // displaying a tooltip. So we put the `_nextStyleNameId` as a member of each creator
+        // rather than static shared by all creators (which will cause it increase to fast).
+
         this._nextStyleNameId = getRandomIdBase();
       }
 
@@ -19328,6 +21739,22 @@
           return marker.content;
         }
       };
+      /**
+       * @usage
+       * ```ts
+       * const styledText = markupStyleCreator.wrapRichTextStyle([
+       *     // The styles will be auto merged.
+       *     {
+       *         fontSize: 12,
+       *         color: 'blue'
+       *     },
+       *     {
+       *         padding: 20
+       *     }
+       * ]);
+       * ```
+       */
+
 
       TooltipMarkupStyleCreator.prototype.wrapRichTextStyle = function (text, styles) {
         var finalStl = {};
@@ -19358,7 +21785,8 @@
       var tooltipDimLen = tooltipDims.length;
       var value = series.getRawValue(dataIndex);
       var isValueArr = isArray(value);
-      var markerColor = retrieveVisualColorForTooltipMarker(series, dataIndex);
+      var markerColor = retrieveVisualColorForTooltipMarker(series, dataIndex); // Complicated rule for pretty tooltip.
+
       var inlineValue;
       var inlineValueType;
       var subBlocks;
@@ -19368,7 +21796,8 @@
         var formatArrResult = formatTooltipArrayValue(value, series, dataIndex, tooltipDims, markerColor);
         inlineValue = formatArrResult.inlineValues;
         inlineValueType = formatArrResult.inlineValueTypes;
-        subBlocks = formatArrResult.blocks;
+        subBlocks = formatArrResult.blocks; // Only support tooltip sort by the first inline value. It's enough in most cases.
+
         sortParam = formatArrResult.inlineValues[0];
       } else if (tooltipDimLen) {
         var dimInfo = data.getDimensionInfo(tooltipDims[0]);
@@ -19376,7 +21805,8 @@
         inlineValueType = dimInfo.type;
       } else {
         sortParam = inlineValue = isValueArr ? value[0] : value;
-      }
+      } // Do not show generated series name. It might not be readable.
+
 
       var seriesNameSpecified = isNameSpecified(series);
       var seriesName = seriesNameSpecified && series.name || '';
@@ -19384,12 +21814,18 @@
       var inlineName = multipleSeries ? seriesName : itemName;
       return createTooltipMarkup('section', {
         header: seriesName,
+        // When series name not specified, do not show a header line with only '-'.
+        // This case alway happen in tooltip.trigger: 'item'.
         noHeader: multipleSeries || !seriesNameSpecified,
         sortParam: sortParam,
         blocks: [createTooltipMarkup('nameValue', {
           markerType: 'item',
           markerColor: markerColor,
+          // Do not mix display seriesName and itemName in one tooltip,
+          // which might confuses users.
           name: inlineName,
+          // name dimension might be auto assigned, where the name might
+          // be not readable. So we check trim here.
           noName: !trim(inlineName),
           value: inlineValue,
           valueType: inlineValueType
@@ -19398,6 +21834,7 @@
     }
 
     function formatTooltipArrayValue(value, series, dataIndex, tooltipDims, colorStr) {
+      // check: category-no-encode-has-axis-data in dataset.html
       var data = series.getData();
       var isValueMultipleLine = reduce(value, function (isValueMultipleLine, val, idx) {
         var dimItem = data.getDimensionInfo(idx);
@@ -19408,10 +21845,11 @@
       var blocks = [];
       tooltipDims.length ? each(tooltipDims, function (dim) {
         setEachItem(retrieveRawValue(data, dataIndex, dim), dim);
-      }) : each(value, setEachItem);
+      }) // By default, all dims is used on tooltip.
+      : each(value, setEachItem);
 
       function setEachItem(val, dim) {
-        var dimInfo = data.getDimensionInfo(dim);
+        var dimInfo = data.getDimensionInfo(dim); // If `dimInfo.tooltip` is not set, show tooltip.
 
         if (!dimInfo || dimInfo.otherDims.tooltip === false) {
           return;
@@ -19444,11 +21882,25 @@
       return data.getName(dataIndex) || data.getId(dataIndex);
     }
 
-    var SeriesModel = function (_super) {
+    var SeriesModel =
+    /** @class */
+    function (_super) {
       __extends(SeriesModel, _super);
 
       function SeriesModel() {
-        var _this = _super !== null && _super.apply(this, arguments) || this;
+        // [Caution]: Becuase this class or desecendants can be used as `XXX.extend(subProto)`,
+        // the class members must not be initialized in constructor or declaration place.
+        // Otherwise there is bad case:
+        //   class A {xxx = 1;}
+        //   enableClassExtend(A);
+        //   class B extends A {}
+        //   var C = B.extend({xxx: 5});
+        //   var c = new C();
+        //   console.log(c.xxx); // expect 5 but always 1.
+        var _this = _super !== null && _super.apply(this, arguments) || this; // ---------------------------------------
+        // Props about data selection
+        // ---------------------------------------
+
 
         _this._selectedDataIndicesMap = {};
         return _this;
@@ -19474,15 +21926,33 @@
           assert(data, 'getInitialData returned invalid data.');
         }
 
-        inner$1(this).dataBeforeProcessed = data;
+        inner$1(this).dataBeforeProcessed = data; // If we reverse the order (make data firstly, and then make
+        // dataBeforeProcessed by cloneShallow), cloneShallow will
+        // cause data.graph.data !== data when using
+        // module:echarts/data/Graph or module:echarts/data/Tree.
+        // See module:echarts/data/helper/linkList
+        // Theoretically, it is unreasonable to call `seriesModel.getData()` in the model
+        // init or merge stage, because the data can be restored. So we do not `restoreData`
+        // and `setData` here, which forbids calling `seriesModel.getData()` in this stage.
+        // Call `seriesModel.getRawData()` instead.
+        // this.restoreData();
+
         autoSeriesName(this);
 
         this._initSelectedMapFromData(data);
       };
+      /**
+       * Util for merge default and theme to option
+       */
+
 
       SeriesModel.prototype.mergeDefaultAndTheme = function (option, ecModel) {
         var layoutMode = fetchLayoutMode(this);
-        var inputPositionParams = layoutMode ? getLayoutParams(option) : {};
+        var inputPositionParams = layoutMode ? getLayoutParams(option) : {}; // Backward compat: using subType on theme.
+        // But if name duplicate between series subType
+        // (for example: parallel) add component mainType,
+        // add suffix 'Series'.
+
         var themeSubType = this.subType;
 
         if (ComponentModel.hasClass(themeSubType)) {
@@ -19490,7 +21960,8 @@
         }
 
         merge(option, ecModel.getTheme().get(this.subType));
-        merge(option, this.getDefaultOption());
+        merge(option, this.getDefaultOption()); // Default label emphasis `show`
+
         defaultEmphasis(option, 'label', ['show']);
         this.fillDataTextStyle(option.data);
 
@@ -19500,6 +21971,7 @@
       };
 
       SeriesModel.prototype.mergeOption = function (newSeriesOption, ecModel) {
+        // this.settingTask.dirty();
         newSeriesOption = merge(this.option, newSeriesOption, true);
         this.fillDataTextStyle(newSeriesOption.data);
         var layoutMode = fetchLayoutMode(this);
@@ -19522,6 +21994,9 @@
       };
 
       SeriesModel.prototype.fillDataTextStyle = function (data) {
+        // Default data label emphasis `show`
+        // FIXME Tree structure data ?
+        // FIXME Performance ?
         if (data && !isTypedArray(data)) {
           var props = ['show'];
 
@@ -19532,15 +22007,34 @@
           }
         }
       };
+      /**
+       * Init a data structure from data related option in series
+       * Must be overriden.
+       */
+
 
       SeriesModel.prototype.getInitialData = function (option, ecModel) {
         return;
       };
+      /**
+       * Append data to list
+       */
+
 
       SeriesModel.prototype.appendData = function (params) {
+        // FIXME ???
+        // (1) If data from dataset, forbidden append.
+        // (2) support append data of dataset.
         var data = this.getRawData();
         data.appendData(params.data);
       };
+      /**
+       * Consider some method like `filter`, `map` need make new data,
+       * We should make sure that `seriesModel.getData()` get correct
+       * data in the stream procedure. So we fetch data from upstream
+       * each time `task.perform` called.
+       */
+
 
       SeriesModel.prototype.getData = function (dataType) {
         var task = getCurrentTask(this);
@@ -19549,6 +22043,10 @@
           var data = task.context.data;
           return dataType == null ? data : data.getLinkedData(dataType);
         } else {
+          // When series is not alive (that may happen when click toolbox
+          // restore or setOption with not merge mode), series data may
+          // be still need to judge animation or something when graphic
+          // elements want to know whether fade out.
           return inner$1(this).data;
         }
       };
@@ -19564,8 +22062,20 @@
         var task = getCurrentTask(this);
 
         if (task) {
-          var context = task.context;
-          context.outputData = data;
+          var context = task.context; // Consider case: filter, data sample.
+          // FIXME:TS never used, so comment it
+          // if (context.data !== data && task.modifyOutputEnd) {
+          //     task.setOutputEnd(data.count());
+          // }
+
+          context.outputData = data; // Caution: setData should update context.data,
+          // Because getData may be called multiply in a
+          // single stage and expect to get the data just
+          // set. (For example, AxisProxy, x y both call
+          // getData and setDate sequentially).
+          // So the context.data should be fetched from
+          // upstream each time when a stage starts to be
+          // performed.
 
           if (task !== this.dataTask) {
             context.data = data;
@@ -19578,15 +22088,42 @@
       SeriesModel.prototype.getSource = function () {
         return inner$1(this).sourceManager.getSource();
       };
+      /**
+       * Get data before processed
+       */
+
 
       SeriesModel.prototype.getRawData = function () {
         return inner$1(this).dataBeforeProcessed;
       };
+      /**
+       * Get base axis if has coordinate system and has axis.
+       * By default use coordSys.getBaseAxis();
+       * Can be overrided for some chart.
+       * @return {type} description
+       */
+
 
       SeriesModel.prototype.getBaseAxis = function () {
-        var coordSys = this.coordinateSystem;
+        var coordSys = this.coordinateSystem; // @ts-ignore
+
         return coordSys && coordSys.getBaseAxis && coordSys.getBaseAxis();
       };
+      /**
+       * Default tooltip formatter
+       *
+       * @param dataIndex
+       * @param multipleSeries
+       * @param dataType
+       * @param renderMode valid values: 'html'(by default) and 'richText'.
+       *        'html' is used for rendering tooltip in extra DOM form, and the result
+       *        string is used as DOM HTML content.
+       *        'richText' is used for rendering tooltip in rich text form, for those where
+       *        DOM operation is not supported.
+       * @return formatted tooltip with `html` and `markers`
+       *        Notice: The override method can also return string
+       */
+
 
       SeriesModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) {
         return defaultSeriesFormatTooltip({
@@ -19617,7 +22154,8 @@
       };
 
       SeriesModel.prototype.getColorFromPalette = function (name, scope, requestColorNum) {
-        var ecModel = this.ecModel;
+        var ecModel = this.ecModel; // PENDING
+
         var color = PaletteMixin.prototype.getColorFromPalette.call(this, name, scope, requestColorNum);
 
         if (!color) {
@@ -19626,18 +22164,32 @@
 
         return color;
       };
+      /**
+       * Use `data.mapDimensionsAll(coordDim)` instead.
+       * @deprecated
+       */
+
 
       SeriesModel.prototype.coordDimToDataDim = function (coordDim) {
         return this.getRawData().mapDimensionsAll(coordDim);
       };
+      /**
+       * Get progressive rendering count each step
+       */
+
 
       SeriesModel.prototype.getProgressive = function () {
         return this.get('progressive');
       };
+      /**
+       * Get progressive rendering count each step
+       */
+
 
       SeriesModel.prototype.getProgressiveThreshold = function () {
         return this.get('progressiveThreshold');
-      };
+      }; // PENGING If selectedMode is null ?
+
 
       SeriesModel.prototype.select = function (innerDataIndices, dataType) {
         this._innerSelect(this.getData(dataType), innerDataIndices);
@@ -19711,7 +22263,8 @@
           var selectedMap = this.option.selectedMap || (this.option.selectedMap = {});
 
           for (var i = 0; i < len; i++) {
-            var dataIndex = innerDataIndices[i];
+            var dataIndex = innerDataIndices[i]; // TODO diffrent types of data share same object.
+
             var nameOrId = getSelectionKey(data, dataIndex);
             selectedMap[nameOrId] = true;
             this._selectedDataIndicesMap[nameOrId] = data.getRawIndex(dataIndex);
@@ -19725,6 +22278,8 @@
       };
 
       SeriesModel.prototype._initSelectedMapFromData = function (data) {
+        // Ignore select info in data if selectedMap exists.
+        // NOTE It's only for legacy usage. edge data is not supported.
         if (this.option.selectedMap) {
           return;
         }
@@ -19744,7 +22299,11 @@
         if (dataIndices.length > 0) {
           this._innerSelect(data, dataIndices);
         }
-      };
+      }; // /**
+      //  * @see {module:echarts/stream/Scheduler}
+      //  */
+      // abstract pipeTask: null
+
 
       SeriesModel.registerClass = function (clz) {
         return ComponentModel.registerClass(clz);
@@ -19757,7 +22316,8 @@
         proto.useColorPaletteOnData = false;
         proto.ignoreStyleOnData = false;
         proto.hasSymbolVisual = false;
-        proto.defaultSymbol = 'circle';
+        proto.defaultSymbol = 'circle'; // Make sure the values can be accessed!
+
         proto.visualStyleAccessPath = 'itemStyle';
         proto.visualDrawType = 'fill';
       }();
@@ -19768,8 +22328,15 @@
     mixin(SeriesModel, DataFormatMixin);
     mixin(SeriesModel, PaletteMixin);
     mountExtend(SeriesModel, ComponentModel);
+    /**
+     * MUST be called after `prepareSource` called
+     * Here we need to make auto series, especially for auto legend. But we
+     * do not modify series.name in option to avoid side effects.
+     */
 
     function autoSeriesName(seriesModel) {
+      // User specified name has higher priority, otherwise it may cause
+      // series can not be queried unexpectedly.
       var name = seriesModel.name;
 
       if (!isNameSpecified(seriesModel)) {
@@ -19799,10 +22366,12 @@
     }
 
     function dataTaskProgress(param, context) {
+      // Avoid repead cloneShallow when data just created in reset.
       if (context.outputData && param.end > context.outputData.count()) {
         context.model.getRawData().cloneShallow(context.outputData);
       }
-    }
+    } // TODO refactor
+
 
     function wrapData(data, seriesModel) {
       each(__spreadArrays(data.CHANGABLE_METHODS, data.DOWNSAMPLE_METHODS), function (methodName) {
@@ -19814,6 +22383,7 @@
       var task = getCurrentTask(seriesModel);
 
       if (task) {
+        // Consider case: filter, selectRange
         task.setOutputEnd((newList || this).count());
       }
 
@@ -19825,6 +22395,8 @@
       var pipeline = scheduler && scheduler.getPipeline(seriesModel.uid);
 
       if (pipeline) {
+        // When pipline finished, the currrentTask keep the last
+        // task (renderTask).
         var task = pipeline.currentTask;
 
         if (task) {
@@ -19839,7 +22411,9 @@
       }
     }
 
-    var ComponentView = function () {
+    var ComponentView =
+    /** @class */
+    function () {
       function ComponentView() {
         this.group = new Group();
         this.uid = getUID('viewComponent');
@@ -19851,26 +22425,42 @@
 
       ComponentView.prototype.dispose = function (ecModel, api) {};
 
-      ComponentView.prototype.updateView = function (model, ecModel, api, payload) {};
+      ComponentView.prototype.updateView = function (model, ecModel, api, payload) {// Do nothing;
+      };
 
-      ComponentView.prototype.updateLayout = function (model, ecModel, api, payload) {};
+      ComponentView.prototype.updateLayout = function (model, ecModel, api, payload) {// Do nothing;
+      };
 
-      ComponentView.prototype.updateVisual = function (model, ecModel, api, payload) {};
+      ComponentView.prototype.updateVisual = function (model, ecModel, api, payload) {// Do nothing;
+      };
+      /**
+       * Hook for blur target series.
+       * Can be used in marker for blur the markers
+       */
 
-      ComponentView.prototype.blurSeries = function (seriesModels, ecModel) {};
+
+      ComponentView.prototype.blurSeries = function (seriesModels, ecModel) {// Do nothing;
+      };
 
       return ComponentView;
     }();
     enableClassExtend(ComponentView);
     enableClassManagement(ComponentView);
 
+    /**
+     * @return {string} If large mode changed, return string 'reset';
+     */
+
     function createRenderPlanner() {
       var inner = makeInner();
       return function (seriesModel) {
         var fields = inner(seriesModel);
         var pipelineContext = seriesModel.pipelineContext;
         var originalLarge = !!fields.large;
-        var originalProgressive = !!fields.progressiveRender;
+        var originalProgressive = !!fields.progressiveRender; // FIXME: if the planner works on a filtered series, `pipelineContext` does not
+        // exists. See #11611 . Probably we need to modify this structure, see the comment
+        // on `performRawSeries` in `Schedular.js`.
+
         var large = fields.large = !!(pipelineContext && pipelineContext.large);
         var progressive = fields.progressiveRender = !!(pipelineContext && pipelineContext.progressiveRender);
         return !!(originalLarge !== large || originalProgressive !== progressive) && 'reset';
@@ -19880,7 +22470,9 @@
     var inner$2 = makeInner();
     var renderPlanner = createRenderPlanner();
 
-    var ChartView = function () {
+    var ChartView =
+    /** @class */
+    function () {
       function ChartView() {
         this.group = new Group();
         this.uid = getUID('viewChart');
@@ -19896,28 +22488,46 @@
       ChartView.prototype.init = function (ecModel, api) {};
 
       ChartView.prototype.render = function (seriesModel, ecModel, api, payload) {};
+      /**
+       * Highlight series or specified data item.
+       */
+
 
       ChartView.prototype.highlight = function (seriesModel, ecModel, api, payload) {
         toggleHighlight(seriesModel.getData(), payload, 'emphasis');
       };
+      /**
+       * Downplay series or specified data item.
+       */
+
 
       ChartView.prototype.downplay = function (seriesModel, ecModel, api, payload) {
         toggleHighlight(seriesModel.getData(), payload, 'normal');
       };
+      /**
+       * Remove self.
+       */
+
 
       ChartView.prototype.remove = function (ecModel, api) {
         this.group.removeAll();
       };
+      /**
+       * Dispose self.
+       */
+
 
       ChartView.prototype.dispose = function (ecModel, api) {};
 
       ChartView.prototype.updateView = function (seriesModel, ecModel, api, payload) {
         this.render(seriesModel, ecModel, api, payload);
-      };
+      }; // FIXME never used?
+
 
       ChartView.prototype.updateLayout = function (seriesModel, ecModel, api, payload) {
         this.render(seriesModel, ecModel, api, payload);
-      };
+      }; // FIXME never used?
+
 
       ChartView.prototype.updateVisual = function (seriesModel, ecModel, api, payload) {
         this.render(seriesModel, ecModel, api, payload);
@@ -19934,6 +22544,9 @@
 
       return ChartView;
     }();
+    /**
+     * Set state of single element
+     */
 
     function elSetState(el, state, highlightDigit) {
       if (el) {
@@ -19967,11 +22580,14 @@
       var seriesModel = context.model;
       var ecModel = context.ecModel;
       var api = context.api;
-      var payload = context.payload;
+      var payload = context.payload; // FIXME: remove updateView updateVisual
+
       var progressiveRender = seriesModel.pipelineContext.progressiveRender;
       var view = context.view;
       var updateMethod = payload && inner$2(payload).updateMethod;
-      var methodName = progressiveRender ? 'incrementalPrepareRender' : updateMethod && view[updateMethod] ? updateMethod : 'render';
+      var methodName = progressiveRender ? 'incrementalPrepareRender' : updateMethod && view[updateMethod] ? updateMethod // `appendData` is also supported when data amount
+      // is less than progressive threshold.
+      : 'render';
 
       if (methodName !== 'render') {
         view[methodName](seriesModel, ecModel, api, payload);
@@ -19987,6 +22603,10 @@
         }
       },
       render: {
+        // Put view.render in `progress` to support appendData. But in this case
+        // view.render should not be called in reset, otherwise it will be called
+        // twise. Use `forceFirstProgress` to make sure that view.render is called
+        // in any cases.
         forceFirstProgress: true,
         progress: function (params, context) {
           context.view.render(context.model, context.ecModel, context.api, context.payload);
@@ -20018,9 +22638,37 @@
      * AUTO-GENERATED FILE. DO NOT MODIFY.
      */
 
+    /*
+    * Licensed to the Apache Software Foundation (ASF) under one
+    * or more contributor license agreements.  See the NOTICE file
+    * distributed with this work for additional information
+    * regarding copyright ownership.  The ASF licenses this file
+    * to you under the Apache License, Version 2.0 (the
+    * "License"); you may not use this file except in compliance
+    * with the License.  You may obtain a copy of the License at
+    *
+    *   http://www.apache.org/licenses/LICENSE-2.0
+    *
+    * Unless required by applicable law or agreed to in writing,
+    * software distributed under the License is distributed on an
+    * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    * KIND, either express or implied.  See the License for the
+    * specific language governing permissions and limitations
+    * under the License.
+    */
     var ORIGIN_METHOD = '\0__throttleOriginMethod';
     var RATE = '\0__throttleRate';
     var THROTTLE_TYPE = '\0__throttleType';
+    /**
+     * @public
+     * @param {(Function)} fn
+     * @param {number} [delay=0] Unit: ms.
+     * @param {boolean} [debounce=false]
+     *        true: If call interval less than `delay`, only the last call works.
+     *        false: If call interval less than `delay, call works on fixed rate.
+     * @return {(Function)} throttled fn.
+     */
+
     function throttle(fn, delay, debounce) {
       var currCall;
       var lastCall = 0;
@@ -20052,7 +22700,14 @@
         var thisDebounce = debounceNextCall || debounce;
         debounceNextCall = null;
         diff = currCall - (thisDebounce ? lastCall : lastExec) - thisDelay;
-        clearTimeout(timer);
+        clearTimeout(timer); // Here we should make sure that: the `exec` SHOULD NOT be called later
+        // than a new call of `cb`, that is, preserving the command order. Consider
+        // calculating "scale rate" when roaming as an example. When a call of `cb`
+        // happens, either the `exec` is called dierectly, or the call is delayed.
+        // But the delayed call should never be later than next call of `cb`. Under
+        // this assurance, we can simply update view state each time `dispatchAction`
+        // triggered by user roaming, but not need to add extra code to avoid the
+        // state being "rolled-back".
 
         if (thisDebounce) {
           timer = setTimeout(exec, thisDelay);
@@ -20066,6 +22721,11 @@
 
         lastCall = currCall;
       };
+      /**
+       * Clear throttle.
+       * @public
+       */
+
 
       cb.clear = function () {
         if (timer) {
@@ -20073,6 +22733,10 @@
           timer = null;
         }
       };
+      /**
+       * Enable debounce once.
+       */
+
 
       cb.debounceNextCall = function (debounceDelay) {
         debounceNextCall = debounceDelay;
@@ -20080,6 +22744,28 @@
 
       return cb;
     }
+    /**
+     * Create throttle method or update throttle rate.
+     *
+     * @example
+     * ComponentView.prototype.render = function () {
+     *     ...
+     *     throttle.createOrUpdate(
+     *         this,
+     *         '_dispatchAction',
+     *         this.model.get('throttle'),
+     *         'fixRate'
+     *     );
+     * };
+     * ComponentView.prototype.remove = function () {
+     *     throttle.clear(this, '_dispatchAction');
+     * };
+     * ComponentView.prototype.dispose = function () {
+     *     throttle.clear(this, '_dispatchAction');
+     * };
+     *
+     */
+
     function createOrUpdate(obj, fnAttr, rate, throttleType) {
       var fn = obj[fnAttr];
 
@@ -20104,6 +22790,10 @@
 
       return fn;
     }
+    /**
+     * Clear throttle. Example see throttle.createOrUpdate.
+     */
+
     function clear(obj, fnAttr) {
       var fn = obj[fnAttr];
 
@@ -20134,6 +22824,7 @@
     }
 
     function getDefaultColorKey(seriesModel, stylePath) {
+      // return defaultColorKey[stylePath] ||
       var colorKey = seriesModel.visualDrawType || defaultColorKey[stylePath];
 
       if (!colorKey) {
@@ -20149,7 +22840,8 @@
       performRawSeries: true,
       reset: function (seriesModel, ecModel) {
         var data = seriesModel.getData();
-        var stylePath = seriesModel.visualStyleAccessPath || 'itemStyle';
+        var stylePath = seriesModel.visualStyleAccessPath || 'itemStyle'; // Set in itemStyle
+
         var styleModel = seriesModel.getModel(stylePath);
         var getStyle = getStyleMapper(seriesModel, stylePath);
         var globalStyle = getStyle(styleModel);
@@ -20158,19 +22850,25 @@
         if (decalOption) {
           data.setVisual('decal', decalOption);
           decalOption.dirty = true;
-        }
+        } // TODO
+
 
         var colorKey = getDefaultColorKey(seriesModel, stylePath);
-        var color = globalStyle[colorKey];
-        var colorCallback = isFunction(color) ? color : null;
+        var color = globalStyle[colorKey]; // TODO style callback
+
+        var colorCallback = isFunction(color) ? color : null; // Get from color palette by default.
 
         if (!globalStyle[colorKey] || colorCallback) {
-          globalStyle[colorKey] = seriesModel.getColorFromPalette(seriesModel.name, null, ecModel.getSeriesCount());
+          // Note: if some series has color specified (e.g., by itemStyle.color), we DO NOT
+          // make it effect palette. Bacause some scenarios users need to make some series
+          // transparent or as background, which should better not effect the palette.
+          globalStyle[colorKey] = seriesModel.getColorFromPalette( // TODO series count changed.
+          seriesModel.name, null, ecModel.getSeriesCount());
           data.setVisual('colorFromPalette', true);
         }
 
         data.setVisual('style', globalStyle);
-        data.setVisual('drawType', colorKey);
+        data.setVisual('drawType', colorKey); // Only visible series has each data be visual encoded
 
         if (!ecModel.isSeriesFiltered(seriesModel) && colorCallback) {
           data.setVisual('colorFromPalette', false);
@@ -20195,11 +22893,13 @@
         }
 
         var data = seriesModel.getData();
-        var stylePath = seriesModel.visualStyleAccessPath || 'itemStyle';
+        var stylePath = seriesModel.visualStyleAccessPath || 'itemStyle'; // Set in itemStyle
+
         var getStyle = getStyleMapper(seriesModel, stylePath);
         var colorKey = data.getVisual('drawType');
         return {
           dataEach: data.hasItemOption ? function (data, idx) {
+            // Not use getItemModel for performance considuration
             var rawItem = data.getRawDataItem(idx);
 
             if (rawItem && rawItem[stylePath]) {
@@ -20220,10 +22920,14 @@
           } : null
         };
       }
-    };
+    }; // Pick color from palette for the data which has not been set with color yet.
+    // Note: do not support stream rendering. No such cases yet.
+
     var dataColorPaletteTask = {
       performRawSeries: true,
       overallReset: function (ecModel) {
+        // Each type of series use one scope.
+        // Pie and funnel are using diferrent scopes
         var paletteScopeGroupByType = createHashMap();
         ecModel.eachSeries(function (seriesModel) {
           if (!seriesModel.useColorPaletteOnData) {
@@ -20253,10 +22957,15 @@
           data.each(function (idx) {
             var rawIdx = data.getRawIndex(idx);
             idxMap[rawIdx] = idx;
-          });
+          }); // Iterate on data before filtered. To make sure color from palette can be
+          // Consistent when toggling legend.
+
           dataAll.each(function (rawIdx) {
             var idx = idxMap[rawIdx];
-            var fromPalette = data.getItemVisual(idx, 'colorFromPalette');
+            var fromPalette = data.getItemVisual(idx, 'colorFromPalette'); // Get color from palette for each data only when the color is inherited from series color, which is
+            // also picked from color palette. So following situation is not in the case:
+            // 1. series.itemStyle.color is set
+            // 2. color is encoded by visualMap
 
             if (fromPalette) {
               var itemStyle = data.ensureUniqueItemVisual(idx, 'style');
@@ -20270,6 +22979,15 @@
     };
 
     var PI$3 = Math.PI;
+    /**
+     * @param {module:echarts/ExtensionAPI} api
+     * @param {Object} [opts]
+     * @param {string} [opts.text]
+     * @param {string} [opts.color]
+     * @param {string} [opts.textColor]
+     * @return {module:zrender/Element}
+     */
+
     function defaultLoading(api, opts) {
       opts = opts || {};
       defaults(opts, {
@@ -20342,12 +23060,17 @@
           startAngle: PI$3 * 3 / 2
         }).delay(300).start('circularInOut');
         group.add(arc);
-      }
+      } // Inject resize
+
 
       group.resize = function () {
         var textWidth = textContent.getBoundingRect().width;
-        var r = opts.showSpinner ? opts.spinnerRadius : 0;
-        var cx = (api.getWidth() - r * 2 - (opts.showSpinner && textWidth ? 10 : 0) - textWidth) / 2 - (opts.showSpinner && textWidth ? 0 : 5 + textWidth / 2) + (opts.showSpinner ? 0 : textWidth / 2) + (textWidth ? 0 : r);
+        var r = opts.showSpinner ? opts.spinnerRadius : 0; // cx = (containerWidth - arcDiameter - textDistance - textWidth) / 2
+        // textDistance needs to be calculated when both animation and text exist
+
+        var cx = (api.getWidth() - r * 2 - (opts.showSpinner && textWidth ? 10 : 0) - textWidth) / 2 - (opts.showSpinner && textWidth ? 0 : 5 + textWidth / 2) // only show the text
+        + (opts.showSpinner ? 0 : textWidth / 2) // only show the spinner
+        + (textWidth ? 0 : r);
         var cy = api.getHeight() / 2;
         opts.showSpinner && arc.setShape({
           cx: cx,
@@ -20371,26 +23094,57 @@
       return group;
     }
 
-    var Scheduler = function () {
+    var Scheduler =
+    /** @class */
+    function () {
       function Scheduler(ecInstance, api, dataProcessorHandlers, visualHandlers) {
+        // key: handlerUID
         this._stageTaskMap = createHashMap();
         this.ecInstance = ecInstance;
-        this.api = api;
+        this.api = api; // Fix current processors in case that in some rear cases that
+        // processors might be registered after echarts instance created.
+        // Register processors incrementally for a echarts instance is
+        // not supported by this stream architecture.
+
         dataProcessorHandlers = this._dataProcessorHandlers = dataProcessorHandlers.slice();
         visualHandlers = this._visualHandlers = visualHandlers.slice();
         this._allHandlers = dataProcessorHandlers.concat(visualHandlers);
       }
 
       Scheduler.prototype.restoreData = function (ecModel, payload) {
-        ecModel.restoreData(payload);
+        // TODO: Only restore needed series and components, but not all components.
+        // Currently `restoreData` of all of the series and component will be called.
+        // But some independent components like `title`, `legend`, `graphic`, `toolbox`,
+        // `tooltip`, `axisPointer`, etc, do not need series refresh when `setOption`,
+        // and some components like coordinate system, axes, dataZoom, visualMap only
+        // need their target series refresh.
+        // (1) If we are implementing this feature some day, we should consider these cases:
+        // if a data processor depends on a component (e.g., dataZoomProcessor depends
+        // on the settings of `dataZoom`), it should be re-performed if the component
+        // is modified by `setOption`.
+        // (2) If a processor depends on sevral series, speicified by its `getTargetSeries`,
+        // it should be re-performed when the result array of `getTargetSeries` changed.
+        // We use `dependencies` to cover these issues.
+        // (3) How to update target series when coordinate system related components modified.
+        // TODO: simply the dirty mechanism? Check whether only the case here can set tasks dirty,
+        // and this case all of the tasks will be set as dirty.
+        ecModel.restoreData(payload); // Theoretically an overall task not only depends on each of its target series, but also
+        // depends on all of the series.
+        // The overall task is not in pipeline, and `ecModel.restoreData` only set pipeline tasks
+        // dirty. If `getTargetSeries` of an overall task returns nothing, we should also ensure
+        // that the overall task is set as dirty and to be performed, otherwise it probably cause
+        // state chaos. So we have to set dirty of all of the overall tasks manually, otherwise it
+        // probably cause state chaos (consider `dataZoomProcessor`).
 
         this._stageTaskMap.each(function (taskRecord) {
           var overallTask = taskRecord.overallTask;
           overallTask && overallTask.dirty();
         });
-      };
+      }; // If seriesModel provided, incremental threshold is check by series data.
+
 
       Scheduler.prototype.getPerformArgs = function (task, isBlock) {
+        // For overall task
         if (!task.__pipeline) {
           return;
         }
@@ -20412,14 +23166,29 @@
       Scheduler.prototype.getPipeline = function (pipelineId) {
         return this._pipelineMap.get(pipelineId);
       };
+      /**
+       * Current, progressive rendering starts from visual and layout.
+       * Always detect render mode in the same stage, avoiding that incorrect
+       * detection caused by data filtering.
+       * Caution:
+       * `updateStreamModes` use `seriesModel.getData()`.
+       */
+
 
       Scheduler.prototype.updateStreamModes = function (seriesModel, view) {
         var pipeline = this._pipelineMap.get(seriesModel.uid);
 
         var data = seriesModel.getData();
-        var dataLen = data.count();
+        var dataLen = data.count(); // `progressiveRender` means that can render progressively in each
+        // animation frame. Note that some types of series do not provide
+        // `view.incrementalPrepareRender` but support `chart.appendData`. We
+        // use the term `incremental` but not `progressive` to describe the
+        // case that `chart.appendData`.
+
         var progressiveRender = pipeline.progressiveEnabled && view.incrementalPrepareRender && dataLen >= pipeline.threshold;
-        var large = seriesModel.get('large') && dataLen >= seriesModel.get('largeThreshold');
+        var large = seriesModel.get('large') && dataLen >= seriesModel.get('largeThreshold'); // TODO: modDataCount should not updated if `appendData`, otherwise cause whole repaint.
+        // see `test/candlestick-large3.html`
+
         var modDataCount = seriesModel.get('progressiveChunkMode') === 'mod' ? dataLen : null;
         seriesModel.pipelineContext = pipeline.context = {
           progressiveRender: progressiveRender,
@@ -20458,6 +23227,7 @@
           var errMsg = '';
 
           if ("development" !== 'production') {
+            // Currently do not need to support to sepecify them both.
             errMsg = '"reset" and "overallReset" must not be both specified.';
           }
 
@@ -20479,6 +23249,7 @@
       };
 
       Scheduler.prototype.performDataProcessorTasks = function (ecModel, payload) {
+        // If we do not use `block` here, it should be considered when to update modes.
         this._performStageTasks(this._dataProcessorHandlers, ecModel, payload, {
           block: true
         });
@@ -20513,7 +23284,11 @@
             });
             overallNeedDirty_1 && overallTask.dirty();
             scheduler.updatePayload(overallTask, payload);
-            var performArgs_1 = scheduler.getPerformArgs(overallTask, opt.block);
+            var performArgs_1 = scheduler.getPerformArgs(overallTask, opt.block); // Execute stubs firstly, which may set the overall task dirty,
+            // then execute the overall task. And stub will call seriesModel.setData,
+            // which ensures that in the overallTask seriesModel.getData() will not
+            // return incorrect data.
+
             agentStubMap.each(function (stub) {
               stub.perform(performArgs_1);
             });
@@ -20527,7 +23302,15 @@
                 task.dirty();
               }
 
-              var performArgs = scheduler.getPerformArgs(task, opt.block);
+              var performArgs = scheduler.getPerformArgs(task, opt.block); // FIXME
+              // if intending to decalare `performRawSeries` in handlers, only
+              // stream-independent (specifically, data item independent) operations can be
+              // performed. Because is a series is filtered, most of the tasks will not
+              // be performed. A stream-dependent operation probably cause wrong biz logic.
+              // Perhaps we should not provide a separate callback for this case instead
+              // of providing the config `performRawSeries`. The stream-dependent operaions
+              // and stream-independent operations should better not be mixed.
+
               performArgs.skip = !stageHandler.performRawSeries && ecModel.isSeriesFiltered(task.context.model);
               scheduler.updatePayload(task, payload);
 
@@ -20548,12 +23331,14 @@
       Scheduler.prototype.performSeriesTasks = function (ecModel) {
         var unfinished;
         ecModel.eachSeries(function (seriesModel) {
+          // Progress to the end for dataInit and dataRestore.
           unfinished = seriesModel.dataTask.perform() || unfinished;
         });
         this.unfinished = unfinished || this.unfinished;
       };
 
       Scheduler.prototype.plan = function () {
+        // Travel pipelines, check block.
         this._pipelineMap.each(function (pipeline) {
           var task = pipeline.tail;
 
@@ -20574,10 +23359,14 @@
 
       Scheduler.prototype._createSeriesStageTask = function (stageHandler, stageHandlerRecord, ecModel, api) {
         var scheduler = this;
-        var oldSeriesTaskMap = stageHandlerRecord.seriesTaskMap;
+        var oldSeriesTaskMap = stageHandlerRecord.seriesTaskMap; // The count of stages are totally about only several dozen, so
+        // do not need to reuse the map.
+
         var newSeriesTaskMap = stageHandlerRecord.seriesTaskMap = createHashMap();
         var seriesType = stageHandler.seriesType;
-        var getTargetSeries = stageHandler.getTargetSeries;
+        var getTargetSeries = stageHandler.getTargetSeries; // If a stageHandler should cover all series, `createOnAllSeries` should be declared mandatorily,
+        // to avoid some typo or abuse. Otherwise if an extension do not specify a `seriesType`,
+        // it works but it may cause other irrelevant charts blocked.
 
         if (stageHandler.createOnAllSeries) {
           ecModel.eachRawSeries(create);
@@ -20588,7 +23377,9 @@
         }
 
         function create(seriesModel) {
-          var pipelineId = seriesModel.uid;
+          var pipelineId = seriesModel.uid; // Init tasks for each seriesModel only once.
+          // Reuse original task instance.
+
           var task = newSeriesTaskMap.set(pipelineId, oldSeriesTaskMap && oldSeriesTaskMap.get(pipelineId) || createTask({
             plan: seriesTaskPlan,
             reset: seriesTaskReset,
@@ -20598,6 +23389,7 @@
             model: seriesModel,
             ecModel: ecModel,
             api: api,
+            // PENDING: `useClearVisual` not used?
             useClearVisual: stageHandler.isVisual && !stageHandler.isLayout,
             plan: stageHandler.plan,
             reset: stageHandler.reset,
@@ -20610,7 +23402,8 @@
 
       Scheduler.prototype._createOverallStageTask = function (stageHandler, stageHandlerRecord, ecModel, api) {
         var scheduler = this;
-        var overallTask = stageHandlerRecord.overallTask = stageHandlerRecord.overallTask || createTask({
+        var overallTask = stageHandlerRecord.overallTask = stageHandlerRecord.overallTask // For overall task, the function only be called on reset stage.
+        || createTask({
           reset: overallTaskReset
         });
         overallTask.context = {
@@ -20619,12 +23412,20 @@
           overallReset: stageHandler.overallReset,
           scheduler: scheduler
         };
-        var oldAgentStubMap = overallTask.agentStubMap;
+        var oldAgentStubMap = overallTask.agentStubMap; // The count of stages are totally about only several dozen, so
+        // do not need to reuse the map.
+
         var newAgentStubMap = overallTask.agentStubMap = createHashMap();
         var seriesType = stageHandler.seriesType;
         var getTargetSeries = stageHandler.getTargetSeries;
         var overallProgress = true;
-        var shouldOverallTaskDirty = false;
+        var shouldOverallTaskDirty = false; // FIXME:TS never used, so comment it
+        // let modifyOutputEnd = stageHandler.modifyOutputEnd;
+        // An overall task with seriesType detected or has `getTargetSeries`, we add
+        // stub in each pipelines, it will set the overall task dirty when the pipeline
+        // progress. Moreover, to avoid call the overall task each frame (too frequent),
+        // we set the pipeline block.
+
         var errMsg = '';
 
         if ("development" !== 'production') {
@@ -20637,20 +23438,28 @@
           ecModel.eachRawSeriesByType(seriesType, createStub);
         } else if (getTargetSeries) {
           getTargetSeries(ecModel, api).each(createStub);
-        } else {
-          overallProgress = false;
-          each(ecModel.getSeries(), createStub);
-        }
+        } // Otherwise, (usually it is legancy case), the overall task will only be
+        // executed when upstream dirty. Otherwise the progressive rendering of all
+        // pipelines will be disabled unexpectedly. But it still needs stubs to receive
+        // dirty info from upsteam.
+        else {
+            overallProgress = false;
+            each(ecModel.getSeries(), createStub);
+          }
 
         function createStub(seriesModel) {
           var pipelineId = seriesModel.uid;
-          var stub = newAgentStubMap.set(pipelineId, oldAgentStubMap && oldAgentStubMap.get(pipelineId) || (shouldOverallTaskDirty = true, createTask({
+          var stub = newAgentStubMap.set(pipelineId, oldAgentStubMap && oldAgentStubMap.get(pipelineId) || ( // When the result of `getTargetSeries` changed, the overallTask
+          // should be set as dirty and re-performed.
+          shouldOverallTaskDirty = true, createTask({
             reset: stubReset,
             onDirty: stubOnDirty
           })));
           stub.context = {
             model: seriesModel,
-            overallProgress: overallProgress
+            overallProgress: overallProgress // FIXME:TS never used, so comment it
+            // modifyOutputEnd: modifyOutputEnd
+
           };
           stub.agent = overallTask;
           stub.__block = overallProgress;
@@ -20742,11 +23551,20 @@
     function seriesTaskCount(context) {
       return context.data.count();
     }
+    /**
+     * Only some legacy stage handlers (usually in echarts extensions) are pure function.
+     * To ensure that they can work normally, they should work in block mode, that is,
+     * they should not be started util the previous tasks finished. So they cause the
+     * progressive rendering disabled. We try to detect the series type, to narrow down
+     * the block range to only the series type they concern, but not all series.
+     */
+
 
     function detectSeriseType(legacyFunc) {
       seriesType = null;
 
       try {
+        // Assume there is no async when calling `eachSeriesByType`.
         legacyFunc(ecModelMock, apiMock);
       } catch (e) {}
 
@@ -20770,9 +23588,13 @@
     };
 
     function mockMethods(target, Clz) {
+      /* eslint-disable */
       for (var name_1 in Clz.prototype) {
+        // Do not use hasOwnProperty
         target[name_1] = noop;
       }
+      /* eslint-enable */
+
     }
 
     /*
@@ -20799,6 +23621,24 @@
      * AUTO-GENERATED FILE. DO NOT MODIFY.
      */
 
+    /*
+    * Licensed to the Apache Software Foundation (ASF) under one
+    * or more contributor license agreements.  See the NOTICE file
+    * distributed with this work for additional information
+    * regarding copyright ownership.  The ASF licenses this file
+    * to you under the Apache License, Version 2.0 (the
+    * "License"); you may not use this file except in compliance
+    * with the License.  You may obtain a copy of the License at
+    *
+    *   http://www.apache.org/licenses/LICENSE-2.0
+    *
+    * Unless required by applicable law or agreed to in writing,
+    * software distributed under the License is distributed on an
+    * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    * KIND, either express or implied.  See the License for the
+    * specific language governing permissions and limitations
+    * under the License.
+    */
     var colorAll = ['#37A2DA', '#32C5E9', '#67E0E3', '#9FE6B8', '#FFDB5C', '#ff9f7f', '#fb7293', '#E062AE', '#E690D1', '#e7bcf3', '#9d96f5', '#8378EA', '#96BFFF'];
     var lightTheme = {
       color: colorAll,
@@ -20829,6 +23669,24 @@
      * AUTO-GENERATED FILE. DO NOT MODIFY.
      */
 
+    /*
+    * Licensed to the Apache Software Foundation (ASF) under one
+    * or more contributor license agreements.  See the NOTICE file
+    * distributed with this work for additional information
+    * regarding copyright ownership.  The ASF licenses this file
+    * to you under the Apache License, Version 2.0 (the
+    * "License"); you may not use this file except in compliance
+    * with the License.  You may obtain a copy of the License at
+    *
+    *   http://www.apache.org/licenses/LICENSE-2.0
+    *
+    * Unless required by applicable law or agreed to in writing,
+    * software distributed under the License is distributed on an
+    * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    * KIND, either express or implied.  See the License for the
+    * specific language governing permissions and limitations
+    * under the License.
+    */
     var contrastColor = '#B9B8CE';
     var backgroundColor = '#100C2A';
 
@@ -20870,6 +23728,7 @@
           color: '#817f91'
         },
         label: {
+          // TODO Contrast of label backgorundColor
           color: '#fff'
         }
       },
@@ -21001,7 +23860,9 @@
           color: '#f64e56',
           color0: '#54ea92',
           borderColor: '#f64e56',
-          borderColor0: '#54ea92'
+          borderColor0: '#54ea92' // borderColor: '#ca2824',
+          // borderColor0: '#09a443'
+
         }
       }
     };
@@ -21024,6 +23885,31 @@
 
     var storage = createHashMap();
     var mapDataStorage = {
+      /**
+       * Compatible with previous `echarts.registerMap`.
+       * @usage
+       * ```js
+       * $.get('USA.json', function (geoJson) {
+       *     echarts.registerMap('USA', geoJson);
+       *     // Or
+       *     echarts.registerMap('USA', {
+       *         geoJson: geoJson,
+       *         specialAreas: {}
+       *     })
+       * });
+       *
+       * $.get('airport.svg', function (svg) {
+       *     echarts.registerMap('airport', {
+       *         svg: svg
+       *     }
+       * });
+       *
+       * echarts.registerMap('eu', [
+       *     {svg: eu-topographic.svg},
+       *     {geoJSON: eu.json}
+       * ])
+       * ```
+       */
       registerMap: function (mapName, rawDef, rawSpecialAreas) {
         var records;
 
@@ -21036,6 +23922,7 @@
             specialAreas: rawDef.specialAreas
           }];
         } else {
+          // Backward compatibility.
           var geoSource = rawDef.geoJson || rawDef.geoJSON;
 
           if (geoSource && !rawDef.features) {
@@ -21072,58 +23959,88 @@
         var source = record.source;
         record.geoJSON = !isString(source) ? source : typeof JSON !== 'undefined' && JSON.parse ? JSON.parse(source) : new Function('return (' + source + ');')();
       },
+      // Only perform parse to XML object here, which might be time
+      // consiming for large SVG.
+      // Although convert XML to zrender element is also time consiming,
+      // if we do it here, the clone of zrender elements has to be
+      // required. So we do it once for each geo instance, util real
+      // performance issues call for optimizing it.
       svg: function (record) {
         record.svgXML = parseXML(record.source);
       }
     };
 
-    var ECEventProcessor = function () {
+    /**
+     * Usage of query:
+     * `chart.on('click', query, handler);`
+     * The `query` can be:
+     * + The component type query string, only `mainType` or `mainType.subType`,
+     *   like: 'xAxis', 'series', 'xAxis.category' or 'series.line'.
+     * + The component query object, like:
+     *   `{seriesIndex: 2}`, `{seriesName: 'xx'}`, `{seriesId: 'some'}`,
+     *   `{xAxisIndex: 2}`, `{xAxisName: 'xx'}`, `{xAxisId: 'some'}`.
+     * + The data query object, like:
+     *   `{dataIndex: 123}`, `{dataType: 'link'}`, `{name: 'some'}`.
+     * + The other query object (cmponent customized query), like:
+     *   `{element: 'some'}` (only available in custom series).
+     *
+     * Caveat: If a prop in the `query` object is `null/undefined`, it is the
+     * same as there is no such prop in the `query` object.
+     */
+
+    var ECEventProcessor =
+    /** @class */
+    function () {
       function ECEventProcessor() {}
 
       ECEventProcessor.prototype.normalizeQuery = function (query) {
         var cptQuery = {};
         var dataQuery = {};
-        var otherQuery = {};
+        var otherQuery = {}; // `query` is `mainType` or `mainType.subType` of component.
 
         if (isString(query)) {
-          var condCptType = parseClassType(query);
+          var condCptType = parseClassType(query); // `.main` and `.sub` may be ''.
+
           cptQuery.mainType = condCptType.main || null;
           cptQuery.subType = condCptType.sub || null;
-        } else {
-          var suffixes_1 = ['Index', 'Name', 'Id'];
-          var dataKeys_1 = {
-            name: 1,
-            dataIndex: 1,
-            dataType: 1
-          };
-          each(query, function (val, key) {
-            var reserved = false;
+        } // `query` is an object, convert to {mainType, index, name, id}.
+        else {
+            // `xxxIndex`, `xxxName`, `xxxId`, `name`, `dataIndex`, `dataType` is reserved,
+            // can not be used in `compomentModel.filterForExposedEvent`.
+            var suffixes_1 = ['Index', 'Name', 'Id'];
+            var dataKeys_1 = {
+              name: 1,
+              dataIndex: 1,
+              dataType: 1
+            };
+            each(query, function (val, key) {
+              var reserved = false;
 
-            for (var i = 0; i < suffixes_1.length; i++) {
-              var propSuffix = suffixes_1[i];
-              var suffixPos = key.lastIndexOf(propSuffix);
+              for (var i = 0; i < suffixes_1.length; i++) {
+                var propSuffix = suffixes_1[i];
+                var suffixPos = key.lastIndexOf(propSuffix);
 
-              if (suffixPos > 0 && suffixPos === key.length - propSuffix.length) {
-                var mainType = key.slice(0, suffixPos);
+                if (suffixPos > 0 && suffixPos === key.length - propSuffix.length) {
+                  var mainType = key.slice(0, suffixPos); // Consider `dataIndex`.
 
-                if (mainType !== 'data') {
-                  cptQuery.mainType = mainType;
-                  cptQuery[propSuffix.toLowerCase()] = val;
-                  reserved = true;
+                  if (mainType !== 'data') {
+                    cptQuery.mainType = mainType;
+                    cptQuery[propSuffix.toLowerCase()] = val;
+                    reserved = true;
+                  }
                 }
               }
-            }
 
-            if (dataKeys_1.hasOwnProperty(key)) {
-              dataQuery[key] = val;
-              reserved = true;
-            }
+              if (dataKeys_1.hasOwnProperty(key)) {
+                dataQuery[key] = val;
+                reserved = true;
+              }
 
-            if (!reserved) {
-              otherQuery[key] = val;
-            }
-          });
-        }
+              if (!reserved) {
+                otherQuery[key] = val;
+              }
+            });
+          }
 
         return {
           cptQuery: cptQuery,
@@ -21133,6 +24050,7 @@
       };
 
       ECEventProcessor.prototype.filter = function (eventType, query) {
+        // They should be assigned before each trigger call.
         var eventInfo = this.eventInfo;
 
         if (!eventInfo) {
@@ -21142,7 +24060,7 @@
         var targetEl = eventInfo.targetEl;
         var packedEvent = eventInfo.packedEvent;
         var model = eventInfo.model;
-        var view = eventInfo.view;
+        var view = eventInfo.view; // For event like 'globalout'.
 
         if (!model || !view) {
           return true;
@@ -21158,6 +24076,7 @@
       };
 
       ECEventProcessor.prototype.afterTrigger = function () {
+        // Make sure the eventInfo wont be used in next trigger.
         this.eventInfo = null;
       };
 
@@ -21166,6 +24085,7 @@
 
     var seriesSymbolTask = {
       createOnAllSeries: true,
+      // For legend.
       performRawSeries: true,
       reset: function (seriesModel, ecModel) {
         var data = seriesModel.getData();
@@ -21191,11 +24111,15 @@
         var seriesSymbolRotate = !hasSymbolRotateCallback ? symbolRotate : null;
         data.setVisual({
           legendSymbol: seriesModel.legendSymbol || seriesSymbol,
+          // If seting callback functions on `symbol` or `symbolSize`, for simplicity and avoiding
+          // to bring trouble, we do not pick a reuslt from one of its calling on data item here,
+          // but just use the default value. Callback on `symbol` or `symbolSize` is convenient in
+          // some cases but generally it is not recommanded.
           symbol: seriesSymbol,
           symbolSize: seriesSymbolSize,
           symbolKeepAspect: keepAspect,
           symbolRotate: seriesSymbolRotate
-        });
+        }); // Only visible series has each data be visual encoded
 
         if (ecModel.isSeriesFiltered(seriesModel)) {
           return;
@@ -21216,11 +24140,13 @@
     };
     var dataSymbolTask = {
       createOnAllSeries: true,
+      // For legend.
       performRawSeries: true,
       reset: function (seriesModel, ecModel) {
         if (!seriesModel.hasSymbolVisual) {
           return;
-        }
+        } // Only visible series has each data be visual encoded
+
 
         if (ecModel.isSeriesFiltered(seriesModel)) {
           return;
@@ -21233,13 +24159,14 @@
           var itemSymbolType = itemModel.getShallow('symbol', true);
           var itemSymbolSize = itemModel.getShallow('symbolSize', true);
           var itemSymbolRotate = itemModel.getShallow('symbolRotate', true);
-          var itemSymbolKeepAspect = itemModel.getShallow('symbolKeepAspect', true);
+          var itemSymbolKeepAspect = itemModel.getShallow('symbolKeepAspect', true); // If has item symbol
 
           if (itemSymbolType != null) {
             data.setItemVisual(idx, 'symbol', itemSymbolType);
           }
 
           if (itemSymbolSize != null) {
+            // PENDING Transform symbolSize ?
             data.setItemVisual(idx, 'symbolSize', itemSymbolSize);
           }
 
@@ -21282,6 +24209,24 @@
      * AUTO-GENERATED FILE. DO NOT MODIFY.
      */
 
+    /*
+    * Licensed to the Apache Software Foundation (ASF) under one
+    * or more contributor license agreements.  See the NOTICE file
+    * distributed with this work for additional information
+    * regarding copyright ownership.  The ASF licenses this file
+    * to you under the Apache License, Version 2.0 (the
+    * "License"); you may not use this file except in compliance
+    * with the License.  You may obtain a copy of the License at
+    *
+    *   http://www.apache.org/licenses/LICENSE-2.0
+    *
+    * Unless required by applicable law or agreed to in writing,
+    * software distributed under the License is distributed on an
+    * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    * KIND, either express or implied.  See the License for the
+    * specific language governing permissions and limitations
+    * under the License.
+    */
     function getItemVisualFromData(data, dataIndex, key) {
       switch (key) {
         case 'color':
@@ -21361,11 +24306,13 @@
       y -= cy;
       var d = Math.sqrt(x * x + y * y);
       x /= d;
-      y /= d;
+      y /= d; // Intersect point.
+
       var ox = x * r + cx;
       var oy = y * r + cy;
 
       if (Math.abs(startAngle - endAngle) % PI2$6 < 1e-4) {
+        // Is a circle
         out[0] = ox;
         out[1] = oy;
         return d - r;
@@ -21391,6 +24338,7 @@
       }
 
       if (angle >= startAngle && angle <= endAngle || angle + PI2$6 >= startAngle && angle + PI2$6 <= endAngle) {
+        // Project point is on the arc.
         out[0] = ox;
         out[1] = oy;
         return d - r;
@@ -21421,7 +24369,8 @@
       var dy1 = y2 - y1;
       var lineLen = Math.sqrt(dx1 * dx1 + dy1 * dy1);
       dx1 /= lineLen;
-      dy1 /= lineLen;
+      dy1 /= lineLen; // dot product
+
       var projectedLen = dx * dx1 + dy * dy1;
       var t = projectedLen / lineLen;
 
@@ -21460,6 +24409,11 @@
       out.set(tmpPt[0], tmpPt[1]);
       return dist;
     }
+    /**
+     * Calculate min distance corresponding point.
+     * This method won't evaluate if point is in the path.
+     */
+
 
     function nearestPointOnPath(pt, path, out) {
       var xi = 0;
@@ -21487,6 +24441,8 @@
 
         switch (cmd) {
           case CMD$3.M:
+            // moveTo 命令重新创建一个新的 subpath, 并且更新新的起点
+            // 在 closePath 的时候使用
             x0 = data[i++];
             y0 = data[i++];
             xi = x0;
@@ -21512,21 +24468,25 @@
             break;
 
           case CMD$3.A:
+            // TODO Arc 判断的开销比较大
             var cx = data[i++];
             var cy = data[i++];
             var rx = data[i++];
             var ry = data[i++];
             var theta = data[i++];
-            var dTheta = data[i++];
+            var dTheta = data[i++]; // TODO Arc 旋转
+
             i += 1;
             var anticlockwise = !!(1 - data[i++]);
             x1 = Math.cos(theta) * rx + cx;
-            y1 = Math.sin(theta) * ry + cy;
+            y1 = Math.sin(theta) * ry + cy; // 不是直接使用 arc 命令
 
             if (i <= 1) {
+              // 第一个命令起点还未定义
               x0 = x1;
               y0 = y1;
-            }
+            } // zr 使用scale来模拟椭圆, 这里也对x做一定的缩放
+
 
             var _x = (x - cx) * ry / rx + cx;
 
@@ -21557,20 +24517,29 @@
       }
 
       return minDist;
-    }
+    } // Temporal varible for intermediate usage.
+
 
     var pt0 = new Point();
     var pt1 = new Point();
     var pt2 = new Point();
     var dir = new Point();
     var dir2 = new Point();
+    /**
+     * Calculate a proper guide line based on the label position and graphic element definition
+     * @param label
+     * @param labelRect
+     * @param target
+     * @param targetRect
+     */
+
     function updateLabelLinePoints(target, labelLineModel) {
       if (!target) {
         return;
       }
 
       var labelLine = target.getTextGuideLine();
-      var label = target.getTextContent();
+      var label = target.getTextContent(); // Needs to create text guide in each charts.
 
       if (!(label && labelLine)) {
         return;
@@ -21594,13 +24563,16 @@
       for (var i = 0; i < searchSpace.length; i++) {
         var candidate = searchSpace[i];
         getCandidateAnchor(candidate, 0, labelRect, pt0, dir);
-        Point.scaleAndAdd(pt1, pt0, dir, len);
-        pt1.transform(targetInversedTransform);
+        Point.scaleAndAdd(pt1, pt0, dir, len); // Transform to target coord space.
+
+        pt1.transform(targetInversedTransform); // Note: getBoundingRect will ensure the `path` being created.
+
         var boundingRect = target.getBoundingRect();
-        var dist = anchorPoint ? anchorPoint.distance(pt1) : target instanceof Path ? nearestPointOnPath(pt1, target.path, pt2) : nearestPointOnRect(pt1, boundingRect, pt2);
+        var dist = anchorPoint ? anchorPoint.distance(pt1) : target instanceof Path ? nearestPointOnPath(pt1, target.path, pt2) : nearestPointOnRect(pt1, boundingRect, pt2); // TODO pt2 is in the path
 
         if (dist < minDist) {
-          minDist = dist;
+          minDist = dist; // Transform back to global space.
+
           pt1.transform(targetTransform);
           pt2.transform(targetTransform);
           pt2.toArray(points[0]);
@@ -21613,15 +24585,26 @@
       labelLine.setShape({
         points: points
       });
-    }
+    } // Temporal variable for the limitTurnAngle function
+
     var tmpArr = [];
     var tmpProjPoint = new Point();
+    /**
+     * Reduce the line segment attached to the label to limit the turn angle between two segments.
+     * @param linePoints
+     * @param minTurnAngle Radian of minimum turn angle. 0 - 180
+     */
+
     function limitTurnAngle(linePoints, minTurnAngle) {
       if (!(minTurnAngle <= 180 && minTurnAngle > 0)) {
         return;
       }
 
-      minTurnAngle = minTurnAngle / 180 * Math.PI;
+      minTurnAngle = minTurnAngle / 180 * Math.PI; // The line points can be
+      //      /pt1----pt2 (label)
+      //     /
+      // pt0/
+
       pt0.fromArray(linePoints[0]);
       pt1.fromArray(linePoints[1]);
       pt2.fromArray(linePoints[2]);
@@ -21640,9 +24623,13 @@
       var minTurnAngleCos = Math.cos(minTurnAngle);
 
       if (minTurnAngleCos < angleCos) {
+        // Smaller than minTurnAngle
+        // Calculate project point of pt0 on pt1-pt2
         var d = projectPointToLine(pt1.x, pt1.y, pt2.x, pt2.y, pt0.x, pt0.y, tmpArr, false);
-        tmpProjPoint.fromArray(tmpArr);
-        tmpProjPoint.scaleAndAdd(dir2, d / Math.tan(Math.PI - minTurnAngle));
+        tmpProjPoint.fromArray(tmpArr); // Calculate new projected length with limited minTurnAngle and get the new connect point
+
+        tmpProjPoint.scaleAndAdd(dir2, d / Math.tan(Math.PI - minTurnAngle)); // Limit the new calculated connect point between pt1 and pt2.
+
         var t = pt2.x !== pt1.x ? (tmpProjPoint.x - pt1.x) / (pt2.x - pt1.x) : (tmpProjPoint.y - pt1.y) / (pt2.y - pt1.y);
 
         if (isNaN(t)) {
@@ -21658,6 +24645,11 @@
         tmpProjPoint.toArray(linePoints[1]);
       }
     }
+    /**
+     * Limit the angle of line and the surface
+     * @param maxSurfaceAngle Radian of minimum turn angle. 0 - 180. 0 is same direction to normal. 180 is opposite
+     */
+
     function limitSurfaceAngle(linePoints, surfaceNormal, maxSurfaceAngle) {
       if (!(maxSurfaceAngle <= 180 && maxSurfaceAngle > 0)) {
         return;
@@ -21682,6 +24674,7 @@
       var maxSurfaceAngleCos = Math.cos(maxSurfaceAngle);
 
       if (angleCos < maxSurfaceAngleCos) {
+        // Calculate project point of pt0 on pt1-pt2
         var d = projectPointToLine(pt1.x, pt1.y, pt2.x, pt2.y, pt0.x, pt0.y, tmpArr, false);
         tmpProjPoint.fromArray(tmpArr);
         var HALF_PI = Math.PI / 2;
@@ -21689,9 +24682,12 @@
         var newAngle = HALF_PI + angle2 - maxSurfaceAngle;
 
         if (newAngle >= HALF_PI) {
+          // parallel
           Point.copy(tmpProjPoint, pt2);
         } else {
-          tmpProjPoint.scaleAndAdd(dir2, d / Math.tan(Math.PI / 2 - newAngle));
+          // Calculate new projected length with limited minTurnAngle and get the new connect point
+          tmpProjPoint.scaleAndAdd(dir2, d / Math.tan(Math.PI / 2 - newAngle)); // Limit the new calculated connect point between pt1 and pt2.
+
           var t = pt2.x !== pt1.x ? (tmpProjPoint.x - pt1.x) / (pt2.x - pt1.x) : (tmpProjPoint.y - pt1.y) / (pt2.y - pt1.y);
 
           if (isNaN(t)) {
@@ -21711,8 +24707,10 @@
 
     function setLabelLineState(labelLine, ignore, stateName, stateModel) {
       var isNormal = stateName === 'normal';
-      var stateObj = isNormal ? labelLine : labelLine.ensureState(stateName);
-      stateObj.ignore = ignore;
+      var stateObj = isNormal ? labelLine : labelLine.ensureState(stateName); // Make sure display.
+
+      stateObj.ignore = ignore; // Set smooth
+
       var smooth = stateModel.get('smooth');
 
       if (smooth && smooth === true) {
@@ -21761,12 +24759,17 @@
         }
       }
     }
+    /**
+     * Create a label line if necessary and set it's style.
+     */
+
 
     function setLabelLineStyle(targetEl, statesModels, defaultStyle) {
       var labelLine = targetEl.getTextGuideLine();
       var label = targetEl.getTextContent();
 
       if (!label) {
+        // Not show label line if there is no label.
         if (labelLine) {
           targetEl.removeTextGuideLine();
         }
@@ -21787,23 +24790,28 @@
           var stateShow = stateModel.get('show');
           var isLabelIgnored = isNormal ? labelIgnoreNormal : retrieve2(label.states[stateName] && label.states[stateName].ignore, labelIgnoreNormal);
 
-          if (isLabelIgnored || !retrieve2(stateShow, showNormal)) {
-            var stateObj = isNormal ? labelLine : labelLine && labelLine.states.normal;
+          if (isLabelIgnored // Not show when label is not shown in this state.
+          || !retrieve2(stateShow, showNormal) // Use normal state by default if not set.
+          ) {
+              var stateObj = isNormal ? labelLine : labelLine && labelLine.states.normal;
 
-            if (stateObj) {
-              stateObj.ignore = true;
-            }
+              if (stateObj) {
+                stateObj.ignore = true;
+              }
+
+              continue;
+            } // Create labelLine if not exists
 
-            continue;
-          }
 
           if (!labelLine) {
             labelLine = new Polyline();
-            targetEl.setTextGuideLine(labelLine);
+            targetEl.setTextGuideLine(labelLine); // Reset state of normal because it's new created.
+            // NOTE: NORMAL should always been the first!
 
             if (!isNormal && (labelIgnoreNormal || !showNormal)) {
               setLabelLineState(labelLine, true, 'normal', statesModels.normal);
-            }
+            } // Use same state proxy.
+
 
             if (targetEl.stateProxy) {
               labelLine.stateProxy = targetEl.stateProxy;
@@ -21815,11 +24823,13 @@
       }
 
       if (labelLine) {
-        defaults(labelLine.style, defaultStyle);
+        defaults(labelLine.style, defaultStyle); // Not fill.
+
         labelLine.style.fill = null;
         var showAbove = normalModel.get('showAbove');
         var labelLineConfig = targetEl.textGuideLineConfig = targetEl.textGuideLineConfig || {};
-        labelLineConfig.showAbove = showAbove || false;
+        labelLineConfig.showAbove = showAbove || false; // Custom the buildPath.
+
         labelLine.buildPath = buildLabelLinePath;
       }
     }
@@ -21848,7 +24858,8 @@
         }
 
         var label = rawItem.label;
-        var transform = label.getComputedTransform();
+        var transform = label.getComputedTransform(); // NOTE: Get bounding rect after getComputedTransform, or label may not been updated by the host el.
+
         var localRect = label.getBoundingRect();
         var isAxisAligned = !transform || transform[1] < 1e-5 && transform[2] < 1e-5;
         var minMargin = label.style.margin || 0;
@@ -21897,6 +24908,7 @@
         delta = rect[xyDim] - lastPos;
 
         if (delta < 0) {
+          // shiftForward(i, len, -delta);
           rect[xyDim] -= delta;
           item.label[xyDim] -= delta;
           adjusted = true;
@@ -21908,19 +24920,23 @@
       }
 
       if (totalShifts > 0 && balanceShift) {
+        // Shift back to make the distribution more equally.
         shiftList(-totalShifts / len, 0, len);
-      }
+      } // TODO bleedMargin?
+
 
       var first = list[0];
       var last = list[len - 1];
       var minGap;
       var maxGap;
-      updateMinMaxGap();
+      updateMinMaxGap(); // If ends exceed two bounds, squeeze at most 80%, then take the gap of two bounds.
+
       minGap < 0 && squeezeGaps(-minGap, 0.8);
       maxGap < 0 && squeezeGaps(maxGap, 0.8);
       updateMinMaxGap();
       takeBoundsGap(minGap, maxGap, 1);
-      takeBoundsGap(maxGap, minGap, -1);
+      takeBoundsGap(maxGap, minGap, -1); // Handle bailout when there is not enough space.
+
       updateMinMaxGap();
 
       if (minGap < 0) {
@@ -21938,6 +24954,7 @@
 
       function takeBoundsGap(gapThisBound, gapOtherBound, moveDir) {
         if (gapThisBound < 0) {
+          // Move from other gap if can.
           var moveFromMaxGap = Math.min(gapOtherBound, -gapThisBound);
 
           if (moveFromMaxGap > 0) {
@@ -21964,7 +24981,8 @@
           rect[xyDim] += delta;
           item.label[xyDim] += delta;
         }
-      }
+      } // Squeeze gaps if the labels exceed margin.
+
 
       function squeezeGaps(delta, maxSqeezePercent) {
         var gaps = [];
@@ -21985,16 +25003,25 @@
 
         if (delta > 0) {
           for (var i = 0; i < len - 1; i++) {
-            var movement = gaps[i] * squeezePercent;
+            // Distribute the shift delta to all gaps.
+            var movement = gaps[i] * squeezePercent; // Forward
+
             shiftList(movement, 0, i + 1);
           }
         } else {
+          // Backward
           for (var i = len - 1; i > 0; i--) {
+            // Distribute the shift delta to all gaps.
             var movement = gaps[i - 1] * squeezePercent;
             shiftList(-movement, i, len);
           }
         }
       }
+      /**
+       * Squeeze to allow overlap if there is no more space available.
+       * Let other overlapping strategy like hideOverlap do the job instead of keep exceeding the bounds.
+       */
+
 
       function squeezeWhenBailout(delta) {
         var dir = delta < 0 ? -1 : 1;
@@ -22003,8 +25030,10 @@
 
         for (var i = 0; i < len - 1; i++) {
           if (dir > 0) {
+            // Forward
             shiftList(moveForEachLabel, 0, i + 1);
           } else {
+            // Backward
             shiftList(-moveForEachLabel, len - i - 1, len);
           }
 
@@ -22018,15 +25047,29 @@
 
       return adjusted;
     }
+    /**
+     * Adjust labels on x direction to avoid overlap.
+     */
+
 
-    function shiftLayoutOnX(list, leftBound, rightBound, balanceShift) {
+    function shiftLayoutOnX(list, leftBound, rightBound, // If average the shifts on all labels and add them to 0
+    // TODO: Not sure if should enable it.
+    // Pros: The angle of lines will distribute more equally
+    // Cons: In some layout. It may not what user wanted. like in pie. the label of last sector is usually changed unexpectedly.
+    balanceShift) {
       return shiftLayout(list, 'x', 'width', leftBound, rightBound, balanceShift);
     }
-    function shiftLayoutOnY(list, topBound, bottomBound, balanceShift) {
+    /**
+     * Adjust labels on y direction to avoid overlap.
+     */
+
+    function shiftLayoutOnY(list, topBound, bottomBound, // If average the shifts on all labels and add them to 0
+    balanceShift) {
       return shiftLayout(list, 'y', 'height', topBound, bottomBound, balanceShift);
     }
     function hideOverlap(labelList) {
-      var displayedLabels = [];
+      var displayedLabels = []; // TODO, render overflow visible first, put in the displayedLabels.
+
       labelList.sort(function (a, b) {
         return b.priority - a.priority;
       });
@@ -22034,6 +25077,7 @@
 
       function hideEl(el) {
         if (!el.ignore) {
+          // Show on emphasis.
           var emphasisState = el.ensureState('emphasis');
 
           if (emphasisState.ignore == null) {
@@ -22051,7 +25095,8 @@
         var transform = labelItem.transform;
         var label = labelItem.label;
         var labelLine = labelItem.labelLine;
-        globalRect.copy(labelItem.rect);
+        globalRect.copy(labelItem.rect); // Add a threshold because layout may be aligned precisely.
+
         globalRect.width -= 0.1;
         globalRect.height -= 0.1;
         globalRect.x += 0.05;
@@ -22060,22 +25105,25 @@
         var overlapped = false;
 
         for (var j = 0; j < displayedLabels.length; j++) {
-          var existsTextCfg = displayedLabels[j];
+          var existsTextCfg = displayedLabels[j]; // Fast rejection.
 
           if (!globalRect.intersect(existsTextCfg.rect)) {
             continue;
           }
 
           if (isAxisAligned && existsTextCfg.axisAligned) {
+            // Is overlapped
             overlapped = true;
             break;
           }
 
           if (!existsTextCfg.obb) {
+            // If self is not axis aligned. But other is.
             existsTextCfg.obb = new OrientedBoundingRect(existsTextCfg.localRect, existsTextCfg.transform);
           }
 
           if (!obb) {
+            // If self is axis aligned. But other is not.
             obb = new OrientedBoundingRect(localRect, transform);
           }
 
@@ -22083,7 +25131,8 @@
             overlapped = true;
             break;
           }
-        }
+        } // TODO Callback to determine if this overlap should be handled?
+
 
         if (overlapped) {
           hideEl(label);
@@ -22118,6 +25167,8 @@
         text: labelItem.label.style.text,
         rect: labelItem.hostRect,
         labelRect: labelItem.rect,
+        // x: labelAttr.x,
+        // y: labelAttr.y,
         align: label.style.align,
         verticalAlign: label.style.verticalAlign,
         labelLinePoints: cloneArr(labelLine && labelLine.shape.points)
@@ -22141,7 +25192,9 @@
 
     var LABEL_LAYOUT_PROPS = ['x', 'y', 'rotation'];
 
-    var LabelManager = function () {
+    var LabelManager =
+    /** @class */
+    function () {
       function LabelManager() {
         this._labelList = [];
         this._chartViewList = [];
@@ -22151,11 +25204,16 @@
         this._labelList = [];
         this._chartViewList = [];
       };
+      /**
+       * Add label to manager
+       */
+
 
       LabelManager.prototype._addLabel = function (dataIndex, dataType, seriesModel, label, layoutOption) {
         var labelStyle = label.style;
         var hostEl = label.__hostTarget;
-        var textConfig = hostEl.textConfig || {};
+        var textConfig = hostEl.textConfig || {}; // TODO: If label is in other state.
+
         var labelTransform = label.getComputedTransform();
         var labelRect = label.getBoundingRect().plain();
         BoundingRect.applyTransform(labelRect, labelRect, labelTransform);
@@ -22163,6 +25221,7 @@
         if (labelTransform) {
           dummyTransformable.setLocalTransform(labelTransform);
         } else {
+          // Identity transform.
           dummyTransformable.x = dummyTransformable.y = dummyTransformable.rotation = dummyTransformable.originX = dummyTransformable.originY = 0;
           dummyTransformable.scaleX = dummyTransformable.scaleY = 1;
         }
@@ -22188,7 +25247,11 @@
           computedLayoutOption: null,
           rect: labelRect,
           hostRect: hostRect,
+          // Label with lower priority will be hidden when overlapped
+          // Use rect size as default priority
           priority: hostRect ? hostRect.width * hostRect.height : 0,
+          // Save default label attributes.
+          // For restore if developers want get back to default value in callback.
           defaultAttr: {
             ignore: label.ignore,
             labelGuideIgnore: labelGuide && labelGuide.ignore,
@@ -22218,6 +25281,9 @@
 
         var seriesModel = chartView.__model;
         var layoutOption = seriesModel.get('labelLayout');
+        /**
+         * Ignore layouting if it's not specified anything.
+         */
 
         if (!(isFunction(layoutOption) || keys(layoutOption).length)) {
           return;
@@ -22225,11 +25291,12 @@
 
         chartView.group.traverse(function (child) {
           if (child.ignore) {
-            return true;
-          }
+            return true; // Stop traverse descendants.
+          } // Only support label being hosted on graphic elements.
+
 
           var textEl = child.getTextContent();
-          var ecData = getECData(child);
+          var ecData = getECData(child); // Can only attach the text on the element with dataIndex
 
           if (textEl && !textEl.disableLabelLayout) {
             _this._addLabel(ecData.dataIndex, ecData.dataType, seriesModel, textEl, layoutOption);
@@ -22252,7 +25319,7 @@
           var label = labelItem.label;
           var hostEl = label.__hostTarget;
           var defaultLabelAttr = labelItem.defaultAttr;
-          var layoutOption = void 0;
+          var layoutOption = void 0; // TODO A global layout option?
 
           if (typeof labelItem.layoutOption === 'function') {
             layoutOption = labelItem.layoutOption(prepareLayoutCallbackParams(labelItem, hostEl));
@@ -22266,8 +25333,11 @@
 
           if (hostEl) {
             hostEl.setTextConfig({
+              // Force to set local false.
               local: false,
+              // Ignore position and rotation config on the host el if x or y is changed.
               position: layoutOption.x != null || layoutOption.y != null ? null : defaultLabelAttr.attachedPos,
+              // Ignore rotation config on the host el if rotation is changed.
               rotation: layoutOption.rotate != null ? layoutOption.rotate * degreeToRadian : defaultLabelAttr.attachedRot,
               offset: [layoutOption.dx || 0, layoutOption.dy || 0]
             });
@@ -22276,8 +25346,10 @@
           var needsUpdateLabelLine = false;
 
           if (layoutOption.x != null) {
+            // TODO width of chart view.
             label.x = parsePercent$1(layoutOption.x, width);
-            label.setStyle('x', 0);
+            label.setStyle('x', 0); // Ignore movement in style. TODO: origin.
+
             needsUpdateLabelLine = true;
           } else {
             label.x = defaultLabelAttr.x;
@@ -22285,8 +25357,10 @@
           }
 
           if (layoutOption.y != null) {
+            // TODO height of chart view.
             label.y = parsePercent$1(layoutOption.y, height);
-            label.setStyle('y', 0);
+            label.setStyle('y', 0); // Ignore movement in style.
+
             needsUpdateLabelLine = true;
           } else {
             label.y = defaultLabelAttr.y;
@@ -22299,7 +25373,8 @@
             if (guideLine) {
               guideLine.setShape({
                 points: layoutOption.labelLinePoints
-              });
+              }); // Not update
+
               needsUpdateLabelLine = false;
             }
           }
@@ -22328,6 +25403,7 @@
               label.on('drag', createDragHandler(hostEl, hostModel.getModel('labelLine')));
             }
           } else {
+            // TODO Other drag functions?
             label.off('drag');
             label.cursor = defaultLabelAttr.cursor;
           }
@@ -22351,6 +25427,10 @@
         });
         hideOverlap(labelsNeedsHideOverlap);
       };
+      /**
+       * Process all labels. Not only labels with layoutOption.
+       */
+
 
       LabelManager.prototype.processLabelsOverall = function () {
         var _this = this;
@@ -22361,7 +25441,7 @@
           var animationEnabled = seriesModel.isAnimationEnabled();
           chartView.group.traverse(function (child) {
             if (child.ignore) {
-              return true;
+              return true; // Stop traverse descendants.
             }
 
             var needsUpdateLabelLine = !ignoreLabelLineUpdate;
@@ -22383,16 +25463,19 @@
       };
 
       LabelManager.prototype._updateLabelLine = function (el, seriesModel) {
-        var textEl = el.getTextContent();
+        // Only support label being hosted on graphic elements.
+        var textEl = el.getTextContent(); // Update label line style.
+
         var ecData = getECData(el);
-        var dataIndex = ecData.dataIndex;
+        var dataIndex = ecData.dataIndex; // Only support labelLine on the labels represent data.
 
         if (textEl && dataIndex != null) {
           var data = seriesModel.getData(ecData.dataType);
           var itemModel = data.getItemModel(dataIndex);
           var defaultStyle = {};
           var visualStyle = data.getItemVisual(dataIndex, 'style');
-          var visualType = data.getVisual('drawType');
+          var visualType = data.getVisual('drawType'); // Default to be same with main color
+
           defaultStyle.stroke = visualStyle[visualType];
           var labelLineModel = itemModel.getModel('labelLine');
           setLabelLineStyle(el, getLabelLineStatesModels(itemModel), defaultStyle);
@@ -22402,7 +25485,7 @@
 
       LabelManager.prototype._animateLabels = function (el, seriesModel) {
         var textEl = el.getTextContent();
-        var guideLine = el.getTextGuideLine();
+        var guideLine = el.getTextGuideLine(); // Animate
 
         if (textEl && !textEl.ignore && !textEl.invisible && !el.disableLabelAnimation && !isElementRemoved(el)) {
           var layoutStore = labelLayoutInnerStore(textEl);
@@ -22417,10 +25500,11 @@
           var data = seriesModel.getData(ecData.dataType);
 
           if (!oldLayout) {
-            textEl.attr(newProps);
+            textEl.attr(newProps); // Disable fade in animation if value animation is enabled.
 
             if (!labelInner(textEl).valueAnimation) {
-              var oldOpacity = retrieve2(textEl.style.opacity, 1);
+              var oldOpacity = retrieve2(textEl.style.opacity, 1); // Fade in animation
+
               textEl.style.opacity = 0;
               initProps(textEl, {
                 style: {
@@ -22429,7 +25513,8 @@
               }, seriesModel, dataIndex);
             }
           } else {
-            textEl.attr(oldLayout);
+            textEl.attr(oldLayout); // Make sure the animation from is in the right status.
+
             var prevStates = el.prevStates;
 
             if (prevStates) {
@@ -22493,6 +25578,8 @@
       return LabelManager;
     }();
 
+    // Inlucdes: pieSelect, pieUnSelect, pieToggleSelect, mapSelect, mapUnSelect, mapToggleSelect
+
     function createLegacyDataSelectAction(seriesType, ecRegisterAction) {
       function getSeriesIndices(ecModel, payload) {
         var seriesIndices = [];
@@ -22594,6 +25681,24 @@
      * AUTO-GENERATED FILE. DO NOT MODIFY.
      */
 
+    /*
+    * Licensed to the Apache Software Foundation (ASF) under one
+    * or more contributor license agreements.  See the NOTICE file
+    * distributed with this work for additional information
+    * regarding copyright ownership.  The ASF licenses this file
+    * to you under the Apache License, Version 2.0 (the
+    * "License"); you may not use this file except in compliance
+    * with the License.  You may obtain a copy of the License at
+    *
+    *   http://www.apache.org/licenses/LICENSE-2.0
+    *
+    * Unless required by applicable law or agreed to in writing,
+    * software distributed under the License is distributed on an
+    * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    * KIND, either express or implied.  See the License for the
+    * specific language governing permissions and limitations
+    * under the License.
+    */
     function findEventDispatcher(target, det, returnFirstMatch) {
       var found;
 
@@ -22653,6 +25758,11 @@
         return WeakMap;
     }());
 
+    /**
+     * Triangle shape
+     * @inner
+     */
+
     var Triangle = Path.extend({
       type: 'triangle',
       shape: {
@@ -22672,6 +25782,11 @@
         path.closePath();
       }
     });
+    /**
+     * Diamond shape
+     * @inner
+     */
+
     var Diamond = Path.extend({
       type: 'diamond',
       shape: {
@@ -22692,9 +25807,15 @@
         path.closePath();
       }
     });
+    /**
+     * Pin shape
+     * @inner
+     */
+
     var Pin = Path.extend({
       type: 'pin',
       shape: {
+        // x, y on the cusp
         x: 0,
         y: 0,
         width: 0,
@@ -22703,12 +25824,15 @@
       buildPath: function (path, shape) {
         var x = shape.x;
         var y = shape.y;
-        var w = shape.width / 5 * 3;
+        var w = shape.width / 5 * 3; // Height must be larger than width
+
         var h = Math.max(w, shape.height);
-        var r = w / 2;
+        var r = w / 2; // Dist on y with tangent point and circle center
+
         var dy = r * r / (h - r);
         var cy = y - h + r + dy;
-        var angle = Math.asin(dy / r);
+        var angle = Math.asin(dy / r); // Dist on x with tangent point and circle center
+
         var dx = Math.cos(angle) * r;
         var tanX = Math.sin(angle);
         var tanY = Math.cos(angle);
@@ -22721,6 +25845,11 @@
         path.closePath();
       }
     });
+    /**
+     * Arrow shape
+     * @inner
+     */
+
     var Arrow = Path.extend({
       type: 'arrow',
       shape: {
@@ -22743,7 +25872,14 @@
         ctx.closePath();
       }
     });
+    /**
+     * Map of path contructors
+     */
+    // TODO Use function to build symbol path.
+
     var symbolCtors = {
+      // Use small height rect to simulate line.
+      // Avoid using stroke.
       line: Rect,
       rect: Rect,
       roundRect: Rect,
@@ -22753,10 +25889,12 @@
       pin: Pin,
       arrow: Arrow,
       triangle: Triangle
-    };
+    }; // NOTICE Only use fill. No line!
+
     var symbolShapeMakers = {
       line: function (x, y, w, h, shape) {
-        var thickness = 2;
+        var thickness = 2; // A thin line
+
         shape.x = x;
         shape.y = y + h / 2 - thickness / 2;
         shape.width = w;
@@ -22783,6 +25921,7 @@
         shape.height = size;
       },
       circle: function (x, y, w, h, shape) {
+        // Put circle in the center of square
         shape.cx = x + w / 2;
         shape.cy = y + h / 2;
         shape.r = Math.min(w, h) / 2;
@@ -22842,6 +25981,7 @@
           var proxySymbol = symbolBuildProxies[symbolType];
 
           if (!proxySymbol) {
+            // Default rect
             symbolType = 'rect';
             proxySymbol = symbolBuildProxies[symbolType];
           }
@@ -22850,7 +25990,7 @@
           proxySymbol.buildPath(ctx, proxySymbol.shape, inBundle);
         }
       }
-    });
+    }); // Provide setColor helper method to avoid determine if set the fill or stroke outside
 
     function symbolPathSetColor(color, innerColor) {
       if (this.type !== 'image') {
@@ -22858,7 +25998,8 @@
 
         if (this.__isEmptyBrush) {
           symbolStyle.stroke = color;
-          symbolStyle.fill = innerColor || '#fff';
+          symbolStyle.fill = innerColor || '#fff'; // TODO Same width with lineStyle in LineView.
+
           symbolStyle.lineWidth = 2;
         } else {
           symbolStyle.fill = color;
@@ -22867,8 +26008,14 @@
         this.markRedraw();
       }
     }
+    /**
+     * Create a symbol element with given symbol configuration: shape, x, y, width, height, color
+     */
 
-    function createSymbol(symbolType, x, y, w, h, color, keepAspect) {
+
+    function createSymbol(symbolType, x, y, w, h, color, // whether to keep the ratio of w/h,
+    keepAspect) {
+      // TODO Support image object, DynamicImage.
       var isEmpty = symbolType.indexOf('empty') === 0;
 
       if (isEmpty) {
@@ -22893,7 +26040,8 @@
         });
       }
 
-      symbolPath.__isEmptyBrush = isEmpty;
+      symbolPath.__isEmptyBrush = isEmpty; // TODO Should deprecate setColor
+
       symbolPath.setColor = symbolPathSetColor;
 
       if (color) {
@@ -23538,6 +26686,13 @@
     var decalMap = new WeakMap();
     var decalCache = new LRU(100);
     var decalKeys = ['symbol', 'symbolSize', 'symbolKeepAspect', 'color', 'backgroundColor', 'dashArrayX', 'dashArrayY', 'maxTileWidth', 'maxTileHeight'];
+    /**
+     * Create or update pattern image from decal options
+     *
+     * @param {InnerDecalObject | 'none'} decalObject decal options, 'none' if no decal
+     * @return {Pattern} pattern with generated image, null if no decal
+     */
+
     function createOrUpdatePatternFromDecal(decalObject, api) {
       if (decalObject === 'none') {
         return null;
@@ -23637,8 +26792,24 @@
         pattern.svgElement = svgRoot;
         pattern.svgWidth = pSize.width;
         pattern.svgHeight = pSize.height;
+        /**
+         * Get minumum length that can make a repeatable pattern.
+         *
+         * @return {Object} pattern width and height
+         */
 
         function getPatternSize() {
+          /**
+           * For example, if dash is [[3, 2], [2, 1]] for X, it looks like
+           * |---  ---  ---  ---  --- ...
+           * |-- -- -- -- -- -- -- -- ...
+           * |---  ---  ---  ---  --- ...
+           * |-- -- -- -- -- -- -- -- ...
+           * So the minumum length of X is 15,
+           * which is the least common multiple of `3 + 2` and `2 + 1`
+           * |---  ---  ---  |---  --- ...
+           * |-- -- -- -- -- |-- -- -- ...
+           */
           var width = 1;
 
           for (var i = 0, xlen = lineBlockLengthsX.length; i < xlen; ++i) {
@@ -23656,6 +26827,7 @@
 
           if ("development" !== 'production') {
             var warn = function (attrName) {
+              /* eslint-disable-next-line */
               console.warn("Calculated decal size is greater than " + attrName + " due to decal option settings so " + attrName + " is used for the decal size. Please consider changing the decal option to make a smaller decal or set " + attrName + " to be larger to avoid incontinuity.");
             };
 
@@ -23691,6 +26863,7 @@
           }
 
           if (ySum <= 0) {
+            // dashArrayY is 0, draw nothing
             return;
           }
 
@@ -23714,8 +26887,10 @@
                 }
 
                 if (xSum <= 0) {
+                  // Skip empty line
                   break;
-                }
+                } // E.g., [15, 5, 20, 5] draws only for 15 and 20
+
 
                 if (xId1 % 2 === 0) {
                   var size = (1 - decalOpt.symbolSize) * 0.5;
@@ -23759,12 +26934,19 @@
             if (isSVG) {
               svgRoot.appendChild(zr.painter.paintOne(symbol));
             } else {
+              // Paint to canvas for all other renderers.
               brushSingle(ctx, symbol);
             }
           }
         }
       }
     }
+    /**
+     * Convert symbol array into normalized array
+     *
+     * @param {string | (string | string[])[]} symbol symbol input
+     * @return {string[][]} normolized symbol array
+     */
 
     function normalizeSymbolArray(symbol) {
       if (!symbol || symbol.length === 0) {
@@ -23800,6 +26982,13 @@
 
       return result;
     }
+    /**
+     * Convert dash input into dashArray
+     *
+     * @param {DecalDashArrayX} dash dash input
+     * @return {number[][]} normolized dash array
+     */
+
 
     function normalizeDashArrayX(dash) {
       if (!dash || dash.length === 0) {
@@ -23810,6 +26999,11 @@
         var dashValue = Math.ceil(dash);
         return [[dashValue, dashValue]];
       }
+      /**
+       * [20, 5] should be normalized into [[20, 5]],
+       * while [20, [5, 10]] should be normalized into [[20, 20], [5, 10]]
+       */
+
 
       var isAllNumber = true;
 
@@ -23836,6 +27030,8 @@
           });
 
           if (dashValue.length % 2 === 1) {
+            // [4, 2, 1] means |----  -    -- |----  -    -- |
+            // so normalize it to be [4, 2, 1, 4, 2, 1]
             result.push(dashValue.concat(dashValue));
           } else {
             result.push(dashValue);
@@ -23845,6 +27041,13 @@
 
       return result;
     }
+    /**
+     * Convert dash input into dashArray
+     *
+     * @param {DecalDashArrayY} dash dash input
+     * @return {number[]} normolized dash array
+     */
+
 
     function normalizeDashArrayY(dash) {
       if (!dash || typeof dash === 'object' && dash.length === 0) {
@@ -23861,6 +27064,15 @@
       });
       return dash.length % 2 ? dashValue.concat(dashValue) : dashValue;
     }
+    /**
+     * Get block length of each line. A block is the length of dash line and space.
+     * For example, a line with [4, 1] has a dash line of 4 and a space of 1 after
+     * that, so the block length of this line is 5.
+     *
+     * @param {number[][]} dash dash arrary of X or Y
+     * @return {number[]} block length of each line
+     */
+
 
     function getLineBlockLengthX(dash) {
       return map(dash, function (line) {
@@ -23876,6 +27088,8 @@
       }
 
       if (dash.length % 2 === 1) {
+        // [4, 2, 1] means |----  -    -- |----  -    -- |
+        // So total length is (4 + 2 + 1) * 2
         return blockLength * 2;
       }
 
@@ -23916,13 +27130,17 @@
     var isObject$2 = isObject;
     var indexOf$1 = indexOf;
     var hasWindow = typeof window !== 'undefined';
-    var version$1 = '5.0.1';
+    var version$1 = '5.0.2';
     var dependencies = {
-      zrender: '5.0.3'
+      zrender: '5.0.4'
     };
     var TEST_FRAME_REMAIN_TIME = 1;
-    var PRIORITY_PROCESSOR_SERIES_FILTER = 800;
-    var PRIORITY_PROCESSOR_DATASTACK = 900;
+    var PRIORITY_PROCESSOR_SERIES_FILTER = 800; // Some data processors depends on the stack result dimension (to calculate data extent).
+    // So data stack stage should be in front of data processing stage.
+
+    var PRIORITY_PROCESSOR_DATASTACK = 900; // "Data filter" will block the stream, so it should be
+    // put at the begining of data processing.
+
     var PRIORITY_PROCESSOR_FILTER = 1000;
     var PRIORITY_PROCESSOR_DEFAULT = 2000;
     var PRIORITY_PROCESSOR_STATISTIC = 5000;
@@ -23930,8 +27148,13 @@
     var PRIORITY_VISUAL_PROGRESSIVE_LAYOUT = 1100;
     var PRIORITY_VISUAL_GLOBAL = 2000;
     var PRIORITY_VISUAL_CHART = 3000;
-    var PRIORITY_VISUAL_COMPONENT = 4000;
-    var PRIORITY_VISUAL_CHART_DATA_CUSTOM = 4500;
+    var PRIORITY_VISUAL_COMPONENT = 4000; // Visual property in data. Greater than `PRIORITY_VISUAL_COMPONENT` to enable to
+    // overwrite the viusal result of component (like `visualMap`)
+    // using data item specific setting (like itemStyle.xxx on data item)
+
+    var PRIORITY_VISUAL_CHART_DATA_CUSTOM = 4500; // Greater than `PRIORITY_VISUAL_CHART_DATA_CUSTOM` to enable to layout based on
+    // visual result like `symbolSize`.
+
     var PRIORITY_VISUAL_POST_CHART_LAYOUT = 4600;
     var PRIORITY_VISUAL_BRUSH = 5000;
     var PRIORITY_VISUAL_ARIA = 6000;
@@ -23954,7 +27177,12 @@
         ARIA: PRIORITY_VISUAL_ARIA,
         DECAL: PRIORITY_VISUAL_DECAL
       }
-    };
+    }; // Main process have three entries: `setOption`, `dispatchAction` and `resize`,
+    // where they must not be invoked nestedly, except the only case: invoke
+    // dispatchAction with updateMethod "none" in main process.
+    // This flag is used to carry out this rule.
+    // All events will be triggered out side main process (i.e. when !this[IN_MAIN_PROCESS]).
+
     var IN_MAIN_PROCESS_KEY = '__flagInMainProcess';
     var OPTION_UPDATED_KEY = '__optionUpdated';
     var STATUS_NEEDS_UPDATE_KEY = '__needsUpdateStatus';
@@ -23994,11 +27222,14 @@
     }
 
     function toLowercaseNameAndCallEventful(host, method, args) {
+      // `args[0]` is event name. Event name is all lowercase.
       args[0] = args[0] && args[0].toLowerCase();
       return Eventful.prototype[method].apply(host, args);
     }
 
-    var MessageCenter = function (_super) {
+    var MessageCenter =
+    /** @class */
+    function (_super) {
       __extends(MessageCenter, _super);
 
       function MessageCenter() {
@@ -24010,7 +27241,10 @@
 
     var messageCenterProto = MessageCenter.prototype;
     messageCenterProto.on = createRegisterEventWithLowercaseMessageCenter('on');
-    messageCenterProto.off = createRegisterEventWithLowercaseMessageCenter('off');
+    messageCenterProto.off = createRegisterEventWithLowercaseMessageCenter('off'); // ---------------------------------------
+    // Internal method names for class ECharts
+    // ---------------------------------------
+
     var prepare;
     var prepareView;
     var updateDirectly;
@@ -24033,18 +27267,22 @@
     var markStatusToUpdate;
     var applyChangedStates;
 
-    var ECharts = function (_super) {
+    var ECharts =
+    /** @class */
+    function (_super) {
       __extends(ECharts, _super);
 
-      function ECharts(dom, theme, opts) {
+      function ECharts(dom, // Theme name or themeOption.
+      theme, opts) {
         var _this = _super.call(this, new ECEventProcessor()) || this;
 
         _this._chartsViews = [];
         _this._chartsMap = {};
         _this._componentsViews = [];
-        _this._componentsMap = {};
+        _this._componentsMap = {}; // Can't dispatch action during rendering procedure
+
         _this._pendingActions = [];
-        opts = opts || {};
+        opts = opts || {}; // Get theme by name
 
         if (typeof theme === 'string') {
           theme = themeStorage[theme];
@@ -24055,7 +27293,9 @@
         var defaultUseDirtyRect = false;
 
         if ("development" !== 'production') {
-          var root = hasWindow ? window : global;
+          var root =
+          /* eslint-disable-next-line */
+          hasWindow ? window : global;
           defaultRenderer = root.__ECHARTS__DEFAULT__RENDERER__ || defaultRenderer;
           var devUseDirtyRect = root.__ECHARTS__DEFAULT__USE_DIRTY_RECT__;
           defaultUseDirtyRect = devUseDirtyRect == null ? defaultUseDirtyRect : devUseDirtyRect;
@@ -24067,14 +27307,15 @@
           width: opts.width,
           height: opts.height,
           useDirtyRect: opts.useDirtyRect == null ? defaultUseDirtyRect : opts.useDirtyRect
-        });
+        }); // Expect 60 fps.
+
         _this._throttledZrFlush = throttle(bind(zr.flush, zr), 17);
         theme = clone(theme);
         theme && globalBackwardCompat(theme, true);
         _this._theme = theme;
         _this._locale = createLocaleObject(opts.locale || SYSTEM_LANG);
         _this._coordSysMgr = new CoordinateSystemManager();
-        var api = _this._api = createExtensionAPI(_this);
+        var api = _this._api = createExtensionAPI(_this); // Sort on demand
 
         function prioritySortFunc(a, b) {
           return a.__prio - b.__prio;
@@ -24084,14 +27325,16 @@
         sort(dataProcessorFuncs, prioritySortFunc);
         _this._scheduler = new Scheduler(_this, api, dataProcessorFuncs, visualFuncs);
         _this._messageCenter = new MessageCenter();
-        _this._labelManager = new LabelManager();
+        _this._labelManager = new LabelManager(); // Init mouse events
+
+        _this._initEvents(); // In case some people write `window.onresize = chart.resize`
 
-        _this._initEvents();
 
         _this.resize = bind(_this.resize, _this);
         zr.animation.on('frame', _this._onframe, _this);
         bindRenderedEvent(zr, _this);
-        bindMouseEvent(zr, _this);
+        bindMouseEvent(zr, _this); // ECharts instance can be used as value.
+
         setAsPrimitive(_this);
         return _this;
       }
@@ -24102,13 +27345,18 @@
         }
 
         applyChangedStates(this);
-        var scheduler = this._scheduler;
+        var scheduler = this._scheduler; // Lazy update
 
         if (this[OPTION_UPDATED_KEY]) {
           var silent = this[OPTION_UPDATED_KEY].silent;
           this[IN_MAIN_PROCESS_KEY] = true;
           prepare(this);
-          updateMethods.update.call(this);
+          updateMethods.update.call(this); // At present, in each frame, zrender performs:
+          //   (1) animation step forward.
+          //   (2) trigger('frame') (where this `_onframe` is called)
+          //   (3) zrender flush (render).
+          // If we do nothing here, since we use `setToFinal: true`, the step (3) above
+          // will render the final state of the elements before the real animation started.
 
           this._zr.flush();
 
@@ -24116,26 +27364,38 @@
           this[OPTION_UPDATED_KEY] = false;
           flushPendingActions.call(this, silent);
           triggerUpdatedEvent.call(this, silent);
-        } else if (scheduler.unfinished) {
-          var remainTime = TEST_FRAME_REMAIN_TIME;
-          var ecModel = this._model;
-          var api = this._api;
-          scheduler.unfinished = false;
+        } // Avoid do both lazy update and progress in one frame.
+        else if (scheduler.unfinished) {
+            // Stream progress.
+            var remainTime = TEST_FRAME_REMAIN_TIME;
+            var ecModel = this._model;
+            var api = this._api;
+            scheduler.unfinished = false;
 
-          do {
-            var startTime = +new Date();
-            scheduler.performSeriesTasks(ecModel);
-            scheduler.performDataProcessorTasks(ecModel);
-            updateStreamModes(this, ecModel);
-            scheduler.performVisualTasks(ecModel);
-            renderSeries(this, this._model, api, 'remain');
-            remainTime -= +new Date() - startTime;
-          } while (remainTime > 0 && scheduler.unfinished);
+            do {
+              var startTime = +new Date();
+              scheduler.performSeriesTasks(ecModel); // Currently dataProcessorFuncs do not check threshold.
+
+              scheduler.performDataProcessorTasks(ecModel);
+              updateStreamModes(this, ecModel); // Do not update coordinate system here. Because that coord system update in
+              // each frame is not a good user experience. So we follow the rule that
+              // the extent of the coordinate system is determin in the first frame (the
+              // frame is executed immedietely after task reset.
+              // this._coordSysMgr.update(ecModel, api);
+              // console.log('--- ec frame visual ---', remainTime);
+
+              scheduler.performVisualTasks(ecModel);
+              renderSeries(this, this._model, api, 'remain');
+              remainTime -= +new Date() - startTime;
+            } while (remainTime > 0 && scheduler.unfinished); // Call flush explicitly for trigger finished event.
+
+
+            if (!scheduler.unfinished) {
+              this._zr.flush();
+            } // Else, zr flushing be ensue within the same frame,
+            // because zr flushing is after onframe event.
 
-          if (!scheduler.unfinished) {
-            this._zr.flush();
           }
-        }
       };
 
       ECharts.prototype.getDom = function () {
@@ -24149,6 +27409,8 @@
       ECharts.prototype.getZr = function () {
         return this._zr;
       };
+      /* eslint-disable-next-line */
+
 
       ECharts.prototype.setOption = function (option, notMerge, lazyUpdate) {
         if ("development" !== 'production') {
@@ -24192,11 +27454,14 @@
           this[OPTION_UPDATED_KEY] = {
             silent: silent
           };
-          this[IN_MAIN_PROCESS_KEY] = false;
+          this[IN_MAIN_PROCESS_KEY] = false; // `setOption(option, {lazyMode: true})` may be called when zrender has been slept.
+          // It should wake it up to make sure zrender start to render at the next frame.
+
           this.getZr().wakeUp();
         } else {
           prepare(this);
-          updateMethods.update.call(this);
+          updateMethods.update.call(this); // Ensure zr refresh sychronously, and then pixel in canvas can be
+          // fetched after `setOption`.
 
           this._zr.flush();
 
@@ -24206,10 +27471,15 @@
           triggerUpdatedEvent.call(this, silent);
         }
       };
+      /**
+       * @DEPRECATED
+       */
+
 
       ECharts.prototype.setTheme = function () {
         console.error('ECharts#setTheme() is DEPRECATED in ECharts 3.0');
-      };
+      }; // We don't want developers to use getModel directly.
+
 
       ECharts.prototype.getModel = function () {
         return this._model;
@@ -24228,8 +27498,14 @@
       };
 
       ECharts.prototype.getDevicePixelRatio = function () {
-        return this._zr.painter.dpr || hasWindow && window.devicePixelRatio || 1;
+        return this._zr.painter.dpr
+        /* eslint-disable-next-line */
+        || hasWindow && window.devicePixelRatio || 1;
       };
+      /**
+       * Get canvas which has all thing rendered
+       */
+
 
       ECharts.prototype.getRenderedCanvas = function (opts) {
         if (!env.canvasSupported) {
@@ -24237,11 +27513,21 @@
         }
 
         opts = extend({}, opts || {});
-        opts.pixelRatio = opts.pixelRatio || 1;
+        opts.pixelRatio = opts.pixelRatio || this.getDevicePixelRatio();
         opts.backgroundColor = opts.backgroundColor || this._model.get('backgroundColor');
-        var zr = this._zr;
+        var zr = this._zr; // let list = zr.storage.getDisplayList();
+        // Stop animations
+        // Never works before in init animation, so remove it.
+        // zrUtil.each(list, function (el) {
+        //     el.stopAnimation(true);
+        // });
+
         return zr.painter.getRenderedCanvas(opts);
       };
+      /**
+       * Get svg data url
+       */
+
 
       ECharts.prototype.getSvgDataURL = function () {
         if (!env.svgSupported) {
@@ -24249,7 +27535,8 @@
         }
 
         var zr = this._zr;
-        var list = zr.storage.getDisplayList();
+        var list = zr.storage.getDisplayList(); // Stop animations
+
         each(list, function (el) {
           el.stopAnimation(null, true);
         });
@@ -24308,7 +27595,7 @@
           var right_1 = -MAX_NUMBER;
           var bottom_1 = -MAX_NUMBER;
           var canvasList_1 = [];
-          var dpr_1 = opts && opts.pixelRatio || 1;
+          var dpr_1 = opts && opts.pixelRatio || this.getDevicePixelRatio();
           each(instances$1, function (chart, id) {
             if (chart.group === groupId) {
               var canvas = isSvg ? chart.getZr().painter.getSvgDom().innerHTML : chart.getRenderedCanvas(clone(opts));
@@ -24355,6 +27642,7 @@
             zr_1.refreshImmediately();
             return zr_1.painter.toDataURL();
           } else {
+            // Background between the charts
             if (opts.connectedBackgroundColor) {
               zr_1.add(new Rect({
                 shape: {
@@ -24394,6 +27682,12 @@
       ECharts.prototype.convertFromPixel = function (finder, value) {
         return doConvertPixel(this, 'convertFromPixel', finder, value);
       };
+      /**
+       * Is the specified coordinate systems or components contain the given pixel point.
+       * @param {Array|number} value
+       * @return {boolean} result
+       */
+
 
       ECharts.prototype.containPixel = function (finder, value) {
         if (this._disposed) {
@@ -24429,6 +27723,22 @@
         }, this);
         return !!result;
       };
+      /**
+       * Get visual from series or data.
+       * @param finder
+       *        If string, e.g., 'series', means {seriesIndex: 0}.
+       *        If Object, could contain some of these properties below:
+       *        {
+       *            seriesIndex / seriesId / seriesName,
+       *            dataIndex / dataIndexInside
+       *        }
+       *        If dataIndex is not specified, series visual will be fetched,
+       *        but not data item visual.
+       *        If all of seriesIndex, seriesId, seriesName are not specified,
+       *        visual will be fetched from first series.
+       * @param visualType 'color', 'symbol', 'symbolSize'
+       */
+
 
       ECharts.prototype.getVisual = function (finder, visualType) {
         var ecModel = this._model;
@@ -24447,10 +27757,18 @@
         var dataIndexInside = parsedFinder.hasOwnProperty('dataIndexInside') ? parsedFinder.dataIndexInside : parsedFinder.hasOwnProperty('dataIndex') ? data.indexOfRawIndex(parsedFinder.dataIndex) : null;
         return dataIndexInside != null ? getItemVisualFromData(data, dataIndexInside, visualType) : getVisualFromData(data, visualType);
       };
+      /**
+       * Get view of corresponding component model
+       */
+
 
       ECharts.prototype.getViewOfComponentModel = function (componentModel) {
         return this._componentsMap[componentModel.__viewId];
       };
+      /**
+       * Get view of corresponding series model
+       */
+
 
       ECharts.prototype.getViewOfSeriesModel = function (seriesModel) {
         return this._chartsMap[seriesModel.__viewId];
@@ -24465,7 +27783,7 @@
 
             var el = e.target;
             var params;
-            var isGlobalOut = eveName === 'globalout';
+            var isGlobalOut = eveName === 'globalout'; // no e.target when 'globalout'.
 
             if (isGlobalOut) {
               params = {};
@@ -24477,16 +27795,28 @@
                   var dataModel = ecData.dataModel || ecModel.getSeriesByIndex(ecData.seriesIndex);
                   params = dataModel && dataModel.getDataParams(ecData.dataIndex, ecData.dataType) || {};
                   return true;
-                } else if (ecData.eventData) {
-                  params = extend({}, ecData.eventData);
-                  return true;
-                }
+                } // If element has custom eventData of components
+                else if (ecData.eventData) {
+                    params = extend({}, ecData.eventData);
+                    return true;
+                  }
               }, true);
-            }
+            } // Contract: if params prepared in mouse event,
+            // these properties must be specified:
+            // {
+            //    componentType: string (component main type)
+            //    componentIndex: number
+            // }
+            // Otherwise event query can not work.
+
 
             if (params) {
               var componentType = params.componentType;
-              var componentIndex = params.componentIndex;
+              var componentIndex = params.componentIndex; // Special handling for historic reason: when trigger by
+              // markLine/markPoint/markArea, the componentType is
+              // 'markLine'/'markPoint'/'markArea', but we should better
+              // enable them to be queried by seriesIndex, since their
+              // option is set in each series.
 
               if (componentType === 'markLine' || componentType === 'markPoint' || componentType === 'markArea') {
                 componentType = 'series';
@@ -24497,6 +27827,9 @@
               var view = model && _this[model.mainType === 'series' ? '_chartsMap' : '_componentsMap'][model.__viewId];
 
               if ("development" !== 'production') {
+                // `event.componentType` and `event[componentTpype + 'Index']` must not
+                // be missed, otherwise there is no way to distinguish source component.
+                // See `dataFormat.getDataParams`.
                 if (!isGlobalOut && !(model && view)) {
                   console.warn('model or view can not be found by params');
                 }
@@ -24513,7 +27846,12 @@
 
               _this.trigger(eveName, params);
             }
-          };
+          }; // Consider that some component (like tooltip, brush, ...)
+          // register zr event handler, but user event handler might
+          // do anything, such as call `setOption` or `dispatchAction`,
+          // which probably update any of the content and probably
+          // cause problem if it is called previous other inner handlers.
+
 
           handler.zrEventfulCallAtLast = true;
 
@@ -24523,7 +27861,9 @@
           _this._messageCenter.on(eventType, function (event) {
             this.trigger(eventType, event);
           }, _this);
-        });
+        }); // Extra events
+        // TODO register?
+
         each$3(['selectchanged'], function (eventType) {
           _this._messageCenter.on(eventType, function (event) {
             this.trigger(eventType, event);
@@ -24562,12 +27902,16 @@
         });
         each$3(this._chartsViews, function (chart) {
           chart.dispose(ecModel, api);
-        });
+        }); // Dispose after all views disposed
 
         this._zr.dispose();
 
         delete instances$1[this.id];
       };
+      /**
+       * Resize the chart
+       */
+
 
       ECharts.prototype.resize = function (opts) {
         if ("development" !== 'production') {
@@ -24581,7 +27925,8 @@
 
         this._zr.resize(opts);
 
-        var ecModel = this._model;
+        var ecModel = this._model; // Resize loading effect
+
         this._loadingFX && this._loadingFX.resize();
 
         if (!ecModel) {
@@ -24595,6 +27940,7 @@
         updateMethods.update.call(this, {
           type: 'resize',
           animation: {
+            // Disable animation
             duration: 0
           }
         });
@@ -24630,6 +27976,10 @@
         this._loadingFX = el;
         zr.add(el);
       };
+      /**
+       * Hide loading effect
+       */
+
 
       ECharts.prototype.hideLoading = function () {
         if (this._disposed) {
@@ -24646,6 +27996,16 @@
         payload.type = eventActionMap[eventObj.type];
         return payload;
       };
+      /**
+       * @param opt If pass boolean, means opt.silent
+       * @param opt.silent Default `false`. Whether trigger events.
+       * @param opt.flush Default `undefined`.
+       *        true: Flush immediately, and then pixel in canvas can be fetched
+       *            immediately. Caution: it might affect performance.
+       *        false: Not flush.
+       *        undefined: Auto decide whether perform flush.
+       */
+
 
       ECharts.prototype.dispatchAction = function (payload, opt) {
         if (this._disposed) {
@@ -24661,11 +28021,13 @@
 
         if (!actions[payload.type]) {
           return;
-        }
+        } // Avoid dispatch action before setOption. Especially in `connect`.
+
 
         if (!this._model) {
           return;
-        }
+        } // May dispatchAction in rendering procedure
+
 
         if (this[IN_MAIN_PROCESS_KEY]) {
           this._pendingActions.push(payload);
@@ -24680,6 +28042,11 @@
         if (flush) {
           this._zr.flush();
         } else if (flush !== false && env.browser.weChat) {
+          // In WeChat embeded browser, `requestAnimationFrame` and `setInterval`
+          // hang when sliding page (on touch event), which cause that zr does not
+          // refresh util user interaction finished, which is not expected.
+          // But `dispatchAction` may be called too frequently when pan on touch
+          // screen, which impacts performance if do not throttle them.
           this._throttledZrFlush();
         }
 
@@ -24708,10 +28075,19 @@
           assert$1(params.data && seriesModel);
         }
 
-        seriesModel.appendData(params);
+        seriesModel.appendData(params); // Note: `appendData` does not support that update extent of coordinate
+        // system, util some scenario require that. In the expected usage of
+        // `appendData`, the initial extent of coordinate system should better
+        // be fixed by axis `min`/`max` setting or initial data, otherwise if
+        // the extent changed while `appendData`, the location of the painted
+        // graphic elements have to be changed, which make the usage of
+        // `appendData` meaningless.
+
         this._scheduler.unfinished = true;
         this.getZr().wakeUp();
-      };
+      }; // A work around for no `internal` modifier in ts yet but
+      // need to strictly hide private methods to JS users.
+
 
       ECharts.internalField = function () {
         prepare = function (ecIns) {
@@ -24722,6 +28098,10 @@
           prepareView(ecIns, false);
           scheduler.plan();
         };
+        /**
+         * Prepare view instances of charts and components
+         */
+
 
         prepareView = function (ecIns, isComponent) {
           var ecModel = ecIns._model;
@@ -24740,14 +28120,27 @@
           }) : ecModel.eachSeries(doPrepare);
 
           function doPrepare(model) {
-            var requireNewView = model.__requireNewView;
-            model.__requireNewView = false;
+            // By defaut view will be reused if possible for the case that `setOption` with "notMerge"
+            // mode and need to enable transition animation. (Usually, when they have the same id, or
+            // especially no id but have the same type & name & index. See the `model.id` generation
+            // rule in `makeIdAndName` and `viewId` generation rule here).
+            // But in `replaceMerge` mode, this feature should be able to disabled when it is clear that
+            // the new model has nothing to do with the old model.
+            var requireNewView = model.__requireNewView; // This command should not work twice.
+
+            model.__requireNewView = false; // Consider: id same and type changed.
+
             var viewId = '_ec_' + model.id + '_' + model.type;
             var view = !requireNewView && viewMap[viewId];
 
             if (!view) {
               var classType = parseClassType(model.type);
-              var Clazz = isComponent ? ComponentView.getClass(classType.main, classType.sub) : ChartView.getClass(classType.sub);
+              var Clazz = isComponent ? ComponentView.getClass(classType.main, classType.sub) : // FIXME:TS
+              // (ChartView as ChartViewConstructor).getClass('series', classType.sub)
+              // For backward compat, still support a chart type declared as only subType
+              // like "liquidfill", but recommend "series.liquidfill"
+              // But need a base class to make a type series.
+              ChartView.getClass(classType.sub);
 
               if ("development" !== 'production') {
                 assert$1(Clazz, classType.sub + ' does not exist.');
@@ -24792,9 +28185,12 @@
 
         updateDirectly = function (ecIns, method, payload, mainType, subType) {
           var ecModel = ecIns._model;
-          ecModel.setUpdatePayload(payload);
+          ecModel.setUpdatePayload(payload); // broadcast
 
           if (!mainType) {
+            // FIXME
+            // Chart will not be update directly here, except set dirty.
+            // But there is no such scenario now.
             each$3([].concat(ecIns._componentsViews).concat(ecIns._chartsViews), callView);
             return;
           }
@@ -24807,7 +28203,8 @@
             mainType: mainType,
             query: query
           };
-          subType && (condition.subType = subType);
+          subType && (condition.subType = subType); // subType may be '' by parseClassType;
+
           var excludeSeriesId = payload.excludeSeriesId;
           var excludeSeriesIdMap;
 
@@ -24820,7 +28217,8 @@
                 excludeSeriesIdMap.set(modelId, true);
               }
             });
-          }
+          } // If dispatchAction before setOption, do nothing.
+
 
           ecModel && ecModel.eachComponent(condition, function (model) {
             if (!excludeSeriesIdMap || excludeSeriesIdMap.get(model.id) == null) {
@@ -24829,6 +28227,7 @@
                   toggleSeriesBlurStateFromPayload(model, payload, ecIns._api);
                 }
               } else if (isSelectChangePayload(payload)) {
+                // TODO geo
                 if (model instanceof SeriesModel) {
                   toggleSelectionFromPayload(model, payload, ecIns._api);
                   updateSeriesElementSelection(model);
@@ -24851,11 +28250,12 @@
             updateMethods.update.call(this, payload);
           },
           update: function (payload) {
+            // console.profile && console.profile('update');
             var ecModel = this._model;
             var api = this._api;
             var zr = this._zr;
             var coordSysMgr = this._coordSysMgr;
-            var scheduler = this._scheduler;
+            var scheduler = this._scheduler; // update before setOption
 
             if (!ecModel) {
               return;
@@ -24863,16 +28263,29 @@
 
             ecModel.setUpdatePayload(payload);
             scheduler.restoreData(ecModel, payload);
-            scheduler.performSeriesTasks(ecModel);
+            scheduler.performSeriesTasks(ecModel); // TODO
+            // Save total ecModel here for undo/redo (after restoring data and before processing data).
+            // Undo (restoration of total ecModel) can be carried out in 'action' or outside API call.
+            // Create new coordinate system each update
+            // In LineView may save the old coordinate system and use it to get the orignal point
+
             coordSysMgr.create(ecModel, api);
-            scheduler.performDataProcessorTasks(ecModel, payload);
-            updateStreamModes(this, ecModel);
+            scheduler.performDataProcessorTasks(ecModel, payload); // Current stream render is not supported in data process. So we can update
+            // stream modes after data processing, where the filtered data is used to
+            // deteming whether use progressive rendering.
+
+            updateStreamModes(this, ecModel); // We update stream modes before coordinate system updated, then the modes info
+            // can be fetched when coord sys updating (consider the barGrid extent fix). But
+            // the drawback is the full coord info can not be fetched. Fortunately this full
+            // coord is not requied in stream mode updater currently.
+
             coordSysMgr.update(ecModel, api);
             clearColorPalette(ecModel);
             scheduler.performVisualTasks(ecModel, payload);
-            render(this, ecModel, api, payload);
+            render(this, ecModel, api, payload); // Set background
+
             var backgroundColor = ecModel.get('backgroundColor') || 'transparent';
-            var darkMode = ecModel.get('darkMode');
+            var darkMode = ecModel.get('darkMode'); // In IE8
 
             if (!env.canvasSupported) {
               var colorArr = parse(backgroundColor);
@@ -24882,26 +28295,27 @@
                 backgroundColor = 'transparent';
               }
             } else {
-              zr.setBackgroundColor(backgroundColor);
+              zr.setBackgroundColor(backgroundColor); // Force set dark mode.
 
               if (darkMode != null && darkMode !== 'auto') {
                 zr.setDarkMode(darkMode);
               }
             }
 
-            performPostUpdateFuncs(ecModel, api);
+            performPostUpdateFuncs(ecModel, api); // console.profile && console.profileEnd('update');
           },
           updateTransform: function (payload) {
             var _this = this;
 
             var ecModel = this._model;
-            var api = this._api;
+            var api = this._api; // update before setOption
 
             if (!ecModel) {
               return;
             }
 
-            ecModel.setUpdatePayload(payload);
+            ecModel.setUpdatePayload(payload); // ChartView.markUpdateMethod(payload, 'updateTransform');
+
             var componentDirtyList = [];
             ecModel.eachComponent(function (componentType, componentModel) {
               if (componentType === 'series') {
@@ -24930,18 +28344,21 @@
                 seriesDirtyMap.set(seriesModel.uid, 1);
               }
             });
-            clearColorPalette(ecModel);
+            clearColorPalette(ecModel); // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline.
+            // this._scheduler.performVisualTasks(ecModel, payload, 'layout', true);
 
             this._scheduler.performVisualTasks(ecModel, payload, {
               setDirty: true,
               dirtyMap: seriesDirtyMap
-            });
+            }); // Currently, not call render of components. Geo render cost a lot.
+            // renderComponents(ecIns, ecModel, api, payload, componentDirtyList);
+
 
             renderSeries(this, ecModel, api, payload, seriesDirtyMap);
             performPostUpdateFuncs(ecModel, this._api);
           },
           updateView: function (payload) {
-            var ecModel = this._model;
+            var ecModel = this._model; // update before setOption
 
             if (!ecModel) {
               return;
@@ -24949,7 +28366,7 @@
 
             ecModel.setUpdatePayload(payload);
             ChartView.markUpdateMethod(payload, 'updateView');
-            clearColorPalette(ecModel);
+            clearColorPalette(ecModel); // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline.
 
             this._scheduler.performVisualTasks(ecModel, payload, {
               setDirty: true
@@ -24959,20 +28376,23 @@
             performPostUpdateFuncs(ecModel, this._api);
           },
           updateVisual: function (payload) {
+            // updateMethods.update.call(this, payload);
             var _this = this;
 
-            var ecModel = this._model;
+            var ecModel = this._model; // update before setOption
 
             if (!ecModel) {
               return;
             }
 
-            ecModel.setUpdatePayload(payload);
+            ecModel.setUpdatePayload(payload); // clear all visual
+
             ecModel.eachSeries(function (seriesModel) {
               seriesModel.getData().clearAllVisual();
-            });
+            }); // Perform visual
+
             ChartView.markUpdateMethod(payload, 'updateVisual');
-            clearColorPalette(ecModel);
+            clearColorPalette(ecModel); // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline.
 
             this._scheduler.performVisualTasks(ecModel, payload, {
               visualType: 'visual',
@@ -25044,7 +28464,7 @@
           var cptType = cptTypeTmp[0] != null && parseClassType(cptTypeTmp[0]);
           this[IN_MAIN_PROCESS_KEY] = true;
           var payloads = [payload];
-          var batched = false;
+          var batched = false; // Batch action
 
           if (payload.batch) {
             batched = true;
@@ -25060,13 +28480,18 @@
           var isSelectChange = isSelectChangePayload(payload);
           var isStatusChange = isHighDownPayload(payload) || isSelectChange;
           each$3(payloads, function (batchItem) {
-            eventObj = actionWrap.action(batchItem, _this._model, _this._api);
-            eventObj = eventObj || extend({}, batchItem);
+            // Action can specify the event by return it.
+            eventObj = actionWrap.action(batchItem, _this._model, _this._api); // Emit event outside
+
+            eventObj = eventObj || extend({}, batchItem); // Convert type to eventType
+
             eventObj.type = actionInfo.event || eventObj.type;
-            eventObjBatch.push(eventObj);
+            eventObjBatch.push(eventObj); // light update does not perform data process, layout and visual.
 
             if (isStatusChange) {
-              updateDirectly(_this, updateMethod, batchItem, 'series');
+              // method, payload, mainType, subType
+              updateDirectly(_this, updateMethod, batchItem, 'series'); // Mark status to update
+
               markStatusToUpdate(_this);
             } else if (cptType) {
               updateDirectly(_this, updateMethod, batchItem, cptType.main, cptType.sub);
@@ -25074,6 +28499,7 @@
           });
 
           if (updateMethod !== 'none' && !isStatusChange && !cptType) {
+            // Still dirty
             if (this[OPTION_UPDATED_KEY]) {
               prepare(this);
               updateMethods.update.call(this, payload);
@@ -25081,7 +28507,8 @@
             } else {
               updateMethods[updateMethod].call(this, payload);
             }
-          }
+          } // Follow the rule of action batch
+
 
           if (batched) {
             eventObj = {
@@ -25097,7 +28524,7 @@
 
           if (!silent) {
             var messageCenter = this._messageCenter;
-            messageCenter.trigger(eventObj.type, eventObj);
+            messageCenter.trigger(eventObj.type, eventObj); // Extra triggered 'selectchanged' event
 
             if (isSelectChange) {
               var newObj = {
@@ -25125,12 +28552,31 @@
         triggerUpdatedEvent = function (silent) {
           !silent && this.trigger('updated');
         };
+        /**
+         * Event `rendered` is triggered when zr
+         * rendered. It is useful for realtime
+         * snapshot (reflect animation).
+         *
+         * Event `finished` is triggered when:
+         * (1) zrender rendering finished.
+         * (2) initial animation finished.
+         * (3) progressive rendering finished.
+         * (4) no pending action.
+         * (5) no delayed setOption needs to be processed.
+         */
+
 
         bindRenderedEvent = function (zr, ecIns) {
           zr.on('rendered', function (params) {
-            ecIns.trigger('rendered', params);
-
-            if (zr.animation.isFinished() && !ecIns[OPTION_UPDATED_KEY] && !ecIns._scheduler.unfinished && !ecIns._pendingActions.length) {
+            ecIns.trigger('rendered', params); // The `finished` event should not be triggered repeatly,
+            // so it should only be triggered when rendering indeed happend
+            // in zrender. (Consider the case that dipatchAction is keep
+            // triggering when mouse move).
+
+            if ( // Although zr is dirty if initial animation is not finished
+            // and this checking is called on frame, we also check
+            // animation finished for robustness.
+            zr.animation.isFinished() && !ecIns[OPTION_UPDATED_KEY] && !ecIns._scheduler.unfinished && !ecIns._pendingActions.length) {
               ecIns.trigger('finished');
             }
           });
@@ -25142,7 +28588,9 @@
             var dispatcher = findEventDispatcher(el, isHighDownDispatcher);
 
             if (dispatcher) {
-              var ecData = getECData(dispatcher);
+              var ecData = getECData(dispatcher); // Try blur all in the related series. Then emphasis the hoverred.
+              // TODO. progressive mode.
+
               toggleSeriesBlurState(ecData.seriesIndex, ecData.focus, ecData.blurScope, ecIns._api, true);
               enterEmphasisWhenMouseOver(dispatcher, e);
               markStatusToUpdate(ecIns);
@@ -25190,7 +28638,8 @@
           each$3(ecIns._chartsViews, function (chart) {
             chart.__alive = false;
           });
-          renderSeries(ecIns, ecModel, api, payload);
+          renderSeries(ecIns, ecModel, api, payload); // Remove groups of unrendered charts
+
           each$3(ecIns._chartsViews, function (chart) {
             if (!chart.__alive) {
               chart.remove(ecModel, api);
@@ -25207,8 +28656,13 @@
             updateStates(componentModel, componentView);
           });
         };
+        /**
+         * Render each chart and component
+         */
+
 
         renderSeries = function (ecIns, ecModel, api, payload, dirtyMap) {
+          // Render all charts
           var scheduler = ecIns._scheduler;
           var labelManager = ecIns._labelManager;
           labelManager.clearLabels();
@@ -25217,7 +28671,8 @@
             var chartView = ecIns._chartsMap[seriesModel.__viewId];
             chartView.__alive = true;
             var renderTask = chartView.renderTask;
-            scheduler.updatePayload(renderTask, payload);
+            scheduler.updatePayload(renderTask, payload); // TODO states on marker.
+
             clearStates(seriesModel, chartView);
 
             if (dirtyMap && dirtyMap.get(seriesModel.uid)) {
@@ -25229,9 +28684,13 @@
             }
 
             seriesModel.__transientTransitionOpt = null;
-            chartView.group.silent = !!seriesModel.get('silent');
+            chartView.group.silent = !!seriesModel.get('silent'); // Should not call markRedraw on group, because it will disable zrender
+            // increamental render (alway render from the __startIndex each frame)
+            // chartView.group.markRedraw();
+
             updateBlend(seriesModel, chartView);
-            updateSeriesElementSelection(seriesModel);
+            updateSeriesElementSelection(seriesModel); // Add labels.
+
             labelManager.addLabelsOfSeries(chartView);
           });
           scheduler.unfinished = unfinished || scheduler.unfinished;
@@ -25239,10 +28698,14 @@
           labelManager.layout(api);
           labelManager.processLabelsOverall();
           ecModel.eachSeries(function (seriesModel) {
-            var chartView = ecIns._chartsMap[seriesModel.__viewId];
-            updateZ(seriesModel, chartView);
+            var chartView = ecIns._chartsMap[seriesModel.__viewId]; // Update Z after labels updated. Before applying states.
+
+            updateZ(seriesModel, chartView); // NOTE: Update states after label is updated.
+            // label should be in normal status when layouting.
+
             updateStates(seriesModel, chartView);
-          });
+          }); // If use hover layer
+
           updateHoverLayerStatus(ecIns, ecModel);
         };
 
@@ -25253,7 +28716,8 @@
         };
 
         markStatusToUpdate = function (ecIns) {
-          ecIns[STATUS_NEEDS_UPDATE_KEY] = true;
+          ecIns[STATUS_NEEDS_UPDATE_KEY] = true; // Wake up zrender if it's sleep. Let it update states in the next frame.
+
           ecIns.getZr().wakeUp();
         };
 
@@ -25263,6 +28727,7 @@
           }
 
           ecIns.getZr().storage.traverse(function (el) {
+            // Not applied on removed elements, it may still in fading.
             if (isElementRemoved(el)) {
               return;
             }
@@ -25274,7 +28739,7 @@
 
         function applyElementStates(el) {
           var newStates = [];
-          var oldStates = el.currentStates;
+          var oldStates = el.currentStates; // Keep other states.
 
           for (var i = 0; i < oldStates.length; i++) {
             var stateName = oldStates[i];
@@ -25282,7 +28747,8 @@
             if (!(stateName === 'emphasis' || stateName === 'blur' || stateName === 'select')) {
               newStates.push(stateName);
             }
-          }
+          } // Only use states when it's exists.
+
 
           if (el.selected && el.states.select) {
             newStates.push('select');
@@ -25325,6 +28791,9 @@
             });
           }
         }
+        /**
+         * Update chart and blend.
+         */
 
         function updateBlend(seriesModel, chartView) {
           var blendMode = seriesModel.get('blendMode') || null;
@@ -25336,7 +28805,9 @@
           }
 
           chartView.group.traverse(function (el) {
+            // FIXME marker and other components
             if (!el.isGroup) {
+              // DONT mark the element dirty. In case element is incremental and don't wan't to rerender.
               el.style.blend = blendMode;
             }
 
@@ -25354,17 +28825,21 @@
           }
 
           var z = model.get('z');
-          var zlevel = model.get('zlevel');
+          var zlevel = model.get('zlevel'); // Set z and zlevel
+
           view.group.traverse(function (el) {
             if (!el.isGroup) {
               z != null && (el.z = z);
-              zlevel != null && (el.zlevel = zlevel);
+              zlevel != null && (el.zlevel = zlevel); // TODO if textContent is on group.
+
               var label = el.getTextContent();
               var labelLine = el.getTextGuideLine();
 
               if (label) {
                 label.z = el.z;
-                label.zlevel = el.zlevel;
+                label.zlevel = el.zlevel; // lift z2 of text content
+                // TODO if el.emphasis.z2 is spcefied, what about textContent.
+
                 label.z2 = el.z2 + 2;
               }
 
@@ -25377,9 +28852,11 @@
             }
           });
         }
+        // TODO States on component.
 
         function clearStates(model, view) {
           view.group.traverse(function (el) {
+            // Not applied on removed elements, it may still in fading.
             if (isElementRemoved(el)) {
               return;
             }
@@ -25397,7 +28874,8 @@
 
             if (textGuide && textGuide.stateTransition) {
               textGuide.stateTransition = null;
-            }
+            } // TODO If el is incremental.
+
 
             if (el.hasState()) {
               el.prevStates = el.currentStates;
@@ -25415,30 +28893,35 @@
           var stateTransition = duration > 0 ? {
             duration: duration,
             delay: stateAnimationModel.get('delay'),
-            easing: stateAnimationModel.get('easing')
+            easing: stateAnimationModel.get('easing') // additive: stateAnimationModel.get('additive')
+
           } : null;
           view.group.traverse(function (el) {
             if (el.states && el.states.emphasis) {
+              // Not applied on removed elements, it may still in fading.
               if (isElementRemoved(el)) {
                 return;
               }
 
               if (el instanceof Path) {
                 savePathStates(el);
-              }
+              } // Only updated on changed element. In case element is incremental and don't wan't to rerender.
+              // TODO, a more proper way?
+
 
               if (el.__dirty) {
-                var prevStates = el.prevStates;
+                var prevStates = el.prevStates; // Restore states without animation
 
                 if (prevStates) {
                   el.useStates(prevStates);
                 }
-              }
+              } // Update state transition and enable animation again.
+
 
               if (enableAnimation) {
                 el.stateTransition = stateTransition;
                 var textContent = el.getTextContent();
-                var textGuide = el.getTextGuideLine();
+                var textGuide = el.getTextGuideLine(); // TODO Is it necessary to animate label?
 
                 if (textContent) {
                   textContent.stateTransition = stateTransition;
@@ -25447,7 +28930,8 @@
                 if (textGuide) {
                   textGuide.stateTransition = stateTransition;
                 }
-              }
+              } // The use higlighted and selected flag to toggle states.
+
 
               if (el.__dirty) {
                 applyElementStates(el);
@@ -25457,7 +28941,9 @@
         }
 
         createExtensionAPI = function (ecIns) {
-          return new (function (_super) {
+          return new (
+          /** @class */
+          function (_super) {
             __extends(class_1, _super);
 
             function class_1() {
@@ -25602,7 +29088,8 @@
 
             if (errMsg != null) {
               throwError(errMsg);
-            }
+            } // Just a temp solution: mount them on series.
+
 
             toSeries.__transientTransitionOpt = {
               from: fromOpt ? fromOpt.dimension : null,
@@ -25619,6 +29106,10 @@
     var echartsProto = ECharts.prototype;
     echartsProto.on = createRegisterEventWithLowercaseECharts('on');
     echartsProto.off = createRegisterEventWithLowercaseECharts('off');
+    /**
+     * @deprecated
+     */
+    // @ts-ignore
 
     echartsProto.one = function (eventName, cb, ctx) {
       var self = this;
@@ -25631,11 +29122,35 @@
           args2[_i] = arguments[_i];
         }
 
-        cb && cb.apply && cb.apply(this, args2);
+        cb && cb.apply && cb.apply(this, args2); // @ts-ignore
+
         self.off(eventName, wrapped);
       }
+
       this.on.call(this, eventName, wrapped, ctx);
-    };
+    }; // /**
+    //  * Encode visual infomation from data after data processing
+    //  *
+    //  * @param {module:echarts/model/Global} ecModel
+    //  * @param {object} layout
+    //  * @param {boolean} [layoutFilter] `true`: only layout,
+    //  *                                 `false`: only not layout,
+    //  *                                 `null`/`undefined`: all.
+    //  * @param {string} taskBaseTag
+    //  * @private
+    //  */
+    // function startVisualEncoding(ecIns, ecModel, api, payload, layoutFilter) {
+    //     each(visualFuncs, function (visual, index) {
+    //         let isLayout = visual.isLayout;
+    //         if (layoutFilter == null
+    //             || (layoutFilter === false && !isLayout)
+    //             || (layoutFilter === true && isLayout)
+    //         ) {
+    //             visual.func(ecModel, api, payload);
+    //         }
+    //     });
+    // }
+
 
     var MOUSE_EVENT_NAMES = ['click', 'dblclick', 'mouseover', 'mouseout', 'mousemove', 'mousedown', 'mouseup', 'globalout', 'contextmenu'];
 
@@ -25646,6 +29161,10 @@
     }
 
     var actions = {};
+    /**
+     * Map eventType to actionType
+     */
+
     var eventActionMap = {};
     var dataProcessorFuncs = [];
     var optionPreprocessorFuncs = [];
@@ -25659,6 +29178,15 @@
     var idBase = +new Date() - 0;
     var groupIdBase = +new Date() - 0;
     var DOM_ATTRIBUTE_KEY = '_echarts_instance_';
+    /**
+     * @param opts.devicePixelRatio Use window.devicePixelRatio by default
+     * @param opts.renderer Can choose 'canvas' or 'svg' to render the chart.
+     * @param opts.width Use clientWidth of the input `dom` by default.
+     *        Can be 'auto' (the same as null/undefined)
+     * @param opts.height Use clientHeight of the input `dom` by default.
+     *        Can be 'auto' (the same as null/undefined)
+     */
+
     function init$1(dom, theme, opts) {
       if ("development" !== 'production') {
         if (!dom) {
@@ -25692,10 +29220,30 @@
       });
       return chart;
     }
+    /**
+     * @usage
+     * (A)
+     * ```js
+     * let chart1 = echarts.init(dom1);
+     * let chart2 = echarts.init(dom2);
+     * chart1.group = 'xxx';
+     * chart2.group = 'xxx';
+     * echarts.connect('xxx');
+     * ```
+     * (B)
+     * ```js
+     * let chart1 = echarts.init(dom1);
+     * let chart2 = echarts.init(dom2);
+     * echarts.connect('xxx', [chart1, chart2]);
+     * ```
+     */
+
     function connect(groupId) {
+      // Is array of charts
       if (isArray(groupId)) {
         var charts = groupId;
-        groupId = null;
+        groupId = null; // If any chart has group
+
         each$3(charts, function (chart) {
           if (chart.group != null) {
             groupId = chart.group;
@@ -25710,14 +29258,27 @@
       connectedGroups[groupId] = true;
       return groupId;
     }
+    /**
+     * @deprecated
+     */
+
     function disConnect(groupId) {
       connectedGroups[groupId] = false;
     }
+    /**
+     * Alias and backword compat
+     */
+
     var disconnect = disConnect;
+    /**
+     * Dispose a chart instance
+     */
+
     function dispose$1(chart) {
       if (typeof chart === 'string') {
         chart = instances$1[chart];
       } else if (!(chart instanceof ECharts)) {
+        // Try to treat as dom
         chart = getInstanceByDom(chart);
       }
 
@@ -25731,9 +29292,17 @@
     function getInstanceById(key) {
       return instances$1[key];
     }
+    /**
+     * Register theme
+     */
+
     function registerTheme(name, theme) {
       themeStorage[name] = theme;
     }
+    /**
+     * Register option preprocessor
+     */
+
     function registerPreprocessor(preprocessorFunc) {
       if (indexOf$1(optionPreprocessorFuncs, preprocessorFunc) < 0) {
         optionPreprocessorFuncs.push(preprocessorFunc);
@@ -25742,11 +29311,21 @@
     function registerProcessor(priority, processor) {
       normalizeRegister(dataProcessorFuncs, priority, processor, PRIORITY_PROCESSOR_DEFAULT);
     }
... 120519 lines suppressed ...


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org


[echarts] 01/02: fix: tweak chalk for error thrown in the recently version.

Posted by su...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

sushuang pushed a commit to branch release-dev
in repository https://gitbox.apache.org/repos/asf/echarts.git

commit 1ea65868b3f8444cf9c826b5d95932dcd908dfac
Author: 100pah <su...@gmail.com>
AuthorDate: Mon Feb 1 21:17:45 2021 +0800

    fix: tweak chalk for error thrown in the recently version.
---
 build/addHeader.js | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/build/addHeader.js b/build/addHeader.js
index 61d21e7..ac948ad 100644
--- a/build/addHeader.js
+++ b/build/addHeader.js
@@ -88,11 +88,11 @@ function run() {
     if (passFiles.length) {
         if (isVerbose) {
             passFiles.forEach(function (path) {
-                console.log(chalk.green.dim(path));
+                console.log(chalk.green(path));
             });
         }
         else {
-            console.log(chalk.green.dim(passFiles.length + ' files. (use argument "--verbose" see details)'));
+            console.log(chalk.green(passFiles.length + ' files. (use argument "--verbose" see details)'));
         }
     }
     else {
@@ -105,7 +105,7 @@ function run() {
     console.log('--------------------');
     if (updatedFiles.length) {
         updatedFiles.forEach(function (path) {
-            console.log(chalk.green.bright(path));
+            console.log(chalk.green(path));
         });
     }
     else {
@@ -118,7 +118,7 @@ function run() {
     console.log('----------------');
     if (pendingFiles.length) {
         pendingFiles.forEach(function (path) {
-            console.log(chalk.red.dim(path));
+            console.log(chalk.red(path));
         });
     }
     else {


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org