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(' ') + '</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