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/08/28 09:36:40 UTC
[echarts] 01/01: release 5.2.0
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 fd9bf4affc8e0b59563340969aa1c158314f973f
Author: sushuang <su...@gmail.com>
AuthorDate: Sat Aug 28 17:34:19 2021 +0800
release 5.2.0
---
dist/echarts.common.js | 12552 ++++++-------
dist/echarts.common.js.map | 2 +-
dist/echarts.common.min.js | 4 +-
dist/echarts.esm.js | 36040 +++++++++++++++++++------------------
dist/echarts.esm.js.map | 2 +-
dist/echarts.esm.min.js | 4 +-
dist/echarts.js | 36041 ++++++++++++++++++++------------------
dist/echarts.js.map | 2 +-
dist/echarts.min.js | 4 +-
dist/echarts.simple.js | 11727 +++++++------
dist/echarts.simple.js.map | 2 +-
dist/echarts.simple.min.js | 4 +-
package-lock.json | 8 +-
package.json | 4 +-
src/core/echarts.ts | 4 +-
src/label/installLabelLayout.ts | 20 +
src/label/sectorLabel.ts | 20 +
17 files changed, 50780 insertions(+), 45660 deletions(-)
diff --git a/dist/echarts.common.js b/dist/echarts.common.js
index 3aa78e6..2036b13 100644
--- a/dist/echarts.common.js
+++ b/dist/echarts.common.js
@@ -48,6 +48,8 @@
};
function __extends(d, b) {
+ if (typeof b !== "function" && b !== null)
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
@@ -64,12 +66,14 @@
return __assign.apply(this, arguments);
};
- function __spreadArrays() {
- for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
- for (var r = Array(s), k = 0, i = 0; i < il; i++)
- for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
- r[k] = a[j];
- return r;
+ function __spreadArray(to, from, pack) {
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
+ if (ar || !(i in from)) {
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
+ ar[i] = from[i];
+ }
+ }
+ return to.concat(ar || from);
}
var Browser = (function () {
@@ -1026,13 +1030,16 @@
return this;
};
Eventful.prototype.triggerWithContext = function (type) {
+ var args = [];
+ for (var _i = 1; _i < arguments.length; _i++) {
+ args[_i - 1] = arguments[_i];
+ }
if (!this._$handlers) {
return this;
}
var _h = this._$handlers[type];
var eventProcessor = this._$eventProcessor;
if (_h) {
- var args = arguments;
var argLen = args.length;
var ctx = args[argLen - 1];
var len = _h.length;
@@ -3291,7 +3298,10 @@
}
};
Track.prototype.needsAnimate = function () {
- return !this._isAllValueEqual && this.keyframes.length >= 2 && this.interpolable;
+ return !this._isAllValueEqual
+ && this.keyframes.length >= 2
+ && this.interpolable
+ && this.maxTime > 0;
};
Track.prototype.getAdditiveTrack = function () {
return this._additiveTrack;
@@ -3397,8 +3407,8 @@
for (var i = 0; i < kfsLen; i++) {
if (arrDim === 0) {
if (this.isValueColor) {
- kfs[i].additiveValue
- = add1DArray([], kfs[i].value, startValue, -1);
+ kfs[i].additiveValue =
+ add1DArray([], kfs[i].value, startValue, -1);
}
else {
kfs[i].additiveValue = kfs[i].value - startValue;
@@ -3624,7 +3634,7 @@
Animator.prototype._doneCallback = function () {
this._setTracksFinished();
this._clip = null;
- var doneList = this._doneList;
+ var doneList = this._doneCbs;
if (doneList) {
var len = doneList.length;
for (var i = 0; i < len; i++) {
@@ -3635,7 +3645,7 @@
Animator.prototype._abortedCallback = function () {
this._setTracksFinished();
var animation = this.animation;
- var abortedList = this._abortedList;
+ var abortedList = this._abortedCbs;
if (animation) {
animation.removeClip(this._clip);
}
@@ -3712,7 +3722,7 @@
for (var i = 0; i < tracks.length; i++) {
tracks[i].step(self._target, percent);
}
- var onframeList = self._onframeList;
+ var onframeList = self._onframeCbs;
if (onframeList) {
for (var i = 0; i < onframeList.length; i++) {
onframeList[i](self._target, percent);
@@ -3752,28 +3762,28 @@
};
Animator.prototype.during = function (cb) {
if (cb) {
- if (!this._onframeList) {
- this._onframeList = [];
+ if (!this._onframeCbs) {
+ this._onframeCbs = [];
}
- this._onframeList.push(cb);
+ this._onframeCbs.push(cb);
}
return this;
};
Animator.prototype.done = function (cb) {
if (cb) {
- if (!this._doneList) {
- this._doneList = [];
+ if (!this._doneCbs) {
+ this._doneCbs = [];
}
- this._doneList.push(cb);
+ this._doneCbs.push(cb);
}
return this;
};
Animator.prototype.aborted = function (cb) {
if (cb) {
- if (!this._abortedList) {
- this._abortedList = [];
+ if (!this._abortedCbs) {
+ this._abortedCbs = [];
}
- this._abortedList.push(cb);
+ this._abortedCbs.push(cb);
}
return this;
};
@@ -4420,6 +4430,9 @@
var Transformable = (function () {
function Transformable() {
}
+ Transformable.prototype.getLocalTransform = function (m) {
+ return Transformable.getLocalTransform(this, m);
+ };
Transformable.prototype.setPosition = function (arr) {
this.x = arr[0];
this.y = arr[1];
@@ -4444,11 +4457,10 @@
|| isNotAroundZero(this.scaleY - 1);
};
Transformable.prototype.updateTransform = function () {
- var parent = this.parent;
- var parentHasTransform = parent && parent.transform;
+ var parentTransform = this.parent && this.parent.transform;
var needLocalTransform = this.needLocalTransform();
var m = this.transform;
- if (!(needLocalTransform || parentHasTransform)) {
+ if (!(needLocalTransform || parentTransform)) {
m && mIdentity(m);
return;
}
@@ -4459,12 +4471,12 @@
else {
mIdentity(m);
}
- if (parentHasTransform) {
+ if (parentTransform) {
if (needLocalTransform) {
- mul$1(m, parent.transform, m);
+ mul$1(m, parentTransform, m);
}
else {
- copy$1(m, parent.transform);
+ copy$1(m, parentTransform);
}
}
this.transform = m;
@@ -4486,9 +4498,6 @@
this.invTransform = this.invTransform || create$1();
invert(this.invTransform, m);
};
- Transformable.prototype.getLocalTransform = function (m) {
- return Transformable.getLocalTransform(this, m);
- };
Transformable.prototype.getComputedTransform = function () {
var transformNode = this;
var ancestors = [];
@@ -4583,6 +4592,13 @@
? Math.sqrt(abs(m[0] * m[3] - m[2] * m[1]))
: 1;
};
+ Transformable.prototype.copyTransform = function (source) {
+ var target = this;
+ for (var i = 0; i < TRANSFORMABLE_PROPS.length; i++) {
+ var propName = TRANSFORMABLE_PROPS[i];
+ target[propName] = source[propName];
+ }
+ };
Transformable.getLocalTransform = function (target, m) {
m = m || [];
var ox = target.originX || 0;
@@ -4625,6 +4641,9 @@
})();
return Transformable;
}());
+ var TRANSFORMABLE_PROPS = [
+ 'x', 'y', 'originX', 'originY', 'rotation', 'scaleX', 'scaleY', 'skewX', 'skewY'
+ ];
var Point = (function () {
function Point(x, y) {
@@ -5207,24 +5226,13 @@
}
var textConfig = this.textConfig;
var isLocal = textConfig.local;
- var attachedTransform = textEl.attachedTransform;
+ var innerTransformable = textEl.innerTransformable;
var textAlign = void 0;
var textVerticalAlign = void 0;
var textStyleChanged = false;
- if (isLocal) {
- attachedTransform.parent = this;
- }
- else {
- attachedTransform.parent = null;
- }
+ innerTransformable.parent = isLocal ? this : null;
var innerOrigin = false;
- attachedTransform.x = textEl.x;
- attachedTransform.y = textEl.y;
- attachedTransform.originX = textEl.originX;
- attachedTransform.originY = textEl.originY;
- attachedTransform.rotation = textEl.rotation;
- attachedTransform.scaleX = textEl.scaleX;
- attachedTransform.scaleY = textEl.scaleY;
+ innerTransformable.copyTransform(textEl);
if (textConfig.position != null) {
var layoutRect = tmpBoundingRect;
if (textConfig.layoutRect) {
@@ -5242,8 +5250,8 @@
else {
calculateTextPosition(tmpTextPosCalcRes, textConfig, layoutRect);
}
- attachedTransform.x = tmpTextPosCalcRes.x;
- attachedTransform.y = tmpTextPosCalcRes.y;
+ innerTransformable.x = tmpTextPosCalcRes.x;
+ innerTransformable.y = tmpTextPosCalcRes.y;
textAlign = tmpTextPosCalcRes.align;
textVerticalAlign = tmpTextPosCalcRes.verticalAlign;
var textOrigin = textConfig.origin;
@@ -5259,20 +5267,20 @@
relOriginY = parsePercent(textOrigin[1], layoutRect.height);
}
innerOrigin = true;
- attachedTransform.originX = -attachedTransform.x + relOriginX + (isLocal ? 0 : layoutRect.x);
- attachedTransform.originY = -attachedTransform.y + relOriginY + (isLocal ? 0 : layoutRect.y);
+ innerTransformable.originX = -innerTransformable.x + relOriginX + (isLocal ? 0 : layoutRect.x);
+ innerTransformable.originY = -innerTransformable.y + relOriginY + (isLocal ? 0 : layoutRect.y);
}
}
if (textConfig.rotation != null) {
- attachedTransform.rotation = textConfig.rotation;
+ innerTransformable.rotation = textConfig.rotation;
}
var textOffset = textConfig.offset;
if (textOffset) {
- attachedTransform.x += textOffset[0];
- attachedTransform.y += textOffset[1];
+ innerTransformable.x += textOffset[0];
+ innerTransformable.y += textOffset[1];
if (!innerOrigin) {
- attachedTransform.originX = -textOffset[0];
- attachedTransform.originY = -textOffset[1];
+ innerTransformable.originX = -textOffset[0];
+ innerTransformable.originY = -textOffset[1];
}
}
var isInside = textConfig.inside == null
@@ -5719,7 +5727,7 @@
if (textEl.__zr && !textEl.__hostTarget) {
throw new Error('Text element has been added to zrender.');
}
- textEl.attachedTransform = new Transformable();
+ textEl.innerTransformable = new Transformable();
this._attachComponent(textEl);
this._textContent = textEl;
this.markRedraw();
@@ -5738,7 +5746,7 @@
Element.prototype.removeTextContent = function () {
var textEl = this._textContent;
if (textEl) {
- textEl.attachedTransform = null;
+ textEl.innerTransformable = null;
this._detachComponent(textEl);
this._textContent = null;
this._innerTextDefaultStyle = null;
@@ -5794,6 +5802,9 @@
}
};
Element.prototype.addSelfToZr = function (zr) {
+ if (this.__zr === zr) {
+ return;
+ }
this.__zr = zr;
var animators = this.animators;
if (animators) {
@@ -5812,6 +5823,9 @@
}
};
Element.prototype.removeSelfFromZr = function (zr) {
+ if (!this.__zr) {
+ return;
+ }
this.__zr = null;
var animators = this.animators;
if (animators) {
@@ -6204,6 +6218,13 @@
}
return this;
};
+ Group.prototype.replace = function (oldChild, newChild) {
+ var idx = indexOf(this._children, oldChild);
+ if (idx >= 0) {
+ this.replaceAt(newChild, idx);
+ }
+ return this;
+ };
Group.prototype.replaceAt = function (child, index) {
var children = this._children;
var old = children[index];
@@ -6572,7 +6593,7 @@
function registerPainter(name, Ctor) {
painterCtors[name] = Ctor;
}
- var version = '5.1.1';
+ var version = '5.2.0';
var zrender = /*#__PURE__*/Object.freeze({
__proto__: null,
@@ -6891,7 +6912,7 @@
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);
+ return new Date(+match[1], +(match[2] || 1) - 1, +match[3] || 1, +match[4] || 0, +(match[5] || 0), +match[6] || 0, match[7] ? +match[7].substring(0, 3) : 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",
@@ -6906,7 +6927,7 @@
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] ? +match[7].substring(0, 3) : 0));
}
} else if (value == null) {
return new Date(NaN);
@@ -7880,7 +7901,7 @@
for (var i = 0; i < length_1; ++i) {
var info = data.getDimensionInfo(i); // Don't interpolate ordinal dims
- if (info.type === 'ordinal') {
+ if (info && 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 {
@@ -7972,7 +7993,7 @@
superClass.apply(this, arguments);
} else {
var ins = createObject( // @ts-ignore
- ExtendedClass.prototype, new (superClass.bind.apply(superClass, __spreadArrays([void 0], args)))());
+ ExtendedClass.prototype, new (superClass.bind.apply(superClass, __spreadArray([void 0], args)))());
return ins;
}
} else {
@@ -9692,6 +9713,7 @@
return this;
};
PathProxy.prototype.bezierCurveTo = function (x1, y1, x2, y2, x3, y3) {
+ this._drawPendingPt();
this.addData(CMD.C, x1, y1, x2, y2, x3, y3);
if (this._ctx) {
this._needsDash ? this._dashedBezierTo(x1, y1, x2, y2, x3, y3)
@@ -9702,6 +9724,7 @@
return this;
};
PathProxy.prototype.quadraticCurveTo = function (x1, y1, x2, y2) {
+ this._drawPendingPt();
this.addData(CMD.Q, x1, y1, x2, y2);
if (this._ctx) {
this._needsDash ? this._dashedQuadraticTo(x1, y1, x2, y2)
@@ -9712,6 +9735,7 @@
return this;
};
PathProxy.prototype.arc = function (cx, cy, r, startAngle, endAngle, anticlockwise) {
+ this._drawPendingPt();
tmpAngles[0] = startAngle;
tmpAngles[1] = endAngle;
normalizeArcAngles(tmpAngles, anticlockwise);
@@ -9725,12 +9749,14 @@
return this;
};
PathProxy.prototype.arcTo = function (x1, y1, x2, y2, radius) {
+ this._drawPendingPt();
if (this._ctx) {
this._ctx.arcTo(x1, y1, x2, y2, radius);
}
return this;
};
PathProxy.prototype.rect = function (x, y, w, h) {
+ this._drawPendingPt();
this._ctx && this._ctx.rect(x, y, w, h);
this.addData(CMD.R, x, y, w, h);
return this;
@@ -10174,12 +10200,12 @@
x0 = xi;
y0 = yi;
}
+ if (cmd !== CMD.L && pendingPtDist > 0) {
+ ctx.lineTo(pendingPtX, pendingPtY);
+ pendingPtDist = 0;
+ }
switch (cmd) {
case CMD.M:
- if (pendingPtDist > 0) {
- ctx.lineTo(pendingPtX, pendingPtY);
- pendingPtDist = 0;
- }
x0 = xi = d[i++];
y0 = yi = d[i++];
ctx.moveTo(xi, yi);
@@ -10327,10 +10353,6 @@
ctx.rect(x, y, width, height);
break;
case CMD.Z:
- if (pendingPtDist > 0) {
- ctx.lineTo(pendingPtX, pendingPtY);
- pendingPtDist = 0;
- }
if (drawPart) {
var l = pathSegLen[segCount++];
if (accumLength + l > displayedLength) {
@@ -10346,6 +10368,14 @@
}
}
};
+ PathProxy.prototype.clone = function () {
+ var newProxy = new PathProxy();
+ var data = this.data;
+ newProxy.data = data.slice ? data.slice()
+ : Array.prototype.slice.call(data);
+ newProxy._len = this._len;
+ return newProxy;
+ };
PathProxy.CMD = CMD;
PathProxy.initDefaultProps = (function () {
var proto = PathProxy.prototype;
@@ -10914,10 +10944,16 @@
}
}
};
- Path.prototype.buildPath = function (ctx, shapeCfg, inBundle) { };
+ Path.prototype.buildPath = function (ctx, shapeCfg, inBatch) { };
Path.prototype.pathUpdated = function () {
this.__dirty &= ~SHAPE_CHANGED_BIT;
};
+ Path.prototype.getUpdatedPathProxy = function (inBatch) {
+ !this.path && this.createPathProxy();
+ this.path.beginPath();
+ this.buildPath(this.path, this.shape, inBatch);
+ return this.path;
+ };
Path.prototype.createPathProxy = function () {
this.path = new PathProxy(false);
};
@@ -11513,6 +11549,7 @@
return this._children;
};
ZRText.prototype.update = function () {
+ _super.prototype.update.call(this);
if (this.styleChanged()) {
this._updateSubTexts();
}
@@ -11525,29 +11562,31 @@
child.cursor = this.cursor;
child.invisible = this.invisible;
}
- var attachedTransform = this.attachedTransform;
- if (attachedTransform) {
- attachedTransform.updateTransform();
- var m = attachedTransform.transform;
- if (m) {
- this.transform = this.transform || [];
- copy$1(this.transform, m);
- }
- else {
- this.transform = null;
+ };
+ ZRText.prototype.updateTransform = function () {
+ var innerTransformable = this.innerTransformable;
+ if (innerTransformable) {
+ innerTransformable.updateTransform();
+ if (innerTransformable.transform) {
+ this.transform = innerTransformable.transform;
}
}
else {
- _super.prototype.update.call(this);
+ _super.prototype.updateTransform.call(this);
}
};
+ ZRText.prototype.getLocalTransform = function (m) {
+ var innerTransformable = this.innerTransformable;
+ return innerTransformable
+ ? innerTransformable.getLocalTransform(m)
+ : _super.prototype.getLocalTransform.call(this, m);
+ };
ZRText.prototype.getComputedTransform = function () {
if (this.__hostTarget) {
this.__hostTarget.getComputedTransform();
this.__hostTarget.updateInnerText(true);
}
- return this.attachedTransform ? this.attachedTransform.getComputedTransform()
- : _super.prototype.getComputedTransform.call(this);
+ return _super.prototype.getComputedTransform.call(this);
};
ZRText.prototype._updateSubTexts = function () {
this._childCursor = 0;
@@ -11816,7 +11855,7 @@
var defaultStyle = this._defaultStyle;
var useDefaultFill = false;
var defaultLineWidth = 0;
- var textFill = getStroke('fill' in tokenStyle ? tokenStyle.fill
+ var textFill = getFill('fill' in tokenStyle ? tokenStyle.fill
: 'fill' in style ? style.fill
: (useDefaultFill = true, defaultStyle.fill));
var textStroke = getStroke('stroke' in tokenStyle ? tokenStyle.stroke
@@ -11863,7 +11902,7 @@
var self = this;
var rectEl;
var imgEl;
- if (isPlainOrGradientBg || (textBorderWidth && textBorderColor)) {
+ if (isPlainOrGradientBg || style.lineHeight || (textBorderWidth && textBorderColor)) {
rectEl = this._getOrCreateChild(Rect);
rectEl.useStyle(rectEl.createStyle());
rectEl.style.fill = null;
@@ -11989,10 +12028,30 @@
}
function needDrawBackground(style) {
return !!(style.backgroundColor
+ || style.lineHeight
|| (style.borderWidth && style.borderColor));
}
var getECData = makeInner();
+ var setCommonECData = function (seriesIndex, dataType, dataIdx, el) {
+ if (el) {
+ var ecData = getECData(el); // Add data index and series index for indexing the data by element
+ // Useful in tooltip
+
+ ecData.dataIndex = dataIdx;
+ ecData.dataType = dataType;
+ ecData.seriesIndex = seriesIndex; // TODO: not store dataIndex on children.
+
+ if (el.type === 'group') {
+ el.traverse(function (child) {
+ var childECData = getECData(child);
+ childECData.seriesIndex = seriesIndex;
+ childECData.dataIndex = dataIdx;
+ childECData.dataType = dataType;
+ });
+ }
+ }
+ };
var _highlightNextDigit = 1;
var _highlightKeyMap = {};
@@ -12134,26 +12193,31 @@
var fromStroke = hasSelect ? store.selectStroke || store.normalStroke : store.normalStroke;
if (hasFillOrStroke(fromFill) || hasFillOrStroke(fromStroke)) {
- state = state || {}; // Apply default color lift
+ state = state || {};
+ var emphasisStyle = state.style || {}; // inherit case
- var emphasisStyle = state.style || {};
+ if (emphasisStyle.fill === 'inherit') {
+ cloned = true;
+ state = extend({}, state);
+ emphasisStyle = extend({}, emphasisStyle);
+ emphasisStyle.fill = fromFill;
+ } // Apply default color lift
+ else if (!hasFillOrStroke(emphasisStyle.fill) && hasFillOrStroke(fromFill)) {
+ cloned = true; // Not modify the original value.
- if (!hasFillOrStroke(emphasisStyle.fill) && hasFillOrStroke(fromFill)) {
- cloned = true; // Not modify the original value.
+ state = extend({}, state);
+ emphasisStyle = extend({}, emphasisStyle); // Already being applied 'emphasis'. DON'T lift color multiple times.
- state = extend({}, state);
- emphasisStyle = extend({}, emphasisStyle); // Already being applied 'emphasis'. DON'T lift color multiple times.
-
- emphasisStyle.fill = liftColor(fromFill);
- } // Not highlight stroke if fill has been highlighted.
- else if (!hasFillOrStroke(emphasisStyle.stroke) && hasFillOrStroke(fromStroke)) {
- if (!cloned) {
- state = extend({}, state);
- emphasisStyle = extend({}, emphasisStyle);
- }
+ emphasisStyle.fill = liftColor(fromFill);
+ } // 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;
}
@@ -12686,6 +12750,9 @@
var mathSqrt$2 = Math.sqrt;
var mathAtan2 = Math.atan2;
function transformPath(path, m) {
+ if (!m) {
+ return;
+ }
var data = path.data;
var len = path.len();
var cmd;
@@ -13082,13 +13149,7 @@
var len = pathEls.length;
for (var i = 0; i < len; i++) {
var pathEl = pathEls[i];
- if (!pathEl.path) {
- pathEl.createPathProxy();
- }
- if (pathEl.shapeChanged()) {
- pathEl.buildPath(pathEl.path, pathEl.shape, true);
- }
- pathList.push(pathEl.path);
+ pathList.push(pathEl.getUpdatedPathProxy(true));
}
var pathBundle = new Path(opts);
pathBundle.createPathProxy();
@@ -14116,195 +14177,66 @@
return IncrementalDisplayable;
}(Displayable));
- 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);
- }
+ var transitionStore = makeInner();
/**
- * 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`.
+ * Return null if animation is disabled.
*/
- 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 getAnimationConfig(animationType, animatableModel, dataIndex, // Extra opts can override the option in animatable model.
+ extraOpts, // TODO It's only for pictorial bar now.
+ extraDelayParams) {
+ 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.
- function getShapeClass(name) {
- if (_customShapeMap.hasOwnProperty(name)) {
- return _customShapeMap[name];
+ if (animatableModel && animatableModel.ecModel) {
+ var updatePayload = animatableModel.ecModel.getUpdatePayload();
+ animationPayload = updatePayload && updatePayload.animation;
}
- }
- /**
- * 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);
+ var animationEnabled = animatableModel && animatableModel.isAnimationEnabled();
+ var isUpdate = animationType === 'update';
- if (rect) {
- if (layout === 'center') {
- rect = centerGraphic(rect, path.getBoundingRect());
- }
+ if (animationEnabled) {
+ var duration = void 0;
+ var easing = void 0;
+ var delay = void 0;
- resizePath(path, rect);
- }
+ if (extraOpts) {
+ duration = retrieve2(extraOpts.duration, 200);
+ easing = retrieve2(extraOpts.easing, 'cubicOut');
+ delay = 0;
+ } else {
+ duration = animatableModel.getShallow(isUpdate ? 'animationDurationUpdate' : 'animationDuration');
+ easing = animatableModel.getShallow(isUpdate ? 'animationEasingUpdate' : 'animationEasing');
+ delay = animatableModel.getShallow(isUpdate ? 'animationDelayUpdate' : 'animationDelay');
+ } // animation from payload has highest priority.
- 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: {
- image: imageUrl,
- x: rect.x,
- y: rect.y,
- width: rect.width,
- height: rect.height
- },
- onload: function (img) {
- if (layout === 'center') {
- var boundingRect = {
- width: img.width,
- height: img.height
- };
- zrImg.setStyle(centerGraphic(rect, boundingRect));
- }
+ if (animationPayload) {
+ animationPayload.duration != null && (duration = animationPayload.duration);
+ animationPayload.easing != null && (easing = animationPayload.easing);
+ animationPayload.delay != null && (delay = animationPayload.delay);
}
- });
- 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;
-
- if (width <= rect.width) {
- height = rect.height;
- } else {
- width = rect.width;
- height = width / aspect;
- }
- var cx = rect.x + rect.width / 2;
- var cy = rect.y + rect.height / 2;
- return {
- x: cx - width / 2,
- y: cy - height / 2,
- width: width,
- height: height
- };
- }
+ if (typeof delay === 'function') {
+ delay = delay(dataIndex, extraDelayParams);
+ }
- var mergePath$1 = mergePath;
- /**
- * Resize a path to fit the rect
- * @param path
- * @param rect
- */
+ if (typeof duration === 'function') {
+ duration = duration(dataIndex);
+ }
- function resizePath(path, rect) {
- if (!path.applyTransform) {
- return;
+ var config = {
+ duration: duration || 0,
+ delay: delay,
+ easing: easing
+ };
+ return config;
+ } else {
+ return null;
}
-
- var pathRect = path.getBoundingRect();
- 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) {
var isFrom = false;
@@ -14322,79 +14254,36 @@
dataIndex = dataIndex.dataIndex;
}
- var isUpdate = animationType === 'update';
var isRemove = animationType === 'remove';
- 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();
- animationPayload = updatePayload && updatePayload.animation;
- }
-
- var animationEnabled = animatableModel && animatableModel.isAnimationEnabled();
if (!isRemove) {
// Must stop the remove animation.
el.stopAnimation('remove');
}
- if (animationEnabled) {
- var duration = void 0;
- var animationEasing = void 0;
- var animationDelay = void 0;
-
- if (animationPayload) {
- duration = animationPayload.duration || 0;
- animationEasing = animationPayload.easing || 'cubicOut';
- animationDelay = animationPayload.delay || 0;
- } else if (isRemove) {
- removeOpt = removeOpt || {};
- duration = retrieve2(removeOpt.duration, 200);
- animationEasing = retrieve2(removeOpt.easing, 'cubicOut');
- animationDelay = 0;
- } else {
- duration = animatableModel.getShallow(isUpdate ? 'animationDurationUpdate' : 'animationDuration');
- animationEasing = animatableModel.getShallow(isUpdate ? 'animationEasingUpdate' : 'animationEasing');
- animationDelay = animatableModel.getShallow(isUpdate ? 'animationDelayUpdate' : 'animationDelay');
- }
-
- if (typeof animationDelay === 'function') {
- animationDelay = animationDelay(dataIndex, animatableModel.getAnimationDelayParams ? animatableModel.getAnimationDelayParams(el, dataIndex) : null);
- }
-
- if (typeof duration === 'function') {
- duration = duration(dataIndex);
- }
+ var animationConfig = getAnimationConfig(animationType, animatableModel, dataIndex, isRemove ? removeOpt || {} : null, animatableModel && animatableModel.getAnimationDelayParams ? animatableModel.getAnimationDelayParams(el, dataIndex) : null);
- duration > 0 ? isFrom ? el.animateFrom(props, {
+ if (animationConfig && animationConfig.duration > 0) {
+ var duration = animationConfig.duration;
+ var animationDelay = animationConfig.delay;
+ var animationEasing = animationConfig.easing;
+ var animateConfig = {
duration: duration,
delay: animationDelay || 0,
easing: animationEasing,
done: cb,
force: !!cb || !!during,
+ // Set to final state in update/init animation.
+ // So the post processing based on the path shape can be done correctly.
+ setToFinal: !isRemove,
scope: animationType,
during: during
- }) : el.animateTo(props, {
- duration: duration,
- delay: animationDelay || 0,
- easing: animationEasing,
- done: cb,
- force: !!cb || !!during,
- setToFinal: true,
- scope: animationType,
- during: during
- }) : ( // 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());
+ };
+ isFrom ? el.animateFrom(props, animateConfig) : el.animateTo(props, animateConfig);
} else {
- el.stopAnimation();
- !isFrom && el.attr(props); // Call during once.
+ el.stopAnimation(); // If `isFrom`, the props is the "from" props.
+
+ !isFrom && el.attr(props); // Call during at least once.
during && during(1);
cb && cb();
@@ -14435,6 +14324,26 @@
animateOrSetProps('init', el, props, animatableModel, dataIndex, cb, during);
}
/**
+ * If element is removed.
+ * It can determine if element is having remove animation.
+ */
+
+ function isElementRemoved(el) {
+ if (!el.__zr) {
+ return true;
+ }
+
+ for (var i = 0; i < el.animators.length; i++) {
+ var animator = el.animators[i];
+
+ if (animator.scope === 'remove') {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ /**
* Remove graphic element
*/
@@ -14476,26 +14385,206 @@
}
}
/**
- * If element is removed.
- * It can determine if element is having remove animation.
+ * Save old style for style transition in universalTransition module.
+ * It's used when element will be reused in each render.
+ * For chart like map, heatmap, which will always create new element.
+ * We don't need to save this because universalTransition can get old style from the old element
*/
- function isElementRemoved(el) {
- if (!el.__zr) {
- return true;
+ function saveOldStyle(el) {
+ transitionStore(el).oldStyle = el.style;
+ }
+
+ 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
+ */
- for (var i = 0; i < el.animators.length; i++) {
- var animator = el.animators[i];
+ function makePath(pathData, opts, rect, layout) {
+ var path = createFromString(pathData, opts);
- if (animator.scope === 'remove') {
- return true;
+ if (rect) {
+ if (layout === 'center') {
+ rect = centerGraphic(rect, path.getBoundingRect());
}
+
+ resizePath(path, rect);
}
- return false;
+ 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: {
+ image: imageUrl,
+ x: rect.x,
+ y: rect.y,
+ width: rect.width,
+ height: rect.height
+ },
+ onload: function (img) {
+ if (layout === 'center') {
+ var boundingRect = {
+ width: img.width,
+ height: img.height
+ };
+ zrImg.setStyle(centerGraphic(rect, boundingRect));
+ }
+ }
+ });
+ 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;
+
+ if (width <= rect.width) {
+ height = rect.height;
+ } else {
+ width = rect.width;
+ height = width / aspect;
+ }
+
+ var cx = rect.x + rect.width / 2;
+ var cy = rect.y + rect.height / 2;
+ return {
+ x: cx - width / 2,
+ y: cy - height / 2,
+ width: width,
+ height: height
+ };
+ }
+
+ var mergePath$1 = mergePath;
+ /**
+ * Resize a path to fit the rect
+ * @param path
+ * @param rect
+ */
+
+ function resizePath(path, rect) {
+ if (!path.applyTransform) {
+ return;
+ }
+
+ var pathRect = path.getBoundingRect();
+ 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;
+ /**
* Get transform matrix of target (param target),
* in coordinate of its ancestor (param ancestor)
*
@@ -14780,6 +14869,11 @@
var graphic = /*#__PURE__*/Object.freeze({
__proto__: null,
+ updateProps: updateProps,
+ initProps: initProps,
+ removeElement: removeElement,
+ removeElementWithFadeOut: removeElementWithFadeOut,
+ isElementRemoved: isElementRemoved,
extendShape: extendShape,
extendPath: extendPath,
registerShape: registerShape,
@@ -14791,11 +14885,6 @@
subPixelOptimizeLine: subPixelOptimizeLine$1,
subPixelOptimizeRect: subPixelOptimizeRect$1,
subPixelOptimize: subPixelOptimize$1,
- updateProps: updateProps,
- initProps: initProps,
- removeElement: removeElement,
- removeElementWithFadeOut: removeElementWithFadeOut,
- isElementRemoved: isElementRemoved,
getTransform: getTransform,
applyTransform: applyTransform$1,
transformDirection: transformDirection,
@@ -15316,32 +15405,6 @@
obj.statesModels = labelStatesModels;
}
}
- function animateLabelValue(textEl, dataIndex, data, animatableModel, labelFetcher) {
- var labelInnerStore = labelInner(textEl);
-
- if (!labelInnerStore.valueAnimation) {
- return;
- }
-
- 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;
-
- function during(percent) {
- var interpolated = interpolateRawValues(data, labelInnerStore.precision, currValue, targetValue, percent);
- labelInnerStore.interpolatedValue = percent === 1 ? null : interpolated;
- var labelText = getLabelText({
- labelDataIndex: dataIndex,
- labelFetcher: labelFetcher,
- defaultText: defaultInterpolatedText ? defaultInterpolatedText(interpolated) : interpolated + ''
- }, labelInnerStore.statesModels, interpolated);
- setLabelText(textEl, labelText);
- }
-
- (currValue == null ? initProps : updateProps)(textEl, {}, animatableModel, dataIndex, null, during);
- }
var PATH_COLOR = ['textStyle', 'color']; // TODO Performance improvement?
@@ -16151,8 +16214,8 @@
hour: '{HH}:{mm}',
minute: '{HH}:{mm}',
second: '{HH}:{mm}:{ss}',
- millisecond: '{hh}:{mm}:{ss} {SSS}',
- none: '{yyyy}-{MM}-{dd} {hh}:{mm}:{ss} {SSS}'
+ millisecond: '{HH}:{mm}:{ss} {SSS}',
+ none: '{yyyy}-{MM}-{dd} {HH}:{mm}:{ss} {SSS}'
};
var fullDayFormatter = '{yyyy}-{MM}-{dd}';
var fullLeveledFormatter = {
@@ -16363,7 +16426,7 @@
return isUTC ? 'getUTCSeconds' : 'getSeconds';
}
function millisecondsGetterName(isUTC) {
- return isUTC ? 'getUTCSeconds' : 'getSeconds';
+ return isUTC ? 'getUTCMilliseconds' : 'getMilliseconds';
}
function fullYearSetterName(isUTC) {
return isUTC ? 'setUTCFullYear' : 'setFullYear';
@@ -16384,7 +16447,7 @@
return isUTC ? 'setUTCSeconds' : 'setSeconds';
}
function millisecondsSetterName(isUTC) {
- return isUTC ? 'setUTCSeconds' : 'setSeconds';
+ return isUTC ? 'setUTCMilliseconds' : 'setMilliseconds';
}
function getTextRect(text, font, align, verticalAlign, padding, rich, truncate, lineHeight) {
@@ -17291,18 +17354,8 @@
var globalDefault = {
darkMode: 'auto',
// 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'],
+ colorBy: 'series',
+ color: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'],
gradientColor: ['#f6efa6', '#d88273', '#bf444c'],
aria: {
decal: {
@@ -17380,7 +17433,7 @@
useUTC: false
};
- var VISUAL_DIMENSIONS = createHashMap(['tooltip', 'label', 'itemName', 'itemId', 'seriesName']);
+ var VISUAL_DIMENSIONS = createHashMap(['tooltip', 'label', 'itemName', 'itemId', 'itemGroupId', 'seriesName']);
var SOURCE_FORMAT_ORIGINAL = 'original';
var SOURCE_FORMAT_ARRAY_ROWS = 'arrayRows';
var SOURCE_FORMAT_OBJECT_ROWS = 'objectRows';
@@ -18215,11 +18268,6 @@
return this._locale;
};
- GlobalModel.prototype.getLocale = function (localePosition) {
- var locale = this.getLocaleModel();
- return locale.get(localePosition);
- };
-
GlobalModel.prototype.setUpdatePayload = function (payload) {
this._payload = payload;
};
@@ -18395,7 +18443,7 @@
GlobalModel.prototype.getSeries = function () {
- return filter(this._componentsMap.get('series').slice(), function (oneSeries) {
+ return filter(this._componentsMap.get('series'), function (oneSeries) {
return !!oneSeries;
});
};
@@ -19658,7 +19706,7 @@
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) {
+ targetData.modify(dims, function (v0, v1, 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.
@@ -19705,26 +19753,33 @@
resultVal[1] = stackedOver;
return resultVal;
});
- targetData.hostModel.setData(newData); // Update for consequent calculation
-
- targetStackInfo.data = newData;
});
}
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; // Visit config
this.seriesLayoutBy = fields.seriesLayoutBy || SERIES_LAYOUT_BY_COLUMN;
this.startIndex = fields.startIndex || 0;
- this.dimensionsDefine = fields.dimensionsDefine;
this.dimensionsDetectedCount = fields.dimensionsDetectedCount;
- this.encodeDefine = fields.encodeDefine;
this.metaRawOption = fields.metaRawOption;
+ var dimensionsDefine = this.dimensionsDefine = fields.dimensionsDefine;
+
+ if (dimensionsDefine) {
+ for (var i = 0; i < dimensionsDefine.length; i++) {
+ var dim = dimensionsDefine[i];
+
+ if (dim.type == null) {
+ if (guessOrdinal(this, i) === BE_ORDINAL.Must) {
+ dim.type = 'ordinal';
+ }
+ }
+ }
+ }
}
return SourceImpl;
@@ -19733,9 +19788,13 @@
function isSourceInstance(val) {
return val instanceof SourceImpl;
}
+ /**
+ * Create a source from option.
+ * NOTE: Created source is immutable. Don't change any properties in it.
+ */
+
function createSource(sourceData, thisMetaRawOption, // can be null. If not provided, auto detect it from `sourceData`.
- sourceFormat, encodeDefine // can be null
- ) {
+ sourceFormat) {
sourceFormat = sourceFormat || detectSourceFormat(sourceData);
var seriesLayoutBy = thisMetaRawOption.seriesLayoutBy;
var determined = determineSourceDimensions(sourceData, sourceFormat, seriesLayoutBy, thisMetaRawOption.sourceHeader, thisMetaRawOption.dimensions);
@@ -19746,7 +19805,6 @@
dimensionsDefine: determined.dimensionsDefine,
startIndex: determined.startIndex,
dimensionsDetectedCount: determined.dimensionsDetectedCount,
- encodeDefine: makeEncodeDefine(encodeDefine),
metaRawOption: clone(thisMetaRawOption)
});
return source;
@@ -19772,20 +19830,13 @@
seriesLayoutBy: source.seriesLayoutBy,
dimensionsDefine: clone(source.dimensionsDefine),
startIndex: source.startIndex,
- dimensionsDetectedCount: source.dimensionsDetectedCount,
- encodeDefine: makeEncodeDefine(source.encodeDefine)
+ dimensionsDetectedCount: source.dimensionsDetectedCount
});
}
-
- 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;
@@ -19985,6 +20036,11 @@
}
}
+ function shouldRetrieveDataByName(source) {
+ var sourceFormat = source.sourceFormat;
+ return sourceFormat === SOURCE_FORMAT_OBJECT_ROWS || sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS;
+ }
+
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
@@ -20213,19 +20269,19 @@
var rawSourceItemGetterMap = (_a = {}, _a[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_COLUMN] = function (rawData, startIndex, dimsDef, idx) {
return rawData[idx + startIndex];
- }, _a[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_ROW] = function (rawData, startIndex, dimsDef, idx) {
+ }, _a[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_ROW] = function (rawData, startIndex, dimsDef, idx, out) {
idx += startIndex;
- var item = [];
+ var item = out || [];
var data = rawData;
for (var i = 0; i < data.length; i++) {
var row = data[i];
- item.push(row ? row[idx] : null);
+ item[i] = row ? row[idx] : null;
}
return item;
- }, _a[SOURCE_FORMAT_OBJECT_ROWS] = getItemSimply, _a[SOURCE_FORMAT_KEYED_COLUMNS] = function (rawData, startIndex, dimsDef, idx) {
- var item = [];
+ }, _a[SOURCE_FORMAT_OBJECT_ROWS] = getItemSimply, _a[SOURCE_FORMAT_KEYED_COLUMNS] = function (rawData, startIndex, dimsDef, idx, out) {
+ var item = out || [];
for (var i = 0; i < dimsDef.length; i++) {
var dimName = dimsDef[i].name;
@@ -20237,7 +20293,7 @@
}
var col = rawData[dimName];
- item.push(col ? col[idx] : null);
+ item[i] = col ? col[idx] : null;
}
return item;
@@ -20246,7 +20302,7 @@
var method = rawSourceItemGetterMap[getMethodMapKey(sourceFormat, seriesLayoutBy)];
if ("development" !== 'production') {
- assert(method, 'Do not suppport get item on "' + sourceFormat + '", "' + seriesLayoutBy + '".');
+ assert(method, 'Do not support get item on "' + sourceFormat + '", "' + seriesLayoutBy + '".');
}
return method;
@@ -20283,17 +20339,17 @@
return method;
}
- var getRawValueSimply = function (dataItem, dimIndex, dimName) {
- return dimIndex != null ? dataItem[dimIndex] : dataItem;
+ var getRawValueSimply = function (dataItem, dimIndex, property) {
+ return dataItem[dimIndex];
};
- 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) {
+ var rawSourceValueGetterMap = (_c = {}, _c[SOURCE_FORMAT_ARRAY_ROWS] = getRawValueSimply, _c[SOURCE_FORMAT_OBJECT_ROWS] = function (dataItem, dimIndex, property) {
+ return dataItem[property];
+ }, _c[SOURCE_FORMAT_KEYED_COLUMNS] = getRawValueSimply, _c[SOURCE_FORMAT_ORIGINAL] = function (dataItem, dimIndex, property) {
// 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];
+ return !(value instanceof Array) ? value : value[dimIndex];
}, _c[SOURCE_FORMAT_TYPED_ARRAY] = getRawValueSimply, _c);
function getRawSourceValueGetter(sourceFormat) {
var method = rawSourceValueGetterMap[sourceFormat];
@@ -20316,9 +20372,9 @@
// TODO: consider how to treat null/undefined/NaN when display?
- function retrieveRawValue(data, dataIndex, dim // If dimIndex is null/undefined, return OptionDataItem.
+ function retrieveRawValue(data, dataIndex, // If dimIndex is null/undefined, return OptionDataItem.
// Otherwise, return OptionDataValue.
- ) {
+ dim) {
if (!data) {
return;
} // Consider data may be not persistent.
@@ -20330,17 +20386,22 @@
return;
}
- var sourceFormat = data.getProvider().getSource().sourceFormat;
- var dimName;
- var dimIndex;
- var dimInfo = data.getDimensionInfo(dim);
+ var store = data.getStore();
+ var sourceFormat = store.getSource().sourceFormat;
- if (dimInfo) {
- dimName = dimInfo.name;
- dimIndex = dimInfo.index;
- }
+ if (dim != null) {
+ var dimIndex = data.getDimensionIndex(dim);
+ var property = store.getDimensionProperty(dimIndex);
+ return getRawSourceValueGetter(sourceFormat)(dataItem, dimIndex, property);
+ } else {
+ var result = dataItem;
- return getRawSourceValueGetter(sourceFormat)(dataItem, dimIndex, dimName);
+ if (sourceFormat === SOURCE_FORMAT_ORIGINAL) {
+ result = getDataItemValue(dataItem);
+ }
+
+ return result;
+ }
}
var DIMENSION_LABEL_REG = /\{@(.+?)\}/g;
@@ -20365,7 +20426,7 @@
var borderColor = style && style.stroke;
var mainType = this.mainType;
var isSeries = mainType === 'series';
- var userOutput = data.userOutput;
+ var userOutput = data.userOutput && data.userOutput.get();
return {
componentType: mainType,
componentSubType: this.subType,
@@ -20381,7 +20442,7 @@
value: rawValue,
color: color,
borderColor: borderColor,
- dimensionNames: userOutput ? userOutput.dimensionNames : null,
+ dimensionNames: userOutput ? userOutput.fullDimensions : null,
encode: userOutput ? userOutput.encode : null,
// Param name list for mapping `a`, `b`, `c`, `d`, `e`
$vars: ['seriesName', 'name', 'value']
@@ -20428,15 +20489,25 @@
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) // Also support: '[]' => 0
- : dimStr;
+ var dimLoose = dimStr;
+
+ if (dimLoose.charAt(0) === '[' && dimLoose.charAt(len - 1) === ']') {
+ dimLoose = +dimLoose.slice(1, len - 1); // Also support: '[]' => 0
+
+ if ("development" !== 'production') {
+ if (isNaN(dimLoose)) {
+ error("Invalide label formatter: @" + dimStr + ", only support @[0], @[1], @[2], ...");
+ }
+ }
+ }
+
var val = retrieveRawValue(data, dataIndex, dimLoose);
if (extendParams && isArray(extendParams.interpolatedValue)) {
- var dimInfo = data.getDimensionInfo(dimLoose);
+ var dimIndex = data.getDimensionIndex(dimLoose);
- if (dimInfo) {
- val = extendParams.interpolatedValue[dimInfo.index];
+ if (dimIndex >= 0) {
+ val = extendParams.interpolatedValue[dimIndex];
}
}
@@ -20873,8 +20944,7 @@
if (dimType === 'ordinal') {
// If given value is a category string
- var ordinalMeta = opt && opt.ordinalMeta;
- return ordinalMeta ? ordinalMeta.parseAndCollect(value) : value;
+ return value;
}
if (dimType === 'time' // spead up when using timestamp
@@ -21405,7 +21475,7 @@
};
}
- return createSource(result.data, resultMetaRawOption, null, null);
+ return createSource(result.data, resultMetaRawOption, null);
});
}
@@ -21413,3021 +21483,2778 @@
return sourceFormat === SOURCE_FORMAT_ARRAY_ROWS || sourceFormat === SOURCE_FORMAT_OBJECT_ROWS;
}
+ var UNDEFINED = 'undefined';
+ /* global Float64Array, Int32Array, Uint32Array, Uint16Array */
+ // Caution: MUST not use `new CtorUint32Array(arr, 0, len)`, because the Ctor of array is
+ // different from the Ctor of typed array.
+
+ var CtorUint32Array = typeof Uint32Array === UNDEFINED ? Array : Uint32Array;
+ var CtorUint16Array = typeof Uint16Array === UNDEFINED ? Array : Uint16Array;
+ var CtorInt32Array = typeof Int32Array === UNDEFINED ? Array : Int32Array;
+ var CtorFloat64Array = typeof Float64Array === UNDEFINED ? Array : Float64Array;
/**
- * [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
- * }]
- * ```
+ * Multi dimensional data store
*/
- var SourceManager =
+ var dataCtors = {
+ 'float': CtorFloat64Array,
+ 'int': CtorInt32Array,
+ // Ordinal data type can be string or int
+ 'ordinal': Array,
+ 'number': Array,
+ 'time': CtorFloat64Array
+ };
+ var defaultDimValueGetters;
+
+ function getIndicesCtor(rawCount) {
+ // The possible max value in this._indicies is always this._rawCount despite of filtering.
+ return rawCount > 65535 ? CtorUint32Array : CtorUint16Array;
+ }
+
+ function getInitialExtent() {
+ return [Infinity, -Infinity];
+ }
+
+ function cloneChunk(originalChunk) {
+ var Ctor = originalChunk.constructor; // Only shallow clone is enough when Array.
+
+ return Ctor === Array ? originalChunk.slice() : new Ctor(originalChunk);
+ }
+
+ function prepareStore(store, dimIdx, dimType, end, append) {
+ var DataCtor = dataCtors[dimType || 'float'];
+
+ if (append) {
+ var oldStore = store[dimIdx];
+ var oldLen = oldStore && oldStore.length;
+
+ if (!(oldLen === end)) {
+ var newStore = new DataCtor(end); // The cost of the copy is probably inconsiderable
+ // within the initial chunkSize.
+
+ for (var j = 0; j < oldLen; j++) {
+ newStore[j] = oldStore[j];
+ }
+
+ store[dimIdx] = newStore;
+ }
+ } else {
+ store[dimIdx] = new DataCtor(end);
+ }
+ }
+ /**
+ * Basically, DataStore API keep immutable.
+ */
+
+ var DataStore =
/** @class */
function () {
- function SourceManager(sourceHost) {
- // Cached source. Do not repeat calculating if not dirty.
- this._sourceList = []; // version sign of each upstream source manager.
+ function DataStore() {
+ this._chunks = []; // It will not be calculated util needed.
- this._upstreamSignList = [];
- this._versionSignBase = 0;
- this._sourceHost = sourceHost;
+ this._rawExtent = [];
+ this._extent = [];
+ this._count = 0;
+ this._rawCount = 0;
+ this._calcDimNameToIdx = createHashMap();
}
/**
- * Mark dirty.
+ * Initialize from data
*/
- SourceManager.prototype.dirty = function () {
- this._setLocalSource([], []);
- };
+ DataStore.prototype.initData = function (provider, inputDimensions, dimValueGetter) {
+ if ("development" !== 'production') {
+ assert(isFunction(provider.getItem) && isFunction(provider.count), 'Inavlid data provider.');
+ }
- SourceManager.prototype._setLocalSource = function (sourceList, upstreamSignList) {
- this._sourceList = sourceList;
- this._upstreamSignList = upstreamSignList;
- this._versionSignBase++;
+ this._provider = provider; // Clear
- if (this._versionSignBase > 9e10) {
- this._versionSignBase = 0;
- }
+ this._chunks = [];
+ this._indices = null;
+ this.getRawIndex = this._getRawIdxIdentity;
+ var source = provider.getSource();
+ var defaultGetter = this.defaultDimValueGetter = defaultDimValueGetters[source.sourceFormat]; // Default dim value getter
+
+ this._dimValueGetter = dimValueGetter || defaultGetter; // Reset raw extent.
+
+ this._rawExtent = [];
+ var willRetrieveDataByName = shouldRetrieveDataByName(source);
+ this._dimensions = map(inputDimensions, function (dim) {
+ if ("development" !== 'production') {
+ if (willRetrieveDataByName) {
+ assert(dim.property != null);
+ }
+ }
+
+ return {
+ // Only pick these two props. Not leak other properties like orderMeta.
+ type: dim.type,
+ property: dim.property
+ };
+ });
+
+ this._initDataFromProvider(0, provider.count());
+ };
+
+ DataStore.prototype.getProvider = function () {
+ return this._provider;
};
/**
- * For detecting whether the upstream source is dirty, so that
- * the local cached source (in `_sourceList`) should be discarded.
+ * Caution: even when a `source` instance owned by a series, the created data store
+ * may still be shared by different sereis (the source hash does not use all `source`
+ * props, see `sourceManager`). In this case, the `source` props that are not used in
+ * hash (like `source.dimensionDefine`) probably only belongs to a certain series and
+ * thus should not be fetch here.
*/
- SourceManager.prototype._getVersionSign = function () {
- return this._sourceHost.uid + '_' + this._versionSignBase;
+ DataStore.prototype.getSource = function () {
+ return this._provider.getSource();
};
/**
- * Always return a source instance. Otherwise throw error.
+ * @caution Only used in dataStack.
*/
- 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();
+ DataStore.prototype.ensureCalculationDimension = function (dimName, type) {
+ var calcDimNameToIdx = this._calcDimNameToIdx;
+ var dimensions = this._dimensions;
+ var calcDimIdx = calcDimNameToIdx.get(dimName);
+
+ if (calcDimIdx != null) {
+ if (dimensions[calcDimIdx].type === type) {
+ return calcDimIdx;
+ }
+ } else {
+ calcDimIdx = dimensions.length;
}
- };
- SourceManager.prototype._createSource = function () {
- this._setLocalSource([], []);
+ dimensions[calcDimIdx] = {
+ type: type
+ };
+ calcDimNameToIdx.set(dimName, calcDimIdx);
+ this._chunks[calcDimIdx] = new dataCtors[type || 'float'](this._rawCount);
+ this._rawExtent[calcDimIdx] = getInitialExtent();
+ return calcDimIdx;
+ };
- var sourceHost = this._sourceHost;
+ DataStore.prototype.collectOrdinalMeta = function (dimIdx, ordinalMeta) {
+ var chunk = this._chunks[dimIdx];
+ var dim = this._dimensions[dimIdx];
+ var rawExtents = this._rawExtent;
+ var offset = dim.ordinalOffset || 0;
+ var len = chunk.length;
- var upSourceMgrList = this._getUpstreamSourceManagers();
+ if (offset === 0) {
+ // We need to reset the rawExtent if collect is from start.
+ // Because this dimension may be guessed as number and calcuating a wrong extent.
+ rawExtents[dimIdx] = getInitialExtent();
+ }
- var hasUpstream = !!upSourceMgrList.length;
- var resultSourceList;
- var upstreamSignList;
+ var dimRawExtent = rawExtents[dimIdx]; // Parse from previous data offset. len may be changed after appendData
- if (isSeries(sourceHost)) {
- var seriesModel = sourceHost;
- var data = void 0;
- var sourceFormat = void 0;
- var upSource = void 0; // Has upstream dataset
+ for (var i = offset; i < len; i++) {
+ var val = chunk[i] = ordinalMeta.parseAndCollect(chunk[i]);
+ dimRawExtent[0] = Math.min(val, dimRawExtent[0]);
+ dimRawExtent[1] = Math.max(val, dimRawExtent[1]);
+ }
- if (hasUpstream) {
- var upSourceMgr = upSourceMgrList[0];
- upSourceMgr.prepareSource();
- upSource = upSourceMgr.getSource();
- data = upSource.data;
- sourceFormat = upSource.sourceFormat;
- upstreamSignList = [upSourceMgr._getVersionSign()];
- } // 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.
+ dim.ordinalMeta = ordinalMeta;
+ dim.ordinalOffset = len;
+ dim.type = 'ordinal'; // Force to be ordinal
+ };
+ DataStore.prototype.getOrdinalMeta = function (dimIdx) {
+ var dimInfo = this._dimensions[dimIdx];
+ var ordinalMeta = dimInfo.ordinalMeta;
+ return ordinalMeta;
+ };
- var newMetaRawOption = this._getSourceMetaRawOption();
+ DataStore.prototype.getDimensionProperty = function (dimIndex) {
+ var item = this._dimensions[dimIndex];
+ return item && item.property;
+ };
+ /**
+ * Caution: Can be only called on raw data (before `this._indices` created).
+ */
- var upMetaRawOption = upSource ? upSource.metaRawOption : null;
- var seriesLayoutBy = retrieve2(newMetaRawOption.seriesLayoutBy, upMetaRawOption ? upMetaRawOption.seriesLayoutBy : 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,
- sourceHeader: sourceHeader,
- dimensions: dimensions
- }, sourceFormat, seriesModel.get('encode', true))];
- } else {
- var datasetModel = sourceHost; // Has upstream dataset.
+ DataStore.prototype.appendData = function (data) {
+ if ("development" !== 'production') {
+ assert(!this._indices, 'appendData can only be called on raw data.');
+ }
- if (hasUpstream) {
- var result = this._applyTransform(upSourceMgrList);
+ var provider = this._provider;
+ var start = this.count();
+ provider.appendData(data);
+ var end = provider.count();
- resultSourceList = result.sourceList;
- upstreamSignList = result.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 (!provider.persistent) {
+ end += start;
}
- if ("development" !== 'production') {
- assert(resultSourceList && upstreamSignList);
+ if (start < end) {
+ this._initDataFromProvider(start, end, true);
}
- this._setLocalSource(resultSourceList, upstreamSignList);
+ return [start, end];
};
- SourceManager.prototype._applyTransform = function (upMgrList) {
- var datasetModel = this._sourceHost;
- var transformOption = datasetModel.get('transform', true);
- var fromTransformResult = datasetModel.get('fromTransformResult', true);
+ DataStore.prototype.appendValues = function (values, minFillLen) {
+ var chunks = this._chunks;
+ var dimensions = this._dimensions;
+ var dimLen = dimensions.length;
+ var rawExtent = this._rawExtent;
+ var start = this.count();
+ var end = start + Math.max(values.length, minFillLen || 0);
- if ("development" !== 'production') {
- assert(fromTransformResult != null || transformOption != null);
+ for (var i = 0; i < dimLen; i++) {
+ var dim = dimensions[i];
+ prepareStore(chunks, i, dim.type, end, true);
}
- if (fromTransformResult != null) {
- var errMsg = '';
+ var emptyDataItem = [];
- if (upMgrList.length !== 1) {
- if ("development" !== 'production') {
- errMsg = 'When using `fromTransformResult`, there should be only one upstream dataset';
- }
+ for (var idx = start; idx < end; idx++) {
+ var sourceIdx = idx - start; // Store the data by dimensions
- doThrow(errMsg);
+ for (var dimIdx = 0; dimIdx < dimLen; dimIdx++) {
+ var dim = dimensions[dimIdx];
+ var val = defaultDimValueGetters.arrayRows.call(this, values[sourceIdx] || emptyDataItem, dim.property, sourceIdx, dimIdx);
+ chunks[dimIdx][idx] = val;
+ var dimRawExtent = rawExtent[dimIdx];
+ val < dimRawExtent[0] && (dimRawExtent[0] = val);
+ val > dimRawExtent[1] && (dimRawExtent[1] = val);
}
}
- var sourceList;
- var upSourceList = [];
- var upstreamSignList = [];
- each(upMgrList, function (upMgr) {
- upMgr.prepareSource();
- var upSource = upMgr.getSource(fromTransformResult || 0);
- var errMsg = '';
+ this._rawCount = this._count = end;
+ return {
+ start: start,
+ end: end
+ };
+ };
- if (fromTransformResult != null && !upSource) {
- if ("development" !== 'production') {
- errMsg = 'Can not retrieve result by `fromTransformResult`: ' + fromTransformResult;
- }
+ DataStore.prototype._initDataFromProvider = function (start, end, append) {
+ var provider = this._provider;
+ var chunks = this._chunks;
+ var dimensions = this._dimensions;
+ var dimLen = dimensions.length;
+ var rawExtent = this._rawExtent;
+ var dimNames = map(dimensions, function (dim) {
+ return dim.property;
+ });
- doThrow(errMsg);
+ for (var i = 0; i < dimLen; i++) {
+ var dim = dimensions[i];
+
+ if (!rawExtent[i]) {
+ rawExtent[i] = getInitialExtent();
}
- upSourceList.push(upSource);
- upstreamSignList.push(upMgr._getVersionSign());
- });
+ prepareStore(chunks, i, dim.type, end, append);
+ }
- if (transformOption) {
- sourceList = applyDataTransform(transformOption, upSourceList, {
- datasetIndex: datasetModel.componentIndex
- });
- } else if (fromTransformResult != null) {
- sourceList = [cloneSourceShallow(upSourceList[0])];
+ if (provider.fillStorage) {
+ provider.fillStorage(start, end, chunks, rawExtent);
+ } else {
+ var dataItem = [];
+
+ for (var idx = start; idx < end; idx++) {
+ // NOTICE: Try not to write things into dataItem
+ dataItem = provider.getItem(idx, dataItem); // Each data item is value
+ // [1, 2]
+ // 2
+ // Bar chart, line chart which uses category axis
+ // only gives the 'y' value. 'x' value is the indices of category
+ // Use a tempValue to normalize the value to be a (x, y) value
+ // Store the data by dimensions
+
+ for (var dimIdx = 0; dimIdx < dimLen; dimIdx++) {
+ var dimStorage = chunks[dimIdx]; // PENDING NULL is empty or zero
+
+ var val = this._dimValueGetter(dataItem, dimNames[dimIdx], idx, dimIdx);
+
+ dimStorage[idx] = val;
+ var dimRawExtent = rawExtent[dimIdx];
+ val < dimRawExtent[0] && (dimRawExtent[0] = val);
+ val > dimRawExtent[1] && (dimRawExtent[1] = val);
+ }
+ }
}
- return {
- sourceList: sourceList,
- upstreamSignList: upstreamSignList
- };
+ if (!provider.persistent && provider.clean) {
+ // Clean unused data if data source is typed array.
+ provider.clean();
+ }
+
+ this._rawCount = this._count = end; // Reset data extent
+
+ this._extent = [];
};
- SourceManager.prototype._isDirty = function () {
- var sourceList = this._sourceList;
+ DataStore.prototype.count = function () {
+ return this._count;
+ };
+ /**
+ * Get value. Return NaN if idx is out of range.
+ */
- if (!sourceList.length) {
- return true;
- } // All sourceList is from the some upsteam.
+ DataStore.prototype.get = function (dim, idx) {
+ if (!(idx >= 0 && idx < this._count)) {
+ return NaN;
+ }
- var upSourceMgrList = this._getUpstreamSourceManagers();
+ var dimStore = this._chunks[dim];
+ return dimStore ? dimStore[this.getRawIndex(idx)] : NaN;
+ };
- for (var i = 0; i < upSourceMgrList.length; i++) {
- var upSrcMgr = upSourceMgrList[i];
+ DataStore.prototype.getValues = function (dimensions, idx) {
+ var values = [];
+ var dimArr = [];
- 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;
+ if (idx == null) {
+ idx = dimensions; // TODO get all from store?
+
+ dimensions = []; // All dimensions
+
+ for (var i = 0; i < this._dimensions.length; i++) {
+ dimArr.push(i);
}
+ } else {
+ dimArr = dimensions;
+ }
+
+ for (var i = 0, len = dimArr.length; i < len; i++) {
+ values.push(this.get(dimArr[i], idx));
}
+
+ return values;
};
/**
- * @param sourceIndex By defualt 0, means "main source".
- * Most cases there is only one source.
+ * @param dim concrete dim
*/
- SourceManager.prototype.getSource = function (sourceIndex) {
- return this._sourceList[sourceIndex || 0];
+ DataStore.prototype.getByRawIndex = function (dim, rawIdx) {
+ if (!(rawIdx >= 0 && rawIdx < this._rawCount)) {
+ return NaN;
+ }
+
+ var dimStore = this._chunks[dim];
+ return dimStore ? dimStore[rawIdx] : NaN;
};
/**
- * PEDING: Is it fast enough?
- * If no upstream, return empty array.
+ * Get sum of data in one dimension
*/
- 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;
+ DataStore.prototype.getSum = function (dim) {
+ var dimData = this._chunks[dim];
+ var sum = 0;
- if (isSeries(sourceHost)) {
- var datasetModel = querySeriesUpstreamDatasetModel(sourceHost);
- return !datasetModel ? [] : [datasetModel.getSourceManager()];
- } else {
- return map(queryDatasetUpstreamDatasetModels(sourceHost), function (datasetModel) {
- return datasetModel.getSourceManager();
- });
+ if (dimData) {
+ for (var i = 0, len = this.count(); i < len; i++) {
+ var value = this.get(dim, i);
+
+ if (!isNaN(value)) {
+ sum += value;
+ }
+ }
}
+
+ return sum;
};
+ /**
+ * Get median of data in one dimension
+ */
- SourceManager.prototype._getSourceMetaRawOption = function () {
- var sourceHost = this._sourceHost;
- var seriesLayoutBy;
- var sourceHeader;
- var dimensions;
- if (isSeries(sourceHost)) {
- seriesLayoutBy = sourceHost.get('seriesLayoutBy', true);
- sourceHeader = sourceHost.get('sourceHeader', true);
- dimensions = sourceHost.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);
+ DataStore.prototype.getMedian = function (dim) {
+ var dimDataArray = []; // map all data of one dimension
+
+ this.each([dim], function (val) {
+ if (!isNaN(val)) {
+ dimDataArray.push(val);
}
+ }); // TODO
+ // Use quick select?
- return {
- seriesLayoutBy: seriesLayoutBy,
- sourceHeader: sourceHeader,
- dimensions: dimensions
- };
- };
+ var sortedDimDataArray = dimDataArray.sort(function (a, b) {
+ return a - b;
+ });
+ var len = this.count(); // calculate median
- return SourceManager;
- }();
- // disable the transform merge, but do not disable transfrom clone from rawOption.
+ return len === 0 ? 0 : len % 2 === 1 ? sortedDimDataArray[(len - 1) / 2] : (sortedDimDataArray[len / 2] + sortedDimDataArray[len / 2 - 1]) / 2;
+ };
+ /**
+ * Retreive the index with given raw data index
+ */
- 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';
- }
+ DataStore.prototype.indexOfRawIndex = function (rawIndex) {
+ if (rawIndex >= this._rawCount || rawIndex < 0) {
+ return -1;
+ }
- function doThrow(errMsg) {
- throw new Error(errMsg);
- }
+ if (!this._indices) {
+ return rawIndex;
+ } // Indices are ascending
- var TOOLTIP_LINE_HEIGHT_CSS = 'line-height:1'; // TODO: more textStyle option
- function getTooltipTextStyle(textStyle, renderMode) {
- var nameFontColor = textStyle.color || '#6e7079';
- var nameFontSize = textStyle.fontSize || 12;
- var nameFontWeight = textStyle.fontWeight || '400';
- var valueFontColor = textStyle.color || '#464646';
- var valueFontSize = textStyle.fontSize || 14;
- var valueFontWeight = textStyle.fontWeight || '900';
+ var indices = this._indices; // If rawIndex === dataIndex
- if (renderMode === 'html') {
- // `textStyle` is probably from user input, should be encoded to reduce security risk.
- return {
- // 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 {
- nameStyle: {
- fontSize: nameFontSize,
- fill: nameFontColor,
- fontWeight: nameFontWeight
- },
- valueStyle: {
- fontSize: valueFontSize,
- fill: valueFontColor,
- fontWeight: valueFontWeight
- }
- };
- }
- } // See `TooltipMarkupLayoutIntent['innerGapLevel']`.
- // (value from UI design)
+ var rawDataIndex = indices[rawIndex];
+ if (rawDataIndex != null && rawDataIndex < this._count && rawDataIndex === rawIndex) {
+ return rawIndex;
+ }
- var HTML_GAPS = [0, 10, 20, 30];
- var RICH_TEXT_GAPS = ['', '\n', '\n\n', '\n\n\n']; // eslint-disable-next-line max-len
+ var left = 0;
+ var right = this._count - 1;
- function createTooltipMarkup(type, option) {
- option.type = type;
- return option;
- }
+ while (left <= right) {
+ var mid = (left + right) / 2 | 0;
- function getBuilder(fragment) {
- return hasOwn(builderMap, fragment.type) && builderMap[fragment.type];
- }
+ if (indices[mid] < rawIndex) {
+ left = mid + 1;
+ } else if (indices[mid] > rawIndex) {
+ right = mid - 1;
+ } else {
+ return mid;
+ }
+ }
- var builderMap = {
+ return -1;
+ };
/**
- * A `section` block is like:
- * ```
- * header
- * subBlock
- * subBlock
- * ...
- * ```
+ * Retreive the index of nearest value
+ * @param dim
+ * @param value
+ * @param [maxDistance=Infinity]
+ * @return If and only if multiple indices has
+ * the same value, they are put to the result.
*/
- section: {
- planLayout: function (fragment) {
- var subBlockLen = fragment.blocks.length;
- var thisBlockHasInnerGap = subBlockLen > 1 || subBlockLen > 0 && !fragment.noHeader;
- var thisGapLevelBetweenSubBlocks = 0;
- each(fragment.blocks, function (subBlock) {
- getBuilder(subBlock).planLayout(subBlock);
- 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 && ( // 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);
+
+ DataStore.prototype.indicesOfNearest = function (dim, value, maxDistance) {
+ var chunks = this._chunks;
+ var dimData = chunks[dim];
+ var nearestIndices = [];
+
+ if (!dimData) {
+ return nearestIndices;
+ }
+
+ if (maxDistance == null) {
+ maxDistance = Infinity;
+ }
+
+ var minDist = Infinity;
+ var minDiff = -1;
+ var nearestIndicesLen = 0; // Check the test case of `test/ut/spec/data/SeriesData.js`.
+
+ for (var i = 0, len = this.count(); i < len; i++) {
+ var dataIndex = this.getRawIndex(i);
+ var diff = value - dimData[dataIndex];
+ var dist = Math.abs(diff);
+
+ if (dist <= maxDistance) {
+ // When the `value` is at the middle of `this.get(dim, i)` and `this.get(dim, i+1)`,
+ // we'd better not push both of them to `nearestIndices`, otherwise it is easy to
+ // get more than one item in `nearestIndices` (more specifically, in `tooltip`).
+ // So we chose the one that `diff >= 0` in this csae.
+ // But if `this.get(dim, i)` and `this.get(dim, j)` get the same value, both of them
+ // should be push to `nearestIndices`.
+ if (dist < minDist || dist === minDist && diff >= 0 && minDiff < 0) {
+ minDist = dist;
+ minDiff = diff;
+ nearestIndicesLen = 0;
}
- });
- fragment.__gapLevelBetweenSubBlocks = thisGapLevelBetweenSubBlocks;
- },
- build: function (ctx, fragment, topMarginForOuterGap, toolTipTextStyle) {
- var noHeader = fragment.noHeader;
- var gaps = getGap(fragment);
- var subMarkupText = buildSubBlocks(ctx, fragment, noHeader ? topMarginForOuterGap : gaps.html, toolTipTextStyle);
- if (noHeader) {
- return subMarkupText;
+ if (diff === minDiff) {
+ nearestIndices[nearestIndicesLen++] = i;
+ }
}
+ }
- var displayableHeader = makeValueReadable(fragment.header, 'ordinal', ctx.useUTC);
- var nameStyle = getTooltipTextStyle(toolTipTextStyle, ctx.renderMode).nameStyle;
+ nearestIndices.length = nearestIndicesLen;
+ return nearestIndices;
+ };
- if (ctx.renderMode === 'richText') {
- return wrapInlineNameRichText(ctx, displayableHeader, nameStyle) + gaps.richText + subMarkupText;
+ DataStore.prototype.getIndices = function () {
+ var newIndices;
+ var indices = this._indices;
+
+ if (indices) {
+ var Ctor = indices.constructor;
+ var thisCount = this._count; // `new Array(a, b, c)` is different from `new Uint32Array(a, b, c)`.
+
+ if (Ctor === Array) {
+ newIndices = new Ctor(thisCount);
+
+ for (var i = 0; i < thisCount; i++) {
+ newIndices[i] = indices[i];
+ }
} else {
- return wrapBlockHTML("<div style=\"" + nameStyle + ";" + TOOLTIP_LINE_HEIGHT_CSS + ";\">" + encodeHTML(displayableHeader) + '</div>' + subMarkupText, topMarginForOuterGap);
+ newIndices = new Ctor(indices.buffer, 0, thisCount);
+ }
+ } else {
+ var Ctor = getIndicesCtor(this._rawCount);
+ newIndices = new Ctor(this.count());
+
+ for (var i = 0; i < newIndices.length; i++) {
+ newIndices[i] = i;
}
}
- },
+ return newIndices;
+ };
/**
- * A `nameValue` block is like:
- * ```
- * marker name value
- * ```
+ * Data filter.
*/
- nameValue: {
- planLayout: function (fragment) {
- fragment.__gapLevelBetweenSubBlocks = 0;
- },
- build: function (ctx, fragment, topMarginForOuterGap, toolTipTextStyle) {
- var renderMode = ctx.renderMode;
- var noName = fragment.noName;
- var noValue = fragment.noValue;
- var noMarker = !fragment.markerType;
- var name = fragment.name;
- var value = fragment.value;
- var useUTC = ctx.useUTC;
- if (noName && noValue) {
- return;
- }
- var markerStr = noMarker ? '' : ctx.markupStyleCreator.makeTooltipMarker(fragment.markerType, fragment.markerColor || '#333', renderMode);
- var readableName = noName ? '' : makeValueReadable(name, 'ordinal', useUTC);
- var valueTypeOption = fragment.valueType;
- 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; // It little weird if only value next to marker but far from marker.
+ DataStore.prototype.filter = function (dims, cb) {
+ if (!this._count) {
+ return this;
+ }
- var valueCloseToMarker = !noMarker && noName;
+ var newStore = this.clone();
+ var count = newStore.count();
+ var Ctor = getIndicesCtor(newStore._rawCount);
+ var newIndices = new Ctor(count);
+ var value = [];
+ var dimSize = dims.length;
+ var offset = 0;
+ var dim0 = dims[0];
+ var chunks = newStore._chunks;
- var _a = getTooltipTextStyle(toolTipTextStyle, renderMode),
- nameStyle = _a.nameStyle,
- valueStyle = _a.valueStyle;
+ for (var i = 0; i < count; i++) {
+ var keep = void 0;
+ var rawIdx = newStore.getRawIndex(i); // Simple optimization
- 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);
- }
- }
- };
+ if (dimSize === 0) {
+ keep = cb(i);
+ } else if (dimSize === 1) {
+ var val = chunks[dim0][rawIdx];
+ keep = cb(val, i);
+ } else {
+ var k = 0;
- function buildSubBlocks(ctx, fragment, topMarginForOuterGap, tooltipTextStyle) {
- var subMarkupTextList = [];
- var subBlocks = fragment.blocks || [];
- assert(!subBlocks || isArray(subBlocks));
- subBlocks = subBlocks || [];
- var orderMode = ctx.orderMode;
+ for (; k < dimSize; k++) {
+ value[k] = chunks[dims[k]][rawIdx];
+ }
- if (fragment.sortBlocks && orderMode) {
- subBlocks = subBlocks.slice();
- var orderMap = {
- valueAsc: 'asc',
- valueDesc: 'desc'
- };
+ value[k] = i;
+ keep = cb.apply(null, value);
+ }
- if (hasOwn(orderMap, orderMode)) {
- var comparator_1 = new SortOrderComparator(orderMap[orderMode], null);
- subBlocks.sort(function (a, b) {
- return comparator_1.evaluate(a.sortParam, b.sortParam);
- });
- } // FIXME 'seriesDesc' necessary?
- else if (orderMode === 'seriesDesc') {
- subBlocks.reverse();
+ if (keep) {
+ newIndices[offset++] = rawIdx;
}
- }
+ } // Set indices after filtered.
- var gaps = getGap(fragment);
- each(subBlocks, function (subBlock, idx) {
- var subMarkupText = getBuilder(subBlock).build(ctx, subBlock, idx > 0 ? gaps.html : 0, tooltipTextStyle);
- subMarkupText != null && subMarkupTextList.push(subMarkupText);
- });
- if (!subMarkupTextList.length) {
- return;
- }
+ if (offset < count) {
+ newStore._indices = newIndices;
+ }
- return ctx.renderMode === 'richText' ? subMarkupTextList.join(gaps.richText) : wrapBlockHTML(subMarkupTextList.join(''), topMarginForOuterGap);
- }
- /**
- * @return markupText. null/undefined means no content.
- */
+ newStore._count = offset; // Reset data extent
+ newStore._extent = [];
- function buildTooltipMarkup(fragment, markupStyleCreator, renderMode, orderMode, useUTC, toolTipTextStyle) {
- if (!fragment) {
- return;
- }
+ newStore._updateGetRawIdx();
- var builder = getBuilder(fragment);
- builder.planLayout(fragment);
- var ctx = {
- useUTC: useUTC,
- renderMode: renderMode,
- orderMode: orderMode,
- markupStyleCreator: markupStyleCreator
+ return newStore;
};
- return builder.build(ctx, fragment, 0, toolTipTextStyle);
- }
+ /**
+ * Select data in range. (For optimization of filter)
+ * (Manually inline code, support 5 million data filtering in data zoom.)
+ */
- function getGap(fragment) {
- var gapLevelBetweenSubBlocks = fragment.__gapLevelBetweenSubBlocks;
- return {
- html: HTML_GAPS[gapLevelBetweenSubBlocks],
- richText: RICH_TEXT_GAPS[gapLevelBetweenSubBlocks]
- };
- }
- function wrapBlockHTML(encodedContent, topGap) {
- var clearfix = '<div style="clear:both"></div>';
- var marginCSS = "margin: " + topGap + "px 0 0";
- return "<div style=\"" + marginCSS + ";" + TOOLTIP_LINE_HEIGHT_CSS + ";\">" + encodedContent + clearfix + '</div>';
- }
+ DataStore.prototype.selectRange = function (range) {
+ var newStore = this.clone();
+ var len = newStore._count;
- function wrapInlineNameHTML(name, leftHasMarker, style) {
- var marginCss = leftHasMarker ? 'margin-left:2px' : '';
- return "<span style=\"" + style + ";" + marginCss + "\">" + encodeHTML(name) + '</span>';
- }
+ if (!len) {
+ return this;
+ }
- 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 + "\">" // Value has commas inside, so use ' ' as delimiter for multiple values.
- + map(valueList, function (value) {
- return encodeHTML(value);
- }).join(' ') + '</span>';
- }
+ var dims = keys(range);
+ var dimSize = dims.length;
- function wrapInlineNameRichText(ctx, name, style) {
- return ctx.markupStyleCreator.wrapRichTextStyle(name, style);
- }
+ if (!dimSize) {
+ return this;
+ }
- function wrapInlineValueRichText(ctx, valueList, alignRight, valueCloseToMarker, style) {
- var styles = [style];
- var paddingLeft = valueCloseToMarker ? 10 : 20;
- alignRight && styles.push({
- padding: [0, 0, 0, paddingLeft],
- align: 'right'
- }); // Value has commas inside, so use ' ' as delimiter for multiple values.
+ var originalCount = newStore.count();
+ var Ctor = getIndicesCtor(newStore._rawCount);
+ var newIndices = new Ctor(originalCount);
+ var offset = 0;
+ var dim0 = dims[0];
+ var min = range[dim0][0];
+ var max = range[dim0][1];
+ var storeArr = newStore._chunks;
+ var quickFinished = false;
- return ctx.markupStyleCreator.wrapRichTextStyle(valueList.join(' '), styles);
- }
+ if (!newStore._indices) {
+ // Extreme optimization for common case. About 2x faster in chrome.
+ var idx = 0;
- function retrieveVisualColorForTooltipMarker(series, dataIndex) {
- var style = series.getData().getItemVisual(dataIndex, 'style');
- var color = style[series.visualDrawType];
- return convertToColorString(color);
- }
- function getPaddingFromTooltipModel(model, renderMode) {
- var padding = model.get('padding');
- 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.
- */
+ if (dimSize === 1) {
+ var dimStorage = storeArr[dims[0]];
- var TooltipMarkupStyleCreator =
- /** @class */
- function () {
- function TooltipMarkupStyleCreator() {
- 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).
+ for (var i = 0; i < len; i++) {
+ var val = dimStorage[i]; // NaN will not be filtered. Consider the case, in line chart, empty
+ // value indicates the line should be broken. But for the case like
+ // scatter plot, a data item with empty value will not be rendered,
+ // but the axis extent may be effected if some other dim of the data
+ // item has value. Fortunately it is not a significant negative effect.
- this._nextStyleNameId = getRandomIdBase();
- }
+ if (val >= min && val <= max || isNaN(val)) {
+ newIndices[offset++] = idx;
+ }
- TooltipMarkupStyleCreator.prototype._generateStyleName = function () {
- return '__EC_aUTo_' + this._nextStyleNameId++;
- };
+ idx++;
+ }
- TooltipMarkupStyleCreator.prototype.makeTooltipMarker = function (markerType, colorStr, renderMode) {
- var markerId = renderMode === 'richText' ? this._generateStyleName() : null;
- var marker = getTooltipMarker({
- color: colorStr,
- type: markerType,
- renderMode: renderMode,
- markerId: markerId
- });
+ quickFinished = true;
+ } else if (dimSize === 2) {
+ var dimStorage = storeArr[dims[0]];
+ var dimStorage2 = storeArr[dims[1]];
+ var min2 = range[dims[1]][0];
+ var max2 = range[dims[1]][1];
- if (isString(marker)) {
- return marker;
- } else {
- if ("development" !== 'production') {
- assert(markerId);
- }
+ for (var i = 0; i < len; i++) {
+ var val = dimStorage[i];
+ var val2 = dimStorage2[i]; // Do not filter NaN, see comment above.
- this.richTextStyles[markerId] = marker.style;
- return marker.content;
+ if ((val >= min && val <= max || isNaN(val)) && (val2 >= min2 && val2 <= max2 || isNaN(val2))) {
+ newIndices[offset++] = idx;
+ }
+
+ idx++;
+ }
+
+ quickFinished = true;
+ }
}
- };
- /**
- * @usage
- * ```ts
- * const styledText = markupStyleCreator.wrapRichTextStyle([
- * // The styles will be auto merged.
- * {
- * fontSize: 12,
- * color: 'blue'
- * },
- * {
- * padding: 20
- * }
- * ]);
- * ```
- */
+ if (!quickFinished) {
+ if (dimSize === 1) {
+ for (var i = 0; i < originalCount; i++) {
+ var rawIndex = newStore.getRawIndex(i);
+ var val = storeArr[dims[0]][rawIndex]; // Do not filter NaN, see comment above.
- TooltipMarkupStyleCreator.prototype.wrapRichTextStyle = function (text, styles) {
- var finalStl = {};
+ if (val >= min && val <= max || isNaN(val)) {
+ newIndices[offset++] = rawIndex;
+ }
+ }
+ } else {
+ for (var i = 0; i < originalCount; i++) {
+ var keep = true;
+ var rawIndex = newStore.getRawIndex(i);
- if (isArray(styles)) {
- each(styles, function (stl) {
- return extend(finalStl, stl);
- });
- } else {
- extend(finalStl, styles);
- }
+ for (var k = 0; k < dimSize; k++) {
+ var dimk = dims[k];
+ var val = storeArr[dimk][rawIndex]; // Do not filter NaN, see comment above.
- var styleName = this._generateStyleName();
+ if (val < range[dimk][0] || val > range[dimk][1]) {
+ keep = false;
+ }
+ }
- this.richTextStyles[styleName] = finalStl;
- return "{" + styleName + "|" + text + "}";
- };
+ if (keep) {
+ newIndices[offset++] = newStore.getRawIndex(i);
+ }
+ }
+ }
+ } // Set indices after filtered.
- return TooltipMarkupStyleCreator;
- }();
- function defaultSeriesFormatTooltip(opt) {
- var series = opt.series;
- var dataIndex = opt.dataIndex;
- var multipleSeries = opt.multipleSeries;
- var data = series.getData();
- var tooltipDims = data.mapDimensionsAll('defaultedTooltip');
- var tooltipDimLen = tooltipDims.length;
- var value = series.getRawValue(dataIndex);
- var isValueArr = isArray(value);
- var markerColor = retrieveVisualColorForTooltipMarker(series, dataIndex); // Complicated rule for pretty tooltip.
+ if (offset < originalCount) {
+ newStore._indices = newIndices;
+ }
- var inlineValue;
- var inlineValueType;
- var subBlocks;
- var sortParam;
+ newStore._count = offset; // Reset data extent
- if (tooltipDimLen > 1 || isValueArr && !tooltipDimLen) {
- var formatArrResult = formatTooltipArrayValue(value, series, dataIndex, tooltipDims, markerColor);
- inlineValue = formatArrResult.inlineValues;
- inlineValueType = formatArrResult.inlineValueTypes;
- subBlocks = formatArrResult.blocks; // Only support tooltip sort by the first inline value. It's enough in most cases.
+ newStore._extent = [];
- sortParam = formatArrResult.inlineValues[0];
- } else if (tooltipDimLen) {
- var dimInfo = data.getDimensionInfo(tooltipDims[0]);
- sortParam = inlineValue = retrieveRawValue(data, dataIndex, tooltipDims[0]);
- inlineValueType = dimInfo.type;
- } else {
- sortParam = inlineValue = isValueArr ? value[0] : value;
- } // Do not show generated series name. It might not be readable.
+ newStore._updateGetRawIdx();
+ return newStore;
+ }; // /**
+ // * Data mapping to a plain array
+ // */
+ // mapArray(dims: DimensionIndex[], cb: MapArrayCb): any[] {
+ // const result: any[] = [];
+ // this.each(dims, function () {
+ // result.push(cb && (cb as MapArrayCb).apply(null, arguments));
+ // });
+ // return result;
+ // }
- var seriesNameSpecified = isNameSpecified(series);
- var seriesName = seriesNameSpecified && series.name || '';
- var itemName = data.getName(dataIndex);
- 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
- })].concat(subBlocks || [])
- });
- }
+ /**
+ * Data mapping to a new List with given dimensions
+ */
- 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);
- return isValueMultipleLine = isValueMultipleLine || dimItem && dimItem.tooltip !== false && dimItem.displayName != null;
- }, false);
- var inlineValues = [];
- var inlineValueTypes = [];
- var blocks = [];
- tooltipDims.length ? each(tooltipDims, function (dim) {
- setEachItem(retrieveRawValue(data, dataIndex, dim), dim);
- }) // By default, all dims is used on tooltip.
- : each(value, setEachItem);
- function setEachItem(val, dim) {
- var dimInfo = data.getDimensionInfo(dim); // If `dimInfo.tooltip` is not set, show tooltip.
+ DataStore.prototype.map = function (dims, cb) {
+ // TODO only clone picked chunks.
+ var target = this.clone(dims);
- if (!dimInfo || dimInfo.otherDims.tooltip === false) {
- return;
- }
+ this._updateDims(target, dims, cb);
- if (isValueMultipleLine) {
- blocks.push(createTooltipMarkup('nameValue', {
- markerType: 'subItem',
- markerColor: colorStr,
- name: dimInfo.displayName,
- value: val,
- valueType: dimInfo.type
- }));
- } else {
- inlineValues.push(val);
- inlineValueTypes.push(dimInfo.type);
- }
- }
+ return target;
+ };
+ /**
+ * @caution Danger!! Only used in dataStack.
+ */
- return {
- inlineValues: inlineValues,
- inlineValueTypes: inlineValueTypes,
- blocks: blocks
+
+ DataStore.prototype.modify = function (dims, cb) {
+ this._updateDims(this, dims, cb);
};
- }
- var inner$1 = makeInner();
+ DataStore.prototype._updateDims = function (target, dims, cb) {
+ var targetChunks = target._chunks;
+ var tmpRetValue = [];
+ var dimSize = dims.length;
+ var dataCount = target.count();
+ var values = [];
+ var rawExtent = target._rawExtent;
- function getSelectionKey(data, dataIndex) {
- return data.getName(dataIndex) || data.getId(dataIndex);
- }
+ for (var i = 0; i < dims.length; i++) {
+ rawExtent[dims[i]] = getInitialExtent();
+ }
- var SeriesModel =
- /** @class */
- function (_super) {
- __extends(SeriesModel, _super);
+ for (var dataIndex = 0; dataIndex < dataCount; dataIndex++) {
+ var rawIndex = target.getRawIndex(dataIndex);
- function SeriesModel() {
- // [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
- // ---------------------------------------
+ for (var k = 0; k < dimSize; k++) {
+ values[k] = targetChunks[dims[k]][rawIndex];
+ }
+ values[dimSize] = dataIndex;
+ var retValue = cb && cb.apply(null, values);
- _this._selectedDataIndicesMap = {};
- return _this;
- }
+ if (retValue != null) {
+ // a number or string (in oridinal dimension)?
+ if (typeof retValue !== 'object') {
+ tmpRetValue[0] = retValue;
+ retValue = tmpRetValue;
+ }
- SeriesModel.prototype.init = function (option, parentModel, ecModel) {
- this.seriesIndex = this.componentIndex;
- this.dataTask = createTask({
- count: dataTaskCount,
- reset: dataTaskReset
- });
- this.dataTask.context = {
- model: this
- };
- this.mergeDefaultAndTheme(option, ecModel);
- var sourceManager = inner$1(this).sourceManager = new SourceManager(this);
- sourceManager.prepareSource();
- var data = this.getInitialData(option, ecModel);
- wrapData(data, this);
- this.dataTask.context.data = data;
-
- if ("development" !== 'production') {
- assert(data, 'getInitialData returned invalid data.');
- }
+ for (var i = 0; i < retValue.length; i++) {
+ var dim = dims[i];
+ var val = retValue[i];
+ var rawExtentOnDim = rawExtent[dim];
+ var dimStore = targetChunks[dim];
- 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();
+ if (dimStore) {
+ dimStore[rawIndex] = val;
+ }
- autoSeriesName(this);
+ if (val < rawExtentOnDim[0]) {
+ rawExtentOnDim[0] = val;
+ }
- this._initSelectedMapFromData(data);
+ if (val > rawExtentOnDim[1]) {
+ rawExtentOnDim[1] = val;
+ }
+ }
+ }
+ }
};
/**
- * Util for merge default and theme to option
+ * Large data down sampling using largest-triangle-three-buckets
+ * @param {string} valueDimension
+ * @param {number} targetCount
*/
- SeriesModel.prototype.mergeDefaultAndTheme = function (option, ecModel) {
- var layoutMode = fetchLayoutMode(this);
- 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'.
+ DataStore.prototype.lttbDownSample = function (valueDimension, rate) {
+ var target = this.clone([valueDimension], true);
+ var targetStorage = target._chunks;
+ var dimStore = targetStorage[valueDimension];
+ var len = this.count();
+ var sampledIndex = 0;
+ var frameSize = Math.floor(1 / rate);
+ var currentRawIndex = this.getRawIndex(0);
+ var maxArea;
+ var area;
+ var nextRawIndex;
+ var newIndices = new (getIndicesCtor(this._rawCount))(Math.ceil(len / frameSize) + 2); // First frame use the first data.
- var themeSubType = this.subType;
+ newIndices[sampledIndex++] = currentRawIndex;
- if (ComponentModel.hasClass(themeSubType)) {
- themeSubType += 'Series';
- }
+ for (var i = 1; i < len - 1; i += frameSize) {
+ var nextFrameStart = Math.min(i + frameSize, len - 1);
+ var nextFrameEnd = Math.min(i + frameSize * 2, len);
+ var avgX = (nextFrameEnd + nextFrameStart) / 2;
+ var avgY = 0;
- merge(option, ecModel.getTheme().get(this.subType));
- merge(option, this.getDefaultOption()); // Default label emphasis `show`
+ for (var idx = nextFrameStart; idx < nextFrameEnd; idx++) {
+ var rawIndex = this.getRawIndex(idx);
+ var y = dimStore[rawIndex];
- defaultEmphasis(option, 'label', ['show']);
- this.fillDataTextStyle(option.data);
+ if (isNaN(y)) {
+ continue;
+ }
- if (layoutMode) {
- mergeLayoutParam(option, inputPositionParams, layoutMode);
- }
- };
+ avgY += y;
+ }
- SeriesModel.prototype.mergeOption = function (newSeriesOption, ecModel) {
- // this.settingTask.dirty();
- newSeriesOption = merge(this.option, newSeriesOption, true);
- this.fillDataTextStyle(newSeriesOption.data);
- var layoutMode = fetchLayoutMode(this);
+ avgY /= nextFrameEnd - nextFrameStart;
+ var frameStart = i;
+ var frameEnd = Math.min(i + frameSize, len);
+ var pointAX = i - 1;
+ var pointAY = dimStore[currentRawIndex];
+ maxArea = -1;
+ nextRawIndex = frameStart; // Find a point from current frame that construct a triangel with largest area with previous selected point
+ // And the average of next frame.
- if (layoutMode) {
- mergeLayoutParam(this.option, newSeriesOption, layoutMode);
- }
+ for (var idx = frameStart; idx < frameEnd; idx++) {
+ var rawIndex = this.getRawIndex(idx);
+ var y = dimStore[rawIndex];
- var sourceManager = inner$1(this).sourceManager;
- sourceManager.dirty();
- sourceManager.prepareSource();
- var data = this.getInitialData(newSeriesOption, ecModel);
- wrapData(data, this);
- this.dataTask.dirty();
- this.dataTask.context.data = data;
- inner$1(this).dataBeforeProcessed = data;
- autoSeriesName(this);
+ if (isNaN(y)) {
+ continue;
+ } // Calculate triangle area over three buckets
- this._initSelectedMapFromData(data);
- };
- SeriesModel.prototype.fillDataTextStyle = function (data) {
- // Default data label emphasis `show`
- // FIXME Tree structure data ?
- // FIXME Performance ?
- if (data && !isTypedArray(data)) {
- var props = ['show'];
+ area = Math.abs((pointAX - avgX) * (y - pointAY) - (pointAX - idx) * (avgY - pointAY));
- for (var i = 0; i < data.length; i++) {
- if (data[i] && data[i].label) {
- defaultEmphasis(data[i], 'label', props);
+ if (area > maxArea) {
+ maxArea = area;
+ nextRawIndex = rawIndex; // Next a is this b
}
}
- }
- };
- /**
- * Init a data structure from data related option in series
- * Must be overriden.
- */
-
- SeriesModel.prototype.getInitialData = function (option, ecModel) {
- return;
- };
- /**
- * Append data to list
- */
+ newIndices[sampledIndex++] = nextRawIndex;
+ currentRawIndex = nextRawIndex; // This a is the next a (chosen b)
+ } // First frame use the last data.
- 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);
+ newIndices[sampledIndex++] = this.getRawIndex(len - 1);
+ target._count = sampledIndex;
+ target._indices = newIndices;
+ target.getRawIndex = this._getRawIdx;
+ return target;
};
/**
- * 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.
+ * Large data down sampling on given dimension
+ * @param sampleIndex Sample index for name and id
*/
- SeriesModel.prototype.getData = function (dataType) {
- var task = getCurrentTask(this);
+ DataStore.prototype.downSample = function (dimension, rate, sampleValue, sampleIndex) {
+ var target = this.clone([dimension], true);
+ var targetStorage = target._chunks;
+ var frameValues = [];
+ var frameSize = Math.floor(1 / rate);
+ var dimStore = targetStorage[dimension];
+ var len = this.count();
+ var rawExtentOnDim = target._rawExtent[dimension] = getInitialExtent();
+ var newIndices = new (getIndicesCtor(this._rawCount))(Math.ceil(len / frameSize));
+ var offset = 0;
- if (task) {
- 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;
- }
- };
+ for (var i = 0; i < len; i += frameSize) {
+ // Last frame
+ if (frameSize > len - i) {
+ frameSize = len - i;
+ frameValues.length = frameSize;
+ }
- SeriesModel.prototype.getAllData = function () {
- var mainData = this.getData();
- return mainData && mainData.getLinkedDataAll ? mainData.getLinkedDataAll() : [{
- data: mainData
- }];
- };
+ for (var k = 0; k < frameSize; k++) {
+ var dataIdx = this.getRawIndex(i + k);
+ frameValues[k] = dimStore[dataIdx];
+ }
- SeriesModel.prototype.setData = function (data) {
- var task = getCurrentTask(this);
+ var value = sampleValue(frameValues);
+ var sampleFrameIdx = this.getRawIndex(Math.min(i + sampleIndex(frameValues, value) || 0, len - 1)); // Only write value on the filtered data
- if (task) {
- 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());
- // }
+ dimStore[sampleFrameIdx] = value;
- 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 (value < rawExtentOnDim[0]) {
+ rawExtentOnDim[0] = value;
+ }
- if (task !== this.dataTask) {
- context.data = data;
+ if (value > rawExtentOnDim[1]) {
+ rawExtentOnDim[1] = value;
}
- }
- inner$1(this).data = data;
- };
+ newIndices[offset++] = sampleFrameIdx;
+ }
- SeriesModel.prototype.getSource = function () {
- return inner$1(this).sourceManager.getSource();
- };
- /**
- * Get data before processed
- */
+ target._count = offset;
+ target._indices = newIndices;
+ target._updateGetRawIdx();
- SeriesModel.prototype.getRawData = function () {
- return inner$1(this).dataBeforeProcessed;
+ return target;
};
/**
- * Get base axis if has coordinate system and has axis.
- * By default use coordSys.getBaseAxis();
- * Can be overrided for some chart.
- * @return {type} description
+ * Data iteration
+ * @param ctx default this
+ * @example
+ * list.each('x', function (x, idx) {});
+ * list.each(['x', 'y'], function (x, y, idx) {});
+ * list.each(function (idx) {})
*/
- SeriesModel.prototype.getBaseAxis = function () {
- 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
- */
-
+ DataStore.prototype.each = function (dims, cb) {
+ if (!this._count) {
+ return;
+ }
- SeriesModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) {
- return defaultSeriesFormatTooltip({
- series: this,
- dataIndex: dataIndex,
- multipleSeries: multipleSeries
- });
- };
+ var dimSize = dims.length;
+ var chunks = this._chunks;
- SeriesModel.prototype.isAnimationEnabled = function () {
- if (env.node) {
- return false;
- }
+ for (var i = 0, len = this.count(); i < len; i++) {
+ var rawIdx = this.getRawIndex(i); // Simple optimization
- var animationEnabled = this.getShallow('animation');
+ switch (dimSize) {
+ case 0:
+ cb(i);
+ break;
- if (animationEnabled) {
- if (this.getData().count() > this.getShallow('animationThreshold')) {
- animationEnabled = false;
- }
- }
+ case 1:
+ cb(chunks[dims[0]][rawIdx], i);
+ break;
- return !!animationEnabled;
- };
+ case 2:
+ cb(chunks[dims[0]][rawIdx], chunks[dims[1]][rawIdx], i);
+ break;
- SeriesModel.prototype.restoreData = function () {
- this.dataTask.dirty();
- };
+ default:
+ var k = 0;
+ var value = [];
- SeriesModel.prototype.getColorFromPalette = function (name, scope, requestColorNum) {
- var ecModel = this.ecModel; // PENDING
+ for (; k < dimSize; k++) {
+ value[k] = chunks[dims[k]][rawIdx];
+ } // Index
- var color = PaletteMixin.prototype.getColorFromPalette.call(this, name, scope, requestColorNum);
- if (!color) {
- color = ecModel.getColorFromPalette(name, scope, requestColorNum);
+ value[k] = i;
+ cb.apply(null, value);
+ }
}
-
- return color;
};
/**
- * Use `data.mapDimensionsAll(coordDim)` instead.
- * @deprecated
+ * Get extent of data in one dimension
*/
- SeriesModel.prototype.coordDimToDataDim = function (coordDim) {
- return this.getRawData().mapDimensionsAll(coordDim);
- };
- /**
- * Get progressive rendering count each step
- */
-
+ DataStore.prototype.getDataExtent = function (dim) {
+ // Make sure use concrete dim as cache name.
+ var dimData = this._chunks[dim];
+ var initialExtent = getInitialExtent();
- SeriesModel.prototype.getProgressive = function () {
- return this.get('progressive');
- };
- /**
- * Get progressive rendering count each step
- */
+ if (!dimData) {
+ return initialExtent;
+ } // Make more strict checkings to ensure hitting cache.
- SeriesModel.prototype.getProgressiveThreshold = function () {
- return this.get('progressiveThreshold');
- }; // PENGING If selectedMode is null ?
+ var currEnd = this.count(); // Consider the most cases when using data zoom, `getDataExtent`
+ // happened before filtering. We cache raw extent, which is not
+ // necessary to be cleared and recalculated when restore data.
+ var useRaw = !this._indices;
+ var dimExtent;
- SeriesModel.prototype.select = function (innerDataIndices, dataType) {
- this._innerSelect(this.getData(dataType), innerDataIndices);
- };
+ if (useRaw) {
+ return this._rawExtent[dim].slice();
+ }
- SeriesModel.prototype.unselect = function (innerDataIndices, dataType) {
- var selectedMap = this.option.selectedMap;
+ dimExtent = this._extent[dim];
- if (!selectedMap) {
- return;
+ if (dimExtent) {
+ return dimExtent.slice();
}
- var data = this.getData(dataType);
+ dimExtent = initialExtent;
+ var min = dimExtent[0];
+ var max = dimExtent[1];
- for (var i = 0; i < innerDataIndices.length; i++) {
- var dataIndex = innerDataIndices[i];
- var nameOrId = getSelectionKey(data, dataIndex);
- selectedMap[nameOrId] = false;
- this._selectedDataIndicesMap[nameOrId] = -1;
+ for (var i = 0; i < currEnd; i++) {
+ var rawIdx = this.getRawIndex(i);
+ var value = dimData[rawIdx];
+ value < min && (min = value);
+ value > max && (max = value);
}
+
+ dimExtent = [min, max];
+ this._extent[dim] = dimExtent;
+ return dimExtent;
};
+ /**
+ * Get raw data item
+ */
- SeriesModel.prototype.toggleSelect = function (innerDataIndices, dataType) {
- var tmpArr = [];
- for (var i = 0; i < innerDataIndices.length; i++) {
- tmpArr[0] = innerDataIndices[i];
- this.isSelected(innerDataIndices[i], dataType) ? this.unselect(tmpArr, dataType) : this.select(tmpArr, dataType);
+ DataStore.prototype.getRawDataItem = function (idx) {
+ var rawIdx = this.getRawIndex(idx);
+
+ if (!this._provider.persistent) {
+ var val = [];
+ var chunks = this._chunks;
+
+ for (var i = 0; i < chunks.length; i++) {
+ val.push(chunks[i][rawIdx]);
+ }
+
+ return val;
+ } else {
+ return this._provider.getItem(rawIdx);
}
};
+ /**
+ * Clone shallow.
+ *
+ * @param clonedDims Determine which dims to clone. Will share the data if not specified.
+ */
- SeriesModel.prototype.getSelectedDataIndices = function () {
- var selectedDataIndicesMap = this._selectedDataIndicesMap;
- var nameOrIds = keys(selectedDataIndicesMap);
- var dataIndices = [];
- for (var i = 0; i < nameOrIds.length; i++) {
- var dataIndex = selectedDataIndicesMap[nameOrIds[i]];
+ DataStore.prototype.clone = function (clonedDims, ignoreIndices) {
+ var target = new DataStore();
+ var chunks = this._chunks;
+ var clonedDimsMap = clonedDims && reduce(clonedDims, function (obj, dimIdx) {
+ obj[dimIdx] = true;
+ return obj;
+ }, {});
- if (dataIndex >= 0) {
- dataIndices.push(dataIndex);
+ if (clonedDimsMap) {
+ for (var i = 0; i < chunks.length; i++) {
+ // Not clone if dim is not picked.
+ target._chunks[i] = !clonedDimsMap[i] ? chunks[i] : cloneChunk(chunks[i]);
}
+ } else {
+ target._chunks = chunks;
}
- return dataIndices;
- };
-
- SeriesModel.prototype.isSelected = function (dataIndex, dataType) {
- var selectedMap = this.option.selectedMap;
+ this._copyCommonProps(target);
- if (!selectedMap) {
- return false;
+ if (!ignoreIndices) {
+ target._indices = this._cloneIndices();
}
- var data = this.getData(dataType);
- var nameOrId = getSelectionKey(data, dataIndex);
- return selectedMap[nameOrId] || false;
- };
-
- SeriesModel.prototype._innerSelect = function (data, innerDataIndices) {
- var _a, _b;
+ target._updateGetRawIdx();
- var selectedMode = this.option.selectedMode;
- var len = innerDataIndices.length;
+ return target;
+ };
- if (!selectedMode || !len) {
- return;
- }
+ DataStore.prototype._copyCommonProps = function (target) {
+ target._count = this._count;
+ target._rawCount = this._rawCount;
+ target._provider = this._provider;
+ target._dimensions = this._dimensions;
+ target._extent = clone(this._extent);
+ target._rawExtent = clone(this._rawExtent);
+ };
- if (selectedMode === 'multiple') {
- var selectedMap = this.option.selectedMap || (this.option.selectedMap = {});
+ DataStore.prototype._cloneIndices = function () {
+ if (this._indices) {
+ var Ctor = this._indices.constructor;
+ var indices = void 0;
- for (var i = 0; i < len; i++) {
- var dataIndex = innerDataIndices[i]; // TODO diffrent types of data share same object.
+ if (Ctor === Array) {
+ var thisCount = this._indices.length;
+ indices = new Ctor(thisCount);
- var nameOrId = getSelectionKey(data, dataIndex);
- selectedMap[nameOrId] = true;
- this._selectedDataIndicesMap[nameOrId] = data.getRawIndex(dataIndex);
+ for (var i = 0; i < thisCount; i++) {
+ indices[i] = this._indices[i];
+ }
+ } else {
+ indices = new Ctor(this._indices);
}
- } else if (selectedMode === 'single' || selectedMode === true) {
- var lastDataIndex = innerDataIndices[len - 1];
- var nameOrId = getSelectionKey(data, lastDataIndex);
- this.option.selectedMap = (_a = {}, _a[nameOrId] = true, _a);
- this._selectedDataIndicesMap = (_b = {}, _b[nameOrId] = data.getRawIndex(lastDataIndex), _b);
- }
- };
- 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;
+ return indices;
}
- var dataIndices = [];
-
- if (data.hasItemOption) {
- data.each(function (idx) {
- var rawItem = data.getRawDataItem(idx);
+ return null;
+ };
- if (rawItem && rawItem.selected) {
- dataIndices.push(idx);
- }
- });
- }
+ DataStore.prototype._getRawIdxIdentity = function (idx) {
+ return idx;
+ };
- if (dataIndices.length > 0) {
- this._innerSelect(data, dataIndices);
+ DataStore.prototype._getRawIdx = function (idx) {
+ if (idx < this._count && idx >= 0) {
+ return this._indices[idx];
}
- }; // /**
- // * @see {module:echarts/stream/Scheduler}
- // */
- // abstract pipeTask: null
+ return -1;
+ };
- SeriesModel.registerClass = function (clz) {
- return ComponentModel.registerClass(clz);
+ DataStore.prototype._updateGetRawIdx = function () {
+ this.getRawIndex = this._indices ? this._getRawIdx : this._getRawIdxIdentity;
};
- SeriesModel.protoInitialize = function () {
- var proto = SeriesModel.prototype;
- proto.type = 'series.__base__';
- proto.seriesIndex = 0;
- proto.useColorPaletteOnData = false;
- proto.ignoreStyleOnData = false;
- proto.hasSymbolVisual = false;
- proto.defaultSymbol = 'circle'; // Make sure the values can be accessed!
+ DataStore.internalField = function () {
+ function getDimValueSimply(dataItem, property, dataIndex, dimIndex) {
+ return parseDataValue(dataItem[dimIndex], this._dimensions[dimIndex]);
+ }
- proto.visualStyleAccessPath = 'itemStyle';
- proto.visualDrawType = 'fill';
+ defaultDimValueGetters = {
+ arrayRows: getDimValueSimply,
+ objectRows: function (dataItem, property, dataIndex, dimIndex) {
+ return parseDataValue(dataItem[property], this._dimensions[dimIndex]);
+ },
+ keyedColumns: getDimValueSimply,
+ original: function (dataItem, property, dataIndex, dimIndex) {
+ // Performance sensitive, do not use modelUtil.getDataItemValue.
+ // If dataItem is an plain object with no value field, the let `value`
+ // will be assigned with the object, but it will be tread correctly
+ // in the `convertValue`.
+ var value = dataItem && (dataItem.value == null ? dataItem : dataItem.value);
+ return parseDataValue(value instanceof Array ? value[dimIndex] // If value is a single number or something else not array.
+ : value, this._dimensions[dimIndex]);
+ },
+ typedArray: function (dataItem, property, dataIndex, dimIndex) {
+ return dataItem[dimIndex];
+ }
+ };
}();
- return SeriesModel;
- }(ComponentModel);
+ return DataStore;
+ }();
- 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)) {
- seriesModel.name = getSeriesAutoName(seriesModel) || name;
- }
- }
-
- function getSeriesAutoName(seriesModel) {
- var data = seriesModel.getRawData();
- var dataDims = data.mapDimensionsAll('seriesName');
- var nameArr = [];
- each(dataDims, function (dataDim) {
- var dimInfo = data.getDimensionInfo(dataDim);
- dimInfo.displayName && nameArr.push(dimInfo.displayName);
- });
- return nameArr.join(' ');
- }
-
- function dataTaskCount(context) {
- return context.model.getRawData().count();
- }
+ * [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
+ * }]
+ * ```
+ */
- function dataTaskReset(context) {
- var seriesModel = context.model;
- seriesModel.setData(seriesModel.getRawData().cloneShallow());
- return dataTaskProgress;
- }
+ var SourceManager =
+ /** @class */
+ function () {
+ function SourceManager(sourceHost) {
+ // Cached source. Do not repeat calculating if not dirty.
+ this._sourceList = [];
+ this._storeList = []; // version sign of each upstream source manager.
- 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);
+ this._upstreamSignList = [];
+ this._versionSignBase = 0;
+ this._dirty = true;
+ this._sourceHost = sourceHost;
}
- } // TODO refactor
+ /**
+ * Mark dirty.
+ */
- function wrapData(data, seriesModel) {
- each(__spreadArrays(data.CHANGABLE_METHODS, data.DOWNSAMPLE_METHODS), function (methodName) {
- data.wrapMethod(methodName, curry(onDataChange, seriesModel));
- });
- }
+ SourceManager.prototype.dirty = function () {
+ this._setLocalSource([], []);
- function onDataChange(seriesModel, newList) {
- var task = getCurrentTask(seriesModel);
+ this._storeList = [];
+ this._dirty = true;
+ };
- if (task) {
- // Consider case: filter, selectRange
- task.setOutputEnd((newList || this).count());
- }
+ SourceManager.prototype._setLocalSource = function (sourceList, upstreamSignList) {
+ this._sourceList = sourceList;
+ this._upstreamSignList = upstreamSignList;
+ this._versionSignBase++;
- return newList;
- }
+ if (this._versionSignBase > 9e10) {
+ this._versionSignBase = 0;
+ }
+ };
+ /**
+ * For detecting whether the upstream source is dirty, so that
+ * the local cached source (in `_sourceList`) should be discarded.
+ */
- function getCurrentTask(seriesModel) {
- var scheduler = (seriesModel.ecModel || {}).scheduler;
- var pipeline = scheduler && scheduler.getPipeline(seriesModel.uid);
- if (pipeline) {
- // When pipline finished, the currrentTask keep the last
- // task (renderTask).
- var task = pipeline.currentTask;
+ SourceManager.prototype._getVersionSign = function () {
+ return this._sourceHost.uid + '_' + this._versionSignBase;
+ };
+ /**
+ * Always return a source instance. Otherwise throw error.
+ */
- if (task) {
- var agentStubMap = task.agentStubMap;
- if (agentStubMap) {
- task = agentStubMap.get(seriesModel.uid);
- }
+ 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();
+
+ this._dirty = false;
}
+ };
- return task;
- }
- }
+ SourceManager.prototype._createSource = function () {
+ this._setLocalSource([], []);
- var ComponentView =
- /** @class */
- function () {
- function ComponentView() {
- this.group = new Group();
- this.uid = getUID('viewComponent');
- }
+ var sourceHost = this._sourceHost;
- ComponentView.prototype.init = function (ecModel, api) {};
+ var upSourceMgrList = this._getUpstreamSourceManagers();
- ComponentView.prototype.render = function (model, ecModel, api, payload) {};
+ var hasUpstream = !!upSourceMgrList.length;
+ var resultSourceList;
+ var upstreamSignList;
- ComponentView.prototype.dispose = function (ecModel, api) {};
+ if (isSeries(sourceHost)) {
+ var seriesModel = sourceHost;
+ var data = void 0;
+ var sourceFormat = void 0;
+ var upSource = void 0; // Has upstream dataset
- ComponentView.prototype.updateView = function (model, ecModel, api, payload) {// Do nothing;
- };
+ if (hasUpstream) {
+ var upSourceMgr = upSourceMgrList[0];
+ upSourceMgr.prepareSource();
+ upSource = upSourceMgr.getSource();
+ data = upSource.data;
+ sourceFormat = upSource.sourceFormat;
+ upstreamSignList = [upSourceMgr._getVersionSign()];
+ } // 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.
- ComponentView.prototype.updateLayout = function (model, ecModel, api, payload) {// Do nothing;
- };
- ComponentView.prototype.updateVisual = function (model, ecModel, api, payload) {// Do nothing;
- };
- /**
- * Hook for blur target series.
- * Can be used in marker for blur the markers
- */
+ var newMetaRawOption = this._getSourceMetaRawOption() || {};
+ var upMetaRawOption = upSource && upSource.metaRawOption || {};
+ var seriesLayoutBy = retrieve2(newMetaRawOption.seriesLayoutBy, upMetaRawOption.seriesLayoutBy) || null;
+ var sourceHeader = retrieve2(newMetaRawOption.sourceHeader, 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.dimensions); // We share source with dataset as much as possible
+ // to avoid extra memroy cost of high dimensional data.
- ComponentView.prototype.blurSeries = function (seriesModels, ecModel) {// Do nothing;
- };
+ var needsCreateSource = seriesLayoutBy !== upMetaRawOption.seriesLayoutBy || !!sourceHeader !== !!upMetaRawOption.sourceHeader || dimensions;
+ resultSourceList = needsCreateSource ? [createSource(data, {
+ seriesLayoutBy: seriesLayoutBy,
+ sourceHeader: sourceHeader,
+ dimensions: dimensions
+ }, sourceFormat)] : [];
+ } else {
+ var datasetModel = sourceHost; // Has upstream dataset.
- return ComponentView;
- }();
- enableClassExtend(ComponentView);
- enableClassManagement(ComponentView);
+ if (hasUpstream) {
+ var result = this._applyTransform(upSourceMgrList);
- /**
- * @return {string} If large mode changed, return string 'reset';
- */
+ resultSourceList = result.sourceList;
+ upstreamSignList = result.upstreamSignList;
+ } // Is root dataset.
+ else {
+ var sourceData = datasetModel.get('source', true);
+ resultSourceList = [createSource(sourceData, this._getSourceMetaRawOption(), null)];
+ upstreamSignList = [];
+ }
+ }
- function createRenderPlanner() {
- var inner = makeInner();
- return function (seriesModel) {
- var fields = inner(seriesModel);
- var pipelineContext = seriesModel.pipelineContext;
- var originalLarge = !!fields.large;
- 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`.
+ if ("development" !== 'production') {
+ assert(resultSourceList && upstreamSignList);
+ }
- var large = fields.large = !!(pipelineContext && pipelineContext.large);
- var progressive = fields.progressiveRender = !!(pipelineContext && pipelineContext.progressiveRender);
- return !!(originalLarge !== large || originalProgressive !== progressive) && 'reset';
+ this._setLocalSource(resultSourceList, upstreamSignList);
};
- }
- var inner$2 = makeInner();
- var renderPlanner = createRenderPlanner();
+ SourceManager.prototype._applyTransform = function (upMgrList) {
+ var datasetModel = this._sourceHost;
+ var transformOption = datasetModel.get('transform', true);
+ var fromTransformResult = datasetModel.get('fromTransformResult', true);
- var ChartView =
- /** @class */
- function () {
- function ChartView() {
- this.group = new Group();
- this.uid = getUID('viewChart');
- this.renderTask = createTask({
- plan: renderTaskPlan,
- reset: renderTaskReset
+ if ("development" !== 'production') {
+ assert(fromTransformResult != null || transformOption != null);
+ }
+
+ if (fromTransformResult != null) {
+ var errMsg = '';
+
+ if (upMgrList.length !== 1) {
+ if ("development" !== 'production') {
+ errMsg = 'When using `fromTransformResult`, there should be only one upstream dataset';
+ }
+
+ doThrow(errMsg);
+ }
+ }
+
+ var sourceList;
+ var upSourceList = [];
+ var upstreamSignList = [];
+ each(upMgrList, function (upMgr) {
+ upMgr.prepareSource();
+ var upSource = upMgr.getSource(fromTransformResult || 0);
+ var errMsg = '';
+
+ if (fromTransformResult != null && !upSource) {
+ if ("development" !== 'production') {
+ errMsg = 'Can not retrieve result by `fromTransformResult`: ' + fromTransformResult;
+ }
+
+ doThrow(errMsg);
+ }
+
+ upSourceList.push(upSource);
+ upstreamSignList.push(upMgr._getVersionSign());
});
- this.renderTask.context = {
- view: this
+
+ if (transformOption) {
+ sourceList = applyDataTransform(transformOption, upSourceList, {
+ datasetIndex: datasetModel.componentIndex
+ });
+ } else if (fromTransformResult != null) {
+ sourceList = [cloneSourceShallow(upSourceList[0])];
+ }
+
+ return {
+ sourceList: sourceList,
+ upstreamSignList: upstreamSignList
};
- }
+ };
- ChartView.prototype.init = function (ecModel, api) {};
+ SourceManager.prototype._isDirty = function () {
+ if (this._dirty) {
+ return true;
+ } // All sourceList is from the some upsteam.
- ChartView.prototype.render = function (seriesModel, ecModel, api, payload) {};
- /**
- * Highlight series or specified data item.
- */
+ var upSourceMgrList = this._getUpstreamSourceManagers();
- ChartView.prototype.highlight = function (seriesModel, ecModel, api, payload) {
- toggleHighlight(seriesModel.getData(), payload, 'emphasis');
+ for (var i = 0; i < upSourceMgrList.length; i++) {
+ var upSrcMgr = upSourceMgrList[i];
+
+ 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;
+ }
+ }
};
/**
- * Downplay series or specified data item.
+ * @param sourceIndex By defualt 0, means "main source".
+ * Most cases there is only one source.
*/
- ChartView.prototype.downplay = function (seriesModel, ecModel, api, payload) {
- toggleHighlight(seriesModel.getData(), payload, 'normal');
+ SourceManager.prototype.getSource = function (sourceIndex) {
+ sourceIndex = sourceIndex || 0;
+ var source = this._sourceList[sourceIndex];
+
+ if (!source) {
+ // Series may share source instance with dataset.
+ var upSourceMgrList = this._getUpstreamSourceManagers();
+
+ return upSourceMgrList[0] && upSourceMgrList[0].getSource(sourceIndex);
+ }
+
+ return source;
};
/**
- * Remove self.
+ *
+ * Get a data store which can be shared across series.
+ * Only available for series.
+ *
+ * @param seriesDimRequest Dimensions that are generated in series.
+ * Should have been sorted by `storeDimIndex` asc.
*/
- ChartView.prototype.remove = function (ecModel, api) {
- this.group.removeAll();
+ SourceManager.prototype.getSharedDataStore = function (seriesDimRequest) {
+ if ("development" !== 'production') {
+ assert(isSeries(this._sourceHost), 'Can only call getDataStore on series source manager.');
+ }
+
+ var schema = seriesDimRequest.makeStoreSchema();
+ return this._innerGetDataStore(schema.dimensions, seriesDimRequest.source, schema.hash);
};
- /**
- * Dispose self.
- */
+ SourceManager.prototype._innerGetDataStore = function (storeDims, seriesSource, sourceReadKey) {
+ // TODO Can use other sourceIndex?
+ var sourceIndex = 0;
+ var storeList = this._storeList;
+ var cachedStoreMap = storeList[sourceIndex];
- ChartView.prototype.dispose = function (ecModel, api) {};
+ if (!cachedStoreMap) {
+ cachedStoreMap = storeList[sourceIndex] = {};
+ }
- ChartView.prototype.updateView = function (seriesModel, ecModel, api, payload) {
- this.render(seriesModel, ecModel, api, payload);
- }; // FIXME never used?
+ var cachedStore = cachedStoreMap[sourceReadKey];
+ if (!cachedStore) {
+ var upSourceMgr = this._getUpstreamSourceManagers()[0];
- ChartView.prototype.updateLayout = function (seriesModel, ecModel, api, payload) {
- this.render(seriesModel, ecModel, api, payload);
- }; // FIXME never used?
+ if (isSeries(this._sourceHost) && upSourceMgr) {
+ cachedStore = upSourceMgr._innerGetDataStore(storeDims, seriesSource, sourceReadKey);
+ } else {
+ cachedStore = new DataStore(); // Always create store from source of series.
+ cachedStore.initData(new DefaultDataProvider(seriesSource, storeDims.length), storeDims);
+ }
- ChartView.prototype.updateVisual = function (seriesModel, ecModel, api, payload) {
- this.render(seriesModel, ecModel, api, payload);
+ cachedStoreMap[sourceReadKey] = cachedStore;
+ }
+
+ return cachedStore;
};
+ /**
+ * PEDING: Is it fast enough?
+ * If no upstream, return empty array.
+ */
- ChartView.markUpdateMethod = function (payload, methodName) {
- inner$2(payload).updateMethod = methodName;
+
+ 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)) {
+ var datasetModel = querySeriesUpstreamDatasetModel(sourceHost);
+ return !datasetModel ? [] : [datasetModel.getSourceManager()];
+ } else {
+ return map(queryDatasetUpstreamDatasetModels(sourceHost), function (datasetModel) {
+ return datasetModel.getSourceManager();
+ });
+ }
};
- ChartView.protoInitialize = function () {
- var proto = ChartView.prototype;
- proto.type = 'chart';
- }();
+ SourceManager.prototype._getSourceMetaRawOption = function () {
+ var sourceHost = this._sourceHost;
+ var seriesLayoutBy;
+ var sourceHeader;
+ var dimensions;
- return ChartView;
- }();
- /**
- * Set state of single element
- */
+ if (isSeries(sourceHost)) {
+ seriesLayoutBy = sourceHost.get('seriesLayoutBy', true);
+ sourceHeader = sourceHost.get('sourceHeader', true);
+ dimensions = sourceHost.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);
+ }
- function elSetState(el, state, highlightDigit) {
- if (el) {
- (state === 'emphasis' ? enterEmphasis : leaveEmphasis)(el, highlightDigit);
- }
- }
+ return {
+ seriesLayoutBy: seriesLayoutBy,
+ sourceHeader: sourceHeader,
+ dimensions: dimensions
+ };
+ };
- function toggleHighlight(data, payload, state) {
- var dataIndex = queryDataIndex(data, payload);
- var highlightDigit = payload && payload.highlightKey != null ? getHighlightDigit(payload.highlightKey) : null;
+ return SourceManager;
+ }();
+ // disable the transform merge, but do not disable transfrom clone from rawOption.
- if (dataIndex != null) {
- each(normalizeToArray(dataIndex), function (dataIdx) {
- elSetState(data.getItemGraphicEl(dataIdx), state, highlightDigit);
- });
- } else {
- data.eachItemGraphicEl(function (el) {
- elSetState(el, state, highlightDigit);
- });
- }
+ function disableTransformOptionMerge(datasetModel) {
+ var transformOption = datasetModel.option.transform;
+ transformOption && setAsPrimitive(datasetModel.option.transform);
}
- enableClassExtend(ChartView, ['dispose']);
- enableClassManagement(ChartView);
+ function isSeries(sourceHost) {
+ // Avoid circular dependency with Series.ts
+ return sourceHost.mainType === 'series';
+ }
- function renderTaskPlan(context) {
- return renderPlanner(context.model);
+ function doThrow(errMsg) {
+ throw new Error(errMsg);
}
- function renderTaskReset(context) {
- var seriesModel = context.model;
- var ecModel = context.ecModel;
- var api = context.api;
- var payload = context.payload; // FIXME: remove updateView updateVisual
+ var TOOLTIP_LINE_HEIGHT_CSS = 'line-height:1'; // TODO: more textStyle option
- var progressiveRender = seriesModel.pipelineContext.progressiveRender;
- var view = context.view;
- var updateMethod = payload && inner$2(payload).updateMethod;
- var methodName = progressiveRender ? 'incrementalPrepareRender' : updateMethod && view[updateMethod] ? updateMethod // `appendData` is also supported when data amount
- // is less than progressive threshold.
- : 'render';
+ function getTooltipTextStyle(textStyle, renderMode) {
+ var nameFontColor = textStyle.color || '#6e7079';
+ var nameFontSize = textStyle.fontSize || 12;
+ var nameFontWeight = textStyle.fontWeight || '400';
+ var valueFontColor = textStyle.color || '#464646';
+ var valueFontSize = textStyle.fontSize || 14;
+ var valueFontWeight = textStyle.fontWeight || '900';
- if (methodName !== 'render') {
- view[methodName](seriesModel, ecModel, api, payload);
+ if (renderMode === 'html') {
+ // `textStyle` is probably from user input, should be encoded to reduce security risk.
+ return {
+ // 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 {
+ nameStyle: {
+ fontSize: nameFontSize,
+ fill: nameFontColor,
+ fontWeight: nameFontWeight
+ },
+ valueStyle: {
+ fontSize: valueFontSize,
+ fill: valueFontColor,
+ fontWeight: valueFontWeight
+ }
+ };
}
+ } // See `TooltipMarkupLayoutIntent['innerGapLevel']`.
+ // (value from UI design)
- return progressMethodMap[methodName];
- }
- var progressMethodMap = {
- incrementalPrepareRender: {
- progress: function (params, context) {
- context.view.incrementalRender(params, context.model, context.ecModel, context.api, context.payload);
- }
- },
- 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);
- }
- }
- };
-
- /*
- * 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.
- */
-
-
- /**
- * AUTO-GENERATED FILE. DO NOT MODIFY.
- */
+ var HTML_GAPS = [0, 10, 20, 30];
+ var RICH_TEXT_GAPS = ['', '\n', '\n\n', '\n\n\n']; // eslint-disable-next-line max-len
- /*
- * 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 createTooltipMarkup(type, option) {
+ option.type = type;
+ return option;
+ }
- function throttle(fn, delay, debounce) {
- var currCall;
- var lastCall = 0;
- var lastExec = 0;
- var timer = null;
- var diff;
- var scope;
- var args;
- var debounceNextCall;
- delay = delay || 0;
+ function getBuilder(fragment) {
+ return hasOwn(builderMap, fragment.type) && builderMap[fragment.type];
+ }
- function exec() {
- lastExec = new Date().getTime();
- timer = null;
- fn.apply(scope, args || []);
- }
+ var builderMap = {
+ /**
+ * A `section` block is like:
+ * ```
+ * header
+ * subBlock
+ * subBlock
+ * ...
+ * ```
+ */
+ section: {
+ planLayout: function (fragment) {
+ var subBlockLen = fragment.blocks.length;
+ var thisBlockHasInnerGap = subBlockLen > 1 || subBlockLen > 0 && !fragment.noHeader;
+ var thisGapLevelBetweenSubBlocks = 0;
+ each(fragment.blocks, function (subBlock) {
+ getBuilder(subBlock).planLayout(subBlock);
+ 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.
- var cb = function () {
- var cbArgs = [];
+ if (subGapLevel >= thisGapLevelBetweenSubBlocks) {
+ 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;
+ },
+ build: function (ctx, fragment, topMarginForOuterGap, toolTipTextStyle) {
+ var noHeader = fragment.noHeader;
+ var gaps = getGap(fragment);
+ var subMarkupText = buildSubBlocks(ctx, fragment, noHeader ? topMarginForOuterGap : gaps.html, toolTipTextStyle);
- for (var _i = 0; _i < arguments.length; _i++) {
- cbArgs[_i] = arguments[_i];
- }
+ if (noHeader) {
+ return subMarkupText;
+ }
- currCall = new Date().getTime();
- scope = this;
- args = cbArgs;
- var thisDelay = debounceNextCall || delay;
- var thisDebounce = debounceNextCall || debounce;
- debounceNextCall = null;
- diff = currCall - (thisDebounce ? lastCall : lastExec) - thisDelay;
- 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".
+ var displayableHeader = makeValueReadable(fragment.header, 'ordinal', ctx.useUTC);
+ var nameStyle = getTooltipTextStyle(toolTipTextStyle, ctx.renderMode).nameStyle;
- if (thisDebounce) {
- timer = setTimeout(exec, thisDelay);
- } else {
- if (diff >= 0) {
- exec();
+ if (ctx.renderMode === 'richText') {
+ return wrapInlineNameRichText(ctx, displayableHeader, nameStyle) + gaps.richText + subMarkupText;
} else {
- timer = setTimeout(exec, -diff);
+ return wrapBlockHTML("<div style=\"" + nameStyle + ";" + TOOLTIP_LINE_HEIGHT_CSS + ";\">" + encodeHTML(displayableHeader) + '</div>' + subMarkupText, topMarginForOuterGap);
}
}
+ },
- lastCall = currCall;
- };
/**
- * Clear throttle.
- * @public
+ * A `nameValue` block is like:
+ * ```
+ * marker name value
+ * ```
*/
+ nameValue: {
+ planLayout: function (fragment) {
+ fragment.__gapLevelBetweenSubBlocks = 0;
+ },
+ build: function (ctx, fragment, topMarginForOuterGap, toolTipTextStyle) {
+ var renderMode = ctx.renderMode;
+ var noName = fragment.noName;
+ var noValue = fragment.noValue;
+ var noMarker = !fragment.markerType;
+ var name = fragment.name;
+ var value = fragment.value;
+ var useUTC = ctx.useUTC;
+ if (noName && noValue) {
+ return;
+ }
- cb.clear = function () {
- if (timer) {
- clearTimeout(timer);
- timer = null;
- }
- };
- /**
- * Enable debounce once.
- */
+ var markerStr = noMarker ? '' : ctx.markupStyleCreator.makeTooltipMarker(fragment.markerType, fragment.markerColor || '#333', renderMode);
+ var readableName = noName ? '' : makeValueReadable(name, 'ordinal', useUTC);
+ var valueTypeOption = fragment.valueType;
+ 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; // It little weird if only value next to marker but far from marker.
+ var valueCloseToMarker = !noMarker && noName;
- cb.debounceNextCall = function (debounceDelay) {
- debounceNextCall = debounceDelay;
- };
+ var _a = getTooltipTextStyle(toolTipTextStyle, renderMode),
+ nameStyle = _a.nameStyle,
+ valueStyle = _a.valueStyle;
- 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');
- * };
- *
- */
+ 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);
+ }
+ }
+ };
- function createOrUpdate(obj, fnAttr, rate, throttleType) {
- var fn = obj[fnAttr];
+ function buildSubBlocks(ctx, fragment, topMarginForOuterGap, tooltipTextStyle) {
+ var subMarkupTextList = [];
+ var subBlocks = fragment.blocks || [];
+ assert(!subBlocks || isArray(subBlocks));
+ subBlocks = subBlocks || [];
+ var orderMode = ctx.orderMode;
- if (!fn) {
- return;
- }
+ if (fragment.sortBlocks && orderMode) {
+ subBlocks = subBlocks.slice();
+ var orderMap = {
+ valueAsc: 'asc',
+ valueDesc: 'desc'
+ };
- var originFn = fn[ORIGIN_METHOD] || fn;
- var lastThrottleType = fn[THROTTLE_TYPE];
- var lastRate = fn[RATE];
+ if (hasOwn(orderMap, orderMode)) {
+ var comparator_1 = new SortOrderComparator(orderMap[orderMode], null);
+ subBlocks.sort(function (a, b) {
+ return comparator_1.evaluate(a.sortParam, b.sortParam);
+ });
+ } // FIXME 'seriesDesc' necessary?
+ else if (orderMode === 'seriesDesc') {
+ subBlocks.reverse();
+ }
+ }
- if (lastRate !== rate || lastThrottleType !== throttleType) {
- if (rate == null || !throttleType) {
- return obj[fnAttr] = originFn;
- }
+ var gaps = getGap(fragment);
+ each(subBlocks, function (subBlock, idx) {
+ var subMarkupText = getBuilder(subBlock).build(ctx, subBlock, idx > 0 ? gaps.html : 0, tooltipTextStyle);
+ subMarkupText != null && subMarkupTextList.push(subMarkupText);
+ });
- fn = obj[fnAttr] = throttle(originFn, rate, throttleType === 'debounce');
- fn[ORIGIN_METHOD] = originFn;
- fn[THROTTLE_TYPE] = throttleType;
- fn[RATE] = rate;
+ if (!subMarkupTextList.length) {
+ return;
}
- return fn;
+ return ctx.renderMode === 'richText' ? subMarkupTextList.join(gaps.richText) : wrapBlockHTML(subMarkupTextList.join(''), topMarginForOuterGap);
}
/**
- * Clear throttle. Example see throttle.createOrUpdate.
+ * @return markupText. null/undefined means no content.
*/
- function clear(obj, fnAttr) {
- var fn = obj[fnAttr];
- if (fn && fn[ORIGIN_METHOD]) {
- obj[fnAttr] = fn[ORIGIN_METHOD];
+ function buildTooltipMarkup(fragment, markupStyleCreator, renderMode, orderMode, useUTC, toolTipTextStyle) {
+ if (!fragment) {
+ return;
}
- }
-
- var inner$3 = makeInner();
- var defaultStyleMappers = {
- itemStyle: makeStyleMapper(ITEM_STYLE_KEY_MAP, true),
- lineStyle: makeStyleMapper(LINE_STYLE_KEY_MAP, true)
- };
- var defaultColorKey = {
- lineStyle: 'stroke',
- itemStyle: 'fill'
- };
- function getStyleMapper(seriesModel, stylePath) {
- var styleMapper = seriesModel.visualStyleMapper || defaultStyleMappers[stylePath];
+ var builder = getBuilder(fragment);
+ builder.planLayout(fragment);
+ var ctx = {
+ useUTC: useUTC,
+ renderMode: renderMode,
+ orderMode: orderMode,
+ markupStyleCreator: markupStyleCreator
+ };
+ return builder.build(ctx, fragment, 0, toolTipTextStyle);
+ }
- if (!styleMapper) {
- console.warn("Unkown style type '" + stylePath + "'.");
- return defaultStyleMappers.itemStyle;
- }
+ function getGap(fragment) {
+ var gapLevelBetweenSubBlocks = fragment.__gapLevelBetweenSubBlocks;
+ return {
+ html: HTML_GAPS[gapLevelBetweenSubBlocks],
+ richText: RICH_TEXT_GAPS[gapLevelBetweenSubBlocks]
+ };
+ }
- return styleMapper;
+ function wrapBlockHTML(encodedContent, topGap) {
+ var clearfix = '<div style="clear:both"></div>';
+ var marginCSS = "margin: " + topGap + "px 0 0";
+ return "<div style=\"" + marginCSS + ";" + TOOLTIP_LINE_HEIGHT_CSS + ";\">" + encodedContent + clearfix + '</div>';
}
- function getDefaultColorKey(seriesModel, stylePath) {
- // return defaultColorKey[stylePath] ||
- var colorKey = seriesModel.visualDrawType || defaultColorKey[stylePath];
+ function wrapInlineNameHTML(name, leftHasMarker, style) {
+ var marginCss = leftHasMarker ? 'margin-left:2px' : '';
+ return "<span style=\"" + style + ";" + marginCss + "\">" + encodeHTML(name) + '</span>';
+ }
- if (!colorKey) {
- console.warn("Unkown style type '" + stylePath + "'.");
- return 'fill';
- }
+ 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 + "\">" // Value has commas inside, so use ' ' as delimiter for multiple values.
+ + map(valueList, function (value) {
+ return encodeHTML(value);
+ }).join(' ') + '</span>';
+ }
- return colorKey;
+ function wrapInlineNameRichText(ctx, name, style) {
+ return ctx.markupStyleCreator.wrapRichTextStyle(name, style);
}
- var seriesStyleTask = {
- createOnAllSeries: true,
- performRawSeries: true,
- reset: function (seriesModel, ecModel) {
- var data = seriesModel.getData();
- var stylePath = seriesModel.visualStyleAccessPath || 'itemStyle'; // Set in itemStyle
+ function wrapInlineValueRichText(ctx, valueList, alignRight, valueCloseToMarker, style) {
+ var styles = [style];
+ var paddingLeft = valueCloseToMarker ? 10 : 20;
+ alignRight && styles.push({
+ padding: [0, 0, 0, paddingLeft],
+ align: 'right'
+ }); // Value has commas inside, so use ' ' as delimiter for multiple values.
- var styleModel = seriesModel.getModel(stylePath);
- var getStyle = getStyleMapper(seriesModel, stylePath);
- var globalStyle = getStyle(styleModel);
- var decalOption = styleModel.getShallow('decal');
+ return ctx.markupStyleCreator.wrapRichTextStyle(valueList.join(' '), styles);
+ }
- if (decalOption) {
- data.setVisual('decal', decalOption);
- decalOption.dirty = true;
- } // TODO
+ function retrieveVisualColorForTooltipMarker(series, dataIndex) {
+ var style = series.getData().getItemVisual(dataIndex, 'style');
+ var color = style[series.visualDrawType];
+ return convertToColorString(color);
+ }
+ function getPaddingFromTooltipModel(model, renderMode) {
+ var padding = model.get('padding');
+ 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 =
+ /** @class */
+ function () {
+ function TooltipMarkupStyleCreator() {
+ 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).
- var colorKey = getDefaultColorKey(seriesModel, stylePath);
- var color = globalStyle[colorKey]; // TODO style callback
+ this._nextStyleNameId = getRandomIdBase();
+ }
- var colorCallback = isFunction(color) ? color : null;
- var hasAutoColor = globalStyle.fill === 'auto' || globalStyle.stroke === 'auto'; // Get from color palette by default.
+ TooltipMarkupStyleCreator.prototype._generateStyleName = function () {
+ return '__EC_aUTo_' + this._nextStyleNameId++;
+ };
- if (!globalStyle[colorKey] || colorCallback || hasAutoColor) {
- // 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.
- var colorPalette = seriesModel.getColorFromPalette( // TODO series count changed.
- seriesModel.name, null, ecModel.getSeriesCount());
+ TooltipMarkupStyleCreator.prototype.makeTooltipMarker = function (markerType, colorStr, renderMode) {
+ var markerId = renderMode === 'richText' ? this._generateStyleName() : null;
+ var marker = getTooltipMarker({
+ color: colorStr,
+ type: markerType,
+ renderMode: renderMode,
+ markerId: markerId
+ });
- if (!globalStyle[colorKey]) {
- globalStyle[colorKey] = colorPalette;
- data.setVisual('colorFromPalette', true);
+ if (isString(marker)) {
+ return marker;
+ } else {
+ if ("development" !== 'production') {
+ assert(markerId);
}
- globalStyle.fill = globalStyle.fill === 'auto' || typeof globalStyle.fill === 'function' ? colorPalette : globalStyle.fill;
- globalStyle.stroke = globalStyle.stroke === 'auto' || typeof globalStyle.stroke === 'function' ? colorPalette : globalStyle.stroke;
+ this.richTextStyles[markerId] = marker.style;
+ return marker.content;
}
+ };
+ /**
+ * @usage
+ * ```ts
+ * const styledText = markupStyleCreator.wrapRichTextStyle([
+ * // The styles will be auto merged.
+ * {
+ * fontSize: 12,
+ * color: 'blue'
+ * },
+ * {
+ * padding: 20
+ * }
+ * ]);
+ * ```
+ */
- data.setVisual('style', globalStyle);
- data.setVisual('drawType', colorKey); // Only visible series has each data be visual encoded
- if (!ecModel.isSeriesFiltered(seriesModel) && colorCallback) {
- data.setVisual('colorFromPalette', false);
- return {
- dataEach: function (data, idx) {
- var dataParams = seriesModel.getDataParams(idx);
- var itemStyle = extend({}, globalStyle);
- itemStyle[colorKey] = colorCallback(dataParams);
- data.setItemVisual(idx, 'style', itemStyle);
- }
- };
- }
- }
- };
- var sharedModel = new Model();
- var dataStyleTask = {
- createOnAllSeries: true,
- performRawSeries: true,
- reset: function (seriesModel, ecModel) {
- if (seriesModel.ignoreStyleOnData || ecModel.isSeriesFiltered(seriesModel)) {
- return;
+ TooltipMarkupStyleCreator.prototype.wrapRichTextStyle = function (text, styles) {
+ var finalStl = {};
+
+ if (isArray(styles)) {
+ each(styles, function (stl) {
+ return extend(finalStl, stl);
+ });
+ } else {
+ extend(finalStl, styles);
}
- var data = seriesModel.getData();
- var stylePath = seriesModel.visualStyleAccessPath || 'itemStyle'; // Set in itemStyle
+ var styleName = this._generateStyleName();
- 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);
+ this.richTextStyles[styleName] = finalStl;
+ return "{" + styleName + "|" + text + "}";
+ };
- if (rawItem && rawItem[stylePath]) {
- sharedModel.option = rawItem[stylePath];
- var style = getStyle(sharedModel);
- var existsStyle = data.ensureUniqueItemVisual(idx, 'style');
- extend(existsStyle, style);
+ return TooltipMarkupStyleCreator;
+ }();
- if (sharedModel.option.decal) {
- data.setItemVisual(idx, 'decal', sharedModel.option.decal);
- sharedModel.option.decal.dirty = true;
- }
+ function defaultSeriesFormatTooltip(opt) {
+ var series = opt.series;
+ var dataIndex = opt.dataIndex;
+ var multipleSeries = opt.multipleSeries;
+ var data = series.getData();
+ var tooltipDims = data.mapDimensionsAll('defaultedTooltip');
+ var tooltipDimLen = tooltipDims.length;
+ var value = series.getRawValue(dataIndex);
+ var isValueArr = isArray(value);
+ var markerColor = retrieveVisualColorForTooltipMarker(series, dataIndex); // Complicated rule for pretty tooltip.
- if (colorKey in style) {
- data.setItemVisual(idx, 'colorFromPalette', false);
- }
- }
- } : 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 inlineValue;
+ var inlineValueType;
+ var subBlocks;
+ var sortParam;
- 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) {
- return;
- }
+ if (tooltipDimLen > 1 || isValueArr && !tooltipDimLen) {
+ var formatArrResult = formatTooltipArrayValue(value, series, dataIndex, tooltipDims, markerColor);
+ inlineValue = formatArrResult.inlineValues;
+ inlineValueType = formatArrResult.inlineValueTypes;
+ subBlocks = formatArrResult.blocks; // Only support tooltip sort by the first inline value. It's enough in most cases.
- var colorScope = paletteScopeGroupByType.get(seriesModel.type);
+ sortParam = formatArrResult.inlineValues[0];
+ } else if (tooltipDimLen) {
+ var dimInfo = data.getDimensionInfo(tooltipDims[0]);
+ sortParam = inlineValue = retrieveRawValue(data, dataIndex, tooltipDims[0]);
+ inlineValueType = dimInfo.type;
+ } else {
+ sortParam = inlineValue = isValueArr ? value[0] : value;
+ } // Do not show generated series name. It might not be readable.
- if (!colorScope) {
- colorScope = {};
- paletteScopeGroupByType.set(seriesModel.type, colorScope);
- }
- inner$3(seriesModel).scope = colorScope;
- });
- ecModel.eachSeries(function (seriesModel) {
- if (!seriesModel.useColorPaletteOnData || ecModel.isSeriesFiltered(seriesModel)) {
- return;
- }
+ var seriesNameSpecified = isNameSpecified(series);
+ var seriesName = seriesNameSpecified && series.name || '';
+ var itemName = data.getName(dataIndex);
+ 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
+ })].concat(subBlocks || [])
+ });
+ }
- var dataAll = seriesModel.getRawData();
- var idxMap = {};
- var data = seriesModel.getData();
- var colorScope = inner$3(seriesModel).scope;
- var stylePath = seriesModel.visualStyleAccessPath || 'itemStyle';
- var colorKey = getDefaultColorKey(seriesModel, stylePath);
- 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.
+ 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);
+ return isValueMultipleLine = isValueMultipleLine || dimItem && dimItem.tooltip !== false && dimItem.displayName != null;
+ }, false);
+ var inlineValues = [];
+ var inlineValueTypes = [];
+ var blocks = [];
+ tooltipDims.length ? each(tooltipDims, function (dim) {
+ setEachItem(retrieveRawValue(data, dataIndex, dim), dim);
+ }) // By default, all dims is used on tooltip.
+ : each(value, setEachItem);
- dataAll.each(function (rawIdx) {
- var idx = idxMap[rawIdx];
- 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
+ function setEachItem(val, dim) {
+ var dimInfo = data.getDimensionInfo(dim); // If `dimInfo.tooltip` is not set, show tooltip.
- if (fromPalette) {
- var itemStyle = data.ensureUniqueItemVisual(idx, 'style');
- var name_1 = dataAll.getName(rawIdx) || rawIdx + '';
- var dataCount = dataAll.count();
- itemStyle[colorKey] = seriesModel.getColorFromPalette(name_1, colorScope, dataCount);
- }
- });
- });
- }
- };
+ if (!dimInfo || dimInfo.otherDims.tooltip === false) {
+ return;
+ }
- 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}
- */
+ if (isValueMultipleLine) {
+ blocks.push(createTooltipMarkup('nameValue', {
+ markerType: 'subItem',
+ markerColor: colorStr,
+ name: dimInfo.displayName,
+ value: val,
+ valueType: dimInfo.type
+ }));
+ } else {
+ inlineValues.push(val);
+ inlineValueTypes.push(dimInfo.type);
+ }
+ }
- function defaultLoading(api, opts) {
- opts = opts || {};
- defaults(opts, {
- text: 'loading',
- textColor: '#000',
- fontSize: 12,
- fontWeight: 'normal',
- fontStyle: 'normal',
- fontFamily: 'sans-serif',
- maskColor: 'rgba(255, 255, 255, 0.8)',
- showSpinner: true,
- color: '#5470c6',
- spinnerRadius: 10,
- lineWidth: 5,
- zlevel: 0
- });
- var group = new Group();
- var mask = new Rect({
- style: {
- fill: opts.maskColor
- },
- zlevel: opts.zlevel,
- z: 10000
- });
- group.add(mask);
- var textContent = new ZRText({
- style: {
- text: opts.text,
- fill: opts.textColor,
- fontSize: opts.fontSize,
- fontWeight: opts.fontWeight,
- fontStyle: opts.fontStyle,
- fontFamily: opts.fontFamily
- },
- zlevel: opts.zlevel,
- z: 10001
- });
- var labelRect = new Rect({
- style: {
- fill: 'none'
- },
- textContent: textContent,
- textConfig: {
- position: 'right',
- distance: 10
- },
- zlevel: opts.zlevel,
- z: 10001
- });
- group.add(labelRect);
- var arc;
+ return {
+ inlineValues: inlineValues,
+ inlineValueTypes: inlineValueTypes,
+ blocks: blocks
+ };
+ }
- if (opts.showSpinner) {
- arc = new Arc({
- shape: {
- startAngle: -PI$3 / 2,
- endAngle: -PI$3 / 2 + 0.1,
- r: opts.spinnerRadius
- },
- style: {
- stroke: opts.color,
- lineCap: 'round',
- lineWidth: opts.lineWidth
- },
- zlevel: opts.zlevel,
- z: 10001
- });
- arc.animateShape(true).when(1000, {
- endAngle: PI$3 * 3 / 2
- }).start('circularInOut');
- arc.animateShape(true).when(1000, {
- startAngle: PI$3 * 3 / 2
- }).delay(300).start('circularInOut');
- group.add(arc);
- } // Inject resize
+ var inner$1 = makeInner();
+ function getSelectionKey(data, dataIndex) {
+ return data.getName(dataIndex) || data.getId(dataIndex);
+ }
- group.resize = function () {
- var textWidth = textContent.getBoundingRect().width;
- 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 SERIES_UNIVERSAL_TRANSITION_PROP = '__universalTransitionEnabled';
- 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,
- cy: cy
- });
- labelRect.setShape({
- x: cx - r,
- y: cy - r,
- width: r * 2,
- height: r * 2
- });
- mask.setShape({
- x: 0,
- y: 0,
- width: api.getWidth(),
- height: api.getHeight()
- });
- };
+ var SeriesModel =
+ /** @class */
+ function (_super) {
+ __extends(SeriesModel, _super);
- group.resize();
- return group;
- }
+ function SeriesModel() {
+ // [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
+ // ---------------------------------------
- var Scheduler =
- /** @class */
- function () {
- function Scheduler(ecInstance, api, dataProcessorHandlers, visualHandlers) {
- // key: handlerUID
- this._stageTaskMap = createHashMap();
- this.ecInstance = ecInstance;
- 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);
+ _this._selectedDataIndicesMap = {};
+ return _this;
}
- Scheduler.prototype.restoreData = function (ecModel, 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();
+ SeriesModel.prototype.init = function (option, parentModel, ecModel) {
+ this.seriesIndex = this.componentIndex;
+ this.dataTask = createTask({
+ count: dataTaskCount,
+ reset: dataTaskReset
});
- }; // If seriesModel provided, incremental threshold is check by series data.
-
+ this.dataTask.context = {
+ model: this
+ };
+ this.mergeDefaultAndTheme(option, ecModel);
+ var sourceManager = inner$1(this).sourceManager = new SourceManager(this);
+ sourceManager.prepareSource();
+ var data = this.getInitialData(option, ecModel);
+ wrapData(data, this);
+ this.dataTask.context.data = data;
- Scheduler.prototype.getPerformArgs = function (task, isBlock) {
- // For overall task
- if (!task.__pipeline) {
- return;
+ if ("development" !== 'production') {
+ assert(data, 'getInitialData returned invalid data.');
}
- var pipeline = this._pipelineMap.get(task.__pipeline.id);
+ 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/linkSeriesData
+ // 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();
- var pCtx = pipeline.context;
- var incremental = !isBlock && pipeline.progressiveEnabled && (!pCtx || pCtx.progressiveRender) && task.__idxInPipeline > pipeline.blockIndex;
- var step = incremental ? pipeline.step : null;
- var modDataCount = pCtx && pCtx.modDataCount;
- var modBy = modDataCount != null ? Math.ceil(modDataCount / step) : null;
- return {
- step: step,
- modBy: modBy,
- modDataCount: modDataCount
- };
- };
+ autoSeriesName(this);
- Scheduler.prototype.getPipeline = function (pipelineId) {
- return this._pipelineMap.get(pipelineId);
+ this._initSelectedMapFromData(data);
};
/**
- * 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()`.
+ * Util for merge default and theme to option
*/
- Scheduler.prototype.updateStreamModes = function (seriesModel, view) {
- var pipeline = this._pipelineMap.get(seriesModel.uid);
+ SeriesModel.prototype.mergeDefaultAndTheme = function (option, ecModel) {
+ var layoutMode = fetchLayoutMode(this);
+ 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 data = seriesModel.getData();
- 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 themeSubType = this.subType;
- var progressiveRender = pipeline.progressiveEnabled && view.incrementalPrepareRender && dataLen >= pipeline.threshold;
- 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`
+ if (ComponentModel.hasClass(themeSubType)) {
+ themeSubType += 'Series';
+ }
- var modDataCount = seriesModel.get('progressiveChunkMode') === 'mod' ? dataLen : null;
- seriesModel.pipelineContext = pipeline.context = {
- progressiveRender: progressiveRender,
- modDataCount: modDataCount,
- large: large
- };
- };
+ merge(option, ecModel.getTheme().get(this.subType));
+ merge(option, this.getDefaultOption()); // Default label emphasis `show`
- Scheduler.prototype.restorePipelines = function (ecModel) {
- var scheduler = this;
- var pipelineMap = scheduler._pipelineMap = createHashMap();
- ecModel.eachSeries(function (seriesModel) {
- var progressive = seriesModel.getProgressive();
- var pipelineId = seriesModel.uid;
- pipelineMap.set(pipelineId, {
- id: pipelineId,
- head: null,
- tail: null,
- threshold: seriesModel.getProgressiveThreshold(),
- progressiveEnabled: progressive && !(seriesModel.preventIncremental && seriesModel.preventIncremental()),
- blockIndex: -1,
- step: Math.round(progressive || 700),
- count: 0
- });
+ defaultEmphasis(option, 'label', ['show']);
+ this.fillDataTextStyle(option.data);
- scheduler._pipe(seriesModel, seriesModel.dataTask);
- });
+ if (layoutMode) {
+ mergeLayoutParam(option, inputPositionParams, layoutMode);
+ }
};
- Scheduler.prototype.prepareStageTasks = function () {
- var stageTaskMap = this._stageTaskMap;
- var ecModel = this.api.getModel();
- var api = this.api;
- each(this._allHandlers, function (handler) {
- var record = stageTaskMap.get(handler.uid) || stageTaskMap.set(handler.uid, {});
- var errMsg = '';
+ SeriesModel.prototype.mergeOption = function (newSeriesOption, ecModel) {
+ // this.settingTask.dirty();
+ newSeriesOption = merge(this.option, newSeriesOption, true);
+ this.fillDataTextStyle(newSeriesOption.data);
+ var layoutMode = fetchLayoutMode(this);
- if ("development" !== 'production') {
- // Currently do not need to support to sepecify them both.
- errMsg = '"reset" and "overallReset" must not be both specified.';
- }
+ if (layoutMode) {
+ mergeLayoutParam(this.option, newSeriesOption, layoutMode);
+ }
- assert(!(handler.reset && handler.overallReset), errMsg);
- handler.reset && this._createSeriesStageTask(handler, record, ecModel, api);
- handler.overallReset && this._createOverallStageTask(handler, record, ecModel, api);
- }, this);
+ var sourceManager = inner$1(this).sourceManager;
+ sourceManager.dirty();
+ sourceManager.prepareSource();
+ var data = this.getInitialData(newSeriesOption, ecModel);
+ wrapData(data, this);
+ this.dataTask.dirty();
+ this.dataTask.context.data = data;
+ inner$1(this).dataBeforeProcessed = data;
+ autoSeriesName(this);
+
+ this._initSelectedMapFromData(data);
};
- Scheduler.prototype.prepareView = function (view, model, ecModel, api) {
- var renderTask = view.renderTask;
- var context = renderTask.context;
- context.model = model;
- context.ecModel = ecModel;
- context.api = api;
- renderTask.__block = !view.incrementalPrepareRender;
+ SeriesModel.prototype.fillDataTextStyle = function (data) {
+ // Default data label emphasis `show`
+ // FIXME Tree structure data ?
+ // FIXME Performance ?
+ if (data && !isTypedArray(data)) {
+ var props = ['show'];
- this._pipe(model, renderTask);
+ for (var i = 0; i < data.length; i++) {
+ if (data[i] && data[i].label) {
+ defaultEmphasis(data[i], 'label', props);
+ }
+ }
+ }
};
+ /**
+ * Init a data structure from data related option in series
+ * Must be overriden.
+ */
- 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
- });
- };
- Scheduler.prototype.performVisualTasks = function (ecModel, payload, opt) {
- this._performStageTasks(this._visualHandlers, ecModel, payload, opt);
+ SeriesModel.prototype.getInitialData = function (option, ecModel) {
+ return;
};
+ /**
+ * Append data to list
+ */
- Scheduler.prototype._performStageTasks = function (stageHandlers, ecModel, payload, opt) {
- opt = opt || {};
- var unfinished = false;
- var scheduler = this;
- each(stageHandlers, function (stageHandler, idx) {
- if (opt.visualType && opt.visualType !== stageHandler.visualType) {
- return;
- }
- var stageHandlerRecord = scheduler._stageTaskMap.get(stageHandler.uid);
+ 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.
+ */
- var seriesTaskMap = stageHandlerRecord.seriesTaskMap;
- var overallTask = stageHandlerRecord.overallTask;
- if (overallTask) {
- var overallNeedDirty_1;
- var agentStubMap = overallTask.agentStubMap;
- agentStubMap.each(function (stub) {
- if (needSetDirty(opt, stub)) {
- stub.dirty();
- overallNeedDirty_1 = true;
- }
- });
- overallNeedDirty_1 && overallTask.dirty();
- scheduler.updatePayload(overallTask, payload);
- 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.
+ SeriesModel.prototype.getData = function (dataType) {
+ var task = getCurrentTask(this);
- agentStubMap.each(function (stub) {
- stub.perform(performArgs_1);
- });
+ if (task) {
+ 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;
+ }
+ };
- if (overallTask.perform(performArgs_1)) {
- unfinished = true;
- }
- } else if (seriesTaskMap) {
- seriesTaskMap.each(function (task, pipelineId) {
- if (needSetDirty(opt, task)) {
- task.dirty();
- }
+ SeriesModel.prototype.getAllData = function () {
+ var mainData = this.getData();
+ return mainData && mainData.getLinkedDataAll ? mainData.getLinkedDataAll() : [{
+ data: mainData
+ }];
+ };
- 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.
+ SeriesModel.prototype.setData = function (data) {
+ var task = getCurrentTask(this);
- performArgs.skip = !stageHandler.performRawSeries && ecModel.isSeriesFiltered(task.context.model);
- scheduler.updatePayload(task, payload);
+ if (task) {
+ 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());
+ // }
- if (task.perform(performArgs)) {
- unfinished = true;
- }
- });
+ 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;
}
- });
+ }
- function needSetDirty(opt, task) {
- return opt.setDirty && (!opt.dirtyMap || opt.dirtyMap.get(task.__pipeline.id));
+ inner$1(this).data = data;
+ };
+
+ SeriesModel.prototype.getEncode = function () {
+ var encode = this.get('encode', true);
+
+ if (encode) {
+ return createHashMap(encode);
}
+ };
- this.unfinished = unfinished || this.unfinished;
+ SeriesModel.prototype.getSourceManager = function () {
+ return inner$1(this).sourceManager;
};
- 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;
+ SeriesModel.prototype.getSource = function () {
+ return this.getSourceManager().getSource();
};
+ /**
+ * Get data before processed
+ */
- Scheduler.prototype.plan = function () {
- // Travel pipelines, check block.
- this._pipelineMap.each(function (pipeline) {
- var task = pipeline.tail;
- do {
- if (task.__block) {
- pipeline.blockIndex = task.__idxInPipeline;
- break;
- }
+ SeriesModel.prototype.getRawData = function () {
+ return inner$1(this).dataBeforeProcessed;
+ };
- task = task.getUpstream();
- } while (task);
- });
+ SeriesModel.prototype.getColorBy = function () {
+ var colorBy = this.get('colorBy');
+ return colorBy || 'series';
};
- Scheduler.prototype.updatePayload = function (task, payload) {
- payload !== 'remain' && (task.context.payload = payload);
+ SeriesModel.prototype.isColorBySeries = function () {
+ return this.getColorBy() === 'series';
};
+ /**
+ * Get base axis if has coordinate system and has axis.
+ * By default use coordSys.getBaseAxis();
+ * Can be overrided for some chart.
+ * @return {type} description
+ */
- Scheduler.prototype._createSeriesStageTask = function (stageHandler, stageHandlerRecord, ecModel, api) {
- var scheduler = this;
- 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; // 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.
+ SeriesModel.prototype.getBaseAxis = function () {
+ var coordSys = this.coordinateSystem; // @ts-ignore
- if (stageHandler.createOnAllSeries) {
- ecModel.eachRawSeries(create);
- } else if (seriesType) {
- ecModel.eachRawSeriesByType(seriesType, create);
- } else if (getTargetSeries) {
- getTargetSeries(ecModel, api).each(create);
- }
+ 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
+ */
- function create(seriesModel) {
- 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,
- count: seriesTaskCount
- }));
- task.context = {
- model: seriesModel,
- ecModel: ecModel,
- api: api,
- // PENDING: `useClearVisual` not used?
- useClearVisual: stageHandler.isVisual && !stageHandler.isLayout,
- plan: stageHandler.plan,
- reset: stageHandler.reset,
- scheduler: scheduler
- };
+ SeriesModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) {
+ return defaultSeriesFormatTooltip({
+ series: this,
+ dataIndex: dataIndex,
+ multipleSeries: multipleSeries
+ });
+ };
- scheduler._pipe(seriesModel, task);
+ SeriesModel.prototype.isAnimationEnabled = function () {
+ if (env.node) {
+ return false;
}
+
+ var animationEnabled = this.getShallow('animation');
+
+ if (animationEnabled) {
+ if (this.getData().count() > this.getShallow('animationThreshold')) {
+ animationEnabled = false;
+ }
+ }
+
+ return !!animationEnabled;
};
- Scheduler.prototype._createOverallStageTask = function (stageHandler, stageHandlerRecord, ecModel, api) {
- var scheduler = this;
- var overallTask = stageHandlerRecord.overallTask = stageHandlerRecord.overallTask // For overall task, the function only be called on reset stage.
- || createTask({
- reset: overallTaskReset
- });
- overallTask.context = {
- ecModel: ecModel,
- api: api,
- overallReset: stageHandler.overallReset,
- scheduler: scheduler
- };
- var oldAgentStubMap = overallTask.agentStubMap; // The count of stages are totally about only several dozen, so
- // do not need to reuse the map.
+ SeriesModel.prototype.restoreData = function () {
+ this.dataTask.dirty();
+ };
- var newAgentStubMap = overallTask.agentStubMap = createHashMap();
- var seriesType = stageHandler.seriesType;
- var getTargetSeries = stageHandler.getTargetSeries;
- var overallProgress = true;
- 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.
+ SeriesModel.prototype.getColorFromPalette = function (name, scope, requestColorNum) {
+ var ecModel = this.ecModel; // PENDING
- var errMsg = '';
+ var color = PaletteMixin.prototype.getColorFromPalette.call(this, name, scope, requestColorNum);
- if ("development" !== 'production') {
- errMsg = '"createOnAllSeries" do not supported for "overallReset", ' + 'becuase it will block all streams.';
+ if (!color) {
+ color = ecModel.getColorFromPalette(name, scope, requestColorNum);
}
- assert(!stageHandler.createOnAllSeries, errMsg);
+ return color;
+ };
+ /**
+ * Use `data.mapDimensionsAll(coordDim)` instead.
+ * @deprecated
+ */
- if (seriesType) {
- ecModel.eachRawSeriesByType(seriesType, createStub);
- } else if (getTargetSeries) {
- getTargetSeries(ecModel, api).each(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) || ( // 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 // FIXME:TS never used, so comment it
- // modifyOutputEnd: modifyOutputEnd
+ SeriesModel.prototype.coordDimToDataDim = function (coordDim) {
+ return this.getRawData().mapDimensionsAll(coordDim);
+ };
+ /**
+ * Get progressive rendering count each step
+ */
- };
- stub.agent = overallTask;
- stub.__block = overallProgress;
- scheduler._pipe(seriesModel, stub);
+ 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);
+ };
+
+ SeriesModel.prototype.unselect = function (innerDataIndices, dataType) {
+ var selectedMap = this.option.selectedMap;
+
+ if (!selectedMap) {
+ return;
}
- if (shouldOverallTaskDirty) {
- overallTask.dirty();
+ var data = this.getData(dataType);
+
+ for (var i = 0; i < innerDataIndices.length; i++) {
+ var dataIndex = innerDataIndices[i];
+ var nameOrId = getSelectionKey(data, dataIndex);
+ selectedMap[nameOrId] = false;
+ this._selectedDataIndicesMap[nameOrId] = -1;
}
};
- Scheduler.prototype._pipe = function (seriesModel, task) {
- var pipelineId = seriesModel.uid;
+ SeriesModel.prototype.toggleSelect = function (innerDataIndices, dataType) {
+ var tmpArr = [];
- var pipeline = this._pipelineMap.get(pipelineId);
+ for (var i = 0; i < innerDataIndices.length; i++) {
+ tmpArr[0] = innerDataIndices[i];
+ this.isSelected(innerDataIndices[i], dataType) ? this.unselect(tmpArr, dataType) : this.select(tmpArr, dataType);
+ }
+ };
- !pipeline.head && (pipeline.head = task);
- pipeline.tail && pipeline.tail.pipe(task);
- pipeline.tail = task;
- task.__idxInPipeline = pipeline.count++;
- task.__pipeline = pipeline;
+ SeriesModel.prototype.getSelectedDataIndices = function () {
+ var selectedDataIndicesMap = this._selectedDataIndicesMap;
+ var nameOrIds = keys(selectedDataIndicesMap);
+ var dataIndices = [];
+
+ for (var i = 0; i < nameOrIds.length; i++) {
+ var dataIndex = selectedDataIndicesMap[nameOrIds[i]];
+
+ if (dataIndex >= 0) {
+ dataIndices.push(dataIndex);
+ }
+ }
+
+ return dataIndices;
};
- Scheduler.wrapStageHandler = function (stageHandler, visualType) {
- if (isFunction(stageHandler)) {
- stageHandler = {
- overallReset: stageHandler,
- seriesType: detectSeriseType(stageHandler)
- };
+ SeriesModel.prototype.isSelected = function (dataIndex, dataType) {
+ var selectedMap = this.option.selectedMap;
+
+ if (!selectedMap) {
+ return false;
}
- stageHandler.uid = getUID('stageHandler');
- visualType && (stageHandler.visualType = visualType);
- return stageHandler;
+ var data = this.getData(dataType);
+ var nameOrId = getSelectionKey(data, dataIndex);
+ return selectedMap[nameOrId] || false;
};
- return Scheduler;
- }();
- function overallTaskReset(context) {
- context.overallReset(context.ecModel, context.api, context.payload);
- }
+ SeriesModel.prototype.isUniversalTransitionEnabled = function () {
+ if (this[SERIES_UNIVERSAL_TRANSITION_PROP]) {
+ return true;
+ }
- function stubReset(context) {
- return context.overallProgress && stubProgress;
- }
+ var universalTransitionOpt = this.option.universalTransition; // Quick reject
- function stubProgress() {
- this.agent.dirty();
- this.getDownstream().dirty();
- }
+ if (!universalTransitionOpt) {
+ return false;
+ }
- function stubOnDirty() {
- this.agent && this.agent.dirty();
- }
+ if (universalTransitionOpt === true) {
+ return true;
+ } // Can be simply 'universalTransition: true'
- function seriesTaskPlan(context) {
- return context.plan ? context.plan(context.model, context.ecModel, context.api, context.payload) : null;
- }
- function seriesTaskReset(context) {
- if (context.useClearVisual) {
- context.data.clearAllVisual();
- }
+ return universalTransitionOpt && universalTransitionOpt.enabled;
+ };
- var resetDefines = context.resetDefines = normalizeToArray(context.reset(context.model, context.ecModel, context.api, context.payload));
- return resetDefines.length > 1 ? map(resetDefines, function (v, idx) {
- return makeSeriesTaskProgress(idx);
- }) : singleSeriesTaskProgress;
- }
+ SeriesModel.prototype._innerSelect = function (data, innerDataIndices) {
+ var _a, _b;
- var singleSeriesTaskProgress = makeSeriesTaskProgress(0);
+ var selectedMode = this.option.selectedMode;
+ var len = innerDataIndices.length;
- function makeSeriesTaskProgress(resetDefineIdx) {
- return function (params, context) {
- var data = context.data;
- var resetDefine = context.resetDefines[resetDefineIdx];
+ if (!selectedMode || !len) {
+ return;
+ }
- if (resetDefine && resetDefine.dataEach) {
- for (var i = params.start; i < params.end; i++) {
- resetDefine.dataEach(data, i);
+ if (selectedMode === 'multiple') {
+ var selectedMap = this.option.selectedMap || (this.option.selectedMap = {});
+
+ for (var i = 0; i < len; 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);
}
- } else if (resetDefine && resetDefine.progress) {
- resetDefine.progress(params, data);
+ } else if (selectedMode === 'single' || selectedMode === true) {
+ var lastDataIndex = innerDataIndices[len - 1];
+ var nameOrId = getSelectionKey(data, lastDataIndex);
+ this.option.selectedMap = (_a = {}, _a[nameOrId] = true, _a);
+ this._selectedDataIndicesMap = (_b = {}, _b[nameOrId] = data.getRawIndex(lastDataIndex), _b);
}
};
- }
- 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.
- */
+ 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;
+ }
+ var dataIndices = [];
- function detectSeriseType(legacyFunc) {
- seriesType = null;
+ if (data.hasItemOption) {
+ data.each(function (idx) {
+ var rawItem = data.getRawDataItem(idx);
- try {
- // Assume there is no async when calling `eachSeriesByType`.
- legacyFunc(ecModelMock, apiMock);
- } catch (e) {}
+ if (rawItem && rawItem.selected) {
+ dataIndices.push(idx);
+ }
+ });
+ }
- return seriesType;
- }
+ if (dataIndices.length > 0) {
+ this._innerSelect(data, dataIndices);
+ }
+ }; // /**
+ // * @see {module:echarts/stream/Scheduler}
+ // */
+ // abstract pipeTask: null
- var ecModelMock = {};
- var apiMock = {};
- var seriesType;
- mockMethods(ecModelMock, GlobalModel);
- mockMethods(apiMock, ExtensionAPI);
- ecModelMock.eachSeriesByType = ecModelMock.eachRawSeriesByType = function (type) {
- seriesType = type;
- };
+ SeriesModel.registerClass = function (clz) {
+ return ComponentModel.registerClass(clz);
+ };
- ecModelMock.eachComponent = function (cond) {
- if (cond.mainType === 'series' && cond.subType) {
- seriesType = cond.subType;
- }
- };
+ SeriesModel.protoInitialize = function () {
+ var proto = SeriesModel.prototype;
+ proto.type = 'series.__base__';
+ proto.seriesIndex = 0;
+ proto.ignoreStyleOnData = false;
+ proto.hasSymbolVisual = false;
+ proto.defaultSymbol = 'circle'; // Make sure the values can be accessed!
- function mockMethods(target, Clz) {
- /* eslint-disable */
- for (var name_1 in Clz.prototype) {
- // Do not use hasOwnProperty
- target[name_1] = noop;
+ proto.visualStyleAccessPath = 'itemStyle';
+ proto.visualDrawType = 'fill';
+ }();
+
+ return SeriesModel;
+ }(ComponentModel);
+
+ 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)) {
+ seriesModel.name = getSeriesAutoName(seriesModel) || name;
}
- /* eslint-enable */
+ }
+ function getSeriesAutoName(seriesModel) {
+ var data = seriesModel.getRawData();
+ var dataDims = data.mapDimensionsAll('seriesName');
+ var nameArr = [];
+ each(dataDims, function (dataDim) {
+ var dimInfo = data.getDimensionInfo(dataDim);
+ dimInfo.displayName && nameArr.push(dimInfo.displayName);
+ });
+ return nameArr.join(' ');
}
- /*
- * 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 dataTaskCount(context) {
+ return context.model.getRawData().count();
+ }
+ function dataTaskReset(context) {
+ var seriesModel = context.model;
+ seriesModel.setData(seriesModel.getRawData().cloneShallow());
+ return dataTaskProgress;
+ }
- /**
- * AUTO-GENERATED FILE. DO NOT MODIFY.
- */
+ 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
- /*
- * 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,
- colorLayer: [['#37A2DA', '#ffd85c', '#fd7b5f'], ['#37A2DA', '#67E0E3', '#FFDB5C', '#ff9f7f', '#E062AE', '#9d96f5'], ['#37A2DA', '#32C5E9', '#9FE6B8', '#FFDB5C', '#ff9f7f', '#fb7293', '#e7bcf3', '#8378EA', '#96BFFF'], colorAll]
- };
- /*
- * 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 wrapData(data, seriesModel) {
+ each(concatArray(data.CHANGABLE_METHODS, data.DOWNSAMPLE_METHODS), function (methodName) {
+ data.wrapMethod(methodName, curry(onDataChange, seriesModel));
+ });
+ }
+ function onDataChange(seriesModel, newList) {
+ var task = getCurrentTask(seriesModel);
- /**
- * AUTO-GENERATED FILE. DO NOT MODIFY.
- */
+ if (task) {
+ // Consider case: filter, selectRange
+ task.setOutputEnd((newList || this).count());
+ }
- /*
- * 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';
+ return newList;
+ }
- var axisCommon = function () {
- return {
- axisLine: {
- lineStyle: {
- color: contrastColor
- }
- },
- splitLine: {
- lineStyle: {
- color: '#484753'
- }
- },
- splitArea: {
- areaStyle: {
- color: ['rgba(255,255,255,0.02)', 'rgba(255,255,255,0.05)']
- }
- },
- minorSplitLine: {
- lineStyle: {
- color: '#20203B'
- }
- }
- };
- };
+ function getCurrentTask(seriesModel) {
+ var scheduler = (seriesModel.ecModel || {}).scheduler;
+ var pipeline = scheduler && scheduler.getPipeline(seriesModel.uid);
- var colorPalette = ['#4992ff', '#7cffb2', '#fddd60', '#ff6e76', '#58d9f9', '#05c091', '#ff8a45', '#8d48e3', '#dd79ff'];
- var theme = {
- darkMode: true,
- color: colorPalette,
- backgroundColor: backgroundColor,
- axisPointer: {
- lineStyle: {
- color: '#817f91'
- },
- crossStyle: {
- color: '#817f91'
- },
- label: {
- // TODO Contrast of label backgorundColor
- color: '#fff'
- }
- },
- legend: {
- textStyle: {
- color: contrastColor
- }
- },
- textStyle: {
- color: contrastColor
- },
- title: {
- textStyle: {
- color: '#EEF1FA'
- },
- subtextStyle: {
- color: '#B9B8CE'
- }
- },
- toolbox: {
- iconStyle: {
- borderColor: contrastColor
- }
- },
- dataZoom: {
- borderColor: '#71708A',
- textStyle: {
- color: contrastColor
- },
- brushStyle: {
- color: 'rgba(135,163,206,0.3)'
- },
- handleStyle: {
- color: '#353450',
- borderColor: '#C5CBE3'
- },
- moveHandleStyle: {
- color: '#B0B6C3',
- opacity: 0.3
- },
- fillerColor: 'rgba(135,163,206,0.2)',
- emphasis: {
- handleStyle: {
- borderColor: '#91B7F2',
- color: '#4D587D'
- },
- moveHandleStyle: {
- color: '#636D9A',
- opacity: 0.7
- }
- },
- dataBackground: {
- lineStyle: {
- color: '#71708A',
- width: 1
- },
- areaStyle: {
- color: '#71708A'
- }
- },
- selectedDataBackground: {
- lineStyle: {
- color: '#87A3CE'
- },
- areaStyle: {
- color: '#87A3CE'
- }
- }
- },
- visualMap: {
- textStyle: {
- color: contrastColor
- }
- },
- timeline: {
- lineStyle: {
- color: contrastColor
- },
- label: {
- color: contrastColor
- },
- controlStyle: {
- color: contrastColor,
- borderColor: contrastColor
- }
- },
- calendar: {
- itemStyle: {
- color: backgroundColor
- },
- dayLabel: {
- color: contrastColor
- },
- monthLabel: {
- color: contrastColor
- },
- yearLabel: {
- color: contrastColor
- }
- },
- timeAxis: axisCommon(),
- logAxis: axisCommon(),
- valueAxis: axisCommon(),
- categoryAxis: axisCommon(),
- line: {
- symbol: 'circle'
- },
- graph: {
- color: colorPalette
- },
- gauge: {
- title: {
- color: contrastColor
- },
- axisLine: {
- lineStyle: {
- color: [[1, 'rgba(207,212,219,0.2)']]
+ if (pipeline) {
+ // When pipline finished, the currrentTask keep the last
+ // task (renderTask).
+ var task = pipeline.currentTask;
+
+ if (task) {
+ var agentStubMap = task.agentStubMap;
+
+ if (agentStubMap) {
+ task = agentStubMap.get(seriesModel.uid);
}
- },
- axisLabel: {
- color: contrastColor
- },
- detail: {
- color: '#EEF1FA'
}
- },
- candlestick: {
- itemStyle: {
- color: '#f64e56',
- color0: '#54ea92',
- borderColor: '#f64e56',
- borderColor0: '#54ea92' // borderColor: '#ca2824',
- // borderColor0: '#09a443'
- }
+ return task;
}
- };
- theme.categoryAxis.splitLine.show = false;
-
- /**
- * 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 =
+ var ComponentView =
/** @class */
function () {
- function ECEventProcessor() {}
+ function ComponentView() {
+ this.group = new Group();
+ this.uid = getUID('viewComponent');
+ }
- ECEventProcessor.prototype.normalizeQuery = function (query) {
- var cptQuery = {};
- var dataQuery = {};
- var otherQuery = {}; // `query` is `mainType` or `mainType.subType` of component.
+ ComponentView.prototype.init = function (ecModel, api) {};
- if (isString(query)) {
- var condCptType = parseClassType(query); // `.main` and `.sub` may be ''.
+ ComponentView.prototype.render = function (model, ecModel, api, payload) {};
- cptQuery.mainType = condCptType.main || null;
- cptQuery.subType = condCptType.sub || null;
- } // `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;
+ ComponentView.prototype.dispose = function (ecModel, api) {};
- for (var i = 0; i < suffixes_1.length; i++) {
- var propSuffix = suffixes_1[i];
- var suffixPos = key.lastIndexOf(propSuffix);
+ ComponentView.prototype.updateView = function (model, ecModel, api, payload) {// Do nothing;
+ };
- if (suffixPos > 0 && suffixPos === key.length - propSuffix.length) {
- var mainType = key.slice(0, suffixPos); // Consider `dataIndex`.
+ ComponentView.prototype.updateLayout = function (model, ecModel, api, payload) {// Do nothing;
+ };
- if (mainType !== 'data') {
- cptQuery.mainType = mainType;
- cptQuery[propSuffix.toLowerCase()] = val;
- reserved = true;
- }
- }
- }
+ ComponentView.prototype.updateVisual = function (model, ecModel, api, payload) {// Do nothing;
+ };
+ /**
+ * Hook for blur target series.
+ * Can be used in marker for blur the markers
+ */
- if (dataKeys_1.hasOwnProperty(key)) {
- dataQuery[key] = val;
- reserved = true;
- }
- if (!reserved) {
- otherQuery[key] = val;
- }
- });
- }
+ ComponentView.prototype.blurSeries = function (seriesModels, ecModel) {// Do nothing;
+ };
- return {
- cptQuery: cptQuery,
- dataQuery: dataQuery,
- otherQuery: otherQuery
- };
+ 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; // 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';
};
+ }
- ECEventProcessor.prototype.filter = function (eventType, query) {
- // They should be assigned before each trigger call.
- var eventInfo = this.eventInfo;
+ var inner$2 = makeInner();
+ var renderPlanner = createRenderPlanner();
- if (!eventInfo) {
- return true;
- }
+ var ChartView =
+ /** @class */
+ function () {
+ function ChartView() {
+ this.group = new Group();
+ this.uid = getUID('viewChart');
+ this.renderTask = createTask({
+ plan: renderTaskPlan,
+ reset: renderTaskReset
+ });
+ this.renderTask.context = {
+ view: this
+ };
+ }
- var targetEl = eventInfo.targetEl;
- var packedEvent = eventInfo.packedEvent;
- var model = eventInfo.model;
- var view = eventInfo.view; // For event like 'globalout'.
+ ChartView.prototype.init = function (ecModel, api) {};
- if (!model || !view) {
- return true;
- }
+ ChartView.prototype.render = function (seriesModel, ecModel, api, payload) {};
+ /**
+ * Highlight series or specified data item.
+ */
- var cptQuery = query.cptQuery;
- var dataQuery = query.dataQuery;
- return check(cptQuery, model, 'mainType') && check(cptQuery, model, 'subType') && check(cptQuery, model, 'index', 'componentIndex') && check(cptQuery, model, 'name') && check(cptQuery, model, 'id') && check(dataQuery, packedEvent, 'name') && check(dataQuery, packedEvent, 'dataIndex') && check(dataQuery, packedEvent, 'dataType') && (!view.filterForExposedEvent || view.filterForExposedEvent(eventType, query.otherQuery, targetEl, packedEvent));
- function check(query, host, prop, propOnHost) {
- return query[prop] == null || host[propOnHost || prop] === query[prop];
- }
+ ChartView.prototype.highlight = function (seriesModel, ecModel, api, payload) {
+ toggleHighlight(seriesModel.getData(), payload, 'emphasis');
};
+ /**
+ * Downplay series or specified data item.
+ */
- ECEventProcessor.prototype.afterTrigger = function () {
- // Make sure the eventInfo wont be used in next trigger.
- this.eventInfo = null;
+
+ 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) {};
- return ECEventProcessor;
- }();
+ ChartView.prototype.updateView = function (seriesModel, ecModel, api, payload) {
+ this.render(seriesModel, ecModel, api, payload);
+ }; // FIXME never used?
- var seriesSymbolTask = {
- createOnAllSeries: true,
- // For legend.
- performRawSeries: true,
- reset: function (seriesModel, ecModel) {
- var data = seriesModel.getData();
- if (seriesModel.legendIcon) {
- data.setVisual('legendIcon', seriesModel.legendIcon);
- }
+ ChartView.prototype.updateLayout = function (seriesModel, ecModel, api, payload) {
+ this.render(seriesModel, ecModel, api, payload);
+ }; // FIXME never used?
- if (!seriesModel.hasSymbolVisual) {
- return;
- }
- var symbolType = seriesModel.get('symbol');
- var symbolSize = seriesModel.get('symbolSize');
- var keepAspect = seriesModel.get('symbolKeepAspect');
- var symbolRotate = seriesModel.get('symbolRotate');
- var symbolOffset = seriesModel.get('symbolOffset');
- var hasSymbolTypeCallback = isFunction(symbolType);
- var hasSymbolSizeCallback = isFunction(symbolSize);
- var hasSymbolRotateCallback = isFunction(symbolRotate);
- var hasSymbolOffsetCallback = isFunction(symbolOffset);
- var hasCallback = hasSymbolTypeCallback || hasSymbolSizeCallback || hasSymbolRotateCallback || hasSymbolOffsetCallback;
- var seriesSymbol = !hasSymbolTypeCallback && symbolType ? symbolType : seriesModel.defaultSymbol;
- var seriesSymbolSize = !hasSymbolSizeCallback ? symbolSize : null;
- var seriesSymbolRotate = !hasSymbolRotateCallback ? symbolRotate : null;
- var seriesSymbolOffset = !hasSymbolOffsetCallback ? symbolOffset : null;
- data.setVisual({
- legendIcon: seriesModel.legendIcon || 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,
- symbolOffset: seriesSymbolOffset
- }); // Only visible series has each data be visual encoded
+ ChartView.prototype.updateVisual = function (seriesModel, ecModel, api, payload) {
+ this.render(seriesModel, ecModel, api, payload);
+ };
- if (ecModel.isSeriesFiltered(seriesModel)) {
- return;
- }
+ ChartView.markUpdateMethod = function (payload, methodName) {
+ inner$2(payload).updateMethod = methodName;
+ };
- function dataEach(data, idx) {
- var rawValue = seriesModel.getRawValue(idx);
- var params = seriesModel.getDataParams(idx);
- hasSymbolTypeCallback && data.setItemVisual(idx, 'symbol', symbolType(rawValue, params));
- hasSymbolSizeCallback && data.setItemVisual(idx, 'symbolSize', symbolSize(rawValue, params));
- hasSymbolRotateCallback && data.setItemVisual(idx, 'symbolRotate', symbolRotate(rawValue, params));
- hasSymbolOffsetCallback && data.setItemVisual(idx, 'symbolOffset', symbolOffset(rawValue, params));
- }
+ ChartView.protoInitialize = function () {
+ var proto = ChartView.prototype;
+ proto.type = 'chart';
+ }();
- return {
- dataEach: hasCallback ? dataEach : null
- };
+ return ChartView;
+ }();
+ /**
+ * Set state of single element
+ */
+
+ function elSetState(el, state, highlightDigit) {
+ if (el) {
+ (state === 'emphasis' ? enterEmphasis : leaveEmphasis)(el, highlightDigit);
}
- };
- 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
+ }
+ function toggleHighlight(data, payload, state) {
+ var dataIndex = queryDataIndex(data, payload);
+ var highlightDigit = payload && payload.highlightKey != null ? getHighlightDigit(payload.highlightKey) : null;
- if (ecModel.isSeriesFiltered(seriesModel)) {
- return;
- }
+ if (dataIndex != null) {
+ each(normalizeToArray(dataIndex), function (dataIdx) {
+ elSetState(data.getItemGraphicEl(dataIdx), state, highlightDigit);
+ });
+ } else {
+ data.eachItemGraphicEl(function (el) {
+ elSetState(el, state, highlightDigit);
+ });
+ }
+ }
- var data = seriesModel.getData();
+ enableClassExtend(ChartView, ['dispose']);
+ enableClassManagement(ChartView);
- function dataEach(data, idx) {
- var itemModel = data.getItemModel(idx);
- var itemSymbolType = itemModel.getShallow('symbol', true);
- var itemSymbolSize = itemModel.getShallow('symbolSize', true);
- var itemSymbolRotate = itemModel.getShallow('symbolRotate', true);
- var itemSymbolOffset = itemModel.getShallow('symbolOffset', true);
- var itemSymbolKeepAspect = itemModel.getShallow('symbolKeepAspect', true); // If has item symbol
+ function renderTaskPlan(context) {
+ return renderPlanner(context.model);
+ }
- if (itemSymbolType != null) {
- data.setItemVisual(idx, 'symbol', itemSymbolType);
- }
+ function renderTaskReset(context) {
+ var seriesModel = context.model;
+ var ecModel = context.ecModel;
+ var api = context.api;
+ var payload = context.payload; // FIXME: remove updateView updateVisual
- if (itemSymbolSize != null) {
- // PENDING Transform symbolSize ?
- data.setItemVisual(idx, 'symbolSize', itemSymbolSize);
- }
+ var progressiveRender = seriesModel.pipelineContext.progressiveRender;
+ var view = context.view;
+ var updateMethod = payload && inner$2(payload).updateMethod;
+ var methodName = progressiveRender ? 'incrementalPrepareRender' : updateMethod && view[updateMethod] ? updateMethod // `appendData` is also supported when data amount
+ // is less than progressive threshold.
+ : 'render';
- if (itemSymbolRotate != null) {
- data.setItemVisual(idx, 'symbolRotate', itemSymbolRotate);
- }
+ if (methodName !== 'render') {
+ view[methodName](seriesModel, ecModel, api, payload);
+ }
- if (itemSymbolOffset != null) {
- data.setItemVisual(idx, 'symbolOffset', itemSymbolOffset);
- }
+ return progressMethodMap[methodName];
+ }
- if (itemSymbolKeepAspect != null) {
- data.setItemVisual(idx, 'symbolKeepAspect', itemSymbolKeepAspect);
- }
+ var progressMethodMap = {
+ incrementalPrepareRender: {
+ progress: function (params, context) {
+ context.view.incrementalRender(params, context.model, context.ecModel, context.api, context.payload);
+ }
+ },
+ 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);
}
-
- return {
- dataEach: data.hasItemOption ? dataEach : null
- };
}
};
@@ -24473,1361 +24300,1540 @@
* specific language governing permissions and limitations
* under the License.
*/
- function getItemVisualFromData(data, dataIndex, key) {
- switch (key) {
- case 'color':
- var style = data.getItemVisual(dataIndex, 'style');
- return style[data.getVisual('drawType')];
-
- case 'opacity':
- return data.getItemVisual(dataIndex, 'style').opacity;
-
- case 'symbol':
- case 'symbolSize':
- case 'liftZ':
- return data.getItemVisual(dataIndex, key);
+ 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.
+ */
- default:
- if ("development" !== 'production') {
- console.warn("Unknown visual type " + key);
- }
+ function throttle(fn, delay, debounce) {
+ var currCall;
+ var lastCall = 0;
+ var lastExec = 0;
+ var timer = null;
+ var diff;
+ var scope;
+ var args;
+ var debounceNextCall;
+ delay = delay || 0;
+ function exec() {
+ lastExec = new Date().getTime();
+ timer = null;
+ fn.apply(scope, args || []);
}
- }
- function getVisualFromData(data, key) {
- switch (key) {
- case 'color':
- var style = data.getVisual('style');
- return style[data.getVisual('drawType')];
- case 'opacity':
- return data.getVisual('style').opacity;
+ var cb = function () {
+ var cbArgs = [];
- case 'symbol':
- case 'symbolSize':
- case 'liftZ':
- return data.getVisual(key);
+ for (var _i = 0; _i < arguments.length; _i++) {
+ cbArgs[_i] = arguments[_i];
+ }
- default:
- if ("development" !== 'production') {
- console.warn("Unknown visual type " + key);
+ currCall = new Date().getTime();
+ scope = this;
+ args = cbArgs;
+ var thisDelay = debounceNextCall || delay;
+ var thisDebounce = debounceNextCall || debounce;
+ debounceNextCall = null;
+ diff = currCall - (thisDebounce ? lastCall : lastExec) - thisDelay;
+ 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);
+ } else {
+ if (diff >= 0) {
+ exec();
+ } else {
+ timer = setTimeout(exec, -diff);
}
+ }
- }
- }
+ lastCall = currCall;
+ };
+ /**
+ * Clear throttle.
+ * @public
+ */
- var PI2$6 = Math.PI * 2;
- var CMD$3 = PathProxy.CMD;
- var DEFAULT_SEARCH_SPACE = ['top', 'right', 'bottom', 'left'];
- function getCandidateAnchor(pos, distance, rect, outPt, outDir) {
- var width = rect.width;
- var height = rect.height;
+ cb.clear = function () {
+ if (timer) {
+ clearTimeout(timer);
+ timer = null;
+ }
+ };
+ /**
+ * Enable debounce once.
+ */
- switch (pos) {
- case 'top':
- outPt.set(rect.x + width / 2, rect.y - distance);
- outDir.set(0, -1);
- break;
- case 'bottom':
- outPt.set(rect.x + width / 2, rect.y + height + distance);
- outDir.set(0, 1);
- break;
+ cb.debounceNextCall = function (debounceDelay) {
+ debounceNextCall = debounceDelay;
+ };
- case 'left':
- outPt.set(rect.x - distance, rect.y + height / 2);
- outDir.set(-1, 0);
- break;
+ 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');
+ * };
+ *
+ */
- case 'right':
- outPt.set(rect.x + width + distance, rect.y + height / 2);
- outDir.set(1, 0);
- break;
+ function createOrUpdate(obj, fnAttr, rate, throttleType) {
+ var fn = obj[fnAttr];
+
+ if (!fn) {
+ return;
}
- }
- function projectPointToArc(cx, cy, r, startAngle, endAngle, anticlockwise, x, y, out) {
- x -= cx;
- y -= cy;
- var d = Math.sqrt(x * x + y * y);
- x /= d;
- y /= d; // Intersect point.
+ var originFn = fn[ORIGIN_METHOD] || fn;
+ var lastThrottleType = fn[THROTTLE_TYPE];
+ var lastRate = fn[RATE];
- var ox = x * r + cx;
- var oy = y * r + cy;
+ if (lastRate !== rate || lastThrottleType !== throttleType) {
+ if (rate == null || !throttleType) {
+ return obj[fnAttr] = originFn;
+ }
- if (Math.abs(startAngle - endAngle) % PI2$6 < 1e-4) {
- // Is a circle
- out[0] = ox;
- out[1] = oy;
- return d - r;
+ fn = obj[fnAttr] = throttle(originFn, rate, throttleType === 'debounce');
+ fn[ORIGIN_METHOD] = originFn;
+ fn[THROTTLE_TYPE] = throttleType;
+ fn[RATE] = rate;
}
- if (anticlockwise) {
- var tmp = startAngle;
- startAngle = normalizeRadian(endAngle);
- endAngle = normalizeRadian(tmp);
- } else {
- startAngle = normalizeRadian(startAngle);
- endAngle = normalizeRadian(endAngle);
- }
+ return fn;
+ }
+ /**
+ * Clear throttle. Example see throttle.createOrUpdate.
+ */
+
+ function clear(obj, fnAttr) {
+ var fn = obj[fnAttr];
- if (startAngle > endAngle) {
- endAngle += PI2$6;
+ if (fn && fn[ORIGIN_METHOD]) {
+ obj[fnAttr] = fn[ORIGIN_METHOD];
}
+ }
- var angle = Math.atan2(y, x);
+ var inner$3 = makeInner();
+ var defaultStyleMappers = {
+ itemStyle: makeStyleMapper(ITEM_STYLE_KEY_MAP, true),
+ lineStyle: makeStyleMapper(LINE_STYLE_KEY_MAP, true)
+ };
+ var defaultColorKey = {
+ lineStyle: 'stroke',
+ itemStyle: 'fill'
+ };
- if (angle < 0) {
- angle += PI2$6;
- }
+ function getStyleMapper(seriesModel, stylePath) {
+ var styleMapper = seriesModel.visualStyleMapper || defaultStyleMappers[stylePath];
- 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;
+ if (!styleMapper) {
+ console.warn("Unkown style type '" + stylePath + "'.");
+ return defaultStyleMappers.itemStyle;
}
- var x1 = r * Math.cos(startAngle) + cx;
- var y1 = r * Math.sin(startAngle) + cy;
- var x2 = r * Math.cos(endAngle) + cx;
- var y2 = r * Math.sin(endAngle) + cy;
- var d1 = (x1 - x) * (x1 - x) + (y1 - y) * (y1 - y);
- var d2 = (x2 - x) * (x2 - x) + (y2 - y) * (y2 - y);
+ return styleMapper;
+ }
- if (d1 < d2) {
- out[0] = x1;
- out[1] = y1;
- return Math.sqrt(d1);
- } else {
- out[0] = x2;
- out[1] = y2;
- return Math.sqrt(d2);
+ function getDefaultColorKey(seriesModel, stylePath) {
+ // return defaultColorKey[stylePath] ||
+ var colorKey = seriesModel.visualDrawType || defaultColorKey[stylePath];
+
+ if (!colorKey) {
+ console.warn("Unkown style type '" + stylePath + "'.");
+ return 'fill';
}
+
+ return colorKey;
}
- function projectPointToLine(x1, y1, x2, y2, x, y, out, limitToEnds) {
- var dx = x - x1;
- var dy = y - y1;
- var dx1 = x2 - x1;
- var dy1 = y2 - y1;
- var lineLen = Math.sqrt(dx1 * dx1 + dy1 * dy1);
- dx1 /= lineLen;
- dy1 /= lineLen; // dot product
+ var seriesStyleTask = {
+ createOnAllSeries: true,
+ performRawSeries: true,
+ reset: function (seriesModel, ecModel) {
+ var data = seriesModel.getData();
+ var stylePath = seriesModel.visualStyleAccessPath || 'itemStyle'; // Set in itemStyle
- var projectedLen = dx * dx1 + dy * dy1;
- var t = projectedLen / lineLen;
+ var styleModel = seriesModel.getModel(stylePath);
+ var getStyle = getStyleMapper(seriesModel, stylePath);
+ var globalStyle = getStyle(styleModel);
+ var decalOption = styleModel.getShallow('decal');
- if (limitToEnds) {
- t = Math.min(Math.max(t, 0), 1);
- }
+ if (decalOption) {
+ data.setVisual('decal', decalOption);
+ decalOption.dirty = true;
+ } // TODO
- t *= lineLen;
- var ox = out[0] = x1 + t * dx1;
- var oy = out[1] = y1 + t * dy1;
- return Math.sqrt((ox - x) * (ox - x) + (oy - y) * (oy - y));
- }
- function projectPointToRect(x1, y1, width, height, x, y, out) {
- if (width < 0) {
- x1 = x1 + width;
- width = -width;
- }
+ var colorKey = getDefaultColorKey(seriesModel, stylePath);
+ var color = globalStyle[colorKey]; // TODO style callback
+
+ var colorCallback = isFunction(color) ? color : null;
+ var hasAutoColor = globalStyle.fill === 'auto' || globalStyle.stroke === 'auto'; // Get from color palette by default.
+
+ if (!globalStyle[colorKey] || colorCallback || hasAutoColor) {
+ // 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.
+ var colorPalette = seriesModel.getColorFromPalette( // TODO series count changed.
+ seriesModel.name, null, ecModel.getSeriesCount());
+
+ if (!globalStyle[colorKey]) {
+ globalStyle[colorKey] = colorPalette;
+ data.setVisual('colorFromPalette', true);
+ }
+
+ globalStyle.fill = globalStyle.fill === 'auto' || typeof globalStyle.fill === 'function' ? colorPalette : globalStyle.fill;
+ globalStyle.stroke = globalStyle.stroke === 'auto' || typeof globalStyle.stroke === 'function' ? colorPalette : globalStyle.stroke;
+ }
+
+ data.setVisual('style', globalStyle);
+ data.setVisual('drawType', colorKey); // Only visible series has each data be visual encoded
- if (height < 0) {
- y1 = y1 + height;
- height = -height;
+ if (!ecModel.isSeriesFiltered(seriesModel) && colorCallback) {
+ data.setVisual('colorFromPalette', false);
+ return {
+ dataEach: function (data, idx) {
+ var dataParams = seriesModel.getDataParams(idx);
+ var itemStyle = extend({}, globalStyle);
+ itemStyle[colorKey] = colorCallback(dataParams);
+ data.setItemVisual(idx, 'style', itemStyle);
+ }
+ };
+ }
}
+ };
+ var sharedModel = new Model();
+ var dataStyleTask = {
+ createOnAllSeries: true,
+ performRawSeries: true,
+ reset: function (seriesModel, ecModel) {
+ if (seriesModel.ignoreStyleOnData || ecModel.isSeriesFiltered(seriesModel)) {
+ return;
+ }
- var x2 = x1 + width;
- var y2 = y1 + height;
- var ox = out[0] = Math.min(Math.max(x, x1), x2);
- var oy = out[1] = Math.min(Math.max(y, y1), y2);
- return Math.sqrt((ox - x) * (ox - x) + (oy - y) * (oy - y));
- }
+ var data = seriesModel.getData();
+ var stylePath = seriesModel.visualStyleAccessPath || 'itemStyle'; // Set in itemStyle
- var tmpPt = [];
+ 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);
- function nearestPointOnRect(pt, rect, out) {
- var dist = projectPointToRect(rect.x, rect.y, rect.width, rect.height, pt.x, pt.y, tmpPt);
- 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;
- var yi = 0;
- var x0 = 0;
- var y0 = 0;
- var x1;
- var y1;
- var minDist = Infinity;
- var data = path.data;
- var x = pt.x;
- var y = pt.y;
-
- for (var i = 0; i < data.length;) {
- var cmd = data[i++];
-
- if (i === 1) {
- xi = data[i];
- yi = data[i + 1];
- x0 = xi;
- y0 = yi;
- }
-
- var d = minDist;
-
- switch (cmd) {
- case CMD$3.M:
- // moveTo 命令重新创建一个新的 subpath, 并且更新新的起点
- // 在 closePath 的时候使用
- x0 = data[i++];
- y0 = data[i++];
- xi = x0;
- yi = y0;
- break;
+ if (rawItem && rawItem[stylePath]) {
+ sharedModel.option = rawItem[stylePath];
+ var style = getStyle(sharedModel);
+ var existsStyle = data.ensureUniqueItemVisual(idx, 'style');
+ extend(existsStyle, style);
- case CMD$3.L:
- d = projectPointToLine(xi, yi, data[i], data[i + 1], x, y, tmpPt, true);
- xi = data[i++];
- yi = data[i++];
- break;
+ if (sharedModel.option.decal) {
+ data.setItemVisual(idx, 'decal', sharedModel.option.decal);
+ sharedModel.option.decal.dirty = true;
+ }
- case CMD$3.C:
- d = cubicProjectPoint(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], x, y, tmpPt);
- xi = data[i++];
- yi = data[i++];
- break;
+ if (colorKey in style) {
+ data.setItemVisual(idx, 'colorFromPalette', false);
+ }
+ }
+ } : 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.
- case CMD$3.Q:
- d = quadraticProjectPoint(xi, yi, data[i++], data[i++], data[i], data[i + 1], x, y, tmpPt);
- xi = data[i++];
- yi = data[i++];
- break;
+ 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) {
+ var colorBy = seriesModel.getColorBy();
+
+ if (seriesModel.isColorBySeries()) {
+ return;
+ }
- 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++]; // TODO Arc 旋转
+ var key = seriesModel.type + '-' + colorBy;
+ var colorScope = paletteScopeGroupByType.get(key);
+
+ if (!colorScope) {
+ colorScope = {};
+ paletteScopeGroupByType.set(key, colorScope);
+ }
+
+ inner$3(seriesModel).scope = colorScope;
+ });
+ ecModel.eachSeries(function (seriesModel) {
+ if (seriesModel.isColorBySeries() || ecModel.isSeriesFiltered(seriesModel)) {
+ return;
+ }
- i += 1;
- var anticlockwise = !!(1 - data[i++]);
- x1 = Math.cos(theta) * rx + cx;
- y1 = Math.sin(theta) * ry + cy; // 不是直接使用 arc 命令
+ var dataAll = seriesModel.getRawData();
+ var idxMap = {};
+ var data = seriesModel.getData();
+ var colorScope = inner$3(seriesModel).scope;
+ var stylePath = seriesModel.visualStyleAccessPath || 'itemStyle';
+ var colorKey = getDefaultColorKey(seriesModel, stylePath);
+ 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.
- if (i <= 1) {
- // 第一个命令起点还未定义
- x0 = x1;
- y0 = y1;
- } // zr 使用scale来模拟椭圆, 这里也对x做一定的缩放
+ dataAll.each(function (rawIdx) {
+ var idx = idxMap[rawIdx];
+ 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');
+ var name_1 = dataAll.getName(rawIdx) || rawIdx + '';
+ var dataCount = dataAll.count();
+ itemStyle[colorKey] = seriesModel.getColorFromPalette(name_1, colorScope, dataCount);
+ }
+ });
+ });
+ }
+ };
- var _x = (x - cx) * ry / rx + cx;
+ 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}
+ */
- d = projectPointToArc(cx, cy, ry, theta, theta + dTheta, anticlockwise, _x, y, tmpPt);
- xi = Math.cos(theta + dTheta) * rx + cx;
- yi = Math.sin(theta + dTheta) * ry + cy;
- break;
+ function defaultLoading(api, opts) {
+ opts = opts || {};
+ defaults(opts, {
+ text: 'loading',
+ textColor: '#000',
+ fontSize: 12,
+ fontWeight: 'normal',
+ fontStyle: 'normal',
+ fontFamily: 'sans-serif',
+ maskColor: 'rgba(255, 255, 255, 0.8)',
+ showSpinner: true,
+ color: '#5470c6',
+ spinnerRadius: 10,
+ lineWidth: 5,
+ zlevel: 0
+ });
+ var group = new Group();
+ var mask = new Rect({
+ style: {
+ fill: opts.maskColor
+ },
+ zlevel: opts.zlevel,
+ z: 10000
+ });
+ group.add(mask);
+ var textContent = new ZRText({
+ style: {
+ text: opts.text,
+ fill: opts.textColor,
+ fontSize: opts.fontSize,
+ fontWeight: opts.fontWeight,
+ fontStyle: opts.fontStyle,
+ fontFamily: opts.fontFamily
+ },
+ zlevel: opts.zlevel,
+ z: 10001
+ });
+ var labelRect = new Rect({
+ style: {
+ fill: 'none'
+ },
+ textContent: textContent,
+ textConfig: {
+ position: 'right',
+ distance: 10
+ },
+ zlevel: opts.zlevel,
+ z: 10001
+ });
+ group.add(labelRect);
+ var arc;
- case CMD$3.R:
- x0 = xi = data[i++];
- y0 = yi = data[i++];
- var width = data[i++];
- var height = data[i++];
- d = projectPointToRect(x0, y0, width, height, x, y, tmpPt);
- break;
+ if (opts.showSpinner) {
+ arc = new Arc({
+ shape: {
+ startAngle: -PI$3 / 2,
+ endAngle: -PI$3 / 2 + 0.1,
+ r: opts.spinnerRadius
+ },
+ style: {
+ stroke: opts.color,
+ lineCap: 'round',
+ lineWidth: opts.lineWidth
+ },
+ zlevel: opts.zlevel,
+ z: 10001
+ });
+ arc.animateShape(true).when(1000, {
+ endAngle: PI$3 * 3 / 2
+ }).start('circularInOut');
+ arc.animateShape(true).when(1000, {
+ startAngle: PI$3 * 3 / 2
+ }).delay(300).start('circularInOut');
+ group.add(arc);
+ } // Inject resize
- case CMD$3.Z:
- d = projectPointToLine(xi, yi, x0, y0, x, y, tmpPt, true);
- xi = x0;
- yi = y0;
- break;
- }
- if (d < minDist) {
- minDist = d;
- out.set(tmpPt[0], tmpPt[1]);
- }
- }
+ group.resize = function () {
+ var textWidth = textContent.getBoundingRect().width;
+ var r = opts.showSpinner ? opts.spinnerRadius : 0; // cx = (containerWidth - arcDiameter - textDistance - textWidth) / 2
+ // textDistance needs to be calculated when both animation and text exist
- return minDist;
- } // Temporal varible for intermediate usage.
+ 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,
+ cy: cy
+ });
+ labelRect.setShape({
+ x: cx - r,
+ y: cy - r,
+ width: r * 2,
+ height: r * 2
+ });
+ mask.setShape({
+ x: 0,
+ y: 0,
+ width: api.getWidth(),
+ height: api.getHeight()
+ });
+ };
+ group.resize();
+ return group;
+ }
- 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
- */
+ var Scheduler =
+ /** @class */
+ function () {
+ function Scheduler(ecInstance, api, dataProcessorHandlers, visualHandlers) {
+ // key: handlerUID
+ this._stageTaskMap = createHashMap();
+ this.ecInstance = ecInstance;
+ 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.
- function updateLabelLinePoints(target, labelLineModel) {
- if (!target) {
- return;
+ dataProcessorHandlers = this._dataProcessorHandlers = dataProcessorHandlers.slice();
+ visualHandlers = this._visualHandlers = visualHandlers.slice();
+ this._allHandlers = dataProcessorHandlers.concat(visualHandlers);
}
- var labelLine = target.getTextGuideLine();
- var label = target.getTextContent(); // Needs to create text guide in each charts.
+ Scheduler.prototype.restoreData = function (ecModel, 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`).
- if (!(label && labelLine)) {
- return;
- }
+ this._stageTaskMap.each(function (taskRecord) {
+ var overallTask = taskRecord.overallTask;
+ overallTask && overallTask.dirty();
+ });
+ }; // If seriesModel provided, incremental threshold is check by series data.
- var labelGuideConfig = target.textGuideLineConfig || {};
- var points = [[0, 0], [0, 0], [0, 0]];
- var searchSpace = labelGuideConfig.candidates || DEFAULT_SEARCH_SPACE;
- var labelRect = label.getBoundingRect().clone();
- labelRect.applyTransform(label.getComputedTransform());
- var minDist = Infinity;
- var anchorPoint = labelGuideConfig.anchor;
- var targetTransform = target.getComputedTransform();
- var targetInversedTransform = targetTransform && invert([], targetTransform);
- var len = labelLineModel.get('length2') || 0;
- if (anchorPoint) {
- pt2.copy(anchorPoint);
- }
+ Scheduler.prototype.getPerformArgs = function (task, isBlock) {
+ // For overall task
+ if (!task.__pipeline) {
+ return;
+ }
- for (var i = 0; i < searchSpace.length; i++) {
- var candidate = searchSpace[i];
- getCandidateAnchor(candidate, 0, labelRect, pt0, dir);
- Point.scaleAndAdd(pt1, pt0, dir, len); // Transform to target coord space.
+ var pipeline = this._pipelineMap.get(task.__pipeline.id);
- pt1.transform(targetInversedTransform); // Note: getBoundingRect will ensure the `path` being created.
+ var pCtx = pipeline.context;
+ var incremental = !isBlock && pipeline.progressiveEnabled && (!pCtx || pCtx.progressiveRender) && task.__idxInPipeline > pipeline.blockIndex;
+ var step = incremental ? pipeline.step : null;
+ var modDataCount = pCtx && pCtx.modDataCount;
+ var modBy = modDataCount != null ? Math.ceil(modDataCount / step) : null;
+ return {
+ step: step,
+ modBy: modBy,
+ modDataCount: modDataCount
+ };
+ };
- var boundingRect = target.getBoundingRect();
- var dist = anchorPoint ? anchorPoint.distance(pt1) : target instanceof Path ? nearestPointOnPath(pt1, target.path, pt2) : nearestPointOnRect(pt1, boundingRect, pt2); // TODO pt2 is in the path
+ 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()`.
+ */
- if (dist < minDist) {
- minDist = dist; // Transform back to global space.
- pt1.transform(targetTransform);
- pt2.transform(targetTransform);
- pt2.toArray(points[0]);
- pt1.toArray(points[1]);
- pt0.toArray(points[2]);
- }
- }
+ Scheduler.prototype.updateStreamModes = function (seriesModel, view) {
+ var pipeline = this._pipelineMap.get(seriesModel.uid);
- limitTurnAngle(points, labelLineModel.get('minTurnAngle'));
- labelLine.setShape({
- points: points
- });
- } // Temporal variable for the limitTurnAngle function
+ var data = seriesModel.getData();
+ 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 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
- */
+ var progressiveRender = pipeline.progressiveEnabled && view.incrementalPrepareRender && dataLen >= pipeline.threshold;
+ 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`
- function limitTurnAngle(linePoints, minTurnAngle) {
- if (!(minTurnAngle <= 180 && minTurnAngle > 0)) {
- return;
- }
+ var modDataCount = seriesModel.get('progressiveChunkMode') === 'mod' ? dataLen : null;
+ seriesModel.pipelineContext = pipeline.context = {
+ progressiveRender: progressiveRender,
+ modDataCount: modDataCount,
+ large: large
+ };
+ };
- minTurnAngle = minTurnAngle / 180 * Math.PI; // The line points can be
- // /pt1----pt2 (label)
- // /
- // pt0/
+ Scheduler.prototype.restorePipelines = function (ecModel) {
+ var scheduler = this;
+ var pipelineMap = scheduler._pipelineMap = createHashMap();
+ ecModel.eachSeries(function (seriesModel) {
+ var progressive = seriesModel.getProgressive();
+ var pipelineId = seriesModel.uid;
+ pipelineMap.set(pipelineId, {
+ id: pipelineId,
+ head: null,
+ tail: null,
+ threshold: seriesModel.getProgressiveThreshold(),
+ progressiveEnabled: progressive && !(seriesModel.preventIncremental && seriesModel.preventIncremental()),
+ blockIndex: -1,
+ step: Math.round(progressive || 700),
+ count: 0
+ });
- pt0.fromArray(linePoints[0]);
- pt1.fromArray(linePoints[1]);
- pt2.fromArray(linePoints[2]);
- Point.sub(dir, pt0, pt1);
- Point.sub(dir2, pt2, pt1);
- var len1 = dir.len();
- var len2 = dir2.len();
+ scheduler._pipe(seriesModel, seriesModel.dataTask);
+ });
+ };
- if (len1 < 1e-3 || len2 < 1e-3) {
- return;
- }
+ Scheduler.prototype.prepareStageTasks = function () {
+ var stageTaskMap = this._stageTaskMap;
+ var ecModel = this.api.getModel();
+ var api = this.api;
+ each(this._allHandlers, function (handler) {
+ var record = stageTaskMap.get(handler.uid) || stageTaskMap.set(handler.uid, {});
+ var errMsg = '';
- dir.scale(1 / len1);
- dir2.scale(1 / len2);
- var angleCos = dir.dot(dir2);
- var minTurnAngleCos = Math.cos(minTurnAngle);
+ if ("development" !== 'production') {
+ // Currently do not need to support to sepecify them both.
+ errMsg = '"reset" and "overallReset" must not be both specified.';
+ }
- 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); // Calculate new projected length with limited minTurnAngle and get the new connect point
+ assert(!(handler.reset && handler.overallReset), errMsg);
+ handler.reset && this._createSeriesStageTask(handler, record, ecModel, api);
+ handler.overallReset && this._createOverallStageTask(handler, record, ecModel, api);
+ }, this);
+ };
- tmpProjPoint.scaleAndAdd(dir2, d / Math.tan(Math.PI - minTurnAngle)); // Limit the new calculated connect point between pt1 and pt2.
+ Scheduler.prototype.prepareView = function (view, model, ecModel, api) {
+ var renderTask = view.renderTask;
+ var context = renderTask.context;
+ context.model = model;
+ context.ecModel = ecModel;
+ context.api = api;
+ renderTask.__block = !view.incrementalPrepareRender;
- var t = pt2.x !== pt1.x ? (tmpProjPoint.x - pt1.x) / (pt2.x - pt1.x) : (tmpProjPoint.y - pt1.y) / (pt2.y - pt1.y);
+ this._pipe(model, renderTask);
+ };
- if (isNaN(t)) {
- return;
- }
+ 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
+ });
+ };
- if (t < 0) {
- Point.copy(tmpProjPoint, pt1);
- } else if (t > 1) {
- Point.copy(tmpProjPoint, pt2);
- }
+ Scheduler.prototype.performVisualTasks = function (ecModel, payload, opt) {
+ this._performStageTasks(this._visualHandlers, ecModel, payload, opt);
+ };
- 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
- */
+ Scheduler.prototype._performStageTasks = function (stageHandlers, ecModel, payload, opt) {
+ opt = opt || {};
+ var unfinished = false;
+ var scheduler = this;
+ each(stageHandlers, function (stageHandler, idx) {
+ if (opt.visualType && opt.visualType !== stageHandler.visualType) {
+ return;
+ }
- function limitSurfaceAngle(linePoints, surfaceNormal, maxSurfaceAngle) {
- if (!(maxSurfaceAngle <= 180 && maxSurfaceAngle > 0)) {
- return;
- }
+ var stageHandlerRecord = scheduler._stageTaskMap.get(stageHandler.uid);
- maxSurfaceAngle = maxSurfaceAngle / 180 * Math.PI;
- pt0.fromArray(linePoints[0]);
- pt1.fromArray(linePoints[1]);
- pt2.fromArray(linePoints[2]);
- Point.sub(dir, pt1, pt0);
- Point.sub(dir2, pt2, pt1);
- var len1 = dir.len();
- var len2 = dir2.len();
+ var seriesTaskMap = stageHandlerRecord.seriesTaskMap;
+ var overallTask = stageHandlerRecord.overallTask;
- if (len1 < 1e-3 || len2 < 1e-3) {
- return;
- }
+ if (overallTask) {
+ var overallNeedDirty_1;
+ var agentStubMap = overallTask.agentStubMap;
+ agentStubMap.each(function (stub) {
+ if (needSetDirty(opt, stub)) {
+ stub.dirty();
+ overallNeedDirty_1 = true;
+ }
+ });
+ overallNeedDirty_1 && overallTask.dirty();
+ scheduler.updatePayload(overallTask, payload);
+ 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.
- dir.scale(1 / len1);
- dir2.scale(1 / len2);
- var angleCos = dir.dot(surfaceNormal);
- var maxSurfaceAngleCos = Math.cos(maxSurfaceAngle);
+ agentStubMap.each(function (stub) {
+ stub.perform(performArgs_1);
+ });
- 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;
- var angle2 = Math.acos(dir2.dot(surfaceNormal));
- var newAngle = HALF_PI + angle2 - maxSurfaceAngle;
+ if (overallTask.perform(performArgs_1)) {
+ unfinished = true;
+ }
+ } else if (seriesTaskMap) {
+ seriesTaskMap.each(function (task, pipelineId) {
+ if (needSetDirty(opt, task)) {
+ task.dirty();
+ }
- if (newAngle >= HALF_PI) {
- // parallel
- Point.copy(tmpProjPoint, pt2);
- } else {
- // 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 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.
- var t = pt2.x !== pt1.x ? (tmpProjPoint.x - pt1.x) / (pt2.x - pt1.x) : (tmpProjPoint.y - pt1.y) / (pt2.y - pt1.y);
+ performArgs.skip = !stageHandler.performRawSeries && ecModel.isSeriesFiltered(task.context.model);
+ scheduler.updatePayload(task, payload);
- if (isNaN(t)) {
- return;
+ if (task.perform(performArgs)) {
+ unfinished = true;
+ }
+ });
}
+ });
- if (t < 0) {
- Point.copy(tmpProjPoint, pt1);
- } else if (t > 1) {
- Point.copy(tmpProjPoint, pt2);
- }
+ function needSetDirty(opt, task) {
+ return opt.setDirty && (!opt.dirtyMap || opt.dirtyMap.get(task.__pipeline.id));
}
- tmpProjPoint.toArray(linePoints[1]);
- }
- }
-
- function setLabelLineState(labelLine, ignore, stateName, stateModel) {
- var isNormal = stateName === 'normal';
- var stateObj = isNormal ? labelLine : labelLine.ensureState(stateName); // Make sure display.
+ this.unfinished = unfinished || this.unfinished;
+ };
- stateObj.ignore = ignore; // Set smooth
+ 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;
+ };
- var smooth = stateModel.get('smooth');
+ Scheduler.prototype.plan = function () {
+ // Travel pipelines, check block.
+ this._pipelineMap.each(function (pipeline) {
+ var task = pipeline.tail;
- if (smooth && smooth === true) {
- smooth = 0.3;
- }
+ do {
+ if (task.__block) {
+ pipeline.blockIndex = task.__idxInPipeline;
+ break;
+ }
- stateObj.shape = stateObj.shape || {};
+ task = task.getUpstream();
+ } while (task);
+ });
+ };
- if (smooth > 0) {
- stateObj.shape.smooth = smooth;
- }
+ Scheduler.prototype.updatePayload = function (task, payload) {
+ payload !== 'remain' && (task.context.payload = payload);
+ };
- var styleObj = stateModel.getModel('lineStyle').getLineStyle();
- isNormal ? labelLine.useStyle(styleObj) : stateObj.style = styleObj;
- }
+ Scheduler.prototype._createSeriesStageTask = function (stageHandler, stageHandlerRecord, ecModel, api) {
+ var scheduler = this;
+ var oldSeriesTaskMap = stageHandlerRecord.seriesTaskMap; // The count of stages are totally about only several dozen, so
+ // do not need to reuse the map.
- function buildLabelLinePath(path, shape) {
- var smooth = shape.smooth;
- var points = shape.points;
+ var newSeriesTaskMap = stageHandlerRecord.seriesTaskMap = createHashMap();
+ var seriesType = stageHandler.seriesType;
+ 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 (!points) {
- return;
- }
+ if (stageHandler.createOnAllSeries) {
+ ecModel.eachRawSeries(create);
+ } else if (seriesType) {
+ ecModel.eachRawSeriesByType(seriesType, create);
+ } else if (getTargetSeries) {
+ getTargetSeries(ecModel, api).each(create);
+ }
- path.moveTo(points[0][0], points[0][1]);
+ function create(seriesModel) {
+ var pipelineId = seriesModel.uid; // Init tasks for each seriesModel only once.
+ // Reuse original task instance.
- if (smooth > 0 && points.length >= 3) {
- var len1 = dist(points[0], points[1]);
- var len2 = dist(points[1], points[2]);
+ var task = newSeriesTaskMap.set(pipelineId, oldSeriesTaskMap && oldSeriesTaskMap.get(pipelineId) || createTask({
+ plan: seriesTaskPlan,
+ reset: seriesTaskReset,
+ count: seriesTaskCount
+ }));
+ task.context = {
+ model: seriesModel,
+ ecModel: ecModel,
+ api: api,
+ // PENDING: `useClearVisual` not used?
+ useClearVisual: stageHandler.isVisual && !stageHandler.isLayout,
+ plan: stageHandler.plan,
+ reset: stageHandler.reset,
+ scheduler: scheduler
+ };
- if (!len1 || !len2) {
- path.lineTo(points[1][0], points[1][1]);
- path.lineTo(points[2][0], points[2][1]);
- return;
+ scheduler._pipe(seriesModel, task);
}
+ };
- var moveLen = Math.min(len1, len2) * smooth;
- var midPoint0 = lerp([], points[1], points[0], moveLen / len1);
- var midPoint2 = lerp([], points[1], points[2], moveLen / len2);
- var midPoint1 = lerp([], midPoint0, midPoint2, 0.5);
- path.bezierCurveTo(midPoint0[0], midPoint0[1], midPoint0[0], midPoint0[1], midPoint1[0], midPoint1[1]);
- path.bezierCurveTo(midPoint2[0], midPoint2[1], midPoint2[0], midPoint2[1], points[2][0], points[2][1]);
- } else {
- for (var i = 1; i < points.length; i++) {
- path.lineTo(points[i][0], points[i][1]);
- }
- }
- }
- /**
- * Create a label line if necessary and set it's style.
- */
+ Scheduler.prototype._createOverallStageTask = function (stageHandler, stageHandlerRecord, ecModel, api) {
+ var scheduler = this;
+ var overallTask = stageHandlerRecord.overallTask = stageHandlerRecord.overallTask // For overall task, the function only be called on reset stage.
+ || createTask({
+ reset: overallTaskReset
+ });
+ overallTask.context = {
+ ecModel: ecModel,
+ api: api,
+ overallReset: stageHandler.overallReset,
+ scheduler: scheduler
+ };
+ 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; // 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.
- function setLabelLineStyle(targetEl, statesModels, defaultStyle) {
- var labelLine = targetEl.getTextGuideLine();
- var label = targetEl.getTextContent();
+ var errMsg = '';
- if (!label) {
- // Not show label line if there is no label.
- if (labelLine) {
- targetEl.removeTextGuideLine();
+ if ("development" !== 'production') {
+ errMsg = '"createOnAllSeries" do not supported for "overallReset", ' + 'becuase it will block all streams.';
}
- return;
- }
+ assert(!stageHandler.createOnAllSeries, errMsg);
- var normalModel = statesModels.normal;
- var showNormal = normalModel.get('show');
- var labelIgnoreNormal = label.ignore;
+ if (seriesType) {
+ ecModel.eachRawSeriesByType(seriesType, createStub);
+ } else if (getTargetSeries) {
+ getTargetSeries(ecModel, api).each(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);
+ }
- for (var i = 0; i < DISPLAY_STATES.length; i++) {
- var stateName = DISPLAY_STATES[i];
- var stateModel = statesModels[stateName];
- var isNormal = stateName === 'normal';
+ function createStub(seriesModel) {
+ var pipelineId = seriesModel.uid;
+ 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 // FIXME:TS never used, so comment it
+ // modifyOutputEnd: modifyOutputEnd
- if (stateModel) {
- var stateShow = stateModel.get('show');
- var isLabelIgnored = isNormal ? labelIgnoreNormal : retrieve2(label.states[stateName] && label.states[stateName].ignore, labelIgnoreNormal);
+ };
+ stub.agent = overallTask;
+ stub.__block = overallProgress;
- 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;
+ scheduler._pipe(seriesModel, stub);
+ }
- if (stateObj) {
- stateObj.ignore = true;
- }
+ if (shouldOverallTaskDirty) {
+ overallTask.dirty();
+ }
+ };
- continue;
- } // Create labelLine if not exists
+ Scheduler.prototype._pipe = function (seriesModel, task) {
+ var pipelineId = seriesModel.uid;
+ var pipeline = this._pipelineMap.get(pipelineId);
- if (!labelLine) {
- labelLine = new Polyline();
- targetEl.setTextGuideLine(labelLine); // Reset state of normal because it's new created.
- // NOTE: NORMAL should always been the first!
+ !pipeline.head && (pipeline.head = task);
+ pipeline.tail && pipeline.tail.pipe(task);
+ pipeline.tail = task;
+ task.__idxInPipeline = pipeline.count++;
+ task.__pipeline = pipeline;
+ };
- if (!isNormal && (labelIgnoreNormal || !showNormal)) {
- setLabelLineState(labelLine, true, 'normal', statesModels.normal);
- } // Use same state proxy.
+ Scheduler.wrapStageHandler = function (stageHandler, visualType) {
+ if (isFunction(stageHandler)) {
+ stageHandler = {
+ overallReset: stageHandler,
+ seriesType: detectSeriseType(stageHandler)
+ };
+ }
+ stageHandler.uid = getUID('stageHandler');
+ visualType && (stageHandler.visualType = visualType);
+ return stageHandler;
+ };
+ return Scheduler;
+ }();
- if (targetEl.stateProxy) {
- labelLine.stateProxy = targetEl.stateProxy;
- }
- }
+ function overallTaskReset(context) {
+ context.overallReset(context.ecModel, context.api, context.payload);
+ }
- setLabelLineState(labelLine, false, stateName, stateModel);
- }
- }
+ function stubReset(context) {
+ return context.overallProgress && stubProgress;
+ }
- if (labelLine) {
- defaults(labelLine.style, defaultStyle); // Not fill.
+ function stubProgress() {
+ this.agent.dirty();
+ this.getDownstream().dirty();
+ }
- labelLine.style.fill = null;
- var showAbove = normalModel.get('showAbove');
- var labelLineConfig = targetEl.textGuideLineConfig = targetEl.textGuideLineConfig || {};
- labelLineConfig.showAbove = showAbove || false; // Custom the buildPath.
+ function stubOnDirty() {
+ this.agent && this.agent.dirty();
+ }
- labelLine.buildPath = buildLabelLinePath;
- }
+ function seriesTaskPlan(context) {
+ return context.plan ? context.plan(context.model, context.ecModel, context.api, context.payload) : null;
}
- function getLabelLineStatesModels(itemModel, labelLineName) {
- labelLineName = labelLineName || 'labelLine';
- var statesModels = {
- normal: itemModel.getModel(labelLineName)
- };
- for (var i = 0; i < SPECIAL_STATES.length; i++) {
- var stateName = SPECIAL_STATES[i];
- statesModels[stateName] = itemModel.getModel([stateName, labelLineName]);
+ function seriesTaskReset(context) {
+ if (context.useClearVisual) {
+ context.data.clearAllVisual();
}
- return statesModels;
+ var resetDefines = context.resetDefines = normalizeToArray(context.reset(context.model, context.ecModel, context.api, context.payload));
+ return resetDefines.length > 1 ? map(resetDefines, function (v, idx) {
+ return makeSeriesTaskProgress(idx);
+ }) : singleSeriesTaskProgress;
}
- function prepareLayoutList(input) {
- var list = [];
+ var singleSeriesTaskProgress = makeSeriesTaskProgress(0);
- for (var i = 0; i < input.length; i++) {
- var rawItem = input[i];
+ function makeSeriesTaskProgress(resetDefineIdx) {
+ return function (params, context) {
+ var data = context.data;
+ var resetDefine = context.resetDefines[resetDefineIdx];
- if (rawItem.defaultAttr.ignore) {
- continue;
+ if (resetDefine && resetDefine.dataEach) {
+ for (var i = params.start; i < params.end; i++) {
+ resetDefine.dataEach(data, i);
+ }
+ } else if (resetDefine && resetDefine.progress) {
+ resetDefine.progress(params, data);
}
+ };
+ }
- var label = rawItem.label;
- 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;
- var globalRect = localRect.clone();
- globalRect.applyTransform(transform);
- globalRect.x -= minMargin / 2;
- globalRect.y -= minMargin / 2;
- globalRect.width += minMargin;
- globalRect.height += minMargin;
- var obb = isAxisAligned ? new OrientedBoundingRect(localRect, transform) : null;
- list.push({
- label: label,
- labelLine: rawItem.labelLine,
- rect: globalRect,
- localRect: localRect,
- obb: obb,
- priority: rawItem.priority,
- defaultAttr: rawItem.defaultAttr,
- layoutOption: rawItem.computedLayoutOption,
- axisAligned: isAxisAligned,
- transform: transform
- });
- }
-
- return list;
+ 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 shiftLayout(list, xyDim, sizeDim, minBound, maxBound, balanceShift) {
- var len = list.length;
- if (len < 2) {
- return;
- }
+ function detectSeriseType(legacyFunc) {
+ seriesType = null;
- list.sort(function (a, b) {
- return a.rect[xyDim] - b.rect[xyDim];
- });
- var lastPos = 0;
- var delta;
- var adjusted = false;
- var totalShifts = 0;
+ try {
+ // Assume there is no async when calling `eachSeriesByType`.
+ legacyFunc(ecModelMock, apiMock);
+ } catch (e) {}
- for (var i = 0; i < len; i++) {
- var item = list[i];
- var rect = item.rect;
- delta = rect[xyDim] - lastPos;
+ return seriesType;
+ }
- if (delta < 0) {
- // shiftForward(i, len, -delta);
- rect[xyDim] -= delta;
- item.label[xyDim] -= delta;
- adjusted = true;
- }
+ var ecModelMock = {};
+ var apiMock = {};
+ var seriesType;
+ mockMethods(ecModelMock, GlobalModel);
+ mockMethods(apiMock, ExtensionAPI);
- var shift = Math.max(-delta, 0);
- totalShifts += shift;
- lastPos = rect[xyDim] + rect[sizeDim];
+ ecModelMock.eachSeriesByType = ecModelMock.eachRawSeriesByType = function (type) {
+ seriesType = type;
+ };
+
+ ecModelMock.eachComponent = function (cond) {
+ if (cond.mainType === 'series' && cond.subType) {
+ seriesType = cond.subType;
}
+ };
- if (totalShifts > 0 && balanceShift) {
- // Shift back to make the distribution more equally.
- shiftList(-totalShifts / len, 0, len);
- } // TODO bleedMargin?
+ function mockMethods(target, Clz) {
+ /* eslint-disable */
+ for (var name_1 in Clz.prototype) {
+ // Do not use hasOwnProperty
+ target[name_1] = noop;
+ }
+ /* eslint-enable */
+ }
- var first = list[0];
- var last = list[len - 1];
- var minGap;
- var maxGap;
- updateMinMaxGap(); // If ends exceed two bounds, squeeze at most 80%, then take the gap of two bounds.
+ /*
+ * 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.
+ */
- minGap < 0 && squeezeGaps(-minGap, 0.8);
- maxGap < 0 && squeezeGaps(maxGap, 0.8);
- updateMinMaxGap();
- takeBoundsGap(minGap, maxGap, 1);
- takeBoundsGap(maxGap, minGap, -1); // Handle bailout when there is not enough space.
- updateMinMaxGap();
+ /**
+ * AUTO-GENERATED FILE. DO NOT MODIFY.
+ */
- if (minGap < 0) {
- squeezeWhenBailout(-minGap);
- }
+ /*
+ * 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,
+ colorLayer: [['#37A2DA', '#ffd85c', '#fd7b5f'], ['#37A2DA', '#67E0E3', '#FFDB5C', '#ff9f7f', '#E062AE', '#9d96f5'], ['#37A2DA', '#32C5E9', '#9FE6B8', '#FFDB5C', '#ff9f7f', '#fb7293', '#e7bcf3', '#8378EA', '#96BFFF'], colorAll]
+ };
- if (maxGap < 0) {
- squeezeWhenBailout(maxGap);
- }
+ /*
+ * 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 updateMinMaxGap() {
- minGap = first.rect[xyDim] - minBound;
- maxGap = maxBound - last.rect[xyDim] - last.rect[sizeDim];
- }
- function takeBoundsGap(gapThisBound, gapOtherBound, moveDir) {
- if (gapThisBound < 0) {
- // Move from other gap if can.
- var moveFromMaxGap = Math.min(gapOtherBound, -gapThisBound);
+ /**
+ * AUTO-GENERATED FILE. DO NOT MODIFY.
+ */
- if (moveFromMaxGap > 0) {
- shiftList(moveFromMaxGap * moveDir, 0, len);
- var remained = moveFromMaxGap + gapThisBound;
+ /*
+ * 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';
- if (remained < 0) {
- squeezeGaps(-remained * moveDir, 1);
- }
- } else {
- squeezeGaps(-gapThisBound * moveDir, 1);
+ var axisCommon = function () {
+ return {
+ axisLine: {
+ lineStyle: {
+ color: contrastColor
+ }
+ },
+ splitLine: {
+ lineStyle: {
+ color: '#484753'
+ }
+ },
+ splitArea: {
+ areaStyle: {
+ color: ['rgba(255,255,255,0.02)', 'rgba(255,255,255,0.05)']
+ }
+ },
+ minorSplitLine: {
+ lineStyle: {
+ color: '#20203B'
}
}
- }
+ };
+ };
- function shiftList(delta, start, end) {
- if (delta !== 0) {
- adjusted = true;
+ var colorPalette = ['#4992ff', '#7cffb2', '#fddd60', '#ff6e76', '#58d9f9', '#05c091', '#ff8a45', '#8d48e3', '#dd79ff'];
+ var theme = {
+ darkMode: true,
+ color: colorPalette,
+ backgroundColor: backgroundColor,
+ axisPointer: {
+ lineStyle: {
+ color: '#817f91'
+ },
+ crossStyle: {
+ color: '#817f91'
+ },
+ label: {
+ // TODO Contrast of label backgorundColor
+ color: '#fff'
}
-
- for (var i = start; i < end; i++) {
- var item = list[i];
- var rect = item.rect;
- rect[xyDim] += delta;
- item.label[xyDim] += delta;
+ },
+ legend: {
+ textStyle: {
+ color: contrastColor
+ }
+ },
+ textStyle: {
+ color: contrastColor
+ },
+ title: {
+ textStyle: {
+ color: '#EEF1FA'
+ },
+ subtextStyle: {
+ color: '#B9B8CE'
+ }
+ },
+ toolbox: {
+ iconStyle: {
+ borderColor: contrastColor
+ }
+ },
+ dataZoom: {
+ borderColor: '#71708A',
+ textStyle: {
+ color: contrastColor
+ },
+ brushStyle: {
+ color: 'rgba(135,163,206,0.3)'
+ },
+ handleStyle: {
+ color: '#353450',
+ borderColor: '#C5CBE3'
+ },
+ moveHandleStyle: {
+ color: '#B0B6C3',
+ opacity: 0.3
+ },
+ fillerColor: 'rgba(135,163,206,0.2)',
+ emphasis: {
+ handleStyle: {
+ borderColor: '#91B7F2',
+ color: '#4D587D'
+ },
+ moveHandleStyle: {
+ color: '#636D9A',
+ opacity: 0.7
+ }
+ },
+ dataBackground: {
+ lineStyle: {
+ color: '#71708A',
+ width: 1
+ },
+ areaStyle: {
+ color: '#71708A'
+ }
+ },
+ selectedDataBackground: {
+ lineStyle: {
+ color: '#87A3CE'
+ },
+ areaStyle: {
+ color: '#87A3CE'
+ }
+ }
+ },
+ visualMap: {
+ textStyle: {
+ color: contrastColor
}
- } // Squeeze gaps if the labels exceed margin.
-
-
- function squeezeGaps(delta, maxSqeezePercent) {
- var gaps = [];
- var totalGaps = 0;
-
- for (var i = 1; i < len; i++) {
- var prevItemRect = list[i - 1].rect;
- var gap = Math.max(list[i].rect[xyDim] - prevItemRect[xyDim] - prevItemRect[sizeDim], 0);
- gaps.push(gap);
- totalGaps += gap;
+ },
+ timeline: {
+ lineStyle: {
+ color: contrastColor
+ },
+ label: {
+ color: contrastColor
+ },
+ controlStyle: {
+ color: contrastColor,
+ borderColor: contrastColor
}
-
- if (!totalGaps) {
- return;
+ },
+ calendar: {
+ itemStyle: {
+ color: backgroundColor
+ },
+ dayLabel: {
+ color: contrastColor
+ },
+ monthLabel: {
+ color: contrastColor
+ },
+ yearLabel: {
+ color: contrastColor
}
-
- var squeezePercent = Math.min(Math.abs(delta) / totalGaps, maxSqeezePercent);
-
- if (delta > 0) {
- for (var i = 0; i < len - 1; i++) {
- // 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);
+ },
+ timeAxis: axisCommon(),
+ logAxis: axisCommon(),
+ valueAxis: axisCommon(),
+ categoryAxis: axisCommon(),
+ line: {
+ symbol: 'circle'
+ },
+ graph: {
+ color: colorPalette
+ },
+ gauge: {
+ title: {
+ color: contrastColor
+ },
+ axisLine: {
+ lineStyle: {
+ color: [[1, 'rgba(207,212,219,0.2)']]
}
+ },
+ axisLabel: {
+ color: contrastColor
+ },
+ detail: {
+ color: '#EEF1FA'
}
- }
- /**
- * 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;
- delta = Math.abs(delta);
- var moveForEachLabel = Math.ceil(delta / (len - 1));
-
- 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);
- }
-
- delta -= moveForEachLabel;
+ },
+ candlestick: {
+ itemStyle: {
+ color: '#f64e56',
+ color0: '#54ea92',
+ borderColor: '#f64e56',
+ borderColor0: '#54ea92' // borderColor: '#ca2824',
+ // borderColor0: '#09a443'
- if (delta <= 0) {
- return;
- }
}
}
+ };
+ theme.categoryAxis.splitLine.show = false;
- return adjusted;
- }
- /**
- * Adjust labels on x direction to avoid overlap.
- */
-
-
- 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);
- }
/**
- * Adjust labels on y direction to avoid overlap.
+ * 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.
*/
- 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 = []; // TODO, render overflow visible first, put in the displayedLabels.
-
- labelList.sort(function (a, b) {
- return b.priority - a.priority;
- });
- var globalRect = new BoundingRect(0, 0, 0, 0);
-
- function hideEl(el) {
- if (!el.ignore) {
- // Show on emphasis.
- var emphasisState = el.ensureState('emphasis');
-
- if (emphasisState.ignore == null) {
- emphasisState.ignore = false;
- }
- }
+ var ECEventProcessor =
+ /** @class */
+ function () {
+ function ECEventProcessor() {}
- el.ignore = true;
- }
+ ECEventProcessor.prototype.normalizeQuery = function (query) {
+ var cptQuery = {};
+ var dataQuery = {};
+ var otherQuery = {}; // `query` is `mainType` or `mainType.subType` of component.
- for (var i = 0; i < labelList.length; i++) {
- var labelItem = labelList[i];
- var isAxisAligned = labelItem.axisAligned;
- var localRect = labelItem.localRect;
- var transform = labelItem.transform;
- var label = labelItem.label;
- var labelLine = labelItem.labelLine;
- globalRect.copy(labelItem.rect); // Add a threshold because layout may be aligned precisely.
+ if (isString(query)) {
+ var condCptType = parseClassType(query); // `.main` and `.sub` may be ''.
- globalRect.width -= 0.1;
- globalRect.height -= 0.1;
- globalRect.x += 0.05;
- globalRect.y += 0.05;
- var obb = labelItem.obb;
- var overlapped = false;
+ cptQuery.mainType = condCptType.main || null;
+ cptQuery.subType = condCptType.sub || null;
+ } // `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 j = 0; j < displayedLabels.length; j++) {
- var existsTextCfg = displayedLabels[j]; // Fast rejection.
+ for (var i = 0; i < suffixes_1.length; i++) {
+ var propSuffix = suffixes_1[i];
+ var suffixPos = key.lastIndexOf(propSuffix);
- if (!globalRect.intersect(existsTextCfg.rect)) {
- continue;
- }
+ if (suffixPos > 0 && suffixPos === key.length - propSuffix.length) {
+ var mainType = key.slice(0, suffixPos); // Consider `dataIndex`.
- if (isAxisAligned && existsTextCfg.axisAligned) {
- // Is overlapped
- overlapped = true;
- break;
- }
+ if (mainType !== 'data') {
+ cptQuery.mainType = mainType;
+ cptQuery[propSuffix.toLowerCase()] = val;
+ reserved = true;
+ }
+ }
+ }
- if (!existsTextCfg.obb) {
- // If self is not axis aligned. But other is.
- existsTextCfg.obb = new OrientedBoundingRect(existsTextCfg.localRect, existsTextCfg.transform);
- }
+ if (dataKeys_1.hasOwnProperty(key)) {
+ dataQuery[key] = val;
+ reserved = true;
+ }
- if (!obb) {
- // If self is axis aligned. But other is not.
- obb = new OrientedBoundingRect(localRect, transform);
+ if (!reserved) {
+ otherQuery[key] = val;
+ }
+ });
}
- if (obb.intersect(existsTextCfg.obb)) {
- overlapped = true;
- break;
- }
- } // TODO Callback to determine if this overlap should be handled?
+ return {
+ cptQuery: cptQuery,
+ dataQuery: dataQuery,
+ otherQuery: otherQuery
+ };
+ };
+ ECEventProcessor.prototype.filter = function (eventType, query) {
+ // They should be assigned before each trigger call.
+ var eventInfo = this.eventInfo;
- if (overlapped) {
- hideEl(label);
- labelLine && hideEl(labelLine);
- } else {
- label.attr('ignore', labelItem.defaultAttr.ignore);
- labelLine && labelLine.attr('ignore', labelItem.defaultAttr.labelGuideIgnore);
- displayedLabels.push(labelItem);
+ if (!eventInfo) {
+ return true;
}
- }
- }
- function cloneArr(points) {
- if (points) {
- var newPoints = [];
+ var targetEl = eventInfo.targetEl;
+ var packedEvent = eventInfo.packedEvent;
+ var model = eventInfo.model;
+ var view = eventInfo.view; // For event like 'globalout'.
- for (var i = 0; i < points.length; i++) {
- newPoints.push(points[i].slice());
+ if (!model || !view) {
+ return true;
}
- return newPoints;
- }
- }
-
- function prepareLayoutCallbackParams(labelItem, hostEl) {
- var label = labelItem.label;
- var labelLine = hostEl && hostEl.getTextGuideLine();
- return {
- dataIndex: labelItem.dataIndex,
- dataType: labelItem.dataType,
- seriesIndex: labelItem.seriesModel.seriesIndex,
- 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)
- };
- }
-
- var LABEL_OPTION_TO_STYLE_KEYS = ['align', 'verticalAlign', 'width', 'height', 'fontSize'];
- var dummyTransformable = new Transformable();
- var labelLayoutInnerStore = makeInner();
- var labelLineAnimationStore = makeInner();
-
- function extendWithKeys(target, source, keys) {
- for (var i = 0; i < keys.length; i++) {
- var key = keys[i];
+ var cptQuery = query.cptQuery;
+ var dataQuery = query.dataQuery;
+ return check(cptQuery, model, 'mainType') && check(cptQuery, model, 'subType') && check(cptQuery, model, 'index', 'componentIndex') && check(cptQuery, model, 'name') && check(cptQuery, model, 'id') && check(dataQuery, packedEvent, 'name') && check(dataQuery, packedEvent, 'dataIndex') && check(dataQuery, packedEvent, 'dataType') && (!view.filterForExposedEvent || view.filterForExposedEvent(eventType, query.otherQuery, targetEl, packedEvent));
- if (source[key] != null) {
- target[key] = source[key];
+ function check(query, host, prop, propOnHost) {
+ return query[prop] == null || host[propOnHost || prop] === query[prop];
}
- }
- }
-
- var LABEL_LAYOUT_PROPS = ['x', 'y', 'rotation'];
-
- var LabelManager =
- /** @class */
- function () {
- function LabelManager() {
- this._labelList = [];
- this._chartViewList = [];
- }
-
- LabelManager.prototype.clearLabels = function () {
- this._labelList = [];
- this._chartViewList = [];
};
- /**
- * Add label to manager
- */
+ ECEventProcessor.prototype.afterTrigger = function () {
+ // Make sure the eventInfo wont be used in next trigger.
+ this.eventInfo = null;
+ };
- LabelManager.prototype._addLabel = function (dataIndex, dataType, seriesModel, label, layoutOption) {
- var labelStyle = label.style;
- var hostEl = label.__hostTarget;
- var textConfig = hostEl.textConfig || {}; // TODO: If label is in other state.
+ return ECEventProcessor;
+ }();
- var labelTransform = label.getComputedTransform();
- var labelRect = label.getBoundingRect().plain();
- BoundingRect.applyTransform(labelRect, labelRect, labelTransform);
+ var seriesSymbolTask = {
+ createOnAllSeries: true,
+ // For legend.
+ performRawSeries: true,
+ reset: function (seriesModel, ecModel) {
+ var data = seriesModel.getData();
- if (labelTransform) {
- dummyTransformable.setLocalTransform(labelTransform);
- } else {
- // Identity transform.
- dummyTransformable.x = dummyTransformable.y = dummyTransformable.rotation = dummyTransformable.originX = dummyTransformable.originY = 0;
- dummyTransformable.scaleX = dummyTransformable.scaleY = 1;
+ if (seriesModel.legendIcon) {
+ data.setVisual('legendIcon', seriesModel.legendIcon);
}
- var host = label.__hostTarget;
- var hostRect;
-
- if (host) {
- hostRect = host.getBoundingRect().plain();
- var transform = host.getComputedTransform();
- BoundingRect.applyTransform(hostRect, hostRect, transform);
+ if (!seriesModel.hasSymbolVisual) {
+ return;
}
- var labelGuide = hostRect && host.getTextGuideLine();
-
- this._labelList.push({
- label: label,
- labelLine: labelGuide,
- seriesModel: seriesModel,
- dataIndex: dataIndex,
- dataType: dataType,
- layoutOption: layoutOption,
- 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,
- x: dummyTransformable.x,
- y: dummyTransformable.y,
- scaleX: dummyTransformable.scaleX,
- scaleY: dummyTransformable.scaleY,
- rotation: dummyTransformable.rotation,
- style: {
- x: labelStyle.x,
- y: labelStyle.y,
- align: labelStyle.align,
- verticalAlign: labelStyle.verticalAlign,
- width: labelStyle.width,
- height: labelStyle.height,
- fontSize: labelStyle.fontSize
- },
- cursor: label.cursor,
- attachedPos: textConfig.position,
- attachedRot: textConfig.rotation
- }
- });
- };
-
- LabelManager.prototype.addLabelsOfSeries = function (chartView) {
- var _this = this;
-
- this._chartViewList.push(chartView);
-
- var seriesModel = chartView.__model;
- var layoutOption = seriesModel.get('labelLayout');
- /**
- * Ignore layouting if it's not specified anything.
- */
+ var symbolType = seriesModel.get('symbol');
+ var symbolSize = seriesModel.get('symbolSize');
+ var keepAspect = seriesModel.get('symbolKeepAspect');
+ var symbolRotate = seriesModel.get('symbolRotate');
+ var symbolOffset = seriesModel.get('symbolOffset');
+ var hasSymbolTypeCallback = isFunction(symbolType);
+ var hasSymbolSizeCallback = isFunction(symbolSize);
+ var hasSymbolRotateCallback = isFunction(symbolRotate);
+ var hasSymbolOffsetCallback = isFunction(symbolOffset);
+ var hasCallback = hasSymbolTypeCallback || hasSymbolSizeCallback || hasSymbolRotateCallback || hasSymbolOffsetCallback;
+ var seriesSymbol = !hasSymbolTypeCallback && symbolType ? symbolType : seriesModel.defaultSymbol;
+ var seriesSymbolSize = !hasSymbolSizeCallback ? symbolSize : null;
+ var seriesSymbolRotate = !hasSymbolRotateCallback ? symbolRotate : null;
+ var seriesSymbolOffset = !hasSymbolOffsetCallback ? symbolOffset : null;
+ data.setVisual({
+ legendIcon: seriesModel.legendIcon || 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,
+ symbolOffset: seriesSymbolOffset
+ }); // Only visible series has each data be visual encoded
- if (!(isFunction(layoutOption) || keys(layoutOption).length)) {
+ if (ecModel.isSeriesFiltered(seriesModel)) {
return;
}
- chartView.group.traverse(function (child) {
- if (child.ignore) {
- return true; // Stop traverse descendants.
- } // Only support label being hosted on graphic elements.
-
-
- var textEl = child.getTextContent();
- var ecData = getECData(child); // Can only attach the text on the element with dataIndex
+ function dataEach(data, idx) {
+ var rawValue = seriesModel.getRawValue(idx);
+ var params = seriesModel.getDataParams(idx);
+ hasSymbolTypeCallback && data.setItemVisual(idx, 'symbol', symbolType(rawValue, params));
+ hasSymbolSizeCallback && data.setItemVisual(idx, 'symbolSize', symbolSize(rawValue, params));
+ hasSymbolRotateCallback && data.setItemVisual(idx, 'symbolRotate', symbolRotate(rawValue, params));
+ hasSymbolOffsetCallback && data.setItemVisual(idx, 'symbolOffset', symbolOffset(rawValue, params));
+ }
- if (textEl && !textEl.disableLabelLayout) {
- _this._addLabel(ecData.dataIndex, ecData.dataType, seriesModel, textEl, layoutOption);
- }
- });
- };
+ return {
+ dataEach: hasCallback ? dataEach : null
+ };
+ }
+ };
+ 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
- LabelManager.prototype.updateLayoutConfig = function (api) {
- var width = api.getWidth();
- var height = api.getHeight();
- function createDragHandler(el, labelLineModel) {
- return function () {
- updateLabelLinePoints(el, labelLineModel);
- };
+ if (ecModel.isSeriesFiltered(seriesModel)) {
+ return;
}
- for (var i = 0; i < this._labelList.length; i++) {
- var labelItem = this._labelList[i];
- var label = labelItem.label;
- var hostEl = label.__hostTarget;
- var defaultLabelAttr = labelItem.defaultAttr;
- var layoutOption = void 0; // TODO A global layout option?
-
- if (typeof labelItem.layoutOption === 'function') {
- layoutOption = labelItem.layoutOption(prepareLayoutCallbackParams(labelItem, hostEl));
- } else {
- layoutOption = labelItem.layoutOption;
- }
-
- layoutOption = layoutOption || {};
- labelItem.computedLayoutOption = layoutOption;
- var degreeToRadian = Math.PI / 180; // TODO hostEl should always exists.
- // Or label should not have parent because the x, y is all in global space.
-
- 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]
- });
- }
-
- var needsUpdateLabelLine = false;
+ var data = seriesModel.getData();
- if (layoutOption.x != null) {
- // TODO width of chart view.
- label.x = parsePercent$1(layoutOption.x, width);
- label.setStyle('x', 0); // Ignore movement in style. TODO: origin.
+ function dataEach(data, idx) {
+ var itemModel = data.getItemModel(idx);
+ var itemSymbolType = itemModel.getShallow('symbol', true);
+ var itemSymbolSize = itemModel.getShallow('symbolSize', true);
+ var itemSymbolRotate = itemModel.getShallow('symbolRotate', true);
+ var itemSymbolOffset = itemModel.getShallow('symbolOffset', true);
+ var itemSymbolKeepAspect = itemModel.getShallow('symbolKeepAspect', true); // If has item symbol
- needsUpdateLabelLine = true;
- } else {
- label.x = defaultLabelAttr.x;
- label.setStyle('x', defaultLabelAttr.style.x);
+ if (itemSymbolType != null) {
+ data.setItemVisual(idx, 'symbol', itemSymbolType);
}
- if (layoutOption.y != null) {
- // TODO height of chart view.
- label.y = parsePercent$1(layoutOption.y, height);
- label.setStyle('y', 0); // Ignore movement in style.
-
- needsUpdateLabelLine = true;
- } else {
- label.y = defaultLabelAttr.y;
- label.setStyle('y', defaultLabelAttr.style.y);
+ if (itemSymbolSize != null) {
+ // PENDING Transform symbolSize ?
+ data.setItemVisual(idx, 'symbolSize', itemSymbolSize);
}
- if (layoutOption.labelLinePoints) {
- var guideLine = hostEl.getTextGuideLine();
-
- if (guideLine) {
- guideLine.setShape({
- points: layoutOption.labelLinePoints
- }); // Not update
-
- needsUpdateLabelLine = false;
- }
+ if (itemSymbolRotate != null) {
+ data.setItemVisual(idx, 'symbolRotate', itemSymbolRotate);
}
- var labelLayoutStore = labelLayoutInnerStore(label);
- labelLayoutStore.needsUpdateLabelLine = needsUpdateLabelLine;
- label.rotation = layoutOption.rotate != null ? layoutOption.rotate * degreeToRadian : defaultLabelAttr.rotation;
- label.scaleX = defaultLabelAttr.scaleX;
- label.scaleY = defaultLabelAttr.scaleY;
-
- for (var k = 0; k < LABEL_OPTION_TO_STYLE_KEYS.length; k++) {
- var key = LABEL_OPTION_TO_STYLE_KEYS[k];
- label.setStyle(key, layoutOption[key] != null ? layoutOption[key] : defaultLabelAttr.style[key]);
+ if (itemSymbolOffset != null) {
+ data.setItemVisual(idx, 'symbolOffset', itemSymbolOffset);
}
- if (layoutOption.draggable) {
- label.draggable = true;
- label.cursor = 'move';
-
- if (hostEl) {
- var hostModel = labelItem.seriesModel;
-
- if (labelItem.dataIndex != null) {
- var data = labelItem.seriesModel.getData(labelItem.dataType);
- hostModel = data.getItemModel(labelItem.dataIndex);
- }
-
- label.on('drag', createDragHandler(hostEl, hostModel.getModel('labelLine')));
- }
- } else {
- // TODO Other drag functions?
- label.off('drag');
- label.cursor = defaultLabelAttr.cursor;
+ if (itemSymbolKeepAspect != null) {
+ data.setItemVisual(idx, 'symbolKeepAspect', itemSymbolKeepAspect);
}
}
- };
-
- LabelManager.prototype.layout = function (api) {
- var width = api.getWidth();
- var height = api.getHeight();
- var labelList = prepareLayoutList(this._labelList);
- var labelsNeedsAdjustOnX = filter(labelList, function (item) {
- return item.layoutOption.moveOverlap === 'shiftX';
- });
- var labelsNeedsAdjustOnY = filter(labelList, function (item) {
- return item.layoutOption.moveOverlap === 'shiftY';
- });
- shiftLayoutOnX(labelsNeedsAdjustOnX, 0, width);
- shiftLayoutOnY(labelsNeedsAdjustOnY, 0, height);
- var labelsNeedsHideOverlap = filter(labelList, function (item) {
- return item.layoutOption.hideOverlap;
- });
- hideOverlap(labelsNeedsHideOverlap);
- };
- /**
- * Process all labels. Not only labels with layoutOption.
- */
-
-
- LabelManager.prototype.processLabelsOverall = function () {
- var _this = this;
-
- each(this._chartViewList, function (chartView) {
- var seriesModel = chartView.__model;
- var ignoreLabelLineUpdate = chartView.ignoreLabelLineUpdate;
- var animationEnabled = seriesModel.isAnimationEnabled();
- chartView.group.traverse(function (child) {
- if (child.ignore) {
- return true; // Stop traverse descendants.
- }
-
- var needsUpdateLabelLine = !ignoreLabelLineUpdate;
- var label = child.getTextContent();
- if (!needsUpdateLabelLine && label) {
- needsUpdateLabelLine = labelLayoutInnerStore(label).needsUpdateLabelLine;
- }
-
- if (needsUpdateLabelLine) {
- _this._updateLabelLine(child, seriesModel);
- }
-
- if (animationEnabled) {
- _this._animateLabels(child, seriesModel);
- }
- });
- });
- };
-
- LabelManager.prototype._updateLabelLine = function (el, seriesModel) {
- // Only support label being hosted on graphic elements.
- var textEl = el.getTextContent(); // Update label line style.
-
- var ecData = getECData(el);
- 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'); // Default to be same with main color
-
- defaultStyle.stroke = visualStyle[visualType];
- var labelLineModel = itemModel.getModel('labelLine');
- setLabelLineStyle(el, getLabelLineStatesModels(itemModel), defaultStyle);
- updateLabelLinePoints(el, labelLineModel);
- }
- };
-
- LabelManager.prototype._animateLabels = function (el, seriesModel) {
- var textEl = el.getTextContent();
- var guideLine = el.getTextGuideLine(); // Animate
-
- if (textEl && !textEl.ignore && !textEl.invisible && !el.disableLabelAnimation && !isElementRemoved(el)) {
- var layoutStore = labelLayoutInnerStore(textEl);
- var oldLayout = layoutStore.oldLayout;
- var ecData = getECData(el);
- var dataIndex = ecData.dataIndex;
- var newProps = {
- x: textEl.x,
- y: textEl.y,
- rotation: textEl.rotation
- };
- var data = seriesModel.getData(ecData.dataType);
-
- if (!oldLayout) {
- textEl.attr(newProps); // Disable fade in animation if value animation is enabled.
-
- if (!labelInner(textEl).valueAnimation) {
- var oldOpacity = retrieve2(textEl.style.opacity, 1); // Fade in animation
+ return {
+ dataEach: data.hasItemOption ? dataEach : null
+ };
+ }
+ };
- textEl.style.opacity = 0;
- initProps(textEl, {
- style: {
- opacity: oldOpacity
- }
- }, seriesModel, dataIndex);
- }
- } else {
- textEl.attr(oldLayout); // Make sure the animation from is in the right status.
+ /*
+ * 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 prevStates = el.prevStates;
- if (prevStates) {
- if (indexOf(prevStates, 'select') >= 0) {
- textEl.attr(layoutStore.oldLayoutSelect);
- }
+ /**
+ * AUTO-GENERATED FILE. DO NOT MODIFY.
+ */
- if (indexOf(prevStates, 'emphasis') >= 0) {
- textEl.attr(layoutStore.oldLayoutEmphasis);
- }
- }
+ /*
+ * 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':
+ var style = data.getItemVisual(dataIndex, 'style');
+ return style[data.getVisual('drawType')];
- updateProps(textEl, newProps, seriesModel, dataIndex);
- }
+ case 'opacity':
+ return data.getItemVisual(dataIndex, 'style').opacity;
- layoutStore.oldLayout = newProps;
+ case 'symbol':
+ case 'symbolSize':
+ case 'liftZ':
+ return data.getItemVisual(dataIndex, key);
- if (textEl.states.select) {
- var layoutSelect = layoutStore.oldLayoutSelect = {};
- extendWithKeys(layoutSelect, newProps, LABEL_LAYOUT_PROPS);
- extendWithKeys(layoutSelect, textEl.states.select, LABEL_LAYOUT_PROPS);
+ default:
+ if ("development" !== 'production') {
+ console.warn("Unknown visual type " + key);
}
- if (textEl.states.emphasis) {
- var layoutEmphasis = layoutStore.oldLayoutEmphasis = {};
- extendWithKeys(layoutEmphasis, newProps, LABEL_LAYOUT_PROPS);
- extendWithKeys(layoutEmphasis, textEl.states.emphasis, LABEL_LAYOUT_PROPS);
- }
+ }
+ }
+ function getVisualFromData(data, key) {
+ switch (key) {
+ case 'color':
+ var style = data.getVisual('style');
+ return style[data.getVisual('drawType')];
- animateLabelValue(textEl, dataIndex, data, seriesModel, seriesModel);
- }
+ case 'opacity':
+ return data.getVisual('style').opacity;
- if (guideLine && !guideLine.ignore && !guideLine.invisible) {
- var layoutStore = labelLineAnimationStore(guideLine);
- var oldLayout = layoutStore.oldLayout;
- var newLayout = {
- points: guideLine.shape.points
- };
+ case 'symbol':
+ case 'symbolSize':
+ case 'liftZ':
+ return data.getVisual(key);
- if (!oldLayout) {
- guideLine.setShape(newLayout);
- guideLine.style.strokePercent = 0;
- initProps(guideLine, {
- style: {
- strokePercent: 1
- }
- }, seriesModel);
- } else {
- guideLine.attr({
- shape: oldLayout
- });
- updateProps(guideLine, {
- shape: newLayout
- }, seriesModel);
+ default:
+ if ("development" !== 'production') {
+ console.warn("Unknown visual type " + key);
}
- layoutStore.oldLayout = newLayout;
- }
- };
-
- return LabelManager;
- }();
+ }
+ }
// Inlucdes: pieSelect, pieUnSelect, pieToggleSelect, mapSelect, mapUnSelect, mapToggleSelect
@@ -25969,6 +25975,7 @@
}
var wmUniqueIndex = Math.round(Math.random() * 9);
+ var supportDefineProperty = typeof Object.defineProperty === 'function';
var WeakMap = (function () {
function WeakMap() {
this._id = '__ec_inner_' + wmUniqueIndex++;
@@ -25978,7 +25985,7 @@
};
WeakMap.prototype.set = function (key, value) {
var target = this._guard(key);
- if (typeof Object.defineProperty === 'function') {
+ if (supportDefineProperty) {
Object.defineProperty(target, this._id, {
value: value,
enumerable: false,
@@ -26298,6 +26305,24 @@
return symbolPath;
}
+ function normalizeSymbolSize(symbolSize) {
+ if (!isArray(symbolSize)) {
+ symbolSize = [+symbolSize, +symbolSize];
+ }
+
+ return [symbolSize[0] || 0, symbolSize[1] || 0];
+ }
+ function normalizeSymbolOffset(symbolOffset, symbolSize) {
+ if (symbolOffset == null) {
+ return;
+ }
+
+ if (!isArray(symbolOffset)) {
+ symbolOffset = [symbolOffset, symbolOffset];
+ }
+
+ return [parsePercent$1(symbolOffset[0], symbolSize[0]) || 0, parsePercent$1(retrieve2(symbolOffset[1], symbolOffset[0]), symbolSize[1]) || 0];
+ }
function createLinearGradient(ctx, obj, rect) {
var x = obj.x == null ? 0 : obj.x;
@@ -26375,6 +26400,9 @@
var stroke = style.stroke;
return !(stroke == null || stroke === 'none' || !(style.lineWidth > 0));
}
+ function isValidStrokeFillStyle(strokeOrFill) {
+ return typeof strokeOrFill === 'string' && strokeOrFill !== 'none';
+ }
function styleHasFill(style) {
var fill = style.fill;
return fill != null && fill !== 'none';
@@ -26692,14 +26720,14 @@
flushPathDrawn(ctx, scope);
styleChanged = true;
}
- ctx.fillStyle = style.fill;
+ isValidStrokeFillStyle(style.fill) && (ctx.fillStyle = style.fill);
}
if (forceSetAll || style.stroke !== prevStyle.stroke) {
if (!styleChanged) {
flushPathDrawn(ctx, scope);
styleChanged = true;
}
- ctx.strokeStyle = style.stroke;
+ isValidStrokeFillStyle(style.stroke) && (ctx.strokeStyle = style.stroke);
}
if (forceSetAll || style.opacity !== prevStyle.opacity) {
if (!styleChanged) {
@@ -29070,15 +29098,12 @@
}
};
- var assert$1 = assert;
- var each$3 = each;
- var isFunction$1 = isFunction;
- var isObject$2 = isObject;
- var indexOf$1 = indexOf;
+ var lifecycle = new Eventful();
+
var hasWindow = typeof window !== 'undefined';
- var version$1 = '5.1.2';
+ var version$1 = '5.2.0';
var dependencies = {
- zrender: '5.1.1'
+ zrender: '5.2.0'
};
var TEST_FRAME_REMAIN_TIME = 1;
var PRIORITY_PROCESSOR_SERIES_FILTER = 800; // Some data processors depends on the stack result dimension (to calculate data extent).
@@ -29130,7 +29155,7 @@
// 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 PENDING_UPDATE = '__pendingUpdate';
var STATUS_NEEDS_UPDATE_KEY = '__needsUpdateStatus';
var ACTION_REG = /^[a-zA-Z0-9_]+$/;
var CONNECT_STATUS_KEY = '__connectUpdateStatus';
@@ -29206,10 +29231,8 @@
var render;
var renderComponents;
var renderSeries;
- var performPostUpdateFuncs;
var createExtensionAPI;
var enableConnect;
- var setTransitionOpt;
var markStatusToUpdate;
var applyChangedStates;
@@ -29270,8 +29293,7 @@
sort(visualFuncs, prioritySortFunc);
sort(dataProcessorFuncs, prioritySortFunc);
_this._scheduler = new Scheduler(_this, api, dataProcessorFuncs, visualFuncs);
- _this._messageCenter = new MessageCenter();
- _this._labelManager = new LabelManager(); // Init mouse events
+ _this._messageCenter = new MessageCenter(); // Init mouse events
_this._initEvents(); // In case some people write `window.onresize = chart.resize`
@@ -29293,11 +29315,11 @@
applyChangedStates(this);
var scheduler = this._scheduler; // Lazy update
- if (this[OPTION_UPDATED_KEY]) {
- var silent = this[OPTION_UPDATED_KEY].silent;
+ if (this[PENDING_UPDATE]) {
+ var silent = this[PENDING_UPDATE].silent;
this[IN_MAIN_PROCESS_KEY] = true;
prepare(this);
- updateMethods.update.call(this); // At present, in each frame, zrender performs:
+ updateMethods.update.call(this, null, this[PENDING_UPDATE].updateParams); // At present, in each frame, zrender performs:
// (1) animation step forward.
// (2) trigger('frame') (where this `_onframe` is called)
// (3) zrender flush (render).
@@ -29307,7 +29329,7 @@
this._zr.flush();
this[IN_MAIN_PROCESS_KEY] = false;
- this[OPTION_UPDATED_KEY] = false;
+ this[PENDING_UPDATE] = null;
flushPendingActions.call(this, silent);
triggerUpdatedEvent.call(this, silent);
} // Avoid do both lazy update and progress in one frame.
@@ -29331,7 +29353,7 @@
// console.log('--- ec frame visual ---', remainTime);
scheduler.performVisualTasks(ecModel);
- renderSeries(this, this._model, api, 'remain');
+ renderSeries(this, this._model, api, 'remain', {});
remainTime -= +new Date() - startTime;
} while (remainTime > 0 && scheduler.unfinished); // Call flush explicitly for trigger finished event.
@@ -29360,7 +29382,7 @@
ECharts.prototype.setOption = function (option, notMerge, lazyUpdate) {
if ("development" !== 'production') {
- assert$1(!this[IN_MAIN_PROCESS_KEY], '`setOption` should not be called during main process.');
+ assert(!this[IN_MAIN_PROCESS_KEY], '`setOption` should not be called during main process.');
}
if (this._disposed) {
@@ -29372,7 +29394,7 @@
var replaceMerge;
var transitionOpt;
- if (isObject$2(notMerge)) {
+ if (isObject(notMerge)) {
lazyUpdate = notMerge.lazyUpdate;
silent = notMerge.silent;
replaceMerge = notMerge.replaceMerge;
@@ -29394,11 +29416,15 @@
replaceMerge: replaceMerge
}, optionPreprocessorFuncs);
- setTransitionOpt(this, transitionOpt);
+ var updateParams = {
+ seriesTransition: transitionOpt,
+ optionChanged: true
+ };
if (lazyUpdate) {
- this[OPTION_UPDATED_KEY] = {
- silent: silent
+ this[PENDING_UPDATE] = {
+ silent: silent,
+ updateParams: updateParams
};
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.
@@ -29406,12 +29432,12 @@
this.getZr().wakeUp();
} else {
prepare(this);
- updateMethods.update.call(this); // Ensure zr refresh sychronously, and then pixel in canvas can be
+ updateMethods.update.call(this, null, updateParams); // Ensure zr refresh sychronously, and then pixel in canvas can be
// fetched after `setOption`.
this._zr.flush();
- this[OPTION_UPDATED_KEY] = false;
+ this[PENDING_UPDATE] = null;
this[IN_MAIN_PROCESS_KEY] = false;
flushPendingActions.call(this, silent);
triggerUpdatedEvent.call(this, silent);
@@ -29494,7 +29520,7 @@
var ecModel = this._model;
var excludesComponentViews = [];
var self = this;
- each$3(excludeComponents, function (componentType) {
+ each(excludeComponents, function (componentType) {
ecModel.eachComponent({
mainType: componentType
}, function (component) {
@@ -29507,7 +29533,7 @@
});
});
var url = this._zr.painter.getType() === 'svg' ? this.getSvgDataURL() : this.getRenderedCanvas(opts).toDataURL('image/' + (opts && opts.type || 'png'));
- each$3(excludesComponentViews, function (view) {
+ each(excludesComponentViews, function (view) {
view.group.ignore = false;
});
return url;
@@ -29568,7 +29594,7 @@
if (isSvg) {
var content_1 = '';
- each$3(canvasList_1, function (item) {
+ each(canvasList_1, function (item) {
var x = item.left - left_1;
var y = item.top - top_1;
content_1 += '<g transform="translate(' + x + ',' + y + ')">' + item.dom + '</g>';
@@ -29597,7 +29623,7 @@
}));
}
- each$3(canvasList_1, function (item) {
+ each(canvasList_1, function (item) {
var img = new ZRImage({
style: {
x: item.left * dpr_1 - left_1,
@@ -29717,7 +29743,7 @@
ECharts.prototype._initEvents = function () {
var _this = this;
- each$3(MOUSE_EVENT_NAMES, function (eveName) {
+ each(MOUSE_EVENT_NAMES, function (eveName) {
var handler = function (e) {
var ecModel = _this.getModel();
@@ -29797,14 +29823,14 @@
_this._zr.on(eveName, handler, _this);
});
- each$3(eventActionMap, function (actionType, eventType) {
+ each(eventActionMap, function (actionType, eventType) {
_this._messageCenter.on(eventType, function (event) {
this.trigger(eventType, event);
}, _this);
}); // Extra events
// TODO register?
- each$3(['selectchanged'], function (eventType) {
+ each(['selectchanged'], function (eventType) {
_this._messageCenter.on(eventType, function (event) {
this.trigger(eventType, event);
}, _this);
@@ -29835,18 +29861,22 @@
this._disposed = true;
setAttribute(this.getDom(), DOM_ATTRIBUTE_KEY, '');
- var api = this._api;
- var ecModel = this._model;
- each$3(this._componentsViews, function (component) {
+ var chart = this;
+ var api = chart._api;
+ var ecModel = chart._model;
+ each(chart._componentsViews, function (component) {
component.dispose(ecModel, api);
});
- each$3(this._chartsViews, function (chart) {
+ each(chart._chartsViews, function (chart) {
chart.dispose(ecModel, api);
}); // Dispose after all views disposed
- this._zr.dispose();
+ chart._zr.dispose(); // Set properties to null.
+ // To reduce the memory cost in case the top code still holds this instance unexpectedly.
- delete instances$1[this.id];
+
+ chart._dom = chart._model = chart._chartsMap = chart._componentsMap = chart._chartsViews = chart._componentsViews = chart._scheduler = chart._api = chart._zr = chart._throttledZrFlush = chart._theme = chart._coordSysMgr = chart._messageCenter = null;
+ delete instances$1[chart.id];
};
/**
* Resize the chart
@@ -29855,7 +29885,7 @@
ECharts.prototype.resize = function (opts) {
if ("development" !== 'production') {
- assert$1(!this[IN_MAIN_PROCESS_KEY], '`resize` should not be called during main process.');
+ assert(!this[IN_MAIN_PROCESS_KEY], '`resize` should not be called during main process.');
}
if (this._disposed) {
@@ -29878,13 +29908,13 @@
// chart.setOption(option, { lazyUpdate: true });
// chart.resize();
- if (this[OPTION_UPDATED_KEY]) {
+ if (this[PENDING_UPDATE]) {
if (silent == null) {
- silent = this[OPTION_UPDATED_KEY].silent;
+ silent = this[PENDING_UPDATE].silent;
}
needPrepare = true;
- this[OPTION_UPDATED_KEY] = false;
+ this[PENDING_UPDATE] = null;
}
this[IN_MAIN_PROCESS_KEY] = true;
@@ -29907,7 +29937,7 @@
return;
}
- if (isObject$2(name)) {
+ if (isObject(name)) {
cfg = name;
name = '';
}
@@ -29965,7 +29995,7 @@
return;
}
- if (!isObject$2(opt)) {
+ if (!isObject(opt)) {
opt = {
silent: !!opt
};
@@ -30007,10 +30037,11 @@
};
ECharts.prototype.updateLabelLayout = function () {
- var labelManager = this._labelManager;
- labelManager.updateLayoutConfig(this._api);
- labelManager.layout(this._api);
- labelManager.processLabelsOverall();
+ lifecycle.trigger('series:layoutlabels', this._model, this._api, {
+ // Not adding series labels.
+ // TODO
+ updatedSeries: []
+ });
};
ECharts.prototype.appendData = function (params) {
@@ -30024,7 +30055,7 @@
var seriesModel = ecModel.getSeriesByIndex(seriesIndex);
if ("development" !== 'production') {
- assert$1(params.data && seriesModel);
+ assert(params.data && seriesModel);
}
seriesModel.appendData(params); // Note: `appendData` does not support that update extent of coordinate
@@ -30095,7 +30126,7 @@
ChartView.getClass(classType.sub);
if ("development" !== 'production') {
- assert$1(Clazz, classType.sub + ' does not exist.');
+ assert(Clazz, classType.sub + ' does not exist.');
}
view = new Clazz();
@@ -30143,7 +30174,7 @@
// 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);
+ each([].concat(ecIns._componentsViews).concat(ecIns._chartsViews), callView);
return;
}
@@ -30162,7 +30193,7 @@
if (excludeSeriesId != null) {
excludeSeriesIdMap = createHashMap();
- each$3(normalizeToArray(excludeSeriesId), function (id) {
+ each(normalizeToArray(excludeSeriesId), function (id) {
var modelId = convertOptionIdName(id, null);
if (modelId != null) {
@@ -30177,42 +30208,52 @@
ecModel && ecModel.eachComponent(condition, function (model) {
- if (!excludeSeriesIdMap || excludeSeriesIdMap.get(model.id) == null) {
- if (isHighDownPayload(payload)) {
- if (model instanceof SeriesModel) {
- if (payload.type === HIGHLIGHT_ACTION_TYPE && !payload.notBlur) {
- blurSeriesFromHighlightPayload(model, payload, ecIns._api);
- }
- } else {
- var _a = findComponentHighDownDispatchers(model.mainType, model.componentIndex, payload.name, ecIns._api),
- focusSelf = _a.focusSelf,
- dispatchers = _a.dispatchers;
-
- if (payload.type === HIGHLIGHT_ACTION_TYPE && focusSelf && !payload.notBlur) {
- blurComponent(model.mainType, model.componentIndex, ecIns._api);
- } // PENDING:
- // Whether to put this "enter emphasis" code in `ComponentView`,
- // which will be the same as `ChartView` but might be not necessary
- // and will be far from this logic.
+ var isExcluded = excludeSeriesIdMap && excludeSeriesIdMap.get(model.id) !== null;
+ if (isExcluded) {
+ return;
+ }
- if (dispatchers) {
- each$3(dispatchers, function (dispatcher) {
- payload.type === HIGHLIGHT_ACTION_TYPE ? enterEmphasis(dispatcher) : leaveEmphasis(dispatcher);
- });
- }
+ if (isHighDownPayload(payload)) {
+ if (model instanceof SeriesModel) {
+ if (payload.type === HIGHLIGHT_ACTION_TYPE && !payload.notBlur) {
+ blurSeriesFromHighlightPayload(model, payload, ecIns._api);
}
- } else if (isSelectChangePayload(payload)) {
- // TODO geo
- if (model instanceof SeriesModel) {
- toggleSelectionFromPayload(model, payload, ecIns._api);
- updateSeriesElementSelection(model);
- markStatusToUpdate(ecIns);
+ } else {
+ var _a = findComponentHighDownDispatchers(model.mainType, model.componentIndex, payload.name, ecIns._api),
+ focusSelf = _a.focusSelf,
+ dispatchers = _a.dispatchers;
+
+ if (payload.type === HIGHLIGHT_ACTION_TYPE && focusSelf && !payload.notBlur) {
+ blurComponent(model.mainType, model.componentIndex, ecIns._api);
+ } // PENDING:
... 108430 lines suppressed ...
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org