;
+
+ var markupFragment;
+
+ if (isObject(result)) {
+ if (result.type) {
+ markupFragment = result;
+ } else {
+ if ("development" !== 'production') {
+ console.warn('The return type of `formatTooltip` is not supported: ' + makePrintable(result));
+ }
+ } // else {
+ // markupText = (result as TooltipFormatResultLegacyObject).html;
+ // markers = (result as TooltipFormatResultLegacyObject).markers;
+ // if (markersExisting) {
+ // markers = zrUtil.merge(markersExisting, markers);
+ // }
+ // }
+
+ } else {
+ markupText = result;
+ }
+
+ return {
+ markupText: markupText,
+ // markers: markers || markersExisting,
+ markupFragment: markupFragment
+ };
+ }
+
+ /**
+ * @param {Object} define
+ * @return See the return of `createTask`.
+ */
+
+ function createTask(define) {
+ return new Task(define);
+ }
+
+ var Task =
+ /** @class */
+ function () {
+ function Task(define) {
+ define = define || {};
+ this._reset = define.reset;
+ this._plan = define.plan;
+ this._count = define.count;
+ this._onDirty = define.onDirty;
+ this._dirty = true;
+ }
+ /**
+ * @param step Specified step.
+ * @param skip Skip customer perform call.
+ * @param modBy Sampling window size.
+ * @param modDataCount Sampling count.
+ * @return whether unfinished.
+ */
+
+
+ Task.prototype.perform = function (performArgs) {
+ var upTask = this._upstream;
+ var skip = performArgs && performArgs.skip; // TODO some refactor.
+ // Pull data. Must pull data each time, because context.data
+ // may be updated by Series.setData.
+
+ if (this._dirty && upTask) {
+ var context = this.context;
+ context.data = context.outputData = upTask.context.outputData;
+ }
+
+ if (this.__pipeline) {
+ this.__pipeline.currentTask = this;
+ }
+
+ var planResult;
+
+ if (this._plan && !skip) {
+ planResult = this._plan(this.context);
+ } // Support sharding by mod, which changes the render sequence and makes the rendered graphic
+ // elements uniformed distributed when progress, especially when moving or zooming.
+
+
+ var lastModBy = normalizeModBy(this._modBy);
+ var lastModDataCount = this._modDataCount || 0;
+ var modBy = normalizeModBy(performArgs && performArgs.modBy);
+ var modDataCount = performArgs && performArgs.modDataCount || 0;
+
+ if (lastModBy !== modBy || lastModDataCount !== modDataCount) {
+ planResult = 'reset';
+ }
+
+ function normalizeModBy(val) {
+ !(val >= 1) && (val = 1); // jshint ignore:line
+
+ return val;
+ }
+
+ var forceFirstProgress;
+
+ if (this._dirty || planResult === 'reset') {
+ this._dirty = false;
+ forceFirstProgress = this._doReset(skip);
+ }
+
+ this._modBy = modBy;
+ this._modDataCount = modDataCount;
+ var step = performArgs && performArgs.step;
+
+ if (upTask) {
+ if ("development" !== 'production') {
+ assert(upTask._outputDueEnd != null);
+ }
+
+ this._dueEnd = upTask._outputDueEnd;
+ } // DataTask or overallTask
+ else {
+ if ("development" !== 'production') {
+ assert(!this._progress || this._count);
+ }
+
+ this._dueEnd = this._count ? this._count(this.context) : Infinity;
+ } // Note: Stubs, that its host overall task let it has progress, has progress.
+ // If no progress, pass index from upstream to downstream each time plan called.
+
+
+ if (this._progress) {
+ var start = this._dueIndex;
+ var end = Math.min(step != null ? this._dueIndex + step : Infinity, this._dueEnd);
+
+ if (!skip && (forceFirstProgress || start < end)) {
+ var progress = this._progress;
+
+ if (isArray(progress)) {
+ for (var i = 0; i < progress.length; i++) {
+ this._doProgress(progress[i], start, end, modBy, modDataCount);
+ }
+ } else {
+ this._doProgress(progress, start, end, modBy, modDataCount);
+ }
+ }
+
+ this._dueIndex = end; // If no `outputDueEnd`, assume that output data and
+ // input data is the same, so use `dueIndex` as `outputDueEnd`.
+
+ var outputDueEnd = this._settedOutputEnd != null ? this._settedOutputEnd : end;
+
+ if ("development" !== 'production') {
+ // ??? Can not rollback.
+ assert(outputDueEnd >= this._outputDueEnd);
+ }
+
+ this._outputDueEnd = outputDueEnd;
+ } else {
+ // (1) Some overall task has no progress.
+ // (2) Stubs, that its host overall task do not let it has progress, has no progress.
+ // This should always be performed so it can be passed to downstream.
+ this._dueIndex = this._outputDueEnd = this._settedOutputEnd != null ? this._settedOutputEnd : this._dueEnd;
+ }
+
+ return this.unfinished();
+ };
+
+ Task.prototype.dirty = function () {
+ this._dirty = true;
+ this._onDirty && this._onDirty(this.context);
+ };
+
+ Task.prototype._doProgress = function (progress, start, end, modBy, modDataCount) {
+ iterator.reset(start, end, modBy, modDataCount);
+ this._callingProgress = progress;
+
+ this._callingProgress({
+ start: start,
+ end: end,
+ count: end - start,
+ next: iterator.next
+ }, this.context);
+ };
+
+ Task.prototype._doReset = function (skip) {
+ this._dueIndex = this._outputDueEnd = this._dueEnd = 0;
+ this._settedOutputEnd = null;
+ var progress;
+ var forceFirstProgress;
+
+ if (!skip && this._reset) {
+ progress = this._reset(this.context);
+
+ if (progress && progress.progress) {
+ forceFirstProgress = progress.forceFirstProgress;
+ progress = progress.progress;
+ } // To simplify no progress checking, array must has item.
+
+
+ if (isArray(progress) && !progress.length) {
+ progress = null;
+ }
+ }
+
+ this._progress = progress;
+ this._modBy = this._modDataCount = null;
+ var downstream = this._downstream;
+ downstream && downstream.dirty();
+ return forceFirstProgress;
+ };
+
+ Task.prototype.unfinished = function () {
+ return this._progress && this._dueIndex < this._dueEnd;
+ };
+ /**
+ * @param downTask The downstream task.
+ * @return The downstream task.
+ */
+
+
+ Task.prototype.pipe = function (downTask) {
+ if ("development" !== 'production') {
+ assert(downTask && !downTask._disposed && downTask !== this);
+ } // If already downstream, do not dirty downTask.
+
+
+ if (this._downstream !== downTask || this._dirty) {
+ this._downstream = downTask;
+ downTask._upstream = this;
+ downTask.dirty();
+ }
+ };
+
+ Task.prototype.dispose = function () {
+ if (this._disposed) {
+ return;
+ }
+
+ this._upstream && (this._upstream._downstream = null);
+ this._downstream && (this._downstream._upstream = null);
+ this._dirty = false;
+ this._disposed = true;
+ };
+
+ Task.prototype.getUpstream = function () {
+ return this._upstream;
+ };
+
+ Task.prototype.getDownstream = function () {
+ return this._downstream;
+ };
+
+ Task.prototype.setOutputEnd = function (end) {
+ // This only happend in dataTask, dataZoom, map, currently.
+ // where dataZoom do not set end each time, but only set
+ // when reset. So we should record the setted end, in case
+ // that the stub of dataZoom perform again and earse the
+ // setted end by upstream.
+ this._outputDueEnd = this._settedOutputEnd = end;
+ };
+
+ return Task;
+ }();
+
+ var iterator = function () {
+ var end;
+ var current;
+ var modBy;
+ var modDataCount;
+ var winCount;
+ var it = {
+ reset: function (s, e, sStep, sCount) {
+ current = s;
+ end = e;
+ modBy = sStep;
+ modDataCount = sCount;
+ winCount = Math.ceil(modDataCount / modBy);
+ it.next = modBy > 1 && modDataCount > 0 ? modNext : sequentialNext;
+ }
+ };
+ return it;
+
+ function sequentialNext() {
+ return current < end ? current++ : null;
+ }
+
+ function modNext() {
+ var dataIndex = current % winCount * modBy + Math.ceil(current / winCount);
+ var result = current >= end ? null : dataIndex < modDataCount ? dataIndex // If modDataCount is smaller than data.count() (consider `appendData` case),
+ // Use normal linear rendering mode.
+ : current;
+ current++;
+ return result;
+ }
+ }(); ///////////////////////////////////////////////////////////
+ // For stream debug (Should be commented out after used!)
+ // @usage: printTask(this, 'begin');
+ // @usage: printTask(this, null, {someExtraProp});
+ // @usage: Use `__idxInPipeline` as conditional breakpiont.
+ //
+ // window.printTask = function (task: any, prefix: string, extra: { [key: string]: unknown }): void {
+ // window.ecTaskUID == null && (window.ecTaskUID = 0);
+ // task.uidDebug == null && (task.uidDebug = `task_${window.ecTaskUID++}`);
+ // task.agent && task.agent.uidDebug == null && (task.agent.uidDebug = `task_${window.ecTaskUID++}`);
+ // let props = [];
+ // if (task.__pipeline) {
+ // let val = `${task.__idxInPipeline}/${task.__pipeline.tail.__idxInPipeline} ${task.agent ? '(stub)' : ''}`;
+ // props.push({text: '__idxInPipeline/total', value: val});
+ // } else {
+ // let stubCount = 0;
+ // task.agentStubMap.each(() => stubCount++);
+ // props.push({text: 'idx', value: `overall (stubs: ${stubCount})`});
+ // }
+ // props.push({text: 'uid', value: task.uidDebug});
+ // if (task.__pipeline) {
+ // props.push({text: 'pipelineId', value: task.__pipeline.id});
+ // task.agent && props.push(
+ // {text: 'stubFor', value: task.agent.uidDebug}
+ // );
+ // }
+ // props.push(
+ // {text: 'dirty', value: task._dirty},
+ // {text: 'dueIndex', value: task._dueIndex},
+ // {text: 'dueEnd', value: task._dueEnd},
+ // {text: 'outputDueEnd', value: task._outputDueEnd}
+ // );
+ // if (extra) {
+ // Object.keys(extra).forEach(key => {
+ // props.push({text: key, value: extra[key]});
+ // });
+ // }
+ // let args = ['color: blue'];
+ // let msg = `%c[${prefix || 'T'}] %c` + props.map(item => (
+ // args.push('color: green', 'color: red'),
+ // `${item.text}: %c${item.value}`
+ // )).join('%c, ');
+ // console.log.apply(console, [msg].concat(args));
+ // // console.log(this);
+ // };
+ // window.printPipeline = function (task: any, prefix: string) {
+ // const pipeline = task.__pipeline;
+ // let currTask = pipeline.head;
+ // while (currTask) {
+ // window.printTask(currTask, prefix);
+ // currTask = currTask._downstream;
+ // }
+ // };
+ // window.showChain = function (chainHeadTask) {
+ // var chain = [];
+ // var task = chainHeadTask;
+ // while (task) {
+ // chain.push({
+ // task: task,
+ // up: task._upstream,
+ // down: task._downstream,
+ // idxInPipeline: task.__idxInPipeline
+ // });
+ // task = task._downstream;
+ // }
+ // return chain;
+ // };
+ // window.findTaskInChain = function (task, chainHeadTask) {
+ // let chain = window.showChain(chainHeadTask);
+ // let result = [];
+ // for (let i = 0; i < chain.length; i++) {
+ // let chainItem = chain[i];
+ // if (chainItem.task === task) {
+ // result.push(i);
+ // }
+ // }
+ // return result;
+ // };
+ // window.printChainAEachInChainB = function (chainHeadTaskA, chainHeadTaskB) {
+ // let chainA = window.showChain(chainHeadTaskA);
+ // for (let i = 0; i < chainA.length; i++) {
+ // console.log('chainAIdx:', i, 'inChainB:', window.findTaskInChain(chainA[i].task, chainHeadTaskB));
+ // }
+ // };
+
+ /**
+ * Convert raw the value in to inner value in List.
+ *
+ * [Performance sensitive]
+ *
+ * [Caution]: this is the key logic of user value parser.
+ * For backward compatibiliy, do not modify it until have to!
+ */
+
+ function parseDataValue(value, // For high performance, do not omit the second param.
+ opt) {
+ // Performance sensitive.
+ var dimType = opt && opt.type;
+
+ if (dimType === 'ordinal') {
+ // If given value is a category string
+ return value;
+ }
+
+ if (dimType === 'time' // spead up when using timestamp
+ && typeof value !== 'number' && value != null && value !== '-') {
+ value = +parseDate(value);
+ } // dimType defaults 'number'.
+ // If dimType is not ordinal and value is null or undefined or NaN or '-',
+ // parse to NaN.
+ // number-like string (like ' 123 ') can be converted to a number.
+ // where null/undefined or other string will be converted to NaN.
+
+
+ return value == null || value === '' ? NaN // If string (like '-'), using '+' parse to NaN
+ // If object, also parse to NaN
+ : +value;
+ }
+ var valueParserMap = createHashMap({
+ 'number': function (val) {
+ // Do not use `numericToNumber` here. We have by defualt `numericToNumber`.
+ // Here the number parser can have loose rule:
+ // enable to cut suffix: "120px" => 120, "14%" => 14.
+ return parseFloat(val);
+ },
+ 'time': function (val) {
+ // return timestamp.
+ return +parseDate(val);
+ },
+ 'trim': function (val) {
+ return typeof val === 'string' ? trim(val) : val;
+ }
+ });
+ function getRawValueParser(type) {
+ return valueParserMap.get(type);
+ }
+ var ORDER_COMPARISON_OP_MAP = {
+ lt: function (lval, rval) {
+ return lval < rval;
+ },
+ lte: function (lval, rval) {
+ return lval <= rval;
+ },
+ gt: function (lval, rval) {
+ return lval > rval;
+ },
+ gte: function (lval, rval) {
+ return lval >= rval;
+ }
+ };
+
+ var FilterOrderComparator =
+ /** @class */
+ function () {
+ function FilterOrderComparator(op, rval) {
+ if (typeof rval !== 'number') {
+ var errMsg = '';
+
+ if ("development" !== 'production') {
+ errMsg = 'rvalue of "<", ">", "<=", ">=" can only be number in filter.';
+ }
+
+ throwError(errMsg);
+ }
+
+ this._opFn = ORDER_COMPARISON_OP_MAP[op];
+ this._rvalFloat = numericToNumber(rval);
+ } // Performance sensitive.
+
+
+ FilterOrderComparator.prototype.evaluate = function (lval) {
+ // Most cases is 'number', and typeof maybe 10 times faseter than parseFloat.
+ return typeof lval === 'number' ? this._opFn(lval, this._rvalFloat) : this._opFn(numericToNumber(lval), this._rvalFloat);
+ };
+
+ return FilterOrderComparator;
+ }();
+
+ var SortOrderComparator =
+ /** @class */
+ function () {
+ /**
+ * @param order by defualt: 'asc'
+ * @param incomparable by defualt: Always on the tail.
+ * That is, if 'asc' => 'max', if 'desc' => 'min'
+ * See the definition of "incomparable" in [SORT_COMPARISON_RULE]
+ */
+ function SortOrderComparator(order, incomparable) {
+ var isDesc = order === 'desc';
+ this._resultLT = isDesc ? 1 : -1;
+
+ if (incomparable == null) {
+ incomparable = isDesc ? 'min' : 'max';
+ }
+
+ this._incomparable = incomparable === 'min' ? -Infinity : Infinity;
+ } // See [SORT_COMPARISON_RULE].
+ // Performance sensitive.
+
+
+ SortOrderComparator.prototype.evaluate = function (lval, rval) {
+ // Most cases is 'number', and typeof maybe 10 times faseter than parseFloat.
+ var lvalTypeof = typeof lval;
+ var rvalTypeof = typeof rval;
+ var lvalFloat = lvalTypeof === 'number' ? lval : numericToNumber(lval);
+ var rvalFloat = rvalTypeof === 'number' ? rval : numericToNumber(rval);
+ var lvalNotNumeric = isNaN(lvalFloat);
+ var rvalNotNumeric = isNaN(rvalFloat);
+
+ if (lvalNotNumeric) {
+ lvalFloat = this._incomparable;
+ }
+
+ if (rvalNotNumeric) {
+ rvalFloat = this._incomparable;
+ }
+
+ if (lvalNotNumeric && rvalNotNumeric) {
+ var lvalIsStr = lvalTypeof === 'string';
+ var rvalIsStr = rvalTypeof === 'string';
+
+ if (lvalIsStr) {
+ lvalFloat = rvalIsStr ? lval : 0;
+ }
+
+ if (rvalIsStr) {
+ rvalFloat = lvalIsStr ? rval : 0;
+ }
+ }
+
+ return lvalFloat < rvalFloat ? this._resultLT : lvalFloat > rvalFloat ? -this._resultLT : 0;
+ };
+
+ return SortOrderComparator;
+ }();
+
+ var FilterEqualityComparator =
+ /** @class */
+ function () {
+ function FilterEqualityComparator(isEq, rval) {
+ this._rval = rval;
+ this._isEQ = isEq;
+ this._rvalTypeof = typeof rval;
+ this._rvalFloat = numericToNumber(rval);
+ } // Performance sensitive.
+
+
+ FilterEqualityComparator.prototype.evaluate = function (lval) {
+ var eqResult = lval === this._rval;
+
+ if (!eqResult) {
+ var lvalTypeof = typeof lval;
+
+ if (lvalTypeof !== this._rvalTypeof && (lvalTypeof === 'number' || this._rvalTypeof === 'number')) {
+ eqResult = numericToNumber(lval) === this._rvalFloat;
+ }
+ }
+
+ return this._isEQ ? eqResult : !eqResult;
+ };
+
+ return FilterEqualityComparator;
+ }();
+ /**
+ * [FILTER_COMPARISON_RULE]
+ * `lt`|`lte`|`gt`|`gte`:
+ * + rval must be a number. And lval will be converted to number (`numericToNumber`) to compare.
+ * `eq`:
+ * + If same type, compare with `===`.
+ * + If there is one number, convert to number (`numericToNumber`) to compare.
+ * + Else return `false`.
+ * `ne`:
+ * + Not `eq`.
+ *
+ *
+ * [SORT_COMPARISON_RULE]
+ * All the values are grouped into three categories:
+ * + "numeric" (number and numeric string)
+ * + "non-numeric-string" (string that excluding numeric string)
+ * + "others"
+ * "numeric" vs "numeric": values are ordered by number order.
+ * "non-numeric-string" vs "non-numeric-string": values are ordered by ES spec (#sec-abstract-relational-comparison).
+ * "others" vs "others": do not change order (always return 0).
+ * "numeric" vs "non-numeric-string": "non-numeric-string" is treated as "incomparable".
+ * "number" vs "others": "others" is treated as "incomparable".
+ * "non-numeric-string" vs "others": "others" is treated as "incomparable".
+ * "incomparable" will be seen as -Infinity or Infinity (depends on the settings).
+ * MEMO:
+ * non-numeric string sort make sence when need to put the items with the same tag together.
+ * But if we support string sort, we still need to avoid the misleading like `'2' > '12'`,
+ * So we treat "numeric-string" sorted by number order rather than string comparison.
+ *
+ *
+ * [CHECK_LIST_OF_THE_RULE_DESIGN]
+ * + Do not support string comparison until required. And also need to
+ * void the misleading of "2" > "12".
+ * + Should avoid the misleading case:
+ * `" 22 " gte "22"` is `true` but `" 22 " eq "22"` is `false`.
+ * + JS bad case should be avoided: null <= 0, [] <= 0, ' ' <= 0, ...
+ * + Only "numeric" can be converted to comparable number, otherwise converted to NaN.
+ * See `util/number.ts#numericToNumber`.
+ *
+ * @return If `op` is not `RelationalOperator`, return null;
+ */
+
+
+ function createFilterComparator(op, rval) {
+ return op === 'eq' || op === 'ne' ? new FilterEqualityComparator(op === 'eq', rval) : hasOwn(ORDER_COMPARISON_OP_MAP, op) ? new FilterOrderComparator(op, rval) : null;
+ }
+
+ /**
+ * TODO: disable writable.
+ * This structure will be exposed to users.
+ */
+
+ var ExternalSource =
+ /** @class */
+ function () {
+ function ExternalSource() {}
+
+ ExternalSource.prototype.getRawData = function () {
+ // Only built-in transform available.
+ throw new Error('not supported');
+ };
+
+ ExternalSource.prototype.getRawDataItem = function (dataIndex) {
+ // Only built-in transform available.
+ throw new Error('not supported');
+ };
+
+ ExternalSource.prototype.cloneRawData = function () {
+ return;
+ };
+ /**
+ * @return If dimension not found, return null/undefined.
+ */
+
+
+ ExternalSource.prototype.getDimensionInfo = function (dim) {
+ return;
+ };
+ /**
+ * dimensions defined if and only if either:
+ * (a) dataset.dimensions are declared.
+ * (b) dataset data include dimensions definitions in data (detected or via specified `sourceHeader`).
+ * If dimensions are defined, `dimensionInfoAll` is corresponding to
+ * the defined dimensions.
+ * Otherwise, `dimensionInfoAll` is determined by data columns.
+ * @return Always return an array (even empty array).
+ */
+
+
+ ExternalSource.prototype.cloneAllDimensionInfo = function () {
+ return;
+ };
+
+ ExternalSource.prototype.count = function () {
+ return;
+ };
+ /**
+ * Only support by dimension index.
+ * No need to support by dimension name in transform function,
+ * becuase transform function is not case-specific, no need to use name literally.
+ */
+
+
+ ExternalSource.prototype.retrieveValue = function (dataIndex, dimIndex) {
+ return;
+ };
+
+ ExternalSource.prototype.retrieveValueFromItem = function (dataItem, dimIndex) {
+ return;
+ };
+
+ ExternalSource.prototype.convertValue = function (rawVal, dimInfo) {
+ return parseDataValue(rawVal, dimInfo);
+ };
+
+ return ExternalSource;
+ }();
+
+ function createExternalSource(internalSource, externalTransform) {
+ var extSource = new ExternalSource();
+ var data = internalSource.data;
+ var sourceFormat = extSource.sourceFormat = internalSource.sourceFormat;
+ var sourceHeaderCount = internalSource.startIndex;
+ var errMsg = '';
+
+ if (internalSource.seriesLayoutBy !== SERIES_LAYOUT_BY_COLUMN) {
+ // For the logic simplicity in transformer, only 'culumn' is
+ // supported in data transform. Otherwise, the `dimensionsDefine`
+ // might be detected by 'row', which probably confuses users.
+ if ("development" !== 'production') {
+ errMsg = '`seriesLayoutBy` of upstream dataset can only be "column" in data transform.';
+ }
+
+ throwError(errMsg);
+ } // [MEMO]
+ // Create a new dimensions structure for exposing.
+ // Do not expose all dimension info to users directly.
+ // Becuase the dimension is probably auto detected from data and not might reliable.
+ // Should not lead the transformers to think that is relialbe and return it.
+ // See [DIMENSION_INHERIT_RULE] in `sourceManager.ts`.
+
+
+ var dimensions = [];
+ var dimsByName = {};
+ var dimsDef = internalSource.dimensionsDefine;
+
+ if (dimsDef) {
+ each(dimsDef, function (dimDef, idx) {
+ var name = dimDef.name;
+ var dimDefExt = {
+ index: idx,
+ name: name,
+ displayName: dimDef.displayName
+ };
+ dimensions.push(dimDefExt); // Users probably not sepcify dimension name. For simplicity, data transform
+ // do not generate dimension name.
+
+ if (name != null) {
+ // Dimension name should not be duplicated.
+ // For simplicity, data transform forbid name duplication, do not generate
+ // new name like module `completeDimensions.ts` did, but just tell users.
+ var errMsg_1 = '';
+
+ if (hasOwn(dimsByName, name)) {
+ if ("development" !== 'production') {
+ errMsg_1 = 'dimension name "' + name + '" duplicated.';
+ }
+
+ throwError(errMsg_1);
+ }
+
+ dimsByName[name] = dimDefExt;
+ }
+ });
+ } // If dimension definitions are not defined and can not be detected.
+ // e.g., pure data `[[11, 22], ...]`.
+ else {
+ for (var i = 0; i < internalSource.dimensionsDetectedCount || 0; i++) {
+ // Do not generete name or anything others. The consequence process in
+ // `transform` or `series` probably have there own name generation strategry.
+ dimensions.push({
+ index: i
+ });
+ }
+ } // Implement public methods:
+
+
+ var rawItemGetter = getRawSourceItemGetter(sourceFormat, SERIES_LAYOUT_BY_COLUMN);
+
+ if (externalTransform.__isBuiltIn) {
+ extSource.getRawDataItem = function (dataIndex) {
+ return rawItemGetter(data, sourceHeaderCount, dimensions, dataIndex);
+ };
+
+ extSource.getRawData = bind(getRawData, null, internalSource);
+ }
+
+ extSource.cloneRawData = bind(cloneRawData, null, internalSource);
+ var rawCounter = getRawSourceDataCounter(sourceFormat, SERIES_LAYOUT_BY_COLUMN);
+ extSource.count = bind(rawCounter, null, data, sourceHeaderCount, dimensions);
+ var rawValueGetter = getRawSourceValueGetter(sourceFormat);
+
+ extSource.retrieveValue = function (dataIndex, dimIndex) {
+ var rawItem = rawItemGetter(data, sourceHeaderCount, dimensions, dataIndex);
+ return retrieveValueFromItem(rawItem, dimIndex);
+ };
+
+ var retrieveValueFromItem = extSource.retrieveValueFromItem = function (dataItem, dimIndex) {
+ if (dataItem == null) {
+ return;
+ }
+
+ var dimDef = dimensions[dimIndex]; // When `dimIndex` is `null`, `rawValueGetter` return the whole item.
+
+ if (dimDef) {
+ return rawValueGetter(dataItem, dimIndex, dimDef.name);
+ }
+ };
+
+ extSource.getDimensionInfo = bind(getDimensionInfo, null, dimensions, dimsByName);
+ extSource.cloneAllDimensionInfo = bind(cloneAllDimensionInfo, null, dimensions);
+ return extSource;
+ }
+
+ function getRawData(upstream) {
+ var sourceFormat = upstream.sourceFormat;
+
+ if (!isSupportedSourceFormat(sourceFormat)) {
+ var errMsg = '';
+
+ if ("development" !== 'production') {
+ errMsg = '`getRawData` is not supported in source format ' + sourceFormat;
+ }
+
+ throwError(errMsg);
+ }
+
+ return upstream.data;
+ }
+
+ function cloneRawData(upstream) {
+ var sourceFormat = upstream.sourceFormat;
+ var data = upstream.data;
+
+ if (!isSupportedSourceFormat(sourceFormat)) {
+ var errMsg = '';
+
+ if ("development" !== 'production') {
+ errMsg = '`cloneRawData` is not supported in source format ' + sourceFormat;
+ }
+
+ throwError(errMsg);
+ }
+
+ if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) {
+ var result = [];
+
+ for (var i = 0, len = data.length; i < len; i++) {
+ // Not strictly clone for performance
+ result.push(data[i].slice());
+ }
+
+ return result;
+ } else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) {
+ var result = [];
+
+ for (var i = 0, len = data.length; i < len; i++) {
+ // Not strictly clone for performance
+ result.push(extend({}, data[i]));
+ }
+
+ return result;
+ }
+ }
+
+ function getDimensionInfo(dimensions, dimsByName, dim) {
+ if (dim == null) {
+ return;
+ } // Keep the same logic as `List::getDimension` did.
+
+
+ if (typeof dim === 'number' // If being a number-like string but not being defined a dimension name.
+ || !isNaN(dim) && !hasOwn(dimsByName, dim)) {
+ return dimensions[dim];
+ } else if (hasOwn(dimsByName, dim)) {
+ return dimsByName[dim];
+ }
+ }
+
+ function cloneAllDimensionInfo(dimensions) {
+ return clone(dimensions);
+ }
+
+ var externalTransformMap = createHashMap();
+ function registerExternalTransform(externalTransform) {
+ externalTransform = clone(externalTransform);
+ var type = externalTransform.type;
+ var errMsg = '';
+
+ if (!type) {
+ if ("development" !== 'production') {
+ errMsg = 'Must have a `type` when `registerTransform`.';
+ }
+
+ throwError(errMsg);
+ }
+
+ var typeParsed = type.split(':');
+
+ if (typeParsed.length !== 2) {
+ if ("development" !== 'production') {
+ errMsg = 'Name must include namespace like "ns:regression".';
+ }
+
+ throwError(errMsg);
+ } // Namespace 'echarts:xxx' is official namespace, where the transforms should
+ // be called directly via 'xxx' rather than 'echarts:xxx'.
+
+
+ var isBuiltIn = false;
+
+ if (typeParsed[0] === 'echarts') {
+ type = typeParsed[1];
+ isBuiltIn = true;
+ }
+
+ externalTransform.__isBuiltIn = isBuiltIn;
+ externalTransformMap.set(type, externalTransform);
+ }
+ function applyDataTransform(rawTransOption, sourceList, infoForPrint) {
+ var pipedTransOption = normalizeToArray(rawTransOption);
+ var pipeLen = pipedTransOption.length;
+ var errMsg = '';
+
+ if (!pipeLen) {
+ if ("development" !== 'production') {
+ errMsg = 'If `transform` declared, it should at least contain one transform.';
+ }
+
+ throwError(errMsg);
+ }
+
+ for (var i = 0, len = pipeLen; i < len; i++) {
+ var transOption = pipedTransOption[i];
+ sourceList = applySingleDataTransform(transOption, sourceList, infoForPrint, pipeLen === 1 ? null : i); // piped transform only support single input, except the fist one.
+ // piped transform only support single output, except the last one.
+
+ if (i !== len - 1) {
+ sourceList.length = Math.max(sourceList.length, 1);
+ }
+ }
+
+ return sourceList;
+ }
+
+ function applySingleDataTransform(transOption, upSourceList, infoForPrint, // If `pipeIndex` is null/undefined, no piped transform.
+ pipeIndex) {
+ var errMsg = '';
+
+ if (!upSourceList.length) {
+ if ("development" !== 'production') {
+ errMsg = 'Must have at least one upstream dataset.';
+ }
+
+ throwError(errMsg);
+ }
+
+ if (!isObject(transOption)) {
+ if ("development" !== 'production') {
+ errMsg = 'transform declaration must be an object rather than ' + typeof transOption + '.';
+ }
+
+ throwError(errMsg);
+ }
+
+ var transType = transOption.type;
+ var externalTransform = externalTransformMap.get(transType);
+
+ if (!externalTransform) {
+ if ("development" !== 'production') {
+ errMsg = 'Can not find transform on type "' + transType + '".';
+ }
+
+ throwError(errMsg);
+ } // Prepare source
+
+
+ var extUpSourceList = map(upSourceList, function (upSource) {
+ return createExternalSource(upSource, externalTransform);
+ });
+ var resultList = normalizeToArray(externalTransform.transform({
+ upstream: extUpSourceList[0],
+ upstreamList: extUpSourceList,
+ config: clone(transOption.config)
+ }));
+
+ if ("development" !== 'production') {
+ if (transOption.print) {
+ var printStrArr = map(resultList, function (extSource) {
+ var pipeIndexStr = pipeIndex != null ? ' === pipe index: ' + pipeIndex : '';
+ return ['=== dataset index: ' + infoForPrint.datasetIndex + pipeIndexStr + ' ===', '- transform result data:', makePrintable(extSource.data), '- transform result dimensions:', makePrintable(extSource.dimensions)].join('\n');
+ }).join('\n');
+ consoleLog(printStrArr);
+ }
+ }
+
+ return map(resultList, function (result, resultIndex) {
+ var errMsg = '';
+
+ if (!isObject(result)) {
+ if ("development" !== 'production') {
+ errMsg = 'A transform should not return some empty results.';
+ }
+
+ throwError(errMsg);
+ }
+
+ if (!result.data) {
+ if ("development" !== 'production') {
+ errMsg = 'Transform result data should be not be null or undefined';
+ }
+
+ throwError(errMsg);
+ }
+
+ var sourceFormat = detectSourceFormat(result.data);
+
+ if (!isSupportedSourceFormat(sourceFormat)) {
+ if ("development" !== 'production') {
+ errMsg = 'Transform result data should be array rows or object rows.';
+ }
+
+ throwError(errMsg);
+ }
+
+ var resultMetaRawOption;
+ var firstUpSource = upSourceList[0];
+ /**
+ * Intuitively, the end users known the content of the original `dataset.source`,
+ * calucating the transform result in mind.
+ * Suppose the original `dataset.source` is:
+ * ```js
+ * [
+ * ['product', '2012', '2013', '2014', '2015'],
+ * ['AAA', 41.1, 30.4, 65.1, 53.3],
+ * ['BBB', 86.5, 92.1, 85.7, 83.1],
+ * ['CCC', 24.1, 67.2, 79.5, 86.4]
+ * ]
+ * ```
+ * The dimension info have to be detected from the source data.
+ * Some of the transformers (like filter, sort) will follow the dimension info
+ * of upstream, while others use new dimensions (like aggregate).
+ * Transformer can output a field `dimensions` to define the its own output dimensions.
+ * We also allow transformers to ignore the output `dimensions` field, and
+ * inherit the upstream dimensions definition. It can reduce the burden of handling
+ * dimensions in transformers.
+ *
+ * See also [DIMENSION_INHERIT_RULE] in `sourceManager.ts`.
+ */
+
+ if (firstUpSource && resultIndex === 0 // If transformer returns `dimensions`, it means that the transformer has different
+ // dimensions definitions. We do not inherit anything from upstream.
+ && !result.dimensions) {
+ var startIndex = firstUpSource.startIndex; // We copy the header of upstream to the result becuase:
+ // (1) The returned data always does not contain header line and can not be used
+ // as dimension-detection. In this case we can not use "detected dimensions" of
+ // upstream directly, because it might be detected based on different `seriesLayoutBy`.
+ // (2) We should support that the series read the upstream source in `seriesLayoutBy: 'row'`.
+ // So the original detected header should be add to the result, otherwise they can not be read.
+
+ if (startIndex) {
+ result.data = firstUpSource.data.slice(0, startIndex).concat(result.data);
+ }
+
+ resultMetaRawOption = {
+ seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN,
+ sourceHeader: startIndex,
+ dimensions: firstUpSource.metaRawOption.dimensions
+ };
+ } else {
+ resultMetaRawOption = {
+ seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN,
+ sourceHeader: 0,
+ dimensions: result.dimensions
+ };
+ }
+
+ return createSource(result.data, resultMetaRawOption, null);
+ });
+ }
+
+ function isSupportedSourceFormat(sourceFormat) {
+ 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;
+ /**
+ * Multi dimensional data store
+ */
+
+ 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 DataStore() {
+ this._chunks = []; // It will not be calculated util needed.
+
+ this._rawExtent = [];
+ this._extent = [];
+ this._count = 0;
+ this._rawCount = 0;
+ this._calcDimNameToIdx = createHashMap();
+ }
+ /**
+ * Initialize from data
+ */
+
+
+ DataStore.prototype.initData = function (provider, inputDimensions, dimValueGetter) {
+ if ("development" !== 'production') {
+ assert(isFunction(provider.getItem) && isFunction(provider.count), 'Inavlid data provider.');
+ }
+
+ this._provider = provider; // Clear
+
+ 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;
+ };
+ /**
+ * 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.
+ */
+
+
+ DataStore.prototype.getSource = function () {
+ return this._provider.getSource();
+ };
+ /**
+ * @caution Only used in dataStack.
+ */
+
+
+ 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;
+ }
+
+ dimensions[calcDimIdx] = {
+ type: type
+ };
+ calcDimNameToIdx.set(dimName, calcDimIdx);
+ this._chunks[calcDimIdx] = new dataCtors[type || 'float'](this._rawCount);
+ this._rawExtent[calcDimIdx] = getInitialExtent();
+ return calcDimIdx;
+ };
+
+ 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;
+
+ 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 dimRawExtent = rawExtents[dimIdx]; // Parse from previous data offset. len may be changed after appendData
+
+ 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]);
+ }
+
+ 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;
+ };
+
+ 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).
+ */
+
+
+ DataStore.prototype.appendData = function (data) {
+ if ("development" !== 'production') {
+ assert(!this._indices, 'appendData can only be called on raw data.');
+ }
+
+ var provider = this._provider;
+ var start = this.count();
+ provider.appendData(data);
+ var end = provider.count();
+
+ if (!provider.persistent) {
+ end += start;
+ }
+
+ if (start < end) {
+ this._initDataFromProvider(start, end, true);
+ }
+
+ return [start, end];
+ };
+
+ 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);
+
+ for (var i = 0; i < dimLen; i++) {
+ var dim = dimensions[i];
+ prepareStore(chunks, i, dim.type, end, true);
+ }
+
+ var emptyDataItem = [];
+
+ for (var idx = start; idx < end; idx++) {
+ var sourceIdx = idx - start; // Store the data by dimensions
+
+ 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);
+ }
+ }
+
+ this._rawCount = this._count = end;
+ return {
+ start: start,
+ end: end
+ };
+ };
+
+ 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;
+ });
+
+ for (var i = 0; i < dimLen; i++) {
+ var dim = dimensions[i];
+
+ if (!rawExtent[i]) {
+ rawExtent[i] = getInitialExtent();
+ }
+
+ prepareStore(chunks, i, dim.type, end, append);
+ }
+
+ 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);
+ }
+ }
+ }
+
+ 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 = [];
+ };
+
+ DataStore.prototype.count = function () {
+ return this._count;
+ };
+ /**
+ * Get value. Return NaN if idx is out of range.
+ */
+
+
+ DataStore.prototype.get = function (dim, idx) {
+ if (!(idx >= 0 && idx < this._count)) {
+ return NaN;
+ }
+
+ var dimStore = this._chunks[dim];
+ return dimStore ? dimStore[this.getRawIndex(idx)] : NaN;
+ };
+
+ DataStore.prototype.getValues = function (dimensions, idx) {
+ var values = [];
+ var dimArr = [];
+
+ 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 dim concrete dim
+ */
+
+
+ DataStore.prototype.getByRawIndex = function (dim, rawIdx) {
+ if (!(rawIdx >= 0 && rawIdx < this._rawCount)) {
+ return NaN;
+ }
+
+ var dimStore = this._chunks[dim];
+ return dimStore ? dimStore[rawIdx] : NaN;
+ };
+ /**
+ * Get sum of data in one dimension
+ */
+
+
+ DataStore.prototype.getSum = function (dim) {
+ var dimData = this._chunks[dim];
+ var sum = 0;
+
+ 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
+ */
+
+
+ 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?
+
+ var sortedDimDataArray = dimDataArray.sort(function (a, b) {
+ return a - b;
+ });
+ var len = this.count(); // calculate median
+
+ 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
+ */
+
+
+ DataStore.prototype.indexOfRawIndex = function (rawIndex) {
+ if (rawIndex >= this._rawCount || rawIndex < 0) {
+ return -1;
+ }
+
+ if (!this._indices) {
+ return rawIndex;
+ } // Indices are ascending
+
+
+ var indices = this._indices; // If rawIndex === dataIndex
+
+ var rawDataIndex = indices[rawIndex];
+
+ if (rawDataIndex != null && rawDataIndex < this._count && rawDataIndex === rawIndex) {
+ return rawIndex;
+ }
+
+ var left = 0;
+ var right = this._count - 1;
+
+ while (left <= right) {
+ var mid = (left + right) / 2 | 0;
+
+ if (indices[mid] < rawIndex) {
+ left = mid + 1;
+ } else if (indices[mid] > rawIndex) {
+ right = mid - 1;
+ } else {
+ return mid;
+ }
+ }
+
+ return -1;
+ };
+ /**
+ * 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.
+ */
+
+
+ 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;
+ }
+
+ if (diff === minDiff) {
+ nearestIndices[nearestIndicesLen++] = i;
+ }
+ }
+ }
+
+ nearestIndices.length = nearestIndicesLen;
+ return nearestIndices;
+ };
+
+ 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 {
+ 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;
+ };
+ /**
+ * Data filter.
+ */
+
+
+ DataStore.prototype.filter = function (dims, cb) {
+ if (!this._count) {
+ return this;
+ }
+
+ 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;
+
+ for (var i = 0; i < count; i++) {
+ var keep = void 0;
+ var rawIdx = newStore.getRawIndex(i); // Simple optimization
+
+ if (dimSize === 0) {
+ keep = cb(i);
+ } else if (dimSize === 1) {
+ var val = chunks[dim0][rawIdx];
+ keep = cb(val, i);
+ } else {
+ var k = 0;
+
+ for (; k < dimSize; k++) {
+ value[k] = chunks[dims[k]][rawIdx];
+ }
+
+ value[k] = i;
+ keep = cb.apply(null, value);
+ }
+
+ if (keep) {
+ newIndices[offset++] = rawIdx;
+ }
+ } // Set indices after filtered.
+
+
+ if (offset < count) {
+ newStore._indices = newIndices;
+ }
+
+ newStore._count = offset; // Reset data extent
+
+ newStore._extent = [];
+
+ newStore._updateGetRawIdx();
+
+ return newStore;
+ };
+ /**
+ * Select data in range. (For optimization of filter)
+ * (Manually inline code, support 5 million data filtering in data zoom.)
+ */
+
+
+ DataStore.prototype.selectRange = function (range) {
+ var newStore = this.clone();
+ var len = newStore._count;
+
+ if (!len) {
+ return this;
+ }
+
+ var dims = keys(range);
+ var dimSize = dims.length;
+
+ if (!dimSize) {
+ return this;
+ }
+
+ 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;
+
+ if (!newStore._indices) {
+ // Extreme optimization for common case. About 2x faster in chrome.
+ var idx = 0;
+
+ if (dimSize === 1) {
+ var dimStorage = storeArr[dims[0]];
+
+ 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.
+
+ if (val >= min && val <= max || isNaN(val)) {
+ newIndices[offset++] = idx;
+ }
+
+ idx++;
+ }
+
+ 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];
+
+ for (var i = 0; i < len; i++) {
+ var val = dimStorage[i];
+ var val2 = dimStorage2[i]; // Do not filter NaN, see comment above.
+
+ if ((val >= min && val <= max || isNaN(val)) && (val2 >= min2 && val2 <= max2 || isNaN(val2))) {
+ newIndices[offset++] = idx;
+ }
+
+ idx++;
+ }
+
+ quickFinished = true;
+ }
+ }
+
+ 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.
+
+ 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);
+
+ for (var k = 0; k < dimSize; k++) {
+ var dimk = dims[k];
+ var val = storeArr[dimk][rawIndex]; // Do not filter NaN, see comment above.
+
+ if (val < range[dimk][0] || val > range[dimk][1]) {
+ keep = false;
+ }
+ }
+
+ if (keep) {
+ newIndices[offset++] = newStore.getRawIndex(i);
+ }
+ }
+ }
+ } // Set indices after filtered.
+
+
+ if (offset < originalCount) {
+ newStore._indices = newIndices;
+ }
+
+ newStore._count = offset; // Reset data extent
+
+ newStore._extent = [];
+
+ 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;
+ // }
+
+ /**
+ * Data mapping to a new List with given dimensions
+ */
+
+
+ DataStore.prototype.map = function (dims, cb) {
+ // TODO only clone picked chunks.
+ var target = this.clone(dims);
+
+ this._updateDims(target, dims, cb);
+
+ return target;
+ };
+ /**
+ * @caution Danger!! Only used in dataStack.
+ */
+
+
+ DataStore.prototype.modify = function (dims, cb) {
+ this._updateDims(this, dims, cb);
+ };
+
+ 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;
+
+ for (var i = 0; i < dims.length; i++) {
+ rawExtent[dims[i]] = getInitialExtent();
+ }
+
+ for (var dataIndex = 0; dataIndex < dataCount; dataIndex++) {
+ var rawIndex = target.getRawIndex(dataIndex);
+
+ for (var k = 0; k < dimSize; k++) {
+ values[k] = targetChunks[dims[k]][rawIndex];
+ }
+
+ values[dimSize] = dataIndex;
+ var retValue = cb && cb.apply(null, values);
+
+ if (retValue != null) {
+ // a number or string (in oridinal dimension)?
+ if (typeof retValue !== 'object') {
+ tmpRetValue[0] = retValue;
+ retValue = tmpRetValue;
+ }
+
+ for (var i = 0; i < retValue.length; i++) {
+ var dim = dims[i];
+ var val = retValue[i];
+ var rawExtentOnDim = rawExtent[dim];
+ var dimStore = targetChunks[dim];
+
+ if (dimStore) {
+ dimStore[rawIndex] = val;
+ }
+
+ if (val < rawExtentOnDim[0]) {
+ rawExtentOnDim[0] = val;
+ }
+
+ if (val > rawExtentOnDim[1]) {
+ rawExtentOnDim[1] = val;
+ }
+ }
+ }
+ }
+ };
+ /**
+ * Large data down sampling using largest-triangle-three-buckets
+ * @param {string} valueDimension
+ * @param {number} targetCount
+ */
+
+
+ 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.
+
+ newIndices[sampledIndex++] = currentRawIndex;
+
+ 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;
+
+ for (var idx = nextFrameStart; idx < nextFrameEnd; idx++) {
+ var rawIndex = this.getRawIndex(idx);
+ var y = dimStore[rawIndex];
+
+ if (isNaN(y)) {
+ continue;
+ }
+
+ avgY += y;
+ }
+
+ 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.
+
+ for (var idx = frameStart; idx < frameEnd; idx++) {
+ var rawIndex = this.getRawIndex(idx);
+ var y = dimStore[rawIndex];
+
+ if (isNaN(y)) {
+ continue;
+ } // Calculate triangle area over three buckets
+
+
+ area = Math.abs((pointAX - avgX) * (y - pointAY) - (pointAX - idx) * (avgY - pointAY));
+
+ if (area > maxArea) {
+ maxArea = area;
+ nextRawIndex = rawIndex; // Next a is this b
+ }
+ }
+
+ newIndices[sampledIndex++] = nextRawIndex;
+ currentRawIndex = nextRawIndex; // This a is the next a (chosen b)
+ } // First frame use the last data.
+
+
+ newIndices[sampledIndex++] = this.getRawIndex(len - 1);
+ target._count = sampledIndex;
+ target._indices = newIndices;
+ target.getRawIndex = this._getRawIdx;
+ return target;
+ };
+ /**
+ * Large data down sampling on given dimension
+ * @param sampleIndex Sample index for name and id
+ */
+
+
+ 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;
+
+ for (var i = 0; i < len; i += frameSize) {
+ // Last frame
+ if (frameSize > len - i) {
+ frameSize = len - i;
+ frameValues.length = frameSize;
+ }
+
+ for (var k = 0; k < frameSize; k++) {
+ var dataIdx = this.getRawIndex(i + k);
+ frameValues[k] = dimStore[dataIdx];
+ }
+
+ var value = sampleValue(frameValues);
+ var sampleFrameIdx = this.getRawIndex(Math.min(i + sampleIndex(frameValues, value) || 0, len - 1)); // Only write value on the filtered data
+
+ dimStore[sampleFrameIdx] = value;
+
+ if (value < rawExtentOnDim[0]) {
+ rawExtentOnDim[0] = value;
+ }
+
+ if (value > rawExtentOnDim[1]) {
+ rawExtentOnDim[1] = value;
+ }
+
+ newIndices[offset++] = sampleFrameIdx;
+ }
+
+ target._count = offset;
+ target._indices = newIndices;
+
+ target._updateGetRawIdx();
+
+ return target;
+ };
+ /**
+ * 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) {})
+ */
+
+
+ DataStore.prototype.each = function (dims, cb) {
+ if (!this._count) {
+ return;
+ }
+
+ var dimSize = dims.length;
+ var chunks = this._chunks;
+
+ for (var i = 0, len = this.count(); i < len; i++) {
+ var rawIdx = this.getRawIndex(i); // Simple optimization
+
+ switch (dimSize) {
+ case 0:
+ cb(i);
+ break;
+
+ case 1:
+ cb(chunks[dims[0]][rawIdx], i);
+ break;
+
+ case 2:
+ cb(chunks[dims[0]][rawIdx], chunks[dims[1]][rawIdx], i);
+ break;
+
+ default:
+ var k = 0;
+ var value = [];
+
+ for (; k < dimSize; k++) {
+ value[k] = chunks[dims[k]][rawIdx];
+ } // Index
+
+
+ value[k] = i;
+ cb.apply(null, value);
+ }
+ }
+ };
+ /**
+ * Get extent of data in one dimension
+ */
+
+
+ DataStore.prototype.getDataExtent = function (dim) {
+ // Make sure use concrete dim as cache name.
+ var dimData = this._chunks[dim];
+ var initialExtent = getInitialExtent();
+
+ if (!dimData) {
+ return initialExtent;
+ } // Make more strict checkings to ensure hitting cache.
+
+
+ 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;
+
+ if (useRaw) {
+ return this._rawExtent[dim].slice();
+ }
+
+ dimExtent = this._extent[dim];
+
+ if (dimExtent) {
+ return dimExtent.slice();
+ }
+
+ dimExtent = initialExtent;
+ var min = dimExtent[0];
+ var max = dimExtent[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
+ */
+
+
+ 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.
+ */
+
+
+ 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 (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;
+ }
+
+ this._copyCommonProps(target);
+
+ if (!ignoreIndices) {
+ target._indices = this._cloneIndices();
+ }
+
+ target._updateGetRawIdx();
+
+ return target;
+ };
+
+ 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);
+ };
+
+ DataStore.prototype._cloneIndices = function () {
+ if (this._indices) {
+ var Ctor = this._indices.constructor;
+ var indices = void 0;
+
+ if (Ctor === Array) {
+ var thisCount = this._indices.length;
+ indices = new Ctor(thisCount);
+
+ for (var i = 0; i < thisCount; i++) {
+ indices[i] = this._indices[i];
+ }
+ } else {
+ indices = new Ctor(this._indices);
+ }
+
+ return indices;
+ }
+
+ return null;
+ };
+
+ DataStore.prototype._getRawIdxIdentity = function (idx) {
+ return idx;
+ };
+
+ DataStore.prototype._getRawIdx = function (idx) {
+ if (idx < this._count && idx >= 0) {
+ return this._indices[idx];
+ }
+
+ return -1;
+ };
+
+ DataStore.prototype._updateGetRawIdx = function () {
+ this.getRawIndex = this._indices ? this._getRawIdx : this._getRawIdxIdentity;
+ };
+
+ DataStore.internalField = function () {
+ function getDimValueSimply(dataItem, property, dataIndex, dimIndex) {
+ return parseDataValue(dataItem[dimIndex], this._dimensions[dimIndex]);
+ }
+
+ 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 DataStore;
+ }();
+
+ /**
+ * [REQUIREMENT_MEMO]:
+ * (0) `metaRawOption` means `dimensions`/`sourceHeader`/`seriesLayoutBy` in raw option.
+ * (1) Keep support the feature: `metaRawOption` can be specified both on `series` and
+ * `root-dataset`. Them on `series` has higher priority.
+ * (2) Do not support to set `metaRawOption` on a `non-root-dataset`, because it might
+ * confuse users: whether those props indicate how to visit the upstream source or visit
+ * the transform result source, and some transforms has nothing to do with these props,
+ * and some transforms might have multiple upstream.
+ * (3) Transforms should specify `metaRawOption` in each output, just like they can be
+ * declared in `root-dataset`.
+ * (4) At present only support visit source in `SERIES_LAYOUT_BY_COLUMN` in transforms.
+ * That is for reducing complexity in transfroms.
+ * PENDING: Whether to provide transposition transform?
+ *
+ * [IMPLEMENTAION_MEMO]:
+ * "sourceVisitConfig" are calculated from `metaRawOption` and `data`.
+ * They will not be calculated until `source` is about to be visited (to prevent from
+ * duplicate calcuation). `source` is visited only in series and input to transforms.
+ *
+ * [DIMENSION_INHERIT_RULE]:
+ * By default the dimensions are inherited from ancestors, unless a transform return
+ * a new dimensions definition.
+ * Consider the case:
+ * ```js
+ * dataset: [{
+ * source: [ ['Product', 'Sales', 'Prise'], ['Cookies', 321, 44.21], ...]
+ * }, {
+ * transform: { type: 'filter', ... }
+ * }]
+ * dataset: [{
+ * dimension: ['Product', 'Sales', 'Prise'],
+ * source: [ ['Cookies', 321, 44.21], ...]
+ * }, {
+ * transform: { type: 'filter', ... }
+ * }]
+ * ```
+ * The two types of option should have the same behavior after transform.
+ *
+ *
+ * [SCENARIO]:
+ * (1) Provide source data directly:
+ * ```js
+ * series: {
+ * encode: {...},
+ * dimensions: [...]
+ * seriesLayoutBy: 'row',
+ * data: [[...]]
+ * }
+ * ```
+ * (2) Series refer to dataset.
+ * ```js
+ * series: [{
+ * encode: {...}
+ * // Ignore datasetIndex means `datasetIndex: 0`
+ * // and the dimensions defination in dataset is used
+ * }, {
+ * encode: {...},
+ * seriesLayoutBy: 'column',
+ * datasetIndex: 1
+ * }]
+ * ```
+ * (3) dataset transform
+ * ```js
+ * dataset: [{
+ * source: [...]
+ * }, {
+ * source: [...]
+ * }, {
+ * // By default from 0.
+ * transform: { type: 'filter', config: {...} }
+ * }, {
+ * // Piped.
+ * transform: [
+ * { type: 'filter', config: {...} },
+ * { type: 'sort', config: {...} }
+ * ]
+ * }, {
+ * id: 'regressionData',
+ * fromDatasetIndex: 1,
+ * // Third-party transform
+ * transform: { type: 'ecStat:regression', config: {...} }
+ * }, {
+ * // retrieve the extra result.
+ * id: 'regressionFormula',
+ * fromDatasetId: 'regressionData',
+ * fromTransformResult: 1
+ * }]
+ * ```
+ */
+
+ var SourceManager =
+ /** @class */
+ function () {
+ function SourceManager(sourceHost) {
+ // Cached source. Do not repeat calculating if not dirty.
+ this._sourceList = [];
+ this._storeList = []; // version sign of each upstream source manager.
+
+ this._upstreamSignList = [];
+ this._versionSignBase = 0;
+ this._dirty = true;
+ this._sourceHost = sourceHost;
+ }
+ /**
+ * Mark dirty.
+ */
+
+
+ SourceManager.prototype.dirty = function () {
+ this._setLocalSource([], []);
+
+ this._storeList = [];
+ this._dirty = true;
+ };
+
+ SourceManager.prototype._setLocalSource = function (sourceList, upstreamSignList) {
+ this._sourceList = sourceList;
+ this._upstreamSignList = upstreamSignList;
+ this._versionSignBase++;
+
+ 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.
+ */
+
+
+ SourceManager.prototype._getVersionSign = function () {
+ return this._sourceHost.uid + '_' + this._versionSignBase;
+ };
+ /**
+ * Always return a source instance. Otherwise throw error.
+ */
+
+
+ SourceManager.prototype.prepareSource = function () {
+ // For the case that call `setOption` multiple time but no data changed,
+ // cache the result source to prevent from repeating transform.
+ if (this._isDirty()) {
+ this._createSource();
+
+ this._dirty = false;
+ }
+ };
+
+ SourceManager.prototype._createSource = function () {
+ this._setLocalSource([], []);
+
+ var sourceHost = this._sourceHost;
+
+ var upSourceMgrList = this._getUpstreamSourceManagers();
+
+ var hasUpstream = !!upSourceMgrList.length;
+ var resultSourceList;
+ var upstreamSignList;
+
+ if (isSeries(sourceHost)) {
+ var seriesModel = sourceHost;
+ var data = void 0;
+ var sourceFormat = void 0;
+ var upSource = void 0; // Has upstream dataset
+
+ 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.
+
+
+ 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.
+
+ 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.
+
+ if (hasUpstream) {
+ var result = this._applyTransform(upSourceMgrList);
+
+ resultSourceList = result.sourceList;
+ upstreamSignList = result.upstreamSignList;
+ } // Is root dataset.
+ else {
+ var sourceData = datasetModel.get('source', true);
+ resultSourceList = [createSource(sourceData, this._getSourceMetaRawOption(), null)];
+ upstreamSignList = [];
+ }
+ }
+
+ if ("development" !== 'production') {
+ assert(resultSourceList && upstreamSignList);
+ }
+
+ this._setLocalSource(resultSourceList, upstreamSignList);
+ };
+
+ SourceManager.prototype._applyTransform = function (upMgrList) {
+ var datasetModel = this._sourceHost;
+ var transformOption = datasetModel.get('transform', true);
+ var fromTransformResult = datasetModel.get('fromTransformResult', true);
+
+ 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());
+ });
+
+ if (transformOption) {
+ sourceList = applyDataTransform(transformOption, upSourceList, {
+ datasetIndex: datasetModel.componentIndex
+ });
+ } else if (fromTransformResult != null) {
+ sourceList = [cloneSourceShallow(upSourceList[0])];
+ }
+
+ return {
+ sourceList: sourceList,
+ upstreamSignList: upstreamSignList
+ };
+ };
+
+ SourceManager.prototype._isDirty = function () {
+ if (this._dirty) {
+ return true;
+ } // All sourceList is from the some upsteam.
+
+
+ var upSourceMgrList = this._getUpstreamSourceManagers();
+
+ for (var i = 0; i < upSourceMgrList.length; i++) {
+ var upSrcMgr = upSourceMgrList[i];
+
+ if ( // Consider the case that there is ancestor diry, call it recursively.
+ // The performance is probably not an issue because usually the chain is not long.
+ upSrcMgr._isDirty() || this._upstreamSignList[i] !== upSrcMgr._getVersionSign()) {
+ return true;
+ }
+ }
+ };
+ /**
+ * @param sourceIndex By defualt 0, means "main source".
+ * Most cases there is only one source.
+ */
+
+
+ SourceManager.prototype.getSource = function (sourceIndex) {
+ 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;
+ };
+ /**
+ *
+ * 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.
+ */
+
+
+ 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);
+ };
+
+ SourceManager.prototype._innerGetDataStore = function (storeDims, seriesSource, sourceReadKey) {
+ // TODO Can use other sourceIndex?
+ var sourceIndex = 0;
+ var storeList = this._storeList;
+ var cachedStoreMap = storeList[sourceIndex];
+
+ if (!cachedStoreMap) {
+ cachedStoreMap = storeList[sourceIndex] = {};
+ }
+
+ var cachedStore = cachedStoreMap[sourceReadKey];
+
+ if (!cachedStore) {
+ var upSourceMgr = this._getUpstreamSourceManagers()[0];
+
+ 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);
+ }
+
+ cachedStoreMap[sourceReadKey] = cachedStore;
+ }
+
+ return cachedStore;
+ };
+ /**
+ * PEDING: Is it fast enough?
+ * If no upstream, return empty array.
+ */
+
+
+ SourceManager.prototype._getUpstreamSourceManagers = function () {
+ // Always get the relationship from the raw option.
+ // Do not cache the link of the dependency graph, so that
+ // no need to update them when change happen.
+ var sourceHost = this._sourceHost;
+
+ if (isSeries(sourceHost)) {
+ var datasetModel = querySeriesUpstreamDatasetModel(sourceHost);
+ return !datasetModel ? [] : [datasetModel.getSourceManager()];
+ } else {
+ return map(queryDatasetUpstreamDatasetModels(sourceHost), function (datasetModel) {
+ return datasetModel.getSourceManager();
+ });
+ }
+ };
+
+ 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);
+ }
+
+ return {
+ seriesLayoutBy: seriesLayoutBy,
+ sourceHeader: sourceHeader,
+ dimensions: dimensions
+ };
+ };
+
+ return SourceManager;
+ }();
+ // disable the transform merge, but do not disable transfrom clone from rawOption.
+
+ function disableTransformOptionMerge(datasetModel) {
+ var transformOption = datasetModel.option.transform;
+ transformOption && setAsPrimitive(datasetModel.option.transform);
+ }
+
+ function isSeries(sourceHost) {
+ // Avoid circular dependency with Series.ts
+ return sourceHost.mainType === 'series';
+ }
+
+ function doThrow(errMsg) {
+ throw new Error(errMsg);
+ }
+
+ 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';
+
+ 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 HTML_GAPS = [0, 10, 20, 30];
+ var RICH_TEXT_GAPS = ['', '\n', '\n\n', '\n\n\n']; // eslint-disable-next-line max-len
+
+ function createTooltipMarkup(type, option) {
+ option.type = type;
+ return option;
+ }
+
+ function getBuilder(fragment) {
+ return hasOwn(builderMap, fragment.type) && builderMap[fragment.type];
+ }
+
+ 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.
+
+ 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);
+
+ if (noHeader) {
+ return subMarkupText;
+ }
+
+ var displayableHeader = makeValueReadable(fragment.header, 'ordinal', ctx.useUTC);
+ var nameStyle = getTooltipTextStyle(toolTipTextStyle, ctx.renderMode).nameStyle;
+
+ if (ctx.renderMode === 'richText') {
+ return wrapInlineNameRichText(ctx, displayableHeader, nameStyle) + gaps.richText + subMarkupText;
+ } else {
+ return wrapBlockHTML("" + encodeHTML(displayableHeader) + '
' + subMarkupText, topMarginForOuterGap);
+ }
+ }
+ },
+
+ /**
+ * 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;
+ }
+
+ 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;
+
+ var _a = getTooltipTextStyle(toolTipTextStyle, renderMode),
+ nameStyle = _a.nameStyle,
+ valueStyle = _a.valueStyle;
+
+ 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 buildSubBlocks(ctx, fragment, topMarginForOuterGap, tooltipTextStyle) {
+ var subMarkupTextList = [];
+ var subBlocks = fragment.blocks || [];
+ assert(!subBlocks || isArray(subBlocks));
+ subBlocks = subBlocks || [];
+ var orderMode = ctx.orderMode;
+
+ if (fragment.sortBlocks && orderMode) {
+ subBlocks = subBlocks.slice();
+ var orderMap = {
+ valueAsc: 'asc',
+ valueDesc: 'desc'
+ };
+
+ 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();
+ }
+ }
+
+ 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;
+ }
+
+ return ctx.renderMode === 'richText' ? subMarkupTextList.join(gaps.richText) : wrapBlockHTML(subMarkupTextList.join(''), topMarginForOuterGap);
+ }
+ /**
+ * @return markupText. null/undefined means no content.
+ */
+
+
+ function buildTooltipMarkup(fragment, markupStyleCreator, renderMode, orderMode, useUTC, toolTipTextStyle) {
+ if (!fragment) {
+ return;
+ }
+
+ var builder = getBuilder(fragment);
+ builder.planLayout(fragment);
+ var ctx = {
+ useUTC: useUTC,
+ renderMode: renderMode,
+ orderMode: orderMode,
+ markupStyleCreator: markupStyleCreator
+ };
+ return builder.build(ctx, fragment, 0, toolTipTextStyle);
+ }
+
+ function getGap(fragment) {
+ var gapLevelBetweenSubBlocks = fragment.__gapLevelBetweenSubBlocks;
+ return {
+ html: HTML_GAPS[gapLevelBetweenSubBlocks],
+ richText: RICH_TEXT_GAPS[gapLevelBetweenSubBlocks]
+ };
+ }
+
+ function wrapBlockHTML(encodedContent, topGap) {
+ var clearfix = '';
+ var marginCSS = "margin: " + topGap + "px 0 0";
+ return "" + encodedContent + clearfix + '
';
+ }
+
+ function wrapInlineNameHTML(name, leftHasMarker, style) {
+ var marginCss = leftHasMarker ? 'margin-left:2px' : '';
+ return "" + encodeHTML(name) + '';
+ }
+
+ 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 "" // Value has commas inside, so use ' ' as delimiter for multiple values.
+ + map(valueList, function (value) {
+ return encodeHTML(value);
+ }).join(' ') + '';
+ }
+
+ function wrapInlineNameRichText(ctx, name, style) {
+ return ctx.markupStyleCreator.wrapRichTextStyle(name, style);
+ }
+
+ 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.
+
+ return ctx.markupStyleCreator.wrapRichTextStyle(valueList.join(' '), styles);
+ }
+
+ 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).
+
+ this._nextStyleNameId = getRandomIdBase();
+ }
+
+ TooltipMarkupStyleCreator.prototype._generateStyleName = function () {
+ return '__EC_aUTo_' + this._nextStyleNameId++;
+ };
+
+ 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 (isString(marker)) {
+ return marker;
+ } else {
+ if ("development" !== 'production') {
+ assert(markerId);
+ }
+
+ 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
+ * }
+ * ]);
+ * ```
+ */
+
+
+ TooltipMarkupStyleCreator.prototype.wrapRichTextStyle = function (text, styles) {
+ var finalStl = {};
+
+ if (isArray(styles)) {
+ each(styles, function (stl) {
+ return extend(finalStl, stl);
+ });
+ } else {
+ extend(finalStl, styles);
+ }
+
+ var styleName = this._generateStyleName();
+
+ this.richTextStyles[styleName] = finalStl;
+ return "{" + styleName + "|" + text + "}";
+ };
+
+ 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.
+
+ var inlineValue;
+ var inlineValueType;
+ var subBlocks;
+ var sortParam;
+
+ 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.
+
+ 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.
+
+
+ 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 || [])
+ });
+ }
+
+ 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.
+
+ if (!dimInfo || dimInfo.otherDims.tooltip === false) {
+ return;
+ }
+
+ 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 {
+ inlineValues: inlineValues,
+ inlineValueTypes: inlineValueTypes,
+ blocks: blocks
+ };
+ }
+
+ var inner$1 = makeInner();
+
+ function getSelectionKey(data, dataIndex) {
+ return data.getName(dataIndex) || data.getId(dataIndex);
+ }
+
+ var SERIES_UNIVERSAL_TRANSITION_PROP = '__universalTransitionEnabled';
+
+ var SeriesModel =
+ /** @class */
+ function (_super) {
+ __extends(SeriesModel, _super);
+
+ 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
+ // ---------------------------------------
+
+
+ _this._selectedDataIndicesMap = {};
+ return _this;
+ }
+
+ 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.');
+ }
+
+ 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();
+
+ autoSeriesName(this);
+
+ this._initSelectedMapFromData(data);
+ };
+ /**
+ * Util for merge default and theme to option
+ */
+
+
+ SeriesModel.prototype.mergeDefaultAndTheme = function (option, ecModel) {
+ var layoutMode = fetchLayoutMode(this);
+ var inputPositionParams = layoutMode ? getLayoutParams(option) : {}; // Backward compat: using subType on theme.
+ // But if name duplicate between series subType
+ // (for example: parallel) add component mainType,
+ // add suffix 'Series'.
+
+ var themeSubType = this.subType;
+
+ if (ComponentModel.hasClass(themeSubType)) {
+ themeSubType += 'Series';
+ }
+
+ merge(option, ecModel.getTheme().get(this.subType));
+ merge(option, this.getDefaultOption()); // Default label emphasis `show`
+
+ defaultEmphasis(option, 'label', ['show']);
+ this.fillDataTextStyle(option.data);
+
+ if (layoutMode) {
+ mergeLayoutParam(option, inputPositionParams, layoutMode);
+ }
+ };
+
+ SeriesModel.prototype.mergeOption = function (newSeriesOption, ecModel) {
+ // this.settingTask.dirty();
+ newSeriesOption = merge(this.option, newSeriesOption, true);
+ this.fillDataTextStyle(newSeriesOption.data);
+ var layoutMode = fetchLayoutMode(this);
+
+ if (layoutMode) {
+ mergeLayoutParam(this.option, newSeriesOption, layoutMode);
+ }
+
+ 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);
+ };
+
+ SeriesModel.prototype.fillDataTextStyle = function (data) {
+ // Default data label emphasis `show`
+ // FIXME Tree structure data ?
+ // FIXME Performance ?
+ if (data && !isTypedArray(data)) {
+ var props = ['show'];
+
+ 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.
+ */
+
+
+ SeriesModel.prototype.getInitialData = function (option, ecModel) {
+ return;
+ };
+ /**
+ * Append data to list
+ */
+
+
+ SeriesModel.prototype.appendData = function (params) {
+ // FIXME ???
+ // (1) If data from dataset, forbidden append.
+ // (2) support append data of dataset.
+ var data = this.getRawData();
+ data.appendData(params.data);
+ };
+ /**
+ * Consider some method like `filter`, `map` need make new data,
+ * We should make sure that `seriesModel.getData()` get correct
+ * data in the stream procedure. So we fetch data from upstream
+ * each time `task.perform` called.
+ */
+
+
+ SeriesModel.prototype.getData = function (dataType) {
+ var task = getCurrentTask(this);
+
+ 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;
+ }
+ };
+
+ SeriesModel.prototype.getAllData = function () {
+ var mainData = this.getData();
+ return mainData && mainData.getLinkedDataAll ? mainData.getLinkedDataAll() : [{
+ data: mainData
+ }];
+ };
+
+ SeriesModel.prototype.setData = function (data) {
+ var task = getCurrentTask(this);
+
+ 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());
+ // }
+
+ 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;
+ }
+ }
+
+ inner$1(this).data = data;
+ };
+
+ SeriesModel.prototype.getEncode = function () {
+ var encode = this.get('encode', true);
+
+ if (encode) {
+ return createHashMap(encode);
+ }
+ };
+
+ SeriesModel.prototype.getSourceManager = function () {
+ return inner$1(this).sourceManager;
+ };
+
+ SeriesModel.prototype.getSource = function () {
+ return this.getSourceManager().getSource();
+ };
+ /**
+ * Get data before processed
+ */
+
+
+ SeriesModel.prototype.getRawData = function () {
+ return inner$1(this).dataBeforeProcessed;
+ };
+
+ SeriesModel.prototype.getColorBy = function () {
+ var colorBy = this.get('colorBy');
+ return colorBy || 'series';
+ };
+
+ 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
+ */
+
+
+ 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
+ */
+
+
+ SeriesModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) {
+ return defaultSeriesFormatTooltip({
+ series: this,
+ dataIndex: dataIndex,
+ multipleSeries: multipleSeries
+ });
+ };
+
+ 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;
+ };
+
+ SeriesModel.prototype.restoreData = function () {
+ this.dataTask.dirty();
+ };
+
+ SeriesModel.prototype.getColorFromPalette = function (name, scope, requestColorNum) {
+ var ecModel = this.ecModel; // PENDING
+
+ var color = PaletteMixin.prototype.getColorFromPalette.call(this, name, scope, requestColorNum);
+
+ if (!color) {
+ color = ecModel.getColorFromPalette(name, scope, requestColorNum);
+ }
+
+ return color;
+ };
+ /**
+ * Use `data.mapDimensionsAll(coordDim)` instead.
+ * @deprecated
+ */
+
+
+ SeriesModel.prototype.coordDimToDataDim = function (coordDim) {
+ return this.getRawData().mapDimensionsAll(coordDim);
+ };
+ /**
+ * Get progressive rendering count each step
+ */
+
+
+ SeriesModel.prototype.getProgressive = function () {
+ return this.get('progressive');
+ };
+ /**
+ * Get progressive rendering count each step
+ */
+
+
+ SeriesModel.prototype.getProgressiveThreshold = function () {
+ return this.get('progressiveThreshold');
+ }; // PENGING If selectedMode is null ?
+
+
+ SeriesModel.prototype.select = function (innerDataIndices, dataType) {
+ this._innerSelect(this.getData(dataType), innerDataIndices);
+ };
+
+ SeriesModel.prototype.unselect = function (innerDataIndices, dataType) {
+ var selectedMap = this.option.selectedMap;
+
+ if (!selectedMap) {
+ return;
+ }
+
+ 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;
+ }
+ };
+
+ 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);
+ }
+ };
+
+ 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;
+ };
+
+ SeriesModel.prototype.isSelected = function (dataIndex, dataType) {
+ var selectedMap = this.option.selectedMap;
+
+ if (!selectedMap) {
+ return false;
+ }
+
+ var data = this.getData(dataType);
+ var nameOrId = getSelectionKey(data, dataIndex);
+ return selectedMap[nameOrId] || false;
+ };
+
+ SeriesModel.prototype.isUniversalTransitionEnabled = function () {
+ if (this[SERIES_UNIVERSAL_TRANSITION_PROP]) {
+ return true;
+ }
+
+ var universalTransitionOpt = this.option.universalTransition; // Quick reject
+
+ if (!universalTransitionOpt) {
+ return false;
+ }
+
+ if (universalTransitionOpt === true) {
+ return true;
+ } // Can be simply 'universalTransition: true'
+
+
+ return universalTransitionOpt && universalTransitionOpt.enabled;
+ };
+
+ SeriesModel.prototype._innerSelect = function (data, innerDataIndices) {
+ var _a, _b;
+
+ var selectedMode = this.option.selectedMode;
+ var len = innerDataIndices.length;
+
+ if (!selectedMode || !len) {
+ return;
+ }
+
+ 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 (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;
+ }
+
+ var dataIndices = [];
+
+ if (data.hasItemOption) {
+ data.each(function (idx) {
+ var rawItem = data.getRawDataItem(idx);
+
+ if (rawItem && rawItem.selected) {
+ dataIndices.push(idx);
+ }
+ });
+ }
+
+ if (dataIndices.length > 0) {
+ this._innerSelect(data, dataIndices);
+ }
+ }; // /**
+ // * @see {module:echarts/stream/Scheduler}
+ // */
+ // abstract pipeTask: null
+
+
+ SeriesModel.registerClass = function (clz) {
+ return ComponentModel.registerClass(clz);
+ };
+
+ 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!
+
+ 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;
+ }
+ }
+
+ 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();
+ }
+
+ function dataTaskReset(context) {
+ var seriesModel = context.model;
+ seriesModel.setData(seriesModel.getRawData().cloneShallow());
+ return dataTaskProgress;
+ }
+
+ function dataTaskProgress(param, context) {
+ // Avoid repead cloneShallow when data just created in reset.
+ if (context.outputData && param.end > context.outputData.count()) {
+ context.model.getRawData().cloneShallow(context.outputData);
+ }
+ } // TODO refactor
+
+
+ function wrapData(data, seriesModel) {
+ each(concatArray(data.CHANGABLE_METHODS, data.DOWNSAMPLE_METHODS), function (methodName) {
+ data.wrapMethod(methodName, curry(onDataChange, seriesModel));
+ });
+ }
+
+ function onDataChange(seriesModel, newList) {
+ var task = getCurrentTask(seriesModel);
+
+ if (task) {
+ // Consider case: filter, selectRange
+ task.setOutputEnd((newList || this).count());
+ }
+
+ return newList;
+ }
+
+ 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;
+
+ if (task) {
+ var agentStubMap = task.agentStubMap;
+
+ if (agentStubMap) {
+ task = agentStubMap.get(seriesModel.uid);
+ }
+ }
+
+ return task;
+ }
+ }
+
+ var ComponentView =
+ /** @class */
+ function () {
+ function ComponentView() {
+ this.group = new Group();
+ this.uid = getUID('viewComponent');
+ }
+
+ ComponentView.prototype.init = function (ecModel, api) {};
+
+ ComponentView.prototype.render = function (model, ecModel, api, payload) {};
+
+ ComponentView.prototype.dispose = function (ecModel, api) {};
+
+ ComponentView.prototype.updateView = function (model, ecModel, api, payload) {// Do nothing;
+ };
+
+ 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
+ */
+
+
+ ComponentView.prototype.blurSeries = function (seriesModels, ecModel) {// Do nothing;
+ };
+
+ return ComponentView;
+ }();
+ enableClassExtend(ComponentView);
+ enableClassManagement(ComponentView);
+
+ /**
+ * @return {string} If large mode changed, return string 'reset';
+ */
+
+ function createRenderPlanner() {
+ var inner = makeInner();
+ return function (seriesModel) {
+ var fields = inner(seriesModel);
+ var pipelineContext = seriesModel.pipelineContext;
+ var originalLarge = !!fields.large;
+ var originalProgressive = !!fields.progressiveRender; // 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';
+ };
+ }
+
+ var inner$2 = makeInner();
+ var renderPlanner = createRenderPlanner();
+
+ 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
+ };
+ }
+
+ ChartView.prototype.init = function (ecModel, api) {};
+
+ ChartView.prototype.render = function (seriesModel, ecModel, api, payload) {};
+ /**
+ * Highlight series or specified data item.
+ */
+
+
+ ChartView.prototype.highlight = function (seriesModel, ecModel, api, payload) {
+ toggleHighlight(seriesModel.getData(), payload, 'emphasis');
+ };
+ /**
+ * Downplay series or specified data item.
+ */
+
+
+ ChartView.prototype.downplay = function (seriesModel, ecModel, api, payload) {
+ toggleHighlight(seriesModel.getData(), payload, 'normal');
+ };
+ /**
+ * Remove self.
+ */
+
+
+ ChartView.prototype.remove = function (ecModel, api) {
+ this.group.removeAll();
+ };
+ /**
+ * Dispose self.
+ */
+
+
+ ChartView.prototype.dispose = function (ecModel, api) {};
+
+ ChartView.prototype.updateView = function (seriesModel, ecModel, api, payload) {
+ this.render(seriesModel, ecModel, api, payload);
+ }; // FIXME never used?
+
+
+ ChartView.prototype.updateLayout = function (seriesModel, ecModel, api, payload) {
+ this.render(seriesModel, ecModel, api, payload);
+ }; // FIXME never used?
+
+
+ ChartView.prototype.updateVisual = function (seriesModel, ecModel, api, payload) {
+ this.render(seriesModel, ecModel, api, payload);
+ };
+
+ ChartView.markUpdateMethod = function (payload, methodName) {
+ inner$2(payload).updateMethod = methodName;
+ };
+
+ ChartView.protoInitialize = function () {
+ var proto = ChartView.prototype;
+ proto.type = 'chart';
+ }();
+
+ return ChartView;
+ }();
+ /**
+ * Set state of single element
+ */
+
+ function elSetState(el, state, highlightDigit) {
+ if (el) {
+ (state === 'emphasis' ? enterEmphasis : leaveEmphasis)(el, highlightDigit);
+ }
+ }
+
+ function toggleHighlight(data, payload, state) {
+ var dataIndex = queryDataIndex(data, payload);
+ var highlightDigit = payload && payload.highlightKey != null ? getHighlightDigit(payload.highlightKey) : null;
+
+ if (dataIndex != null) {
+ each(normalizeToArray(dataIndex), function (dataIdx) {
+ elSetState(data.getItemGraphicEl(dataIdx), state, highlightDigit);
+ });
+ } else {
+ data.eachItemGraphicEl(function (el) {
+ elSetState(el, state, highlightDigit);
+ });
+ }
+ }
+
+ enableClassExtend(ChartView, ['dispose']);
+ enableClassManagement(ChartView);
+
+ function renderTaskPlan(context) {
+ return renderPlanner(context.model);
+ }
+
+ function renderTaskReset(context) {
+ var seriesModel = context.model;
+ var ecModel = context.ecModel;
+ var api = context.api;
+ var payload = context.payload; // FIXME: remove updateView updateVisual
+
+ var progressiveRender = seriesModel.pipelineContext.progressiveRender;
+ var view = context.view;
+ var updateMethod = payload && inner$2(payload).updateMethod;
+ var methodName = progressiveRender ? 'incrementalPrepareRender' : updateMethod && view[updateMethod] ? updateMethod // `appendData` is also supported when data amount
+ // is less than progressive threshold.
+ : 'render';
+
+ if (methodName !== 'render') {
+ view[methodName](seriesModel, ecModel, api, payload);
+ }
+
+ 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.
+ */
+
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ var ORIGIN_METHOD = '\0__throttleOriginMethod';
+ var RATE = '\0__throttleRate';
+ var THROTTLE_TYPE = '\0__throttleType';
+ /**
+ * @public
+ * @param {(Function)} fn
+ * @param {number} [delay=0] Unit: ms.
+ * @param {boolean} [debounce=false]
+ * true: If call interval less than `delay`, only the last call works.
+ * false: If call interval less than `delay, call works on fixed rate.
+ * @return {(Function)} throttled fn.
+ */
+
+ function throttle(fn, delay, debounce) {
+ var currCall;
+ var lastCall = 0;
+ 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 || []);
+ }
+
+ var cb = function () {
+ var cbArgs = [];
+
+ for (var _i = 0; _i < arguments.length; _i++) {
+ cbArgs[_i] = arguments[_i];
+ }
+
+ 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
+ */
+
+
+ cb.clear = function () {
+ if (timer) {
+ clearTimeout(timer);
+ timer = null;
+ }
+ };
+ /**
+ * Enable debounce once.
+ */
+
+
+ cb.debounceNextCall = function (debounceDelay) {
+ debounceNextCall = debounceDelay;
+ };
+
+ return cb;
+ }
+ /**
+ * Create throttle method or update throttle rate.
+ *
+ * @example
+ * ComponentView.prototype.render = function () {
+ * ...
+ * throttle.createOrUpdate(
+ * this,
+ * '_dispatchAction',
+ * this.model.get('throttle'),
+ * 'fixRate'
+ * );
+ * };
+ * ComponentView.prototype.remove = function () {
+ * throttle.clear(this, '_dispatchAction');
+ * };
+ * ComponentView.prototype.dispose = function () {
+ * throttle.clear(this, '_dispatchAction');
+ * };
+ *
+ */
+
+ function createOrUpdate(obj, fnAttr, rate, throttleType) {
+ var fn = obj[fnAttr];
+
+ if (!fn) {
+ return;
+ }
+
+ var originFn = fn[ORIGIN_METHOD] || fn;
+ var lastThrottleType = fn[THROTTLE_TYPE];
+ var lastRate = fn[RATE];
+
+ if (lastRate !== rate || lastThrottleType !== throttleType) {
+ if (rate == null || !throttleType) {
+ return obj[fnAttr] = originFn;
+ }
+
+ fn = obj[fnAttr] = throttle(originFn, rate, throttleType === 'debounce');
+ fn[ORIGIN_METHOD] = originFn;
+ fn[THROTTLE_TYPE] = throttleType;
+ fn[RATE] = rate;
+ }
+
+ return fn;
+ }
+ /**
+ * Clear throttle. Example see throttle.createOrUpdate.
+ */
+
+ function clear(obj, fnAttr) {
+ var fn = obj[fnAttr];
+
+ if (fn && fn[ORIGIN_METHOD]) {
+ obj[fnAttr] = fn[ORIGIN_METHOD];
+ }
+ }
+
+ 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];
+
+ if (!styleMapper) {
+ console.warn("Unkown style type '" + stylePath + "'.");
+ return defaultStyleMappers.itemStyle;
+ }
+
+ return styleMapper;
+ }
+
+ 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;
+ }
+
+ var seriesStyleTask = {
+ createOnAllSeries: true,
+ performRawSeries: true,
+ reset: function (seriesModel, ecModel) {
+ var data = seriesModel.getData();
+ var stylePath = seriesModel.visualStyleAccessPath || 'itemStyle'; // Set in itemStyle
+
+ var styleModel = seriesModel.getModel(stylePath);
+ var getStyle = getStyleMapper(seriesModel, stylePath);
+ var globalStyle = getStyle(styleModel);
+ var decalOption = styleModel.getShallow('decal');
+
+ if (decalOption) {
+ data.setVisual('decal', decalOption);
+ decalOption.dirty = true;
+ } // TODO
+
+
+ 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 (!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 data = seriesModel.getData();
+ var stylePath = seriesModel.visualStyleAccessPath || 'itemStyle'; // Set in itemStyle
+
+ var getStyle = getStyleMapper(seriesModel, stylePath);
+ var colorKey = data.getVisual('drawType');
+ return {
+ dataEach: data.hasItemOption ? function (data, idx) {
+ // Not use getItemModel for performance considuration
+ var rawItem = data.getRawDataItem(idx);
+
+ if (rawItem && rawItem[stylePath]) {
+ sharedModel.option = rawItem[stylePath];
+ var style = getStyle(sharedModel);
+ var existsStyle = data.ensureUniqueItemVisual(idx, 'style');
+ extend(existsStyle, style);
+
+ if (sharedModel.option.decal) {
+ data.setItemVisual(idx, 'decal', sharedModel.option.decal);
+ sharedModel.option.decal.dirty = true;
+ }
+
+ 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 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;
+ }
+
+ 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;
+ }
+
+ 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.
+
+ 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 PI$3 = Math.PI;
+ /**
+ * @param {module:echarts/ExtensionAPI} api
+ * @param {Object} [opts]
+ * @param {string} [opts.text]
+ * @param {string} [opts.color]
+ * @param {string} [opts.textColor]
+ * @return {module:zrender/Element}
+ */
+
+ function defaultLoading(api, opts) {
+ opts = opts || {};
+ defaults(opts, {
+ 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;
+
+ 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
+
+
+ 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 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 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);
+ }
+
+ 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();
+ });
+ }; // If seriesModel provided, incremental threshold is check by series data.
+
+
+ Scheduler.prototype.getPerformArgs = function (task, isBlock) {
+ // For overall task
+ if (!task.__pipeline) {
+ return;
+ }
+
+ var pipeline = this._pipelineMap.get(task.__pipeline.id);
+
+ 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
+ };
+ };
+
+ Scheduler.prototype.getPipeline = function (pipelineId) {
+ return this._pipelineMap.get(pipelineId);
+ };
+ /**
+ * Current, progressive rendering starts from visual and layout.
+ * Always detect render mode in the same stage, avoiding that incorrect
+ * detection caused by data filtering.
+ * Caution:
+ * `updateStreamModes` use `seriesModel.getData()`.
+ */
+
+
+ Scheduler.prototype.updateStreamModes = function (seriesModel, view) {
+ var pipeline = this._pipelineMap.get(seriesModel.uid);
+
+ var data = seriesModel.getData();
+ var dataLen = data.count(); // `progressiveRender` means that can render progressively in each
+ // animation frame. Note that some types of series do not provide
+ // `view.incrementalPrepareRender` but support `chart.appendData`. We
+ // use the term `incremental` but not `progressive` to describe the
+ // case that `chart.appendData`.
+
+ var progressiveRender = pipeline.progressiveEnabled && view.incrementalPrepareRender && dataLen >= pipeline.threshold;
+ var large = seriesModel.get('large') && dataLen >= seriesModel.get('largeThreshold'); // TODO: modDataCount should not updated if `appendData`, otherwise cause whole repaint.
+ // see `test/candlestick-large3.html`
+
+ var modDataCount = seriesModel.get('progressiveChunkMode') === 'mod' ? dataLen : null;
+ seriesModel.pipelineContext = pipeline.context = {
+ progressiveRender: progressiveRender,
+ modDataCount: modDataCount,
+ large: large
+ };
+ };
+
+ 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
+ });
+
+ scheduler._pipe(seriesModel, seriesModel.dataTask);
+ });
+ };
+
+ 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 = '';
+
+ if ("development" !== 'production') {
+ // Currently do not need to support to sepecify them both.
+ errMsg = '"reset" and "overallReset" must not be both specified.';
+ }
+
+ assert(!(handler.reset && handler.overallReset), errMsg);
+ handler.reset && this._createSeriesStageTask(handler, record, ecModel, api);
+ handler.overallReset && this._createOverallStageTask(handler, record, ecModel, api);
+ }, this);
+ };
+
+ 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;
+
+ this._pipe(model, renderTask);
+ };
+
+ 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);
+ };
+
+ 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);
+
+ 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.
+
+ agentStubMap.each(function (stub) {
+ stub.perform(performArgs_1);
+ });
+
+ if (overallTask.perform(performArgs_1)) {
+ unfinished = true;
+ }
+ } else if (seriesTaskMap) {
+ seriesTaskMap.each(function (task, pipelineId) {
+ if (needSetDirty(opt, task)) {
+ task.dirty();
+ }
+
+ var performArgs = scheduler.getPerformArgs(task, opt.block); // FIXME
+ // if intending to decalare `performRawSeries` in handlers, only
+ // stream-independent (specifically, data item independent) operations can be
+ // performed. Because is a series is filtered, most of the tasks will not
+ // be performed. A stream-dependent operation probably cause wrong biz logic.
+ // Perhaps we should not provide a separate callback for this case instead
+ // of providing the config `performRawSeries`. The stream-dependent operaions
+ // and stream-independent operations should better not be mixed.
+
+ performArgs.skip = !stageHandler.performRawSeries && ecModel.isSeriesFiltered(task.context.model);
+ scheduler.updatePayload(task, payload);
+
+ if (task.perform(performArgs)) {
+ unfinished = true;
+ }
+ });
+ }
+ });
+
+ function needSetDirty(opt, task) {
+ return opt.setDirty && (!opt.dirtyMap || opt.dirtyMap.get(task.__pipeline.id));
+ }
+
+ this.unfinished = unfinished || this.unfinished;
+ };
+
+ Scheduler.prototype.performSeriesTasks = function (ecModel) {
+ var unfinished;
+ ecModel.eachSeries(function (seriesModel) {
+ // Progress to the end for dataInit and dataRestore.
+ unfinished = seriesModel.dataTask.perform() || unfinished;
+ });
+ this.unfinished = unfinished || this.unfinished;
+ };
+
+ Scheduler.prototype.plan = function () {
+ // Travel pipelines, check block.
+ this._pipelineMap.each(function (pipeline) {
+ var task = pipeline.tail;
+
+ do {
+ if (task.__block) {
+ pipeline.blockIndex = task.__idxInPipeline;
+ break;
+ }
+
+ task = task.getUpstream();
+ } while (task);
+ });
+ };
+
+ Scheduler.prototype.updatePayload = function (task, payload) {
+ payload !== 'remain' && (task.context.payload = payload);
+ };
+
+ 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.
+
+ if (stageHandler.createOnAllSeries) {
+ ecModel.eachRawSeries(create);
+ } else if (seriesType) {
+ ecModel.eachRawSeriesByType(seriesType, create);
+ } else if (getTargetSeries) {
+ getTargetSeries(ecModel, api).each(create);
+ }
+
+ 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
+ };
+
+ scheduler._pipe(seriesModel, task);
+ }
+ };
+
+ 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.
+
+ var errMsg = '';
+
+ if ("development" !== 'production') {
+ errMsg = '"createOnAllSeries" do not supported for "overallReset", ' + 'becuase it will block all streams.';
+ }
+
+ assert(!stageHandler.createOnAllSeries, errMsg);
+
+ 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
+
+ };
+ stub.agent = overallTask;
+ stub.__block = overallProgress;
+
+ scheduler._pipe(seriesModel, stub);
+ }
+
+ if (shouldOverallTaskDirty) {
+ overallTask.dirty();
+ }
+ };
+
+ Scheduler.prototype._pipe = function (seriesModel, task) {
+ var pipelineId = seriesModel.uid;
+
+ var pipeline = this._pipelineMap.get(pipelineId);
+
+ !pipeline.head && (pipeline.head = task);
+ pipeline.tail && pipeline.tail.pipe(task);
+ pipeline.tail = task;
+ task.__idxInPipeline = pipeline.count++;
+ task.__pipeline = pipeline;
+ };
+
+ 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;
+ }();
+
+ function overallTaskReset(context) {
+ context.overallReset(context.ecModel, context.api, context.payload);
+ }
+
+ function stubReset(context) {
+ return context.overallProgress && stubProgress;
+ }
+
+ function stubProgress() {
+ this.agent.dirty();
+ this.getDownstream().dirty();
+ }
+
+ function stubOnDirty() {
+ this.agent && this.agent.dirty();
+ }
+
+ 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();
+ }
+
+ 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;
+ }
+
+ var singleSeriesTaskProgress = makeSeriesTaskProgress(0);
+
+ function makeSeriesTaskProgress(resetDefineIdx) {
+ return function (params, context) {
+ var data = context.data;
+ var resetDefine = context.resetDefines[resetDefineIdx];
+
+ 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);
+ }
+ };
+ }
+
+ function seriesTaskCount(context) {
+ return context.data.count();
+ }
+ /**
+ * Only some legacy stage handlers (usually in echarts extensions) are pure function.
+ * To ensure that they can work normally, they should work in block mode, that is,
+ * they should not be started util the previous tasks finished. So they cause the
+ * progressive rendering disabled. We try to detect the series type, to narrow down
+ * the block range to only the series type they concern, but not all series.
+ */
+
+
+ function detectSeriseType(legacyFunc) {
+ seriesType = null;
+
+ try {
+ // Assume there is no async when calling `eachSeriesByType`.
+ legacyFunc(ecModelMock, apiMock);
+ } catch (e) {}
+
+ return seriesType;
+ }
+
+ var ecModelMock = {};
+ var apiMock = {};
+ var seriesType;
+ mockMethods(ecModelMock, GlobalModel);
+ mockMethods(apiMock, ExtensionAPI);
+
+ ecModelMock.eachSeriesByType = ecModelMock.eachRawSeriesByType = function (type) {
+ seriesType = type;
+ };
+
+ ecModelMock.eachComponent = function (cond) {
+ if (cond.mainType === 'series' && cond.subType) {
+ seriesType = cond.subType;
+ }
+ };
+
+ function mockMethods(target, Clz) {
+ /* eslint-disable */
+ for (var name_1 in Clz.prototype) {
+ // Do not use hasOwnProperty
+ target[name_1] = noop;
+ }
+ /* eslint-enable */
+
+ }
+
+ /*
+ * 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.
+ */
+
+ /*
+ * 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.
+ */
+
+
+ /**
+ * AUTO-GENERATED FILE. DO NOT MODIFY.
+ */
+
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ var contrastColor = '#B9B8CE';
+ var backgroundColor = '#100C2A';
+
+ 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'
+ }
+ }
+ };
+ };
+
+ 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)']]
+ }
+ },
+ axisLabel: {
+ color: contrastColor
+ },
+ detail: {
+ color: '#EEF1FA'
+ }
+ },
+ candlestick: {
+ itemStyle: {
+ color: '#f64e56',
+ color0: '#54ea92',
+ borderColor: '#f64e56',
+ borderColor0: '#54ea92' // borderColor: '#ca2824',
+ // borderColor0: '#09a443'
+
+ }
+ }
+ };
+ 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 =
+ /** @class */
+ function () {
+ function ECEventProcessor() {}
+
+ ECEventProcessor.prototype.normalizeQuery = function (query) {
+ var cptQuery = {};
+ var dataQuery = {};
+ var otherQuery = {}; // `query` is `mainType` or `mainType.subType` of component.
+
+ if (isString(query)) {
+ var condCptType = parseClassType(query); // `.main` and `.sub` may be ''.
+
+ 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 i = 0; i < suffixes_1.length; i++) {
+ var propSuffix = suffixes_1[i];
+ var suffixPos = key.lastIndexOf(propSuffix);
+
+ if (suffixPos > 0 && suffixPos === key.length - propSuffix.length) {
+ var mainType = key.slice(0, suffixPos); // Consider `dataIndex`.
+
+ if (mainType !== 'data') {
+ cptQuery.mainType = mainType;
+ cptQuery[propSuffix.toLowerCase()] = val;
+ reserved = true;
+ }
+ }
+ }
+
+ if (dataKeys_1.hasOwnProperty(key)) {
+ dataQuery[key] = val;
+ reserved = true;
+ }
+
+ if (!reserved) {
+ otherQuery[key] = val;
+ }
+ });
+ }
+
+ 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 (!eventInfo) {
+ return true;
+ }
+
+ var targetEl = eventInfo.targetEl;
+ var packedEvent = eventInfo.packedEvent;
+ var model = eventInfo.model;
+ var view = eventInfo.view; // For event like 'globalout'.
+
+ if (!model || !view) {
+ return true;
+ }
+
+ 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];
+ }
+ };
+
+ ECEventProcessor.prototype.afterTrigger = function () {
+ // Make sure the eventInfo wont be used in next trigger.
+ this.eventInfo = null;
+ };
+
+ return ECEventProcessor;
+ }();
+
+ var seriesSymbolTask = {
+ createOnAllSeries: true,
+ // For legend.
+ performRawSeries: true,
+ reset: function (seriesModel, ecModel) {
+ var data = seriesModel.getData();
+
+ if (seriesModel.legendIcon) {
+ data.setVisual('legendIcon', seriesModel.legendIcon);
+ }
+
+ 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
+
+ if (ecModel.isSeriesFiltered(seriesModel)) {
+ return;
+ }
+
+ 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));
+ }
+
+ 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
+
+
+ if (ecModel.isSeriesFiltered(seriesModel)) {
+ return;
+ }
+
+ var data = seriesModel.getData();
+
+ 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
+
+ if (itemSymbolType != null) {
+ data.setItemVisual(idx, 'symbol', itemSymbolType);
+ }
+
+ if (itemSymbolSize != null) {
+ // PENDING Transform symbolSize ?
+ data.setItemVisual(idx, 'symbolSize', itemSymbolSize);
+ }
+
+ if (itemSymbolRotate != null) {
+ data.setItemVisual(idx, 'symbolRotate', itemSymbolRotate);
+ }
+
+ if (itemSymbolOffset != null) {
+ data.setItemVisual(idx, 'symbolOffset', itemSymbolOffset);
+ }
+
+ if (itemSymbolKeepAspect != null) {
+ data.setItemVisual(idx, 'symbolKeepAspect', itemSymbolKeepAspect);
+ }
+ }
+
+ return {
+ dataEach: data.hasItemOption ? dataEach : null
+ };
+ }
+ };
+
+ /*
+ * 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.
+ */
+
+ /*
+ * 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')];
+
+ case 'opacity':
+ return data.getItemVisual(dataIndex, 'style').opacity;
+
+ case 'symbol':
+ case 'symbolSize':
+ case 'liftZ':
+ return data.getItemVisual(dataIndex, key);
+
+ default:
+ if ("development" !== 'production') {
+ console.warn("Unknown visual type " + key);
+ }
+
+ }
+ }
+ 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;
+
+ case 'symbol':
+ case 'symbolSize':
+ case 'liftZ':
+ return data.getVisual(key);
+
+ default:
+ if ("development" !== 'production') {
+ console.warn("Unknown visual type " + key);
+ }
+
+ }
+ }
+ function setItemVisualFromData(data, dataIndex, key, value) {
+ switch (key) {
+ case 'color':
+ // Make sure not sharing style object.
+ var style = data.ensureUniqueItemVisual(dataIndex, 'style');
+ style[data.getVisual('drawType')] = value; // Mark the color has been changed, not from palette anymore
+
+ data.setItemVisual(dataIndex, 'colorFromPalette', false);
+ break;
+
+ case 'opacity':
+ data.ensureUniqueItemVisual(dataIndex, 'style').opacity = value;
+ break;
+
+ case 'symbol':
+ case 'symbolSize':
+ case 'liftZ':
+ data.setItemVisual(dataIndex, key, value);
+ break;
+
+ default:
+ if ("development" !== 'production') {
+ console.warn("Unknown visual type " + key);
+ }
+
+ }
+ }
+
+ // Inlucdes: pieSelect, pieUnSelect, pieToggleSelect, mapSelect, mapUnSelect, mapToggleSelect
+
+ function createLegacyDataSelectAction(seriesType, ecRegisterAction) {
+ function getSeriesIndices(ecModel, payload) {
+ var seriesIndices = [];
+ ecModel.eachComponent({
+ mainType: 'series',
+ subType: seriesType,
+ query: payload
+ }, function (seriesModel) {
+ seriesIndices.push(seriesModel.seriesIndex);
+ });
+ return seriesIndices;
+ }
+
+ each([[seriesType + 'ToggleSelect', 'toggleSelect'], [seriesType + 'Select', 'select'], [seriesType + 'UnSelect', 'unselect']], function (eventsMap) {
+ ecRegisterAction(eventsMap[0], function (payload, ecModel, api) {
+ payload = extend({}, payload);
+
+ if ("development" !== 'production') {
+ deprecateReplaceLog(payload.type, eventsMap[1]);
+ }
+
+ api.dispatchAction(extend(payload, {
+ type: eventsMap[1],
+ seriesIndex: getSeriesIndices(ecModel, payload)
+ }));
+ });
+ });
+ }
+
+ function handleSeriesLegacySelectEvents(type, eventPostfix, ecIns, ecModel, payload) {
+ var legacyEventName = type + eventPostfix;
+
+ if (!ecIns.isSilent(legacyEventName)) {
+ if ("development" !== 'production') {
+ deprecateLog("event " + legacyEventName + " is deprecated.");
+ }
+
+ ecModel.eachComponent({
+ mainType: 'series',
+ subType: 'pie'
+ }, function (seriesModel) {
+ var seriesIndex = seriesModel.seriesIndex;
+ var selected = payload.selected;
+
+ for (var i = 0; i < selected.length; i++) {
+ if (selected[i].seriesIndex === seriesIndex) {
+ var data = seriesModel.getData();
+ var dataIndex = queryDataIndex(data, payload.fromActionPayload);
+ ecIns.trigger(legacyEventName, {
+ type: legacyEventName,
+ seriesId: seriesModel.id,
+ name: isArray(dataIndex) ? data.getName(dataIndex[0]) : data.getName(dataIndex),
+ selected: extend({}, seriesModel.option.selectedMap)
+ });
+ }
+ }
+ });
+ }
+ }
+
+ function handleLegacySelectEvents(messageCenter, ecIns, api) {
+ messageCenter.on('selectchanged', function (params) {
+ var ecModel = api.getModel();
+
+ if (params.isFromClick) {
+ handleSeriesLegacySelectEvents('map', 'selectchanged', ecIns, ecModel, params);
+ handleSeriesLegacySelectEvents('pie', 'selectchanged', ecIns, ecModel, params);
+ } else if (params.fromAction === 'select') {
+ handleSeriesLegacySelectEvents('map', 'selected', ecIns, ecModel, params);
+ handleSeriesLegacySelectEvents('pie', 'selected', ecIns, ecModel, params);
+ } else if (params.fromAction === 'unselect') {
+ handleSeriesLegacySelectEvents('map', 'unselected', ecIns, ecModel, params);
+ handleSeriesLegacySelectEvents('pie', 'unselected', ecIns, ecModel, params);
+ }
+ });
+ }
+
+ /*
+ * 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.
+ */
+
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ function findEventDispatcher(target, det, returnFirstMatch) {
+ var found;
+
+ while (target) {
+ if (det(target)) {
+ found = target;
+
+ if (returnFirstMatch) {
+ break;
+ }
+ }
+
+ target = target.__hostTarget || target.parent;
+ }
+
+ return found;
+ }
+
+ var wmUniqueIndex = Math.round(Math.random() * 9);
+ var supportDefineProperty = typeof Object.defineProperty === 'function';
+ var WeakMap = (function () {
+ function WeakMap() {
+ this._id = '__ec_inner_' + wmUniqueIndex++;
+ }
+ WeakMap.prototype.get = function (key) {
+ return this._guard(key)[this._id];
+ };
+ WeakMap.prototype.set = function (key, value) {
+ var target = this._guard(key);
+ if (supportDefineProperty) {
+ Object.defineProperty(target, this._id, {
+ value: value,
+ enumerable: false,
+ configurable: true
+ });
+ }
+ else {
+ target[this._id] = value;
+ }
+ return this;
+ };
+ WeakMap.prototype["delete"] = function (key) {
+ if (this.has(key)) {
+ delete this._guard(key)[this._id];
+ return true;
+ }
+ return false;
+ };
+ WeakMap.prototype.has = function (key) {
+ return !!this._guard(key)[this._id];
+ };
+ WeakMap.prototype._guard = function (key) {
+ if (key !== Object(key)) {
+ throw TypeError('Value of WeakMap is not a non-null object.');
+ }
+ return key;
+ };
+ return WeakMap;
+ }());
+
+ /**
+ * Triangle shape
+ * @inner
+ */
+
+ var Triangle = Path.extend({
+ type: 'triangle',
+ shape: {
+ cx: 0,
+ cy: 0,
+ width: 0,
+ height: 0
+ },
+ buildPath: function (path, shape) {
+ var cx = shape.cx;
+ var cy = shape.cy;
+ var width = shape.width / 2;
+ var height = shape.height / 2;
+ path.moveTo(cx, cy - height);
+ path.lineTo(cx + width, cy + height);
+ path.lineTo(cx - width, cy + height);
+ path.closePath();
+ }
+ });
+ /**
+ * Diamond shape
+ * @inner
+ */
+
+ var Diamond = Path.extend({
+ type: 'diamond',
+ shape: {
+ cx: 0,
+ cy: 0,
+ width: 0,
+ height: 0
+ },
+ buildPath: function (path, shape) {
+ var cx = shape.cx;
+ var cy = shape.cy;
+ var width = shape.width / 2;
+ var height = shape.height / 2;
+ path.moveTo(cx, cy - height);
+ path.lineTo(cx + width, cy);
+ path.lineTo(cx, cy + height);
+ path.lineTo(cx - width, cy);
+ path.closePath();
+ }
+ });
+ /**
+ * Pin shape
+ * @inner
+ */
+
+ var Pin = Path.extend({
+ type: 'pin',
+ shape: {
+ // x, y on the cusp
+ x: 0,
+ y: 0,
+ width: 0,
+ height: 0
+ },
+ buildPath: function (path, shape) {
+ var x = shape.x;
+ var y = shape.y;
+ var w = shape.width / 5 * 3; // Height must be larger than width
+
+ var h = Math.max(w, shape.height);
+ var r = w / 2; // Dist on y with tangent point and circle center
+
+ var dy = r * r / (h - r);
+ var cy = y - h + r + dy;
+ var angle = Math.asin(dy / r); // Dist on x with tangent point and circle center
+
+ var dx = Math.cos(angle) * r;
+ var tanX = Math.sin(angle);
+ var tanY = Math.cos(angle);
+ var cpLen = r * 0.6;
+ var cpLen2 = r * 0.7;
+ path.moveTo(x - dx, cy + dy);
+ path.arc(x, cy, r, Math.PI - angle, Math.PI * 2 + angle);
+ path.bezierCurveTo(x + dx - tanX * cpLen, cy + dy + tanY * cpLen, x, y - cpLen2, x, y);
+ path.bezierCurveTo(x, y - cpLen2, x - dx + tanX * cpLen, cy + dy + tanY * cpLen, x - dx, cy + dy);
+ path.closePath();
+ }
+ });
+ /**
+ * Arrow shape
+ * @inner
+ */
+
+ var Arrow = Path.extend({
+ type: 'arrow',
+ shape: {
+ x: 0,
+ y: 0,
+ width: 0,
+ height: 0
+ },
+ buildPath: function (ctx, shape) {
+ var height = shape.height;
+ var width = shape.width;
+ var x = shape.x;
+ var y = shape.y;
+ var dx = width / 3 * 2;
+ ctx.moveTo(x, y);
+ ctx.lineTo(x + dx, y + height);
+ ctx.lineTo(x, y + height / 4 * 3);
+ ctx.lineTo(x - dx, y + height);
+ ctx.lineTo(x, y);
+ ctx.closePath();
+ }
+ });
+ /**
+ * Map of path contructors
+ */
+ // TODO Use function to build symbol path.
+
+ var symbolCtors = {
+ line: Line,
+ rect: Rect,
+ roundRect: Rect,
+ square: Rect,
+ circle: Circle,
+ diamond: Diamond,
+ pin: Pin,
+ arrow: Arrow,
+ triangle: Triangle
+ };
+ var symbolShapeMakers = {
+ line: function (x, y, w, h, shape) {
+ shape.x1 = x;
+ shape.y1 = y + h / 2;
+ shape.x2 = x + w;
+ shape.y2 = y + h / 2;
+ },
+ rect: function (x, y, w, h, shape) {
+ shape.x = x;
+ shape.y = y;
+ shape.width = w;
+ shape.height = h;
+ },
+ roundRect: function (x, y, w, h, shape) {
+ shape.x = x;
+ shape.y = y;
+ shape.width = w;
+ shape.height = h;
+ shape.r = Math.min(w, h) / 4;
+ },
+ square: function (x, y, w, h, shape) {
+ var size = Math.min(w, h);
+ shape.x = x;
+ shape.y = y;
+ shape.width = size;
+ shape.height = size;
+ },
+ circle: function (x, y, w, h, shape) {
+ // Put circle in the center of square
+ shape.cx = x + w / 2;
+ shape.cy = y + h / 2;
+ shape.r = Math.min(w, h) / 2;
+ },
+ diamond: function (x, y, w, h, shape) {
+ shape.cx = x + w / 2;
+ shape.cy = y + h / 2;
+ shape.width = w;
+ shape.height = h;
+ },
+ pin: function (x, y, w, h, shape) {
+ shape.x = x + w / 2;
+ shape.y = y + h / 2;
+ shape.width = w;
+ shape.height = h;
+ },
+ arrow: function (x, y, w, h, shape) {
+ shape.x = x + w / 2;
+ shape.y = y + h / 2;
+ shape.width = w;
+ shape.height = h;
+ },
+ triangle: function (x, y, w, h, shape) {
+ shape.cx = x + w / 2;
+ shape.cy = y + h / 2;
+ shape.width = w;
+ shape.height = h;
+ }
+ };
+ var symbolBuildProxies = {};
+ each(symbolCtors, function (Ctor, name) {
+ symbolBuildProxies[name] = new Ctor();
+ });
+ var SymbolClz = Path.extend({
+ type: 'symbol',
+ shape: {
+ symbolType: '',
+ x: 0,
+ y: 0,
+ width: 0,
+ height: 0
+ },
+ calculateTextPosition: function (out, config, rect) {
+ var res = calculateTextPosition(out, config, rect);
+ var shape = this.shape;
+
+ if (shape && shape.symbolType === 'pin' && config.position === 'inside') {
+ res.y = rect.y + rect.height * 0.4;
+ }
+
+ return res;
+ },
+ buildPath: function (ctx, shape, inBundle) {
+ var symbolType = shape.symbolType;
+
+ if (symbolType !== 'none') {
+ var proxySymbol = symbolBuildProxies[symbolType];
+
+ if (!proxySymbol) {
+ // Default rect
+ symbolType = 'rect';
+ proxySymbol = symbolBuildProxies[symbolType];
+ }
+
+ symbolShapeMakers[symbolType](shape.x, shape.y, shape.width, shape.height, proxySymbol.shape);
+ proxySymbol.buildPath(ctx, proxySymbol.shape, inBundle);
+ }
+ }
+ }); // Provide setColor helper method to avoid determine if set the fill or stroke outside
+
+ function symbolPathSetColor(color, innerColor) {
+ if (this.type !== 'image') {
+ var symbolStyle = this.style;
+
+ if (this.__isEmptyBrush) {
+ symbolStyle.stroke = color;
+ symbolStyle.fill = innerColor || '#fff'; // TODO Same width with lineStyle in LineView
+
+ symbolStyle.lineWidth = 2;
+ } else if (this.shape.symbolType === 'line') {
+ symbolStyle.stroke = color;
+ } else {
+ symbolStyle.fill = color;
+ }
+
+ this.markRedraw();
+ }
+ }
+ /**
+ * Create a symbol element with given symbol configuration: shape, x, y, width, height, color
+ */
+
+
+ function createSymbol(symbolType, x, y, w, h, color, // whether to keep the ratio of w/h,
+ keepAspect) {
+ // TODO Support image object, DynamicImage.
+ var isEmpty = symbolType.indexOf('empty') === 0;
+
+ if (isEmpty) {
+ symbolType = symbolType.substr(5, 1).toLowerCase() + symbolType.substr(6);
+ }
+
+ var symbolPath;
+
+ if (symbolType.indexOf('image://') === 0) {
+ symbolPath = makeImage(symbolType.slice(8), new BoundingRect(x, y, w, h), keepAspect ? 'center' : 'cover');
+ } else if (symbolType.indexOf('path://') === 0) {
+ symbolPath = makePath(symbolType.slice(7), {}, new BoundingRect(x, y, w, h), keepAspect ? 'center' : 'cover');
+ } else {
+ symbolPath = new SymbolClz({
+ shape: {
+ symbolType: symbolType,
+ x: x,
+ y: y,
+ width: w,
+ height: h
+ }
+ });
+ }
+
+ symbolPath.__isEmptyBrush = isEmpty; // TODO Should deprecate setColor
+
+ symbolPath.setColor = symbolPathSetColor;
+
+ if (color) {
+ symbolPath.setColor(color);
+ }
+
+ 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;
+ var x2 = obj.x2 == null ? 1 : obj.x2;
+ var y = obj.y == null ? 0 : obj.y;
+ var y2 = obj.y2 == null ? 0 : obj.y2;
+ if (!obj.global) {
+ x = x * rect.width + rect.x;
+ x2 = x2 * rect.width + rect.x;
+ y = y * rect.height + rect.y;
+ y2 = y2 * rect.height + rect.y;
+ }
+ x = isNaN(x) ? 0 : x;
+ x2 = isNaN(x2) ? 1 : x2;
+ y = isNaN(y) ? 0 : y;
+ y2 = isNaN(y2) ? 0 : y2;
+ var canvasGradient = ctx.createLinearGradient(x, y, x2, y2);
+ return canvasGradient;
+ }
+ function createRadialGradient(ctx, obj, rect) {
+ var width = rect.width;
+ var height = rect.height;
+ var min = Math.min(width, height);
+ var x = obj.x == null ? 0.5 : obj.x;
+ var y = obj.y == null ? 0.5 : obj.y;
+ var r = obj.r == null ? 0.5 : obj.r;
+ if (!obj.global) {
+ x = x * width + rect.x;
+ y = y * height + rect.y;
+ r = r * min;
+ }
+ var canvasGradient = ctx.createRadialGradient(x, y, 0, x, y, r);
+ return canvasGradient;
+ }
+ function getCanvasGradient(ctx, obj, rect) {
+ var canvasGradient = obj.type === 'radial'
+ ? createRadialGradient(ctx, obj, rect)
+ : createLinearGradient(ctx, obj, rect);
+ var colorStops = obj.colorStops;
+ for (var i = 0; i < colorStops.length; i++) {
+ canvasGradient.addColorStop(colorStops[i].offset, colorStops[i].color);
+ }
+ return canvasGradient;
+ }
+ function isClipPathChanged(clipPaths, prevClipPaths) {
+ if (clipPaths === prevClipPaths || (!clipPaths && !prevClipPaths)) {
+ return false;
+ }
+ if (!clipPaths || !prevClipPaths || (clipPaths.length !== prevClipPaths.length)) {
+ return true;
+ }
+ for (var i = 0; i < clipPaths.length; i++) {
+ if (clipPaths[i] !== prevClipPaths[i]) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ function normalizeLineDash(lineType, lineWidth) {
+ if (!lineType || lineType === 'solid' || !(lineWidth > 0)) {
+ return null;
+ }
+ lineWidth = lineWidth || 1;
+ return lineType === 'dashed'
+ ? [4 * lineWidth, 2 * lineWidth]
+ : lineType === 'dotted'
+ ? [lineWidth]
+ : isNumber(lineType)
+ ? [lineType] : isArray(lineType) ? lineType : null;
+ }
+
+ var pathProxyForDraw = new PathProxy(true);
+ function styleHasStroke(style) {
+ 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';
+ }
+ function doFillPath(ctx, style) {
+ if (style.fillOpacity != null && style.fillOpacity !== 1) {
+ var originalGlobalAlpha = ctx.globalAlpha;
+ ctx.globalAlpha = style.fillOpacity * style.opacity;
+ ctx.fill();
+ ctx.globalAlpha = originalGlobalAlpha;
+ }
+ else {
+ ctx.fill();
+ }
+ }
+ function doStrokePath(ctx, style) {
+ if (style.strokeOpacity != null && style.strokeOpacity !== 1) {
+ var originalGlobalAlpha = ctx.globalAlpha;
+ ctx.globalAlpha = style.strokeOpacity * style.opacity;
+ ctx.stroke();
+ ctx.globalAlpha = originalGlobalAlpha;
+ }
+ else {
+ ctx.stroke();
+ }
+ }
+ function createCanvasPattern(ctx, pattern, el) {
+ var image = createOrUpdateImage(pattern.image, pattern.__image, el);
+ if (isImageReady(image)) {
+ var canvasPattern = ctx.createPattern(image, pattern.repeat || 'repeat');
+ if (typeof DOMMatrix === 'function'
+ && canvasPattern.setTransform) {
+ var matrix = new DOMMatrix();
+ matrix.rotateSelf(0, 0, (pattern.rotation || 0) / Math.PI * 180);
+ matrix.scaleSelf((pattern.scaleX || 1), (pattern.scaleY || 1));
+ matrix.translateSelf((pattern.x || 0), (pattern.y || 0));
+ canvasPattern.setTransform(matrix);
+ }
+ return canvasPattern;
+ }
+ }
+ function brushPath(ctx, el, style, inBatch) {
+ var hasStroke = styleHasStroke(style);
+ var hasFill = styleHasFill(style);
+ var strokePercent = style.strokePercent;
+ var strokePart = strokePercent < 1;
+ var firstDraw = !el.path;
+ if ((!el.silent || strokePart) && firstDraw) {
+ el.createPathProxy();
+ }
+ var path = el.path || pathProxyForDraw;
+ if (!inBatch) {
+ var fill = style.fill;
+ var stroke = style.stroke;
+ var hasFillGradient = hasFill && !!fill.colorStops;
+ var hasStrokeGradient = hasStroke && !!stroke.colorStops;
+ var hasFillPattern = hasFill && !!fill.image;
+ var hasStrokePattern = hasStroke && !!stroke.image;
+ var fillGradient = void 0;
+ var strokeGradient = void 0;
+ var fillPattern = void 0;
+ var strokePattern = void 0;
+ var rect = void 0;
+ if (hasFillGradient || hasStrokeGradient) {
+ rect = el.getBoundingRect();
+ }
+ if (hasFillGradient) {
+ fillGradient = el.__dirty
+ ? getCanvasGradient(ctx, fill, rect)
+ : el.__canvasFillGradient;
+ el.__canvasFillGradient = fillGradient;
+ }
+ if (hasStrokeGradient) {
+ strokeGradient = el.__dirty
+ ? getCanvasGradient(ctx, stroke, rect)
+ : el.__canvasStrokeGradient;
+ el.__canvasStrokeGradient = strokeGradient;
+ }
+ if (hasFillPattern) {
+ fillPattern = (el.__dirty || !el.__canvasFillPattern)
+ ? createCanvasPattern(ctx, fill, el)
+ : el.__canvasFillPattern;
+ el.__canvasFillPattern = fillPattern;
+ }
+ if (hasStrokePattern) {
+ strokePattern = (el.__dirty || !el.__canvasStrokePattern)
+ ? createCanvasPattern(ctx, stroke, el)
+ : el.__canvasStrokePattern;
+ el.__canvasStrokePattern = fillPattern;
+ }
+ if (hasFillGradient) {
+ ctx.fillStyle = fillGradient;
+ }
+ else if (hasFillPattern) {
+ if (fillPattern) {
+ ctx.fillStyle = fillPattern;
+ }
+ else {
+ hasFill = false;
+ }
+ }
+ if (hasStrokeGradient) {
+ ctx.strokeStyle = strokeGradient;
+ }
+ else if (hasStrokePattern) {
+ if (strokePattern) {
+ ctx.strokeStyle = strokePattern;
+ }
+ else {
+ hasStroke = false;
+ }
+ }
+ }
+ var lineDash = style.lineDash && style.lineWidth > 0 && normalizeLineDash(style.lineDash, style.lineWidth);
+ var lineDashOffset = style.lineDashOffset;
+ var ctxLineDash = !!ctx.setLineDash;
+ var scale = el.getGlobalScale();
+ path.setScale(scale[0], scale[1], el.segmentIgnoreThreshold);
+ if (lineDash) {
+ var lineScale_1 = (style.strokeNoScale && el.getLineScale) ? el.getLineScale() : 1;
+ if (lineScale_1 && lineScale_1 !== 1) {
+ lineDash = map(lineDash, function (rawVal) {
+ return rawVal / lineScale_1;
+ });
+ lineDashOffset /= lineScale_1;
+ }
+ }
+ var needsRebuild = true;
+ if (firstDraw || (el.__dirty & SHAPE_CHANGED_BIT)
+ || (lineDash && !ctxLineDash && hasStroke)) {
+ path.setDPR(ctx.dpr);
+ if (strokePart) {
+ path.setContext(null);
+ }
+ else {
+ path.setContext(ctx);
+ needsRebuild = false;
+ }
+ path.reset();
+ if (lineDash && !ctxLineDash) {
+ path.setLineDash(lineDash);
+ path.setLineDashOffset(lineDashOffset);
+ }
+ el.buildPath(path, el.shape, inBatch);
+ path.toStatic();
+ el.pathUpdated();
+ }
+ if (needsRebuild) {
+ path.rebuildPath(ctx, strokePart ? strokePercent : 1);
+ }
+ if (lineDash && ctxLineDash) {
+ ctx.setLineDash(lineDash);
+ ctx.lineDashOffset = lineDashOffset;
+ }
+ if (!inBatch) {
+ if (style.strokeFirst) {
+ if (hasStroke) {
+ doStrokePath(ctx, style);
+ }
+ if (hasFill) {
+ doFillPath(ctx, style);
+ }
+ }
+ else {
+ if (hasFill) {
+ doFillPath(ctx, style);
+ }
+ if (hasStroke) {
+ doStrokePath(ctx, style);
+ }
+ }
+ }
+ if (lineDash && ctxLineDash) {
+ ctx.setLineDash([]);
+ }
+ }
+ function brushImage(ctx, el, style) {
+ var image = el.__image = createOrUpdateImage(style.image, el.__image, el, el.onload);
+ if (!image || !isImageReady(image)) {
+ return;
+ }
+ var x = style.x || 0;
+ var y = style.y || 0;
+ var width = el.getWidth();
+ var height = el.getHeight();
+ var aspect = image.width / image.height;
+ if (width == null && height != null) {
+ width = height * aspect;
+ }
+ else if (height == null && width != null) {
+ height = width / aspect;
+ }
+ else if (width == null && height == null) {
+ width = image.width;
+ height = image.height;
+ }
+ if (style.sWidth && style.sHeight) {
+ var sx = style.sx || 0;
+ var sy = style.sy || 0;
+ ctx.drawImage(image, sx, sy, style.sWidth, style.sHeight, x, y, width, height);
+ }
+ else if (style.sx && style.sy) {
+ var sx = style.sx;
+ var sy = style.sy;
+ var sWidth = width - sx;
+ var sHeight = height - sy;
+ ctx.drawImage(image, sx, sy, sWidth, sHeight, x, y, width, height);
+ }
+ else {
+ ctx.drawImage(image, x, y, width, height);
+ }
+ }
+ function brushText(ctx, el, style) {
+ var text = style.text;
+ text != null && (text += '');
+ if (text) {
+ ctx.font = style.font || DEFAULT_FONT;
+ ctx.textAlign = style.textAlign;
+ ctx.textBaseline = style.textBaseline;
+ var hasLineDash = void 0;
+ if (ctx.setLineDash) {
+ var lineDash = style.lineDash && style.lineWidth > 0 && normalizeLineDash(style.lineDash, style.lineWidth);
+ var lineDashOffset = style.lineDashOffset;
+ if (lineDash) {
+ var lineScale_2 = (style.strokeNoScale && el.getLineScale) ? el.getLineScale() : 1;
+ if (lineScale_2 && lineScale_2 !== 1) {
+ lineDash = map(lineDash, function (rawVal) {
+ return rawVal / lineScale_2;
+ });
+ lineDashOffset /= lineScale_2;
+ }
+ ctx.setLineDash(lineDash);
+ ctx.lineDashOffset = lineDashOffset;
+ hasLineDash = true;
+ }
+ }
+ if (style.strokeFirst) {
+ if (styleHasStroke(style)) {
+ ctx.strokeText(text, style.x, style.y);
+ }
+ if (styleHasFill(style)) {
+ ctx.fillText(text, style.x, style.y);
+ }
+ }
+ else {
+ if (styleHasFill(style)) {
+ ctx.fillText(text, style.x, style.y);
+ }
+ if (styleHasStroke(style)) {
+ ctx.strokeText(text, style.x, style.y);
+ }
+ }
+ if (hasLineDash) {
+ ctx.setLineDash([]);
+ }
+ }
+ }
+ var SHADOW_NUMBER_PROPS = ['shadowBlur', 'shadowOffsetX', 'shadowOffsetY'];
+ var STROKE_PROPS = [
+ ['lineCap', 'butt'], ['lineJoin', 'miter'], ['miterLimit', 10]
+ ];
+ function bindCommonProps(ctx, style, prevStyle, forceSetAll, scope) {
+ var styleChanged = false;
+ if (!forceSetAll) {
+ prevStyle = prevStyle || {};
+ if (style === prevStyle) {
+ return false;
+ }
+ }
+ if (forceSetAll || style.opacity !== prevStyle.opacity) {
+ if (!styleChanged) {
+ flushPathDrawn(ctx, scope);
+ styleChanged = true;
+ }
+ var opacity = Math.max(Math.min(style.opacity, 1), 0);
+ ctx.globalAlpha = isNaN(opacity) ? DEFAULT_COMMON_STYLE.opacity : opacity;
+ }
+ if (forceSetAll || style.blend !== prevStyle.blend) {
+ if (!styleChanged) {
+ flushPathDrawn(ctx, scope);
+ styleChanged = true;
+ }
+ ctx.globalCompositeOperation = style.blend || DEFAULT_COMMON_STYLE.blend;
+ }
+ for (var i = 0; i < SHADOW_NUMBER_PROPS.length; i++) {
+ var propName = SHADOW_NUMBER_PROPS[i];
+ if (forceSetAll || style[propName] !== prevStyle[propName]) {
+ if (!styleChanged) {
+ flushPathDrawn(ctx, scope);
+ styleChanged = true;
+ }
+ ctx[propName] = ctx.dpr * (style[propName] || 0);
+ }
+ }
+ if (forceSetAll || style.shadowColor !== prevStyle.shadowColor) {
+ if (!styleChanged) {
+ flushPathDrawn(ctx, scope);
+ styleChanged = true;
+ }
+ ctx.shadowColor = style.shadowColor || DEFAULT_COMMON_STYLE.shadowColor;
+ }
+ return styleChanged;
+ }
+ function bindPathAndTextCommonStyle(ctx, el, prevEl, forceSetAll, scope) {
+ var style = getStyle(el, scope.inHover);
+ var prevStyle = forceSetAll
+ ? null
+ : (prevEl && getStyle(prevEl, scope.inHover) || {});
+ if (style === prevStyle) {
+ return false;
+ }
+ var styleChanged = bindCommonProps(ctx, style, prevStyle, forceSetAll, scope);
+ if (forceSetAll || style.fill !== prevStyle.fill) {
+ if (!styleChanged) {
+ flushPathDrawn(ctx, scope);
+ styleChanged = true;
+ }
+ isValidStrokeFillStyle(style.fill) && (ctx.fillStyle = style.fill);
+ }
+ if (forceSetAll || style.stroke !== prevStyle.stroke) {
+ if (!styleChanged) {
+ flushPathDrawn(ctx, scope);
+ styleChanged = true;
+ }
+ isValidStrokeFillStyle(style.stroke) && (ctx.strokeStyle = style.stroke);
+ }
+ if (forceSetAll || style.opacity !== prevStyle.opacity) {
+ if (!styleChanged) {
+ flushPathDrawn(ctx, scope);
+ styleChanged = true;
+ }
+ ctx.globalAlpha = style.opacity == null ? 1 : style.opacity;
+ }
+ if (el.hasStroke()) {
+ var lineWidth = style.lineWidth;
+ var newLineWidth = lineWidth / ((style.strokeNoScale && el && el.getLineScale) ? el.getLineScale() : 1);
+ if (ctx.lineWidth !== newLineWidth) {
+ if (!styleChanged) {
+ flushPathDrawn(ctx, scope);
+ styleChanged = true;
+ }
+ ctx.lineWidth = newLineWidth;
+ }
+ }
+ for (var i = 0; i < STROKE_PROPS.length; i++) {
+ var prop = STROKE_PROPS[i];
+ var propName = prop[0];
+ if (forceSetAll || style[propName] !== prevStyle[propName]) {
+ if (!styleChanged) {
+ flushPathDrawn(ctx, scope);
+ styleChanged = true;
+ }
+ ctx[propName] = style[propName] || prop[1];
+ }
+ }
+ return styleChanged;
+ }
+ function bindImageStyle(ctx, el, prevEl, forceSetAll, scope) {
+ return bindCommonProps(ctx, getStyle(el, scope.inHover), prevEl && getStyle(prevEl, scope.inHover), forceSetAll, scope);
+ }
+ function setContextTransform(ctx, el) {
+ var m = el.transform;
+ var dpr = ctx.dpr || 1;
+ if (m) {
+ ctx.setTransform(dpr * m[0], dpr * m[1], dpr * m[2], dpr * m[3], dpr * m[4], dpr * m[5]);
+ }
+ else {
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
+ }
+ }
+ function updateClipStatus(clipPaths, ctx, scope) {
+ var allClipped = false;
+ for (var i = 0; i < clipPaths.length; i++) {
+ var clipPath = clipPaths[i];
+ allClipped = allClipped || clipPath.isZeroArea();
+ setContextTransform(ctx, clipPath);
+ ctx.beginPath();
+ clipPath.buildPath(ctx, clipPath.shape);
+ ctx.clip();
+ }
+ scope.allClipped = allClipped;
+ }
+ function isTransformChanged(m0, m1) {
+ if (m0 && m1) {
+ return m0[0] !== m1[0]
+ || m0[1] !== m1[1]
+ || m0[2] !== m1[2]
+ || m0[3] !== m1[3]
+ || m0[4] !== m1[4]
+ || m0[5] !== m1[5];
+ }
+ else if (!m0 && !m1) {
+ return false;
+ }
+ return true;
+ }
+ var DRAW_TYPE_PATH = 1;
+ var DRAW_TYPE_IMAGE = 2;
+ var DRAW_TYPE_TEXT = 3;
+ var DRAW_TYPE_INCREMENTAL = 4;
+ function canPathBatch(style) {
+ var hasFill = styleHasFill(style);
+ var hasStroke = styleHasStroke(style);
+ return !(style.lineDash
+ || !(+hasFill ^ +hasStroke)
+ || (hasFill && typeof style.fill !== 'string')
+ || (hasStroke && typeof style.stroke !== 'string')
+ || style.strokePercent < 1
+ || style.strokeOpacity < 1
+ || style.fillOpacity < 1);
+ }
+ function flushPathDrawn(ctx, scope) {
+ scope.batchFill && ctx.fill();
+ scope.batchStroke && ctx.stroke();
+ scope.batchFill = '';
+ scope.batchStroke = '';
+ }
+ function getStyle(el, inHover) {
+ return inHover ? (el.__hoverStyle || el.style) : el.style;
+ }
+ function brushSingle(ctx, el) {
+ brush(ctx, el, { inHover: false, viewWidth: 0, viewHeight: 0 }, true);
+ }
+ function brush(ctx, el, scope, isLast) {
+ var m = el.transform;
+ if (!el.shouldBePainted(scope.viewWidth, scope.viewHeight, false, false)) {
+ el.__dirty &= ~REDRAW_BIT;
+ el.__isRendered = false;
+ return;
+ }
+ var clipPaths = el.__clipPaths;
+ var prevElClipPaths = scope.prevElClipPaths;
+ var forceSetTransform = false;
+ var forceSetStyle = false;
+ if (!prevElClipPaths || isClipPathChanged(clipPaths, prevElClipPaths)) {
+ if (prevElClipPaths && prevElClipPaths.length) {
+ flushPathDrawn(ctx, scope);
+ ctx.restore();
+ forceSetStyle = forceSetTransform = true;
+ scope.prevElClipPaths = null;
+ scope.allClipped = false;
+ scope.prevEl = null;
+ }
+ if (clipPaths && clipPaths.length) {
+ flushPathDrawn(ctx, scope);
+ ctx.save();
+ updateClipStatus(clipPaths, ctx, scope);
+ forceSetTransform = true;
+ }
+ scope.prevElClipPaths = clipPaths;
+ }
+ if (scope.allClipped) {
+ el.__isRendered = false;
+ return;
+ }
+ el.beforeBrush && el.beforeBrush();
+ el.innerBeforeBrush();
+ var prevEl = scope.prevEl;
+ if (!prevEl) {
+ forceSetStyle = forceSetTransform = true;
+ }
+ var canBatchPath = el instanceof Path
+ && el.autoBatch
+ && canPathBatch(el.style);
+ if (forceSetTransform || isTransformChanged(m, prevEl.transform)) {
+ flushPathDrawn(ctx, scope);
+ setContextTransform(ctx, el);
+ }
+ else if (!canBatchPath) {
+ flushPathDrawn(ctx, scope);
+ }
+ var style = getStyle(el, scope.inHover);
+ if (el instanceof Path) {
+ if (scope.lastDrawType !== DRAW_TYPE_PATH) {
+ forceSetStyle = true;
+ scope.lastDrawType = DRAW_TYPE_PATH;
+ }
+ bindPathAndTextCommonStyle(ctx, el, prevEl, forceSetStyle, scope);
+ if (!canBatchPath || (!scope.batchFill && !scope.batchStroke)) {
+ ctx.beginPath();
+ }
+ brushPath(ctx, el, style, canBatchPath);
+ if (canBatchPath) {
+ scope.batchFill = style.fill || '';
+ scope.batchStroke = style.stroke || '';
+ }
+ }
+ else {
+ if (el instanceof TSpan) {
+ if (scope.lastDrawType !== DRAW_TYPE_TEXT) {
+ forceSetStyle = true;
+ scope.lastDrawType = DRAW_TYPE_TEXT;
+ }
+ bindPathAndTextCommonStyle(ctx, el, prevEl, forceSetStyle, scope);
+ brushText(ctx, el, style);
+ }
+ else if (el instanceof ZRImage) {
+ if (scope.lastDrawType !== DRAW_TYPE_IMAGE) {
+ forceSetStyle = true;
+ scope.lastDrawType = DRAW_TYPE_IMAGE;
+ }
+ bindImageStyle(ctx, el, prevEl, forceSetStyle, scope);
+ brushImage(ctx, el, style);
+ }
+ else if (el instanceof IncrementalDisplayable) {
+ if (scope.lastDrawType !== DRAW_TYPE_INCREMENTAL) {
+ forceSetStyle = true;
+ scope.lastDrawType = DRAW_TYPE_INCREMENTAL;
+ }
+ brushIncremental(ctx, el, scope);
+ }
+ }
+ if (canBatchPath && isLast) {
+ flushPathDrawn(ctx, scope);
+ }
+ el.innerAfterBrush();
+ el.afterBrush && el.afterBrush();
+ scope.prevEl = el;
+ el.__dirty = 0;
+ el.__isRendered = true;
+ }
+ function brushIncremental(ctx, el, scope) {
+ var displayables = el.getDisplayables();
+ var temporalDisplayables = el.getTemporalDisplayables();
+ ctx.save();
+ var innerScope = {
+ prevElClipPaths: null,
+ prevEl: null,
+ allClipped: false,
+ viewWidth: scope.viewWidth,
+ viewHeight: scope.viewHeight,
+ inHover: scope.inHover
+ };
+ var i;
+ var len;
+ for (i = el.getCursor(), len = displayables.length; i < len; i++) {
+ var displayable = displayables[i];
+ displayable.beforeBrush && displayable.beforeBrush();
+ displayable.innerBeforeBrush();
+ brush(ctx, displayable, innerScope, i === len - 1);
+ displayable.innerAfterBrush();
+ displayable.afterBrush && displayable.afterBrush();
+ innerScope.prevEl = displayable;
+ }
+ for (var i_1 = 0, len_1 = temporalDisplayables.length; i_1 < len_1; i_1++) {
+ var displayable = temporalDisplayables[i_1];
+ displayable.beforeBrush && displayable.beforeBrush();
+ displayable.innerBeforeBrush();
+ brush(ctx, displayable, innerScope, i_1 === len_1 - 1);
+ displayable.innerAfterBrush();
+ displayable.afterBrush && displayable.afterBrush();
+ innerScope.prevEl = displayable;
+ }
+ el.clearTemporalDisplayables();
+ el.notClear = true;
+ ctx.restore();
+ }
+
+ var decalMap = new WeakMap();
+ var decalCache = new LRU(100);
+ var decalKeys = ['symbol', 'symbolSize', 'symbolKeepAspect', 'color', 'backgroundColor', 'dashArrayX', 'dashArrayY', 'maxTileWidth', 'maxTileHeight'];
+ /**
+ * Create or update pattern image from decal options
+ *
+ * @param {InnerDecalObject | 'none'} decalObject decal options, 'none' if no decal
+ * @return {Pattern} pattern with generated image, null if no decal
+ */
+
+ function createOrUpdatePatternFromDecal(decalObject, api) {
+ if (decalObject === 'none') {
+ return null;
+ }
+
+ var dpr = api.getDevicePixelRatio();
+ var zr = api.getZr();
+ var isSVG = zr.painter.type === 'svg';
+
+ if (decalObject.dirty) {
+ decalMap["delete"](decalObject);
+ }
+
+ var oldPattern = decalMap.get(decalObject);
+
+ if (oldPattern) {
+ return oldPattern;
+ }
+
+ var decalOpt = defaults(decalObject, {
+ symbol: 'rect',
+ symbolSize: 1,
+ symbolKeepAspect: true,
+ color: 'rgba(0, 0, 0, 0.2)',
+ backgroundColor: null,
+ dashArrayX: 5,
+ dashArrayY: 5,
+ rotation: 0,
+ maxTileWidth: 512,
+ maxTileHeight: 512
+ });
+
+ if (decalOpt.backgroundColor === 'none') {
+ decalOpt.backgroundColor = null;
+ }
+
+ var pattern = {
+ repeat: 'repeat'
+ };
+ setPatternnSource(pattern);
+ pattern.rotation = decalOpt.rotation;
+ pattern.scaleX = pattern.scaleY = isSVG ? 1 : 1 / dpr;
+ decalMap.set(decalObject, pattern);
+ decalObject.dirty = false;
+ return pattern;
+
+ function setPatternnSource(pattern) {
+ var keys = [dpr];
+ var isValidKey = true;
+
+ for (var i = 0; i < decalKeys.length; ++i) {
+ var value = decalOpt[decalKeys[i]];
+ var valueType = typeof value;
+
+ if (value != null && !isArray(value) && valueType !== 'string' && valueType !== 'number' && valueType !== 'boolean') {
+ isValidKey = false;
+ break;
+ }
+
+ keys.push(value);
+ }
+
+ var cacheKey;
+
+ if (isValidKey) {
+ cacheKey = keys.join(',') + (isSVG ? '-svg' : '');
+ var cache = decalCache.get(cacheKey);
+
+ if (cache) {
+ isSVG ? pattern.svgElement = cache : pattern.image = cache;
+ }
+ }
+
+ var dashArrayX = normalizeDashArrayX(decalOpt.dashArrayX);
+ var dashArrayY = normalizeDashArrayY(decalOpt.dashArrayY);
+ var symbolArray = normalizeSymbolArray(decalOpt.symbol);
+ var lineBlockLengthsX = getLineBlockLengthX(dashArrayX);
+ var lineBlockLengthY = getLineBlockLengthY(dashArrayY);
+ var canvas = !isSVG && createCanvas();
+ var svgRoot = isSVG && zr.painter.createSVGElement('g');
+ var pSize = getPatternSize();
+ var ctx;
+
+ if (canvas) {
+ canvas.width = pSize.width * dpr;
+ canvas.height = pSize.height * dpr;
+ ctx = canvas.getContext('2d');
+ }
+
+ brushDecal();
+
+ if (isValidKey) {
+ decalCache.put(cacheKey, canvas || svgRoot);
+ }
+
+ pattern.image = canvas;
+ pattern.svgElement = svgRoot;
+ pattern.svgWidth = pSize.width;
+ pattern.svgHeight = pSize.height;
+ /**
+ * Get minumum length that can make a repeatable pattern.
+ *
+ * @return {Object} pattern width and height
+ */
+
+ function getPatternSize() {
+ /**
+ * For example, if dash is [[3, 2], [2, 1]] for X, it looks like
+ * |--- --- --- --- --- ...
+ * |-- -- -- -- -- -- -- -- ...
+ * |--- --- --- --- --- ...
+ * |-- -- -- -- -- -- -- -- ...
+ * So the minumum length of X is 15,
+ * which is the least common multiple of `3 + 2` and `2 + 1`
+ * |--- --- --- |--- --- ...
+ * |-- -- -- -- -- |-- -- -- ...
+ */
+ var width = 1;
+
+ for (var i = 0, xlen = lineBlockLengthsX.length; i < xlen; ++i) {
+ width = getLeastCommonMultiple(width, lineBlockLengthsX[i]);
+ }
+
+ var symbolRepeats = 1;
+
+ for (var i = 0, xlen = symbolArray.length; i < xlen; ++i) {
+ symbolRepeats = getLeastCommonMultiple(symbolRepeats, symbolArray[i].length);
+ }
+
+ width *= symbolRepeats;
+ var height = lineBlockLengthY * lineBlockLengthsX.length * symbolArray.length;
+
+ if ("development" !== 'production') {
+ var warn = function (attrName) {
+ /* eslint-disable-next-line */
+ console.warn("Calculated decal size is greater than " + attrName + " due to decal option settings so " + attrName + " is used for the decal size. Please consider changing the decal option to make a smaller decal or set " + attrName + " to be larger to avoid incontinuity.");
+ };
+
+ if (width > decalOpt.maxTileWidth) {
+ warn('maxTileWidth');
+ }
+
+ if (height > decalOpt.maxTileHeight) {
+ warn('maxTileHeight');
+ }
+ }
+
+ return {
+ width: Math.max(1, Math.min(width, decalOpt.maxTileWidth)),
+ height: Math.max(1, Math.min(height, decalOpt.maxTileHeight))
+ };
+ }
+
+ function brushDecal() {
+ if (ctx) {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ if (decalOpt.backgroundColor) {
+ ctx.fillStyle = decalOpt.backgroundColor;
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+ }
+ }
+
+ var ySum = 0;
+
+ for (var i = 0; i < dashArrayY.length; ++i) {
+ ySum += dashArrayY[i];
+ }
+
+ if (ySum <= 0) {
+ // dashArrayY is 0, draw nothing
+ return;
+ }
+
+ var y = -lineBlockLengthY;
+ var yId = 0;
+ var yIdTotal = 0;
+ var xId0 = 0;
+
+ while (y < pSize.height) {
+ if (yId % 2 === 0) {
+ var symbolYId = yIdTotal / 2 % symbolArray.length;
+ var x = 0;
+ var xId1 = 0;
+ var xId1Total = 0;
+
+ while (x < pSize.width * 2) {
+ var xSum = 0;
+
+ for (var i = 0; i < dashArrayX[xId0].length; ++i) {
+ xSum += dashArrayX[xId0][i];
+ }
+
+ if (xSum <= 0) {
+ // Skip empty line
+ break;
+ } // E.g., [15, 5, 20, 5] draws only for 15 and 20
+
+
+ if (xId1 % 2 === 0) {
+ var size = (1 - decalOpt.symbolSize) * 0.5;
+ var left = x + dashArrayX[xId0][xId1] * size;
+ var top_1 = y + dashArrayY[yId] * size;
+ var width = dashArrayX[xId0][xId1] * decalOpt.symbolSize;
+ var height = dashArrayY[yId] * decalOpt.symbolSize;
+ var symbolXId = xId1Total / 2 % symbolArray[symbolYId].length;
+ brushSymbol(left, top_1, width, height, symbolArray[symbolYId][symbolXId]);
+ }
+
+ x += dashArrayX[xId0][xId1];
+ ++xId1Total;
+ ++xId1;
+
+ if (xId1 === dashArrayX[xId0].length) {
+ xId1 = 0;
+ }
+ }
+
+ ++xId0;
+
+ if (xId0 === dashArrayX.length) {
+ xId0 = 0;
+ }
+ }
+
+ y += dashArrayY[yId];
+ ++yIdTotal;
+ ++yId;
+
+ if (yId === dashArrayY.length) {
+ yId = 0;
+ }
+ }
+
+ function brushSymbol(x, y, width, height, symbolType) {
+ var scale = isSVG ? 1 : dpr;
+ var symbol = createSymbol(symbolType, x * scale, y * scale, width * scale, height * scale, decalOpt.color, decalOpt.symbolKeepAspect);
+
+ if (isSVG) {
+ svgRoot.appendChild(zr.painter.paintOne(symbol));
+ } else {
+ // Paint to canvas for all other renderers.
+ brushSingle(ctx, symbol);
+ }
+ }
+ }
+ }
+ }
+ /**
+ * Convert symbol array into normalized array
+ *
+ * @param {string | (string | string[])[]} symbol symbol input
+ * @return {string[][]} normolized symbol array
+ */
+
+ function normalizeSymbolArray(symbol) {
+ if (!symbol || symbol.length === 0) {
+ return [['rect']];
+ }
+
+ if (typeof symbol === 'string') {
+ return [[symbol]];
+ }
+
+ var isAllString = true;
+
+ for (var i = 0; i < symbol.length; ++i) {
+ if (typeof symbol[i] !== 'string') {
+ isAllString = false;
+ break;
+ }
+ }
+
+ if (isAllString) {
+ return normalizeSymbolArray([symbol]);
+ }
+
+ var result = [];
+
+ for (var i = 0; i < symbol.length; ++i) {
+ if (typeof symbol[i] === 'string') {
+ result.push([symbol[i]]);
+ } else {
+ result.push(symbol[i]);
+ }
+ }
+
+ return result;
+ }
+ /**
+ * Convert dash input into dashArray
+ *
+ * @param {DecalDashArrayX} dash dash input
+ * @return {number[][]} normolized dash array
+ */
+
+
+ function normalizeDashArrayX(dash) {
+ if (!dash || dash.length === 0) {
+ return [[0, 0]];
+ }
+
+ if (typeof dash === 'number') {
+ var dashValue = Math.ceil(dash);
+ return [[dashValue, dashValue]];
+ }
+ /**
+ * [20, 5] should be normalized into [[20, 5]],
+ * while [20, [5, 10]] should be normalized into [[20, 20], [5, 10]]
+ */
+
+
+ var isAllNumber = true;
+
+ for (var i = 0; i < dash.length; ++i) {
+ if (typeof dash[i] !== 'number') {
+ isAllNumber = false;
+ break;
+ }
+ }
+
+ if (isAllNumber) {
+ return normalizeDashArrayX([dash]);
+ }
+
+ var result = [];
+
+ for (var i = 0; i < dash.length; ++i) {
+ if (typeof dash[i] === 'number') {
+ var dashValue = Math.ceil(dash[i]);
+ result.push([dashValue, dashValue]);
+ } else {
+ var dashValue = map(dash[i], function (n) {
+ return Math.ceil(n);
+ });
+
+ if (dashValue.length % 2 === 1) {
+ // [4, 2, 1] means |---- - -- |---- - -- |
+ // so normalize it to be [4, 2, 1, 4, 2, 1]
+ result.push(dashValue.concat(dashValue));
+ } else {
+ result.push(dashValue);
+ }
+ }
+ }
+
+ return result;
+ }
+ /**
+ * Convert dash input into dashArray
+ *
+ * @param {DecalDashArrayY} dash dash input
+ * @return {number[]} normolized dash array
+ */
+
+
+ function normalizeDashArrayY(dash) {
+ if (!dash || typeof dash === 'object' && dash.length === 0) {
+ return [0, 0];
+ }
+
+ if (typeof dash === 'number') {
+ var dashValue_1 = Math.ceil(dash);
+ return [dashValue_1, dashValue_1];
+ }
+
+ var dashValue = map(dash, function (n) {
+ return Math.ceil(n);
+ });
+ return dash.length % 2 ? dashValue.concat(dashValue) : dashValue;
+ }
+ /**
+ * Get block length of each line. A block is the length of dash line and space.
+ * For example, a line with [4, 1] has a dash line of 4 and a space of 1 after
+ * that, so the block length of this line is 5.
+ *
+ * @param {number[][]} dash dash arrary of X or Y
+ * @return {number[]} block length of each line
+ */
+
+
+ function getLineBlockLengthX(dash) {
+ return map(dash, function (line) {
+ return getLineBlockLengthY(line);
+ });
+ }
+
+ function getLineBlockLengthY(dash) {
+ var blockLength = 0;
+
+ for (var i = 0; i < dash.length; ++i) {
+ blockLength += dash[i];
+ }
+
+ if (dash.length % 2 === 1) {
+ // [4, 2, 1] means |---- - -- |---- - -- |
+ // So total length is (4 + 2 + 1) * 2
+ return blockLength * 2;
+ }
+
+ return blockLength;
+ }
+
+ function decalVisual(ecModel, api) {
+ ecModel.eachRawSeries(function (seriesModel) {
+ if (ecModel.isSeriesFiltered(seriesModel)) {
+ return;
+ }
+
+ var data = seriesModel.getData();
+
+ if (data.hasItemVisual()) {
+ data.each(function (idx) {
+ var decal = data.getItemVisual(idx, 'decal');
+
+ if (decal) {
+ var itemStyle = data.ensureUniqueItemVisual(idx, 'style');
+ itemStyle.decal = createOrUpdatePatternFromDecal(decal, api);
+ }
+ });
+ }
+
+ var decal = data.getVisual('decal');
+
+ if (decal) {
+ var style = data.getVisual('style');
+ style.decal = createOrUpdatePatternFromDecal(decal, api);
+ }
+ });
+ }
+
+ function parseXML(svg) {
+ if (isString(svg)) {
+ var parser = new DOMParser();
+ svg = parser.parseFromString(svg, 'text/xml');
+ }
+ var svgNode = svg;
+ if (svgNode.nodeType === 9) {
+ svgNode = svgNode.firstChild;
+ }
+ while (svgNode.nodeName.toLowerCase() !== 'svg' || svgNode.nodeType !== 1) {
+ svgNode = svgNode.nextSibling;
+ }
+ return svgNode;
+ }
+
+ var nodeParsers;
+ var INHERITABLE_STYLE_ATTRIBUTES_MAP = {
+ 'fill': 'fill',
+ 'stroke': 'stroke',
+ 'stroke-width': 'lineWidth',
+ 'opacity': 'opacity',
+ 'fill-opacity': 'fillOpacity',
+ 'stroke-opacity': 'strokeOpacity',
+ 'stroke-dasharray': 'lineDash',
+ 'stroke-dashoffset': 'lineDashOffset',
+ 'stroke-linecap': 'lineCap',
+ 'stroke-linejoin': 'lineJoin',
+ 'stroke-miterlimit': 'miterLimit',
+ 'font-family': 'fontFamily',
+ 'font-size': 'fontSize',
+ 'font-style': 'fontStyle',
+ 'font-weight': 'fontWeight',
+ 'text-anchor': 'textAlign',
+ 'visibility': 'visibility',
+ 'display': 'display'
+ };
+ var INHERITABLE_STYLE_ATTRIBUTES_MAP_KEYS = keys(INHERITABLE_STYLE_ATTRIBUTES_MAP);
+ var SELF_STYLE_ATTRIBUTES_MAP = {
+ 'alignment-baseline': 'textBaseline',
+ 'stop-color': 'stopColor'
+ };
+ var SELF_STYLE_ATTRIBUTES_MAP_KEYS = keys(SELF_STYLE_ATTRIBUTES_MAP);
+ var SVGParser = (function () {
+ function SVGParser() {
+ this._defs = {};
+ this._root = null;
+ }
+ SVGParser.prototype.parse = function (xml, opt) {
+ opt = opt || {};
+ var svg = parseXML(xml);
+ if (!svg) {
+ throw new Error('Illegal svg');
+ }
+ this._defsUsePending = [];
+ var root = new Group();
+ this._root = root;
+ var named = [];
+ var viewBox = svg.getAttribute('viewBox') || '';
+ var width = parseFloat((svg.getAttribute('width') || opt.width));
+ var height = parseFloat((svg.getAttribute('height') || opt.height));
+ isNaN(width) && (width = null);
+ isNaN(height) && (height = null);
+ parseAttributes(svg, root, null, true, false);
+ var child = svg.firstChild;
+ while (child) {
+ this._parseNode(child, root, named, null, false, false);
+ child = child.nextSibling;
+ }
+ applyDefs(this._defs, this._defsUsePending);
+ this._defsUsePending = [];
+ var viewBoxRect;
+ var viewBoxTransform;
+ if (viewBox) {
+ var viewBoxArr = splitNumberSequence(viewBox);
+ if (viewBoxArr.length >= 4) {
+ viewBoxRect = {
+ x: parseFloat((viewBoxArr[0] || 0)),
+ y: parseFloat((viewBoxArr[1] || 0)),
+ width: parseFloat(viewBoxArr[2]),
+ height: parseFloat(viewBoxArr[3])
+ };
+ }
+ }
+ if (viewBoxRect && width != null && height != null) {
+ viewBoxTransform = makeViewBoxTransform(viewBoxRect, { x: 0, y: 0, width: width, height: height });
+ if (!opt.ignoreViewBox) {
+ var elRoot = root;
+ root = new Group();
+ root.add(elRoot);
+ elRoot.scaleX = elRoot.scaleY = viewBoxTransform.scale;
+ elRoot.x = viewBoxTransform.x;
+ elRoot.y = viewBoxTransform.y;
+ }
+ }
+ if (!opt.ignoreRootClip && width != null && height != null) {
+ root.setClipPath(new Rect({
+ shape: { x: 0, y: 0, width: width, height: height }
+ }));
+ }
+ return {
+ root: root,
+ width: width,
+ height: height,
+ viewBoxRect: viewBoxRect,
+ viewBoxTransform: viewBoxTransform,
+ named: named
+ };
+ };
+ SVGParser.prototype._parseNode = function (xmlNode, parentGroup, named, namedFrom, isInDefs, isInText) {
+ var nodeName = xmlNode.nodeName.toLowerCase();
+ var el;
+ var namedFromForSub = namedFrom;
+ if (nodeName === 'defs') {
+ isInDefs = true;
+ }
+ if (nodeName === 'text') {
+ isInText = true;
+ }
+ if (nodeName === 'defs' || nodeName === 'switch') {
+ el = parentGroup;
+ }
+ else {
+ if (!isInDefs) {
+ var parser_1 = nodeParsers[nodeName];
+ if (parser_1 && hasOwn(nodeParsers, nodeName)) {
+ el = parser_1.call(this, xmlNode, parentGroup);
+ var nameAttr = xmlNode.getAttribute('name');
+ if (nameAttr) {
+ var newNamed = {
+ name: nameAttr,
+ namedFrom: null,
+ svgNodeTagLower: nodeName,
+ el: el
+ };
+ named.push(newNamed);
+ if (nodeName === 'g') {
+ namedFromForSub = newNamed;
+ }
+ }
+ else if (namedFrom) {
+ named.push({
+ name: namedFrom.name,
+ namedFrom: namedFrom,
+ svgNodeTagLower: nodeName,
+ el: el
+ });
+ }
+ parentGroup.add(el);
+ }
+ }
+ var parser = paintServerParsers[nodeName];
+ if (parser && hasOwn(paintServerParsers, nodeName)) {
+ var def = parser.call(this, xmlNode);
+ var id = xmlNode.getAttribute('id');
+ if (id) {
+ this._defs[id] = def;
+ }
+ }
+ }
+ if (el && el.isGroup) {
+ var child = xmlNode.firstChild;
+ while (child) {
+ if (child.nodeType === 1) {
+ this._parseNode(child, el, named, namedFromForSub, isInDefs, isInText);
+ }
+ else if (child.nodeType === 3 && isInText) {
+ this._parseText(child, el);
+ }
+ child = child.nextSibling;
+ }
+ }
+ };
+ SVGParser.prototype._parseText = function (xmlNode, parentGroup) {
+ var text = new TSpan({
+ style: {
+ text: xmlNode.textContent
+ },
+ silent: true,
+ x: this._textX || 0,
+ y: this._textY || 0
+ });
+ inheritStyle(parentGroup, text);
+ parseAttributes(xmlNode, text, this._defsUsePending, false, false);
+ applyTextAlignment(text, parentGroup);
+ var textStyle = text.style;
+ var fontSize = textStyle.fontSize;
+ if (fontSize && fontSize < 9) {
+ textStyle.fontSize = 9;
+ text.scaleX *= fontSize / 9;
+ text.scaleY *= fontSize / 9;
+ }
+ var font = (textStyle.fontSize || textStyle.fontFamily) && [
+ textStyle.fontStyle,
+ textStyle.fontWeight,
+ (textStyle.fontSize || 12) + 'px',
+ textStyle.fontFamily || 'sans-serif'
+ ].join(' ');
+ textStyle.font = font;
+ var rect = text.getBoundingRect();
+ this._textX += rect.width;
+ parentGroup.add(text);
+ return text;
+ };
+ SVGParser.internalField = (function () {
+ nodeParsers = {
+ 'g': function (xmlNode, parentGroup) {
+ var g = new Group();
+ inheritStyle(parentGroup, g);
+ parseAttributes(xmlNode, g, this._defsUsePending, false, false);
+ return g;
+ },
+ 'rect': function (xmlNode, parentGroup) {
+ var rect = new Rect();
+ inheritStyle(parentGroup, rect);
+ parseAttributes(xmlNode, rect, this._defsUsePending, false, false);
+ rect.setShape({
+ x: parseFloat(xmlNode.getAttribute('x') || '0'),
+ y: parseFloat(xmlNode.getAttribute('y') || '0'),
+ width: parseFloat(xmlNode.getAttribute('width') || '0'),
+ height: parseFloat(xmlNode.getAttribute('height') || '0')
+ });
+ rect.silent = true;
+ return rect;
+ },
+ 'circle': function (xmlNode, parentGroup) {
+ var circle = new Circle();
+ inheritStyle(parentGroup, circle);
+ parseAttributes(xmlNode, circle, this._defsUsePending, false, false);
+ circle.setShape({
+ cx: parseFloat(xmlNode.getAttribute('cx') || '0'),
+ cy: parseFloat(xmlNode.getAttribute('cy') || '0'),
+ r: parseFloat(xmlNode.getAttribute('r') || '0')
+ });
+ circle.silent = true;
+ return circle;
+ },
+ 'line': function (xmlNode, parentGroup) {
+ var line = new Line();
+ inheritStyle(parentGroup, line);
+ parseAttributes(xmlNode, line, this._defsUsePending, false, false);
+ line.setShape({
+ x1: parseFloat(xmlNode.getAttribute('x1') || '0'),
+ y1: parseFloat(xmlNode.getAttribute('y1') || '0'),
+ x2: parseFloat(xmlNode.getAttribute('x2') || '0'),
+ y2: parseFloat(xmlNode.getAttribute('y2') || '0')
+ });
+ line.silent = true;
+ return line;
+ },
+ 'ellipse': function (xmlNode, parentGroup) {
+ var ellipse = new Ellipse();
+ inheritStyle(parentGroup, ellipse);
+ parseAttributes(xmlNode, ellipse, this._defsUsePending, false, false);
+ ellipse.setShape({
+ cx: parseFloat(xmlNode.getAttribute('cx') || '0'),
+ cy: parseFloat(xmlNode.getAttribute('cy') || '0'),
+ rx: parseFloat(xmlNode.getAttribute('rx') || '0'),
+ ry: parseFloat(xmlNode.getAttribute('ry') || '0')
+ });
+ ellipse.silent = true;
+ return ellipse;
+ },
+ 'polygon': function (xmlNode, parentGroup) {
+ var pointsStr = xmlNode.getAttribute('points');
+ var pointsArr;
+ if (pointsStr) {
+ pointsArr = parsePoints(pointsStr);
+ }
+ var polygon = new Polygon({
+ shape: {
+ points: pointsArr || []
+ },
+ silent: true
+ });
+ inheritStyle(parentGroup, polygon);
+ parseAttributes(xmlNode, polygon, this._defsUsePending, false, false);
+ return polygon;
+ },
+ 'polyline': function (xmlNode, parentGroup) {
+ var pointsStr = xmlNode.getAttribute('points');
+ var pointsArr;
+ if (pointsStr) {
+ pointsArr = parsePoints(pointsStr);
+ }
+ var polyline = new Polyline({
+ shape: {
+ points: pointsArr || []
+ },
+ silent: true
+ });
+ inheritStyle(parentGroup, polyline);
+ parseAttributes(xmlNode, polyline, this._defsUsePending, false, false);
+ return polyline;
+ },
+ 'image': function (xmlNode, parentGroup) {
+ var img = new ZRImage();
+ inheritStyle(parentGroup, img);
+ parseAttributes(xmlNode, img, this._defsUsePending, false, false);
+ img.setStyle({
+ image: xmlNode.getAttribute('xlink:href') || xmlNode.getAttribute('href'),
+ x: +xmlNode.getAttribute('x'),
+ y: +xmlNode.getAttribute('y'),
+ width: +xmlNode.getAttribute('width'),
+ height: +xmlNode.getAttribute('height')
+ });
+ img.silent = true;
+ return img;
+ },
+ 'text': function (xmlNode, parentGroup) {
+ var x = xmlNode.getAttribute('x') || '0';
+ var y = xmlNode.getAttribute('y') || '0';
+ var dx = xmlNode.getAttribute('dx') || '0';
+ var dy = xmlNode.getAttribute('dy') || '0';
+ this._textX = parseFloat(x) + parseFloat(dx);
+ this._textY = parseFloat(y) + parseFloat(dy);
+ var g = new Group();
+ inheritStyle(parentGroup, g);
+ parseAttributes(xmlNode, g, this._defsUsePending, false, true);
+ return g;
+ },
+ 'tspan': function (xmlNode, parentGroup) {
+ var x = xmlNode.getAttribute('x');
+ var y = xmlNode.getAttribute('y');
+ if (x != null) {
+ this._textX = parseFloat(x);
+ }
+ if (y != null) {
+ this._textY = parseFloat(y);
+ }
+ var dx = xmlNode.getAttribute('dx') || '0';
+ var dy = xmlNode.getAttribute('dy') || '0';
+ var g = new Group();
+ inheritStyle(parentGroup, g);
+ parseAttributes(xmlNode, g, this._defsUsePending, false, true);
+ this._textX += parseFloat(dx);
+ this._textY += parseFloat(dy);
+ return g;
+ },
+ 'path': function (xmlNode, parentGroup) {
+ var d = xmlNode.getAttribute('d') || '';
+ var path = createFromString(d);
+ inheritStyle(parentGroup, path);
+ parseAttributes(xmlNode, path, this._defsUsePending, false, false);
+ path.silent = true;
+ return path;
+ }
+ };
+ })();
+ return SVGParser;
+ }());
+ var paintServerParsers = {
+ 'lineargradient': function (xmlNode) {
+ var x1 = parseInt(xmlNode.getAttribute('x1') || '0', 10);
+ var y1 = parseInt(xmlNode.getAttribute('y1') || '0', 10);
+ var x2 = parseInt(xmlNode.getAttribute('x2') || '10', 10);
+ var y2 = parseInt(xmlNode.getAttribute('y2') || '0', 10);
+ var gradient = new LinearGradient(x1, y1, x2, y2);
+ parsePaintServerUnit(xmlNode, gradient);
+ parseGradientColorStops(xmlNode, gradient);
+ return gradient;
+ },
+ 'radialgradient': function (xmlNode) {
+ var cx = parseInt(xmlNode.getAttribute('cx') || '0', 10);
+ var cy = parseInt(xmlNode.getAttribute('cy') || '0', 10);
+ var r = parseInt(xmlNode.getAttribute('r') || '0', 10);
+ var gradient = new RadialGradient(cx, cy, r);
+ parsePaintServerUnit(xmlNode, gradient);
+ parseGradientColorStops(xmlNode, gradient);
+ return gradient;
+ }
+ };
+ function parsePaintServerUnit(xmlNode, gradient) {
+ var gradientUnits = xmlNode.getAttribute('gradientUnits');
+ if (gradientUnits === 'userSpaceOnUse') {
+ gradient.global = true;
+ }
+ }
+ function parseGradientColorStops(xmlNode, gradient) {
+ var stop = xmlNode.firstChild;
+ while (stop) {
+ if (stop.nodeType === 1
+ && stop.nodeName.toLocaleLowerCase() === 'stop') {
+ var offsetStr = stop.getAttribute('offset');
+ var offset = void 0;
+ if (offsetStr && offsetStr.indexOf('%') > 0) {
+ offset = parseInt(offsetStr, 10) / 100;
+ }
+ else if (offsetStr) {
+ offset = parseFloat(offsetStr);
+ }
+ else {
+ offset = 0;
+ }
+ var styleVals = {};
+ parseInlineStyle(stop, styleVals, styleVals);
+ var stopColor = styleVals.stopColor
+ || stop.getAttribute('stop-color')
+ || '#000000';
+ gradient.colorStops.push({
+ offset: offset,
+ color: stopColor
+ });
+ }
+ stop = stop.nextSibling;
+ }
+ }
+ function inheritStyle(parent, child) {
+ if (parent && parent.__inheritedStyle) {
+ if (!child.__inheritedStyle) {
+ child.__inheritedStyle = {};
+ }
+ defaults(child.__inheritedStyle, parent.__inheritedStyle);
+ }
+ }
+ function parsePoints(pointsString) {
+ var list = splitNumberSequence(pointsString);
+ var points = [];
+ for (var i = 0; i < list.length; i += 2) {
+ var x = parseFloat(list[i]);
+ var y = parseFloat(list[i + 1]);
+ points.push([x, y]);
+ }
+ return points;
+ }
+ function parseAttributes(xmlNode, el, defsUsePending, onlyInlineStyle, isTextGroup) {
+ var disp = el;
+ var inheritedStyle = disp.__inheritedStyle = disp.__inheritedStyle || {};
+ var selfStyle = {};
+ if (xmlNode.nodeType === 1) {
+ parseTransformAttribute(xmlNode, el);
+ parseInlineStyle(xmlNode, inheritedStyle, selfStyle);
+ if (!onlyInlineStyle) {
+ parseAttributeStyle(xmlNode, inheritedStyle, selfStyle);
+ }
+ }
+ disp.style = disp.style || {};
+ if (inheritedStyle.fill != null) {
+ disp.style.fill = getFillStrokeStyle(disp, 'fill', inheritedStyle.fill, defsUsePending);
+ }
+ if (inheritedStyle.stroke != null) {
+ disp.style.stroke = getFillStrokeStyle(disp, 'stroke', inheritedStyle.stroke, defsUsePending);
+ }
+ each([
+ 'lineWidth', 'opacity', 'fillOpacity', 'strokeOpacity', 'miterLimit', 'fontSize'
+ ], function (propName) {
+ if (inheritedStyle[propName] != null) {
+ disp.style[propName] = parseFloat(inheritedStyle[propName]);
+ }
+ });
+ each([
+ 'lineDashOffset', 'lineCap', 'lineJoin', 'fontWeight', 'fontFamily', 'fontStyle', 'textAlign'
+ ], function (propName) {
+ if (inheritedStyle[propName] != null) {
+ disp.style[propName] = inheritedStyle[propName];
+ }
+ });
+ if (isTextGroup) {
+ disp.__selfStyle = selfStyle;
+ }
+ if (inheritedStyle.lineDash) {
+ disp.style.lineDash = map(splitNumberSequence(inheritedStyle.lineDash), function (str) {
+ return parseFloat(str);
+ });
+ }
+ if (inheritedStyle.visibility === 'hidden' || inheritedStyle.visibility === 'collapse') {
+ disp.invisible = true;
+ }
+ if (inheritedStyle.display === 'none') {
+ disp.ignore = true;
+ }
+ }
+ function applyTextAlignment(text, parentGroup) {
+ var parentSelfStyle = parentGroup.__selfStyle;
+ if (parentSelfStyle) {
+ var textBaseline = parentSelfStyle.textBaseline;
+ var zrTextBaseline = textBaseline;
+ if (!textBaseline || textBaseline === 'auto') {
+ zrTextBaseline = 'alphabetic';
+ }
+ else if (textBaseline === 'baseline') {
+ zrTextBaseline = 'alphabetic';
+ }
+ else if (textBaseline === 'before-edge' || textBaseline === 'text-before-edge') {
+ zrTextBaseline = 'top';
+ }
+ else if (textBaseline === 'after-edge' || textBaseline === 'text-after-edge') {
+ zrTextBaseline = 'bottom';
+ }
+ else if (textBaseline === 'central' || textBaseline === 'mathematical') {
+ zrTextBaseline = 'middle';
+ }
+ text.style.textBaseline = zrTextBaseline;
+ }
+ var parentInheritedStyle = parentGroup.__inheritedStyle;
+ if (parentInheritedStyle) {
+ var textAlign = parentInheritedStyle.textAlign;
+ var zrTextAlign = textAlign;
+ if (textAlign) {
+ if (textAlign === 'middle') {
+ zrTextAlign = 'center';
+ }
+ text.style.textAlign = zrTextAlign;
+ }
+ }
+ }
+ var urlRegex = /^url\(\s*#(.*?)\)/;
+ function getFillStrokeStyle(el, method, str, defsUsePending) {
+ var urlMatch = str && str.match(urlRegex);
+ if (urlMatch) {
+ var url = trim(urlMatch[1]);
+ defsUsePending.push([el, method, url]);
+ return;
+ }
+ if (str === 'none') {
+ str = null;
+ }
+ return str;
+ }
+ function applyDefs(defs, defsUsePending) {
+ for (var i = 0; i < defsUsePending.length; i++) {
+ var item = defsUsePending[i];
+ item[0].style[item[1]] = defs[item[2]];
+ }
+ }
+ var numberReg$1 = /-?([0-9]*\.)?[0-9]+([eE]-?[0-9]+)?/g;
+ function splitNumberSequence(rawStr) {
+ return rawStr.match(numberReg$1) || [];
+ }
+ var transformRegex = /(translate|scale|rotate|skewX|skewY|matrix)\(([\-\s0-9\.eE,]*)\)/g;
+ var DEGREE_TO_ANGLE = Math.PI / 180;
+ function parseTransformAttribute(xmlNode, node) {
+ var transform = xmlNode.getAttribute('transform');
+ if (transform) {
+ transform = transform.replace(/,/g, ' ');
+ var transformOps_1 = [];
+ var mt = null;
+ transform.replace(transformRegex, function (str, type, value) {
+ transformOps_1.push(type, value);
+ return '';
+ });
+ for (var i = transformOps_1.length - 1; i > 0; i -= 2) {
+ var value = transformOps_1[i];
+ var type = transformOps_1[i - 1];
+ var valueArr = splitNumberSequence(value);
+ mt = mt || create$1();
+ switch (type) {
+ case 'translate':
+ translate(mt, mt, [parseFloat(valueArr[0]), parseFloat(valueArr[1] || '0')]);
+ break;
+ case 'scale':
+ scale$1(mt, mt, [parseFloat(valueArr[0]), parseFloat(valueArr[1] || valueArr[0])]);
+ break;
+ case 'rotate':
+ rotate(mt, mt, -parseFloat(valueArr[0]) * DEGREE_TO_ANGLE);
+ break;
+ case 'skewX':
+ var sx = Math.tan(parseFloat(valueArr[0]) * DEGREE_TO_ANGLE);
+ mul$1(mt, [1, 0, sx, 1, 0, 0], mt);
+ break;
+ case 'skewY':
+ var sy = Math.tan(parseFloat(valueArr[0]) * DEGREE_TO_ANGLE);
+ mul$1(mt, [1, sy, 0, 1, 0, 0], mt);
+ break;
+ case 'matrix':
+ mt[0] = parseFloat(valueArr[0]);
+ mt[1] = parseFloat(valueArr[1]);
+ mt[2] = parseFloat(valueArr[2]);
+ mt[3] = parseFloat(valueArr[3]);
+ mt[4] = parseFloat(valueArr[4]);
+ mt[5] = parseFloat(valueArr[5]);
+ break;
+ }
+ }
+ node.setLocalTransform(mt);
+ }
+ }
+ var styleRegex = /([^\s:;]+)\s*:\s*([^:;]+)/g;
+ function parseInlineStyle(xmlNode, inheritableStyleResult, selfStyleResult) {
+ var style = xmlNode.getAttribute('style');
+ if (!style) {
+ return;
+ }
+ styleRegex.lastIndex = 0;
+ var styleRegResult;
+ while ((styleRegResult = styleRegex.exec(style)) != null) {
+ var svgStlAttr = styleRegResult[1];
+ var zrInheritableStlAttr = hasOwn(INHERITABLE_STYLE_ATTRIBUTES_MAP, svgStlAttr)
+ ? INHERITABLE_STYLE_ATTRIBUTES_MAP[svgStlAttr]
+ : null;
+ if (zrInheritableStlAttr) {
+ inheritableStyleResult[zrInheritableStlAttr] = styleRegResult[2];
+ }
+ var zrSelfStlAttr = hasOwn(SELF_STYLE_ATTRIBUTES_MAP, svgStlAttr)
+ ? SELF_STYLE_ATTRIBUTES_MAP[svgStlAttr]
+ : null;
+ if (zrSelfStlAttr) {
+ selfStyleResult[zrSelfStlAttr] = styleRegResult[2];
+ }
+ }
+ }
+ function parseAttributeStyle(xmlNode, inheritableStyleResult, selfStyleResult) {
+ for (var i = 0; i < INHERITABLE_STYLE_ATTRIBUTES_MAP_KEYS.length; i++) {
+ var svgAttrName = INHERITABLE_STYLE_ATTRIBUTES_MAP_KEYS[i];
+ var attrValue = xmlNode.getAttribute(svgAttrName);
+ if (attrValue != null) {
+ inheritableStyleResult[INHERITABLE_STYLE_ATTRIBUTES_MAP[svgAttrName]] = attrValue;
+ }
+ }
+ for (var i = 0; i < SELF_STYLE_ATTRIBUTES_MAP_KEYS.length; i++) {
+ var svgAttrName = SELF_STYLE_ATTRIBUTES_MAP_KEYS[i];
+ var attrValue = xmlNode.getAttribute(svgAttrName);
+ if (attrValue != null) {
+ selfStyleResult[SELF_STYLE_ATTRIBUTES_MAP[svgAttrName]] = attrValue;
+ }
+ }
+ }
+ function makeViewBoxTransform(viewBoxRect, boundingRect) {
+ var scaleX = boundingRect.width / viewBoxRect.width;
+ var scaleY = boundingRect.height / viewBoxRect.height;
+ var scale = Math.min(scaleX, scaleY);
+ return {
+ scale: scale,
+ x: -(viewBoxRect.x + viewBoxRect.width / 2) * scale + (boundingRect.x + boundingRect.width / 2),
+ y: -(viewBoxRect.y + viewBoxRect.height / 2) * scale + (boundingRect.y + boundingRect.height / 2)
+ };
+ }
+ function parseSVG(xml, opt) {
+ var parser = new SVGParser();
+ return parser.parse(xml, opt);
+ }
+
+ var EPSILON$3 = 1e-8;
+ function isAroundEqual$1(a, b) {
+ return Math.abs(a - b) < EPSILON$3;
+ }
+ function contain$1(points, x, y) {
+ var w = 0;
+ var p = points[0];
+ if (!p) {
+ return false;
+ }
+ for (var i = 1; i < points.length; i++) {
+ var p2 = points[i];
+ w += windingLine(p[0], p[1], p2[0], p2[1], x, y);
+ p = p2;
+ }
+ var p0 = points[0];
+ if (!isAroundEqual$1(p[0], p0[0]) || !isAroundEqual$1(p[1], p0[1])) {
+ w += windingLine(p[0], p[1], p0[0], p0[1], x, y);
+ }
+ return w !== 0;
+ }
+
+ var TMP_TRANSFORM = [];
+
+ var Region =
+ /** @class */
+ function () {
+ function Region(name) {
+ this.name = name;
+ }
+ /**
+ * Get center point in data unit. That is,
+ * for GeoJSONRegion, the unit is lat/lng,
+ * for GeoSVGRegion, the unit is SVG local coord.
+ */
+
+
+ Region.prototype.getCenter = function () {
+ return;
+ };
+
+ return Region;
+ }();
+
+ var GeoJSONRegion =
+ /** @class */
+ function (_super) {
+ __extends(GeoJSONRegion, _super);
+
+ function GeoJSONRegion(name, geometries, cp) {
+ var _this = _super.call(this, name) || this;
+
+ _this.type = 'geoJSON';
+ _this.geometries = geometries;
+
+ if (!cp) {
+ var rect = _this.getBoundingRect();
+
+ cp = [rect.x + rect.width / 2, rect.y + rect.height / 2];
+ } else {
+ cp = [cp[0], cp[1]];
+ }
+
+ _this._center = cp;
+ return _this;
+ }
+
+ GeoJSONRegion.prototype.getBoundingRect = function () {
+ var rect = this._rect;
+
+ if (rect) {
+ return rect;
+ }
+
+ var MAX_NUMBER = Number.MAX_VALUE;
+ var min$1 = [MAX_NUMBER, MAX_NUMBER];
+ var max$1 = [-MAX_NUMBER, -MAX_NUMBER];
+ var min2 = [];
+ var max2 = [];
+ var geometries = this.geometries;
+ var i = 0;
+
+ for (; i < geometries.length; i++) {
+ // Only support polygon
+ if (geometries[i].type !== 'polygon') {
+ continue;
+ } // Doesn't consider hole
+
+
+ var exterior = geometries[i].exterior;
+ fromPoints(exterior, min2, max2);
+ min(min$1, min$1, min2);
+ max(max$1, max$1, max2);
+ } // No data
+
+
+ if (i === 0) {
+ min$1[0] = min$1[1] = max$1[0] = max$1[1] = 0;
+ }
+
+ return this._rect = new BoundingRect(min$1[0], min$1[1], max$1[0] - min$1[0], max$1[1] - min$1[1]);
+ };
+
+ GeoJSONRegion.prototype.contain = function (coord) {
+ var rect = this.getBoundingRect();
+ var geometries = this.geometries;
+
+ if (!rect.contain(coord[0], coord[1])) {
+ return false;
+ }
+
+ loopGeo: for (var i = 0, len = geometries.length; i < len; i++) {
+ // Only support polygon.
+ if (geometries[i].type !== 'polygon') {
+ continue;
+ }
+
+ var exterior = geometries[i].exterior;
+ var interiors = geometries[i].interiors;
+
+ if (contain$1(exterior, coord[0], coord[1])) {
+ // Not in the region if point is in the hole.
+ for (var k = 0; k < (interiors ? interiors.length : 0); k++) {
+ if (contain$1(interiors[k], coord[0], coord[1])) {
+ continue loopGeo;
+ }
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+ };
+
+ GeoJSONRegion.prototype.transformTo = function (x, y, width, height) {
+ var rect = this.getBoundingRect();
+ var aspect = rect.width / rect.height;
+
+ if (!width) {
+ width = aspect * height;
+ } else if (!height) {
+ height = width / aspect;
+ }
+
+ var target = new BoundingRect(x, y, width, height);
+ var transform = rect.calculateTransform(target);
+ var geometries = this.geometries;
+
+ for (var i = 0; i < geometries.length; i++) {
+ // Only support polygon.
+ if (geometries[i].type !== 'polygon') {
+ continue;
+ }
+
+ var exterior = geometries[i].exterior;
+ var interiors = geometries[i].interiors;
+
+ for (var p = 0; p < exterior.length; p++) {
+ applyTransform(exterior[p], exterior[p], transform);
+ }
+
+ for (var h = 0; h < (interiors ? interiors.length : 0); h++) {
+ for (var p = 0; p < interiors[h].length; p++) {
+ applyTransform(interiors[h][p], interiors[h][p], transform);
+ }
+ }
+ }
+
+ rect = this._rect;
+ rect.copy(target); // Update center
+
+ this._center = [rect.x + rect.width / 2, rect.y + rect.height / 2];
+ };
+
+ GeoJSONRegion.prototype.cloneShallow = function (name) {
+ name == null && (name = this.name);
+ var newRegion = new GeoJSONRegion(name, this.geometries, this._center);
+ newRegion._rect = this._rect;
+ newRegion.transformTo = null; // Simply avoid to be called.
+
+ return newRegion;
+ };
+
+ GeoJSONRegion.prototype.getCenter = function () {
+ return this._center;
+ };
+
+ GeoJSONRegion.prototype.setCenter = function (center) {
+ this._center = center;
+ };
+
+ return GeoJSONRegion;
+ }(Region);
+
+ var GeoSVGRegion =
+ /** @class */
+ function (_super) {
+ __extends(GeoSVGRegion, _super);
+
+ function GeoSVGRegion(name, elOnlyForCalculate) {
+ var _this = _super.call(this, name) || this;
+
+ _this.type = 'geoSVG';
+ _this._elOnlyForCalculate = elOnlyForCalculate;
+ return _this;
+ }
+
+ GeoSVGRegion.prototype.getCenter = function () {
+ var center = this._center;
+
+ if (!center) {
+ // In most cases there are no need to calculate this center.
+ // So calculate only when called.
+ center = this._center = this._calculateCenter();
+ }
+
+ return center;
+ };
+
+ GeoSVGRegion.prototype._calculateCenter = function () {
+ var el = this._elOnlyForCalculate;
+ var rect = el.getBoundingRect();
+ var center = [rect.x + rect.width / 2, rect.y + rect.height / 2];
+ var mat = identity(TMP_TRANSFORM);
+ var target = el;
+
+ while (target && !target.isGeoSVGGraphicRoot) {
+ mul$1(mat, target.getLocalTransform(), mat);
+ target = target.parent;
+ }
+
+ invert(mat, mat);
+ applyTransform(center, center, mat);
+ return center;
+ };
+
+ return GeoSVGRegion;
+ }(Region);
+
+ /**
+ * "region available" means that: enable users to set attribute `name="xxx"` on those tags
+ * to make it be a region.
+ * 1. region styles and its label styles can be defined in echarts opton:
+ * ```js
+ * geo: {
+ * regions: [{
+ * name: 'xxx',
+ * itemStyle: { ... },
+ * label: { ... }
+ * }, {
+ * ...
+ * },
+ * ...]
+ * };
+ * ```
+ * 2. name can be duplicated in different SVG tag. All of the tags with the same name share
+ * a region option. For exampel if there are two representing two lung lobes. They have
+ * no common parents but both of them need to display label "lung" inside.
+ */
+
+ var REGION_AVAILABLE_SVG_TAG_MAP = createHashMap(['rect', 'circle', 'line', 'ellipse', 'polygon', 'polyline', 'path', // are also enabled becuase some SVG might paint text itself,
+ // but still need to trigger events or tooltip.
+ 'text', 'tspan', // is also enabled because this case: if multiple tags share one name
+ // and need label displayed, every tags will display the name, which is not
+ // expected. So we can put them into a . Thereby only one label
+ // displayed and located based on the bounding rect of the .
+ 'g']);
+
+ var GeoSVGResource =
+ /** @class */
+ function () {
+ function GeoSVGResource(mapName, svg) {
+ this.type = 'geoSVG'; // All used graphics. key: hostKey, value: root
+
+ this._usedGraphicMap = createHashMap(); // All unused graphics.
+
+ this._freedGraphics = [];
+ this._mapName = mapName; // Only perform parse to XML object here, which might be time
+ // consiming for large SVG.
+ // Although convert XML to zrender element is also time consiming,
+ // if we do it here, the clone of zrender elements has to be
+ // required. So we do it once for each geo instance, util real
+ // performance issues call for optimizing it.
+
+ this._parsedXML = parseXML(svg);
+ }
+
+ GeoSVGResource.prototype.load = function ()
+ /* nameMap: NameMap */
+ {
+ // In the "load" stage, graphic need to be built to
+ // get boundingRect for geo coordinate system.
+ var firstGraphic = this._firstGraphic; // Create the return data structure only when first graphic created.
+ // Because they will be used in geo coordinate system update stage,
+ // and `regions` will be mounted at `geo` coordinate system,
+ // in which there is no "view" info, so that it should better not to
+ // make references to graphic elements.
+
+ if (!firstGraphic) {
+ firstGraphic = this._firstGraphic = this._buildGraphic(this._parsedXML);
+
+ this._freedGraphics.push(firstGraphic);
+
+ this._boundingRect = this._firstGraphic.boundingRect.clone(); // PENDING: `nameMap` will not be supported until some real requirement come.
+ // if (nameMap) {
+ // named = applyNameMap(named, nameMap);
+ // }
+
+ var _a = createRegions(firstGraphic.named),
+ regions = _a.regions,
+ regionsMap = _a.regionsMap;
+
+ this._regions = regions;
+ this._regionsMap = regionsMap;
+ }
+
+ return {
+ boundingRect: this._boundingRect,
+ regions: this._regions,
+ regionsMap: this._regionsMap
+ };
+ };
+
+ GeoSVGResource.prototype._buildGraphic = function (svgXML) {
+ var result;
+ var rootFromParse;
+
+ try {
+ result = svgXML && parseSVG(svgXML, {
+ ignoreViewBox: true,
+ ignoreRootClip: true
+ }) || {};
+ rootFromParse = result.root;
+ assert(rootFromParse != null);
+ } catch (e) {
+ throw new Error('Invalid svg format\n' + e.message);
+ } // Note: we keep the covenant that the root has no transform. So always add an extra root.
+
+
+ var root = new Group();
+ root.add(rootFromParse);
+ root.isGeoSVGGraphicRoot = true; // [THE_RULE_OF_VIEWPORT_AND_VIEWBOX]
+ //
+ // Consider: `