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:32 UTC

[echarts] 02/02: release: 5.0.2

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