;
var markupFragment
if (isObject$2(result)) {
if (result.type) {
markupFragment = result
} // else {
// markupText = (result as TooltipFormatResultLegacyObject).html;
// markers = (result as TooltipFormatResultLegacyObject).markers;
// if (markersExisting) {
// markers = zrUtil.merge(markersExisting, markers);
// }
// }
} else {
markupText = result
}
return {
text: markupText, // markers: markers || markersExisting,
frag: 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) {
this._dueEnd = upTask._outputDueEnd
} // DataTask or overallTask
else {
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
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 (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 reset(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
!isNumber(value) &&
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 number(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 time(val) {
// return timestamp.
return +parseDate(val)
},
trim: function trim(val) {
return isString(val) ? trim$1(val) : val
}
})
function getRawValueParser(type) {
return valueParserMap.get(type)
}
var ORDER_COMPARISON_OP_MAP = {
lt: function lt(lval, rval) {
return lval < rval
},
lte: function lte(lval, rval) {
return lval <= rval
},
gt: function gt(lval, rval) {
return lval > rval
},
gte: function gte(lval, rval) {
return lval >= rval
}
}
var FilterOrderComparator = /** @class */ (function () {
function FilterOrderComparator(op, rval) {
if (!isNumber(rval)) {
var errMsg = ''
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 isNumber(lval)
? 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 lvalFloat = isNumber(lval) ? lval : numericToNumber(lval)
var rvalFloat = isNumber(rval) ? 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 = isString(lval)
var rvalIsStr = isString(rval)
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) {
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$7(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)) {
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$1(getRawData, null, internalSource)
}
extSource.cloneRawData = bind$1(cloneRawData, null, internalSource)
var rawCounter = getRawSourceDataCounter(
sourceFormat,
SERIES_LAYOUT_BY_COLUMN
)
extSource.count = bind$1(
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$1(
getDimensionInfo,
null,
dimensions,
dimsByName
)
extSource.cloneAllDimensionInfo = bind$1(
cloneAllDimensionInfo,
null,
dimensions
)
return extSource
}
function getRawData(upstream) {
var sourceFormat = upstream.sourceFormat
if (!isSupportedSourceFormat(sourceFormat)) {
var errMsg = ''
throwError(errMsg)
}
return upstream.data
}
function cloneRawData(upstream) {
var sourceFormat = upstream.sourceFormat
var data = upstream.data
if (!isSupportedSourceFormat(sourceFormat)) {
var errMsg = ''
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 (
isNumber(dim) || // 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$3(dimensions)
}
var externalTransformMap = createHashMap()
function registerExternalTransform(externalTransform) {
externalTransform = clone$3(externalTransform)
var type = externalTransform.type
var errMsg = ''
if (!type) {
throwError(errMsg)
}
var typeParsed = type.split(':')
if (typeParsed.length !== 2) {
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) {
throwError(errMsg)
}
for (var i = 0, len = pipeLen; i < len; i++) {
var transOption = pipedTransOption[i]
sourceList = applySingleDataTransform(transOption, sourceList) // 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) {
throwError(errMsg)
}
if (!isObject$2(transOption)) {
throwError(errMsg)
}
var transType = transOption.type
var externalTransform = externalTransformMap.get(transType)
if (!externalTransform) {
throwError(errMsg)
} // Prepare source
var extUpSourceList = map$1(upSourceList, function (upSource) {
return createExternalSource(upSource, externalTransform)
})
var resultList = normalizeToArray(
externalTransform.transform({
upstream: extUpSourceList[0],
upstreamList: extUpSourceList,
config: clone$3(transOption.config)
})
)
return map$1(resultList, function (result, resultIndex) {
var errMsg = ''
if (!isObject$2(result)) {
throwError(errMsg)
}
if (!result.data) {
throwError(errMsg)
}
var sourceFormat = detectSourceFormat(result.data)
if (!isSupportedSourceFormat(sourceFormat)) {
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'
? 'undefined'
: _typeof(Uint32Array)) === UNDEFINED
? Array
: Uint32Array
var CtorUint16Array =
(typeof Uint16Array === 'undefined'
? 'undefined'
: _typeof(Uint16Array)) === UNDEFINED
? Array
: Uint16Array
var CtorInt32Array$1 =
(typeof Int32Array === 'undefined'
? 'undefined'
: _typeof(Int32Array)) === UNDEFINED
? Array
: Int32Array
var CtorFloat64Array =
(typeof Float64Array === 'undefined'
? 'undefined'
: _typeof(Float64Array)) === UNDEFINED
? Array
: Float64Array
/**
* Multi dimensional data store
*/ var dataCtors = {
float: CtorFloat64Array,
int: CtorInt32Array$1, // 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
) {
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 = []
shouldRetrieveDataByName(source)
this._dimensions = map$1(inputDimensions, function (dim) {
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]))
if (!isNaN(val)) {
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) {
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$1(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.min((Math.ceil(len / frameSize) + 2) * 2, len)
) // 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
var firstNaNIndex = -1
var countNaN = 0 // 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)) {
countNaN++
if (firstNaNIndex < 0) {
firstNaNIndex = rawIndex
}
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
}
}
if (countNaN > 0 && countNaN < frameEnd - frameStart) {
// Append first NaN point in every bucket.
// It is necessary to ensure the correct order of indices.
newIndices[sampledIndex++] = Math.min(
firstNaNIndex,
nextRawIndex
)
nextRawIndex = Math.max(firstNaNIndex, nextRawIndex)
}
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$3(this._extent)
target._rawExtent = clone$3(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 objectRows(
dataItem,
property,
dataIndex,
dimIndex
) {
return parseDataValue(
dataItem[property],
this._dimensions[dimIndex]
)
},
keyedColumns: getDimValueSimply,
original: function original(
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 typedArray(
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
) // 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 = []
}
}
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 (fromTransformResult != null) {
var errMsg = ''
if (upMgrList.length !== 1) {
doThrow(errMsg)
}
}
var sourceList
var upSourceList = []
var upstreamSignList = []
each$7(upMgrList, function (upMgr) {
upMgr.prepareSource()
var upSource = upMgr.getSource(fromTransformResult || 0)
var errMsg = ''
if (fromTransformResult != null && !upSource) {
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
) {
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$1(
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 isSectionFragment(frag) {
return frag.type === 'section'
}
function getBuilder(frag) {
return isSectionFragment(frag) ? buildSection : buildNameValue
}
function getBlockGapLevel(frag) {
if (isSectionFragment(frag)) {
var gapLevel_1 = 0
var subBlockLen = frag.blocks.length
var hasInnerGap_1 =
subBlockLen > 1 || (subBlockLen > 0 && !frag.noHeader)
each$7(frag.blocks, function (subBlock) {
var subGapLevel = getBlockGapLevel(subBlock) // 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 >= gapLevel_1) {
gapLevel_1 =
subGapLevel +
+(
hasInnerGap_1 && // 0 always can not be readable gap level.
(!subGapLevel || // If no header, always keep the sub gap level. Otherwise
// look weird in case `multipleSeries`.
(isSectionFragment(subBlock) && !subBlock.noHeader))
)
}
})
return gapLevel_1
}
return 0
}
function buildSection(
ctx,
fragment,
topMarginForOuterGap,
toolTipTextStyle
) {
var noHeader = fragment.noHeader
var gaps = getGap(getBlockGapLevel(fragment))
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()
}
}
each$7(subBlocks, function (subBlock, idx) {
var valueFormatter = fragment.valueFormatter
var subMarkupText = getBuilder(subBlock)(
// Inherit valueFormatter
valueFormatter
? extend(extend({}, ctx), { valueFormatter: valueFormatter })
: ctx,
subBlock,
idx > 0 ? gaps.html : 0,
toolTipTextStyle
)
subMarkupText != null && subMarkupTextList.push(subMarkupText)
})
var subMarkupText =
ctx.renderMode === 'richText'
? subMarkupTextList.join(gaps.richText)
: wrapBlockHTML(
subMarkupTextList.join(''),
noHeader ? topMarginForOuterGap : gaps.html
)
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
)
}
}
function buildNameValue(
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 useUTC = ctx.useUTC
var valueFormatter =
fragment.valueFormatter ||
ctx.valueFormatter ||
function (value) {
value = isArray(value) ? value : [value]
return map$1(value, function (val, idx) {
return makeValueReadable(
val,
isArray(valueTypeOption)
? valueTypeOption[idx]
: valueTypeOption,
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
? []
: valueFormatter(fragment.value)
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
)
}
/**
* @return markupText. null/undefined means no content.
*/ function buildTooltipMarkup(
fragment,
markupStyleCreator,
renderMode,
orderMode,
useUTC,
toolTipTextStyle
) {
if (!fragment) {
return
}
var builder = getBuilder(fragment)
var ctx = {
useUTC: useUTC,
renderMode: renderMode,
orderMode: orderMode,
markupStyleCreator: markupStyleCreator,
valueFormatter: fragment.valueFormatter
}
return builder(ctx, fragment, 0, toolTipTextStyle)
}
function getGap(gapLevel) {
return {
html: HTML_GAPS[gapLevel],
richText: RICH_TEXT_GAPS[gapLevel]
}
}
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
: ''
valueList = isArray(valueList) ? valueList : [valueList]
return (
'' + // Value has commas inside, so use ' ' as delimiter for multiple values.
map$1(valueList, function (value) {
return encodeHTML(value)
}).join(' ') +
''
)
}
function wrapInlineNameRichText(ctx, name, style) {
return ctx.markupStyleCreator.wrapRichTextStyle(name, style)
}
function wrapInlineValueRichText(
ctx,
values,
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(
isArray(values) ? values.join(' ') : values,
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 {
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$7(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$1(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$7(tooltipDims, function (dim) {
setEachItem(retrieveRawValue(data, dataIndex, dim), dim)
}) // By default, all dims is used on tooltip.
: each$7(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$a = 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$a(this).sourceManager =
new SourceManager(this))
sourceManager.prepareSource()
var data = this.getInitialData(option, ecModel)
wrapData(data, this)
this.dataTask.context.data = data
inner$a(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$1.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$a(this).sourceManager
sourceManager.dirty()
sourceManager.prepareSource()
var data = this.getInitialData(newSeriesOption, ecModel)
wrapData(data, this)
this.dataTask.dirty()
this.dataTask.context.data = data
inner$a(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$a(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$a(this).data = data
}
SeriesModel.prototype.getEncode = function () {
var encode = this.get('encode', true)
if (encode) {
return createHashMap(encode)
}
}
SeriesModel.prototype.getSourceManager = function () {
return inner$a(this).sourceManager
}
SeriesModel.prototype.getSource = function () {
return this.getSourceManager().getSource()
}
/**
* Get data before processed
*/ SeriesModel.prototype.getRawData = function () {
return inner$a(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 () {
var ecModel = this.ecModel // Disable animation if using echarts in node but not give ssr flag.
// In ssr mode, renderToString will generate svg with css animation.
if (env$1.node && !(ecModel && ecModel.ssr)) {
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 selectedMode = this.option.selectedMode
var data = this.getData(dataType)
if (selectedMode === 'series' || selectedMap === 'all') {
this.option.selectedMap = {}
this._selectedDataIndicesMap = {}
return
}
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 () {
if (this.option.selectedMap === 'all') {
return [].slice.call(this.getData().getIndices())
}
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)
return (
(selectedMap === 'all' ||
selectedMap[getSelectionKey(data, dataIndex)]) &&
!data.getItemModel(dataIndex).get(['select', 'disabled'])
)
}
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 option = this.option
var selectedMode = option.selectedMode
var len = innerDataIndices.length
if (!selectedMode || !len) {
return
}
if (selectedMode === 'series') {
option.selectedMap = 'all'
} else if (selectedMode === 'multiple') {
if (!isObject$2(option.selectedMap)) {
option.selectedMap = {}
}
var selectedMap = 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)
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$1.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$1)
mixin(SeriesModel, DataFormatMixin)
mixin(SeriesModel, PaletteMixin)
mountExtend(SeriesModel, ComponentModel$1)
/**
* 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$7(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$7(
concatArray(data.CHANGABLE_METHODS, data.DOWNSAMPLE_METHODS),
function (methodName) {
data.wrapMethod(methodName, curry$1(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 SeriesModel$1 = SeriesModel
var ComponentView = /** @class */ (function () {
function ComponentView() {
this.group = new Group$3()
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 toggle blur target series.
* Can be used in marker for blur or leave blur the markers
*/ ComponentView.prototype.toggleBlurSeries = function (
seriesModels,
isBlur,
ecModel
) {
// Do nothing;
}
/**
* Traverse the new rendered elements.
*
* It will traverse the new added element in progressive rendering.
* And traverse all in normal rendering.
*/ ComponentView.prototype.eachRendered = function (cb) {
var group = this.group
if (group) {
group.traverse(cb)
}
}
return ComponentView
})()
enableClassExtend(ComponentView)
enableClassManagement(ComponentView)
var ComponentView$1 = 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$9 = makeInner()
var renderPlanner = createRenderPlanner()
var ChartView = /** @class */ (function () {
function ChartView() {
this.group = new Group$3()
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
) {
var data = seriesModel.getData(payload && payload.dataType)
if (!data) {
return
}
toggleHighlight(data, payload, 'emphasis')
}
/**
* Downplay series or specified data item.
*/ ChartView.prototype.downplay = function (
seriesModel,
ecModel,
api,
payload
) {
var data = seriesModel.getData(payload && payload.dataType)
if (!data) {
return
}
toggleHighlight(data, 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)
}
/**
* Traverse the new rendered elements.
*
* It will traverse the new added element in progressive rendering.
* And traverse all in normal rendering.
*/ ChartView.prototype.eachRendered = function (cb) {
traverseElements(this.group, cb)
}
ChartView.markUpdateMethod = function (payload, methodName) {
inner$9(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 && isHighDownDispatcher(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$7(normalizeToArray(dataIndex), function (dataIdx) {
elSetState(
data.getItemGraphicEl(dataIdx),
state,
highlightDigit
)
})
} else {
data.eachItemGraphicEl(function (el) {
elSetState(el, state, highlightDigit)
})
}
}
enableClassExtend(ChartView)
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$9(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 progress(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 progress(params, context) {
context.view.render(
context.model,
context.ecModel,
context.api,
context.payload
)
}
}
}
var ChartView$1 = ChartView
/*
* 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 cb() {
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$1(obj, fnAttr) {
var fn = obj[fnAttr]
if (fn && fn[ORIGIN_METHOD]) {
// Clear throttle
fn.clear && fn.clear()
obj[fnAttr] = fn[ORIGIN_METHOD]
}
}
var inner$8 = 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 reset(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' || isFunction(globalStyle.fill)
? colorPalette
: globalStyle.fill
globalStyle.stroke =
globalStyle.stroke === 'auto' ||
isFunction(globalStyle.stroke)
? 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 dataEach(data, idx) {
var dataParams = seriesModel.getDataParams(idx)
var itemStyle = extend({}, globalStyle)
itemStyle[colorKey] = colorCallback(dataParams)
data.setItemVisual(idx, 'style', itemStyle)
}
}
}
}
}
var sharedModel = new Model$1()
var dataStyleTask = {
createOnAllSeries: true,
performRawSeries: true,
reset: function reset(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 overallReset(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$8(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$8(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$1 = 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$3()
var mask = new Rect$2({
style: { fill: opts.maskColor },
zlevel: opts.zlevel,
z: 10000
})
group.add(mask)
var textContent = new ZRText$1({
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$2({
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$1({
shape: {
startAngle: -PI$1 / 2,
endAngle: -PI$1 / 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$1 * 3) / 2 })
.start('circularInOut')
arc
.animateShape(true)
.when(1000, { startAngle: (PI$1 * 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$7(
this._allHandlers,
function (handler) {
var record =
stageTaskMap.get(handler.uid) ||
stageTaskMap.set(handler.uid, {})
var errMsg = ''
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$7(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 = ''
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$7(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$1(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$1)
mockMethods(apiMock, ExtensionAPI$1)
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 */
}
var Scheduler$1 = Scheduler
/*
* 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 axisCommon() {
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
var darkTheme = theme
/**
* 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$7(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 SYMBOL_PROPS_WITH_CB = [
'symbol',
'symbolSize',
'symbolRotate',
'symbolOffset'
]
var SYMBOL_PROPS = SYMBOL_PROPS_WITH_CB.concat(['symbolKeepAspect']) // Encoding visual for all series include which is filtered for legend drawing
var seriesSymbolTask = {
createOnAllSeries: true, // For legend.
performRawSeries: true,
reset: function reset(seriesModel, ecModel) {
var data = seriesModel.getData()
if (seriesModel.legendIcon) {
data.setVisual('legendIcon', seriesModel.legendIcon)
}
if (!seriesModel.hasSymbolVisual) {
return
}
var symbolOptions = {}
var symbolOptionsCb = {}
var hasCallback = false
for (var i = 0; i < SYMBOL_PROPS_WITH_CB.length; i++) {
var symbolPropName = SYMBOL_PROPS_WITH_CB[i]
var val = seriesModel.get(symbolPropName)
if (isFunction(val)) {
hasCallback = true
symbolOptionsCb[symbolPropName] = val
} else {
symbolOptions[symbolPropName] = val
}
}
symbolOptions.symbol =
symbolOptions.symbol || seriesModel.defaultSymbol
data.setVisual(
extend(
{
legendIcon: seriesModel.legendIcon || symbolOptions.symbol,
symbolKeepAspect: seriesModel.get('symbolKeepAspect')
},
symbolOptions
)
) // Only visible series has each data be visual encoded
if (ecModel.isSeriesFiltered(seriesModel)) {
return
}
var symbolPropsCb = keys(symbolOptionsCb)
function dataEach(data, idx) {
var rawValue = seriesModel.getRawValue(idx)
var params = seriesModel.getDataParams(idx)
for (var i = 0; i < symbolPropsCb.length; i++) {
var symbolPropName = symbolPropsCb[i]
data.setItemVisual(
idx,
symbolPropName,
symbolOptionsCb[symbolPropName](rawValue, params)
)
}
}
return { dataEach: hasCallback ? dataEach : null }
}
}
var dataSymbolTask = {
createOnAllSeries: true, // For legend.
performRawSeries: true,
reset: function reset(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)
for (var i = 0; i < SYMBOL_PROPS.length; i++) {
var symbolPropName = SYMBOL_PROPS[i]
var val = itemModel.getShallow(symbolPropName, true)
if (val != null) {
data.setItemVisual(idx, symbolPropName, val)
}
}
}
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)
}
}
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)
}
} // 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$7(
[
[seriesType + 'ToggleSelect', 'toggleSelect'],
[seriesType + 'Select', 'select'],
[seriesType + 'UnSelect', 'unselect']
],
function (eventsMap) {
ecRegisterAction(
eventsMap[0],
function (payload, ecModel, api) {
payload = extend({}, payload)
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)) {
ecModel.eachComponent(
{ mainType: 'series', subType: 'pie' },
function (seriesModel) {
var seriesIndex = seriesModel.seriesIndex
var selectedMap = seriesModel.option.selectedMap
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: isString(selectedMap)
? selectedMap
: extend({}, 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
})()
var WeakMap$1 = WeakMap
/**
* Triangle shape
* @inner
*/ var Triangle = Path$1.extend({
type: 'triangle',
shape: { cx: 0, cy: 0, width: 0, height: 0 },
buildPath: function buildPath(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$1.extend({
type: 'diamond',
shape: { cx: 0, cy: 0, width: 0, height: 0 },
buildPath: function buildPath(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$1.extend({
type: 'pin',
shape: {
// x, y on the cusp
x: 0,
y: 0,
width: 0,
height: 0
},
buildPath: function buildPath(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$1.extend({
type: 'arrow',
shape: { x: 0, y: 0, width: 0, height: 0 },
buildPath: function buildPath(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$1,
rect: Rect$2,
roundRect: Rect$2,
square: Rect$2,
circle: Circle$1,
diamond: Diamond,
pin: Pin,
arrow: Arrow,
triangle: Triangle
}
var symbolShapeMakers = {
line: function line(x, y, w, h, shape) {
shape.x1 = x
shape.y1 = y + h / 2
shape.x2 = x + w
shape.y2 = y + h / 2
},
rect: function rect(x, y, w, h, shape) {
shape.x = x
shape.y = y
shape.width = w
shape.height = h
},
roundRect: function roundRect(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 square(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 circle(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 diamond(x, y, w, h, shape) {
shape.cx = x + w / 2
shape.cy = y + h / 2
shape.width = w
shape.height = h
},
pin: function pin(x, y, w, h, shape) {
shape.x = x + w / 2
shape.y = y + h / 2
shape.width = w
shape.height = h
},
arrow: function arrow(x, y, w, h, shape) {
shape.x = x + w / 2
shape.y = y + h / 2
shape.width = w
shape.height = h
},
triangle: function triangle(x, y, w, h, shape) {
shape.cx = x + w / 2
shape.cy = y + h / 2
shape.width = w
shape.height = h
}
}
var symbolBuildProxies = {}
each$7(symbolCtors, function (Ctor, name) {
symbolBuildProxies[name] = new Ctor()
})
var SymbolClz$1 = Path$1.extend({
type: 'symbol',
shape: { symbolType: '', x: 0, y: 0, width: 0, height: 0 },
calculateTextPosition: function calculateTextPosition(
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 buildPath(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$1(x, y, w, h),
keepAspect ? 'center' : 'cover'
)
} else if (symbolType.indexOf('path://') === 0) {
symbolPath = makePath(
symbolType.slice(7),
{},
new BoundingRect$1(x, y, w, h),
keepAspect ? 'center' : 'cover'
)
} else {
symbolPath = new SymbolClz$1({
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(symbolOffset[0], symbolSize[0]) || 0,
parsePercent(
retrieve2(symbolOffset[1], symbolOffset[0]),
symbolSize[1]
) || 0
]
}
function isSafeNum(num) {
return isFinite(num)
}
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 = isSafeNum(x) ? x : 0
x2 = isSafeNum(x2) ? x2 : 1
y = isSafeNum(y) ? y : 0
y2 = isSafeNum(y2) ? y2 : 0
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
}
x = isSafeNum(x) ? x : 0.5
y = isSafeNum(y) ? y : 0.5
r = r >= 0 && isSafeNum(r) ? r : 0.5
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 parseInt10(val) {
return parseInt(val, 10)
}
function getSize$1(root, whIdx, opts) {
var wh = ['width', 'height'][whIdx]
var cwh = ['clientWidth', 'clientHeight'][whIdx]
var plt = ['paddingLeft', 'paddingTop'][whIdx]
var prb = ['paddingRight', 'paddingBottom'][whIdx]
if (opts[wh] != null && opts[wh] !== 'auto') {
return parseFloat(opts[wh])
}
var stl = document.defaultView.getComputedStyle(root)
return (
((root[cwh] ||
parseInt10(stl[wh]) ||
parseInt10(root.style[wh])) -
(parseInt10(stl[plt]) || 0) -
(parseInt10(stl[prb]) || 0)) |
0
)
}
function normalizeLineDash(lineType, lineWidth) {
if (!lineType || lineType === 'solid' || !(lineWidth > 0)) {
return null
}
return lineType === 'dashed'
? [4 * lineWidth, 2 * lineWidth]
: lineType === 'dotted'
? [lineWidth]
: isNumber(lineType)
? [lineType]
: isArray(lineType)
? lineType
: null
}
function getLineDash(el) {
var style = el.style
var lineDash =
style.lineDash &&
style.lineWidth > 0 &&
normalizeLineDash(style.lineDash, style.lineWidth)
var lineDashOffset = style.lineDashOffset
if (lineDash) {
var lineScale_1 =
style.strokeNoScale && el.getLineScale ? el.getLineScale() : 1
if (lineScale_1 && lineScale_1 !== 1) {
lineDash = map$1(lineDash, function (rawVal) {
return rawVal / lineScale_1
})
lineDashOffset /= lineScale_1
}
}
return [lineDash, lineDashOffset]
}
var pathProxyForDraw = new PathProxy$1(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 &&
canvasPattern.setTransform
) {
var matrix = new DOMMatrix()
matrix.translateSelf(pattern.x || 0, pattern.y || 0)
matrix.rotateSelf(
0,
0,
(pattern.rotation || 0) * RADIAN_TO_DEGREE
)
matrix.scaleSelf(pattern.scaleX || 1, pattern.scaleY || 1)
canvasPattern.setTransform(matrix)
}
return canvasPattern
}
}
function brushPath(ctx, el, style, inBatch) {
var _a
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
var dirtyFlag = el.__dirty
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 = dirtyFlag
? getCanvasGradient(ctx, fill, rect)
: el.__canvasFillGradient
el.__canvasFillGradient = fillGradient
}
if (hasStrokeGradient) {
strokeGradient = dirtyFlag
? getCanvasGradient(ctx, stroke, rect)
: el.__canvasStrokeGradient
el.__canvasStrokeGradient = strokeGradient
}
if (hasFillPattern) {
fillPattern =
dirtyFlag || !el.__canvasFillPattern
? createCanvasPattern(ctx, fill, el)
: el.__canvasFillPattern
el.__canvasFillPattern = fillPattern
}
if (hasStrokePattern) {
strokePattern =
dirtyFlag || !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 scale = el.getGlobalScale()
path.setScale(scale[0], scale[1], el.segmentIgnoreThreshold)
var lineDash
var lineDashOffset
if (ctx.setLineDash && style.lineDash) {
;(_a = getLineDash(el)),
(lineDash = _a[0]),
(lineDashOffset = _a[1])
}
var needsRebuild = true
if (firstDraw || dirtyFlag & SHAPE_CHANGED_BIT) {
path.setDPR(ctx.dpr)
if (strokePart) {
path.setContext(null)
} else {
path.setContext(ctx)
needsRebuild = false
}
path.reset()
el.buildPath(path, el.shape, inBatch)
path.toStatic()
el.pathUpdated()
}
if (needsRebuild) {
path.rebuildPath(ctx, strokePart ? strokePercent : 1)
}
if (lineDash) {
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) {
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 _a
var text = style.text
text != null && (text += '')
if (text) {
ctx.font = style.font || DEFAULT_FONT
ctx.textAlign = style.textAlign
ctx.textBaseline = style.textBaseline
var lineDash = void 0
var lineDashOffset = void 0
if (ctx.setLineDash && style.lineDash) {
;(_a = getLineDash(el)),
(lineDash = _a[0]),
(lineDashOffset = _a[1])
}
if (lineDash) {
ctx.setLineDash(lineDash)
ctx.lineDashOffset = lineDashOffset
}
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 (lineDash) {
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) {
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.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$1 && 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$1) {
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$1) {
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$1) {
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.getTemporalDisplayables) {
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$1()
var decalCache = new LRU$1(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]]
if (
value != null &&
!isArray(value) &&
!isString(value) &&
!isNumber(value) &&
typeof value !== '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 && platformApi.createCanvas()
var svgRoot = isSVG && {
tag: 'g',
attrs: {},
key: 'dcl',
children: []
}
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
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) {
var symbolVNode = zr.painter.renderOneToVNode(symbol)
if (symbolVNode) {
svgRoot.children.push(symbolVNode)
}
} 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 (isString(symbol)) {
return [[symbol]]
}
var isAllString = true
for (var i = 0; i < symbol.length; ++i) {
if (!isString(symbol[i])) {
isAllString = false
break
}
}
if (isAllString) {
return normalizeSymbolArray([symbol])
}
var result = []
for (var i = 0; i < symbol.length; ++i) {
if (isString(symbol[i])) {
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 (isNumber(dash)) {
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 (!isNumber(dash[i])) {
isAllNumber = false
break
}
}
if (isAllNumber) {
return normalizeDashArrayX([dash])
}
var result = []
for (var i = 0; i < dash.length; ++i) {
if (isNumber(dash[i])) {
var dashValue = Math.ceil(dash[i])
result.push([dashValue, dashValue])
} else {
var dashValue = map$1(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 (isNumber(dash)) {
var dashValue_1 = Math.ceil(dash)
return [dashValue_1, dashValue_1]
}
var dashValue = map$1(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$1(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)
}
})
}
var lifecycle = new Eventful$1()
var lifecycle$1 = lifecycle // The implentations will be registered when installing the component.
// Avoid these code being bundled to the core module.
var implsStore = {} // TODO Type
function registerImpl(name, impl) {
implsStore[name] = impl
}
function getImpl(name) {
return implsStore[name]
}
var hasWindow = typeof window !== 'undefined'
var TEST_FRAME_REMAIN_TIME = 1
var PRIORITY_PROCESSOR_SERIES_FILTER = 800 // Some data processors depends on the stack result dimension (to calculate data extent).
// So data stack stage should be in front of data processing stage.
var PRIORITY_PROCESSOR_DATASTACK = 900 // "Data filter" will block the stream, so it should be
// put at the begining of data processing.
var PRIORITY_PROCESSOR_FILTER = 1000
var PRIORITY_PROCESSOR_DEFAULT = 2000
var PRIORITY_PROCESSOR_STATISTIC = 5000
var PRIORITY_VISUAL_LAYOUT = 1000
var PRIORITY_VISUAL_PROGRESSIVE_LAYOUT = 1100
var PRIORITY_VISUAL_GLOBAL = 2000
var PRIORITY_VISUAL_CHART = 3000
var PRIORITY_VISUAL_COMPONENT = 4000 // Visual property in data. Greater than `PRIORITY_VISUAL_COMPONENT` to enable to
// overwrite the viusal result of component (like `visualMap`)
// using data item specific setting (like itemStyle.xxx on data item)
var PRIORITY_VISUAL_CHART_DATA_CUSTOM = 4500 // Greater than `PRIORITY_VISUAL_CHART_DATA_CUSTOM` to enable to layout based on
// visual result like `symbolSize`.
var PRIORITY_VISUAL_POST_CHART_LAYOUT = 4600
var PRIORITY_VISUAL_BRUSH = 5000
var PRIORITY_VISUAL_ARIA = 6000
var PRIORITY_VISUAL_DECAL = 7000
var PRIORITY = {
PROCESSOR: {
FILTER: PRIORITY_PROCESSOR_FILTER,
SERIES_FILTER: PRIORITY_PROCESSOR_SERIES_FILTER,
STATISTIC: PRIORITY_PROCESSOR_STATISTIC
},
VISUAL: {
LAYOUT: PRIORITY_VISUAL_LAYOUT,
PROGRESSIVE_LAYOUT: PRIORITY_VISUAL_PROGRESSIVE_LAYOUT,
GLOBAL: PRIORITY_VISUAL_GLOBAL,
CHART: PRIORITY_VISUAL_CHART,
POST_CHART_LAYOUT: PRIORITY_VISUAL_POST_CHART_LAYOUT,
COMPONENT: PRIORITY_VISUAL_COMPONENT,
BRUSH: PRIORITY_VISUAL_BRUSH,
CHART_ITEM: PRIORITY_VISUAL_CHART_DATA_CUSTOM,
ARIA: PRIORITY_VISUAL_ARIA,
DECAL: PRIORITY_VISUAL_DECAL
}
} // Main process have three entries: `setOption`, `dispatchAction` and `resize`,
// where they must not be invoked nestedly, except the only case: invoke
// dispatchAction with updateMethod "none" in main process.
// This flag is used to carry out this rule.
// All events will be triggered out side main process (i.e. when !this[IN_MAIN_PROCESS]).
var IN_MAIN_PROCESS_KEY = '__flagInMainProcess'
var PENDING_UPDATE = '__pendingUpdate'
var STATUS_NEEDS_UPDATE_KEY = '__needsUpdateStatus'
var ACTION_REG = /^[a-zA-Z0-9_]+$/
var CONNECT_STATUS_KEY = '__connectUpdateStatus'
var CONNECT_STATUS_PENDING = 0
var CONNECT_STATUS_UPDATING = 1
var CONNECT_STATUS_UPDATED = 2
function createRegisterEventWithLowercaseECharts(method) {
return function () {
var args = []
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i]
}
if (this.isDisposed()) {
disposedWarning(this.id)
return
}
return toLowercaseNameAndCallEventful(this, method, args)
}
}
function createRegisterEventWithLowercaseMessageCenter(method) {
return function () {
var args = []
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i]
}
return toLowercaseNameAndCallEventful(this, method, args)
}
}
function toLowercaseNameAndCallEventful(host, method, args) {
// `args[0]` is event name. Event name is all lowercase.
args[0] = args[0] && args[0].toLowerCase()
return Eventful$1.prototype[method].apply(host, args)
}
var MessageCenter = /** @class */ (function (_super) {
__extends(MessageCenter, _super)
function MessageCenter() {
return (_super !== null && _super.apply(this, arguments)) || this
}
return MessageCenter
})(Eventful$1)
var messageCenterProto = MessageCenter.prototype
messageCenterProto.on =
createRegisterEventWithLowercaseMessageCenter('on')
messageCenterProto.off =
createRegisterEventWithLowercaseMessageCenter('off') // ---------------------------------------
// Internal method names for class ECharts
// ---------------------------------------
var prepare
var prepareView
var updateDirectly
var updateMethods
var doConvertPixel
var updateStreamModes
var doDispatchAction
var flushPendingActions
var triggerUpdatedEvent
var bindRenderedEvent
var bindMouseEvent
var render
var renderComponents
var renderSeries
var createExtensionAPI
var enableConnect
var markStatusToUpdate
var applyChangedStates
var ECharts = /** @class */ (function (_super) {
__extends(ECharts, _super)
function ECharts(
dom, // Theme name or themeOption.
theme,
opts
) {
var _this = _super.call(this, new ECEventProcessor()) || this
_this._chartsViews = []
_this._chartsMap = {}
_this._componentsViews = []
_this._componentsMap = {} // Can't dispatch action during rendering procedure
_this._pendingActions = []
opts = opts || {} // Get theme by name
if (isString(theme)) {
theme = themeStorage[theme]
}
_this._dom = dom
var defaultRenderer = 'canvas'
var defaultUseDirtyRect = false
var zr = (_this._zr = init$1(dom, {
renderer: opts.renderer || defaultRenderer,
devicePixelRatio: opts.devicePixelRatio,
width: opts.width,
height: opts.height,
ssr: opts.ssr,
useDirtyRect:
opts.useDirtyRect == null
? defaultUseDirtyRect
: opts.useDirtyRect
}))
_this._ssr = opts.ssr // Expect 60 fps.
_this._throttledZrFlush = throttle(bind$1(zr.flush, zr), 17)
theme = clone$3(theme)
theme && globalBackwardCompat(theme, true)
_this._theme = theme
_this._locale = createLocaleObject(opts.locale || SYSTEM_LANG)
_this._coordSysMgr = new CoordinateSystem()
var api = (_this._api = createExtensionAPI(_this)) // Sort on demand
function prioritySortFunc(a, b) {
return a.__prio - b.__prio
}
sort(visualFuncs, prioritySortFunc)
sort(dataProcessorFuncs, prioritySortFunc)
_this._scheduler = new Scheduler$1(
_this,
api,
dataProcessorFuncs,
visualFuncs
)
_this._messageCenter = new MessageCenter() // Init mouse events
_this._initEvents() // In case some people write `window.onresize = chart.resize`
_this.resize = bind$1(_this.resize, _this)
zr.animation.on('frame', _this._onframe, _this)
bindRenderedEvent(zr, _this)
bindMouseEvent(zr, _this) // ECharts instance can be used as value.
setAsPrimitive(_this)
return _this
}
ECharts.prototype._onframe = function () {
if (this._disposed) {
return
}
applyChangedStates(this)
var scheduler = this._scheduler // Lazy update
if (this[PENDING_UPDATE]) {
var silent = this[PENDING_UPDATE].silent
this[IN_MAIN_PROCESS_KEY] = true
try {
prepare(this)
updateMethods.update.call(
this,
null,
this[PENDING_UPDATE].updateParams
)
} catch (e) {
this[IN_MAIN_PROCESS_KEY] = false
this[PENDING_UPDATE] = null
throw e
} // At present, in each frame, zrender performs:
// (1) animation step forward.
// (2) trigger('frame') (where this `_onframe` is called)
// (3) zrender flush (render).
// If we do nothing here, since we use `setToFinal: true`, the step (3) above
// will render the final state of the elements before the real animation started.
this._zr.flush()
this[IN_MAIN_PROCESS_KEY] = false
this[PENDING_UPDATE] = null
flushPendingActions.call(this, silent)
triggerUpdatedEvent.call(this, silent)
} // Avoid do both lazy update and progress in one frame.
else if (scheduler.unfinished) {
// Stream progress.
var remainTime = TEST_FRAME_REMAIN_TIME
var ecModel = this._model
var api = this._api
scheduler.unfinished = false
do {
var startTime = +new Date()
scheduler.performSeriesTasks(ecModel) // Currently dataProcessorFuncs do not check threshold.
scheduler.performDataProcessorTasks(ecModel)
updateStreamModes(this, ecModel) // Do not update coordinate system here. Because that coord system update in
// each frame is not a good user experience. So we follow the rule that
// the extent of the coordinate system is determin in the first frame (the
// frame is executed immedietely after task reset.
// this._coordSysMgr.update(ecModel, api);
// console.log('--- ec frame visual ---', remainTime);
scheduler.performVisualTasks(ecModel)
renderSeries(this, this._model, api, 'remain', {})
remainTime -= +new Date() - startTime
} while (remainTime > 0 && scheduler.unfinished) // Call flush explicitly for trigger finished event.
if (!scheduler.unfinished) {
this._zr.flush()
} // Else, zr flushing be ensue within the same frame,
// because zr flushing is after onframe event.
}
}
ECharts.prototype.getDom = function () {
return this._dom
}
ECharts.prototype.getId = function () {
return this.id
}
ECharts.prototype.getZr = function () {
return this._zr
}
ECharts.prototype.isSSR = function () {
return this._ssr
}
/* eslint-disable-next-line */ ECharts.prototype.setOption =
function (option, notMerge, lazyUpdate) {
if (this[IN_MAIN_PROCESS_KEY]) {
return
}
if (this._disposed) {
disposedWarning(this.id)
return
}
var silent
var replaceMerge
var transitionOpt
if (isObject$2(notMerge)) {
lazyUpdate = notMerge.lazyUpdate
silent = notMerge.silent
replaceMerge = notMerge.replaceMerge
transitionOpt = notMerge.transition
notMerge = notMerge.notMerge
}
this[IN_MAIN_PROCESS_KEY] = true
if (!this._model || notMerge) {
var optionManager = new OptionManager$1(this._api)
var theme = this._theme
var ecModel = (this._model = new GlobalModel$1())
ecModel.scheduler = this._scheduler
ecModel.ssr = this._ssr
ecModel.init(
null,
null,
null,
theme,
this._locale,
optionManager
)
}
this._model.setOption(
option,
{ replaceMerge: replaceMerge },
optionPreprocessorFuncs
)
var updateParams = {
seriesTransition: transitionOpt,
optionChanged: true
}
if (lazyUpdate) {
this[PENDING_UPDATE] = {
silent: silent,
updateParams: updateParams
}
this[IN_MAIN_PROCESS_KEY] = false // `setOption(option, {lazyMode: true})` may be called when zrender has been slept.
// It should wake it up to make sure zrender start to render at the next frame.
this.getZr().wakeUp()
} else {
try {
prepare(this)
updateMethods.update.call(this, null, updateParams)
} catch (e) {
this[PENDING_UPDATE] = null
this[IN_MAIN_PROCESS_KEY] = false
throw e
} // Ensure zr refresh sychronously, and then pixel in canvas can be
// fetched after `setOption`.
if (!this._ssr) {
// not use flush when using ssr mode.
this._zr.flush()
}
this[PENDING_UPDATE] = null
this[IN_MAIN_PROCESS_KEY] = false
flushPendingActions.call(this, silent)
triggerUpdatedEvent.call(this, silent)
}
}
/**
* @deprecated
*/ ECharts.prototype.setTheme = function () {} // We don't want developers to use getModel directly.
ECharts.prototype.getModel = function () {
return this._model
}
ECharts.prototype.getOption = function () {
return this._model && this._model.getOption()
}
ECharts.prototype.getWidth = function () {
return this._zr.getWidth()
}
ECharts.prototype.getHeight = function () {
return this._zr.getHeight()
}
ECharts.prototype.getDevicePixelRatio = function () {
return (
this._zr.painter.dpr /* eslint-disable-next-line */ ||
(hasWindow && window.devicePixelRatio) ||
1
)
}
/**
* Get canvas which has all thing rendered
* @deprecated Use renderToCanvas instead.
*/ ECharts.prototype.getRenderedCanvas = function (opts) {
return this.renderToCanvas(opts)
}
ECharts.prototype.renderToCanvas = function (opts) {
opts = opts || {}
var painter = this._zr.painter
return painter.getRenderedCanvas({
backgroundColor:
opts.backgroundColor || this._model.get('backgroundColor'),
pixelRatio: opts.pixelRatio || this.getDevicePixelRatio()
})
}
ECharts.prototype.renderToSVGString = function (opts) {
opts = opts || {}
var painter = this._zr.painter
return painter.renderToString({ useViewBox: opts.useViewBox })
}
/**
* Get svg data url
*/ ECharts.prototype.getSvgDataURL = function () {
if (!env$1.svgSupported) {
return
}
var zr = this._zr
var list = zr.storage.getDisplayList() // Stop animations
each$7(list, function (el) {
el.stopAnimation(null, true)
})
return zr.painter.toDataURL()
}
ECharts.prototype.getDataURL = function (opts) {
if (this._disposed) {
disposedWarning(this.id)
return
}
opts = opts || {}
var excludeComponents = opts.excludeComponents
var ecModel = this._model
var excludesComponentViews = []
var self = this
each$7(excludeComponents, function (componentType) {
ecModel.eachComponent(
{ mainType: componentType },
function (component) {
var view = self._componentsMap[component.__viewId]
if (!view.group.ignore) {
excludesComponentViews.push(view)
view.group.ignore = true
}
}
)
})
var url =
this._zr.painter.getType() === 'svg'
? this.getSvgDataURL()
: this.renderToCanvas(opts).toDataURL(
'image/' + ((opts && opts.type) || 'png')
)
each$7(excludesComponentViews, function (view) {
view.group.ignore = false
})
return url
}
ECharts.prototype.getConnectedDataURL = function (opts) {
if (this._disposed) {
disposedWarning(this.id)
return
}
var isSvg = opts.type === 'svg'
var groupId = this.group
var mathMin = Math.min
var mathMax = Math.max
var MAX_NUMBER = Infinity
if (connectedGroups[groupId]) {
var left_1 = MAX_NUMBER
var top_1 = MAX_NUMBER
var right_1 = -MAX_NUMBER
var bottom_1 = -MAX_NUMBER
var canvasList_1 = []
var dpr_1 =
(opts && opts.pixelRatio) || this.getDevicePixelRatio()
each$7(instances, function (chart, id) {
if (chart.group === groupId) {
var canvas = isSvg
? chart.getZr().painter.getSvgDom().innerHTML
: chart.renderToCanvas(clone$3(opts))
var boundingRect = chart.getDom().getBoundingClientRect()
left_1 = mathMin(boundingRect.left, left_1)
top_1 = mathMin(boundingRect.top, top_1)
right_1 = mathMax(boundingRect.right, right_1)
bottom_1 = mathMax(boundingRect.bottom, bottom_1)
canvasList_1.push({
dom: canvas,
left: boundingRect.left,
top: boundingRect.top
})
}
})
left_1 *= dpr_1
top_1 *= dpr_1
right_1 *= dpr_1
bottom_1 *= dpr_1
var width = right_1 - left_1
var height = bottom_1 - top_1
var targetCanvas = platformApi.createCanvas()
var zr_1 = init$1(targetCanvas, {
renderer: isSvg ? 'svg' : 'canvas'
})
zr_1.resize({ width: width, height: height })
if (isSvg) {
var content_1 = ''
each$7(canvasList_1, function (item) {
var x = item.left - left_1
var y = item.top - top_1
content_1 +=
'' +
item.dom +
''
})
zr_1.painter.getSvgRoot().innerHTML = content_1
if (opts.connectedBackgroundColor) {
zr_1.painter.setBackgroundColor(
opts.connectedBackgroundColor
)
}
zr_1.refreshImmediately()
return zr_1.painter.toDataURL()
} else {
// Background between the charts
if (opts.connectedBackgroundColor) {
zr_1.add(
new Rect$2({
shape: { x: 0, y: 0, width: width, height: height },
style: { fill: opts.connectedBackgroundColor }
})
)
}
each$7(canvasList_1, function (item) {
var img = new ZRImage$1({
style: {
x: item.left * dpr_1 - left_1,
y: item.top * dpr_1 - top_1,
image: item.dom
}
})
zr_1.add(img)
})
zr_1.refreshImmediately()
return targetCanvas.toDataURL(
'image/' + ((opts && opts.type) || 'png')
)
}
} else {
return this.getDataURL(opts)
}
}
ECharts.prototype.convertToPixel = function (finder, value) {
return doConvertPixel(this, 'convertToPixel', finder, value)
}
ECharts.prototype.convertFromPixel = function (finder, value) {
return doConvertPixel(this, 'convertFromPixel', finder, value)
}
/**
* Is the specified coordinate systems or components contain the given pixel point.
* @param {Array|number} value
* @return {boolean} result
*/ ECharts.prototype.containPixel = function (finder, value) {
if (this._disposed) {
disposedWarning(this.id)
return
}
var ecModel = this._model
var result
var findResult = parseFinder$1(ecModel, finder)
each$7(
findResult,
function (models, key) {
key.indexOf('Models') >= 0 &&
each$7(
models,
function (model) {
var coordSys = model.coordinateSystem
if (coordSys && coordSys.containPoint) {
result = result || !!coordSys.containPoint(value)
} else if (key === 'seriesModels') {
var view = this._chartsMap[model.__viewId]
if (view && view.containPoint) {
result = result || view.containPoint(value, model)
}
} else;
},
this
)
},
this
)
return !!result
}
/**
* Get visual from series or data.
* @param finder
* If string, e.g., 'series', means {seriesIndex: 0}.
* If Object, could contain some of these properties below:
* {
* seriesIndex / seriesId / seriesName,
* dataIndex / dataIndexInside
* }
* If dataIndex is not specified, series visual will be fetched,
* but not data item visual.
* If all of seriesIndex, seriesId, seriesName are not specified,
* visual will be fetched from first series.
* @param visualType 'color', 'symbol', 'symbolSize'
*/ ECharts.prototype.getVisual = function (finder, visualType) {
var ecModel = this._model
var parsedFinder = parseFinder$1(ecModel, finder, {
defaultMainType: 'series'
})
var seriesModel = parsedFinder.seriesModel
var data = seriesModel.getData()
var dataIndexInside = parsedFinder.hasOwnProperty(
'dataIndexInside'
)
? parsedFinder.dataIndexInside
: parsedFinder.hasOwnProperty('dataIndex')
? data.indexOfRawIndex(parsedFinder.dataIndex)
: null
return dataIndexInside != null
? getItemVisualFromData(data, dataIndexInside, visualType)
: getVisualFromData(data, visualType)
}
/**
* Get view of corresponding component model
*/ ECharts.prototype.getViewOfComponentModel = function (
componentModel
) {
return this._componentsMap[componentModel.__viewId]
}
/**
* Get view of corresponding series model
*/ ECharts.prototype.getViewOfSeriesModel = function (
seriesModel
) {
return this._chartsMap[seriesModel.__viewId]
}
ECharts.prototype._initEvents = function () {
var _this = this
each$7(MOUSE_EVENT_NAMES, function (eveName) {
var handler = function handler(e) {
var ecModel = _this.getModel()
var el = e.target
var params
var isGlobalOut = eveName === 'globalout' // no e.target when 'globalout'.
if (isGlobalOut) {
params = {}
} else {
el &&
findEventDispatcher(
el,
function (parent) {
var ecData = getECData(parent)
if (ecData && ecData.dataIndex != null) {
var dataModel =
ecData.dataModel ||
ecModel.getSeriesByIndex(ecData.seriesIndex)
params =
(dataModel &&
dataModel.getDataParams(
ecData.dataIndex,
ecData.dataType
)) ||
{}
return true
} // If element has custom eventData of components
else if (ecData.eventData) {
params = extend({}, ecData.eventData)
return true
}
},
true
)
} // Contract: if params prepared in mouse event,
// these properties must be specified:
// {
// componentType: string (component main type)
// componentIndex: number
// }
// Otherwise event query can not work.
if (params) {
var componentType = params.componentType
var componentIndex = params.componentIndex // Special handling for historic reason: when trigger by
// markLine/markPoint/markArea, the componentType is
// 'markLine'/'markPoint'/'markArea', but we should better
// enable them to be queried by seriesIndex, since their
// option is set in each series.
if (
componentType === 'markLine' ||
componentType === 'markPoint' ||
componentType === 'markArea'
) {
componentType = 'series'
componentIndex = params.seriesIndex
}
var model =
componentType &&
componentIndex != null &&
ecModel.getComponent(componentType, componentIndex)
var view =
model &&
_this[
model.mainType === 'series'
? '_chartsMap'
: '_componentsMap'
][model.__viewId]
params.event = e
params.type = eveName
_this._$eventProcessor.eventInfo = {
targetEl: el,
packedEvent: params,
model: model,
view: view
}
_this.trigger(eveName, params)
}
} // Consider that some component (like tooltip, brush, ...)
// register zr event handler, but user event handler might
// do anything, such as call `setOption` or `dispatchAction`,
// which probably update any of the content and probably
// cause problem if it is called previous other inner handlers.
handler.zrEventfulCallAtLast = true
_this._zr.on(eveName, handler, _this)
})
each$7(eventActionMap, function (actionType, eventType) {
_this._messageCenter.on(
eventType,
function (event) {
this.trigger(eventType, event)
},
_this
)
}) // Extra events
// TODO register?
each$7(['selectchanged'], function (eventType) {
_this._messageCenter.on(
eventType,
function (event) {
this.trigger(eventType, event)
},
_this
)
})
handleLegacySelectEvents(this._messageCenter, this, this._api)
}
ECharts.prototype.isDisposed = function () {
return this._disposed
}
ECharts.prototype.clear = function () {
if (this._disposed) {
disposedWarning(this.id)
return
}
this.setOption({ series: [] }, true)
}
ECharts.prototype.dispose = function () {
if (this._disposed) {
disposedWarning(this.id)
return
}
this._disposed = true
var dom = this.getDom()
if (dom) {
setAttribute(this.getDom(), DOM_ATTRIBUTE_KEY, '')
}
var chart = this
var api = chart._api
var ecModel = chart._model
each$7(chart._componentsViews, function (component) {
component.dispose(ecModel, api)
})
each$7(chart._chartsViews, function (chart) {
chart.dispose(ecModel, api)
}) // Dispose after all views disposed
chart._zr.dispose() // Set properties to null.
// To reduce the memory cost in case the top code still holds this instance unexpectedly.
chart._dom =
chart._model =
chart._chartsMap =
chart._componentsMap =
chart._chartsViews =
chart._componentsViews =
chart._scheduler =
chart._api =
chart._zr =
chart._throttledZrFlush =
chart._theme =
chart._coordSysMgr =
chart._messageCenter =
null
delete instances[chart.id]
}
/**
* Resize the chart
*/ ECharts.prototype.resize = function (opts) {
if (this[IN_MAIN_PROCESS_KEY]) {
return
}
if (this._disposed) {
disposedWarning(this.id)
return
}
this._zr.resize(opts)
var ecModel = this._model // Resize loading effect
this._loadingFX && this._loadingFX.resize()
if (!ecModel) {
return
}
var needPrepare = ecModel.resetOption('media')
var silent = opts && opts.silent // There is some real cases that:
// chart.setOption(option, { lazyUpdate: true });
// chart.resize();
if (this[PENDING_UPDATE]) {
if (silent == null) {
silent = this[PENDING_UPDATE].silent
}
needPrepare = true
this[PENDING_UPDATE] = null
}
this[IN_MAIN_PROCESS_KEY] = true
try {
needPrepare && prepare(this)
updateMethods.update.call(this, {
type: 'resize',
animation: extend(
{
// Disable animation
duration: 0
},
opts && opts.animation
)
})
} catch (e) {
this[IN_MAIN_PROCESS_KEY] = false
throw e
}
this[IN_MAIN_PROCESS_KEY] = false
flushPendingActions.call(this, silent)
triggerUpdatedEvent.call(this, silent)
}
ECharts.prototype.showLoading = function (name, cfg) {
if (this._disposed) {
disposedWarning(this.id)
return
}
if (isObject$2(name)) {
cfg = name
name = ''
}
name = name || 'default'
this.hideLoading()
if (!loadingEffects[name]) {
return
}
var el = loadingEffects[name](this._api, cfg)
var zr = this._zr
this._loadingFX = el
zr.add(el)
}
/**
* Hide loading effect
*/ ECharts.prototype.hideLoading = function () {
if (this._disposed) {
disposedWarning(this.id)
return
}
this._loadingFX && this._zr.remove(this._loadingFX)
this._loadingFX = null
}
ECharts.prototype.makeActionFromEvent = function (eventObj) {
var payload = extend({}, eventObj)
payload.type = eventActionMap[eventObj.type]
return payload
}
/**
* @param opt If pass boolean, means opt.silent
* @param opt.silent Default `false`. Whether trigger events.
* @param opt.flush Default `undefined`.
* true: Flush immediately, and then pixel in canvas can be fetched
* immediately. Caution: it might affect performance.
* false: Not flush.
* undefined: Auto decide whether perform flush.
*/ ECharts.prototype.dispatchAction = function (payload, opt) {
if (this._disposed) {
disposedWarning(this.id)
return
}
if (!isObject$2(opt)) {
opt = { silent: !!opt }
}
if (!actions[payload.type]) {
return
} // Avoid dispatch action before setOption. Especially in `connect`.
if (!this._model) {
return
} // May dispatchAction in rendering procedure
if (this[IN_MAIN_PROCESS_KEY]) {
this._pendingActions.push(payload)
return
}
var silent = opt.silent
doDispatchAction.call(this, payload, silent)
var flush = opt.flush
if (flush) {
this._zr.flush()
} else if (flush !== false && env$1.browser.weChat) {
// In WeChat embeded browser, `requestAnimationFrame` and `setInterval`
// hang when sliding page (on touch event), which cause that zr does not
// refresh util user interaction finished, which is not expected.
// But `dispatchAction` may be called too frequently when pan on touch
// screen, which impacts performance if do not throttle them.
this._throttledZrFlush()
}
flushPendingActions.call(this, silent)
triggerUpdatedEvent.call(this, silent)
}
ECharts.prototype.updateLabelLayout = function () {
lifecycle$1.trigger(
'series:layoutlabels',
this._model,
this._api,
{
// Not adding series labels.
// TODO
updatedSeries: []
}
)
}
ECharts.prototype.appendData = function (params) {
if (this._disposed) {
disposedWarning(this.id)
return
}
var seriesIndex = params.seriesIndex
var ecModel = this.getModel()
var seriesModel = ecModel.getSeriesByIndex(seriesIndex)
seriesModel.appendData(params) // Note: `appendData` does not support that update extent of coordinate
// system, util some scenario require that. In the expected usage of
// `appendData`, the initial extent of coordinate system should better
// be fixed by axis `min`/`max` setting or initial data, otherwise if
// the extent changed while `appendData`, the location of the painted
// graphic elements have to be changed, which make the usage of
// `appendData` meaningless.
this._scheduler.unfinished = true
this.getZr().wakeUp()
} // A work around for no `internal` modifier in ts yet but
// need to strictly hide private methods to JS users.
ECharts.internalField = (function () {
prepare = function prepare(ecIns) {
var scheduler = ecIns._scheduler
scheduler.restorePipelines(ecIns._model)
scheduler.prepareStageTasks()
prepareView(ecIns, true)
prepareView(ecIns, false)
scheduler.plan()
}
/**
* Prepare view instances of charts and components
*/ prepareView = function prepareView(ecIns, isComponent) {
var ecModel = ecIns._model
var scheduler = ecIns._scheduler
var viewList = isComponent
? ecIns._componentsViews
: ecIns._chartsViews
var viewMap = isComponent
? ecIns._componentsMap
: ecIns._chartsMap
var zr = ecIns._zr
var api = ecIns._api
for (var i = 0; i < viewList.length; i++) {
viewList[i].__alive = false
}
isComponent
? ecModel.eachComponent(function (componentType, model) {
componentType !== 'series' && doPrepare(model)
})
: ecModel.eachSeries(doPrepare)
function doPrepare(model) {
// By defaut view will be reused if possible for the case that `setOption` with "notMerge"
// mode and need to enable transition animation. (Usually, when they have the same id, or
// especially no id but have the same type & name & index. See the `model.id` generation
// rule in `makeIdAndName` and `viewId` generation rule here).
// But in `replaceMerge` mode, this feature should be able to disabled when it is clear that
// the new model has nothing to do with the old model.
var requireNewView = model.__requireNewView // This command should not work twice.
model.__requireNewView = false // Consider: id same and type changed.
var viewId = '_ec_' + model.id + '_' + model.type
var view = !requireNewView && viewMap[viewId]
if (!view) {
var classType = parseClassType(model.type)
var Clazz = isComponent
? ComponentView$1.getClass(classType.main, classType.sub) // FIXME:TS
: // (ChartView as ChartViewConstructor).getClass('series', classType.sub)
// For backward compat, still support a chart type declared as only subType
// like "liquidfill", but recommend "series.liquidfill"
// But need a base class to make a type series.
ChartView$1.getClass(classType.sub)
view = new Clazz()
view.init(ecModel, api)
viewMap[viewId] = view
viewList.push(view)
zr.add(view.group)
}
model.__viewId = view.__id = viewId
view.__alive = true
view.__model = model
view.group.__ecComponentInfo = {
mainType: model.mainType,
index: model.componentIndex
}
!isComponent &&
scheduler.prepareView(view, model, ecModel, api)
}
for (var i = 0; i < viewList.length; ) {
var view = viewList[i]
if (!view.__alive) {
!isComponent && view.renderTask.dispose()
zr.remove(view.group)
view.dispose(ecModel, api)
viewList.splice(i, 1)
if (viewMap[view.__id] === view) {
delete viewMap[view.__id]
}
view.__id = view.group.__ecComponentInfo = null
} else {
i++
}
}
}
updateDirectly = function updateDirectly(
ecIns,
method,
payload,
mainType,
subType
) {
var ecModel = ecIns._model
ecModel.setUpdatePayload(payload) // broadcast
if (!mainType) {
// FIXME
// Chart will not be update directly here, except set dirty.
// But there is no such scenario now.
each$7(
[]
.concat(ecIns._componentsViews)
.concat(ecIns._chartsViews),
callView
)
return
}
var query = {}
query[mainType + 'Id'] = payload[mainType + 'Id']
query[mainType + 'Index'] = payload[mainType + 'Index']
query[mainType + 'Name'] = payload[mainType + 'Name']
var condition = { mainType: mainType, query: query }
subType && (condition.subType = subType) // subType may be '' by parseClassType;
var excludeSeriesId = payload.excludeSeriesId
var excludeSeriesIdMap
if (excludeSeriesId != null) {
excludeSeriesIdMap = createHashMap()
each$7(normalizeToArray(excludeSeriesId), function (id) {
var modelId = convertOptionIdName(id, null)
if (modelId != null) {
excludeSeriesIdMap.set(modelId, true)
}
})
} // If dispatchAction before setOption, do nothing.
ecModel &&
ecModel.eachComponent(
condition,
function (model) {
var isExcluded =
excludeSeriesIdMap &&
excludeSeriesIdMap.get(model.id) !== null
if (isExcluded) {
return
}
if (isHighDownPayload(payload)) {
if (model instanceof SeriesModel$1) {
if (
payload.type === HIGHLIGHT_ACTION_TYPE &&
!payload.notBlur &&
!model.get(['emphasis', 'disabled'])
) {
blurSeriesFromHighlightPayload(
model,
payload,
ecIns._api
)
}
} else {
var _a = findComponentHighDownDispatchers(
model.mainType,
model.componentIndex,
payload.name,
ecIns._api
),
focusSelf = _a.focusSelf,
dispatchers = _a.dispatchers
if (
payload.type === HIGHLIGHT_ACTION_TYPE &&
focusSelf &&
!payload.notBlur
) {
blurComponent(
model.mainType,
model.componentIndex,
ecIns._api
)
} // PENDING:
// Whether to put this "enter emphasis" code in `ComponentView`,
// which will be the same as `ChartView` but might be not necessary
// and will be far from this logic.
if (dispatchers) {
each$7(dispatchers, function (dispatcher) {
payload.type === HIGHLIGHT_ACTION_TYPE
? enterEmphasis(dispatcher)
: leaveEmphasis(dispatcher)
})
}
}
} else if (isSelectChangePayload(payload)) {
// TODO geo
if (model instanceof SeriesModel$1) {
toggleSelectionFromPayload(model, payload, ecIns._api)
updateSeriesElementSelection(model)
markStatusToUpdate(ecIns)
}
}
},
ecIns
)
ecModel &&
ecModel.eachComponent(
condition,
function (model) {
var isExcluded =
excludeSeriesIdMap &&
excludeSeriesIdMap.get(model.id) !== null
if (isExcluded) {
return
}
callView(
ecIns[
mainType === 'series'
? '_chartsMap'
: '_componentsMap'
][model.__viewId]
)
},
ecIns
)
function callView(view) {
view &&
view.__alive &&
view[method] &&
view[method](view.__model, ecModel, ecIns._api, payload)
}
}
updateMethods = {
prepareAndUpdate: function prepareAndUpdate(payload) {
prepare(this)
updateMethods.update.call(this, payload, {
// Needs to mark option changed if newOption is given.
// It's from MagicType.
// TODO If use a separate flag optionChanged in payload?
optionChanged: payload.newOption != null
})
},
update: function update(payload, updateParams) {
var ecModel = this._model
var api = this._api
var zr = this._zr
var coordSysMgr = this._coordSysMgr
var scheduler = this._scheduler // update before setOption
if (!ecModel) {
return
}
ecModel.setUpdatePayload(payload)
scheduler.restoreData(ecModel, payload)
scheduler.performSeriesTasks(ecModel) // TODO
// Save total ecModel here for undo/redo (after restoring data and before processing data).
// Undo (restoration of total ecModel) can be carried out in 'action' or outside API call.
// Create new coordinate system each update
// In LineView may save the old coordinate system and use it to get the orignal point
coordSysMgr.create(ecModel, api)
scheduler.performDataProcessorTasks(ecModel, payload) // Current stream render is not supported in data process. So we can update
// stream modes after data processing, where the filtered data is used to
// deteming whether use progressive rendering.
updateStreamModes(this, ecModel) // We update stream modes before coordinate system updated, then the modes info
// can be fetched when coord sys updating (consider the barGrid extent fix). But
// the drawback is the full coord info can not be fetched. Fortunately this full
// coord is not requied in stream mode updater currently.
coordSysMgr.update(ecModel, api)
clearColorPalette(ecModel)
scheduler.performVisualTasks(ecModel, payload)
render(this, ecModel, api, payload, updateParams) // Set background
var backgroundColor =
ecModel.get('backgroundColor') || 'transparent'
var darkMode = ecModel.get('darkMode')
zr.setBackgroundColor(backgroundColor) // Force set dark mode.
if (darkMode != null && darkMode !== 'auto') {
zr.setDarkMode(darkMode)
}
lifecycle$1.trigger('afterupdate', ecModel, api)
},
updateTransform: function updateTransform(payload) {
var _this = this
var ecModel = this._model
var api = this._api // update before setOption
if (!ecModel) {
return
}
ecModel.setUpdatePayload(payload) // ChartView.markUpdateMethod(payload, 'updateTransform');
var componentDirtyList = []
ecModel.eachComponent(function (
componentType,
componentModel
) {
if (componentType === 'series') {
return
}
var componentView =
_this.getViewOfComponentModel(componentModel)
if (componentView && componentView.__alive) {
if (componentView.updateTransform) {
var result = componentView.updateTransform(
componentModel,
ecModel,
api,
payload
)
result &&
result.update &&
componentDirtyList.push(componentView)
} else {
componentDirtyList.push(componentView)
}
}
})
var seriesDirtyMap = createHashMap()
ecModel.eachSeries(function (seriesModel) {
var chartView = _this._chartsMap[seriesModel.__viewId]
if (chartView.updateTransform) {
var result = chartView.updateTransform(
seriesModel,
ecModel,
api,
payload
)
result &&
result.update &&
seriesDirtyMap.set(seriesModel.uid, 1)
} else {
seriesDirtyMap.set(seriesModel.uid, 1)
}
})
clearColorPalette(ecModel) // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline.
// this._scheduler.performVisualTasks(ecModel, payload, 'layout', true);
this._scheduler.performVisualTasks(ecModel, payload, {
setDirty: true,
dirtyMap: seriesDirtyMap
}) // Currently, not call render of components. Geo render cost a lot.
// renderComponents(ecIns, ecModel, api, payload, componentDirtyList);
renderSeries(this, ecModel, api, payload, {}, seriesDirtyMap)
lifecycle$1.trigger('afterupdate', ecModel, api)
},
updateView: function updateView(payload) {
var ecModel = this._model // update before setOption
if (!ecModel) {
return
}
ecModel.setUpdatePayload(payload)
ChartView$1.markUpdateMethod(payload, 'updateView')
clearColorPalette(ecModel) // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline.
this._scheduler.performVisualTasks(ecModel, payload, {
setDirty: true
})
render(this, ecModel, this._api, payload, {})
lifecycle$1.trigger('afterupdate', ecModel, this._api)
},
updateVisual: function updateVisual(payload) {
// updateMethods.update.call(this, payload);
var _this = this
var ecModel = this._model // update before setOption
if (!ecModel) {
return
}
ecModel.setUpdatePayload(payload) // clear all visual
ecModel.eachSeries(function (seriesModel) {
seriesModel.getData().clearAllVisual()
}) // Perform visual
ChartView$1.markUpdateMethod(payload, 'updateVisual')
clearColorPalette(ecModel) // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline.
this._scheduler.performVisualTasks(ecModel, payload, {
visualType: 'visual',
setDirty: true
})
ecModel.eachComponent(function (
componentType,
componentModel
) {
if (componentType !== 'series') {
var componentView =
_this.getViewOfComponentModel(componentModel)
componentView &&
componentView.__alive &&
componentView.updateVisual(
componentModel,
ecModel,
_this._api,
payload
)
}
})
ecModel.eachSeries(function (seriesModel) {
var chartView = _this._chartsMap[seriesModel.__viewId]
chartView.updateVisual(
seriesModel,
ecModel,
_this._api,
payload
)
})
lifecycle$1.trigger('afterupdate', ecModel, this._api)
},
updateLayout: function updateLayout(payload) {
updateMethods.update.call(this, payload)
}
}
doConvertPixel = function doConvertPixel(
ecIns,
methodName,
finder,
value
) {
if (ecIns._disposed) {
disposedWarning(ecIns.id)
return
}
var ecModel = ecIns._model
var coordSysList = ecIns._coordSysMgr.getCoordinateSystems()
var result
var parsedFinder = parseFinder$1(ecModel, finder)
for (var i = 0; i < coordSysList.length; i++) {
var coordSys = coordSysList[i]
if (
coordSys[methodName] &&
(result = coordSys[methodName](
ecModel,
parsedFinder,
value
)) != null
) {
return result
}
}
}
updateStreamModes = function updateStreamModes(ecIns, ecModel) {
var chartsMap = ecIns._chartsMap
var scheduler = ecIns._scheduler
ecModel.eachSeries(function (seriesModel) {
scheduler.updateStreamModes(
seriesModel,
chartsMap[seriesModel.__viewId]
)
})
}
doDispatchAction = function doDispatchAction(payload, silent) {
var _this = this
var ecModel = this.getModel()
var payloadType = payload.type
var escapeConnect = payload.escapeConnect
var actionWrap = actions[payloadType]
var actionInfo = actionWrap.actionInfo
var cptTypeTmp = (actionInfo.update || 'update').split(':')
var updateMethod = cptTypeTmp.pop()
var cptType =
cptTypeTmp[0] != null && parseClassType(cptTypeTmp[0])
this[IN_MAIN_PROCESS_KEY] = true
var payloads = [payload]
var batched = false // Batch action
if (payload.batch) {
batched = true
payloads = map$1(payload.batch, function (item) {
item = defaults(extend({}, item), payload)
item.batch = null
return item
})
}
var eventObjBatch = []
var eventObj
var isSelectChange = isSelectChangePayload(payload)
var isHighDown = isHighDownPayload(payload) // Only leave blur once if there are multiple batches.
if (isHighDown) {
allLeaveBlur(this._api)
}
each$7(payloads, function (batchItem) {
// Action can specify the event by return it.
eventObj = actionWrap.action(
batchItem,
_this._model,
_this._api
) // Emit event outside
eventObj = eventObj || extend({}, batchItem) // Convert type to eventType
eventObj.type = actionInfo.event || eventObj.type
eventObjBatch.push(eventObj) // light update does not perform data process, layout and visual.
if (isHighDown) {
var _a = preParseFinder(payload),
queryOptionMap = _a.queryOptionMap,
mainTypeSpecified = _a.mainTypeSpecified
var componentMainType = mainTypeSpecified
? queryOptionMap.keys()[0]
: 'series'
updateDirectly(
_this,
updateMethod,
batchItem,
componentMainType
)
markStatusToUpdate(_this)
} else if (isSelectChange) {
// At present `dispatchAction({ type: 'select', ... })` is not supported on components.
// geo still use 'geoselect'.
updateDirectly(_this, updateMethod, batchItem, 'series')
markStatusToUpdate(_this)
} else if (cptType) {
updateDirectly(
_this,
updateMethod,
batchItem,
cptType.main,
cptType.sub
)
}
})
if (
updateMethod !== 'none' &&
!isHighDown &&
!isSelectChange &&
!cptType
) {
try {
// Still dirty
if (this[PENDING_UPDATE]) {
prepare(this)
updateMethods.update.call(this, payload)
this[PENDING_UPDATE] = null
} else {
updateMethods[updateMethod].call(this, payload)
}
} catch (e) {
this[IN_MAIN_PROCESS_KEY] = false
throw e
}
} // Follow the rule of action batch
if (batched) {
eventObj = {
type: actionInfo.event || payloadType,
escapeConnect: escapeConnect,
batch: eventObjBatch
}
} else {
eventObj = eventObjBatch[0]
}
this[IN_MAIN_PROCESS_KEY] = false
if (!silent) {
var messageCenter = this._messageCenter
messageCenter.trigger(eventObj.type, eventObj) // Extra triggered 'selectchanged' event
if (isSelectChange) {
var newObj = {
type: 'selectchanged',
escapeConnect: escapeConnect,
selected: getAllSelectedIndices(ecModel),
isFromClick: payload.isFromClick || false,
fromAction: payload.type,
fromActionPayload: payload
}
messageCenter.trigger(newObj.type, newObj)
}
}
}
flushPendingActions = function flushPendingActions(silent) {
var pendingActions = this._pendingActions
while (pendingActions.length) {
var payload = pendingActions.shift()
doDispatchAction.call(this, payload, silent)
}
}
triggerUpdatedEvent = function triggerUpdatedEvent(silent) {
!silent && this.trigger('updated')
}
/**
* Event `rendered` is triggered when zr
* rendered. It is useful for realtime
* snapshot (reflect animation).
*
* Event `finished` is triggered when:
* (1) zrender rendering finished.
* (2) initial animation finished.
* (3) progressive rendering finished.
* (4) no pending action.
* (5) no delayed setOption needs to be processed.
*/ bindRenderedEvent = function bindRenderedEvent(zr, ecIns) {
zr.on('rendered', function (params) {
ecIns.trigger('rendered', params) // The `finished` event should not be triggered repeatly,
// so it should only be triggered when rendering indeed happend
// in zrender. (Consider the case that dipatchAction is keep
// triggering when mouse move).
if (
// Although zr is dirty if initial animation is not finished
// and this checking is called on frame, we also check
// animation finished for robustness.
zr.animation.isFinished() &&
!ecIns[PENDING_UPDATE] &&
!ecIns._scheduler.unfinished &&
!ecIns._pendingActions.length
) {
ecIns.trigger('finished')
}
})
}
bindMouseEvent = function bindMouseEvent(zr, ecIns) {
zr.on('mouseover', function (e) {
var el = e.target
var dispatcher = findEventDispatcher(el, isHighDownDispatcher)
if (dispatcher) {
handleGlobalMouseOverForHighDown(dispatcher, e, ecIns._api)
markStatusToUpdate(ecIns)
}
})
.on('mouseout', function (e) {
var el = e.target
var dispatcher = findEventDispatcher(
el,
isHighDownDispatcher
)
if (dispatcher) {
handleGlobalMouseOutForHighDown(dispatcher, e, ecIns._api)
markStatusToUpdate(ecIns)
}
})
.on('click', function (e) {
var el = e.target
var dispatcher = findEventDispatcher(
el,
function (target) {
return getECData(target).dataIndex != null
},
true
)
if (dispatcher) {
var actionType = dispatcher.selected
? 'unselect'
: 'select'
var ecData = getECData(dispatcher)
ecIns._api.dispatchAction({
type: actionType,
dataType: ecData.dataType,
dataIndexInside: ecData.dataIndex,
seriesIndex: ecData.seriesIndex,
isFromClick: true
})
}
})
}
function clearColorPalette(ecModel) {
ecModel.clearColorPalette()
ecModel.eachSeries(function (seriesModel) {
seriesModel.clearColorPalette()
})
}
function allocateZlevels(ecModel) {
var componentZLevels = []
var seriesZLevels = []
var hasSeperateZLevel = false
ecModel.eachComponent(function (componentType, componentModel) {
var zlevel = componentModel.get('zlevel') || 0
var z = componentModel.get('z') || 0
var zlevelKey = componentModel.getZLevelKey()
hasSeperateZLevel = hasSeperateZLevel || !!zlevelKey
;(componentType === 'series'
? seriesZLevels
: componentZLevels
).push({
zlevel: zlevel,
z: z,
idx: componentModel.componentIndex,
type: componentType,
key: zlevelKey
})
})
if (hasSeperateZLevel) {
// Series after component
var zLevels = componentZLevels.concat(seriesZLevels)
var lastSeriesZLevel_1
var lastSeriesKey_1
sort(zLevels, function (a, b) {
if (a.zlevel === b.zlevel) {
return a.z - b.z
}
return a.zlevel - b.zlevel
})
each$7(zLevels, function (item) {
var componentModel = ecModel.getComponent(
item.type,
item.idx
)
var zlevel = item.zlevel
var key = item.key
if (lastSeriesZLevel_1 != null) {
zlevel = Math.max(lastSeriesZLevel_1, zlevel)
}
if (key) {
if (
zlevel === lastSeriesZLevel_1 &&
key !== lastSeriesKey_1
) {
zlevel++
}
lastSeriesKey_1 = key
} else if (lastSeriesKey_1) {
if (zlevel === lastSeriesZLevel_1) {
zlevel++
}
lastSeriesKey_1 = ''
}
lastSeriesZLevel_1 = zlevel
componentModel.setZLevel(zlevel)
})
}
}
render = function render(
ecIns,
ecModel,
api,
payload,
updateParams
) {
allocateZlevels(ecModel)
renderComponents(ecIns, ecModel, api, payload, updateParams)
each$7(ecIns._chartsViews, function (chart) {
chart.__alive = false
})
renderSeries(ecIns, ecModel, api, payload, updateParams) // Remove groups of unrendered charts
each$7(ecIns._chartsViews, function (chart) {
if (!chart.__alive) {
chart.remove(ecModel, api)
}
})
}
renderComponents = function renderComponents(
ecIns,
ecModel,
api,
payload,
updateParams,
dirtyList
) {
each$7(
dirtyList || ecIns._componentsViews,
function (componentView) {
var componentModel = componentView.__model
clearStates(componentModel, componentView)
componentView.render(componentModel, ecModel, api, payload)
updateZ(componentModel, componentView)
updateStates(componentModel, componentView)
}
)
}
/**
* Render each chart and component
*/ renderSeries = function renderSeries(
ecIns,
ecModel,
api,
payload,
updateParams,
dirtyMap
) {
// Render all charts
var scheduler = ecIns._scheduler
updateParams = extend(updateParams || {}, {
updatedSeries: ecModel.getSeries()
}) // TODO progressive?
lifecycle$1.trigger(
'series:beforeupdate',
ecModel,
api,
updateParams
)
var unfinished = false
ecModel.eachSeries(function (seriesModel) {
var chartView = ecIns._chartsMap[seriesModel.__viewId]
chartView.__alive = true
var renderTask = chartView.renderTask
scheduler.updatePayload(renderTask, payload) // TODO states on marker.
clearStates(seriesModel, chartView)
if (dirtyMap && dirtyMap.get(seriesModel.uid)) {
renderTask.dirty()
}
if (
renderTask.perform(scheduler.getPerformArgs(renderTask))
) {
unfinished = true
}
chartView.group.silent = !!seriesModel.get('silent') // Should not call markRedraw on group, because it will disable zrender
// increamental render (alway render from the __startIndex each frame)
// chartView.group.markRedraw();
updateBlend(seriesModel, chartView)
updateSeriesElementSelection(seriesModel)
})
scheduler.unfinished = unfinished || scheduler.unfinished
lifecycle$1.trigger(
'series:layoutlabels',
ecModel,
api,
updateParams
) // transition after label is layouted.
lifecycle$1.trigger(
'series:transition',
ecModel,
api,
updateParams
)
ecModel.eachSeries(function (seriesModel) {
var chartView = ecIns._chartsMap[seriesModel.__viewId] // Update Z after labels updated. Before applying states.
updateZ(seriesModel, chartView) // NOTE: Update states after label is updated.
// label should be in normal status when layouting.
updateStates(seriesModel, chartView)
}) // If use hover layer
updateHoverLayerStatus(ecIns, ecModel)
lifecycle$1.trigger(
'series:afterupdate',
ecModel,
api,
updateParams
)
}
markStatusToUpdate = function markStatusToUpdate(ecIns) {
ecIns[STATUS_NEEDS_UPDATE_KEY] = true // Wake up zrender if it's sleep. Let it update states in the next frame.
ecIns.getZr().wakeUp()
}
applyChangedStates = function applyChangedStates(ecIns) {
if (!ecIns[STATUS_NEEDS_UPDATE_KEY]) {
return
}
ecIns.getZr().storage.traverse(function (el) {
// Not applied on removed elements, it may still in fading.
if (isElementRemoved(el)) {
return
}
applyElementStates(el)
})
ecIns[STATUS_NEEDS_UPDATE_KEY] = false
}
function applyElementStates(el) {
var newStates = []
var oldStates = el.currentStates // Keep other states.
for (var i = 0; i < oldStates.length; i++) {
var stateName = oldStates[i]
if (
!(
stateName === 'emphasis' ||
stateName === 'blur' ||
stateName === 'select'
)
) {
newStates.push(stateName)
}
} // Only use states when it's exists.
if (el.selected && el.states.select) {
newStates.push('select')
}
if (
el.hoverState === HOVER_STATE_EMPHASIS &&
el.states.emphasis
) {
newStates.push('emphasis')
} else if (
el.hoverState === HOVER_STATE_BLUR &&
el.states.blur
) {
newStates.push('blur')
}
el.useStates(newStates)
}
function updateHoverLayerStatus(ecIns, ecModel) {
var zr = ecIns._zr
var storage = zr.storage
var elCount = 0
storage.traverse(function (el) {
if (!el.isGroup) {
elCount++
}
})
if (
elCount > ecModel.get('hoverLayerThreshold') &&
!env$1.node &&
!env$1.worker
) {
ecModel.eachSeries(function (seriesModel) {
if (seriesModel.preventUsingHoverLayer) {
return
}
var chartView = ecIns._chartsMap[seriesModel.__viewId]
if (chartView.__alive) {
chartView.eachRendered(function (el) {
if (el.states.emphasis) {
el.states.emphasis.hoverLayer = true
}
})
}
})
}
}
/**
* Update chart and blend.
*/ function updateBlend(seriesModel, chartView) {
var blendMode = seriesModel.get('blendMode') || null
chartView.eachRendered(function (el) {
// FIXME marker and other components
if (!el.isGroup) {
// DONT mark the element dirty. In case element is incremental and don't wan't to rerender.
el.style.blend = blendMode
}
})
}
function updateZ(model, view) {
if (model.preventAutoZ) {
return
}
var z = model.get('z') || 0
var zlevel = model.get('zlevel') || 0 // Set z and zlevel
view.eachRendered(function (el) {
doUpdateZ(el, z, zlevel, -Infinity) // Don't traverse the children because it has been traversed in _updateZ.
return true
})
}
function doUpdateZ(el, z, zlevel, maxZ2) {
// Group may also have textContent
var label = el.getTextContent()
var labelLine = el.getTextGuideLine()
var isGroup = el.isGroup
if (isGroup) {
// set z & zlevel of children elements of Group
var children = el.childrenRef()
for (var i = 0; i < children.length; i++) {
maxZ2 = Math.max(
doUpdateZ(children[i], z, zlevel, maxZ2),
maxZ2
)
}
} else {
// not Group
el.z = z
el.zlevel = zlevel
maxZ2 = Math.max(el.z2, maxZ2)
} // always set z and zlevel if label/labelLine exists
if (label) {
label.z = z
label.zlevel = zlevel // lift z2 of text content
// TODO if el.emphasis.z2 is spcefied, what about textContent.
isFinite(maxZ2) && (label.z2 = maxZ2 + 2)
}
if (labelLine) {
var textGuideLineConfig = el.textGuideLineConfig
labelLine.z = z
labelLine.zlevel = zlevel
isFinite(maxZ2) &&
(labelLine.z2 =
maxZ2 +
(textGuideLineConfig && textGuideLineConfig.showAbove
? 1
: -1))
}
return maxZ2
} // Clear states without animation.
// TODO States on component.
function clearStates(model, view) {
view.eachRendered(function (el) {
// Not applied on removed elements, it may still in fading.
if (isElementRemoved(el)) {
return
}
var textContent = el.getTextContent()
var textGuide = el.getTextGuideLine()
if (el.stateTransition) {
el.stateTransition = null
}
if (textContent && textContent.stateTransition) {
textContent.stateTransition = null
}
if (textGuide && textGuide.stateTransition) {
textGuide.stateTransition = null
} // TODO If el is incremental.
if (el.hasState()) {
el.prevStates = el.currentStates
el.clearStates()
} else if (el.prevStates) {
el.prevStates = null
}
})
}
function updateStates(model, view) {
var stateAnimationModel = model.getModel('stateAnimation')
var enableAnimation = model.isAnimationEnabled()
var duration = stateAnimationModel.get('duration')
var stateTransition =
duration > 0
? {
duration: duration,
delay: stateAnimationModel.get('delay'),
easing: stateAnimationModel.get('easing') // additive: stateAnimationModel.get('additive')
}
: null
view.eachRendered(function (el) {
if (el.states && el.states.emphasis) {
// Not applied on removed elements, it may still in fading.
if (isElementRemoved(el)) {
return
}
if (el instanceof Path$1) {
savePathStates(el)
} // Only updated on changed element. In case element is incremental and don't wan't to rerender.
// TODO, a more proper way?
if (el.__dirty) {
var prevStates = el.prevStates // Restore states without animation
if (prevStates) {
el.useStates(prevStates)
}
} // Update state transition and enable animation again.
if (enableAnimation) {
el.stateTransition = stateTransition
var textContent = el.getTextContent()
var textGuide = el.getTextGuideLine() // TODO Is it necessary to animate label?
if (textContent) {
textContent.stateTransition = stateTransition
}
if (textGuide) {
textGuide.stateTransition = stateTransition
}
} // The use higlighted and selected flag to toggle states.
if (el.__dirty) {
applyElementStates(el)
}
}
})
}
createExtensionAPI = function createExtensionAPI(ecIns) {
return new /** @class */ ((function (_super) {
__extends(class_1, _super)
function class_1() {
return (
(_super !== null && _super.apply(this, arguments)) || this
)
}
class_1.prototype.getCoordinateSystems = function () {
return ecIns._coordSysMgr.getCoordinateSystems()
}
class_1.prototype.getComponentByElement = function (el) {
while (el) {
var modelInfo = el.__ecComponentInfo
if (modelInfo != null) {
return ecIns._model.getComponent(
modelInfo.mainType,
modelInfo.index
)
}
el = el.parent
}
}
class_1.prototype.enterEmphasis = function (
el,
highlightDigit
) {
enterEmphasis(el, highlightDigit)
markStatusToUpdate(ecIns)
}
class_1.prototype.leaveEmphasis = function (
el,
highlightDigit
) {
leaveEmphasis(el, highlightDigit)
markStatusToUpdate(ecIns)
}
class_1.prototype.enterBlur = function (el) {
enterBlur(el)
markStatusToUpdate(ecIns)
}
class_1.prototype.leaveBlur = function (el) {
leaveBlur(el)
markStatusToUpdate(ecIns)
}
class_1.prototype.enterSelect = function (el) {
enterSelect(el)
markStatusToUpdate(ecIns)
}
class_1.prototype.leaveSelect = function (el) {
leaveSelect(el)
markStatusToUpdate(ecIns)
}
class_1.prototype.getModel = function () {
return ecIns.getModel()
}
class_1.prototype.getViewOfComponentModel = function (
componentModel
) {
return ecIns.getViewOfComponentModel(componentModel)
}
class_1.prototype.getViewOfSeriesModel = function (
seriesModel
) {
return ecIns.getViewOfSeriesModel(seriesModel)
}
return class_1
})(ExtensionAPI$1))(ecIns)
}
enableConnect = function enableConnect(chart) {
function updateConnectedChartsStatus(charts, status) {
for (var i = 0; i < charts.length; i++) {
var otherChart = charts[i]
otherChart[CONNECT_STATUS_KEY] = status
}
}
each$7(eventActionMap, function (actionType, eventType) {
chart._messageCenter.on(eventType, function (event) {
if (
connectedGroups[chart.group] &&
chart[CONNECT_STATUS_KEY] !== CONNECT_STATUS_PENDING
) {
if (event && event.escapeConnect) {
return
}
var action_1 = chart.makeActionFromEvent(event)
var otherCharts_1 = []
each$7(instances, function (otherChart) {
if (
otherChart !== chart &&
otherChart.group === chart.group
) {
otherCharts_1.push(otherChart)
}
})
updateConnectedChartsStatus(
otherCharts_1,
CONNECT_STATUS_PENDING
)
each$7(otherCharts_1, function (otherChart) {
if (
otherChart[CONNECT_STATUS_KEY] !==
CONNECT_STATUS_UPDATING
) {
otherChart.dispatchAction(action_1)
}
})
updateConnectedChartsStatus(
otherCharts_1,
CONNECT_STATUS_UPDATED
)
}
})
})
}
})()
return ECharts
})(Eventful$1)
var echartsProto = ECharts.prototype
echartsProto.on = createRegisterEventWithLowercaseECharts('on')
echartsProto.off = createRegisterEventWithLowercaseECharts('off')
/**
* @deprecated
*/ // @ts-ignore
echartsProto.one = function (eventName, cb, ctx) {
var self = this
function wrapped() {
var args2 = []
for (var _i = 0; _i < arguments.length; _i++) {
args2[_i] = arguments[_i]
}
cb && cb.apply && cb.apply(this, args2) // @ts-ignore
self.off(eventName, wrapped)
}
this.on.call(this, eventName, wrapped, ctx)
}
var MOUSE_EVENT_NAMES = [
'click',
'dblclick',
'mouseover',
'mouseout',
'mousemove',
'mousedown',
'mouseup',
'globalout',
'contextmenu'
]
function disposedWarning(id) {}
var actions = {}
/**
* Map eventType to actionType
*/ var eventActionMap = {}
var dataProcessorFuncs = []
var optionPreprocessorFuncs = []
var visualFuncs = []
var themeStorage = {}
var loadingEffects = {}
var instances = {}
var connectedGroups = {}
var idBase = +new Date() - 0
var DOM_ATTRIBUTE_KEY = '_echarts_instance_'
/**
* @param opts.devicePixelRatio Use window.devicePixelRatio by default
* @param opts.renderer Can choose 'canvas' or 'svg' to render the chart.
* @param opts.width Use clientWidth of the input `dom` by default.
* Can be 'auto' (the same as null/undefined)
* @param opts.height Use clientHeight of the input `dom` by default.
* Can be 'auto' (the same as null/undefined)
* @param opts.locale Specify the locale.
* @param opts.useDirtyRect Enable dirty rectangle rendering or not.
*/ function init(dom, theme, opts) {
var isClient = !(opts && opts.ssr)
if (isClient) {
var existInstance = getInstanceByDom(dom)
if (existInstance) {
return existInstance
}
}
var chart = new ECharts(dom, theme, opts)
chart.id = 'ec_' + idBase++
instances[chart.id] = chart
isClient && setAttribute(dom, DOM_ATTRIBUTE_KEY, chart.id)
enableConnect(chart)
lifecycle$1.trigger('afterinit', chart)
return chart
}
function getInstanceByDom(dom) {
return instances[getAttribute(dom, DOM_ATTRIBUTE_KEY)]
}
/**
* Register theme
*/ function registerTheme(name, theme) {
themeStorage[name] = theme
}
/**
* Register option preprocessor
*/ function registerPreprocessor(preprocessorFunc) {
if (indexOf(optionPreprocessorFuncs, preprocessorFunc) < 0) {
optionPreprocessorFuncs.push(preprocessorFunc)
}
}
function registerProcessor(priority, processor) {
normalizeRegister(
dataProcessorFuncs,
priority,
processor,
PRIORITY_PROCESSOR_DEFAULT
)
}
/**
* Register postIniter
* @param {Function} postInitFunc
*/ function registerPostInit(postInitFunc) {
registerUpdateLifecycle('afterinit', postInitFunc)
}
/**
* Register postUpdater
* @param {Function} postUpdateFunc
*/ function registerPostUpdate(postUpdateFunc) {
registerUpdateLifecycle('afterupdate', postUpdateFunc)
}
function registerUpdateLifecycle(name, cb) {
lifecycle$1.on(name, cb)
}
function registerAction(actionInfo, eventName, action) {
if (isFunction(eventName)) {
action = eventName
eventName = ''
}
var actionType = isObject$2(actionInfo)
? actionInfo.type
: [actionInfo, (actionInfo = { event: eventName })][0] // Event name is all lowercase
actionInfo.event = (actionInfo.event || actionType).toLowerCase()
eventName = actionInfo.event
if (eventActionMap[eventName]) {
// Already registered.
return
} // Validate action type and event name.
assert(ACTION_REG.test(actionType) && ACTION_REG.test(eventName))
if (!actions[actionType]) {
actions[actionType] = { action: action, actionInfo: actionInfo }
}
eventActionMap[eventName] = actionType
}
function registerCoordinateSystem(type, coordSysCreator) {
CoordinateSystem.register(type, coordSysCreator)
}
function registerLayout(priority, layoutTask) {
normalizeRegister(
visualFuncs,
priority,
layoutTask,
PRIORITY_VISUAL_LAYOUT,
'layout'
)
}
function registerVisual(priority, visualTask) {
normalizeRegister(
visualFuncs,
priority,
visualTask,
PRIORITY_VISUAL_CHART,
'visual'
)
}
var registeredTasks = []
function normalizeRegister(
targetList,
priority,
fn,
defaultPriority,
visualType
) {
if (isFunction(priority) || isObject$2(priority)) {
fn = priority
priority = defaultPriority
}
if (indexOf(registeredTasks, fn) >= 0) {
return
}
registeredTasks.push(fn)
var stageHandler = Scheduler$1.wrapStageHandler(fn, visualType)
stageHandler.__prio = priority
stageHandler.__raw = fn
targetList.push(stageHandler)
}
function registerLoading(name, loadingFx) {
loadingEffects[name] = loadingFx
}
/**
* The parameters and usage: see `geoSourceManager.registerMap`.
* Compatible with previous `echarts.registerMap`.
*/ function registerMap(mapName, geoJson, specialAreas) {
var registerMap = getImpl('registerMap')
registerMap && registerMap(mapName, geoJson, specialAreas)
}
var registerTransform = registerExternalTransform
/**
* Globa dispatchAction to a specified chart instance.
*/ // export function dispatchAction(payload: { chartId: string } & Payload, opt?: Parameters[1]) {
// if (!payload || !payload.chartId) {
// // Must have chartId to find chart
// return;
// }
// const chart = instances[payload.chartId];
// if (chart) {
// chart.dispatchAction(payload, opt);
// }
// }
// Buitlin global visual
registerVisual(PRIORITY_VISUAL_GLOBAL, seriesStyleTask)
registerVisual(PRIORITY_VISUAL_CHART_DATA_CUSTOM, dataStyleTask)
registerVisual(
PRIORITY_VISUAL_CHART_DATA_CUSTOM,
dataColorPaletteTask
)
registerVisual(PRIORITY_VISUAL_GLOBAL, seriesSymbolTask)
registerVisual(PRIORITY_VISUAL_CHART_DATA_CUSTOM, dataSymbolTask)
registerVisual(PRIORITY_VISUAL_DECAL, decalVisual)
registerPreprocessor(globalBackwardCompat)
registerProcessor(PRIORITY_PROCESSOR_DATASTACK, dataStack)
registerLoading('default', defaultLoading) // Default actions
registerAction(
{
type: HIGHLIGHT_ACTION_TYPE,
event: HIGHLIGHT_ACTION_TYPE,
update: HIGHLIGHT_ACTION_TYPE
},
noop
)
registerAction(
{
type: DOWNPLAY_ACTION_TYPE,
event: DOWNPLAY_ACTION_TYPE,
update: DOWNPLAY_ACTION_TYPE
},
noop
)
registerAction(
{
type: SELECT_ACTION_TYPE,
event: SELECT_ACTION_TYPE,
update: SELECT_ACTION_TYPE
},
noop
)
registerAction(
{
type: UNSELECT_ACTION_TYPE,
event: UNSELECT_ACTION_TYPE,
update: UNSELECT_ACTION_TYPE
},
noop
)
registerAction(
{
type: TOGGLE_SELECT_ACTION_TYPE,
event: TOGGLE_SELECT_ACTION_TYPE,
update: TOGGLE_SELECT_ACTION_TYPE
},
noop
) // Default theme
registerTheme('light', lightTheme)
registerTheme('dark', darkTheme) // For backward compatibility, where the namespace `dataTool` will
/*
* 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 dataIndexMapValueLength(valNumOrArrLengthMoreThan2) {
return valNumOrArrLengthMoreThan2 == null
? 0
: valNumOrArrLengthMoreThan2.length || 1
}
function defaultKeyGetter(item) {
return item
}
var DataDiffer = /** @class */ (function () {
/**
* @param context Can be visited by this.context in callback.
*/ function DataDiffer(
oldArr,
newArr,
oldKeyGetter,
newKeyGetter,
context, // By default: 'oneToOne'.
diffMode
) {
this._old = oldArr
this._new = newArr
this._oldKeyGetter = oldKeyGetter || defaultKeyGetter
this._newKeyGetter = newKeyGetter || defaultKeyGetter // Visible in callback via `this.context`;
this.context = context
this._diffModeMultiple = diffMode === 'multiple'
}
/**
* Callback function when add a data
*/ DataDiffer.prototype.add = function (func) {
this._add = func
return this
}
/**
* Callback function when update a data
*/ DataDiffer.prototype.update = function (func) {
this._update = func
return this
}
/**
* Callback function when update a data and only work in `cbMode: 'byKey'`.
*/ DataDiffer.prototype.updateManyToOne = function (func) {
this._updateManyToOne = func
return this
}
/**
* Callback function when update a data and only work in `cbMode: 'byKey'`.
*/ DataDiffer.prototype.updateOneToMany = function (func) {
this._updateOneToMany = func
return this
}
/**
* Callback function when update a data and only work in `cbMode: 'byKey'`.
*/ DataDiffer.prototype.updateManyToMany = function (func) {
this._updateManyToMany = func
return this
}
/**
* Callback function when remove a data
*/ DataDiffer.prototype.remove = function (func) {
this._remove = func
return this
}
DataDiffer.prototype.execute = function () {
this[
this._diffModeMultiple ? '_executeMultiple' : '_executeOneToOne'
]()
}
DataDiffer.prototype._executeOneToOne = function () {
var oldArr = this._old
var newArr = this._new
var newDataIndexMap = {}
var oldDataKeyArr = new Array(oldArr.length)
var newDataKeyArr = new Array(newArr.length)
this._initIndexMap(oldArr, null, oldDataKeyArr, '_oldKeyGetter')
this._initIndexMap(
newArr,
newDataIndexMap,
newDataKeyArr,
'_newKeyGetter'
)
for (var i = 0; i < oldArr.length; i++) {
var oldKey = oldDataKeyArr[i]
var newIdxMapVal = newDataIndexMap[oldKey]
var newIdxMapValLen = dataIndexMapValueLength(newIdxMapVal) // idx can never be empty array here. see 'set null' logic below.
if (newIdxMapValLen > 1) {
// Consider there is duplicate key (for example, use dataItem.name as key).
// We should make sure every item in newArr and oldArr can be visited.
var newIdx = newIdxMapVal.shift()
if (newIdxMapVal.length === 1) {
newDataIndexMap[oldKey] = newIdxMapVal[0]
}
this._update && this._update(newIdx, i)
} else if (newIdxMapValLen === 1) {
newDataIndexMap[oldKey] = null
this._update && this._update(newIdxMapVal, i)
} else {
this._remove && this._remove(i)
}
}
this._performRestAdd(newDataKeyArr, newDataIndexMap)
}
/**
* For example, consider the case:
* oldData: [o0, o1, o2, o3, o4, o5, o6, o7],
* newData: [n0, n1, n2, n3, n4, n5, n6, n7, n8],
* Where:
* o0, o1, n0 has key 'a' (many to one)
* o5, n4, n5, n6 has key 'b' (one to many)
* o2, n1 has key 'c' (one to one)
* n2, n3 has key 'd' (add)
* o3, o4 has key 'e' (remove)
* o6, o7, n7, n8 has key 'f' (many to many, treated as add and remove)
* Then:
* (The order of the following directives are not ensured.)
* this._updateManyToOne(n0, [o0, o1]);
* this._updateOneToMany([n4, n5, n6], o5);
* this._update(n1, o2);
* this._remove(o3);
* this._remove(o4);
* this._remove(o6);
* this._remove(o7);
* this._add(n2);
* this._add(n3);
* this._add(n7);
* this._add(n8);
*/ DataDiffer.prototype._executeMultiple = function () {
var oldArr = this._old
var newArr = this._new
var oldDataIndexMap = {}
var newDataIndexMap = {}
var oldDataKeyArr = []
var newDataKeyArr = []
this._initIndexMap(
oldArr,
oldDataIndexMap,
oldDataKeyArr,
'_oldKeyGetter'
)
this._initIndexMap(
newArr,
newDataIndexMap,
newDataKeyArr,
'_newKeyGetter'
)
for (var i = 0; i < oldDataKeyArr.length; i++) {
var oldKey = oldDataKeyArr[i]
var oldIdxMapVal = oldDataIndexMap[oldKey]
var newIdxMapVal = newDataIndexMap[oldKey]
var oldIdxMapValLen = dataIndexMapValueLength(oldIdxMapVal)
var newIdxMapValLen = dataIndexMapValueLength(newIdxMapVal)
if (oldIdxMapValLen > 1 && newIdxMapValLen === 1) {
this._updateManyToOne &&
this._updateManyToOne(newIdxMapVal, oldIdxMapVal)
newDataIndexMap[oldKey] = null
} else if (oldIdxMapValLen === 1 && newIdxMapValLen > 1) {
this._updateOneToMany &&
this._updateOneToMany(newIdxMapVal, oldIdxMapVal)
newDataIndexMap[oldKey] = null
} else if (oldIdxMapValLen === 1 && newIdxMapValLen === 1) {
this._update && this._update(newIdxMapVal, oldIdxMapVal)
newDataIndexMap[oldKey] = null
} else if (oldIdxMapValLen > 1 && newIdxMapValLen > 1) {
this._updateManyToMany &&
this._updateManyToMany(newIdxMapVal, oldIdxMapVal)
newDataIndexMap[oldKey] = null
} else if (oldIdxMapValLen > 1) {
for (var i_1 = 0; i_1 < oldIdxMapValLen; i_1++) {
this._remove && this._remove(oldIdxMapVal[i_1])
}
} else {
this._remove && this._remove(oldIdxMapVal)
}
}
this._performRestAdd(newDataKeyArr, newDataIndexMap)
}
DataDiffer.prototype._performRestAdd = function (
newDataKeyArr,
newDataIndexMap
) {
for (var i = 0; i < newDataKeyArr.length; i++) {
var newKey = newDataKeyArr[i]
var newIdxMapVal = newDataIndexMap[newKey]
var idxMapValLen = dataIndexMapValueLength(newIdxMapVal)
if (idxMapValLen > 1) {
for (var j = 0; j < idxMapValLen; j++) {
this._add && this._add(newIdxMapVal[j])
}
} else if (idxMapValLen === 1) {
this._add && this._add(newIdxMapVal)
} // Support both `newDataKeyArr` are duplication removed or not removed.
newDataIndexMap[newKey] = null
}
}
DataDiffer.prototype._initIndexMap = function (
arr, // Can be null.
map, // In 'byKey', the output `keyArr` is duplication removed.
// In 'byIndex', the output `keyArr` is not duplication removed and
// its indices are accurately corresponding to `arr`.
keyArr,
keyGetterName
) {
var cbModeMultiple = this._diffModeMultiple
for (var i = 0; i < arr.length; i++) {
// Add prefix to avoid conflict with Object.prototype.
var key = '_ec_' + this[keyGetterName](arr[i], i)
if (!cbModeMultiple) {
keyArr[i] = key
}
if (!map) {
continue
}
var idxMapVal = map[key]
var idxMapValLen = dataIndexMapValueLength(idxMapVal)
if (idxMapValLen === 0) {
// Simple optimize: in most cases, one index has one key,
// do not need array.
map[key] = i
if (cbModeMultiple) {
keyArr.push(key)
}
} else if (idxMapValLen === 1) {
map[key] = [idxMapVal, i]
} else {
idxMapVal.push(i)
}
}
}
return DataDiffer
})()
var DataDiffer$1 = DataDiffer
var DimensionUserOuput = /** @class */ (function () {
function DimensionUserOuput(encode, dimRequest) {
this._encode = encode
this._schema = dimRequest
}
DimensionUserOuput.prototype.get = function () {
return {
// Do not generate full dimension name until fist used.
fullDimensions: this._getFullDimensionNames(),
encode: this._encode
}
}
/**
* Get all data store dimension names.
* Theoretically a series data store is defined both by series and used dataset (if any).
* If some dimensions are omitted for performance reason in `this.dimensions`,
* the dimension name may not be auto-generated if user does not specify a dimension name.
* In this case, the dimension name is `null`/`undefined`.
*/ DimensionUserOuput.prototype._getFullDimensionNames =
function () {
if (!this._cachedDimNames) {
this._cachedDimNames = this._schema
? this._schema.makeOutputDimensionNames()
: []
}
return this._cachedDimNames
}
return DimensionUserOuput
})()
function summarizeDimensions(data, schema) {
var summary = {}
var encode = (summary.encode = {})
var notExtraCoordDimMap = createHashMap()
var defaultedLabel = []
var defaultedTooltip = []
var userOutputEncode = {}
each$7(data.dimensions, function (dimName) {
var dimItem = data.getDimensionInfo(dimName)
var coordDim = dimItem.coordDim
if (coordDim) {
var coordDimIndex = dimItem.coordDimIndex
getOrCreateEncodeArr(encode, coordDim)[coordDimIndex] = dimName
if (!dimItem.isExtraCoord) {
notExtraCoordDimMap.set(coordDim, 1) // Use the last coord dim (and label friendly) as default label,
// because when dataset is used, it is hard to guess which dimension
// can be value dimension. If both show x, y on label is not look good,
// and conventionally y axis is focused more.
if (mayLabelDimType(dimItem.type)) {
defaultedLabel[0] = dimName
} // User output encode do not contain generated coords.
// And it only has index. User can use index to retrieve value from the raw item array.
getOrCreateEncodeArr(userOutputEncode, coordDim)[
coordDimIndex
] = data.getDimensionIndex(dimItem.name)
}
if (dimItem.defaultTooltip) {
defaultedTooltip.push(dimName)
}
}
VISUAL_DIMENSIONS.each(function (v, otherDim) {
var encodeArr = getOrCreateEncodeArr(encode, otherDim)
var dimIndex = dimItem.otherDims[otherDim]
if (dimIndex != null && dimIndex !== false) {
encodeArr[dimIndex] = dimItem.name
}
})
})
var dataDimsOnCoord = []
var encodeFirstDimNotExtra = {}
notExtraCoordDimMap.each(function (v, coordDim) {
var dimArr = encode[coordDim]
encodeFirstDimNotExtra[coordDim] = dimArr[0] // Not necessary to remove duplicate, because a data
// dim canot on more than one coordDim.
dataDimsOnCoord = dataDimsOnCoord.concat(dimArr)
})
summary.dataDimsOnCoord = dataDimsOnCoord
summary.dataDimIndicesOnCoord = map$1(
dataDimsOnCoord,
function (dimName) {
return data.getDimensionInfo(dimName).storeDimIndex
}
)
summary.encodeFirstDimNotExtra = encodeFirstDimNotExtra
var encodeLabel = encode.label // FIXME `encode.label` is not recommanded, because formatter can not be set
// in this way. Use label.formatter instead. May be remove this approach someday.
if (encodeLabel && encodeLabel.length) {
defaultedLabel = encodeLabel.slice()
}
var encodeTooltip = encode.tooltip
if (encodeTooltip && encodeTooltip.length) {
defaultedTooltip = encodeTooltip.slice()
} else if (!defaultedTooltip.length) {
defaultedTooltip = defaultedLabel.slice()
}
encode.defaultedLabel = defaultedLabel
encode.defaultedTooltip = defaultedTooltip
summary.userOutput = new DimensionUserOuput(
userOutputEncode,
schema
)
return summary
}
function getOrCreateEncodeArr(encode, dim) {
if (!encode.hasOwnProperty(dim)) {
encode[dim] = []
}
return encode[dim]
} // FIXME:TS should be type `AxisType`
function getDimensionTypeByAxis(axisType) {
return axisType === 'category'
? 'ordinal'
: axisType === 'time'
? 'time'
: 'float'
}
function mayLabelDimType(dimType) {
// In most cases, ordinal and time do not suitable for label.
// Ordinal info can be displayed on axis. Time is too long.
return !(dimType === 'ordinal' || dimType === 'time')
} // function findTheLastDimMayLabel(data) {
// // Get last value dim
// let dimensions = data.dimensions.slice();
// let valueType;
// let valueDim;
// while (dimensions.length && (
// valueDim = dimensions.pop(),
// valueType = data.getDimensionInfo(valueDim).type,
// valueType === 'ordinal' || valueType === 'time'
// )) {} // jshint ignore:line
// return valueDim;
// }
var SeriesDimensionDefine = /** @class */ (function () {
/**
* @param opt All of the fields will be shallow copied.
*/ function SeriesDimensionDefine(opt) {
/**
* The format of `otherDims` is:
* ```js
* {
* tooltip?: number
* label?: number
* itemName?: number
* seriesName?: number
* }
* ```
*
* A `series.encode` can specified these fields:
* ```js
* encode: {
* // "3, 1, 5" is the index of data dimension.
* tooltip: [3, 1, 5],
* label: [0, 3],
* ...
* }
* ```
* `otherDims` is the parse result of the `series.encode` above, like:
* ```js
* // Suppose the index of this data dimension is `3`.
* this.otherDims = {
* // `3` is at the index `0` of the `encode.tooltip`
* tooltip: 0,
* // `3` is at the index `1` of the `encode.label`
* label: 1
* };
* ```
*
* This prop should never be `null`/`undefined` after initialized.
*/ this.otherDims = {}
if (opt != null) {
extend(this, opt)
}
}
return SeriesDimensionDefine
})()
var SeriesDimensionDefine$1 = SeriesDimensionDefine
var inner$7 = makeInner()
var dimTypeShort = {
float: 'f',
int: 'i',
ordinal: 'o',
number: 'n',
time: 't'
}
/**
* Represents the dimension requirement of a series.
*
* NOTICE:
* When there are too many dimensions in dataset and many series, only the used dimensions
* (i.e., used by coord sys and declared in `series.encode`) are add to `dimensionDefineList`.
* But users may query data by other unused dimension names.
* In this case, users can only query data if and only if they have defined dimension names
* via ec option, so we provide `getDimensionIndexFromSource`, which only query them from
* `source` dimensions.
*/ var SeriesDataSchema = /** @class */ (function () {
function SeriesDataSchema(opt) {
this.dimensions = opt.dimensions
this._dimOmitted = opt.dimensionOmitted
this.source = opt.source
this._fullDimCount = opt.fullDimensionCount
this._updateDimOmitted(opt.dimensionOmitted)
}
SeriesDataSchema.prototype.isDimensionOmitted = function () {
return this._dimOmitted
}
SeriesDataSchema.prototype._updateDimOmitted = function (
dimensionOmitted
) {
this._dimOmitted = dimensionOmitted
if (!dimensionOmitted) {
return
}
if (!this._dimNameMap) {
this._dimNameMap = ensureSourceDimNameMap(this.source)
}
}
/**
* @caution Can only be used when `dimensionOmitted: true`.
*
* Get index by user defined dimension name (i.e., not internal generate name).
* That is, get index from `dimensionsDefine`.
* If no `dimensionsDefine`, or no name get, return -1.
*/ SeriesDataSchema.prototype.getSourceDimensionIndex = function (
dimName
) {
return retrieve2(this._dimNameMap.get(dimName), -1)
}
/**
* @caution Can only be used when `dimensionOmitted: true`.
*
* Notice: may return `null`/`undefined` if user not specify dimension names.
*/ SeriesDataSchema.prototype.getSourceDimension = function (
dimIndex
) {
var dimensionsDefine = this.source.dimensionsDefine
if (dimensionsDefine) {
return dimensionsDefine[dimIndex]
}
}
SeriesDataSchema.prototype.makeStoreSchema = function () {
var dimCount = this._fullDimCount
var willRetrieveDataByName = shouldRetrieveDataByName(this.source)
var makeHashStrict = !shouldOmitUnusedDimensions(dimCount) // If source don't have dimensions or series don't omit unsed dimensions.
// Generate from seriesDimList directly
var dimHash = ''
var dims = []
for (
var fullDimIdx = 0, seriesDimIdx = 0;
fullDimIdx < dimCount;
fullDimIdx++
) {
var property = void 0
var type = void 0
var ordinalMeta = void 0
var seriesDimDef = this.dimensions[seriesDimIdx] // The list has been sorted by `storeDimIndex` asc.
if (seriesDimDef && seriesDimDef.storeDimIndex === fullDimIdx) {
property = willRetrieveDataByName ? seriesDimDef.name : null
type = seriesDimDef.type
ordinalMeta = seriesDimDef.ordinalMeta
seriesDimIdx++
} else {
var sourceDimDef = this.getSourceDimension(fullDimIdx)
if (sourceDimDef) {
property = willRetrieveDataByName ? sourceDimDef.name : null
type = sourceDimDef.type
}
}
dims.push({
property: property,
type: type,
ordinalMeta: ordinalMeta
}) // If retrieving data by index,
// use to determine whether data can be shared.
// (Becuase in this case there might be no dimension name defined in dataset, but indices always exists).
// (indices are always 0, 1, 2, ..., so we can ignore them to shorten the hash).
// Otherwise if retrieving data by property name (like `data: [{aa: 123, bb: 765}, ...]`),
// use in hash.
if (
willRetrieveDataByName &&
property != null && // For data stack, we have make sure each series has its own dim on this store.
// So we do not add property to hash to make sure they can share this store.
(!seriesDimDef || !seriesDimDef.isCalculationCoord)
) {
dimHash += makeHashStrict // Use escape character '`' in case that property name contains '$'.
? property.replace(/\`/g, '`1').replace(/\$/g, '`2') // For better performance, when there are large dimensions, tolerant this defects that hardly meet.
: property
}
dimHash += '$'
dimHash += dimTypeShort[type] || 'f'
if (ordinalMeta) {
dimHash += ordinalMeta.uid
}
dimHash += '$'
} // Source from endpoint(usually series) will be read differently
// when seriesLayoutBy or startIndex(which is affected by sourceHeader) are different.
// So we use this three props as key.
var source = this.source
var hash = [
source.seriesLayoutBy,
source.startIndex,
dimHash
].join('$$')
return { dimensions: dims, hash: hash }
}
SeriesDataSchema.prototype.makeOutputDimensionNames = function () {
var result = []
for (
var fullDimIdx = 0, seriesDimIdx = 0;
fullDimIdx < this._fullDimCount;
fullDimIdx++
) {
var name_1 = void 0
var seriesDimDef = this.dimensions[seriesDimIdx] // The list has been sorted by `storeDimIndex` asc.
if (seriesDimDef && seriesDimDef.storeDimIndex === fullDimIdx) {
if (!seriesDimDef.isCalculationCoord) {
name_1 = seriesDimDef.name
}
seriesDimIdx++
} else {
var sourceDimDef = this.getSourceDimension(fullDimIdx)
if (sourceDimDef) {
name_1 = sourceDimDef.name
}
}
result.push(name_1)
}
return result
}
SeriesDataSchema.prototype.appendCalculationDimension = function (
dimDef
) {
this.dimensions.push(dimDef)
dimDef.isCalculationCoord = true
this._fullDimCount++ // If append dimension on a data store, consider the store
// might be shared by different series, series dimensions not
// really map to store dimensions.
this._updateDimOmitted(true)
}
return SeriesDataSchema
})()
function isSeriesDataSchema(schema) {
return schema instanceof SeriesDataSchema
}
function createDimNameMap(dimsDef) {
var dataDimNameMap = createHashMap()
for (var i = 0; i < (dimsDef || []).length; i++) {
var dimDefItemRaw = dimsDef[i]
var userDimName = isObject$2(dimDefItemRaw)
? dimDefItemRaw.name
: dimDefItemRaw
if (
userDimName != null &&
dataDimNameMap.get(userDimName) == null
) {
dataDimNameMap.set(userDimName, i)
}
}
return dataDimNameMap
}
function ensureSourceDimNameMap(source) {
var innerSource = inner$7(source)
return (
innerSource.dimNameMap ||
(innerSource.dimNameMap = createDimNameMap(
source.dimensionsDefine
))
)
}
function shouldOmitUnusedDimensions(dimCount) {
return dimCount > 30
}
var isObject = isObject$2
var map = map$1
var CtorInt32Array =
typeof Int32Array === 'undefined' ? Array : Int32Array // Use prefix to avoid index to be the same as otherIdList[idx],
// which will cause weird udpate animation.
var ID_PREFIX = 'e\0\0'
var INDEX_NOT_FOUND = -1 // type SeriesDimensionIndex = DimensionIndex;
var TRANSFERABLE_PROPERTIES = [
'hasItemOption',
'_nameList',
'_idList',
'_invertedIndicesMap',
'_dimSummary',
'userOutput',
'_rawData',
'_dimValueGetter',
'_nameDimIdx',
'_idDimIdx',
'_nameRepeatCount'
]
var CLONE_PROPERTIES = ['_approximateExtent'] // -----------------------------
// Internal method declarations:
// -----------------------------
var prepareInvertedIndex
var getId
var getIdNameFromStore
var normalizeDimensions
var transferProperties
var cloneListForMapAndSample
var makeIdFromName
var SeriesData = /** @class */ (function () {
/**
* @param dimensionsInput.dimensions
* For example, ['someDimName', {name: 'someDimName', type: 'someDimType'}, ...].
* Dimensions should be concrete names like x, y, z, lng, lat, angle, radius
*/ function SeriesData(dimensionsInput, hostModel) {
this.type = 'list'
this._dimOmitted = false
this._nameList = []
this._idList = [] // Models of data option is stored sparse for optimizing memory cost
// Never used yet (not used yet).
// private _optionModels: Model[] = [];
// Global visual properties after visual coding
this._visual = {} // Globel layout properties.
this._layout = {} // Item visual properties after visual coding
this._itemVisuals = [] // Item layout properties after layout
this._itemLayouts = [] // Graphic elemnents
this._graphicEls = [] // key: dim, value: extent
this._approximateExtent = {}
this._calculationInfo = {} // Having detected that there is data item is non primitive type
// (in type `OptionDataItemObject`).
// Like `data: [ { value: xx, itemStyle: {...} }, ...]`
// At present it only happen in `SOURCE_FORMAT_ORIGINAL`.
this.hasItemOption = false // Methods that create a new list based on this list should be listed here.
// Notice that those method should `RETURN` the new list.
this.TRANSFERABLE_METHODS = [
'cloneShallow',
'downSample',
'lttbDownSample',
'map'
] // Methods that change indices of this list should be listed here.
this.CHANGABLE_METHODS = ['filterSelf', 'selectRange']
this.DOWNSAMPLE_METHODS = ['downSample', 'lttbDownSample']
var dimensions
var assignStoreDimIdx = false
if (isSeriesDataSchema(dimensionsInput)) {
dimensions = dimensionsInput.dimensions
this._dimOmitted = dimensionsInput.isDimensionOmitted()
this._schema = dimensionsInput
} else {
assignStoreDimIdx = true
dimensions = dimensionsInput
}
dimensions = dimensions || ['x', 'y']
var dimensionInfos = {}
var dimensionNames = []
var invertedIndicesMap = {}
var needsHasOwn = false
var emptyObj = {}
for (var i = 0; i < dimensions.length; i++) {
// Use the original dimensions[i], where other flag props may exists.
var dimInfoInput = dimensions[i]
var dimensionInfo = isString(dimInfoInput)
? new SeriesDimensionDefine$1({ name: dimInfoInput })
: !(dimInfoInput instanceof SeriesDimensionDefine$1)
? new SeriesDimensionDefine$1(dimInfoInput)
: dimInfoInput
var dimensionName = dimensionInfo.name
dimensionInfo.type = dimensionInfo.type || 'float'
if (!dimensionInfo.coordDim) {
dimensionInfo.coordDim = dimensionName
dimensionInfo.coordDimIndex = 0
}
var otherDims = (dimensionInfo.otherDims =
dimensionInfo.otherDims || {})
dimensionNames.push(dimensionName)
dimensionInfos[dimensionName] = dimensionInfo
if (emptyObj[dimensionName] != null) {
needsHasOwn = true
}
if (dimensionInfo.createInvertedIndices) {
invertedIndicesMap[dimensionName] = []
}
if (otherDims.itemName === 0) {
this._nameDimIdx = i
}
if (otherDims.itemId === 0) {
this._idDimIdx = i
}
if (assignStoreDimIdx) {
dimensionInfo.storeDimIndex = i
}
}
this.dimensions = dimensionNames
this._dimInfos = dimensionInfos
this._initGetDimensionInfo(needsHasOwn)
this.hostModel = hostModel
this._invertedIndicesMap = invertedIndicesMap
if (this._dimOmitted) {
var dimIdxToName_1 = (this._dimIdxToName = createHashMap())
each$7(dimensionNames, function (dimName) {
dimIdxToName_1.set(
dimensionInfos[dimName].storeDimIndex,
dimName
)
})
}
}
/**
*
* Get concrete dimension name by dimension name or dimension index.
* If input a dimension name, do not validate whether the dimension name exits.
*
* @caution
* @param dim Must make sure the dimension is `SeriesDimensionLoose`.
* Because only those dimensions will have auto-generated dimension names if not
* have a user-specified name, and other dimensions will get a return of null/undefined.
*
* @notice Becuause of this reason, should better use `getDimensionIndex` instead, for examples:
* ```js
* const val = data.getStore().get(data.getDimensionIndex(dim), dataIdx);
* ```
*
* @return Concrete dim name.
*/ SeriesData.prototype.getDimension = function (dim) {
var dimIdx = this._recognizeDimIndex(dim)
if (dimIdx == null) {
return dim
}
dimIdx = dim
if (!this._dimOmitted) {
return this.dimensions[dimIdx]
} // Retrieve from series dimension definition becuase it probably contains
// generated dimension name (like 'x', 'y').
var dimName = this._dimIdxToName.get(dimIdx)
if (dimName != null) {
return dimName
}
var sourceDimDef = this._schema.getSourceDimension(dimIdx)
if (sourceDimDef) {
return sourceDimDef.name
}
}
/**
* Get dimension index in data store. Return -1 if not found.
* Can be used to index value from getRawValue.
*/ SeriesData.prototype.getDimensionIndex = function (dim) {
var dimIdx = this._recognizeDimIndex(dim)
if (dimIdx != null) {
return dimIdx
}
if (dim == null) {
return -1
}
var dimInfo = this._getDimInfo(dim)
return dimInfo
? dimInfo.storeDimIndex
: this._dimOmitted
? this._schema.getSourceDimensionIndex(dim)
: -1
}
/**
* The meanings of the input parameter `dim`:
*
* + If dim is a number (e.g., `1`), it means the index of the dimension.
* For example, `getDimension(0)` will return 'x' or 'lng' or 'radius'.
* + If dim is a number-like string (e.g., `"1"`):
* + If there is the same concrete dim name defined in `series.dimensions` or `dataset.dimensions`,
* it means that concrete name.
* + If not, it will be converted to a number, which means the index of the dimension.
* (why? because of the backward compatbility. We have been tolerating number-like string in
* dimension setting, although now it seems that it is not a good idea.)
* For example, `visualMap[i].dimension: "1"` is the same meaning as `visualMap[i].dimension: 1`,
* if no dimension name is defined as `"1"`.
* + If dim is a not-number-like string, it means the concrete dim name.
* For example, it can be be default name `"x"`, `"y"`, `"z"`, `"lng"`, `"lat"`, `"angle"`, `"radius"`,
* or customized in `dimensions` property of option like `"age"`.
*
* @return recogonized `DimensionIndex`. Otherwise return null/undefined (means that dim is `DimensionName`).
*/ SeriesData.prototype._recognizeDimIndex = function (dim) {
if (
isNumber(dim) || // If being a number-like string but not being defined as a dimension name.
(dim != null &&
!isNaN(dim) &&
!this._getDimInfo(dim) &&
(!this._dimOmitted ||
this._schema.getSourceDimensionIndex(dim) < 0))
) {
return +dim
}
}
SeriesData.prototype._getStoreDimIndex = function (dim) {
var dimIdx = this.getDimensionIndex(dim)
return dimIdx
}
/**
* Get type and calculation info of particular dimension
* @param dim
* Dimension can be concrete names like x, y, z, lng, lat, angle, radius
* Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius'
*/ SeriesData.prototype.getDimensionInfo = function (dim) {
// Do not clone, because there may be categories in dimInfo.
return this._getDimInfo(this.getDimension(dim))
}
SeriesData.prototype._initGetDimensionInfo = function (
needsHasOwn
) {
var dimensionInfos = this._dimInfos
this._getDimInfo = needsHasOwn
? function (dimName) {
return dimensionInfos.hasOwnProperty(dimName)
? dimensionInfos[dimName]
: undefined
}
: function (dimName) {
return dimensionInfos[dimName]
}
}
/**
* concrete dimension name list on coord.
*/ SeriesData.prototype.getDimensionsOnCoord = function () {
return this._dimSummary.dataDimsOnCoord.slice()
}
SeriesData.prototype.mapDimension = function (coordDim, idx) {
var dimensionsSummary = this._dimSummary
if (idx == null) {
return dimensionsSummary.encodeFirstDimNotExtra[coordDim]
}
var dims = dimensionsSummary.encode[coordDim]
return dims ? dims[idx] : null
}
SeriesData.prototype.mapDimensionsAll = function (coordDim) {
var dimensionsSummary = this._dimSummary
var dims = dimensionsSummary.encode[coordDim]
return (dims || []).slice()
}
SeriesData.prototype.getStore = function () {
return this._store
}
/**
* Initialize from data
* @param data source or data or data store.
* @param nameList The name of a datum is used on data diff and
* default label/tooltip.
* A name can be specified in encode.itemName,
* or dataItem.name (only for series option data),
* or provided in nameList from outside.
*/ SeriesData.prototype.initData = function (
data,
nameList,
dimValueGetter
) {
var _this = this
var store
if (data instanceof DataStore) {
store = data
}
if (!store) {
var dimensions = this.dimensions
var provider =
isSourceInstance(data) || isArrayLike(data)
? new DefaultDataProvider(data, dimensions.length)
: data
store = new DataStore()
var dimensionInfos = map(dimensions, function (dimName) {
return {
type: _this._dimInfos[dimName].type,
property: dimName
}
})
store.initData(provider, dimensionInfos, dimValueGetter)
}
this._store = store // Reset
this._nameList = (nameList || []).slice()
this._idList = []
this._nameRepeatCount = {}
this._doInit(0, store.count()) // Cache summary info for fast visit. See "dimensionHelper".
// Needs to be initialized after store is prepared.
this._dimSummary = summarizeDimensions(this, this._schema)
this.userOutput = this._dimSummary.userOutput
}
/**
* Caution: Can be only called on raw data (before `this._indices` created).
*/ SeriesData.prototype.appendData = function (data) {
var range = this._store.appendData(data)
this._doInit(range[0], range[1])
}
/**
* Caution: Can be only called on raw data (before `this._indices` created).
* This method does not modify `rawData` (`dataProvider`), but only
* add values to store.
*
* The final count will be increased by `Math.max(values.length, names.length)`.
*
* @param values That is the SourceType: 'arrayRows', like
* [
* [12, 33, 44],
* [NaN, 43, 1],
* ['-', 'asdf', 0]
* ]
* Each item is exaclty cooresponding to a dimension.
*/ SeriesData.prototype.appendValues = function (values, names) {
var _a = this._store.appendValues(values, names.length),
start = _a.start,
end = _a.end
var shouldMakeIdFromName = this._shouldMakeIdFromName()
this._updateOrdinalMeta()
if (names) {
for (var idx = start; idx < end; idx++) {
var sourceIdx = idx - start
this._nameList[idx] = names[sourceIdx]
if (shouldMakeIdFromName) {
makeIdFromName(this, idx)
}
}
}
}
SeriesData.prototype._updateOrdinalMeta = function () {
var store = this._store
var dimensions = this.dimensions
for (var i = 0; i < dimensions.length; i++) {
var dimInfo = this._dimInfos[dimensions[i]]
if (dimInfo.ordinalMeta) {
store.collectOrdinalMeta(
dimInfo.storeDimIndex,
dimInfo.ordinalMeta
)
}
}
}
SeriesData.prototype._shouldMakeIdFromName = function () {
var provider = this._store.getProvider()
return (
this._idDimIdx == null &&
provider.getSource().sourceFormat !==
SOURCE_FORMAT_TYPED_ARRAY &&
!provider.fillStorage
)
}
SeriesData.prototype._doInit = function (start, end) {
if (start >= end) {
return
}
var store = this._store
var provider = store.getProvider()
this._updateOrdinalMeta()
var nameList = this._nameList
var idList = this._idList
var sourceFormat = provider.getSource().sourceFormat
var isFormatOriginal = sourceFormat === SOURCE_FORMAT_ORIGINAL // 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
// If dataItem is {name: ...} or {id: ...}, it has highest priority.
// This kind of ids and names are always stored `_nameList` and `_idList`.
if (isFormatOriginal && !provider.pure) {
var sharedDataItem = []
for (var idx = start; idx < end; idx++) {
// NOTICE: Try not to write things into dataItem
var dataItem = provider.getItem(idx, sharedDataItem)
if (!this.hasItemOption && isDataItemOption(dataItem)) {
this.hasItemOption = true
}
if (dataItem) {
var itemName = dataItem.name
if (nameList[idx] == null && itemName != null) {
nameList[idx] = convertOptionIdName(itemName, null)
}
var itemId = dataItem.id
if (idList[idx] == null && itemId != null) {
idList[idx] = convertOptionIdName(itemId, null)
}
}
}
}
if (this._shouldMakeIdFromName()) {
for (var idx = start; idx < end; idx++) {
makeIdFromName(this, idx)
}
}
prepareInvertedIndex(this)
}
/**
* PENDING: In fact currently this function is only used to short-circuit
* the calling of `scale.unionExtentFromData` when data have been filtered by modules
* like "dataZoom". `scale.unionExtentFromData` is used to calculate data extent for series on
* an axis, but if a "axis related data filter module" is used, the extent of the axis have
* been fixed and no need to calling `scale.unionExtentFromData` actually.
* But if we add "custom data filter" in future, which is not "axis related", this method may
* be still needed.
*
* Optimize for the scenario that data is filtered by a given extent.
* Consider that if data amount is more than hundreds of thousand,
* extent calculation will cost more than 10ms and the cache will
* be erased because of the filtering.
*/ SeriesData.prototype.getApproximateExtent = function (dim) {
return (
this._approximateExtent[dim] ||
this._store.getDataExtent(this._getStoreDimIndex(dim))
)
}
/**
* Calculate extent on a filtered data might be time consuming.
* Approximate extent is only used for: calculte extent of filtered data outside.
*/ SeriesData.prototype.setApproximateExtent = function (
extent,
dim
) {
dim = this.getDimension(dim)
this._approximateExtent[dim] = extent.slice()
}
SeriesData.prototype.getCalculationInfo = function (key) {
return this._calculationInfo[key]
}
SeriesData.prototype.setCalculationInfo = function (key, value) {
isObject(key)
? extend(this._calculationInfo, key)
: (this._calculationInfo[key] = value)
}
/**
* @return Never be null/undefined. `number` will be converted to string. Becuase:
* In most cases, name is used in display, where returning a string is more convenient.
* In other cases, name is used in query (see `indexOfName`), where we can keep the
* rule that name `2` equals to name `'2'`.
*/ SeriesData.prototype.getName = function (idx) {
var rawIndex = this.getRawIndex(idx)
var name = this._nameList[rawIndex]
if (name == null && this._nameDimIdx != null) {
name = getIdNameFromStore(this, this._nameDimIdx, rawIndex)
}
if (name == null) {
name = ''
}
return name
}
SeriesData.prototype._getCategory = function (dimIdx, idx) {
var ordinal = this._store.get(dimIdx, idx)
var ordinalMeta = this._store.getOrdinalMeta(dimIdx)
if (ordinalMeta) {
return ordinalMeta.categories[ordinal]
}
return ordinal
}
/**
* @return Never null/undefined. `number` will be converted to string. Becuase:
* In all cases having encountered at present, id is used in making diff comparison, which
* are usually based on hash map. We can keep the rule that the internal id are always string
* (treat `2` is the same as `'2'`) to make the related logic simple.
*/ SeriesData.prototype.getId = function (idx) {
return getId(this, this.getRawIndex(idx))
}
SeriesData.prototype.count = function () {
return this._store.count()
}
/**
* Get value. Return NaN if idx is out of range.
*
* @notice Should better to use `data.getStore().get(dimIndex, dataIdx)` instead.
*/ SeriesData.prototype.get = function (dim, idx) {
var store = this._store
var dimInfo = this._dimInfos[dim]
if (dimInfo) {
return store.get(dimInfo.storeDimIndex, idx)
}
}
/**
* @notice Should better to use `data.getStore().getByRawIndex(dimIndex, dataIdx)` instead.
*/ SeriesData.prototype.getByRawIndex = function (dim, rawIdx) {
var store = this._store
var dimInfo = this._dimInfos[dim]
if (dimInfo) {
return store.getByRawIndex(dimInfo.storeDimIndex, rawIdx)
}
}
SeriesData.prototype.getIndices = function () {
return this._store.getIndices()
}
SeriesData.prototype.getDataExtent = function (dim) {
return this._store.getDataExtent(this._getStoreDimIndex(dim))
}
SeriesData.prototype.getSum = function (dim) {
return this._store.getSum(this._getStoreDimIndex(dim))
}
SeriesData.prototype.getMedian = function (dim) {
return this._store.getMedian(this._getStoreDimIndex(dim))
}
SeriesData.prototype.getValues = function (dimensions, idx) {
var _this = this
var store = this._store
return isArray(dimensions)
? store.getValues(
map(dimensions, function (dim) {
return _this._getStoreDimIndex(dim)
}),
idx
)
: store.getValues(dimensions)
}
/**
* If value is NaN. Inlcuding '-'
* Only check the coord dimensions.
*/ SeriesData.prototype.hasValue = function (idx) {
var dataDimIndicesOnCoord = this._dimSummary.dataDimIndicesOnCoord
for (
var i = 0, len = dataDimIndicesOnCoord.length;
i < len;
i++
) {
// Ordinal type originally can be string or number.
// But when an ordinal type is used on coord, it can
// not be string but only number. So we can also use isNaN.
if (isNaN(this._store.get(dataDimIndicesOnCoord[i], idx))) {
return false
}
}
return true
}
/**
* Retreive the index with given name
*/ SeriesData.prototype.indexOfName = function (name) {
for (var i = 0, len = this._store.count(); i < len; i++) {
if (this.getName(i) === name) {
return i
}
}
return -1
}
SeriesData.prototype.getRawIndex = function (idx) {
return this._store.getRawIndex(idx)
}
SeriesData.prototype.indexOfRawIndex = function (rawIndex) {
return this._store.indexOfRawIndex(rawIndex)
}
/**
* Only support the dimension which inverted index created.
* Do not support other cases until required.
* @param dim concrete dim
* @param value ordinal index
* @return rawIndex
*/ SeriesData.prototype.rawIndexOf = function (dim, value) {
var invertedIndices = dim && this._invertedIndicesMap[dim]
var rawIndex = invertedIndices[value]
if (rawIndex == null || isNaN(rawIndex)) {
return INDEX_NOT_FOUND
}
return rawIndex
}
/**
* 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.
*/ SeriesData.prototype.indicesOfNearest = function (
dim,
value,
maxDistance
) {
return this._store.indicesOfNearest(
this._getStoreDimIndex(dim),
value,
maxDistance
)
}
SeriesData.prototype.each = function (dims, cb, ctx) {
if (isFunction(dims)) {
ctx = cb
cb = dims
dims = []
} // ctxCompat just for compat echarts3
var fCtx = ctx || this
var dimIndices = map(
normalizeDimensions(dims),
this._getStoreDimIndex,
this
)
this._store.each(dimIndices, fCtx ? bind$1(cb, fCtx) : cb)
}
SeriesData.prototype.filterSelf = function (dims, cb, ctx) {
if (isFunction(dims)) {
ctx = cb
cb = dims
dims = []
} // ctxCompat just for compat echarts3
var fCtx = ctx || this
var dimIndices = map(
normalizeDimensions(dims),
this._getStoreDimIndex,
this
)
this._store = this._store.filter(
dimIndices,
fCtx ? bind$1(cb, fCtx) : cb
)
return this
}
/**
* Select data in range. (For optimization of filter)
* (Manually inline code, support 5 million data filtering in data zoom.)
*/ SeriesData.prototype.selectRange = function (range) {
var _this = this
var innerRange = {}
var dims = keys(range)
each$7(dims, function (dim) {
var dimIdx = _this._getStoreDimIndex(dim)
innerRange[dimIdx] = range[dim]
})
this._store = this._store.selectRange(innerRange)
return this
}
/* eslint-enable max-len */ SeriesData.prototype.mapArray =
function (dims, cb, ctx) {
if (isFunction(dims)) {
ctx = cb
cb = dims
dims = []
} // ctxCompat just for compat echarts3
ctx = ctx || this
var result = []
this.each(
dims,
function () {
result.push(cb && cb.apply(this, arguments))
},
ctx
)
return result
}
SeriesData.prototype.map = function (dims, cb, ctx, ctxCompat) {
var fCtx = ctx || ctxCompat || this
var dimIndices = map(
normalizeDimensions(dims),
this._getStoreDimIndex,
this
)
var list = cloneListForMapAndSample(this)
list._store = this._store.map(
dimIndices,
fCtx ? bind$1(cb, fCtx) : cb
)
return list
}
SeriesData.prototype.modify = function (dims, cb, ctx, ctxCompat) {
var fCtx = ctx || ctxCompat || this
var dimIndices = map(
normalizeDimensions(dims),
this._getStoreDimIndex,
this
) // If do shallow clone here, if there are too many stacked series,
// it still cost lots of memory, becuase `_store.dimensions` are not shared.
// We should consider there probably be shallow clone happen in each sereis
// in consequent filter/map.
this._store.modify(dimIndices, fCtx ? bind$1(cb, fCtx) : cb)
}
/**
* Large data down sampling on given dimension
* @param sampleIndex Sample index for name and id
*/ SeriesData.prototype.downSample = function (
dimension,
rate,
sampleValue,
sampleIndex
) {
var list = cloneListForMapAndSample(this)
list._store = this._store.downSample(
this._getStoreDimIndex(dimension),
rate,
sampleValue,
sampleIndex
)
return list
}
/**
* Large data down sampling using largest-triangle-three-buckets
* @param {string} valueDimension
* @param {number} targetCount
*/ SeriesData.prototype.lttbDownSample = function (
valueDimension,
rate
) {
var list = cloneListForMapAndSample(this)
list._store = this._store.lttbDownSample(
this._getStoreDimIndex(valueDimension),
rate
)
return list
}
SeriesData.prototype.getRawDataItem = function (idx) {
return this._store.getRawDataItem(idx)
}
/**
* Get model of one data item.
*/ // TODO: Type of data item
SeriesData.prototype.getItemModel = function (idx) {
var hostModel = this.hostModel
var dataItem = this.getRawDataItem(idx)
return new Model$1(
dataItem,
hostModel,
hostModel && hostModel.ecModel
)
}
/**
* Create a data differ
*/ SeriesData.prototype.diff = function (otherList) {
var thisList = this
return new DataDiffer$1(
otherList ? otherList.getStore().getIndices() : [],
this.getStore().getIndices(),
function (idx) {
return getId(otherList, idx)
},
function (idx) {
return getId(thisList, idx)
}
)
}
/**
* Get visual property.
*/ SeriesData.prototype.getVisual = function (key) {
var visual = this._visual
return visual && visual[key]
}
SeriesData.prototype.setVisual = function (kvObj, val) {
this._visual = this._visual || {}
if (isObject(kvObj)) {
extend(this._visual, kvObj)
} else {
this._visual[kvObj] = val
}
}
/**
* Get visual property of single data item
*/ // eslint-disable-next-line
SeriesData.prototype.getItemVisual = function (idx, key) {
var itemVisual = this._itemVisuals[idx]
var val = itemVisual && itemVisual[key]
if (val == null) {
// Use global visual property
return this.getVisual(key)
}
return val
}
/**
* If exists visual property of single data item
*/ SeriesData.prototype.hasItemVisual = function () {
return this._itemVisuals.length > 0
}
/**
* Make sure itemVisual property is unique
*/ // TODO: use key to save visual to reduce memory.
SeriesData.prototype.ensureUniqueItemVisual = function (idx, key) {
var itemVisuals = this._itemVisuals
var itemVisual = itemVisuals[idx]
if (!itemVisual) {
itemVisual = itemVisuals[idx] = {}
}
var val = itemVisual[key]
if (val == null) {
val = this.getVisual(key) // TODO Performance?
if (isArray(val)) {
val = val.slice()
} else if (isObject(val)) {
val = extend({}, val)
}
itemVisual[key] = val
}
return val
} // eslint-disable-next-line
SeriesData.prototype.setItemVisual = function (idx, key, value) {
var itemVisual = this._itemVisuals[idx] || {}
this._itemVisuals[idx] = itemVisual
if (isObject(key)) {
extend(itemVisual, key)
} else {
itemVisual[key] = value
}
}
/**
* Clear itemVisuals and list visual.
*/ SeriesData.prototype.clearAllVisual = function () {
this._visual = {}
this._itemVisuals = []
}
SeriesData.prototype.setLayout = function (key, val) {
isObject(key)
? extend(this._layout, key)
: (this._layout[key] = val)
}
/**
* Get layout property.
*/ SeriesData.prototype.getLayout = function (key) {
return this._layout[key]
}
/**
* Get layout of single data item
*/ SeriesData.prototype.getItemLayout = function (idx) {
return this._itemLayouts[idx]
}
/**
* Set layout of single data item
*/ SeriesData.prototype.setItemLayout = function (
idx,
layout,
merge
) {
this._itemLayouts[idx] = merge
? extend(this._itemLayouts[idx] || {}, layout)
: layout
}
/**
* Clear all layout of single data item
*/ SeriesData.prototype.clearItemLayouts = function () {
this._itemLayouts.length = 0
}
/**
* Set graphic element relative to data. It can be set as null
*/ SeriesData.prototype.setItemGraphicEl = function (idx, el) {
var seriesIndex = this.hostModel && this.hostModel.seriesIndex
setCommonECData(seriesIndex, this.dataType, idx, el)
this._graphicEls[idx] = el
}
SeriesData.prototype.getItemGraphicEl = function (idx) {
return this._graphicEls[idx]
}
SeriesData.prototype.eachItemGraphicEl = function (cb, context) {
each$7(this._graphicEls, function (el, idx) {
if (el) {
cb && cb.call(context, el, idx)
}
})
}
/**
* Shallow clone a new list except visual and layout properties, and graph elements.
* New list only change the indices.
*/ SeriesData.prototype.cloneShallow = function (list) {
if (!list) {
list = new SeriesData(
this._schema
? this._schema
: map(this.dimensions, this._getDimInfo, this),
this.hostModel
)
}
transferProperties(list, this)
list._store = this._store
return list
}
/**
* Wrap some method to add more feature
*/ SeriesData.prototype.wrapMethod = function (
methodName,
injectFunction
) {
var originalMethod = this[methodName]
if (!isFunction(originalMethod)) {
return
}
this.__wrappedMethods = this.__wrappedMethods || []
this.__wrappedMethods.push(methodName)
this[methodName] = function () {
var res = originalMethod.apply(this, arguments)
return injectFunction.apply(
this,
[res].concat(slice(arguments))
)
}
} // ----------------------------------------------------------
// A work around for internal method visiting private member.
// ----------------------------------------------------------
SeriesData.internalField = (function () {
prepareInvertedIndex = function prepareInvertedIndex(data) {
var invertedIndicesMap = data._invertedIndicesMap
each$7(invertedIndicesMap, function (invertedIndices, dim) {
var dimInfo = data._dimInfos[dim] // Currently, only dimensions that has ordinalMeta can create inverted indices.
var ordinalMeta = dimInfo.ordinalMeta
var store = data._store
if (ordinalMeta) {
invertedIndices = invertedIndicesMap[dim] =
new CtorInt32Array(ordinalMeta.categories.length) // The default value of TypedArray is 0. To avoid miss
// mapping to 0, we should set it as INDEX_NOT_FOUND.
for (var i = 0; i < invertedIndices.length; i++) {
invertedIndices[i] = INDEX_NOT_FOUND
}
for (var i = 0; i < store.count(); i++) {
// Only support the case that all values are distinct.
invertedIndices[store.get(dimInfo.storeDimIndex, i)] = i
}
}
})
}
getIdNameFromStore = function getIdNameFromStore(
data,
dimIdx,
idx
) {
return convertOptionIdName(data._getCategory(dimIdx, idx), null)
}
/**
* @see the comment of `List['getId']`.
*/ getId = function getId(data, rawIndex) {
var id = data._idList[rawIndex]
if (id == null && data._idDimIdx != null) {
id = getIdNameFromStore(data, data._idDimIdx, rawIndex)
}
if (id == null) {
id = ID_PREFIX + rawIndex
}
return id
}
normalizeDimensions = function normalizeDimensions(dimensions) {
if (!isArray(dimensions)) {
dimensions = dimensions != null ? [dimensions] : []
}
return dimensions
}
/**
* Data in excludeDimensions is copied, otherwise transfered.
*/ cloneListForMapAndSample = function cloneListForMapAndSample(
original
) {
var list = new SeriesData(
original._schema
? original._schema
: map(original.dimensions, original._getDimInfo, original),
original.hostModel
) // FIXME If needs stackedOn, value may already been stacked
transferProperties(list, original)
return list
}
transferProperties = function transferProperties(target, source) {
each$7(
TRANSFERABLE_PROPERTIES.concat(source.__wrappedMethods || []),
function (propName) {
if (source.hasOwnProperty(propName)) {
target[propName] = source[propName]
}
}
)
target.__wrappedMethods = source.__wrappedMethods
each$7(CLONE_PROPERTIES, function (propName) {
target[propName] = clone$3(source[propName])
})
target._calculationInfo = extend({}, source._calculationInfo)
}
makeIdFromName = function makeIdFromName(data, idx) {
var nameList = data._nameList
var idList = data._idList
var nameDimIdx = data._nameDimIdx
var idDimIdx = data._idDimIdx
var name = nameList[idx]
var id = idList[idx]
if (name == null && nameDimIdx != null) {
nameList[idx] = name = getIdNameFromStore(
data,
nameDimIdx,
idx
)
}
if (id == null && idDimIdx != null) {
idList[idx] = id = getIdNameFromStore(data, idDimIdx, idx)
}
if (id == null && name != null) {
var nameRepeatCount = data._nameRepeatCount
var nmCnt = (nameRepeatCount[name] =
(nameRepeatCount[name] || 0) + 1)
id = name
if (nmCnt > 1) {
id += '__ec__' + nmCnt
}
idList[idx] = id
}
}
})()
return SeriesData
})()
var SeriesData$1 = SeriesData
/**
* This method builds the relationship between:
* + "what the coord sys or series requires (see `coordDimensions`)",
* + "what the user defines (in `encode` and `dimensions`, see `opt.dimensionsDefine` and `opt.encodeDefine`)"
* + "what the data source provids (see `source`)".
*
* Some guess strategy will be adapted if user does not define something.
* If no 'value' dimension specified, the first no-named dimension will be
* named as 'value'.
*
* @return The results are always sorted by `storeDimIndex` asc.
*/ function prepareSeriesDataSchema(source, opt) { // TODO: TYPE completeDimensions type
if (!isSourceInstance(source)) {
source = createSourceFromSeriesDataOption(source)
}
opt = opt || {}
var sysDims = opt.coordDimensions || []
var dimsDef = opt.dimensionsDefine || source.dimensionsDefine || []
var coordDimNameMap = createHashMap()
var resultList = []
var dimCount = getDimCount(
source,
sysDims,
dimsDef,
opt.dimensionsCount
) // Try to ignore unsed dimensions if sharing a high dimension datastore
// 30 is an experience value.
var omitUnusedDimensions =
opt.canOmitUnusedDimensions &&
shouldOmitUnusedDimensions(dimCount)
var isUsingSourceDimensionsDef = dimsDef === source.dimensionsDefine
var dataDimNameMap = isUsingSourceDimensionsDef
? ensureSourceDimNameMap(source)
: createDimNameMap(dimsDef)
var encodeDef = opt.encodeDefine
if (!encodeDef && opt.encodeDefaulter) {
encodeDef = opt.encodeDefaulter(source, dimCount)
}
var encodeDefMap = createHashMap(encodeDef)
var indicesMap = new CtorInt32Array$1(dimCount)
for (var i = 0; i < indicesMap.length; i++) {
indicesMap[i] = -1
}
function getResultItem(dimIdx) {
var idx = indicesMap[dimIdx]
if (idx < 0) {
var dimDefItemRaw = dimsDef[dimIdx]
var dimDefItem = isObject$2(dimDefItemRaw)
? dimDefItemRaw
: { name: dimDefItemRaw }
var resultItem = new SeriesDimensionDefine$1()
var userDimName = dimDefItem.name
if (
userDimName != null &&
dataDimNameMap.get(userDimName) != null
) {
// Only if `series.dimensions` is defined in option
// displayName, will be set, and dimension will be diplayed vertically in
// tooltip by default.
resultItem.name = resultItem.displayName = userDimName
}
dimDefItem.type != null && (resultItem.type = dimDefItem.type)
dimDefItem.displayName != null &&
(resultItem.displayName = dimDefItem.displayName)
var newIdx = resultList.length
indicesMap[dimIdx] = newIdx
resultItem.storeDimIndex = dimIdx
resultList.push(resultItem)
return resultItem
}
return resultList[idx]
}
if (!omitUnusedDimensions) {
for (var i = 0; i < dimCount; i++) {
getResultItem(i)
}
} // Set `coordDim` and `coordDimIndex` by `encodeDefMap` and normalize `encodeDefMap`.
encodeDefMap.each(function (dataDimsRaw, coordDim) {
var dataDims = normalizeToArray(dataDimsRaw).slice() // Note: It is allowed that `dataDims.length` is `0`, e.g., options is
// `{encode: {x: -1, y: 1}}`. Should not filter anything in
// this case.
if (
dataDims.length === 1 &&
!isString(dataDims[0]) &&
dataDims[0] < 0
) {
encodeDefMap.set(coordDim, false)
return
}
var validDataDims = encodeDefMap.set(coordDim, [])
each$7(dataDims, function (resultDimIdxOrName, idx) {
// The input resultDimIdx can be dim name or index.
var resultDimIdx = isString(resultDimIdxOrName)
? dataDimNameMap.get(resultDimIdxOrName)
: resultDimIdxOrName
if (resultDimIdx != null && resultDimIdx < dimCount) {
validDataDims[idx] = resultDimIdx
applyDim(getResultItem(resultDimIdx), coordDim, idx)
}
})
}) // Apply templetes and default order from `sysDims`.
var availDimIdx = 0
each$7(sysDims, function (sysDimItemRaw) {
var coordDim
var sysDimItemDimsDef
var sysDimItemOtherDims
var sysDimItem
if (isString(sysDimItemRaw)) {
coordDim = sysDimItemRaw
sysDimItem = {}
} else {
sysDimItem = sysDimItemRaw
coordDim = sysDimItem.name
var ordinalMeta = sysDimItem.ordinalMeta
sysDimItem.ordinalMeta = null
sysDimItem = extend({}, sysDimItem)
sysDimItem.ordinalMeta = ordinalMeta // `coordDimIndex` should not be set directly.
sysDimItemDimsDef = sysDimItem.dimsDef
sysDimItemOtherDims = sysDimItem.otherDims
sysDimItem.name =
sysDimItem.coordDim =
sysDimItem.coordDimIndex =
sysDimItem.dimsDef =
sysDimItem.otherDims =
null
}
var dataDims = encodeDefMap.get(coordDim) // negative resultDimIdx means no need to mapping.
if (dataDims === false) {
return
}
dataDims = normalizeToArray(dataDims) // dimensions provides default dim sequences.
if (!dataDims.length) {
for (
var i = 0;
i < ((sysDimItemDimsDef && sysDimItemDimsDef.length) || 1);
i++
) {
while (
availDimIdx < dimCount &&
getResultItem(availDimIdx).coordDim != null
) {
availDimIdx++
}
availDimIdx < dimCount && dataDims.push(availDimIdx++)
}
} // Apply templates.
each$7(dataDims, function (resultDimIdx, coordDimIndex) {
var resultItem = getResultItem(resultDimIdx) // Coordinate system has a higher priority on dim type than source.
if (isUsingSourceDimensionsDef && sysDimItem.type != null) {
resultItem.type = sysDimItem.type
}
applyDim(
defaults(resultItem, sysDimItem),
coordDim,
coordDimIndex
)
if (resultItem.name == null && sysDimItemDimsDef) {
var sysDimItemDimsDefItem = sysDimItemDimsDef[coordDimIndex]
!isObject$2(sysDimItemDimsDefItem) &&
(sysDimItemDimsDefItem = { name: sysDimItemDimsDefItem })
resultItem.name = resultItem.displayName =
sysDimItemDimsDefItem.name
resultItem.defaultTooltip =
sysDimItemDimsDefItem.defaultTooltip
} // FIXME refactor, currently only used in case: {otherDims: {tooltip: false}}
sysDimItemOtherDims &&
defaults(resultItem.otherDims, sysDimItemOtherDims)
})
})
function applyDim(resultItem, coordDim, coordDimIndex) {
if (VISUAL_DIMENSIONS.get(coordDim) != null) {
resultItem.otherDims[coordDim] = coordDimIndex
} else {
resultItem.coordDim = coordDim
resultItem.coordDimIndex = coordDimIndex
coordDimNameMap.set(coordDim, true)
}
} // Make sure the first extra dim is 'value'.
var generateCoord = opt.generateCoord
var generateCoordCount = opt.generateCoordCount
var fromZero = generateCoordCount != null
generateCoordCount = generateCoord ? generateCoordCount || 1 : 0
var extra = generateCoord || 'value'
function ifNoNameFillWithCoordName(resultItem) {
if (resultItem.name == null) {
// Duplication will be removed in the next step.
resultItem.name = resultItem.coordDim
}
} // Set dim `name` and other `coordDim` and other props.
if (!omitUnusedDimensions) {
for (
var resultDimIdx = 0;
resultDimIdx < dimCount;
resultDimIdx++
) {
var resultItem = getResultItem(resultDimIdx)
var coordDim = resultItem.coordDim
if (coordDim == null) {
// TODO no need to generate coordDim for isExtraCoord?
resultItem.coordDim = genCoordDimName(
extra,
coordDimNameMap,
fromZero
)
resultItem.coordDimIndex = 0 // Series specified generateCoord is using out.
if (!generateCoord || generateCoordCount <= 0) {
resultItem.isExtraCoord = true
}
generateCoordCount--
}
ifNoNameFillWithCoordName(resultItem)
if (
resultItem.type == null &&
(guessOrdinal(source, resultDimIdx) === BE_ORDINAL.Must || // Consider the case:
// {
// dataset: {source: [
// ['2001', 123],
// ['2002', 456],
// ...
// ['The others', 987],
// ]},
// series: {type: 'pie'}
// }
// The first colum should better be treated as a "ordinal" although it
// might not able to be detected as an "ordinal" by `guessOrdinal`.
(resultItem.isExtraCoord &&
(resultItem.otherDims.itemName != null ||
resultItem.otherDims.seriesName != null)))
) {
resultItem.type = 'ordinal'
}
}
} else {
each$7(resultList, function (resultItem) {
// PENDING: guessOrdinal or let user specify type: 'ordinal' manually?
ifNoNameFillWithCoordName(resultItem)
}) // Sort dimensions: there are some rule that use the last dim as label,
// and for some latter travel process easier.
resultList.sort(function (item0, item1) {
return item0.storeDimIndex - item1.storeDimIndex
})
}
removeDuplication(resultList)
return new SeriesDataSchema({
source: source,
dimensions: resultList,
fullDimensionCount: dimCount,
dimensionOmitted: omitUnusedDimensions
})
}
function removeDuplication(result) {
var duplicationMap = createHashMap()
for (var i = 0; i < result.length; i++) {
var dim = result[i]
var dimOriginalName = dim.name
var count = duplicationMap.get(dimOriginalName) || 0
if (count > 0) {
// Starts from 0.
dim.name = dimOriginalName + (count - 1)
}
count++
duplicationMap.set(dimOriginalName, count)
}
} // ??? TODO
// Originally detect dimCount by data[0]. Should we
// optimize it to only by sysDims and dimensions and encode.
// So only necessary dims will be initialized.
// But
// (1) custom series should be considered. where other dims
// may be visited.
// (2) sometimes user need to calcualte bubble size or use visualMap
// on other dimensions besides coordSys needed.
// So, dims that is not used by system, should be shared in data store?
function getDimCount(source, sysDims, dimsDef, optDimCount) {
// Note that the result dimCount should not small than columns count
// of data, otherwise `dataDimNameMap` checking will be incorrect.
var dimCount = Math.max(
source.dimensionsDetectedCount || 1,
sysDims.length,
dimsDef.length,
optDimCount || 0
)
each$7(sysDims, function (sysDimItem) {
var sysDimItemDimsDef
if (
isObject$2(sysDimItem) &&
(sysDimItemDimsDef = sysDimItem.dimsDef)
) {
dimCount = Math.max(dimCount, sysDimItemDimsDef.length)
}
})
return dimCount
}
function genCoordDimName(name, map, fromZero) {
var mapData = map.data
if (fromZero || mapData.hasOwnProperty(name)) {
var i = 0
while (mapData.hasOwnProperty(name + i)) {
i++
}
name += i
}
map.set(name, true)
return name
}
/**
* @class
* For example:
* {
* coordSysName: 'cartesian2d',
* coordSysDims: ['x', 'y', ...],
* axisMap: HashMap({
* x: xAxisModel,
* y: yAxisModel
* }),
* categoryAxisMap: HashMap({
* x: xAxisModel,
* y: undefined
* }),
* // The index of the first category axis in `coordSysDims`.
* // `null/undefined` means no category axis exists.
* firstCategoryDimIndex: 1,
* // To replace user specified encode.
* }
*/ var CoordSysInfo = /** @class */ (function () {
function CoordSysInfo(coordSysName) {
this.coordSysDims = []
this.axisMap = createHashMap()
this.categoryAxisMap = createHashMap()
this.coordSysName = coordSysName
}
return CoordSysInfo
})()
function getCoordSysInfoBySeries(seriesModel) {
var coordSysName = seriesModel.get('coordinateSystem')
var result = new CoordSysInfo(coordSysName)
var fetch = fetchers[coordSysName]
if (fetch) {
fetch(seriesModel, result, result.axisMap, result.categoryAxisMap)
return result
}
}
var fetchers = {
cartesian2d: function cartesian2d(
seriesModel,
result,
axisMap,
categoryAxisMap
) {
var xAxisModel = seriesModel.getReferringComponents(
'xAxis',
SINGLE_REFERRING
).models[0]
var yAxisModel = seriesModel.getReferringComponents(
'yAxis',
SINGLE_REFERRING
).models[0]
result.coordSysDims = ['x', 'y']
axisMap.set('x', xAxisModel)
axisMap.set('y', yAxisModel)
if (isCategory(xAxisModel)) {
categoryAxisMap.set('x', xAxisModel)
result.firstCategoryDimIndex = 0
}
if (isCategory(yAxisModel)) {
categoryAxisMap.set('y', yAxisModel)
result.firstCategoryDimIndex == null &&
(result.firstCategoryDimIndex = 1)
}
},
singleAxis: function singleAxis(
seriesModel,
result,
axisMap,
categoryAxisMap
) {
var singleAxisModel = seriesModel.getReferringComponents(
'singleAxis',
SINGLE_REFERRING
).models[0]
result.coordSysDims = ['single']
axisMap.set('single', singleAxisModel)
if (isCategory(singleAxisModel)) {
categoryAxisMap.set('single', singleAxisModel)
result.firstCategoryDimIndex = 0
}
},
polar: function polar(
seriesModel,
result,
axisMap,
categoryAxisMap
) {
var polarModel = seriesModel.getReferringComponents(
'polar',
SINGLE_REFERRING
).models[0]
var radiusAxisModel = polarModel.findAxisModel('radiusAxis')
var angleAxisModel = polarModel.findAxisModel('angleAxis')
result.coordSysDims = ['radius', 'angle']
axisMap.set('radius', radiusAxisModel)
axisMap.set('angle', angleAxisModel)
if (isCategory(radiusAxisModel)) {
categoryAxisMap.set('radius', radiusAxisModel)
result.firstCategoryDimIndex = 0
}
if (isCategory(angleAxisModel)) {
categoryAxisMap.set('angle', angleAxisModel)
result.firstCategoryDimIndex == null &&
(result.firstCategoryDimIndex = 1)
}
},
geo: function geo(seriesModel, result, axisMap, categoryAxisMap) {
result.coordSysDims = ['lng', 'lat']
},
parallel: function parallel(
seriesModel,
result,
axisMap,
categoryAxisMap
) {
var ecModel = seriesModel.ecModel
var parallelModel = ecModel.getComponent(
'parallel',
seriesModel.get('parallelIndex')
)
var coordSysDims = (result.coordSysDims =
parallelModel.dimensions.slice())
each$7(
parallelModel.parallelAxisIndex,
function (axisIndex, index) {
var axisModel = ecModel.getComponent(
'parallelAxis',
axisIndex
)
var axisDim = coordSysDims[index]
axisMap.set(axisDim, axisModel)
if (isCategory(axisModel)) {
categoryAxisMap.set(axisDim, axisModel)
if (result.firstCategoryDimIndex == null) {
result.firstCategoryDimIndex = index
}
}
}
)
}
}
function isCategory(axisModel) {
return axisModel.get('type') === 'category'
}
/**
* Note that it is too complicated to support 3d stack by value
* (have to create two-dimension inverted index), so in 3d case
* we just support that stacked by index.
*
* @param seriesModel
* @param dimensionsInput The same as the input of .
* The input will be modified.
* @param opt
* @param opt.stackedCoordDimension Specify a coord dimension if needed.
* @param opt.byIndex=false
* @return calculationInfo
* {
* stackedDimension: string
* stackedByDimension: string
* isStackedByIndex: boolean
* stackedOverDimension: string
* stackResultDimension: string
* }
*/ function enableDataStack(seriesModel, dimensionsInput, opt) {
opt = opt || {}
var byIndex = opt.byIndex
var stackedCoordDimension = opt.stackedCoordDimension
var dimensionDefineList
var schema
var store
if (isLegacyDimensionsInput(dimensionsInput)) {
dimensionDefineList = dimensionsInput
} else {
schema = dimensionsInput.schema
dimensionDefineList = schema.dimensions
store = dimensionsInput.store
} // Compatibal: when `stack` is set as '', do not stack.
var mayStack = !!(seriesModel && seriesModel.get('stack'))
var stackedByDimInfo
var stackedDimInfo
var stackResultDimension
var stackedOverDimension
each$7(dimensionDefineList, function (dimensionInfo, index) {
if (isString(dimensionInfo)) {
dimensionDefineList[index] = dimensionInfo = {
name: dimensionInfo
}
}
if (mayStack && !dimensionInfo.isExtraCoord) {
// Find the first ordinal dimension as the stackedByDimInfo.
if (
!byIndex &&
!stackedByDimInfo &&
dimensionInfo.ordinalMeta
) {
stackedByDimInfo = dimensionInfo
} // Find the first stackable dimension as the stackedDimInfo.
if (
!stackedDimInfo &&
dimensionInfo.type !== 'ordinal' &&
dimensionInfo.type !== 'time' &&
(!stackedCoordDimension ||
stackedCoordDimension === dimensionInfo.coordDim)
) {
stackedDimInfo = dimensionInfo
}
}
})
if (stackedDimInfo && !byIndex && !stackedByDimInfo) {
// Compatible with previous design, value axis (time axis) only stack by index.
// It may make sense if the user provides elaborately constructed data.
byIndex = true
} // Add stack dimension, they can be both calculated by coordinate system in `unionExtent`.
// That put stack logic in List is for using conveniently in echarts extensions, but it
// might not be a good way.
if (stackedDimInfo) {
// Use a weird name that not duplicated with other names.
// Also need to use seriesModel.id as postfix because different
// series may share same data store. The stack dimension needs to be distinguished.
stackResultDimension = '__\0ecstackresult_' + seriesModel.id
stackedOverDimension = '__\0ecstackedover_' + seriesModel.id // Create inverted index to fast query index by value.
if (stackedByDimInfo) {
stackedByDimInfo.createInvertedIndices = true
}
var stackedDimCoordDim_1 = stackedDimInfo.coordDim
var stackedDimType = stackedDimInfo.type
var stackedDimCoordIndex_1 = 0
each$7(dimensionDefineList, function (dimensionInfo) {
if (dimensionInfo.coordDim === stackedDimCoordDim_1) {
stackedDimCoordIndex_1++
}
})
var stackedOverDimensionDefine = {
name: stackResultDimension,
coordDim: stackedDimCoordDim_1,
coordDimIndex: stackedDimCoordIndex_1,
type: stackedDimType,
isExtraCoord: true,
isCalculationCoord: true,
storeDimIndex: dimensionDefineList.length
}
var stackResultDimensionDefine = {
name: stackedOverDimension, // This dimension contains stack base (generally, 0), so do not set it as
// `stackedDimCoordDim` to avoid extent calculation, consider log scale.
coordDim: stackedOverDimension,
coordDimIndex: stackedDimCoordIndex_1 + 1,
type: stackedDimType,
isExtraCoord: true,
isCalculationCoord: true,
storeDimIndex: dimensionDefineList.length + 1
}
if (schema) {
if (store) {
stackedOverDimensionDefine.storeDimIndex =
store.ensureCalculationDimension(
stackedOverDimension,
stackedDimType
)
stackResultDimensionDefine.storeDimIndex =
store.ensureCalculationDimension(
stackResultDimension,
stackedDimType
)
}
schema.appendCalculationDimension(stackedOverDimensionDefine)
schema.appendCalculationDimension(stackResultDimensionDefine)
} else {
dimensionDefineList.push(stackedOverDimensionDefine)
dimensionDefineList.push(stackResultDimensionDefine)
}
}
return {
stackedDimension: stackedDimInfo && stackedDimInfo.name,
stackedByDimension: stackedByDimInfo && stackedByDimInfo.name,
isStackedByIndex: byIndex,
stackedOverDimension: stackedOverDimension,
stackResultDimension: stackResultDimension
}
}
function isLegacyDimensionsInput(dimensionsInput) {
return !isSeriesDataSchema(dimensionsInput.schema)
}
function isDimensionStacked(data, stackedDim) {
// Each single series only maps to one pair of axis. So we do not need to
// check stackByDim, whatever stacked by a dimension or stacked by index.
return (
!!stackedDim &&
stackedDim === data.getCalculationInfo('stackedDimension')
)
}
function getStackedDimension(data, targetDim) {
return isDimensionStacked(data, targetDim)
? data.getCalculationInfo('stackResultDimension')
: targetDim
}
function getCoordSysDimDefs(seriesModel, coordSysInfo) {
var coordSysName = seriesModel.get('coordinateSystem')
var registeredCoordSys = CoordinateSystem.get(coordSysName)
var coordSysDimDefs
if (coordSysInfo && coordSysInfo.coordSysDims) {
coordSysDimDefs = map$1(
coordSysInfo.coordSysDims,
function (dim) {
var dimInfo = { name: dim }
var axisModel = coordSysInfo.axisMap.get(dim)
if (axisModel) {
var axisType = axisModel.get('type')
dimInfo.type = getDimensionTypeByAxis(axisType)
}
return dimInfo
}
)
}
if (!coordSysDimDefs) {
// Get dimensions from registered coordinate system
coordSysDimDefs = (registeredCoordSys &&
(registeredCoordSys.getDimensionsInfo
? registeredCoordSys.getDimensionsInfo()
: registeredCoordSys.dimensions.slice())) || ['x', 'y']
}
return coordSysDimDefs
}
function injectOrdinalMeta(
dimInfoList,
createInvertedIndices,
coordSysInfo
) {
var firstCategoryDimIndex
var hasNameEncode
coordSysInfo &&
each$7(dimInfoList, function (dimInfo, dimIndex) {
var coordDim = dimInfo.coordDim
var categoryAxisModel =
coordSysInfo.categoryAxisMap.get(coordDim)
if (categoryAxisModel) {
if (firstCategoryDimIndex == null) {
firstCategoryDimIndex = dimIndex
}
dimInfo.ordinalMeta = categoryAxisModel.getOrdinalMeta()
if (createInvertedIndices) {
dimInfo.createInvertedIndices = true
}
}
if (dimInfo.otherDims.itemName != null) {
hasNameEncode = true
}
})
if (!hasNameEncode && firstCategoryDimIndex != null) {
dimInfoList[firstCategoryDimIndex].otherDims.itemName = 0
}
return firstCategoryDimIndex
}
/**
* Caution: there are side effects to `sourceManager` in this method.
* Should better only be called in `Series['getInitialData']`.
*/ function createSeriesData(sourceRaw, seriesModel, opt) {
opt = opt || {}
var sourceManager = seriesModel.getSourceManager()
var source
var isOriginalSource = false
if (sourceRaw) {
isOriginalSource = true
source = createSourceFromSeriesDataOption(sourceRaw)
} else {
source = sourceManager.getSource() // Is series.data. not dataset.
isOriginalSource = source.sourceFormat === SOURCE_FORMAT_ORIGINAL
}
var coordSysInfo = getCoordSysInfoBySeries(seriesModel)
var coordSysDimDefs = getCoordSysDimDefs(seriesModel, coordSysInfo)
var useEncodeDefaulter = opt.useEncodeDefaulter
var encodeDefaulter = isFunction(useEncodeDefaulter)
? useEncodeDefaulter
: useEncodeDefaulter
? curry$1(
makeSeriesEncodeForAxisCoordSys,
coordSysDimDefs,
seriesModel
)
: null
var createDimensionOptions = {
coordDimensions: coordSysDimDefs,
generateCoord: opt.generateCoord,
encodeDefine: seriesModel.getEncode(),
encodeDefaulter: encodeDefaulter,
canOmitUnusedDimensions: !isOriginalSource
}
var schema = prepareSeriesDataSchema(source, createDimensionOptions)
var firstCategoryDimIndex = injectOrdinalMeta(
schema.dimensions,
opt.createInvertedIndices,
coordSysInfo
)
var store = !isOriginalSource
? sourceManager.getSharedDataStore(schema)
: null
var stackCalculationInfo = enableDataStack(seriesModel, {
schema: schema,
store: store
})
var data = new SeriesData$1(schema, seriesModel)
data.setCalculationInfo(stackCalculationInfo)
var dimValueGetter =
firstCategoryDimIndex != null && isNeedCompleteOrdinalData(source)
? function (itemOpt, dimName, dataIndex, dimIndex) {
// Use dataIndex as ordinal value in categoryAxis
return dimIndex === firstCategoryDimIndex
? dataIndex
: this.defaultDimValueGetter(
itemOpt,
dimName,
dataIndex,
dimIndex
)
}
: null
data.hasItemOption = false
data.initData(
// Try to reuse the data store in sourceManager if using dataset.
isOriginalSource ? source : store,
null,
dimValueGetter
)
return data
}
function isNeedCompleteOrdinalData(source) {
if (source.sourceFormat === SOURCE_FORMAT_ORIGINAL) {
var sampleItem = firstDataNotNull(source.data || [])
return !isArray(getDataItemValue(sampleItem))
}
}
function firstDataNotNull(arr) {
var i = 0
while (i < arr.length && arr[i] == null) {
i++
}
return arr[i]
}
var Scale = /** @class */ (function () {
function Scale(setting) {
this._setting = setting || {}
this._extent = [Infinity, -Infinity]
}
Scale.prototype.getSetting = function (name) {
return this._setting[name]
}
/**
* Set extent from data
*/ Scale.prototype.unionExtent = function (other) {
var extent = this._extent
other[0] < extent[0] && (extent[0] = other[0])
other[1] > extent[1] && (extent[1] = other[1]) // not setExtent because in log axis it may transformed to power
// this.setExtent(extent[0], extent[1]);
}
/**
* Set extent from data
*/ Scale.prototype.unionExtentFromData = function (data, dim) {
this.unionExtent(data.getApproximateExtent(dim))
}
/**
* Get extent
*
* Extent is always in increase order.
*/ Scale.prototype.getExtent = function () {
return this._extent.slice()
}
/**
* Set extent
*/ Scale.prototype.setExtent = function (start, end) {
var thisExtent = this._extent
if (!isNaN(start)) {
thisExtent[0] = start
}
if (!isNaN(end)) {
thisExtent[1] = end
}
}
/**
* If value is in extent range
*/ Scale.prototype.isInExtentRange = function (value) {
return this._extent[0] <= value && this._extent[1] >= value
}
/**
* When axis extent depends on data and no data exists,
* axis ticks should not be drawn, which is named 'blank'.
*/ Scale.prototype.isBlank = function () {
return this._isBlank
}
/**
* When axis extent depends on data and no data exists,
* axis ticks should not be drawn, which is named 'blank'.
*/ Scale.prototype.setBlank = function (isBlank) {
this._isBlank = isBlank
}
return Scale
})()
enableClassManagement(Scale)
var Scale$1 = Scale
var uidBase = 0
var OrdinalMeta = /** @class */ (function () {
function OrdinalMeta(opt) {
this.categories = opt.categories || []
this._needCollect = opt.needCollect
this._deduplication = opt.deduplication
this.uid = ++uidBase
}
OrdinalMeta.createByAxisModel = function (axisModel) {
var option = axisModel.option
var data = option.data
var categories = data && map$1(data, getName)
return new OrdinalMeta({
categories: categories,
needCollect: !categories, // deduplication is default in axis.
deduplication: option.dedplication !== false
})
}
OrdinalMeta.prototype.getOrdinal = function (category) {
// @ts-ignore
return this._getOrCreateMap().get(category)
}
/**
* @return The ordinal. If not found, return NaN.
*/ OrdinalMeta.prototype.parseAndCollect = function (category) {
var index
var needCollect = this._needCollect // The value of category dim can be the index of the given category set.
// This feature is only supported when !needCollect, because we should
// consider a common case: a value is 2017, which is a number but is
// expected to be tread as a category. This case usually happen in dataset,
// where it happent to be no need of the index feature.
if (!isString(category) && !needCollect) {
return category
} // Optimize for the scenario:
// category is ['2012-01-01', '2012-01-02', ...], where the input
// data has been ensured not duplicate and is large data.
// Notice, if a dataset dimension provide categroies, usually echarts
// should remove duplication except user tell echarts dont do that
// (set axis.deduplication = false), because echarts do not know whether
// the values in the category dimension has duplication (consider the
// parallel-aqi example)
if (needCollect && !this._deduplication) {
index = this.categories.length
this.categories[index] = category
return index
}
var map = this._getOrCreateMap() // @ts-ignore
index = map.get(category)
if (index == null) {
if (needCollect) {
index = this.categories.length
this.categories[index] = category // @ts-ignore
map.set(category, index)
} else {
index = NaN
}
}
return index
} // Consider big data, do not create map until needed.
OrdinalMeta.prototype._getOrCreateMap = function () {
return this._map || (this._map = createHashMap(this.categories))
}
return OrdinalMeta
})()
function getName(obj) {
if (isObject$2(obj) && obj.value != null) {
return obj.value
} else {
return obj + ''
}
}
var OrdinalMeta$1 = OrdinalMeta
function isIntervalOrLogScale(scale) {
return scale.type === 'interval' || scale.type === 'log'
}
/**
* @param extent Both extent[0] and extent[1] should be valid number.
* Should be extent[0] < extent[1].
* @param splitNumber splitNumber should be >= 1.
*/ function intervalScaleNiceTicks(
extent,
splitNumber,
minInterval,
maxInterval
) {
var result = {}
var span = extent[1] - extent[0]
var interval = (result.interval = nice(span / splitNumber, true))
if (minInterval != null && interval < minInterval) {
interval = result.interval = minInterval
}
if (maxInterval != null && interval > maxInterval) {
interval = result.interval = maxInterval
} // Tow more digital for tick.
var precision = (result.intervalPrecision =
getIntervalPrecision(interval)) // Niced extent inside original extent
var niceTickExtent = (result.niceTickExtent = [
round$1(Math.ceil(extent[0] / interval) * interval, precision),
round$1(Math.floor(extent[1] / interval) * interval, precision)
])
fixExtent(niceTickExtent, extent)
return result
}
function increaseInterval(interval) {
var exp10 = Math.pow(10, quantityExponent(interval)) // Increase interval
var f = interval / exp10
if (!f) {
f = 1
} else if (f === 2) {
f = 3
} else if (f === 3) {
f = 5
} else {
// f is 1 or 5
f *= 2
}
return round$1(f * exp10)
}
/**
* @return interval precision
*/ function getIntervalPrecision(interval) {
// Tow more digital for tick.
return getPrecision(interval) + 2
}
function clamp(niceTickExtent, idx, extent) {
niceTickExtent[idx] = Math.max(
Math.min(niceTickExtent[idx], extent[1]),
extent[0]
)
} // In some cases (e.g., splitNumber is 1), niceTickExtent may be out of extent.
function fixExtent(niceTickExtent, extent) {
!isFinite(niceTickExtent[0]) && (niceTickExtent[0] = extent[0])
!isFinite(niceTickExtent[1]) && (niceTickExtent[1] = extent[1])
clamp(niceTickExtent, 0, extent)
clamp(niceTickExtent, 1, extent)
if (niceTickExtent[0] > niceTickExtent[1]) {
niceTickExtent[0] = niceTickExtent[1]
}
}
function contain(val, extent) {
return val >= extent[0] && val <= extent[1]
}
function normalize(val, extent) {
if (extent[1] === extent[0]) {
return 0.5
}
return (val - extent[0]) / (extent[1] - extent[0])
}
function scale(val, extent) {
return val * (extent[1] - extent[0]) + extent[0]
}
var OrdinalScale = /** @class */ (function (_super) {
__extends(OrdinalScale, _super)
function OrdinalScale(setting) {
var _this = _super.call(this, setting) || this
_this.type = 'ordinal'
var ordinalMeta = _this.getSetting('ordinalMeta') // Caution: Should not use instanceof, consider ec-extensions using
// import approach to get OrdinalMeta class.
if (!ordinalMeta) {
ordinalMeta = new OrdinalMeta$1({})
}
if (isArray(ordinalMeta)) {
ordinalMeta = new OrdinalMeta$1({
categories: map$1(ordinalMeta, function (item) {
return isObject$2(item) ? item.value : item
})
})
}
_this._ordinalMeta = ordinalMeta
_this._extent = _this.getSetting('extent') || [
0,
ordinalMeta.categories.length - 1
]
return _this
}
OrdinalScale.prototype.parse = function (val) {
// Caution: Math.round(null) will return `0` rather than `NaN`
if (val == null) {
return NaN
}
return isString(val)
? this._ordinalMeta.getOrdinal(val) // val might be float.
: Math.round(val)
}
OrdinalScale.prototype.contain = function (rank) {
rank = this.parse(rank)
return (
contain(rank, this._extent) &&
this._ordinalMeta.categories[rank] != null
)
}
/**
* Normalize given rank or name to linear [0, 1]
* @param val raw ordinal number.
* @return normalized value in [0, 1].
*/ OrdinalScale.prototype.normalize = function (val) {
val = this._getTickNumber(this.parse(val))
return normalize(val, this._extent)
}
/**
* @param val normalized value in [0, 1].
* @return raw ordinal number.
*/ OrdinalScale.prototype.scale = function (val) {
val = Math.round(scale(val, this._extent))
return this.getRawOrdinalNumber(val)
}
OrdinalScale.prototype.getTicks = function () {
var ticks = []
var extent = this._extent
var rank = extent[0]
while (rank <= extent[1]) {
ticks.push({ value: rank })
rank++
}
return ticks
}
OrdinalScale.prototype.getMinorTicks = function (splitNumber) {
// Not support.
return
}
/**
* @see `Ordinal['_ordinalNumbersByTick']`
*/ OrdinalScale.prototype.setSortInfo = function (info) {
if (info == null) {
this._ordinalNumbersByTick = this._ticksByOrdinalNumber = null
return
}
var infoOrdinalNumbers = info.ordinalNumbers
var ordinalsByTick = (this._ordinalNumbersByTick = [])
var ticksByOrdinal = (this._ticksByOrdinalNumber = []) // Unnecessary support negative tick in `realtimeSort`.
var tickNum = 0
var allCategoryLen = this._ordinalMeta.categories.length
for (
var len = Math.min(allCategoryLen, infoOrdinalNumbers.length);
tickNum < len;
++tickNum
) {
var ordinalNumber = infoOrdinalNumbers[tickNum]
ordinalsByTick[tickNum] = ordinalNumber
ticksByOrdinal[ordinalNumber] = tickNum
} // Handle that `series.data` only covers part of the `axis.category.data`.
var unusedOrdinal = 0
for (; tickNum < allCategoryLen; ++tickNum) {
while (ticksByOrdinal[unusedOrdinal] != null) {
unusedOrdinal++
}
ordinalsByTick.push(unusedOrdinal)
ticksByOrdinal[unusedOrdinal] = tickNum
}
}
OrdinalScale.prototype._getTickNumber = function (ordinal) {
var ticksByOrdinalNumber = this._ticksByOrdinalNumber // also support ordinal out of range of `ordinalMeta.categories.length`,
// where ordinal numbers are used as tick value directly.
return ticksByOrdinalNumber &&
ordinal >= 0 &&
ordinal < ticksByOrdinalNumber.length
? ticksByOrdinalNumber[ordinal]
: ordinal
}
/**
* @usage
* ```js
* const ordinalNumber = ordinalScale.getRawOrdinalNumber(tickVal);
*
* // case0
* const rawOrdinalValue = axisModel.getCategories()[ordinalNumber];
* // case1
* const rawOrdinalValue = this._ordinalMeta.categories[ordinalNumber];
* // case2
* const coord = axis.dataToCoord(ordinalNumber);
* ```
*
* @param {OrdinalNumber} tickNumber index of display
*/ OrdinalScale.prototype.getRawOrdinalNumber = function (
tickNumber
) {
var ordinalNumbersByTick = this._ordinalNumbersByTick // tickNumber may be out of range, e.g., when axis max is larger than `ordinalMeta.categories.length`.,
// where ordinal numbers are used as tick value directly.
return ordinalNumbersByTick &&
tickNumber >= 0 &&
tickNumber < ordinalNumbersByTick.length
? ordinalNumbersByTick[tickNumber]
: tickNumber
}
/**
* Get item on tick
*/ OrdinalScale.prototype.getLabel = function (tick) {
if (!this.isBlank()) {
var ordinalNumber = this.getRawOrdinalNumber(tick.value)
var cateogry = this._ordinalMeta.categories[ordinalNumber] // Note that if no data, ordinalMeta.categories is an empty array.
// Return empty if it's not exist.
return cateogry == null ? '' : cateogry + ''
}
}
OrdinalScale.prototype.count = function () {
return this._extent[1] - this._extent[0] + 1
}
OrdinalScale.prototype.unionExtentFromData = function (data, dim) {
this.unionExtent(data.getApproximateExtent(dim))
}
/**
* @override
* If value is in extent range
*/ OrdinalScale.prototype.isInExtentRange = function (value) {
value = this._getTickNumber(value)
return this._extent[0] <= value && this._extent[1] >= value
}
OrdinalScale.prototype.getOrdinalMeta = function () {
return this._ordinalMeta
}
OrdinalScale.prototype.calcNiceTicks = function () {}
OrdinalScale.prototype.calcNiceExtent = function () {}
OrdinalScale.type = 'ordinal'
return OrdinalScale
})(Scale$1)
Scale$1.registerClass(OrdinalScale)
var OrdinalScale$1 = OrdinalScale
var roundNumber = round$1
var IntervalScale = /** @class */ (function (_super) {
__extends(IntervalScale, _super)
function IntervalScale() {
var _this =
(_super !== null && _super.apply(this, arguments)) || this
_this.type = 'interval' // Step is calculated in adjustExtent.
_this._interval = 0
_this._intervalPrecision = 2
return _this
}
IntervalScale.prototype.parse = function (val) {
return val
}
IntervalScale.prototype.contain = function (val) {
return contain(val, this._extent)
}
IntervalScale.prototype.normalize = function (val) {
return normalize(val, this._extent)
}
IntervalScale.prototype.scale = function (val) {
return scale(val, this._extent)
}
IntervalScale.prototype.setExtent = function (start, end) {
var thisExtent = this._extent // start,end may be a Number like '25',so...
if (!isNaN(start)) {
thisExtent[0] = parseFloat(start)
}
if (!isNaN(end)) {
thisExtent[1] = parseFloat(end)
}
}
IntervalScale.prototype.unionExtent = function (other) {
var extent = this._extent
other[0] < extent[0] && (extent[0] = other[0])
other[1] > extent[1] && (extent[1] = other[1]) // unionExtent may called by it's sub classes
this.setExtent(extent[0], extent[1])
}
IntervalScale.prototype.getInterval = function () {
return this._interval
}
IntervalScale.prototype.setInterval = function (interval) {
this._interval = interval // Dropped auto calculated niceExtent and use user setted extent
// We assume user wan't to set both interval, min, max to get a better result
this._niceExtent = this._extent.slice()
this._intervalPrecision = getIntervalPrecision(interval)
}
/**
* @param expandToNicedExtent Whether expand the ticks to niced extent.
*/ IntervalScale.prototype.getTicks = function (
expandToNicedExtent
) {
var interval = this._interval
var extent = this._extent
var niceTickExtent = this._niceExtent
var intervalPrecision = this._intervalPrecision
var ticks = [] // If interval is 0, return [];
if (!interval) {
return ticks
} // Consider this case: using dataZoom toolbox, zoom and zoom.
var safeLimit = 10000
if (extent[0] < niceTickExtent[0]) {
if (expandToNicedExtent) {
ticks.push({
value: roundNumber(
niceTickExtent[0] - interval,
intervalPrecision
)
})
} else {
ticks.push({ value: extent[0] })
}
}
var tick = niceTickExtent[0]
while (tick <= niceTickExtent[1]) {
ticks.push({ value: tick }) // Avoid rounding error
tick = roundNumber(tick + interval, intervalPrecision)
if (tick === ticks[ticks.length - 1].value) {
// Consider out of safe float point, e.g.,
// -3711126.9907707 + 2e-10 === -3711126.9907707
break
}
if (ticks.length > safeLimit) {
return []
}
} // Consider this case: the last item of ticks is smaller
// than niceTickExtent[1] and niceTickExtent[1] === extent[1].
var lastNiceTick = ticks.length
? ticks[ticks.length - 1].value
: niceTickExtent[1]
if (extent[1] > lastNiceTick) {
if (expandToNicedExtent) {
ticks.push({
value: roundNumber(
lastNiceTick + interval,
intervalPrecision
)
})
} else {
ticks.push({ value: extent[1] })
}
}
return ticks
}
IntervalScale.prototype.getMinorTicks = function (splitNumber) {
var ticks = this.getTicks(true)
var minorTicks = []
var extent = this.getExtent()
for (var i = 1; i < ticks.length; i++) {
var nextTick = ticks[i]
var prevTick = ticks[i - 1]
var count = 0
var minorTicksGroup = []
var interval = nextTick.value - prevTick.value
var minorInterval = interval / splitNumber
while (count < splitNumber - 1) {
var minorTick = roundNumber(
prevTick.value + (count + 1) * minorInterval
) // For the first and last interval. The count may be less than splitNumber.
if (minorTick > extent[0] && minorTick < extent[1]) {
minorTicksGroup.push(minorTick)
}
count++
}
minorTicks.push(minorTicksGroup)
}
return minorTicks
}
/**
* @param opt.precision If 'auto', use nice presision.
* @param opt.pad returns 1.50 but not 1.5 if precision is 2.
*/ IntervalScale.prototype.getLabel = function (data, opt) {
if (data == null) {
return ''
}
var precision = opt && opt.precision
if (precision == null) {
precision = getPrecision(data.value) || 0
} else if (precision === 'auto') {
// Should be more precise then tick.
precision = this._intervalPrecision
} // (1) If `precision` is set, 12.005 should be display as '12.00500'.
// (2) Use roundNumber (toFixed) to avoid scientific notation like '3.5e-7'.
var dataNum = roundNumber(data.value, precision, true)
return addCommas(dataNum)
}
/**
* @param splitNumber By default `5`.
*/ IntervalScale.prototype.calcNiceTicks = function (
splitNumber,
minInterval,
maxInterval
) {
splitNumber = splitNumber || 5
var extent = this._extent
var span = extent[1] - extent[0]
if (!isFinite(span)) {
return
} // User may set axis min 0 and data are all negative
// FIXME If it needs to reverse ?
if (span < 0) {
span = -span
extent.reverse()
}
var result = intervalScaleNiceTicks(
extent,
splitNumber,
minInterval,
maxInterval
)
this._intervalPrecision = result.intervalPrecision
this._interval = result.interval
this._niceExtent = result.niceTickExtent
}
IntervalScale.prototype.calcNiceExtent = function (opt) {
var extent = this._extent // If extent start and end are same, expand them
if (extent[0] === extent[1]) {
if (extent[0] !== 0) {
// Expand extent
var expandSize = extent[0] // In the fowllowing case
// Axis has been fixed max 100
// Plus data are all 100 and axis extent are [100, 100].
// Extend to the both side will cause expanded max is larger than fixed max.
// So only expand to the smaller side.
if (!opt.fixMax) {
extent[1] += expandSize / 2
extent[0] -= expandSize / 2
} else {
extent[0] -= expandSize / 2
}
} else {
extent[1] = 1
}
}
var span = extent[1] - extent[0] // If there are no data and extent are [Infinity, -Infinity]
if (!isFinite(span)) {
extent[0] = 0
extent[1] = 1
}
this.calcNiceTicks(
opt.splitNumber,
opt.minInterval,
opt.maxInterval
) // let extent = this._extent;
var interval = this._interval
if (!opt.fixMin) {
extent[0] = roundNumber(
Math.floor(extent[0] / interval) * interval
)
}
if (!opt.fixMax) {
extent[1] = roundNumber(
Math.ceil(extent[1] / interval) * interval
)
}
}
IntervalScale.prototype.setNiceExtent = function (min, max) {
this._niceExtent = [min, max]
}
IntervalScale.type = 'interval'
return IntervalScale
})(Scale$1)
Scale$1.registerClass(IntervalScale)
var IntervalScale$1 = IntervalScale
/* global Float32Array */ var supportFloat32Array =
typeof Float32Array !== 'undefined'
var Float32ArrayCtor = !supportFloat32Array ? Array : Float32Array
function createFloat32Array(arg) {
if (isArray(arg)) {
// Return self directly if don't support TypedArray.
return supportFloat32Array ? new Float32Array(arg) : arg
} // Else is number
return new Float32ArrayCtor(arg)
}
var STACK_PREFIX = '__ec_stack_'
function getSeriesStackId(seriesModel) {
return (
seriesModel.get('stack') || STACK_PREFIX + seriesModel.seriesIndex
)
}
function getAxisKey(axis) {
return axis.dim + axis.index
}
function prepareLayoutBarSeries(seriesType, ecModel) {
var seriesModels = []
ecModel.eachSeriesByType(seriesType, function (seriesModel) {
// Check series coordinate, do layout for cartesian2d only
if (isOnCartesian(seriesModel)) {
seriesModels.push(seriesModel)
}
})
return seriesModels
}
/**
* Map from (baseAxis.dim + '_' + baseAxis.index) to min gap of two adjacent
* values.
* This works for time axes, value axes, and log axes.
* For a single time axis, return value is in the form like
* {'x_0': [1000000]}.
* The value of 1000000 is in milliseconds.
*/ function getValueAxesMinGaps(barSeries) {
/**
* Map from axis.index to values.
* For a single time axis, axisValues is in the form like
* {'x_0': [1495555200000, 1495641600000, 1495728000000]}.
* Items in axisValues[x], e.g. 1495555200000, are time values of all
* series.
*/ var axisValues = {}
each$7(barSeries, function (seriesModel) {
var cartesian = seriesModel.coordinateSystem
var baseAxis = cartesian.getBaseAxis()
if (baseAxis.type !== 'time' && baseAxis.type !== 'value') {
return
}
var data = seriesModel.getData()
var key = baseAxis.dim + '_' + baseAxis.index
var dimIdx = data.getDimensionIndex(
data.mapDimension(baseAxis.dim)
)
var store = data.getStore()
for (var i = 0, cnt = store.count(); i < cnt; ++i) {
var value = store.get(dimIdx, i)
if (!axisValues[key]) {
// No previous data for the axis
axisValues[key] = [value]
} else {
// No value in previous series
axisValues[key].push(value)
} // Ignore duplicated time values in the same axis
}
})
var axisMinGaps = {}
for (var key in axisValues) {
if (axisValues.hasOwnProperty(key)) {
var valuesInAxis = axisValues[key]
if (valuesInAxis) {
// Sort axis values into ascending order to calculate gaps
valuesInAxis.sort(function (a, b) {
return a - b
})
var min = null
for (var j = 1; j < valuesInAxis.length; ++j) {
var delta = valuesInAxis[j] - valuesInAxis[j - 1]
if (delta > 0) {
// Ignore 0 delta because they are of the same axis value
min = min === null ? delta : Math.min(min, delta)
}
} // Set to null if only have one data
axisMinGaps[key] = min
}
}
}
return axisMinGaps
}
function makeColumnLayout(barSeries) {
var axisMinGaps = getValueAxesMinGaps(barSeries)
var seriesInfoList = []
each$7(barSeries, function (seriesModel) {
var cartesian = seriesModel.coordinateSystem
var baseAxis = cartesian.getBaseAxis()
var axisExtent = baseAxis.getExtent()
var bandWidth
if (baseAxis.type === 'category') {
bandWidth = baseAxis.getBandWidth()
} else if (
baseAxis.type === 'value' ||
baseAxis.type === 'time'
) {
var key = baseAxis.dim + '_' + baseAxis.index
var minGap = axisMinGaps[key]
var extentSpan = Math.abs(axisExtent[1] - axisExtent[0])
var scale = baseAxis.scale.getExtent()
var scaleSpan = Math.abs(scale[1] - scale[0])
bandWidth = minGap
? (extentSpan / scaleSpan) * minGap
: extentSpan // When there is only one data value
} else {
var data = seriesModel.getData()
bandWidth =
Math.abs(axisExtent[1] - axisExtent[0]) / data.count()
}
var barWidth = parsePercent(
seriesModel.get('barWidth'),
bandWidth
)
var barMaxWidth = parsePercent(
seriesModel.get('barMaxWidth'),
bandWidth
)
var barMinWidth = parsePercent(
// barMinWidth by default is 0.5 / 1 in cartesian. Because in value axis,
// the auto-calculated bar width might be less than 0.5 / 1.
seriesModel.get('barMinWidth') ||
(isInLargeMode(seriesModel) ? 0.5 : 1),
bandWidth
)
var barGap = seriesModel.get('barGap')
var barCategoryGap = seriesModel.get('barCategoryGap')
seriesInfoList.push({
bandWidth: bandWidth,
barWidth: barWidth,
barMaxWidth: barMaxWidth,
barMinWidth: barMinWidth,
barGap: barGap,
barCategoryGap: barCategoryGap,
axisKey: getAxisKey(baseAxis),
stackId: getSeriesStackId(seriesModel)
})
})
return doCalBarWidthAndOffset(seriesInfoList)
}
function doCalBarWidthAndOffset(seriesInfoList) {
// Columns info on each category axis. Key is cartesian name
var columnsMap = {}
each$7(seriesInfoList, function (seriesInfo, idx) {
var axisKey = seriesInfo.axisKey
var bandWidth = seriesInfo.bandWidth
var columnsOnAxis = columnsMap[axisKey] || {
bandWidth: bandWidth,
remainedWidth: bandWidth,
autoWidthCount: 0,
categoryGap: null,
gap: '20%',
stacks: {}
}
var stacks = columnsOnAxis.stacks
columnsMap[axisKey] = columnsOnAxis
var stackId = seriesInfo.stackId
if (!stacks[stackId]) {
columnsOnAxis.autoWidthCount++
}
stacks[stackId] = stacks[stackId] || { width: 0, maxWidth: 0 } // Caution: In a single coordinate system, these barGrid attributes
// will be shared by series. Consider that they have default values,
// only the attributes set on the last series will work.
// Do not change this fact unless there will be a break change.
var barWidth = seriesInfo.barWidth
if (barWidth && !stacks[stackId].width) {
// See #6312, do not restrict width.
stacks[stackId].width = barWidth
barWidth = Math.min(columnsOnAxis.remainedWidth, barWidth)
columnsOnAxis.remainedWidth -= barWidth
}
var barMaxWidth = seriesInfo.barMaxWidth
barMaxWidth && (stacks[stackId].maxWidth = barMaxWidth)
var barMinWidth = seriesInfo.barMinWidth
barMinWidth && (stacks[stackId].minWidth = barMinWidth)
var barGap = seriesInfo.barGap
barGap != null && (columnsOnAxis.gap = barGap)
var barCategoryGap = seriesInfo.barCategoryGap
barCategoryGap != null &&
(columnsOnAxis.categoryGap = barCategoryGap)
})
var result = {}
each$7(columnsMap, function (columnsOnAxis, coordSysName) {
result[coordSysName] = {}
var stacks = columnsOnAxis.stacks
var bandWidth = columnsOnAxis.bandWidth
var categoryGapPercent = columnsOnAxis.categoryGap
if (categoryGapPercent == null) {
var columnCount = keys(stacks).length // More columns in one group
// the spaces between group is smaller. Or the column will be too thin.
categoryGapPercent = Math.max(35 - columnCount * 4, 15) + '%'
}
var categoryGap = parsePercent(categoryGapPercent, bandWidth)
var barGapPercent = parsePercent(columnsOnAxis.gap, 1)
var remainedWidth = columnsOnAxis.remainedWidth
var autoWidthCount = columnsOnAxis.autoWidthCount
var autoWidth =
(remainedWidth - categoryGap) /
(autoWidthCount + (autoWidthCount - 1) * barGapPercent)
autoWidth = Math.max(autoWidth, 0) // Find if any auto calculated bar exceeded maxBarWidth
each$7(stacks, function (column) {
var maxWidth = column.maxWidth
var minWidth = column.minWidth
if (!column.width) {
var finalWidth = autoWidth
if (maxWidth && maxWidth < finalWidth) {
finalWidth = Math.min(maxWidth, remainedWidth)
} // `minWidth` has higher priority. `minWidth` decide that wheter the
// bar is able to be visible. So `minWidth` should not be restricted
// by `maxWidth` or `remainedWidth` (which is from `bandWidth`). In
// the extreme cases for `value` axis, bars are allowed to overlap
// with each other if `minWidth` specified.
if (minWidth && minWidth > finalWidth) {
finalWidth = minWidth
}
if (finalWidth !== autoWidth) {
column.width = finalWidth
remainedWidth -= finalWidth + barGapPercent * finalWidth
autoWidthCount--
}
} else {
// `barMinWidth/barMaxWidth` has higher priority than `barWidth`, as
// CSS does. Becuase barWidth can be a percent value, where
// `barMaxWidth` can be used to restrict the final width.
var finalWidth = column.width
if (maxWidth) {
finalWidth = Math.min(finalWidth, maxWidth)
} // `minWidth` has higher priority, as described above
if (minWidth) {
finalWidth = Math.max(finalWidth, minWidth)
}
column.width = finalWidth
remainedWidth -= finalWidth + barGapPercent * finalWidth
autoWidthCount--
}
}) // Recalculate width again
autoWidth =
(remainedWidth - categoryGap) /
(autoWidthCount + (autoWidthCount - 1) * barGapPercent)
autoWidth = Math.max(autoWidth, 0)
var widthSum = 0
var lastColumn
each$7(stacks, function (column, idx) {
if (!column.width) {
column.width = autoWidth
}
lastColumn = column
widthSum += column.width * (1 + barGapPercent)
})
if (lastColumn) {
widthSum -= lastColumn.width * barGapPercent
}
var offset = -widthSum / 2
each$7(stacks, function (column, stackId) {
result[coordSysName][stackId] = result[coordSysName][
stackId
] || {
bandWidth: bandWidth,
offset: offset,
width: column.width
}
offset += column.width * (1 + barGapPercent)
})
})
return result
}
function retrieveColumnLayout(barWidthAndOffset, axis, seriesModel) {
if (barWidthAndOffset && axis) {
var result = barWidthAndOffset[getAxisKey(axis)]
if (result != null && seriesModel != null) {
return result[getSeriesStackId(seriesModel)]
}
return result
}
}
function layout$2(seriesType, ecModel) {
var seriesModels = prepareLayoutBarSeries(seriesType, ecModel)
var barWidthAndOffset = makeColumnLayout(seriesModels)
each$7(seriesModels, function (seriesModel) {
var data = seriesModel.getData()
var cartesian = seriesModel.coordinateSystem
var baseAxis = cartesian.getBaseAxis()
var stackId = getSeriesStackId(seriesModel)
var columnLayoutInfo =
barWidthAndOffset[getAxisKey(baseAxis)][stackId]
var columnOffset = columnLayoutInfo.offset
var columnWidth = columnLayoutInfo.width
data.setLayout({
bandWidth: columnLayoutInfo.bandWidth,
offset: columnOffset,
size: columnWidth
})
})
} // TODO: Do not support stack in large mode yet.
function createProgressiveLayout(seriesType) {
return {
seriesType: seriesType,
plan: createRenderPlanner(),
reset: function reset(seriesModel) {
if (!isOnCartesian(seriesModel)) {
return
}
var data = seriesModel.getData()
var cartesian = seriesModel.coordinateSystem
var baseAxis = cartesian.getBaseAxis()
var valueAxis = cartesian.getOtherAxis(baseAxis)
var valueDimIdx = data.getDimensionIndex(
data.mapDimension(valueAxis.dim)
)
var baseDimIdx = data.getDimensionIndex(
data.mapDimension(baseAxis.dim)
)
var drawBackground = seriesModel.get('showBackground', true)
var valueDim = data.mapDimension(valueAxis.dim)
var stackResultDim = data.getCalculationInfo(
'stackResultDimension'
)
var stacked =
isDimensionStacked(data, valueDim) &&
!!data.getCalculationInfo('stackedOnSeries')
var isValueAxisH = valueAxis.isHorizontal()
var valueAxisStart = getValueAxisStart(baseAxis, valueAxis)
var isLarge = isInLargeMode(seriesModel)
var barMinHeight = seriesModel.get('barMinHeight') || 0
var stackedDimIdx =
stackResultDim && data.getDimensionIndex(stackResultDim) // Layout info.
var columnWidth = data.getLayout('size')
var columnOffset = data.getLayout('offset')
return {
progress: function progress(params, data) {
var count = params.count
var largePoints = isLarge && createFloat32Array(count * 3)
var largeBackgroundPoints =
isLarge && drawBackground && createFloat32Array(count * 3)
var largeDataIndices = isLarge && createFloat32Array(count)
var coordLayout = cartesian.master.getRect()
var bgSize = isValueAxisH
? coordLayout.width
: coordLayout.height
var dataIndex
var store = data.getStore()
var idxOffset = 0
while ((dataIndex = params.next()) != null) {
var value = store.get(
stacked ? stackedDimIdx : valueDimIdx,
dataIndex
)
var baseValue = store.get(baseDimIdx, dataIndex)
var baseCoord = valueAxisStart
var startValue = void 0 // Because of the barMinHeight, we can not use the value in
// stackResultDimension directly.
if (stacked) {
startValue = +value - store.get(valueDimIdx, dataIndex)
}
var x = void 0
var y = void 0
var width = void 0
var height = void 0
if (isValueAxisH) {
var coord = cartesian.dataToPoint([value, baseValue])
if (stacked) {
var startCoord = cartesian.dataToPoint([
startValue,
baseValue
])
baseCoord = startCoord[0]
}
x = baseCoord
y = coord[1] + columnOffset
width = coord[0] - baseCoord
height = columnWidth
if (Math.abs(width) < barMinHeight) {
width = (width < 0 ? -1 : 1) * barMinHeight
}
} else {
var coord = cartesian.dataToPoint([baseValue, value])
if (stacked) {
var startCoord = cartesian.dataToPoint([
baseValue,
startValue
])
baseCoord = startCoord[1]
}
x = coord[0] + columnOffset
y = baseCoord
width = columnWidth
height = coord[1] - baseCoord
if (Math.abs(height) < barMinHeight) {
// Include zero to has a positive bar
height = (height <= 0 ? -1 : 1) * barMinHeight
}
}
if (!isLarge) {
data.setItemLayout(dataIndex, {
x: x,
y: y,
width: width,
height: height
})
} else {
largePoints[idxOffset] = x
largePoints[idxOffset + 1] = y
largePoints[idxOffset + 2] = isValueAxisH
? width
: height
if (largeBackgroundPoints) {
largeBackgroundPoints[idxOffset] = isValueAxisH
? coordLayout.x
: x
largeBackgroundPoints[idxOffset + 1] = isValueAxisH
? y
: coordLayout.y
largeBackgroundPoints[idxOffset + 2] = bgSize
}
largeDataIndices[dataIndex] = dataIndex
}
idxOffset += 3
}
if (isLarge) {
data.setLayout({
largePoints: largePoints,
largeDataIndices: largeDataIndices,
largeBackgroundPoints: largeBackgroundPoints,
valueAxisHorizontal: isValueAxisH
})
}
}
}
}
}
}
function isOnCartesian(seriesModel) {
return (
seriesModel.coordinateSystem &&
seriesModel.coordinateSystem.type === 'cartesian2d'
)
}
function isInLargeMode(seriesModel) {
return (
seriesModel.pipelineContext && seriesModel.pipelineContext.large
)
} // See cases in `test/bar-start.html` and `#7412`, `#8747`.
function getValueAxisStart(baseAxis, valueAxis) {
return valueAxis.toGlobalCoord(
valueAxis.dataToCoord(valueAxis.type === 'log' ? 1 : 0)
)
}
var bisect = function bisect(a, x, lo, hi) {
while (lo < hi) {
var mid = (lo + hi) >>> 1
if (a[mid][1] < x) {
lo = mid + 1
} else {
hi = mid
}
}
return lo
}
var TimeScale = /** @class */ (function (_super) {
__extends(TimeScale, _super)
function TimeScale(settings) {
var _this = _super.call(this, settings) || this
_this.type = 'time'
return _this
}
/**
* Get label is mainly for other components like dataZoom, tooltip.
*/ TimeScale.prototype.getLabel = function (tick) {
var useUTC = this.getSetting('useUTC')
return format(
tick.value,
fullLeveledFormatter[
getDefaultFormatPrecisionOfInterval(
getPrimaryTimeUnit(this._minLevelUnit)
)
] || fullLeveledFormatter.second,
useUTC,
this.getSetting('locale')
)
}
TimeScale.prototype.getFormattedLabel = function (
tick,
idx,
labelFormatter
) {
var isUTC = this.getSetting('useUTC')
var lang = this.getSetting('locale')
return leveledFormat(tick, idx, labelFormatter, lang, isUTC)
}
/**
* @override
*/ TimeScale.prototype.getTicks = function () {
var interval = this._interval
var extent = this._extent
var ticks = [] // If interval is 0, return [];
if (!interval) {
return ticks
}
ticks.push({ value: extent[0], level: 0 })
var useUTC = this.getSetting('useUTC')
var innerTicks = getIntervalTicks(
this._minLevelUnit,
this._approxInterval,
useUTC,
extent
)
ticks = ticks.concat(innerTicks)
ticks.push({ value: extent[1], level: 0 })
return ticks
}
TimeScale.prototype.calcNiceExtent = function (opt) {
var extent = this._extent // If extent start and end are same, expand them
if (extent[0] === extent[1]) {
// Expand extent
extent[0] -= ONE_DAY
extent[1] += ONE_DAY
} // If there are no data and extent are [Infinity, -Infinity]
if (extent[1] === -Infinity && extent[0] === Infinity) {
var d = new Date()
extent[1] = +new Date(
d.getFullYear(),
d.getMonth(),
d.getDate()
)
extent[0] = extent[1] - ONE_DAY
}
this.calcNiceTicks(
opt.splitNumber,
opt.minInterval,
opt.maxInterval
)
}
TimeScale.prototype.calcNiceTicks = function (
approxTickNum,
minInterval,
maxInterval
) {
approxTickNum = approxTickNum || 10
var extent = this._extent
var span = extent[1] - extent[0]
this._approxInterval = span / approxTickNum
if (minInterval != null && this._approxInterval < minInterval) {
this._approxInterval = minInterval
}
if (maxInterval != null && this._approxInterval > maxInterval) {
this._approxInterval = maxInterval
}
var scaleIntervalsLen = scaleIntervals.length
var idx = Math.min(
bisect(
scaleIntervals,
this._approxInterval,
0,
scaleIntervalsLen
),
scaleIntervalsLen - 1
) // Interval that can be used to calculate ticks
this._interval = scaleIntervals[idx][1] // Min level used when picking ticks from top down.
// We check one more level to avoid the ticks are to sparse in some case.
this._minLevelUnit = scaleIntervals[Math.max(idx - 1, 0)][0]
}
TimeScale.prototype.parse = function (val) {
// val might be float.
return isNumber(val) ? val : +parseDate(val)
}
TimeScale.prototype.contain = function (val) {
return contain(this.parse(val), this._extent)
}
TimeScale.prototype.normalize = function (val) {
return normalize(this.parse(val), this._extent)
}
TimeScale.prototype.scale = function (val) {
return scale(val, this._extent)
}
TimeScale.type = 'time'
return TimeScale
})(IntervalScale$1)
/**
* This implementation was originally copied from "d3.js"
*
* with some modifications made for this program.
* See the license statement at the head of this file.
*/ var scaleIntervals = [
// Format interval
['second', ONE_SECOND],
['minute', ONE_MINUTE],
['hour', ONE_HOUR],
['quarter-day', ONE_HOUR * 6],
['half-day', ONE_HOUR * 12],
['day', ONE_DAY * 1.2],
['half-week', ONE_DAY * 3.5],
['week', ONE_DAY * 7],
['month', ONE_DAY * 31],
['quarter', ONE_DAY * 95],
['half-year', ONE_YEAR / 2],
['year', ONE_YEAR] // 1Y
]
function isUnitValueSame(unit, valueA, valueB, isUTC) {
var dateA = parseDate(valueA)
var dateB = parseDate(valueB)
var isSame = function isSame(unit) {
return (
getUnitValue(dateA, unit, isUTC) ===
getUnitValue(dateB, unit, isUTC)
)
}
var isSameYear = function isSameYear() {
return isSame('year')
} // const isSameHalfYear = () => isSameYear() && isSame('half-year');
// const isSameQuater = () => isSameYear() && isSame('quarter');
var isSameMonth = function isSameMonth() {
return isSameYear() && isSame('month')
}
var isSameDay = function isSameDay() {
return isSameMonth() && isSame('day')
} // const isSameHalfDay = () => isSameDay() && isSame('half-day');
var isSameHour = function isSameHour() {
return isSameDay() && isSame('hour')
}
var isSameMinute = function isSameMinute() {
return isSameHour() && isSame('minute')
}
var isSameSecond = function isSameSecond() {
return isSameMinute() && isSame('second')
}
var isSameMilliSecond = function isSameMilliSecond() {
return isSameSecond() && isSame('millisecond')
}
switch (unit) {
case 'year':
return isSameYear()
case 'month':
return isSameMonth()
case 'day':
return isSameDay()
case 'hour':
return isSameHour()
case 'minute':
return isSameMinute()
case 'second':
return isSameSecond()
case 'millisecond':
return isSameMilliSecond()
}
} // const primaryUnitGetters = {
// year: fullYearGetterName(),
// month: monthGetterName(),
// day: dateGetterName(),
// hour: hoursGetterName(),
// minute: minutesGetterName(),
// second: secondsGetterName(),
// millisecond: millisecondsGetterName()
// };
// const primaryUnitUTCGetters = {
// year: fullYearGetterName(true),
// month: monthGetterName(true),
// day: dateGetterName(true),
// hour: hoursGetterName(true),
// minute: minutesGetterName(true),
// second: secondsGetterName(true),
// millisecond: millisecondsGetterName(true)
// };
// function moveTick(date: Date, unitName: TimeUnit, step: number, isUTC: boolean) {
// step = step || 1;
// switch (getPrimaryTimeUnit(unitName)) {
// case 'year':
// date[fullYearSetterName(isUTC)](date[fullYearGetterName(isUTC)]() + step);
// break;
// case 'month':
// date[monthSetterName(isUTC)](date[monthGetterName(isUTC)]() + step);
// break;
// case 'day':
// date[dateSetterName(isUTC)](date[dateGetterName(isUTC)]() + step);
// break;
// case 'hour':
// date[hoursSetterName(isUTC)](date[hoursGetterName(isUTC)]() + step);
// break;
// case 'minute':
// date[minutesSetterName(isUTC)](date[minutesGetterName(isUTC)]() + step);
// break;
// case 'second':
// date[secondsSetterName(isUTC)](date[secondsGetterName(isUTC)]() + step);
// break;
// case 'millisecond':
// date[millisecondsSetterName(isUTC)](date[millisecondsGetterName(isUTC)]() + step);
// break;
// }
// return date.getTime();
// }
// const DATE_INTERVALS = [[8, 7.5], [4, 3.5], [2, 1.5]];
// const MONTH_INTERVALS = [[6, 5.5], [3, 2.5], [2, 1.5]];
// const MINUTES_SECONDS_INTERVALS = [[30, 30], [20, 20], [15, 15], [10, 10], [5, 5], [2, 2]];
function getDateInterval(approxInterval, daysInMonth) {
approxInterval /= ONE_DAY
return approxInterval > 16
? 16 // Math.floor(daysInMonth / 2) + 1 // In this case we only want one tick betwen two month.
: approxInterval > 7.5
? 7 // TODO week 7 or day 8?
: approxInterval > 3.5
? 4
: approxInterval > 1.5
? 2
: 1
}
function getMonthInterval(approxInterval) {
var APPROX_ONE_MONTH = 30 * ONE_DAY
approxInterval /= APPROX_ONE_MONTH
return approxInterval > 6
? 6
: approxInterval > 3
? 3
: approxInterval > 2
? 2
: 1
}
function getHourInterval(approxInterval) {
approxInterval /= ONE_HOUR
return approxInterval > 12
? 12
: approxInterval > 6
? 6
: approxInterval > 3.5
? 4
: approxInterval > 2
? 2
: 1
}
function getMinutesAndSecondsInterval(approxInterval, isMinutes) {
approxInterval /= isMinutes ? ONE_MINUTE : ONE_SECOND
return approxInterval > 30
? 30
: approxInterval > 20
? 20
: approxInterval > 15
? 15
: approxInterval > 10
? 10
: approxInterval > 5
? 5
: approxInterval > 2
? 2
: 1
}
function getMillisecondsInterval(approxInterval) {
return nice(approxInterval, true)
}
function getFirstTimestampOfUnit(date, unitName, isUTC) {
var outDate = new Date(date)
switch (getPrimaryTimeUnit(unitName)) {
case 'year':
case 'month':
outDate[monthSetterName(isUTC)](0)
case 'day':
outDate[dateSetterName(isUTC)](1)
case 'hour':
outDate[hoursSetterName(isUTC)](0)
case 'minute':
outDate[minutesSetterName(isUTC)](0)
case 'second':
outDate[secondsSetterName(isUTC)](0)
outDate[millisecondsSetterName(isUTC)](0)
}
return outDate.getTime()
}
function getIntervalTicks(
bottomUnitName,
approxInterval,
isUTC,
extent
) {
var safeLimit = 10000
var unitNames = timeUnits
var iter = 0
function addTicksInSpan(
interval,
minTimestamp,
maxTimestamp,
getMethodName,
setMethodName,
isDate,
out
) {
var date = new Date(minTimestamp)
var dateTime = minTimestamp
var d = date[getMethodName]() // if (isDate) {
// d -= 1; // Starts with 0; PENDING
// }
while (dateTime < maxTimestamp && dateTime <= extent[1]) {
out.push({ value: dateTime })
d += interval
date[setMethodName](d)
dateTime = date.getTime()
} // This extra tick is for calcuating ticks of next level. Will not been added to the final result
out.push({ value: dateTime, notAdd: true })
}
function addLevelTicks(unitName, lastLevelTicks, levelTicks) {
var newAddedTicks = []
var isFirstLevel = !lastLevelTicks.length
if (
isUnitValueSame(
getPrimaryTimeUnit(unitName),
extent[0],
extent[1],
isUTC
)
) {
return
}
if (isFirstLevel) {
lastLevelTicks = [
{
// TODO Optimize. Not include so may ticks.
value: getFirstTimestampOfUnit(
new Date(extent[0]),
unitName,
isUTC
)
},
{ value: extent[1] }
]
}
for (var i = 0; i < lastLevelTicks.length - 1; i++) {
var startTick = lastLevelTicks[i].value
var endTick = lastLevelTicks[i + 1].value
if (startTick === endTick) {
continue
}
var interval = void 0
var getterName = void 0
var setterName = void 0
var isDate = false
switch (unitName) {
case 'year':
interval = Math.max(
1,
Math.round(approxInterval / ONE_DAY / 365)
)
getterName = fullYearGetterName(isUTC)
setterName = fullYearSetterName(isUTC)
break
case 'half-year':
case 'quarter':
case 'month':
interval = getMonthInterval(approxInterval)
getterName = monthGetterName(isUTC)
setterName = monthSetterName(isUTC)
break
case 'week': // PENDING If week is added. Ignore day.
case 'half-week':
case 'day':
interval = getDateInterval(approxInterval) // Use 32 days and let interval been 16
getterName = dateGetterName(isUTC)
setterName = dateSetterName(isUTC)
isDate = true
break
case 'half-day':
case 'quarter-day':
case 'hour':
interval = getHourInterval(approxInterval)
getterName = hoursGetterName(isUTC)
setterName = hoursSetterName(isUTC)
break
case 'minute':
interval = getMinutesAndSecondsInterval(
approxInterval,
true
)
getterName = minutesGetterName(isUTC)
setterName = minutesSetterName(isUTC)
break
case 'second':
interval = getMinutesAndSecondsInterval(
approxInterval,
false
)
getterName = secondsGetterName(isUTC)
setterName = secondsSetterName(isUTC)
break
case 'millisecond':
interval = getMillisecondsInterval(approxInterval)
getterName = millisecondsGetterName(isUTC)
setterName = millisecondsSetterName(isUTC)
break
}
addTicksInSpan(
interval,
startTick,
endTick,
getterName,
setterName,
isDate,
newAddedTicks
)
if (unitName === 'year' && levelTicks.length > 1 && i === 0) {
// Add nearest years to the left extent.
levelTicks.unshift({ value: levelTicks[0].value - interval })
}
}
for (var i = 0; i < newAddedTicks.length; i++) {
levelTicks.push(newAddedTicks[i])
} // newAddedTicks.length && console.log(unitName, newAddedTicks);
return newAddedTicks
}
var levelsTicks = []
var currentLevelTicks = []
var tickCount = 0
var lastLevelTickCount = 0
for (var i = 0; i < unitNames.length && iter++ < safeLimit; ++i) {
var primaryTimeUnit = getPrimaryTimeUnit(unitNames[i])
if (!isPrimaryTimeUnit(unitNames[i])) {
// TODO
continue
}
addLevelTicks(
unitNames[i],
levelsTicks[levelsTicks.length - 1] || [],
currentLevelTicks
)
var nextPrimaryTimeUnit = unitNames[i + 1]
? getPrimaryTimeUnit(unitNames[i + 1])
: null
if (primaryTimeUnit !== nextPrimaryTimeUnit) {
if (currentLevelTicks.length) {
lastLevelTickCount = tickCount // Remove the duplicate so the tick count can be precisely.
currentLevelTicks.sort(function (a, b) {
return a.value - b.value
})
var levelTicksRemoveDuplicated = []
for (var i_1 = 0; i_1 < currentLevelTicks.length; ++i_1) {
var tickValue = currentLevelTicks[i_1].value
if (
i_1 === 0 ||
currentLevelTicks[i_1 - 1].value !== tickValue
) {
levelTicksRemoveDuplicated.push(currentLevelTicks[i_1])
if (tickValue >= extent[0] && tickValue <= extent[1]) {
tickCount++
}
}
}
var targetTickNum = (extent[1] - extent[0]) / approxInterval // Added too much in this level and not too less in last level
if (
tickCount > targetTickNum * 1.5 &&
lastLevelTickCount > targetTickNum / 1.5
) {
break
} // Only treat primary time unit as one level.
levelsTicks.push(levelTicksRemoveDuplicated)
if (
tickCount > targetTickNum ||
bottomUnitName === unitNames[i]
) {
break
}
} // Reset if next unitName is primary
currentLevelTicks = []
}
}
var levelsTicksInExtent = filter(
map$1(levelsTicks, function (levelTicks) {
return filter(levelTicks, function (tick) {
return (
tick.value >= extent[0] &&
tick.value <= extent[1] &&
!tick.notAdd
)
})
}),
function (levelTicks) {
return levelTicks.length > 0
}
)
var ticks = []
var maxLevel = levelsTicksInExtent.length - 1
for (var i = 0; i < levelsTicksInExtent.length; ++i) {
var levelTicks = levelsTicksInExtent[i]
for (var k = 0; k < levelTicks.length; ++k) {
ticks.push({ value: levelTicks[k].value, level: maxLevel - i })
}
}
ticks.sort(function (a, b) {
return a.value - b.value
}) // Remove duplicates
var result = []
for (var i = 0; i < ticks.length; ++i) {
if (i === 0 || ticks[i].value !== ticks[i - 1].value) {
result.push(ticks[i])
}
}
return result
}
Scale$1.registerClass(TimeScale)
var TimeScale$1 = TimeScale
var scaleProto = Scale$1.prototype // FIXME:TS refactor: not good to call it directly with `this`?
var intervalScaleProto = IntervalScale$1.prototype
var roundingErrorFix = round$1
var mathFloor = Math.floor
var mathCeil = Math.ceil
var mathPow$1 = Math.pow
var mathLog$1 = Math.log
var LogScale = /** @class */ (function (_super) {
__extends(LogScale, _super)
function LogScale() {
var _this =
(_super !== null && _super.apply(this, arguments)) || this
_this.type = 'log'
_this.base = 10
_this._originalScale = new IntervalScale$1() // FIXME:TS actually used by `IntervalScale`
_this._interval = 0
return _this
}
/**
* @param Whether expand the ticks to niced extent.
*/ LogScale.prototype.getTicks = function (expandToNicedExtent) {
var originalScale = this._originalScale
var extent = this._extent
var originalExtent = originalScale.getExtent()
var ticks = intervalScaleProto.getTicks.call(
this,
expandToNicedExtent
)
return map$1(
ticks,
function (tick) {
var val = tick.value
var powVal = round$1(mathPow$1(this.base, val)) // Fix #4158
powVal =
val === extent[0] && this._fixMin
? fixRoundingError(powVal, originalExtent[0])
: powVal
powVal =
val === extent[1] && this._fixMax
? fixRoundingError(powVal, originalExtent[1])
: powVal
return { value: powVal }
},
this
)
}
LogScale.prototype.setExtent = function (start, end) {
var base = this.base
start = mathLog$1(start) / mathLog$1(base)
end = mathLog$1(end) / mathLog$1(base)
intervalScaleProto.setExtent.call(this, start, end)
}
/**
* @return {number} end
*/ LogScale.prototype.getExtent = function () {
var base = this.base
var extent = scaleProto.getExtent.call(this)
extent[0] = mathPow$1(base, extent[0])
extent[1] = mathPow$1(base, extent[1]) // Fix #4158
var originalScale = this._originalScale
var originalExtent = originalScale.getExtent()
this._fixMin &&
(extent[0] = fixRoundingError(extent[0], originalExtent[0]))
this._fixMax &&
(extent[1] = fixRoundingError(extent[1], originalExtent[1]))
return extent
}
LogScale.prototype.unionExtent = function (extent) {
this._originalScale.unionExtent(extent)
var base = this.base
extent[0] = mathLog$1(extent[0]) / mathLog$1(base)
extent[1] = mathLog$1(extent[1]) / mathLog$1(base)
scaleProto.unionExtent.call(this, extent)
}
LogScale.prototype.unionExtentFromData = function (data, dim) {
// TODO
// filter value that <= 0
this.unionExtent(data.getApproximateExtent(dim))
}
/**
* Update interval and extent of intervals for nice ticks
* @param approxTickNum default 10 Given approx tick number
*/ LogScale.prototype.calcNiceTicks = function (approxTickNum) {
approxTickNum = approxTickNum || 10
var extent = this._extent
var span = extent[1] - extent[0]
if (span === Infinity || span <= 0) {
return
}
var interval = quantity(span)
var err = (approxTickNum / span) * interval // Filter ticks to get closer to the desired count.
if (err <= 0.5) {
interval *= 10
} // Interval should be integer
while (
!isNaN(interval) &&
Math.abs(interval) < 1 &&
Math.abs(interval) > 0
) {
interval *= 10
}
var niceExtent = [
round$1(mathCeil(extent[0] / interval) * interval),
round$1(mathFloor(extent[1] / interval) * interval)
]
this._interval = interval
this._niceExtent = niceExtent
}
LogScale.prototype.calcNiceExtent = function (opt) {
intervalScaleProto.calcNiceExtent.call(this, opt)
this._fixMin = opt.fixMin
this._fixMax = opt.fixMax
}
LogScale.prototype.parse = function (val) {
return val
}
LogScale.prototype.contain = function (val) {
val = mathLog$1(val) / mathLog$1(this.base)
return contain(val, this._extent)
}
LogScale.prototype.normalize = function (val) {
val = mathLog$1(val) / mathLog$1(this.base)
return normalize(val, this._extent)
}
LogScale.prototype.scale = function (val) {
val = scale(val, this._extent)
return mathPow$1(this.base, val)
}
LogScale.type = 'log'
return LogScale
})(Scale$1)
var proto = LogScale.prototype
proto.getMinorTicks = intervalScaleProto.getMinorTicks
proto.getLabel = intervalScaleProto.getLabel
function fixRoundingError(val, originalVal) {
return roundingErrorFix(val, getPrecision(originalVal))
}
Scale$1.registerClass(LogScale)
var LogScale$1 = LogScale
var ScaleRawExtentInfo = /** @class */ (function () {
function ScaleRawExtentInfo(
scale,
model, // Usually: data extent from all series on this axis.
originalExtent
) {
this._prepareParams(scale, model, originalExtent)
}
/**
* Parameters depending on ouside (like model, user callback)
* are prepared and fixed here.
*/ ScaleRawExtentInfo.prototype._prepareParams = function (
scale,
model, // Usually: data extent from all series on this axis.
dataExtent
) {
if (dataExtent[1] < dataExtent[0]) {
dataExtent = [NaN, NaN]
}
this._dataMin = dataExtent[0]
this._dataMax = dataExtent[1]
var isOrdinal = (this._isOrdinal = scale.type === 'ordinal')
this._needCrossZero =
scale.type === 'interval' &&
model.getNeedCrossZero &&
model.getNeedCrossZero()
var modelMinRaw = (this._modelMinRaw = model.get('min', true))
if (isFunction(modelMinRaw)) {
// This callback alway provide users the full data extent (before data filtered).
this._modelMinNum = parseAxisModelMinMax(
scale,
modelMinRaw({ min: dataExtent[0], max: dataExtent[1] })
)
} else if (modelMinRaw !== 'dataMin') {
this._modelMinNum = parseAxisModelMinMax(scale, modelMinRaw)
}
var modelMaxRaw = (this._modelMaxRaw = model.get('max', true))
if (isFunction(modelMaxRaw)) {
// This callback alway provide users the full data extent (before data filtered).
this._modelMaxNum = parseAxisModelMinMax(
scale,
modelMaxRaw({ min: dataExtent[0], max: dataExtent[1] })
)
} else if (modelMaxRaw !== 'dataMax') {
this._modelMaxNum = parseAxisModelMinMax(scale, modelMaxRaw)
}
if (isOrdinal) {
// FIXME: there is a flaw here: if there is no "block" data processor like `dataZoom`,
// and progressive rendering is using, here the category result might just only contain
// the processed chunk rather than the entire result.
this._axisDataLen = model.getCategories().length
} else {
var boundaryGap = model.get('boundaryGap')
var boundaryGapArr = isArray(boundaryGap)
? boundaryGap
: [boundaryGap || 0, boundaryGap || 0]
if (
typeof boundaryGapArr[0] === 'boolean' ||
typeof boundaryGapArr[1] === 'boolean'
) {
this._boundaryGapInner = [0, 0]
} else {
this._boundaryGapInner = [
parsePercent$1(boundaryGapArr[0], 1),
parsePercent$1(boundaryGapArr[1], 1)
]
}
}
}
/**
* Calculate extent by prepared parameters.
* This method has no external dependency and can be called duplicatedly,
* getting the same result.
* If parameters changed, should call this method to recalcuate.
*/ ScaleRawExtentInfo.prototype.calculate = function () {
// Notice: When min/max is not set (that is, when there are null/undefined,
// which is the most common case), these cases should be ensured:
// (1) For 'ordinal', show all axis.data.
// (2) For others:
// + `boundaryGap` is applied (if min/max set, boundaryGap is
// disabled).
// + If `needCrossZero`, min/max should be zero, otherwise, min/max should
// be the result that originalExtent enlarged by boundaryGap.
// (3) If no data, it should be ensured that `scale.setBlank` is set.
var isOrdinal = this._isOrdinal
var dataMin = this._dataMin
var dataMax = this._dataMax
var axisDataLen = this._axisDataLen
var boundaryGapInner = this._boundaryGapInner
var span = !isOrdinal
? dataMax - dataMin || Math.abs(dataMin)
: null // Currently if a `'value'` axis model min is specified as 'dataMin'/'dataMax',
// `boundaryGap` will not be used. It's the different from specifying as `null`/`undefined`.
var min =
this._modelMinRaw === 'dataMin' ? dataMin : this._modelMinNum
var max =
this._modelMaxRaw === 'dataMax' ? dataMax : this._modelMaxNum // If `_modelMinNum`/`_modelMaxNum` is `null`/`undefined`, should not be fixed.
var minFixed = min != null
var maxFixed = max != null
if (min == null) {
min = isOrdinal
? axisDataLen
? 0
: NaN
: dataMin - boundaryGapInner[0] * span
}
if (max == null) {
max = isOrdinal
? axisDataLen
? axisDataLen - 1
: NaN
: dataMax + boundaryGapInner[1] * span
}
;(min == null || !isFinite(min)) && (min = NaN)
;(max == null || !isFinite(max)) && (max = NaN)
var isBlank =
eqNaN(min) || eqNaN(max) || (isOrdinal && !axisDataLen) // If data extent modified, need to recalculated to ensure cross zero.
if (this._needCrossZero) {
// Axis is over zero and min is not set
if (min > 0 && max > 0 && !minFixed) {
min = 0 // minFixed = true;
} // Axis is under zero and max is not set
if (min < 0 && max < 0 && !maxFixed) {
max = 0 // maxFixed = true;
} // PENDING:
// When `needCrossZero` and all data is positive/negative, should it be ensured
// that the results processed by boundaryGap are positive/negative?
// If so, here `minFixed`/`maxFixed` need to be set.
}
var determinedMin = this._determinedMin
var determinedMax = this._determinedMax
if (determinedMin != null) {
min = determinedMin
minFixed = true
}
if (determinedMax != null) {
max = determinedMax
maxFixed = true
} // Ensure min/max be finite number or NaN here. (not to be null/undefined)
// `NaN` means min/max axis is blank.
return {
min: min,
max: max,
minFixed: minFixed,
maxFixed: maxFixed,
isBlank: isBlank
}
}
ScaleRawExtentInfo.prototype.modifyDataMinMax = function (
minMaxName,
val
) {
this[DATA_MIN_MAX_ATTR[minMaxName]] = val
}
ScaleRawExtentInfo.prototype.setDeterminedMinMax = function (
minMaxName,
val
) {
var attr = DETERMINED_MIN_MAX_ATTR[minMaxName]
this[attr] = val
}
ScaleRawExtentInfo.prototype.freeze = function () {
// @ts-ignore
this.frozen = true
}
return ScaleRawExtentInfo
})()
var DETERMINED_MIN_MAX_ATTR = {
min: '_determinedMin',
max: '_determinedMax'
}
var DATA_MIN_MAX_ATTR = { min: '_dataMin', max: '_dataMax' }
/**
* Get scale min max and related info only depends on model settings.
* This method can be called after coordinate system created.
* For example, in data processing stage.
*
* Scale extent info probably be required multiple times during a workflow.
* For example:
* (1) `dataZoom` depends it to get the axis extent in "100%" state.
* (2) `processor/extentCalculator` depends it to make sure whether axis extent is specified.
* (3) `coordSys.update` use it to finally decide the scale extent.
* But the callback of `min`/`max` should not be called multiple times.
* The code below should not be implemented repeatedly either.
* So we cache the result in the scale instance, which will be recreated at the begining
* of the workflow (because `scale` instance will be recreated each round of the workflow).
*/ function ensureScaleRawExtentInfo(
scale,
model, // Usually: data extent from all series on this axis.
originalExtent
) {
// Do not permit to recreate.
var rawExtentInfo = scale.rawExtentInfo
if (rawExtentInfo) {
return rawExtentInfo
}
rawExtentInfo = new ScaleRawExtentInfo(scale, model, originalExtent) // @ts-ignore
scale.rawExtentInfo = rawExtentInfo
return rawExtentInfo
}
function parseAxisModelMinMax(scale, minMax) {
return minMax == null
? null
: eqNaN(minMax)
? NaN
: scale.parse(minMax)
}
/**
* Get axis scale extent before niced.
* Item of returned array can only be number (including Infinity and NaN).
*
* Caution:
* Precondition of calling this method:
* The scale extent has been initialized using series data extent via
* `scale.setExtent` or `scale.unionExtentFromData`;
*/ function getScaleExtent(scale, model) {
var scaleType = scale.type
var rawExtentResult = ensureScaleRawExtentInfo(
scale,
model,
scale.getExtent()
).calculate()
scale.setBlank(rawExtentResult.isBlank)
var min = rawExtentResult.min
var max = rawExtentResult.max // If bars are placed on a base axis of type time or interval account for axis boundary overflow and current axis
// is base axis
// FIXME
// (1) Consider support value axis, where below zero and axis `onZero` should be handled properly.
// (2) Refactor the logic with `barGrid`. Is it not need to `makeBarWidthAndOffsetInfo` twice with different extent?
// Should not depend on series type `bar`?
// (3) Fix that might overlap when using dataZoom.
// (4) Consider other chart types using `barGrid`?
// See #6728, #4862, `test/bar-overflow-time-plot.html`
var ecModel = model.ecModel
if (
ecModel &&
scaleType === 'time' /*|| scaleType === 'interval' */
) {
var barSeriesModels = prepareLayoutBarSeries('bar', ecModel)
var isBaseAxisAndHasBarSeries_1 = false
each$7(barSeriesModels, function (seriesModel) {
isBaseAxisAndHasBarSeries_1 =
isBaseAxisAndHasBarSeries_1 ||
seriesModel.getBaseAxis() === model.axis
})
if (isBaseAxisAndHasBarSeries_1) {
// Calculate placement of bars on axis. TODO should be decoupled
// with barLayout
var barWidthAndOffset = makeColumnLayout(barSeriesModels) // Adjust axis min and max to account for overflow
var adjustedScale = adjustScaleForOverflow(
min,
max,
model,
barWidthAndOffset
)
min = adjustedScale.min
max = adjustedScale.max
}
}
return {
extent: [min, max], // "fix" means "fixed", the value should not be
// changed in the subsequent steps.
fixMin: rawExtentResult.minFixed,
fixMax: rawExtentResult.maxFixed
}
}
function adjustScaleForOverflow(
min,
max,
model, // Only support cartesian coord yet.
barWidthAndOffset
) {
// Get Axis Length
var axisExtent = model.axis.getExtent()
var axisLength = axisExtent[1] - axisExtent[0] // Get bars on current base axis and calculate min and max overflow
var barsOnCurrentAxis = retrieveColumnLayout(
barWidthAndOffset,
model.axis
)
if (barsOnCurrentAxis === undefined) {
return { min: min, max: max }
}
var minOverflow = Infinity
each$7(barsOnCurrentAxis, function (item) {
minOverflow = Math.min(item.offset, minOverflow)
})
var maxOverflow = -Infinity
each$7(barsOnCurrentAxis, function (item) {
maxOverflow = Math.max(item.offset + item.width, maxOverflow)
})
minOverflow = Math.abs(minOverflow)
maxOverflow = Math.abs(maxOverflow)
var totalOverFlow = minOverflow + maxOverflow // Calculate required buffer based on old range and overflow
var oldRange = max - min
var oldRangePercentOfNew =
1 - (minOverflow + maxOverflow) / axisLength
var overflowBuffer = oldRange / oldRangePercentOfNew - oldRange
max += overflowBuffer * (maxOverflow / totalOverFlow)
min -= overflowBuffer * (minOverflow / totalOverFlow)
return { min: min, max: max }
} // Precondition of calling this method:
// The scale extent has been initailized using series data extent via
// `scale.setExtent` or `scale.unionExtentFromData`;
function niceScaleExtent(scale, inModel) {
var model = inModel
var extentInfo = getScaleExtent(scale, model)
var extent = extentInfo.extent
var splitNumber = model.get('splitNumber')
if (scale instanceof LogScale$1) {
scale.base = model.get('logBase')
}
var scaleType = scale.type
var interval = model.get('interval')
var isIntervalOrTime =
scaleType === 'interval' || scaleType === 'time'
scale.setExtent(extent[0], extent[1])
scale.calcNiceExtent({
splitNumber: splitNumber,
fixMin: extentInfo.fixMin,
fixMax: extentInfo.fixMax,
minInterval: isIntervalOrTime ? model.get('minInterval') : null,
maxInterval: isIntervalOrTime ? model.get('maxInterval') : null
}) // If some one specified the min, max. And the default calculated interval
// is not good enough. He can specify the interval. It is often appeared
// in angle axis with angle 0 - 360. Interval calculated in interval scale is hard
// to be 60.
// FIXME
if (interval != null) {
scale.setInterval && scale.setInterval(interval)
}
}
/**
* @param axisType Default retrieve from model.type
*/ function createScaleByModel(model, axisType) {
axisType = axisType || model.get('type')
if (axisType) {
switch (
axisType // Buildin scale
) {
case 'category':
return new OrdinalScale$1({
ordinalMeta: model.getOrdinalMeta
? model.getOrdinalMeta()
: model.getCategories(),
extent: [Infinity, -Infinity]
})
case 'time':
return new TimeScale$1({
locale: model.ecModel.getLocaleModel(),
useUTC: model.ecModel.get('useUTC')
})
default:
// case 'value'/'interval', 'log', or others.
return new (Scale$1.getClass(axisType) || IntervalScale$1)()
}
}
}
/**
* Check if the axis cross 0
*/ function ifAxisCrossZero(axis) {
var dataExtent = axis.scale.getExtent()
var min = dataExtent[0]
var max = dataExtent[1]
return !((min > 0 && max > 0) || (min < 0 && max < 0))
}
/**
* @param axis
* @return Label formatter function.
* param: {number} tickValue,
* param: {number} idx, the index in all ticks.
* If category axis, this param is not required.
* return: {string} label string.
*/ function makeLabelFormatter(axis) {
var labelFormatter = axis.getLabelModel().get('formatter')
var categoryTickStart =
axis.type === 'category' ? axis.scale.getExtent()[0] : null
if (axis.scale.type === 'time') {
return (function (tpl) {
return function (tick, idx) {
return axis.scale.getFormattedLabel(tick, idx, tpl)
}
})(labelFormatter)
} else if (isString(labelFormatter)) {
return (function (tpl) {
return function (tick) {
// For category axis, get raw value; for numeric axis,
// get formatted label like '1,333,444'.
var label = axis.scale.getLabel(tick)
var text = tpl.replace('{value}', label != null ? label : '')
return text
}
})(labelFormatter)
} else if (isFunction(labelFormatter)) {
return (function (cb) {
return function (tick, idx) {
// The original intention of `idx` is "the index of the tick in all ticks".
// But the previous implementation of category axis do not consider the
// `axisLabel.interval`, which cause that, for example, the `interval` is
// `1`, then the ticks "name5", "name7", "name9" are displayed, where the
// corresponding `idx` are `0`, `2`, `4`, but not `0`, `1`, `2`. So we keep
// the definition here for back compatibility.
if (categoryTickStart != null) {
idx = tick.value - categoryTickStart
}
return cb(
getAxisRawValue(axis, tick),
idx,
tick.level != null ? { level: tick.level } : null
)
}
})(labelFormatter)
} else {
return function (tick) {
return axis.scale.getLabel(tick)
}
}
}
function getAxisRawValue(axis, tick) {
// In category axis with data zoom, tick is not the original
// index of axis.data. So tick should not be exposed to user
// in category axis.
return axis.type === 'category'
? axis.scale.getLabel(tick)
: tick.value
}
/**
* @param axis
* @return Be null/undefined if no labels.
*/ function estimateLabelUnionRect(axis) {
var axisModel = axis.model
var scale = axis.scale
if (!axisModel.get(['axisLabel', 'show']) || scale.isBlank()) {
return
}
var realNumberScaleTicks
var tickCount
var categoryScaleExtent = scale.getExtent() // Optimize for large category data, avoid call `getTicks()`.
if (scale instanceof OrdinalScale$1) {
tickCount = scale.count()
} else {
realNumberScaleTicks = scale.getTicks()
tickCount = realNumberScaleTicks.length
}
var axisLabelModel = axis.getLabelModel()
var labelFormatter = makeLabelFormatter(axis)
var rect
var step = 1 // Simple optimization for large amount of labels
if (tickCount > 40) {
step = Math.ceil(tickCount / 40)
}
for (var i = 0; i < tickCount; i += step) {
var tick = realNumberScaleTicks
? realNumberScaleTicks[i]
: { value: categoryScaleExtent[0] + i }
var label = labelFormatter(tick, i)
var unrotatedSingleRect = axisLabelModel.getTextRect(label)
var singleRect = rotateTextRect(
unrotatedSingleRect,
axisLabelModel.get('rotate') || 0
)
rect ? rect.union(singleRect) : (rect = singleRect)
}
return rect
}
function rotateTextRect(textRect, rotate) {
var rotateRadians = (rotate * Math.PI) / 180
var beforeWidth = textRect.width
var beforeHeight = textRect.height
var afterWidth =
beforeWidth * Math.abs(Math.cos(rotateRadians)) +
Math.abs(beforeHeight * Math.sin(rotateRadians))
var afterHeight =
beforeWidth * Math.abs(Math.sin(rotateRadians)) +
Math.abs(beforeHeight * Math.cos(rotateRadians))
var rotatedRect = new BoundingRect$1(
textRect.x,
textRect.y,
afterWidth,
afterHeight
)
return rotatedRect
}
/**
* @param model axisLabelModel or axisTickModel
* @return {number|String} Can be null|'auto'|number|function
*/ function getOptionCategoryInterval(model) {
var interval = model.get('interval')
return interval == null ? 'auto' : interval
}
/**
* Set `categoryInterval` as 0 implicitly indicates that
* show all labels reguardless of overlap.
* @param {Object} axis axisModel.axis
*/ function shouldShowAllLabels(axis) {
return (
axis.type === 'category' &&
getOptionCategoryInterval(axis.getLabelModel()) === 0
)
}
function getDataDimensionsOnAxis(data, axisDim) {
// Remove duplicated dat dimensions caused by `getStackedDimension`.
var dataDimMap = {} // Currently `mapDimensionsAll` will contain stack result dimension ('__\0ecstackresult').
// PENDING: is it reasonable? Do we need to remove the original dim from "coord dim" since
// there has been stacked result dim?
each$7(data.mapDimensionsAll(axisDim), function (dataDim) {
// For example, the extent of the original dimension
// is [0.1, 0.5], the extent of the `stackResultDimension`
// is [7, 9], the final extent should NOT include [0.1, 0.5],
// because there is no graphic corresponding to [0.1, 0.5].
// See the case in `test/area-stack.html` `main1`, where area line
// stack needs `yAxis` not start from 0.
dataDimMap[getStackedDimension(data, dataDim)] = true
})
return keys(dataDimMap)
}
function unionAxisExtentFromData(dataExtent, data, axisDim) {
if (data) {
each$7(getDataDimensionsOnAxis(data, axisDim), function (dim) {
var seriesExtent = data.getApproximateExtent(dim)
seriesExtent[0] < dataExtent[0] &&
(dataExtent[0] = seriesExtent[0])
seriesExtent[1] > dataExtent[1] &&
(dataExtent[1] = seriesExtent[1])
})
}
}
/*
* 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.
*/ // eslint-disable-next-line @typescript-eslint/no-unused-vars
var AxisModelCommonMixin = /** @class */ (function () {
function AxisModelCommonMixin() {}
AxisModelCommonMixin.prototype.getNeedCrossZero = function () {
var option = this.option
return !option.scale
}
/**
* Should be implemented by each axis model if necessary.
* @return coordinate system model
*/ AxisModelCommonMixin.prototype.getCoordSysModel = function () {
return
}
return AxisModelCommonMixin
})()
var extensions = []
var extensionRegisters = {
registerPreprocessor: registerPreprocessor,
registerProcessor: registerProcessor,
registerPostInit: registerPostInit,
registerPostUpdate: registerPostUpdate,
registerUpdateLifecycle: registerUpdateLifecycle,
registerAction: registerAction,
registerCoordinateSystem: registerCoordinateSystem,
registerLayout: registerLayout,
registerVisual: registerVisual,
registerTransform: registerTransform,
registerLoading: registerLoading,
registerMap: registerMap,
registerImpl: registerImpl,
PRIORITY: PRIORITY,
ComponentModel: ComponentModel$1,
ComponentView: ComponentView$1,
SeriesModel: SeriesModel$1,
ChartView: ChartView$1, // TODO Use ComponentModel and SeriesModel instead of Constructor
registerComponentModel: function registerComponentModel(
ComponentModelClass
) {
ComponentModel$1.registerClass(ComponentModelClass)
},
registerComponentView: function registerComponentView(
ComponentViewClass
) {
ComponentView$1.registerClass(ComponentViewClass)
},
registerSeriesModel: function registerSeriesModel(
SeriesModelClass
) {
SeriesModel$1.registerClass(SeriesModelClass)
},
registerChartView: function registerChartView(ChartViewClass) {
ChartView$1.registerClass(ChartViewClass)
},
registerSubTypeDefaulter: function registerSubTypeDefaulter(
componentType,
defaulter
) {
ComponentModel$1.registerSubTypeDefaulter(
componentType,
defaulter
)
},
registerPainter: function registerPainter(
painterType,
PainterCtor
) {
_registerPainter(painterType, PainterCtor)
}
}
function use(ext) {
if (isArray(ext)) {
// use([ChartLine, ChartBar]);
each$7(ext, function (singleExt) {
use(singleExt)
})
return
}
if (indexOf(extensions, ext) >= 0) {
return
}
extensions.push(ext)
if (isFunction(ext)) {
ext = { install: ext }
}
ext.install(extensionRegisters)
}
var inner$6 = makeInner()
function createAxisLabels(axis) {
// Only ordinal scale support tick interval
return axis.type === 'category'
? makeCategoryLabels(axis)
: makeRealNumberLabels(axis)
}
/**
* @param {module:echats/coord/Axis} axis
* @param {module:echarts/model/Model} tickModel For example, can be axisTick, splitLine, splitArea.
* @return {Object} {
* ticks: Array.
* tickCategoryInterval: number
* }
*/ function createAxisTicks(axis, tickModel) {
// Only ordinal scale support tick interval
return axis.type === 'category'
? makeCategoryTicks(axis, tickModel)
: {
ticks: map$1(axis.scale.getTicks(), function (tick) {
return tick.value
})
}
}
function makeCategoryLabels(axis) {
var labelModel = axis.getLabelModel()
var result = makeCategoryLabelsActually(axis, labelModel)
return !labelModel.get('show') || axis.scale.isBlank()
? {
labels: [],
labelCategoryInterval: result.labelCategoryInterval
}
: result
}
function makeCategoryLabelsActually(axis, labelModel) {
var labelsCache = getListCache(axis, 'labels')
var optionLabelInterval = getOptionCategoryInterval(labelModel)
var result = listCacheGet(labelsCache, optionLabelInterval)
if (result) {
return result
}
var labels
var numericLabelInterval
if (isFunction(optionLabelInterval)) {
labels = makeLabelsByCustomizedCategoryInterval(
axis,
optionLabelInterval
)
} else {
numericLabelInterval =
optionLabelInterval === 'auto'
? makeAutoCategoryInterval(axis)
: optionLabelInterval
labels = makeLabelsByNumericCategoryInterval(
axis,
numericLabelInterval
)
} // Cache to avoid calling interval function repeatly.
return listCacheSet(labelsCache, optionLabelInterval, {
labels: labels,
labelCategoryInterval: numericLabelInterval
})
}
function makeCategoryTicks(axis, tickModel) {
var ticksCache = getListCache(axis, 'ticks')
var optionTickInterval = getOptionCategoryInterval(tickModel)
var result = listCacheGet(ticksCache, optionTickInterval)
if (result) {
return result
}
var ticks
var tickCategoryInterval // Optimize for the case that large category data and no label displayed,
// we should not return all ticks.
if (!tickModel.get('show') || axis.scale.isBlank()) {
ticks = []
}
if (isFunction(optionTickInterval)) {
ticks = makeLabelsByCustomizedCategoryInterval(
axis,
optionTickInterval,
true
)
} // Always use label interval by default despite label show. Consider this
// scenario, Use multiple grid with the xAxis sync, and only one xAxis shows
// labels. `splitLine` and `axisTick` should be consistent in this case.
else if (optionTickInterval === 'auto') {
var labelsResult = makeCategoryLabelsActually(
axis,
axis.getLabelModel()
)
tickCategoryInterval = labelsResult.labelCategoryInterval
ticks = map$1(labelsResult.labels, function (labelItem) {
return labelItem.tickValue
})
} else {
tickCategoryInterval = optionTickInterval
ticks = makeLabelsByNumericCategoryInterval(
axis,
tickCategoryInterval,
true
)
} // Cache to avoid calling interval function repeatly.
return listCacheSet(ticksCache, optionTickInterval, {
ticks: ticks,
tickCategoryInterval: tickCategoryInterval
})
}
function makeRealNumberLabels(axis) {
var ticks = axis.scale.getTicks()
var labelFormatter = makeLabelFormatter(axis)
return {
labels: map$1(ticks, function (tick, idx) {
return {
level: tick.level,
formattedLabel: labelFormatter(tick, idx),
rawLabel: axis.scale.getLabel(tick),
tickValue: tick.value
}
})
}
}
function getListCache(axis, prop) {
// Because key can be funciton, and cache size always be small, we use array cache.
return inner$6(axis)[prop] || (inner$6(axis)[prop] = [])
}
function listCacheGet(cache, key) {
for (var i = 0; i < cache.length; i++) {
if (cache[i].key === key) {
return cache[i].value
}
}
}
function listCacheSet(cache, key, value) {
cache.push({ key: key, value: value })
return value
}
function makeAutoCategoryInterval(axis) {
var result = inner$6(axis).autoInterval
return result != null
? result
: (inner$6(axis).autoInterval = axis.calculateCategoryInterval())
}
/**
* Calculate interval for category axis ticks and labels.
* To get precise result, at least one of `getRotate` and `isHorizontal`
* should be implemented in axis.
*/ function calculateCategoryInterval(axis) {
var params = fetchAutoCategoryIntervalCalculationParams(axis)
var labelFormatter = makeLabelFormatter(axis)
var rotation =
((params.axisRotate - params.labelRotate) / 180) * Math.PI
var ordinalScale = axis.scale
var ordinalExtent = ordinalScale.getExtent() // Providing this method is for optimization:
// avoid generating a long array by `getTicks`
// in large category data case.
var tickCount = ordinalScale.count()
if (ordinalExtent[1] - ordinalExtent[0] < 1) {
return 0
}
var step = 1 // Simple optimization. Empirical value: tick count should less than 40.
if (tickCount > 40) {
step = Math.max(1, Math.floor(tickCount / 40))
}
var tickValue = ordinalExtent[0]
var unitSpan =
axis.dataToCoord(tickValue + 1) - axis.dataToCoord(tickValue)
var unitW = Math.abs(unitSpan * Math.cos(rotation))
var unitH = Math.abs(unitSpan * Math.sin(rotation))
var maxW = 0
var maxH = 0 // Caution: Performance sensitive for large category data.
// Consider dataZoom, we should make appropriate step to avoid O(n) loop.
for (; tickValue <= ordinalExtent[1]; tickValue += step) {
var width = 0
var height = 0 // Not precise, do not consider align and vertical align
// and each distance from axis line yet.
var rect = getBoundingRect(
labelFormatter({ value: tickValue }),
params.font,
'center',
'top'
) // Magic number
width = rect.width * 1.3
height = rect.height * 1.3 // Min size, void long loop.
maxW = Math.max(maxW, width, 7)
maxH = Math.max(maxH, height, 7)
}
var dw = maxW / unitW
var dh = maxH / unitH // 0/0 is NaN, 1/0 is Infinity.
isNaN(dw) && (dw = Infinity)
isNaN(dh) && (dh = Infinity)
var interval = Math.max(0, Math.floor(Math.min(dw, dh)))
var cache = inner$6(axis.model)
var axisExtent = axis.getExtent()
var lastAutoInterval = cache.lastAutoInterval
var lastTickCount = cache.lastTickCount // Use cache to keep interval stable while moving zoom window,
// otherwise the calculated interval might jitter when the zoom
// window size is close to the interval-changing size.
// For example, if all of the axis labels are `a, b, c, d, e, f, g`.
// The jitter will cause that sometimes the displayed labels are
// `a, d, g` (interval: 2) sometimes `a, c, e`(interval: 1).
if (
lastAutoInterval != null &&
lastTickCount != null &&
Math.abs(lastAutoInterval - interval) <= 1 &&
Math.abs(lastTickCount - tickCount) <= 1 && // Always choose the bigger one, otherwise the critical
// point is not the same when zooming in or zooming out.
lastAutoInterval > interval && // If the axis change is caused by chart resize, the cache should not
// be used. Otherwise some hiden labels might not be shown again.
cache.axisExtent0 === axisExtent[0] &&
cache.axisExtent1 === axisExtent[1]
) {
interval = lastAutoInterval
} // Only update cache if cache not used, otherwise the
// changing of interval is too insensitive.
else {
cache.lastTickCount = tickCount
cache.lastAutoInterval = interval
cache.axisExtent0 = axisExtent[0]
cache.axisExtent1 = axisExtent[1]
}
return interval
}
function fetchAutoCategoryIntervalCalculationParams(axis) {
var labelModel = axis.getLabelModel()
return {
axisRotate: axis.getRotate
? axis.getRotate()
: axis.isHorizontal && !axis.isHorizontal()
? 90
: 0,
labelRotate: labelModel.get('rotate') || 0,
font: labelModel.getFont()
}
}
function makeLabelsByNumericCategoryInterval(
axis,
categoryInterval,
onlyTick
) {
var labelFormatter = makeLabelFormatter(axis)
var ordinalScale = axis.scale
var ordinalExtent = ordinalScale.getExtent()
var labelModel = axis.getLabelModel()
var result = [] // TODO: axisType: ordinalTime, pick the tick from each month/day/year/...
var step = Math.max((categoryInterval || 0) + 1, 1)
var startTick = ordinalExtent[0]
var tickCount = ordinalScale.count() // Calculate start tick based on zero if possible to keep label consistent
// while zooming and moving while interval > 0. Otherwise the selection
// of displayable ticks and symbols probably keep changing.
// 3 is empirical value.
if (startTick !== 0 && step > 1 && tickCount / step > 2) {
startTick = Math.round(Math.ceil(startTick / step) * step)
} // (1) Only add min max label here but leave overlap checking
// to render stage, which also ensure the returned list
// suitable for splitLine and splitArea rendering.
// (2) Scales except category always contain min max label so
// do not need to perform this process.
var showAllLabel = shouldShowAllLabels(axis)
var includeMinLabel = labelModel.get('showMinLabel') || showAllLabel
var includeMaxLabel = labelModel.get('showMaxLabel') || showAllLabel
if (includeMinLabel && startTick !== ordinalExtent[0]) {
addItem(ordinalExtent[0])
} // Optimize: avoid generating large array by `ordinalScale.getTicks()`.
var tickValue = startTick
for (; tickValue <= ordinalExtent[1]; tickValue += step) {
addItem(tickValue)
}
if (includeMaxLabel && tickValue - step !== ordinalExtent[1]) {
addItem(ordinalExtent[1])
}
function addItem(tickValue) {
var tickObj = { value: tickValue }
result.push(
onlyTick
? tickValue
: {
formattedLabel: labelFormatter(tickObj),
rawLabel: ordinalScale.getLabel(tickObj),
tickValue: tickValue
}
)
}
return result
}
function makeLabelsByCustomizedCategoryInterval(
axis,
categoryInterval,
onlyTick
) {
var ordinalScale = axis.scale
var labelFormatter = makeLabelFormatter(axis)
var result = []
each$7(ordinalScale.getTicks(), function (tick) {
var rawLabel = ordinalScale.getLabel(tick)
var tickValue = tick.value
if (categoryInterval(tick.value, rawLabel)) {
result.push(
onlyTick
? tickValue
: {
formattedLabel: labelFormatter(tick),
rawLabel: rawLabel,
tickValue: tickValue
}
)
}
})
return result
}
var NORMALIZED_EXTENT = [0, 1]
/**
* Base class of Axis.
*/ var Axis = /** @class */ (function () {
function Axis(dim, scale, extent) {
this.onBand = false
this.inverse = false
this.dim = dim
this.scale = scale
this._extent = extent || [0, 0]
}
/**
* If axis extent contain given coord
*/ Axis.prototype.contain = function (coord) {
var extent = this._extent
var min = Math.min(extent[0], extent[1])
var max = Math.max(extent[0], extent[1])
return coord >= min && coord <= max
}
/**
* If axis extent contain given data
*/ Axis.prototype.containData = function (data) {
return this.scale.contain(data)
}
/**
* Get coord extent.
*/ Axis.prototype.getExtent = function () {
return this._extent.slice()
}
/**
* Get precision used for formatting
*/ Axis.prototype.getPixelPrecision = function (dataExtent) {
return getPixelPrecision(
dataExtent || this.scale.getExtent(),
this._extent
)
}
/**
* Set coord extent
*/ Axis.prototype.setExtent = function (start, end) {
var extent = this._extent
extent[0] = start
extent[1] = end
}
/**
* Convert data to coord. Data is the rank if it has an ordinal scale
*/ Axis.prototype.dataToCoord = function (data, clamp) {
var extent = this._extent
var scale = this.scale
data = scale.normalize(data)
if (this.onBand && scale.type === 'ordinal') {
extent = extent.slice()
fixExtentWithBands(extent, scale.count())
}
return linearMap(data, NORMALIZED_EXTENT, extent, clamp)
}
/**
* Convert coord to data. Data is the rank if it has an ordinal scale
*/ Axis.prototype.coordToData = function (coord, clamp) {
var extent = this._extent
var scale = this.scale
if (this.onBand && scale.type === 'ordinal') {
extent = extent.slice()
fixExtentWithBands(extent, scale.count())
}
var t = linearMap(coord, extent, NORMALIZED_EXTENT, clamp)
return this.scale.scale(t)
}
/**
* Convert pixel point to data in axis
*/ Axis.prototype.pointToData = function (point, clamp) {
// Should be implemented in derived class if necessary.
return
}
/**
* Different from `zrUtil.map(axis.getTicks(), axis.dataToCoord, axis)`,
* `axis.getTicksCoords` considers `onBand`, which is used by
* `boundaryGap:true` of category axis and splitLine and splitArea.
* @param opt.tickModel default: axis.model.getModel('axisTick')
* @param opt.clamp If `true`, the first and the last
* tick must be at the axis end points. Otherwise, clip ticks
* that outside the axis extent.
*/ Axis.prototype.getTicksCoords = function (opt) {
opt = opt || {}
var tickModel = opt.tickModel || this.getTickModel()
var result = createAxisTicks(this, tickModel)
var ticks = result.ticks
var ticksCoords = map$1(
ticks,
function (tickVal) {
return {
coord: this.dataToCoord(
this.scale.type === 'ordinal'
? this.scale.getRawOrdinalNumber(tickVal)
: tickVal
),
tickValue: tickVal
}
},
this
)
var alignWithLabel = tickModel.get('alignWithLabel')
fixOnBandTicksCoords(this, ticksCoords, alignWithLabel, opt.clamp)
return ticksCoords
}
Axis.prototype.getMinorTicksCoords = function () {
if (this.scale.type === 'ordinal') {
// Category axis doesn't support minor ticks
return []
}
var minorTickModel = this.model.getModel('minorTick')
var splitNumber = minorTickModel.get('splitNumber') // Protection.
if (!(splitNumber > 0 && splitNumber < 100)) {
splitNumber = 5
}
var minorTicks = this.scale.getMinorTicks(splitNumber)
var minorTicksCoords = map$1(
minorTicks,
function (minorTicksGroup) {
return map$1(
minorTicksGroup,
function (minorTick) {
return {
coord: this.dataToCoord(minorTick),
tickValue: minorTick
}
},
this
)
},
this
)
return minorTicksCoords
}
Axis.prototype.getViewLabels = function () {
return createAxisLabels(this).labels
}
Axis.prototype.getLabelModel = function () {
return this.model.getModel('axisLabel')
}
/**
* Notice here we only get the default tick model. For splitLine
* or splitArea, we should pass the splitLineModel or splitAreaModel
* manually when calling `getTicksCoords`.
* In GL, this method may be overrided to:
* `axisModel.getModel('axisTick', grid3DModel.getModel('axisTick'));`
*/ Axis.prototype.getTickModel = function () {
return this.model.getModel('axisTick')
}
/**
* Get width of band
*/ Axis.prototype.getBandWidth = function () {
var axisExtent = this._extent
var dataExtent = this.scale.getExtent()
var len = dataExtent[1] - dataExtent[0] + (this.onBand ? 1 : 0) // Fix #2728, avoid NaN when only one data.
len === 0 && (len = 1)
var size = Math.abs(axisExtent[1] - axisExtent[0])
return Math.abs(size) / len
}
/**
* Only be called in category axis.
* Can be overrided, consider other axes like in 3D.
* @return Auto interval for cateogry axis tick and label
*/ Axis.prototype.calculateCategoryInterval = function () {
return calculateCategoryInterval(this)
}
return Axis
})()
function fixExtentWithBands(extent, nTick) {
var size = extent[1] - extent[0]
var len = nTick
var margin = size / len / 2
extent[0] += margin
extent[1] -= margin
} // If axis has labels [1, 2, 3, 4]. Bands on the axis are
// |---1---|---2---|---3---|---4---|.
// So the displayed ticks and splitLine/splitArea should between
// each data item, otherwise cause misleading (e.g., split tow bars
// of a single data item when there are two bar series).
// Also consider if tickCategoryInterval > 0 and onBand, ticks and
// splitLine/spliteArea should layout appropriately corresponding
// to displayed labels. (So we should not use `getBandWidth` in this
// case).
function fixOnBandTicksCoords(
axis,
ticksCoords,
alignWithLabel,
clamp
) {
var ticksLen = ticksCoords.length
if (!axis.onBand || alignWithLabel || !ticksLen) {
return
}
var axisExtent = axis.getExtent()
var last
var diffSize
if (ticksLen === 1) {
ticksCoords[0].coord = axisExtent[0]
last = ticksCoords[1] = { coord: axisExtent[0] }
} else {
var crossLen =
ticksCoords[ticksLen - 1].tickValue - ticksCoords[0].tickValue
var shift_1 =
(ticksCoords[ticksLen - 1].coord - ticksCoords[0].coord) /
crossLen
each$7(ticksCoords, function (ticksItem) {
ticksItem.coord -= shift_1 / 2
})
var dataExtent = axis.scale.getExtent()
diffSize = 1 + dataExtent[1] - ticksCoords[ticksLen - 1].tickValue
last = {
coord: ticksCoords[ticksLen - 1].coord + shift_1 * diffSize
}
ticksCoords.push(last)
}
var inverse = axisExtent[0] > axisExtent[1] // Handling clamp.
if (littleThan(ticksCoords[0].coord, axisExtent[0])) {
clamp
? (ticksCoords[0].coord = axisExtent[0])
: ticksCoords.shift()
}
if (clamp && littleThan(axisExtent[0], ticksCoords[0].coord)) {
ticksCoords.unshift({ coord: axisExtent[0] })
}
if (littleThan(axisExtent[1], last.coord)) {
clamp ? (last.coord = axisExtent[1]) : ticksCoords.pop()
}
if (clamp && littleThan(last.coord, axisExtent[1])) {
ticksCoords.push({ coord: axisExtent[1] })
}
function littleThan(a, b) {
// Avoid rounding error cause calculated tick coord different with extent.
// It may cause an extra unecessary tick added.
a = round$1(a)
b = round$1(b)
return inverse ? a > b : a < b
}
}
var Axis$1 = Axis
var PI2$1 = Math.PI * 2
var CMD$1 = PathProxy$1.CMD
var DEFAULT_SEARCH_SPACE = ['top', 'right', 'bottom', 'left']
function getCandidateAnchor(pos, distance, rect, outPt, outDir) {
var width = rect.width
var height = rect.height
switch (pos) {
case 'top':
outPt.set(rect.x + width / 2, rect.y - distance)
outDir.set(0, -1)
break
case 'bottom':
outPt.set(rect.x + width / 2, rect.y + height + distance)
outDir.set(0, 1)
break
case 'left':
outPt.set(rect.x - distance, rect.y + height / 2)
outDir.set(-1, 0)
break
case 'right':
outPt.set(rect.x + width + distance, rect.y + height / 2)
outDir.set(1, 0)
break
}
}
function projectPointToArc(
cx,
cy,
r,
startAngle,
endAngle,
anticlockwise,
x,
y,
out
) {
x -= cx
y -= cy
var d = Math.sqrt(x * x + y * y)
x /= d
y /= d // Intersect point.
var ox = x * r + cx
var oy = y * r + cy
if (Math.abs(startAngle - endAngle) % PI2$1 < 1e-4) {
// Is a circle
out[0] = ox
out[1] = oy
return d - r
}
if (anticlockwise) {
var tmp = startAngle
startAngle = normalizeRadian(endAngle)
endAngle = normalizeRadian(tmp)
} else {
startAngle = normalizeRadian(startAngle)
endAngle = normalizeRadian(endAngle)
}
if (startAngle > endAngle) {
endAngle += PI2$1
}
var angle = Math.atan2(y, x)
if (angle < 0) {
angle += PI2$1
}
if (
(angle >= startAngle && angle <= endAngle) ||
(angle + PI2$1 >= startAngle && angle + PI2$1 <= endAngle)
) {
// Project point is on the arc.
out[0] = ox
out[1] = oy
return d - r
}
var x1 = r * Math.cos(startAngle) + cx
var y1 = r * Math.sin(startAngle) + cy
var x2 = r * Math.cos(endAngle) + cx
var y2 = r * Math.sin(endAngle) + cy
var d1 = (x1 - x) * (x1 - x) + (y1 - y) * (y1 - y)
var d2 = (x2 - x) * (x2 - x) + (y2 - y) * (y2 - y)
if (d1 < d2) {
out[0] = x1
out[1] = y1
return Math.sqrt(d1)
} else {
out[0] = x2
out[1] = y2
return Math.sqrt(d2)
}
}
function projectPointToLine(x1, y1, x2, y2, x, y, out, limitToEnds) {
var dx = x - x1
var dy = y - y1
var dx1 = x2 - x1
var dy1 = y2 - y1
var lineLen = Math.sqrt(dx1 * dx1 + dy1 * dy1)
dx1 /= lineLen
dy1 /= lineLen // dot product
var projectedLen = dx * dx1 + dy * dy1
var t = projectedLen / lineLen
if (limitToEnds) {
t = Math.min(Math.max(t, 0), 1)
}
t *= lineLen
var ox = (out[0] = x1 + t * dx1)
var oy = (out[1] = y1 + t * dy1)
return Math.sqrt((ox - x) * (ox - x) + (oy - y) * (oy - y))
}
function projectPointToRect(x1, y1, width, height, x, y, out) {
if (width < 0) {
x1 = x1 + width
width = -width
}
if (height < 0) {
y1 = y1 + height
height = -height
}
var x2 = x1 + width
var y2 = y1 + height
var ox = (out[0] = Math.min(Math.max(x, x1), x2))
var oy = (out[1] = Math.min(Math.max(y, y1), y2))
return Math.sqrt((ox - x) * (ox - x) + (oy - y) * (oy - y))
}
var tmpPt = []
function nearestPointOnRect(pt, rect, out) {
var dist = projectPointToRect(
rect.x,
rect.y,
rect.width,
rect.height,
pt.x,
pt.y,
tmpPt
)
out.set(tmpPt[0], tmpPt[1])
return dist
}
/**
* Calculate min distance corresponding point.
* This method won't evaluate if point is in the path.
*/ function nearestPointOnPath(pt, path, out) {
var xi = 0
var yi = 0
var x0 = 0
var y0 = 0
var x1
var y1
var minDist = Infinity
var data = path.data
var x = pt.x
var y = pt.y
for (var i = 0; i < data.length; ) {
var cmd = data[i++]
if (i === 1) {
xi = data[i]
yi = data[i + 1]
x0 = xi
y0 = yi
}
var d = minDist
switch (cmd) {
case CMD$1.M: // moveTo 命令重新创建一个新的 subpath, 并且更新新的起点
// 在 closePath 的时候使用
x0 = data[i++]
y0 = data[i++]
xi = x0
yi = y0
break
case CMD$1.L:
d = projectPointToLine(
xi,
yi,
data[i],
data[i + 1],
x,
y,
tmpPt,
true
)
xi = data[i++]
yi = data[i++]
break
case CMD$1.C:
d = cubicProjectPoint(
xi,
yi,
data[i++],
data[i++],
data[i++],
data[i++],
data[i],
data[i + 1],
x,
y,
tmpPt
)
xi = data[i++]
yi = data[i++]
break
case CMD$1.Q:
d = quadraticProjectPoint(
xi,
yi,
data[i++],
data[i++],
data[i],
data[i + 1],
x,
y,
tmpPt
)
xi = data[i++]
yi = data[i++]
break
case CMD$1.A: // TODO Arc 判断的开销比较大
var cx = data[i++]
var cy = data[i++]
var rx = data[i++]
var ry = data[i++]
var theta = data[i++]
var dTheta = data[i++] // TODO Arc 旋转
i += 1
var anticlockwise = !!(1 - data[i++])
x1 = Math.cos(theta) * rx + cx
y1 = Math.sin(theta) * ry + cy // 不是直接使用 arc 命令
if (i <= 1) {
// 第一个命令起点还未定义
x0 = x1
y0 = y1
} // zr 使用scale来模拟椭圆, 这里也对x做一定的缩放
var _x = ((x - cx) * ry) / rx + cx
d = projectPointToArc(
cx,
cy,
ry,
theta,
theta + dTheta,
anticlockwise,
_x,
y,
tmpPt
)
xi = Math.cos(theta + dTheta) * rx + cx
yi = Math.sin(theta + dTheta) * ry + cy
break
case CMD$1.R:
x0 = xi = data[i++]
y0 = yi = data[i++]
var width = data[i++]
var height = data[i++]
d = projectPointToRect(x0, y0, width, height, x, y, tmpPt)
break
case CMD$1.Z:
d = projectPointToLine(xi, yi, x0, y0, x, y, tmpPt, true)
xi = x0
yi = y0
break
}
if (d < minDist) {
minDist = d
out.set(tmpPt[0], tmpPt[1])
}
}
return minDist
} // Temporal varible for intermediate usage.
var pt0 = new Point$1()
var pt1 = new Point$1()
var pt2 = new Point$1()
var dir = new Point$1()
var dir2 = new Point$1()
/**
* Calculate a proper guide line based on the label position and graphic element definition
* @param label
* @param labelRect
* @param target
* @param targetRect
*/ function updateLabelLinePoints(target, labelLineModel) {
if (!target) {
return
}
var labelLine = target.getTextGuideLine()
var label = target.getTextContent() // Needs to create text guide in each charts.
if (!(label && labelLine)) {
return
}
var labelGuideConfig = target.textGuideLineConfig || {}
var points = [
[0, 0],
[0, 0],
[0, 0]
]
var searchSpace =
labelGuideConfig.candidates || DEFAULT_SEARCH_SPACE
var labelRect = label.getBoundingRect().clone()
labelRect.applyTransform(label.getComputedTransform())
var minDist = Infinity
var anchorPoint = labelGuideConfig.anchor
var targetTransform = target.getComputedTransform()
var targetInversedTransform =
targetTransform && invert([], targetTransform)
var len = labelLineModel.get('length2') || 0
if (anchorPoint) {
pt2.copy(anchorPoint)
}
for (var i = 0; i < searchSpace.length; i++) {
var candidate = searchSpace[i]
getCandidateAnchor(candidate, 0, labelRect, pt0, dir)
Point$1.scaleAndAdd(pt1, pt0, dir, len) // Transform to target coord space.
pt1.transform(targetInversedTransform) // Note: getBoundingRect will ensure the `path` being created.
var boundingRect = target.getBoundingRect()
var dist = anchorPoint
? anchorPoint.distance(pt1)
: target instanceof Path$1
? nearestPointOnPath(pt1, target.path, pt2)
: nearestPointOnRect(pt1, boundingRect, pt2) // TODO pt2 is in the path
if (dist < minDist) {
minDist = dist // Transform back to global space.
pt1.transform(targetTransform)
pt2.transform(targetTransform)
pt2.toArray(points[0])
pt1.toArray(points[1])
pt0.toArray(points[2])
}
}
limitTurnAngle(points, labelLineModel.get('minTurnAngle'))
labelLine.setShape({ points: points })
} // Temporal variable for the limitTurnAngle function
var tmpArr = []
var tmpProjPoint = new Point$1()
/**
* Reduce the line segment attached to the label to limit the turn angle between two segments.
* @param linePoints
* @param minTurnAngle Radian of minimum turn angle. 0 - 180
*/ function limitTurnAngle(linePoints, minTurnAngle) {
if (!(minTurnAngle <= 180 && minTurnAngle > 0)) {
return
}
minTurnAngle = (minTurnAngle / 180) * Math.PI // The line points can be
// /pt1----pt2 (label)
// /
// pt0/
pt0.fromArray(linePoints[0])
pt1.fromArray(linePoints[1])
pt2.fromArray(linePoints[2])
Point$1.sub(dir, pt0, pt1)
Point$1.sub(dir2, pt2, pt1)
var len1 = dir.len()
var len2 = dir2.len()
if (len1 < 1e-3 || len2 < 1e-3) {
return
}
dir.scale(1 / len1)
dir2.scale(1 / len2)
var angleCos = dir.dot(dir2)
var minTurnAngleCos = Math.cos(minTurnAngle)
if (minTurnAngleCos < angleCos) {
// Smaller than minTurnAngle
// Calculate project point of pt0 on pt1-pt2
var d = projectPointToLine(
pt1.x,
pt1.y,
pt2.x,
pt2.y,
pt0.x,
pt0.y,
tmpArr,
false
)
tmpProjPoint.fromArray(tmpArr) // Calculate new projected length with limited minTurnAngle and get the new connect point
tmpProjPoint.scaleAndAdd(
dir2,
d / Math.tan(Math.PI - minTurnAngle)
) // Limit the new calculated connect point between pt1 and pt2.
var t =
pt2.x !== pt1.x
? (tmpProjPoint.x - pt1.x) / (pt2.x - pt1.x)
: (tmpProjPoint.y - pt1.y) / (pt2.y - pt1.y)
if (isNaN(t)) {
return
}
if (t < 0) {
Point$1.copy(tmpProjPoint, pt1)
} else if (t > 1) {
Point$1.copy(tmpProjPoint, pt2)
}
tmpProjPoint.toArray(linePoints[1])
}
}
/**
* Limit the angle of line and the surface
* @param maxSurfaceAngle Radian of minimum turn angle. 0 - 180. 0 is same direction to normal. 180 is opposite
*/ function limitSurfaceAngle(
linePoints,
surfaceNormal,
maxSurfaceAngle
) {
if (!(maxSurfaceAngle <= 180 && maxSurfaceAngle > 0)) {
return
}
maxSurfaceAngle = (maxSurfaceAngle / 180) * Math.PI
pt0.fromArray(linePoints[0])
pt1.fromArray(linePoints[1])
pt2.fromArray(linePoints[2])
Point$1.sub(dir, pt1, pt0)
Point$1.sub(dir2, pt2, pt1)
var len1 = dir.len()
var len2 = dir2.len()
if (len1 < 1e-3 || len2 < 1e-3) {
return
}
dir.scale(1 / len1)
dir2.scale(1 / len2)
var angleCos = dir.dot(surfaceNormal)
var maxSurfaceAngleCos = Math.cos(maxSurfaceAngle)
if (angleCos < maxSurfaceAngleCos) {
// Calculate project point of pt0 on pt1-pt2
var d = projectPointToLine(
pt1.x,
pt1.y,
pt2.x,
pt2.y,
pt0.x,
pt0.y,
tmpArr,
false
)
tmpProjPoint.fromArray(tmpArr)
var HALF_PI = Math.PI / 2
var angle2 = Math.acos(dir2.dot(surfaceNormal))
var newAngle = HALF_PI + angle2 - maxSurfaceAngle
if (newAngle >= HALF_PI) {
// parallel
Point$1.copy(tmpProjPoint, pt2)
} else {
// Calculate new projected length with limited minTurnAngle and get the new connect point
tmpProjPoint.scaleAndAdd(
dir2,
d / Math.tan(Math.PI / 2 - newAngle)
) // Limit the new calculated connect point between pt1 and pt2.
var t =
pt2.x !== pt1.x
? (tmpProjPoint.x - pt1.x) / (pt2.x - pt1.x)
: (tmpProjPoint.y - pt1.y) / (pt2.y - pt1.y)
if (isNaN(t)) {
return
}
if (t < 0) {
Point$1.copy(tmpProjPoint, pt1)
} else if (t > 1) {
Point$1.copy(tmpProjPoint, pt2)
}
}
tmpProjPoint.toArray(linePoints[1])
}
}
function setLabelLineState(labelLine, ignore, stateName, stateModel) {
var isNormal = stateName === 'normal'
var stateObj = isNormal
? labelLine
: labelLine.ensureState(stateName) // Make sure display.
stateObj.ignore = ignore // Set smooth
var smooth = stateModel.get('smooth')
if (smooth && smooth === true) {
smooth = 0.3
}
stateObj.shape = stateObj.shape || {}
if (smooth > 0) {
stateObj.shape.smooth = smooth
}
var styleObj = stateModel.getModel('lineStyle').getLineStyle()
isNormal
? labelLine.useStyle(styleObj)
: (stateObj.style = styleObj)
}
function buildLabelLinePath(path, shape) {
var smooth = shape.smooth
var points = shape.points
if (!points) {
return
}
path.moveTo(points[0][0], points[0][1])
if (smooth > 0 && points.length >= 3) {
var len1 = dist$1(points[0], points[1])
var len2 = dist$1(points[1], points[2])
if (!len1 || !len2) {
path.lineTo(points[1][0], points[1][1])
path.lineTo(points[2][0], points[2][1])
return
}
var moveLen = Math.min(len1, len2) * smooth
var midPoint0 = lerp$1([], points[1], points[0], moveLen / len1)
var midPoint2 = lerp$1([], points[1], points[2], moveLen / len2)
var midPoint1 = lerp$1([], midPoint0, midPoint2, 0.5)
path.bezierCurveTo(
midPoint0[0],
midPoint0[1],
midPoint0[0],
midPoint0[1],
midPoint1[0],
midPoint1[1]
)
path.bezierCurveTo(
midPoint2[0],
midPoint2[1],
midPoint2[0],
midPoint2[1],
points[2][0],
points[2][1]
)
} else {
for (var i = 1; i < points.length; i++) {
path.lineTo(points[i][0], points[i][1])
}
}
}
/**
* Create a label line if necessary and set it's style.
*/ function setLabelLineStyle(targetEl, statesModels, defaultStyle) {
var labelLine = targetEl.getTextGuideLine()
var label = targetEl.getTextContent()
if (!label) {
// Not show label line if there is no label.
if (labelLine) {
targetEl.removeTextGuideLine()
}
return
}
var normalModel = statesModels.normal
var showNormal = normalModel.get('show')
var labelIgnoreNormal = label.ignore
for (var i = 0; i < DISPLAY_STATES.length; i++) {
var stateName = DISPLAY_STATES[i]
var stateModel = statesModels[stateName]
var isNormal = stateName === 'normal'
if (stateModel) {
var stateShow = stateModel.get('show')
var isLabelIgnored = isNormal
? labelIgnoreNormal
: retrieve2(
label.states[stateName] && label.states[stateName].ignore,
labelIgnoreNormal
)
if (
isLabelIgnored || // Not show when label is not shown in this state.
!retrieve2(stateShow, showNormal) // Use normal state by default if not set.
) {
var stateObj = isNormal
? labelLine
: labelLine && labelLine.states[stateName]
if (stateObj) {
stateObj.ignore = true
}
continue
} // Create labelLine if not exists
if (!labelLine) {
labelLine = new Polyline$1()
targetEl.setTextGuideLine(labelLine) // Reset state of normal because it's new created.
// NOTE: NORMAL should always been the first!
if (!isNormal && (labelIgnoreNormal || !showNormal)) {
setLabelLineState(
labelLine,
true,
'normal',
statesModels.normal
)
} // Use same state proxy.
if (targetEl.stateProxy) {
labelLine.stateProxy = targetEl.stateProxy
}
}
setLabelLineState(labelLine, false, stateName, stateModel)
}
}
if (labelLine) {
defaults(labelLine.style, defaultStyle) // Not fill.
labelLine.style.fill = null
var showAbove = normalModel.get('showAbove')
var labelLineConfig = (targetEl.textGuideLineConfig =
targetEl.textGuideLineConfig || {})
labelLineConfig.showAbove = showAbove || false // Custom the buildPath.
labelLine.buildPath = buildLabelLinePath
}
}
function getLabelLineStatesModels(itemModel, labelLineName) {
labelLineName = labelLineName || 'labelLine'
var statesModels = { normal: itemModel.getModel(labelLineName) }
for (var i = 0; i < SPECIAL_STATES.length; i++) {
var stateName = SPECIAL_STATES[i]
statesModels[stateName] = itemModel.getModel([
stateName,
labelLineName
])
}
return statesModels
}
function prepareLayoutList(input) {
var list = []
for (var i = 0; i < input.length; i++) {
var rawItem = input[i]
if (rawItem.defaultAttr.ignore) {
continue
}
var label = rawItem.label
var transform = label.getComputedTransform() // NOTE: Get bounding rect after getComputedTransform, or label may not been updated by the host el.
var localRect = label.getBoundingRect()
var isAxisAligned =
!transform || (transform[1] < 1e-5 && transform[2] < 1e-5)
var minMargin = label.style.margin || 0
var globalRect = localRect.clone()
globalRect.applyTransform(transform)
globalRect.x -= minMargin / 2
globalRect.y -= minMargin / 2
globalRect.width += minMargin
globalRect.height += minMargin
var obb = isAxisAligned
? new OrientedBoundingRect$1(localRect, transform)
: null
list.push({
label: label,
labelLine: rawItem.labelLine,
rect: globalRect,
localRect: localRect,
obb: obb,
priority: rawItem.priority,
defaultAttr: rawItem.defaultAttr,
layoutOption: rawItem.computedLayoutOption,
axisAligned: isAxisAligned,
transform: transform
})
}
return list
}
function shiftLayout(
list,
xyDim,
sizeDim,
minBound,
maxBound,
balanceShift
) {
var len = list.length
if (len < 2) {
return
}
list.sort(function (a, b) {
return a.rect[xyDim] - b.rect[xyDim]
})
var lastPos = 0
var delta
var adjusted = false
var totalShifts = 0
for (var i = 0; i < len; i++) {
var item = list[i]
var rect = item.rect
delta = rect[xyDim] - lastPos
if (delta < 0) {
// shiftForward(i, len, -delta);
rect[xyDim] -= delta
item.label[xyDim] -= delta
adjusted = true
}
var shift = Math.max(-delta, 0)
totalShifts += shift
lastPos = rect[xyDim] + rect[sizeDim]
}
if (totalShifts > 0 && balanceShift) {
// Shift back to make the distribution more equally.
shiftList(-totalShifts / len, 0, len)
} // TODO bleedMargin?
var first = list[0]
var last = list[len - 1]
var minGap
var maxGap
updateMinMaxGap() // If ends exceed two bounds, squeeze at most 80%, then take the gap of two bounds.
minGap < 0 && squeezeGaps(-minGap, 0.8)
maxGap < 0 && squeezeGaps(maxGap, 0.8)
updateMinMaxGap()
takeBoundsGap(minGap, maxGap, 1)
takeBoundsGap(maxGap, minGap, -1) // Handle bailout when there is not enough space.
updateMinMaxGap()
if (minGap < 0) {
squeezeWhenBailout(-minGap)
}
if (maxGap < 0) {
squeezeWhenBailout(maxGap)
}
function updateMinMaxGap() {
minGap = first.rect[xyDim] - minBound
maxGap = maxBound - last.rect[xyDim] - last.rect[sizeDim]
}
function takeBoundsGap(gapThisBound, gapOtherBound, moveDir) {
if (gapThisBound < 0) {
// Move from other gap if can.
var moveFromMaxGap = Math.min(gapOtherBound, -gapThisBound)
if (moveFromMaxGap > 0) {
shiftList(moveFromMaxGap * moveDir, 0, len)
var remained = moveFromMaxGap + gapThisBound
if (remained < 0) {
squeezeGaps(-remained * moveDir, 1)
}
} else {
squeezeGaps(-gapThisBound * moveDir, 1)
}
}
}
function shiftList(delta, start, end) {
if (delta !== 0) {
adjusted = true
}
for (var i = start; i < end; i++) {
var item = list[i]
var rect = item.rect
rect[xyDim] += delta
item.label[xyDim] += delta
}
} // Squeeze gaps if the labels exceed margin.
function squeezeGaps(delta, maxSqeezePercent) {
var gaps = []
var totalGaps = 0
for (var i = 1; i < len; i++) {
var prevItemRect = list[i - 1].rect
var gap = Math.max(
list[i].rect[xyDim] -
prevItemRect[xyDim] -
prevItemRect[sizeDim],
0
)
gaps.push(gap)
totalGaps += gap
}
if (!totalGaps) {
return
}
var squeezePercent = Math.min(
Math.abs(delta) / totalGaps,
maxSqeezePercent
)
if (delta > 0) {
for (var i = 0; i < len - 1; i++) {
// Distribute the shift delta to all gaps.
var movement = gaps[i] * squeezePercent // Forward
shiftList(movement, 0, i + 1)
}
} else {
// Backward
for (var i = len - 1; i > 0; i--) {
// Distribute the shift delta to all gaps.
var movement = gaps[i - 1] * squeezePercent
shiftList(-movement, i, len)
}
}
}
/**
* Squeeze to allow overlap if there is no more space available.
* Let other overlapping strategy like hideOverlap do the job instead of keep exceeding the bounds.
*/ function squeezeWhenBailout(delta) {
var dir = delta < 0 ? -1 : 1
delta = Math.abs(delta)
var moveForEachLabel = Math.ceil(delta / (len - 1))
for (var i = 0; i < len - 1; i++) {
if (dir > 0) {
// Forward
shiftList(moveForEachLabel, 0, i + 1)
} else {
// Backward
shiftList(-moveForEachLabel, len - i - 1, len)
}
delta -= moveForEachLabel
if (delta <= 0) {
return
}
}
}
return adjusted
}
/**
* Adjust labels on x direction to avoid overlap.
*/ function shiftLayoutOnX(
list,
leftBound,
rightBound, // If average the shifts on all labels and add them to 0
// TODO: Not sure if should enable it.
// Pros: The angle of lines will distribute more equally
// Cons: In some layout. It may not what user wanted. like in pie. the label of last sector is usually changed unexpectedly.
balanceShift
) {
return shiftLayout(
list,
'x',
'width',
leftBound,
rightBound,
balanceShift
)
}
/**
* Adjust labels on y direction to avoid overlap.
*/ function shiftLayoutOnY(
list,
topBound,
bottomBound, // If average the shifts on all labels and add them to 0
balanceShift
) {
return shiftLayout(
list,
'y',
'height',
topBound,
bottomBound,
balanceShift
)
}
function hideOverlap(labelList) {
var displayedLabels = [] // TODO, render overflow visible first, put in the displayedLabels.
labelList.sort(function (a, b) {
return b.priority - a.priority
})
var globalRect = new BoundingRect$1(0, 0, 0, 0)
function hideEl(el) {
if (!el.ignore) {
// Show on emphasis.
var emphasisState = el.ensureState('emphasis')
if (emphasisState.ignore == null) {
emphasisState.ignore = false
}
}
el.ignore = true
}
for (var i = 0; i < labelList.length; i++) {
var labelItem = labelList[i]
var isAxisAligned = labelItem.axisAligned
var localRect = labelItem.localRect
var transform = labelItem.transform
var label = labelItem.label
var labelLine = labelItem.labelLine
globalRect.copy(labelItem.rect) // Add a threshold because layout may be aligned precisely.
globalRect.width -= 0.1
globalRect.height -= 0.1
globalRect.x += 0.05
globalRect.y += 0.05
var obb = labelItem.obb
var overlapped = false
for (var j = 0; j < displayedLabels.length; j++) {
var existsTextCfg = displayedLabels[j] // Fast rejection.
if (!globalRect.intersect(existsTextCfg.rect)) {
continue
}
if (isAxisAligned && existsTextCfg.axisAligned) {
// Is overlapped
overlapped = true
break
}
if (!existsTextCfg.obb) {
// If self is not axis aligned. But other is.
existsTextCfg.obb = new OrientedBoundingRect$1(
existsTextCfg.localRect,
existsTextCfg.transform
)
}
if (!obb) {
// If self is axis aligned. But other is not.
obb = new OrientedBoundingRect$1(localRect, transform)
}
if (obb.intersect(existsTextCfg.obb)) {
overlapped = true
break
}
} // TODO Callback to determine if this overlap should be handled?
if (overlapped) {
hideEl(label)
labelLine && hideEl(labelLine)
} else {
label.attr('ignore', labelItem.defaultAttr.ignore)
labelLine &&
labelLine.attr(
'ignore',
labelItem.defaultAttr.labelGuideIgnore
)
displayedLabels.push(labelItem)
}
}
}
function cloneArr(points) {
if (points) {
var newPoints = []
for (var i = 0; i < points.length; i++) {
newPoints.push(points[i].slice())
}
return newPoints
}
}
function prepareLayoutCallbackParams(labelItem, hostEl) {
var label = labelItem.label
var labelLine = hostEl && hostEl.getTextGuideLine()
return {
dataIndex: labelItem.dataIndex,
dataType: labelItem.dataType,
seriesIndex: labelItem.seriesModel.seriesIndex,
text: labelItem.label.style.text,
rect: labelItem.hostRect,
labelRect: labelItem.rect, // x: labelAttr.x,
// y: labelAttr.y,
align: label.style.align,
verticalAlign: label.style.verticalAlign,
labelLinePoints: cloneArr(labelLine && labelLine.shape.points)
}
}
var LABEL_OPTION_TO_STYLE_KEYS = [
'align',
'verticalAlign',
'width',
'height',
'fontSize'
]
var dummyTransformable = new Transformable()
var labelLayoutInnerStore = makeInner()
var labelLineAnimationStore = makeInner()
function extendWithKeys(target, source, keys) {
for (var i = 0; i < keys.length; i++) {
var key = keys[i]
if (source[key] != null) {
target[key] = source[key]
}
}
}
var LABEL_LAYOUT_PROPS = ['x', 'y', 'rotation']
var LabelManager = /** @class */ (function () {
function LabelManager() {
this._labelList = []
this._chartViewList = []
}
LabelManager.prototype.clearLabels = function () {
this._labelList = []
this._chartViewList = []
}
/**
* Add label to manager
*/ LabelManager.prototype._addLabel = function (
dataIndex,
dataType,
seriesModel,
label,
layoutOption
) {
var labelStyle = label.style
var hostEl = label.__hostTarget
var textConfig = hostEl.textConfig || {} // TODO: If label is in other state.
var labelTransform = label.getComputedTransform()
var labelRect = label.getBoundingRect().plain()
BoundingRect$1.applyTransform(
labelRect,
labelRect,
labelTransform
)
if (labelTransform) {
dummyTransformable.setLocalTransform(labelTransform)
} else {
// Identity transform.
dummyTransformable.x =
dummyTransformable.y =
dummyTransformable.rotation =
dummyTransformable.originX =
dummyTransformable.originY =
0
dummyTransformable.scaleX = dummyTransformable.scaleY = 1
}
var host = label.__hostTarget
var hostRect
if (host) {
hostRect = host.getBoundingRect().plain()
var transform = host.getComputedTransform()
BoundingRect$1.applyTransform(hostRect, hostRect, transform)
}
var labelGuide = hostRect && host.getTextGuideLine()
this._labelList.push({
label: label,
labelLine: labelGuide,
seriesModel: seriesModel,
dataIndex: dataIndex,
dataType: dataType,
layoutOption: layoutOption,
computedLayoutOption: null,
rect: labelRect,
hostRect: hostRect, // Label with lower priority will be hidden when overlapped
// Use rect size as default priority
priority: hostRect ? hostRect.width * hostRect.height : 0, // Save default label attributes.
// For restore if developers want get back to default value in callback.
defaultAttr: {
ignore: label.ignore,
labelGuideIgnore: labelGuide && labelGuide.ignore,
x: dummyTransformable.x,
y: dummyTransformable.y,
scaleX: dummyTransformable.scaleX,
scaleY: dummyTransformable.scaleY,
rotation: dummyTransformable.rotation,
style: {
x: labelStyle.x,
y: labelStyle.y,
align: labelStyle.align,
verticalAlign: labelStyle.verticalAlign,
width: labelStyle.width,
height: labelStyle.height,
fontSize: labelStyle.fontSize
},
cursor: label.cursor,
attachedPos: textConfig.position,
attachedRot: textConfig.rotation
}
})
}
LabelManager.prototype.addLabelsOfSeries = function (chartView) {
var _this = this
this._chartViewList.push(chartView)
var seriesModel = chartView.__model
var layoutOption = seriesModel.get('labelLayout')
/**
* Ignore layouting if it's not specified anything.
*/ if (
!(isFunction(layoutOption) || keys(layoutOption).length)
) {
return
}
chartView.group.traverse(function (child) {
if (child.ignore) {
return true // Stop traverse descendants.
} // Only support label being hosted on graphic elements.
var textEl = child.getTextContent()
var ecData = getECData(child) // Can only attach the text on the element with dataIndex
if (textEl && !textEl.disableLabelLayout) {
_this._addLabel(
ecData.dataIndex,
ecData.dataType,
seriesModel,
textEl,
layoutOption
)
}
})
}
LabelManager.prototype.updateLayoutConfig = function (api) {
var width = api.getWidth()
var height = api.getHeight()
function createDragHandler(el, labelLineModel) {
return function () {
updateLabelLinePoints(el, labelLineModel)
}
}
for (var i = 0; i < this._labelList.length; i++) {
var labelItem = this._labelList[i]
var label = labelItem.label
var hostEl = label.__hostTarget
var defaultLabelAttr = labelItem.defaultAttr
var layoutOption = void 0 // TODO A global layout option?
if (isFunction(labelItem.layoutOption)) {
layoutOption = labelItem.layoutOption(
prepareLayoutCallbackParams(labelItem, hostEl)
)
} else {
layoutOption = labelItem.layoutOption
}
layoutOption = layoutOption || {}
labelItem.computedLayoutOption = layoutOption
var degreeToRadian = Math.PI / 180 // TODO hostEl should always exists.
// Or label should not have parent because the x, y is all in global space.
if (hostEl) {
hostEl.setTextConfig({
// Force to set local false.
local: false, // Ignore position and rotation config on the host el if x or y is changed.
position:
layoutOption.x != null || layoutOption.y != null
? null
: defaultLabelAttr.attachedPos, // Ignore rotation config on the host el if rotation is changed.
rotation:
layoutOption.rotate != null
? layoutOption.rotate * degreeToRadian
: defaultLabelAttr.attachedRot,
offset: [layoutOption.dx || 0, layoutOption.dy || 0]
})
}
var needsUpdateLabelLine = false
if (layoutOption.x != null) {
// TODO width of chart view.
label.x = parsePercent(layoutOption.x, width)
label.setStyle('x', 0) // Ignore movement in style. TODO: origin.
needsUpdateLabelLine = true
} else {
label.x = defaultLabelAttr.x
label.setStyle('x', defaultLabelAttr.style.x)
}
if (layoutOption.y != null) {
// TODO height of chart view.
label.y = parsePercent(layoutOption.y, height)
label.setStyle('y', 0) // Ignore movement in style.
needsUpdateLabelLine = true
} else {
label.y = defaultLabelAttr.y
label.setStyle('y', defaultLabelAttr.style.y)
}
if (layoutOption.labelLinePoints) {
var guideLine = hostEl.getTextGuideLine()
if (guideLine) {
guideLine.setShape({ points: layoutOption.labelLinePoints }) // Not update
needsUpdateLabelLine = false
}
}
var labelLayoutStore = labelLayoutInnerStore(label)
labelLayoutStore.needsUpdateLabelLine = needsUpdateLabelLine
label.rotation =
layoutOption.rotate != null
? layoutOption.rotate * degreeToRadian
: defaultLabelAttr.rotation
label.scaleX = defaultLabelAttr.scaleX
label.scaleY = defaultLabelAttr.scaleY
for (var k = 0; k < LABEL_OPTION_TO_STYLE_KEYS.length; k++) {
var key = LABEL_OPTION_TO_STYLE_KEYS[k]
label.setStyle(
key,
layoutOption[key] != null
? layoutOption[key]
: defaultLabelAttr.style[key]
)
}
if (layoutOption.draggable) {
label.draggable = true
label.cursor = 'move'
if (hostEl) {
var hostModel = labelItem.seriesModel
if (labelItem.dataIndex != null) {
var data = labelItem.seriesModel.getData(
labelItem.dataType
)
hostModel = data.getItemModel(labelItem.dataIndex)
}
label.on(
'drag',
createDragHandler(hostEl, hostModel.getModel('labelLine'))
)
}
} else {
// TODO Other drag functions?
label.off('drag')
label.cursor = defaultLabelAttr.cursor
}
}
}
LabelManager.prototype.layout = function (api) {
var width = api.getWidth()
var height = api.getHeight()
var labelList = prepareLayoutList(this._labelList)
var labelsNeedsAdjustOnX = filter(labelList, function (item) {
return item.layoutOption.moveOverlap === 'shiftX'
})
var labelsNeedsAdjustOnY = filter(labelList, function (item) {
return item.layoutOption.moveOverlap === 'shiftY'
})
shiftLayoutOnX(labelsNeedsAdjustOnX, 0, width)
shiftLayoutOnY(labelsNeedsAdjustOnY, 0, height)
var labelsNeedsHideOverlap = filter(labelList, function (item) {
return item.layoutOption.hideOverlap
})
hideOverlap(labelsNeedsHideOverlap)
}
/**
* Process all labels. Not only labels with layoutOption.
*/ LabelManager.prototype.processLabelsOverall = function () {
var _this = this
each$7(this._chartViewList, function (chartView) {
var seriesModel = chartView.__model
var ignoreLabelLineUpdate = chartView.ignoreLabelLineUpdate
var animationEnabled = seriesModel.isAnimationEnabled()
chartView.group.traverse(function (child) {
if (child.ignore && !child.forceLabelAnimation) {
return true // Stop traverse descendants.
}
var needsUpdateLabelLine = !ignoreLabelLineUpdate
var label = child.getTextContent()
if (!needsUpdateLabelLine && label) {
needsUpdateLabelLine =
labelLayoutInnerStore(label).needsUpdateLabelLine
}
if (needsUpdateLabelLine) {
_this._updateLabelLine(child, seriesModel)
}
if (animationEnabled) {
_this._animateLabels(child, seriesModel)
}
})
})
}
LabelManager.prototype._updateLabelLine = function (
el,
seriesModel
) {
// Only support label being hosted on graphic elements.
var textEl = el.getTextContent() // Update label line style.
var ecData = getECData(el)
var dataIndex = ecData.dataIndex // Only support labelLine on the labels represent data.
if (textEl && dataIndex != null) {
var data = seriesModel.getData(ecData.dataType)
var itemModel = data.getItemModel(dataIndex)
var defaultStyle = {}
var visualStyle = data.getItemVisual(dataIndex, 'style')
var visualType = data.getVisual('drawType') // Default to be same with main color
defaultStyle.stroke = visualStyle[visualType]
var labelLineModel = itemModel.getModel('labelLine')
setLabelLineStyle(
el,
getLabelLineStatesModels(itemModel),
defaultStyle
)
updateLabelLinePoints(el, labelLineModel)
}
}
LabelManager.prototype._animateLabels = function (el, seriesModel) {
var textEl = el.getTextContent()
var guideLine = el.getTextGuideLine() // Animate
if (
textEl && // `forceLabelAnimation` has the highest priority
(el.forceLabelAnimation ||
(!textEl.ignore &&
!textEl.invisible &&
!el.disableLabelAnimation &&
!isElementRemoved(el)))
) {
var layoutStore = labelLayoutInnerStore(textEl)
var oldLayout = layoutStore.oldLayout
var ecData = getECData(el)
var dataIndex = ecData.dataIndex
var newProps = {
x: textEl.x,
y: textEl.y,
rotation: textEl.rotation
}
var data = seriesModel.getData(ecData.dataType)
if (!oldLayout) {
textEl.attr(newProps) // Disable fade in animation if value animation is enabled.
if (!labelInner(textEl).valueAnimation) {
var oldOpacity = retrieve2(textEl.style.opacity, 1) // Fade in animation
textEl.style.opacity = 0
initProps(
textEl,
{ style: { opacity: oldOpacity } },
seriesModel,
dataIndex
)
}
} else {
textEl.attr(oldLayout) // Make sure the animation from is in the right status.
var prevStates = el.prevStates
if (prevStates) {
if (indexOf(prevStates, 'select') >= 0) {
textEl.attr(layoutStore.oldLayoutSelect)
}
if (indexOf(prevStates, 'emphasis') >= 0) {
textEl.attr(layoutStore.oldLayoutEmphasis)
}
}
updateProps$1(textEl, newProps, seriesModel, dataIndex)
}
layoutStore.oldLayout = newProps
if (textEl.states.select) {
var layoutSelect = (layoutStore.oldLayoutSelect = {})
extendWithKeys(layoutSelect, newProps, LABEL_LAYOUT_PROPS)
extendWithKeys(
layoutSelect,
textEl.states.select,
LABEL_LAYOUT_PROPS
)
}
if (textEl.states.emphasis) {
var layoutEmphasis = (layoutStore.oldLayoutEmphasis = {})
extendWithKeys(layoutEmphasis, newProps, LABEL_LAYOUT_PROPS)
extendWithKeys(
layoutEmphasis,
textEl.states.emphasis,
LABEL_LAYOUT_PROPS
)
}
animateLabelValue(
textEl,
dataIndex,
data,
seriesModel,
seriesModel
)
}
if (guideLine && !guideLine.ignore && !guideLine.invisible) {
var layoutStore = labelLineAnimationStore(guideLine)
var oldLayout = layoutStore.oldLayout
var newLayout = { points: guideLine.shape.points }
if (!oldLayout) {
guideLine.setShape(newLayout)
guideLine.style.strokePercent = 0
initProps(
guideLine,
{ style: { strokePercent: 1 } },
seriesModel
)
} else {
guideLine.attr({ shape: oldLayout })
updateProps$1(guideLine, { shape: newLayout }, seriesModel)
}
layoutStore.oldLayout = newLayout
}
}
return LabelManager
})()
var LabelManager$1 = LabelManager
var getLabelManager = makeInner()
function installLabelLayout(registers) {
registers.registerUpdateLifecycle(
'series:beforeupdate',
function (ecModel, api, params) {
// TODO api provide an namespace that can save stuff per instance
var labelManager = getLabelManager(api).labelManager
if (!labelManager) {
labelManager = getLabelManager(api).labelManager =
new LabelManager$1()
}
labelManager.clearLabels()
}
)
registers.registerUpdateLifecycle(
'series:layoutlabels',
function (ecModel, api, params) {
var labelManager = getLabelManager(api).labelManager
params.updatedSeries.forEach(function (series) {
labelManager.addLabelsOfSeries(
api.getViewOfSeriesModel(series)
)
})
labelManager.updateLayoutConfig(api)
labelManager.layout(api)
labelManager.processLabelsOverall()
}
)
}
var LineSeriesModel = /** @class */ (function (_super) {
__extends(LineSeriesModel, _super)
function LineSeriesModel() {
var _this =
(_super !== null && _super.apply(this, arguments)) || this
_this.type = LineSeriesModel.type
_this.hasSymbolVisual = true
return _this
}
LineSeriesModel.prototype.getInitialData = function (option) {
return createSeriesData(null, this, { useEncodeDefaulter: true })
}
LineSeriesModel.prototype.getLegendIcon = function (opt) {
var group = new Group$3()
var line = createSymbol(
'line',
0,
opt.itemHeight / 2,
opt.itemWidth,
0,
opt.lineStyle.stroke,
false
)
group.add(line)
line.setStyle(opt.lineStyle)
var visualType = this.getData().getVisual('symbol')
var visualRotate = this.getData().getVisual('symbolRotate')
var symbolType = visualType === 'none' ? 'circle' : visualType // Symbol size is 80% when there is a line
var size = opt.itemHeight * 0.8
var symbol = createSymbol(
symbolType,
(opt.itemWidth - size) / 2,
(opt.itemHeight - size) / 2,
size,
size,
opt.itemStyle.fill
)
group.add(symbol)
symbol.setStyle(opt.itemStyle)
var symbolRotate =
opt.iconRotate === 'inherit'
? visualRotate
: opt.iconRotate || 0
symbol.rotation = (symbolRotate * Math.PI) / 180
symbol.setOrigin([opt.itemWidth / 2, opt.itemHeight / 2])
if (symbolType.indexOf('empty') > -1) {
symbol.style.stroke = symbol.style.fill
symbol.style.fill = '#fff'
symbol.style.lineWidth = 2
}
return group
}
LineSeriesModel.type = 'series.line'
LineSeriesModel.dependencies = ['grid', 'polar']
LineSeriesModel.defaultOption = {
// zlevel: 0,
z: 3,
coordinateSystem: 'cartesian2d',
legendHoverLink: true,
clip: true,
label: { position: 'top' }, // itemStyle: {
// },
endLabel: { show: false, valueAnimation: true, distance: 8 },
lineStyle: { width: 2, type: 'solid' },
emphasis: { scale: true }, // areaStyle: {
// origin of areaStyle. Valid values:
// `'auto'/null/undefined`: from axisLine to data
// `'start'`: from min to data
// `'end'`: from data to max
// origin: 'auto'
// },
// false, 'start', 'end', 'middle'
step: false, // Disabled if step is true
smooth: false,
smoothMonotone: null,
symbol: 'emptyCircle',
symbolSize: 4,
symbolRotate: null,
showSymbol: true, // `false`: follow the label interval strategy.
// `true`: show all symbols.
// `'auto'`: If possible, show all symbols, otherwise
// follow the label interval strategy.
showAllSymbol: 'auto', // Whether to connect break point.
connectNulls: false, // Sampling for large data. Can be: 'average', 'max', 'min', 'sum', 'lttb'.
sampling: 'none',
animationEasing: 'linear', // Disable progressive
progressive: 0,
hoverLayerThreshold: Infinity,
universalTransition: { divideShape: 'clone' },
triggerLineEvent: false
}
return LineSeriesModel
})(SeriesModel$1)
var LineSeries = LineSeriesModel
/**
* @return label string. Not null/undefined
*/ function getDefaultLabel(data, dataIndex) {
var labelDims = data.mapDimensionsAll('defaultedLabel')
var len = labelDims.length // Simple optimization (in lots of cases, label dims length is 1)
if (len === 1) {
var rawVal = retrieveRawValue(data, dataIndex, labelDims[0])
return rawVal != null ? rawVal + '' : null
} else if (len) {
var vals = []
for (var i = 0; i < labelDims.length; i++) {
vals.push(retrieveRawValue(data, dataIndex, labelDims[i]))
}
return vals.join(' ')
}
}
function getDefaultInterpolatedLabel(data, interpolatedValue) {
var labelDims = data.mapDimensionsAll('defaultedLabel')
if (!isArray(interpolatedValue)) {
return interpolatedValue + ''
}
var vals = []
for (var i = 0; i < labelDims.length; i++) {
var dimIndex = data.getDimensionIndex(labelDims[i])
if (dimIndex >= 0) {
vals.push(interpolatedValue[dimIndex])
}
}
return vals.join(' ')
}
var Symbol$1 = /** @class */ (function (_super) {
__extends(_Symbol, _super)
function _Symbol(data, idx, seriesScope, opts) {
var _this = _super.call(this) || this
_this.updateData(data, idx, seriesScope, opts)
return _this
}
_Symbol.prototype._createSymbol = function (
symbolType,
data,
idx,
symbolSize,
keepAspect
) {
// Remove paths created before
this.removeAll() // let symbolPath = createSymbol(
// symbolType, -0.5, -0.5, 1, 1, color
// );
// If width/height are set too small (e.g., set to 1) on ios10
// and macOS Sierra, a circle stroke become a rect, no matter what
// the scale is set. So we set width/height as 2. See #4150.
var symbolPath = createSymbol(
symbolType,
-1,
-1,
2,
2,
null,
keepAspect
)
symbolPath.attr({
z2: 100,
culling: true,
scaleX: symbolSize[0] / 2,
scaleY: symbolSize[1] / 2
}) // Rewrite drift method
symbolPath.drift = driftSymbol
this._symbolType = symbolType
this.add(symbolPath)
}
/**
* Stop animation
* @param {boolean} toLastFrame
*/ _Symbol.prototype.stopSymbolAnimation = function (toLastFrame) {
this.childAt(0).stopAnimation(null, toLastFrame)
}
_Symbol.prototype.getSymbolType = function () {
return this._symbolType
}
/**
* FIXME:
* Caution: This method breaks the encapsulation of this module,
* but it indeed brings convenience. So do not use the method
* unless you detailedly know all the implements of `Symbol`,
* especially animation.
*
* Get symbol path element.
*/ _Symbol.prototype.getSymbolPath = function () {
return this.childAt(0)
}
/**
* Highlight symbol
*/ _Symbol.prototype.highlight = function () {
enterEmphasis(this.childAt(0))
}
/**
* Downplay symbol
*/ _Symbol.prototype.downplay = function () {
leaveEmphasis(this.childAt(0))
}
/**
* @param {number} zlevel
* @param {number} z
*/ _Symbol.prototype.setZ = function (zlevel, z) {
var symbolPath = this.childAt(0)
symbolPath.zlevel = zlevel
symbolPath.z = z
}
_Symbol.prototype.setDraggable = function (
draggable,
hasCursorOption
) {
var symbolPath = this.childAt(0)
symbolPath.draggable = draggable
symbolPath.cursor =
!hasCursorOption && draggable ? 'move' : symbolPath.cursor
}
/**
* Update symbol properties
*/ _Symbol.prototype.updateData = function (
data,
idx,
seriesScope,
opts
) {
this.silent = false
var symbolType = data.getItemVisual(idx, 'symbol') || 'circle'
var seriesModel = data.hostModel
var symbolSize = _Symbol.getSymbolSize(data, idx)
var isInit = symbolType !== this._symbolType
var disableAnimation = opts && opts.disableAnimation
if (isInit) {
var keepAspect = data.getItemVisual(idx, 'symbolKeepAspect')
this._createSymbol(
symbolType,
data,
idx,
symbolSize,
keepAspect
)
} else {
var symbolPath = this.childAt(0)
symbolPath.silent = false
var target = {
scaleX: symbolSize[0] / 2,
scaleY: symbolSize[1] / 2
}
disableAnimation
? symbolPath.attr(target)
: updateProps$1(symbolPath, target, seriesModel, idx)
saveOldStyle(symbolPath)
}
this._updateCommon(data, idx, symbolSize, seriesScope, opts)
if (isInit) {
var symbolPath = this.childAt(0)
if (!disableAnimation) {
var target = {
scaleX: this._sizeX,
scaleY: this._sizeY,
style: {
// Always fadeIn. Because it has fadeOut animation when symbol is removed..
opacity: symbolPath.style.opacity
}
}
symbolPath.scaleX = symbolPath.scaleY = 0
symbolPath.style.opacity = 0
initProps(symbolPath, target, seriesModel, idx)
}
}
if (disableAnimation) {
// Must stop leave transition manually if don't call initProps or updateProps.
this.childAt(0).stopAnimation('leave')
}
}
_Symbol.prototype._updateCommon = function (
data,
idx,
symbolSize,
seriesScope,
opts
) {
var symbolPath = this.childAt(0)
var seriesModel = data.hostModel
var emphasisItemStyle
var blurItemStyle
var selectItemStyle
var focus
var blurScope
var emphasisDisabled
var labelStatesModels
var hoverScale
var cursorStyle
if (seriesScope) {
emphasisItemStyle = seriesScope.emphasisItemStyle
blurItemStyle = seriesScope.blurItemStyle
selectItemStyle = seriesScope.selectItemStyle
focus = seriesScope.focus
blurScope = seriesScope.blurScope
labelStatesModels = seriesScope.labelStatesModels
hoverScale = seriesScope.hoverScale
cursorStyle = seriesScope.cursorStyle
emphasisDisabled = seriesScope.emphasisDisabled
}
if (!seriesScope || data.hasItemOption) {
var itemModel =
seriesScope && seriesScope.itemModel
? seriesScope.itemModel
: data.getItemModel(idx)
var emphasisModel = itemModel.getModel('emphasis')
emphasisItemStyle = emphasisModel
.getModel('itemStyle')
.getItemStyle()
selectItemStyle = itemModel
.getModel(['select', 'itemStyle'])
.getItemStyle()
blurItemStyle = itemModel
.getModel(['blur', 'itemStyle'])
.getItemStyle()
focus = emphasisModel.get('focus')
blurScope = emphasisModel.get('blurScope')
emphasisDisabled = emphasisModel.get('disabled')
labelStatesModels = getLabelStatesModels(itemModel)
hoverScale = emphasisModel.getShallow('scale')
cursorStyle = itemModel.getShallow('cursor')
}
var symbolRotate = data.getItemVisual(idx, 'symbolRotate')
symbolPath.attr(
'rotation',
((symbolRotate || 0) * Math.PI) / 180 || 0
)
var symbolOffset = normalizeSymbolOffset(
data.getItemVisual(idx, 'symbolOffset'),
symbolSize
)
if (symbolOffset) {
symbolPath.x = symbolOffset[0]
symbolPath.y = symbolOffset[1]
}
cursorStyle && symbolPath.attr('cursor', cursorStyle)
var symbolStyle = data.getItemVisual(idx, 'style')
var visualColor = symbolStyle.fill
if (symbolPath instanceof ZRImage$1) {
var pathStyle = symbolPath.style
symbolPath.useStyle(
extend(
{
// TODO other properties like x, y ?
image: pathStyle.image,
x: pathStyle.x,
y: pathStyle.y,
width: pathStyle.width,
height: pathStyle.height
},
symbolStyle
)
)
} else {
if (symbolPath.__isEmptyBrush) {
// fill and stroke will be swapped if it's empty.
// So we cloned a new style to avoid it affecting the original style in visual storage.
// TODO Better implementation. No empty logic!
symbolPath.useStyle(extend({}, symbolStyle))
} else {
symbolPath.useStyle(symbolStyle)
} // Disable decal because symbol scale will been applied on the decal.
symbolPath.style.decal = null
symbolPath.setColor(visualColor, opts && opts.symbolInnerColor)
symbolPath.style.strokeNoScale = true
}
var liftZ = data.getItemVisual(idx, 'liftZ')
var z2Origin = this._z2
if (liftZ != null) {
if (z2Origin == null) {
this._z2 = symbolPath.z2
symbolPath.z2 += liftZ
}
} else if (z2Origin != null) {
symbolPath.z2 = z2Origin
this._z2 = null
}
var useNameLabel = opts && opts.useNameLabel
setLabelStyle(symbolPath, labelStatesModels, {
labelFetcher: seriesModel,
labelDataIndex: idx,
defaultText: getLabelDefaultText,
inheritColor: visualColor,
defaultOpacity: symbolStyle.opacity
}) // Do not execute util needed.
function getLabelDefaultText(idx) {
return useNameLabel
? data.getName(idx)
: getDefaultLabel(data, idx)
}
this._sizeX = symbolSize[0] / 2
this._sizeY = symbolSize[1] / 2
var emphasisState = symbolPath.ensureState('emphasis')
emphasisState.style = emphasisItemStyle
symbolPath.ensureState('select').style = selectItemStyle
symbolPath.ensureState('blur').style = blurItemStyle
if (hoverScale) {
var scaleRatio = Math.max(
isNumber(hoverScale) ? hoverScale : 1.1,
3 / this._sizeY
)
emphasisState.scaleX = this._sizeX * scaleRatio
emphasisState.scaleY = this._sizeY * scaleRatio
}
this.setSymbolScale(1)
toggleHoverEmphasis(this, focus, blurScope, emphasisDisabled)
}
_Symbol.prototype.setSymbolScale = function (scale) {
this.scaleX = this.scaleY = scale
}
_Symbol.prototype.fadeOut = function (cb, seriesModel, opt) {
var symbolPath = this.childAt(0)
var dataIndex = getECData(this).dataIndex
var animationOpt = opt && opt.animation // Avoid mistaken hover when fading out
this.silent = symbolPath.silent = true // Not show text when animating
if (opt && opt.fadeLabel) {
var textContent = symbolPath.getTextContent()
if (textContent) {
removeElement(
textContent,
{ style: { opacity: 0 } },
seriesModel,
{
dataIndex: dataIndex,
removeOpt: animationOpt,
cb: function cb() {
symbolPath.removeTextContent()
}
}
)
}
} else {
symbolPath.removeTextContent()
}
removeElement(
symbolPath,
{ style: { opacity: 0 }, scaleX: 0, scaleY: 0 },
seriesModel,
{ dataIndex: dataIndex, cb: cb, removeOpt: animationOpt }
)
}
_Symbol.getSymbolSize = function (data, idx) {
return normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize'))
}
return _Symbol
})(Group$3)
function driftSymbol(dx, dy) {
this.parent.drift(dx, dy)
}
var SymbolClz = Symbol$1
function symbolNeedsDraw(data, point, idx, opt) {
return (
point &&
!isNaN(point[0]) &&
!isNaN(point[1]) &&
!(opt.isIgnore && opt.isIgnore(idx)) && // We do not set clipShape on group, because it will cut part of
// the symbol element shape. We use the same clip shape here as
// the line clip.
!(opt.clipShape && !opt.clipShape.contain(point[0], point[1])) &&
data.getItemVisual(idx, 'symbol') !== 'none'
)
}
function normalizeUpdateOpt(opt) {
if (opt != null && !isObject$2(opt)) {
opt = { isIgnore: opt }
}
return opt || {}
}
function makeSeriesScope(data) {
var seriesModel = data.hostModel
var emphasisModel = seriesModel.getModel('emphasis')
return {
emphasisItemStyle: emphasisModel
.getModel('itemStyle')
.getItemStyle(),
blurItemStyle: seriesModel
.getModel(['blur', 'itemStyle'])
.getItemStyle(),
selectItemStyle: seriesModel
.getModel(['select', 'itemStyle'])
.getItemStyle(),
focus: emphasisModel.get('focus'),
blurScope: emphasisModel.get('blurScope'),
emphasisDisabled: emphasisModel.get('disabled'),
hoverScale: emphasisModel.get('scale'),
labelStatesModels: getLabelStatesModels(seriesModel),
cursorStyle: seriesModel.get('cursor')
}
}
var SymbolDraw = /** @class */ (function () {
function SymbolDraw(SymbolCtor) {
this.group = new Group$3()
this._SymbolCtor = SymbolCtor || SymbolClz
}
/**
* Update symbols draw by new data
*/ SymbolDraw.prototype.updateData = function (data, opt) {
// Remove progressive els.
this._progressiveEls = null
opt = normalizeUpdateOpt(opt)
var group = this.group
var seriesModel = data.hostModel
var oldData = this._data
var SymbolCtor = this._SymbolCtor
var disableAnimation = opt.disableAnimation
var seriesScope = makeSeriesScope(data)
var symbolUpdateOpt = { disableAnimation: disableAnimation }
var getSymbolPoint =
opt.getSymbolPoint ||
function (idx) {
return data.getItemLayout(idx)
} // There is no oldLineData only when first rendering or switching from
// stream mode to normal mode, where previous elements should be removed.
if (!oldData) {
group.removeAll()
}
data
.diff(oldData)
.add(function (newIdx) {
var point = getSymbolPoint(newIdx)
if (symbolNeedsDraw(data, point, newIdx, opt)) {
var symbolEl = new SymbolCtor(
data,
newIdx,
seriesScope,
symbolUpdateOpt
)
symbolEl.setPosition(point)
data.setItemGraphicEl(newIdx, symbolEl)
group.add(symbolEl)
}
})
.update(function (newIdx, oldIdx) {
var symbolEl = oldData.getItemGraphicEl(oldIdx)
var point = getSymbolPoint(newIdx)
if (!symbolNeedsDraw(data, point, newIdx, opt)) {
group.remove(symbolEl)
return
}
var newSymbolType =
data.getItemVisual(newIdx, 'symbol') || 'circle'
var oldSymbolType =
symbolEl &&
symbolEl.getSymbolType &&
symbolEl.getSymbolType()
if (
!symbolEl || // Create a new if symbol type changed.
(oldSymbolType && oldSymbolType !== newSymbolType)
) {
group.remove(symbolEl)
symbolEl = new SymbolCtor(
data,
newIdx,
seriesScope,
symbolUpdateOpt
)
symbolEl.setPosition(point)
} else {
symbolEl.updateData(
data,
newIdx,
seriesScope,
symbolUpdateOpt
)
var target = { x: point[0], y: point[1] }
disableAnimation
? symbolEl.attr(target)
: updateProps$1(symbolEl, target, seriesModel)
} // Add back
group.add(symbolEl)
data.setItemGraphicEl(newIdx, symbolEl)
})
.remove(function (oldIdx) {
var el = oldData.getItemGraphicEl(oldIdx)
el &&
el.fadeOut(function () {
group.remove(el)
}, seriesModel)
})
.execute()
this._getSymbolPoint = getSymbolPoint
this._data = data
}
SymbolDraw.prototype.updateLayout = function () {
var _this = this
var data = this._data
if (data) {
// Not use animation
data.eachItemGraphicEl(function (el, idx) {
var point = _this._getSymbolPoint(idx)
el.setPosition(point)
el.markRedraw()
})
}
}
SymbolDraw.prototype.incrementalPrepareUpdate = function (data) {
this._seriesScope = makeSeriesScope(data)
this._data = null
this.group.removeAll()
}
/**
* Update symbols draw by new data
*/ SymbolDraw.prototype.incrementalUpdate = function (
taskParams,
data,
opt
) {
// Clear
this._progressiveEls = []
opt = normalizeUpdateOpt(opt)
function updateIncrementalAndHover(el) {
if (!el.isGroup) {
el.incremental = true
el.ensureState('emphasis').hoverLayer = true
}
}
for (var idx = taskParams.start; idx < taskParams.end; idx++) {
var point = data.getItemLayout(idx)
if (symbolNeedsDraw(data, point, idx, opt)) {
var el = new this._SymbolCtor(data, idx, this._seriesScope)
el.traverse(updateIncrementalAndHover)
el.setPosition(point)
this.group.add(el)
data.setItemGraphicEl(idx, el)
this._progressiveEls.push(el)
}
}
}
SymbolDraw.prototype.eachRendered = function (cb) {
traverseElements(this._progressiveEls || this.group, cb)
}
SymbolDraw.prototype.remove = function (enableAnimation) {
var group = this.group
var data = this._data // Incremental model do not have this._data.
if (data && enableAnimation) {
data.eachItemGraphicEl(function (el) {
el.fadeOut(function () {
group.remove(el)
}, data.hostModel)
})
} else {
group.removeAll()
}
}
return SymbolDraw
})()
var SymbolDraw$1 = SymbolDraw
function prepareDataCoordInfo(coordSys, data, valueOrigin) {
var baseAxis = coordSys.getBaseAxis()
var valueAxis = coordSys.getOtherAxis(baseAxis)
var valueStart = getValueStart(valueAxis, valueOrigin)
var baseAxisDim = baseAxis.dim
var valueAxisDim = valueAxis.dim
var valueDim = data.mapDimension(valueAxisDim)
var baseDim = data.mapDimension(baseAxisDim)
var baseDataOffset =
valueAxisDim === 'x' || valueAxisDim === 'radius' ? 1 : 0
var dims = map$1(coordSys.dimensions, function (coordDim) {
return data.mapDimension(coordDim)
})
var stacked = false
var stackResultDim = data.getCalculationInfo('stackResultDimension')
if (isDimensionStacked(data, dims[0] /*, dims[1]*/)) {
// jshint ignore:line
stacked = true
dims[0] = stackResultDim
}
if (isDimensionStacked(data, dims[1] /*, dims[0]*/)) {
// jshint ignore:line
stacked = true
dims[1] = stackResultDim
}
return {
dataDimsForPoint: dims,
valueStart: valueStart,
valueAxisDim: valueAxisDim,
baseAxisDim: baseAxisDim,
stacked: !!stacked,
valueDim: valueDim,
baseDim: baseDim,
baseDataOffset: baseDataOffset,
stackedOverDimension: data.getCalculationInfo(
'stackedOverDimension'
)
}
}
function getValueStart(valueAxis, valueOrigin) {
var valueStart = 0
var extent = valueAxis.scale.getExtent()
if (valueOrigin === 'start') {
valueStart = extent[0]
} else if (valueOrigin === 'end') {
valueStart = extent[1]
} // If origin is specified as a number, use it as
// valueStart directly
else if (isNumber(valueOrigin) && !isNaN(valueOrigin)) {
valueStart = valueOrigin
} // auto
else {
// Both positive
if (extent[0] > 0) {
valueStart = extent[0]
} // Both negative
else if (extent[1] < 0) {
valueStart = extent[1]
} // If is one positive, and one negative, onZero shall be true
}
return valueStart
}
function getStackedOnPoint(dataCoordInfo, coordSys, data, idx) {
var value = NaN
if (dataCoordInfo.stacked) {
value = data.get(
data.getCalculationInfo('stackedOverDimension'),
idx
)
}
if (isNaN(value)) {
value = dataCoordInfo.valueStart
}
var baseDataOffset = dataCoordInfo.baseDataOffset
var stackedData = []
stackedData[baseDataOffset] = data.get(dataCoordInfo.baseDim, idx)
stackedData[1 - baseDataOffset] = value
return coordSys.dataToPoint(stackedData)
}
function diffData(oldData, newData) {
var diffResult = []
newData
.diff(oldData)
.add(function (idx) {
diffResult.push({ cmd: '+', idx: idx })
})
.update(function (newIdx, oldIdx) {
diffResult.push({ cmd: '=', idx: oldIdx, idx1: newIdx })
})
.remove(function (idx) {
diffResult.push({ cmd: '-', idx: idx })
})
.execute()
return diffResult
}
function lineAnimationDiff(
oldData,
newData,
oldStackedOnPoints,
newStackedOnPoints,
oldCoordSys,
newCoordSys,
oldValueOrigin,
newValueOrigin
) {
var diff = diffData(oldData, newData) // let newIdList = newData.mapArray(newData.getId);
// let oldIdList = oldData.mapArray(oldData.getId);
// convertToIntId(newIdList, oldIdList);
// // FIXME One data ?
// diff = arrayDiff(oldIdList, newIdList);
var currPoints = []
var nextPoints = [] // Points for stacking base line
var currStackedPoints = []
var nextStackedPoints = []
var status = []
var sortedIndices = []
var rawIndices = []
var newDataOldCoordInfo = prepareDataCoordInfo(
oldCoordSys,
newData,
oldValueOrigin
) // const oldDataNewCoordInfo = prepareDataCoordInfo(newCoordSys, oldData, newValueOrigin);
var oldPoints = oldData.getLayout('points') || []
var newPoints = newData.getLayout('points') || []
for (var i = 0; i < diff.length; i++) {
var diffItem = diff[i]
var pointAdded = true
var oldIdx2 = void 0
var newIdx2 = void 0 // FIXME, animation is not so perfect when dataZoom window moves fast
// Which is in case remvoing or add more than one data in the tail or head
switch (diffItem.cmd) {
case '=':
oldIdx2 = diffItem.idx * 2
newIdx2 = diffItem.idx1 * 2
var currentX = oldPoints[oldIdx2]
var currentY = oldPoints[oldIdx2 + 1]
var nextX = newPoints[newIdx2]
var nextY = newPoints[newIdx2 + 1] // If previous data is NaN, use next point directly
if (isNaN(currentX) || isNaN(currentY)) {
currentX = nextX
currentY = nextY
}
currPoints.push(currentX, currentY)
nextPoints.push(nextX, nextY)
currStackedPoints.push(
oldStackedOnPoints[oldIdx2],
oldStackedOnPoints[oldIdx2 + 1]
)
nextStackedPoints.push(
newStackedOnPoints[newIdx2],
newStackedOnPoints[newIdx2 + 1]
)
rawIndices.push(newData.getRawIndex(diffItem.idx1))
break
case '+':
var newIdx = diffItem.idx
var newDataDimsForPoint = newDataOldCoordInfo.dataDimsForPoint
var oldPt = oldCoordSys.dataToPoint([
newData.get(newDataDimsForPoint[0], newIdx),
newData.get(newDataDimsForPoint[1], newIdx)
])
newIdx2 = newIdx * 2
currPoints.push(oldPt[0], oldPt[1])
nextPoints.push(newPoints[newIdx2], newPoints[newIdx2 + 1])
var stackedOnPoint = getStackedOnPoint(
newDataOldCoordInfo,
oldCoordSys,
newData,
newIdx
)
currStackedPoints.push(stackedOnPoint[0], stackedOnPoint[1])
nextStackedPoints.push(
newStackedOnPoints[newIdx2],
newStackedOnPoints[newIdx2 + 1]
)
rawIndices.push(newData.getRawIndex(newIdx))
break
case '-':
pointAdded = false
} // Original indices
if (pointAdded) {
status.push(diffItem)
sortedIndices.push(sortedIndices.length)
}
} // Diff result may be crossed if all items are changed
// Sort by data index
sortedIndices.sort(function (a, b) {
return rawIndices[a] - rawIndices[b]
})
var len = currPoints.length
var sortedCurrPoints = createFloat32Array(len)
var sortedNextPoints = createFloat32Array(len)
var sortedCurrStackedPoints = createFloat32Array(len)
var sortedNextStackedPoints = createFloat32Array(len)
var sortedStatus = []
for (var i = 0; i < sortedIndices.length; i++) {
var idx = sortedIndices[i]
var i2 = i * 2
var idx2 = idx * 2
sortedCurrPoints[i2] = currPoints[idx2]
sortedCurrPoints[i2 + 1] = currPoints[idx2 + 1]
sortedNextPoints[i2] = nextPoints[idx2]
sortedNextPoints[i2 + 1] = nextPoints[idx2 + 1]
sortedCurrStackedPoints[i2] = currStackedPoints[idx2]
sortedCurrStackedPoints[i2 + 1] = currStackedPoints[idx2 + 1]
sortedNextStackedPoints[i2] = nextStackedPoints[idx2]
sortedNextStackedPoints[i2 + 1] = nextStackedPoints[idx2 + 1]
sortedStatus[i] = status[idx]
}
return {
current: sortedCurrPoints,
next: sortedNextPoints,
stackedOnCurrent: sortedCurrStackedPoints,
stackedOnNext: sortedNextStackedPoints,
status: sortedStatus
}
}
var mathMin$2 = Math.min
var mathMax$2 = Math.max
function isPointNull$1(x, y) {
return isNaN(x) || isNaN(y)
}
/**
* Draw smoothed line in non-monotone, in may cause undesired curve in extreme
* situations. This should be used when points are non-monotone neither in x or
* y dimension.
*/ function drawSegment(
ctx,
points,
start,
segLen,
allLen,
dir,
smooth,
smoothMonotone,
connectNulls
) {
var prevX
var prevY
var cpx0
var cpy0
var cpx1
var cpy1
var idx = start
var k = 0
for (; k < segLen; k++) {
var x = points[idx * 2]
var y = points[idx * 2 + 1]
if (idx >= allLen || idx < 0) {
break
}
if (isPointNull$1(x, y)) {
if (connectNulls) {
idx += dir
continue
}
break
}
if (idx === start) {
ctx[dir > 0 ? 'moveTo' : 'lineTo'](x, y)
cpx0 = x
cpy0 = y
} else {
var dx = x - prevX
var dy = y - prevY // Ignore tiny segment.
if (dx * dx + dy * dy < 0.5) {
idx += dir
continue
}
if (smooth > 0) {
var nextIdx = idx + dir
var nextX = points[nextIdx * 2]
var nextY = points[nextIdx * 2 + 1] // Ignore duplicate point
while (nextX === x && nextY === y && k < segLen) {
k++
nextIdx += dir
idx += dir
nextX = points[nextIdx * 2]
nextY = points[nextIdx * 2 + 1]
x = points[idx * 2]
y = points[idx * 2 + 1]
dx = x - prevX
dy = y - prevY
}
var tmpK = k + 1
if (connectNulls) {
// Find next point not null
while (isPointNull$1(nextX, nextY) && tmpK < segLen) {
tmpK++
nextIdx += dir
nextX = points[nextIdx * 2]
nextY = points[nextIdx * 2 + 1]
}
}
var ratioNextSeg = 0.5
var vx = 0
var vy = 0
var nextCpx0 = void 0
var nextCpy0 = void 0 // Is last point
if (tmpK >= segLen || isPointNull$1(nextX, nextY)) {
cpx1 = x
cpy1 = y
} else {
vx = nextX - prevX
vy = nextY - prevY
var dx0 = x - prevX
var dx1 = nextX - x
var dy0 = y - prevY
var dy1 = nextY - y
var lenPrevSeg = void 0
var lenNextSeg = void 0
if (smoothMonotone === 'x') {
lenPrevSeg = Math.abs(dx0)
lenNextSeg = Math.abs(dx1)
var dir_1 = vx > 0 ? 1 : -1
cpx1 = x - dir_1 * lenPrevSeg * smooth
cpy1 = y
nextCpx0 = x + dir_1 * lenNextSeg * smooth
nextCpy0 = y
} else if (smoothMonotone === 'y') {
lenPrevSeg = Math.abs(dy0)
lenNextSeg = Math.abs(dy1)
var dir_2 = vy > 0 ? 1 : -1
cpx1 = x
cpy1 = y - dir_2 * lenPrevSeg * smooth
nextCpx0 = x
nextCpy0 = y + dir_2 * lenNextSeg * smooth
} else {
lenPrevSeg = Math.sqrt(dx0 * dx0 + dy0 * dy0)
lenNextSeg = Math.sqrt(dx1 * dx1 + dy1 * dy1) // Use ratio of seg length
ratioNextSeg = lenNextSeg / (lenNextSeg + lenPrevSeg)
cpx1 = x - vx * smooth * (1 - ratioNextSeg)
cpy1 = y - vy * smooth * (1 - ratioNextSeg) // cp0 of next segment
nextCpx0 = x + vx * smooth * ratioNextSeg
nextCpy0 = y + vy * smooth * ratioNextSeg // Smooth constraint between point and next point.
// Avoid exceeding extreme after smoothing.
nextCpx0 = mathMin$2(nextCpx0, mathMax$2(nextX, x))
nextCpy0 = mathMin$2(nextCpy0, mathMax$2(nextY, y))
nextCpx0 = mathMax$2(nextCpx0, mathMin$2(nextX, x))
nextCpy0 = mathMax$2(nextCpy0, mathMin$2(nextY, y)) // Reclaculate cp1 based on the adjusted cp0 of next seg.
vx = nextCpx0 - x
vy = nextCpy0 - y
cpx1 = x - (vx * lenPrevSeg) / lenNextSeg
cpy1 = y - (vy * lenPrevSeg) / lenNextSeg // Smooth constraint between point and prev point.
// Avoid exceeding extreme after smoothing.
cpx1 = mathMin$2(cpx1, mathMax$2(prevX, x))
cpy1 = mathMin$2(cpy1, mathMax$2(prevY, y))
cpx1 = mathMax$2(cpx1, mathMin$2(prevX, x))
cpy1 = mathMax$2(cpy1, mathMin$2(prevY, y)) // Adjust next cp0 again.
vx = x - cpx1
vy = y - cpy1
nextCpx0 = x + (vx * lenNextSeg) / lenPrevSeg
nextCpy0 = y + (vy * lenNextSeg) / lenPrevSeg
}
}
ctx.bezierCurveTo(cpx0, cpy0, cpx1, cpy1, x, y)
cpx0 = nextCpx0
cpy0 = nextCpy0
} else {
ctx.lineTo(x, y)
}
}
prevX = x
prevY = y
idx += dir
}
return k
}
var ECPolylineShape = /** @class */ (function () {
function ECPolylineShape() {
this.smooth = 0
this.smoothConstraint = true
}
return ECPolylineShape
})()
var ECPolyline = /** @class */ (function (_super) {
__extends(ECPolyline, _super)
function ECPolyline(opts) {
var _this = _super.call(this, opts) || this
_this.type = 'ec-polyline'
return _this
}
ECPolyline.prototype.getDefaultStyle = function () {
return { stroke: '#000', fill: null }
}
ECPolyline.prototype.getDefaultShape = function () {
return new ECPolylineShape()
}
ECPolyline.prototype.buildPath = function (ctx, shape) {
var points = shape.points
var i = 0
var len = points.length / 2 // const result = getBoundingBox(points, shape.smoothConstraint);
if (shape.connectNulls) {
// Must remove first and last null values avoid draw error in polygon
for (; len > 0; len--) {
if (
!isPointNull$1(points[len * 2 - 2], points[len * 2 - 1])
) {
break
}
}
for (; i < len; i++) {
if (!isPointNull$1(points[i * 2], points[i * 2 + 1])) {
break
}
}
}
while (i < len) {
i +=
drawSegment(
ctx,
points,
i,
len,
len,
1,
shape.smooth,
shape.smoothMonotone,
shape.connectNulls
) + 1
}
}
ECPolyline.prototype.getPointOn = function (xOrY, dim) {
if (!this.path) {
this.createPathProxy()
this.buildPath(this.path, this.shape)
}
var path = this.path
var data = path.data
var CMD = PathProxy$1.CMD
var x0
var y0
var isDimX = dim === 'x'
var roots = []
for (var i = 0; i < data.length; ) {
var cmd = data[i++]
var x = void 0
var y = void 0
var x2 = void 0
var y2 = void 0
var x3 = void 0
var y3 = void 0
var t = void 0
switch (cmd) {
case CMD.M:
x0 = data[i++]
y0 = data[i++]
break
case CMD.L:
x = data[i++]
y = data[i++]
t = isDimX ? (xOrY - x0) / (x - x0) : (xOrY - y0) / (y - y0)
if (t <= 1 && t >= 0) {
var val = isDimX ? (y - y0) * t + y0 : (x - x0) * t + x0
return isDimX ? [xOrY, val] : [val, xOrY]
}
x0 = x
y0 = y
break
case CMD.C:
x = data[i++]
y = data[i++]
x2 = data[i++]
y2 = data[i++]
x3 = data[i++]
y3 = data[i++]
var nRoot = isDimX
? cubicRootAt(x0, x, x2, x3, xOrY, roots)
: cubicRootAt(y0, y, y2, y3, xOrY, roots)
if (nRoot > 0) {
for (var i_1 = 0; i_1 < nRoot; i_1++) {
var t_1 = roots[i_1]
if (t_1 <= 1 && t_1 >= 0) {
var val = isDimX
? cubicAt(y0, y, y2, y3, t_1)
: cubicAt(x0, x, x2, x3, t_1)
return isDimX ? [xOrY, val] : [val, xOrY]
}
}
}
x0 = x3
y0 = y3
break
}
}
}
return ECPolyline
})(Path$1)
var ECPolygonShape = /** @class */ (function (_super) {
__extends(ECPolygonShape, _super)
function ECPolygonShape() {
return (_super !== null && _super.apply(this, arguments)) || this
}
return ECPolygonShape
})(ECPolylineShape)
var ECPolygon = /** @class */ (function (_super) {
__extends(ECPolygon, _super)
function ECPolygon(opts) {
var _this = _super.call(this, opts) || this
_this.type = 'ec-polygon'
return _this
}
ECPolygon.prototype.getDefaultShape = function () {
return new ECPolygonShape()
}
ECPolygon.prototype.buildPath = function (ctx, shape) {
var points = shape.points
var stackedOnPoints = shape.stackedOnPoints
var i = 0
var len = points.length / 2
var smoothMonotone = shape.smoothMonotone
if (shape.connectNulls) {
// Must remove first and last null values avoid draw error in polygon
for (; len > 0; len--) {
if (
!isPointNull$1(points[len * 2 - 2], points[len * 2 - 1])
) {
break
}
}
for (; i < len; i++) {
if (!isPointNull$1(points[i * 2], points[i * 2 + 1])) {
break
}
}
}
while (i < len) {
var k = drawSegment(
ctx,
points,
i,
len,
len,
1,
shape.smooth,
smoothMonotone,
shape.connectNulls
)
drawSegment(
ctx,
stackedOnPoints,
i + k - 1,
k,
len,
-1,
shape.stackedOnSmooth,
smoothMonotone,
shape.connectNulls
)
i += k + 1
ctx.closePath()
}
}
return ECPolygon
})(Path$1)
function createGridClipPath(
cartesian,
hasAnimation,
seriesModel,
done,
during
) {
var rect = cartesian.getArea()
var x = rect.x
var y = rect.y
var width = rect.width
var height = rect.height
var lineWidth = seriesModel.get(['lineStyle', 'width']) || 2 // Expand the clip path a bit to avoid the border is clipped and looks thinner
x -= lineWidth / 2
y -= lineWidth / 2
width += lineWidth
height += lineWidth // fix: https://github.com/apache/incubator-echarts/issues/11369
x = Math.floor(x)
width = Math.round(width)
var clipPath = new Rect$2({
shape: { x: x, y: y, width: width, height: height }
})
if (hasAnimation) {
var baseAxis = cartesian.getBaseAxis()
var isHorizontal = baseAxis.isHorizontal()
var isAxisInversed = baseAxis.inverse
if (isHorizontal) {
if (isAxisInversed) {
clipPath.shape.x += width
}
clipPath.shape.width = 0
} else {
if (!isAxisInversed) {
clipPath.shape.y += height
}
clipPath.shape.height = 0
}
var duringCb = isFunction(during)
? function (percent) {
during(percent, clipPath)
}
: null
initProps(
clipPath,
{ shape: { width: width, height: height, x: x, y: y } },
seriesModel,
null,
done,
duringCb
)
}
return clipPath
}
function createPolarClipPath(polar, hasAnimation, seriesModel) {
var sectorArea = polar.getArea() // Avoid float number rounding error for symbol on the edge of axis extent.
var r0 = round$1(sectorArea.r0, 1)
var r = round$1(sectorArea.r, 1)
var clipPath = new Sector$1({
shape: {
cx: round$1(polar.cx, 1),
cy: round$1(polar.cy, 1),
r0: r0,
r: r,
startAngle: sectorArea.startAngle,
endAngle: sectorArea.endAngle,
clockwise: sectorArea.clockwise
}
})
if (hasAnimation) {
var isRadial = polar.getBaseAxis().dim === 'angle'
if (isRadial) {
clipPath.shape.endAngle = sectorArea.startAngle
} else {
clipPath.shape.r = r0
}
initProps(
clipPath,
{ shape: { endAngle: sectorArea.endAngle, r: r } },
seriesModel
)
}
return clipPath
}
function createClipPath(
coordSys,
hasAnimation,
seriesModel,
done,
during
) {
if (!coordSys) {
return null
} else if (coordSys.type === 'polar') {
return createPolarClipPath(coordSys, hasAnimation, seriesModel)
} else if (coordSys.type === 'cartesian2d') {
return createGridClipPath(
coordSys,
hasAnimation,
seriesModel,
done,
during
)
}
return 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 isCoordinateSystemType(coordSys, type) {
return coordSys.type === type
}
function isPointsSame(points1, points2) {
if (points1.length !== points2.length) {
return
}
for (var i = 0; i < points1.length; i++) {
if (points1[i] !== points2[i]) {
return
}
}
return true
}
function bboxFromPoints(points) {
var minX = Infinity
var minY = Infinity
var maxX = -Infinity
var maxY = -Infinity
for (var i = 0; i < points.length; ) {
var x = points[i++]
var y = points[i++]
if (!isNaN(x)) {
minX = Math.min(x, minX)
maxX = Math.max(x, maxX)
}
if (!isNaN(y)) {
minY = Math.min(y, minY)
maxY = Math.max(y, maxY)
}
}
return [
[minX, minY],
[maxX, maxY]
]
}
function getBoundingDiff(points1, points2) {
var _a = bboxFromPoints(points1),
min1 = _a[0],
max1 = _a[1]
var _b = bboxFromPoints(points2),
min2 = _b[0],
max2 = _b[1] // Get a max value from each corner of two boundings.
return Math.max(
Math.abs(min1[0] - min2[0]),
Math.abs(min1[1] - min2[1]),
Math.abs(max1[0] - max2[0]),
Math.abs(max1[1] - max2[1])
)
}
function getSmooth(smooth) {
return isNumber(smooth) ? smooth : smooth ? 0.5 : 0
}
function getStackedOnPoints(coordSys, data, dataCoordInfo) {
if (!dataCoordInfo.valueDim) {
return []
}
var len = data.count()
var points = createFloat32Array(len * 2)
for (var idx = 0; idx < len; idx++) {
var pt = getStackedOnPoint(dataCoordInfo, coordSys, data, idx)
points[idx * 2] = pt[0]
points[idx * 2 + 1] = pt[1]
}
return points
}
function turnPointsIntoStep(
points,
coordSys,
stepTurnAt,
connectNulls
) {
var baseAxis = coordSys.getBaseAxis()
var baseIndex =
baseAxis.dim === 'x' || baseAxis.dim === 'radius' ? 0 : 1
var stepPoints = []
var i = 0
var stepPt = []
var pt = []
var nextPt = []
var filteredPoints = []
if (connectNulls) {
for (i = 0; i < points.length; i += 2) {
if (!isNaN(points[i]) && !isNaN(points[i + 1])) {
filteredPoints.push(points[i], points[i + 1])
}
}
points = filteredPoints
}
for (i = 0; i < points.length - 2; i += 2) {
nextPt[0] = points[i + 2]
nextPt[1] = points[i + 3]
pt[0] = points[i]
pt[1] = points[i + 1]
stepPoints.push(pt[0], pt[1])
switch (stepTurnAt) {
case 'end':
stepPt[baseIndex] = nextPt[baseIndex]
stepPt[1 - baseIndex] = pt[1 - baseIndex]
stepPoints.push(stepPt[0], stepPt[1])
break
case 'middle':
var middle = (pt[baseIndex] + nextPt[baseIndex]) / 2
var stepPt2 = []
stepPt[baseIndex] = stepPt2[baseIndex] = middle
stepPt[1 - baseIndex] = pt[1 - baseIndex]
stepPt2[1 - baseIndex] = nextPt[1 - baseIndex]
stepPoints.push(stepPt[0], stepPt[1])
stepPoints.push(stepPt2[0], stepPt2[1])
break
default:
// default is start
stepPt[baseIndex] = pt[baseIndex]
stepPt[1 - baseIndex] = nextPt[1 - baseIndex]
stepPoints.push(stepPt[0], stepPt[1])
}
} // Last points
stepPoints.push(points[i++], points[i++])
return stepPoints
}
/**
* Clip color stops to edge. Avoid creating too large gradients.
* Which may lead to blurry when GPU acceleration is enabled. See #15680
*
* The stops has been sorted from small to large.
*/ function clipColorStops(colorStops, maxSize) {
var newColorStops = []
var len = colorStops.length // coord will always < 0 in prevOutOfRangeColorStop.
var prevOutOfRangeColorStop
var prevInRangeColorStop
function lerpStop(stop0, stop1, clippedCoord) {
var coord0 = stop0.coord
var p = (clippedCoord - coord0) / (stop1.coord - coord0)
var color = lerp(p, [stop0.color, stop1.color])
return { coord: clippedCoord, color: color }
}
for (var i = 0; i < len; i++) {
var stop_1 = colorStops[i]
var coord = stop_1.coord
if (coord < 0) {
prevOutOfRangeColorStop = stop_1
} else if (coord > maxSize) {
if (prevInRangeColorStop) {
newColorStops.push(
lerpStop(prevInRangeColorStop, stop_1, maxSize)
)
} else if (prevOutOfRangeColorStop) {
// If there are two stops and coord range is between these two stops
newColorStops.push(
lerpStop(prevOutOfRangeColorStop, stop_1, 0),
lerpStop(prevOutOfRangeColorStop, stop_1, maxSize)
)
} // All following stop will be out of range. So just ignore them.
break
} else {
if (prevOutOfRangeColorStop) {
newColorStops.push(
lerpStop(prevOutOfRangeColorStop, stop_1, 0)
) // Reset
prevOutOfRangeColorStop = null
}
newColorStops.push(stop_1)
prevInRangeColorStop = stop_1
}
}
return newColorStops
}
function getVisualGradient(data, coordSys, api) {
var visualMetaList = data.getVisual('visualMeta')
if (!visualMetaList || !visualMetaList.length || !data.count()) {
// When data.count() is 0, gradient range can not be calculated.
return
}
if (coordSys.type !== 'cartesian2d') {
return
}
var coordDim
var visualMeta
for (var i = visualMetaList.length - 1; i >= 0; i--) {
var dimInfo = data.getDimensionInfo(visualMetaList[i].dimension)
coordDim = dimInfo && dimInfo.coordDim // Can only be x or y
if (coordDim === 'x' || coordDim === 'y') {
visualMeta = visualMetaList[i]
break
}
}
if (!visualMeta) {
return
} // If the area to be rendered is bigger than area defined by LinearGradient,
// the canvas spec prescribes that the color of the first stop and the last
// stop should be used. But if two stops are added at offset 0, in effect
// browsers use the color of the second stop to render area outside
// LinearGradient. So we can only infinitesimally extend area defined in
// LinearGradient to render `outerColors`.
var axis = coordSys.getAxis(coordDim) // dataToCoord mapping may not be linear, but must be monotonic.
var colorStops = map$1(visualMeta.stops, function (stop) {
// offset will be calculated later.
return {
coord: axis.toGlobalCoord(axis.dataToCoord(stop.value)),
color: stop.color
}
})
var stopLen = colorStops.length
var outerColors = visualMeta.outerColors.slice()
if (
stopLen &&
colorStops[0].coord > colorStops[stopLen - 1].coord
) {
colorStops.reverse()
outerColors.reverse()
}
var colorStopsInRange = clipColorStops(
colorStops,
coordDim === 'x' ? api.getWidth() : api.getHeight()
)
var inRangeStopLen = colorStopsInRange.length
if (!inRangeStopLen && stopLen) {
// All stops are out of range. All will be the same color.
return colorStops[0].coord < 0
? outerColors[1]
? outerColors[1]
: colorStops[stopLen - 1].color
: outerColors[0]
? outerColors[0]
: colorStops[0].color
}
var tinyExtent = 10 // Arbitrary value: 10px
var minCoord = colorStopsInRange[0].coord - tinyExtent
var maxCoord =
colorStopsInRange[inRangeStopLen - 1].coord + tinyExtent
var coordSpan = maxCoord - minCoord
if (coordSpan < 1e-3) {
return 'transparent'
}
each$7(colorStopsInRange, function (stop) {
stop.offset = (stop.coord - minCoord) / coordSpan
})
colorStopsInRange.push({
// NOTE: inRangeStopLen may still be 0 if stoplen is zero.
offset: inRangeStopLen
? colorStopsInRange[inRangeStopLen - 1].offset
: 0.5,
color: outerColors[1] || 'transparent'
})
colorStopsInRange.unshift({
offset: inRangeStopLen ? colorStopsInRange[0].offset : 0.5,
color: outerColors[0] || 'transparent'
})
var gradient = new LinearGradient$1(
0,
0,
0,
0,
colorStopsInRange,
true
)
gradient[coordDim] = minCoord
gradient[coordDim + '2'] = maxCoord
return gradient
}
function getIsIgnoreFunc(seriesModel, data, coordSys) {
var showAllSymbol = seriesModel.get('showAllSymbol')
var isAuto = showAllSymbol === 'auto'
if (showAllSymbol && !isAuto) {
return
}
var categoryAxis = coordSys.getAxesByScale('ordinal')[0]
if (!categoryAxis) {
return
} // Note that category label interval strategy might bring some weird effect
// in some scenario: users may wonder why some of the symbols are not
// displayed. So we show all symbols as possible as we can.
if (
isAuto && // Simplify the logic, do not determine label overlap here.
canShowAllSymbolForCategory(categoryAxis, data)
) {
return
} // Otherwise follow the label interval strategy on category axis.
var categoryDataDim = data.mapDimension(categoryAxis.dim)
var labelMap = {}
each$7(categoryAxis.getViewLabels(), function (labelItem) {
var ordinalNumber = categoryAxis.scale.getRawOrdinalNumber(
labelItem.tickValue
)
labelMap[ordinalNumber] = 1
})
return function (dataIndex) {
return !labelMap.hasOwnProperty(
data.get(categoryDataDim, dataIndex)
)
}
}
function canShowAllSymbolForCategory(categoryAxis, data) {
// In mose cases, line is monotonous on category axis, and the label size
// is close with each other. So we check the symbol size and some of the
// label size alone with the category axis to estimate whether all symbol
// can be shown without overlap.
var axisExtent = categoryAxis.getExtent()
var availSize =
Math.abs(axisExtent[1] - axisExtent[0]) /
categoryAxis.scale.count()
isNaN(availSize) && (availSize = 0) // 0/0 is NaN.
// Sampling some points, max 5.
var dataLen = data.count()
var step = Math.max(1, Math.round(dataLen / 5))
for (var dataIndex = 0; dataIndex < dataLen; dataIndex += step) {
if (
SymbolClz.getSymbolSize(
data,
dataIndex // Only for cartesian, where `isHorizontal` exists.
)[categoryAxis.isHorizontal() ? 1 : 0] * // Empirical number
1.5 >
availSize
) {
return false
}
}
return true
}
function isPointNull(x, y) {
return isNaN(x) || isNaN(y)
}
function getLastIndexNotNull(points) {
var len = points.length / 2
for (; len > 0; len--) {
if (!isPointNull(points[len * 2 - 2], points[len * 2 - 1])) {
break
}
}
return len - 1
}
function getPointAtIndex(points, idx) {
return [points[idx * 2], points[idx * 2 + 1]]
}
function getIndexRange(points, xOrY, dim) {
var len = points.length / 2
var dimIdx = dim === 'x' ? 0 : 1
var a
var b
var prevIndex = 0
var nextIndex = -1
for (var i = 0; i < len; i++) {
b = points[i * 2 + dimIdx]
if (isNaN(b) || isNaN(points[i * 2 + 1 - dimIdx])) {
continue
}
if (i === 0) {
a = b
continue
}
if ((a <= xOrY && b >= xOrY) || (a >= xOrY && b <= xOrY)) {
nextIndex = i
break
}
prevIndex = i
a = b
}
return { range: [prevIndex, nextIndex], t: (xOrY - a) / (b - a) }
}
function anyStateShowEndLabel(seriesModel) {
if (seriesModel.get(['endLabel', 'show'])) {
return true
}
for (var i = 0; i < SPECIAL_STATES.length; i++) {
if (seriesModel.get([SPECIAL_STATES[i], 'endLabel', 'show'])) {
return true
}
}
return false
}
function createLineClipPath(
lineView,
coordSys,
hasAnimation,
seriesModel
) {
if (isCoordinateSystemType(coordSys, 'cartesian2d')) {
var endLabelModel_1 = seriesModel.getModel('endLabel')
var valueAnimation_1 = endLabelModel_1.get('valueAnimation')
var data_1 = seriesModel.getData()
var labelAnimationRecord_1 = { lastFrameIndex: 0 }
var during = anyStateShowEndLabel(seriesModel)
? function (percent, clipRect) {
lineView._endLabelOnDuring(
percent,
clipRect,
data_1,
labelAnimationRecord_1,
valueAnimation_1,
endLabelModel_1,
coordSys
)
}
: null
var isHorizontal = coordSys.getBaseAxis().isHorizontal()
var clipPath = createGridClipPath(
coordSys,
hasAnimation,
seriesModel,
function () {
var endLabel = lineView._endLabel
if (endLabel && hasAnimation) {
if (labelAnimationRecord_1.originalX != null) {
endLabel.attr({
x: labelAnimationRecord_1.originalX,
y: labelAnimationRecord_1.originalY
})
}
}
},
during
) // Expand clip shape to avoid clipping when line value exceeds axis
if (!seriesModel.get('clip', true)) {
var rectShape = clipPath.shape
var expandSize = Math.max(rectShape.width, rectShape.height)
if (isHorizontal) {
rectShape.y -= expandSize
rectShape.height += expandSize * 2
} else {
rectShape.x -= expandSize
rectShape.width += expandSize * 2
}
} // Set to the final frame. To make sure label layout is right.
if (during) {
during(1, clipPath)
}
return clipPath
} else {
return createPolarClipPath(coordSys, hasAnimation, seriesModel)
}
}
function getEndLabelStateSpecified(endLabelModel, coordSys) {
var baseAxis = coordSys.getBaseAxis()
var isHorizontal = baseAxis.isHorizontal()
var isBaseInversed = baseAxis.inverse
var align = isHorizontal
? isBaseInversed
? 'right'
: 'left'
: 'center'
var verticalAlign = isHorizontal
? 'middle'
: isBaseInversed
? 'top'
: 'bottom'
return {
normal: {
align: endLabelModel.get('align') || align,
verticalAlign:
endLabelModel.get('verticalAlign') || verticalAlign
}
}
}
var LineView = /** @class */ (function (_super) {
__extends(LineView, _super)
function LineView() {
return (_super !== null && _super.apply(this, arguments)) || this
}
LineView.prototype.init = function () {
var lineGroup = new Group$3()
var symbolDraw = new SymbolDraw$1()
this.group.add(symbolDraw.group)
this._symbolDraw = symbolDraw
this._lineGroup = lineGroup
}
LineView.prototype.render = function (seriesModel, ecModel, api) {
var _this = this
var coordSys = seriesModel.coordinateSystem
var group = this.group
var data = seriesModel.getData()
var lineStyleModel = seriesModel.getModel('lineStyle')
var areaStyleModel = seriesModel.getModel('areaStyle')
var points = data.getLayout('points') || []
var isCoordSysPolar = coordSys.type === 'polar'
var prevCoordSys = this._coordSys
var symbolDraw = this._symbolDraw
var polyline = this._polyline
var polygon = this._polygon
var lineGroup = this._lineGroup
var hasAnimation = seriesModel.get('animation')
var isAreaChart = !areaStyleModel.isEmpty()
var valueOrigin = areaStyleModel.get('origin')
var dataCoordInfo = prepareDataCoordInfo(
coordSys,
data,
valueOrigin
)
var stackedOnPoints =
isAreaChart && getStackedOnPoints(coordSys, data, dataCoordInfo)
var showSymbol = seriesModel.get('showSymbol')
var connectNulls = seriesModel.get('connectNulls')
var isIgnoreFunc =
showSymbol &&
!isCoordSysPolar &&
getIsIgnoreFunc(seriesModel, data, coordSys) // Remove temporary symbols
var oldData = this._data
oldData &&
oldData.eachItemGraphicEl(function (el, idx) {
if (el.__temp) {
group.remove(el)
oldData.setItemGraphicEl(idx, null)
}
}) // Remove previous created symbols if showSymbol changed to false
if (!showSymbol) {
symbolDraw.remove()
}
group.add(lineGroup) // FIXME step not support polar
var step = !isCoordSysPolar ? seriesModel.get('step') : false
var clipShapeForSymbol
if (
coordSys &&
coordSys.getArea &&
seriesModel.get('clip', true)
) {
clipShapeForSymbol = coordSys.getArea() // Avoid float number rounding error for symbol on the edge of axis extent.
// See #7913 and `test/dataZoom-clip.html`.
if (clipShapeForSymbol.width != null) {
clipShapeForSymbol.x -= 0.1
clipShapeForSymbol.y -= 0.1
clipShapeForSymbol.width += 0.2
clipShapeForSymbol.height += 0.2
} else if (clipShapeForSymbol.r0) {
clipShapeForSymbol.r0 -= 0.5
clipShapeForSymbol.r += 0.5
}
}
this._clipShapeForSymbol = clipShapeForSymbol
var visualColor =
getVisualGradient(data, coordSys, api) ||
data.getVisual('style')[data.getVisual('drawType')] // Initialization animation or coordinate system changed
if (
!(
polyline &&
prevCoordSys.type === coordSys.type &&
step === this._step
)
) {
showSymbol &&
symbolDraw.updateData(data, {
isIgnore: isIgnoreFunc,
clipShape: clipShapeForSymbol,
disableAnimation: true,
getSymbolPoint: function getSymbolPoint(idx) {
return [points[idx * 2], points[idx * 2 + 1]]
}
})
hasAnimation &&
this._initSymbolLabelAnimation(
data,
coordSys,
clipShapeForSymbol
)
if (step) {
// TODO If stacked series is not step
points = turnPointsIntoStep(
points,
coordSys,
step,
connectNulls
)
if (stackedOnPoints) {
stackedOnPoints = turnPointsIntoStep(
stackedOnPoints,
coordSys,
step,
connectNulls
)
}
}
polyline = this._newPolyline(points)
if (isAreaChart) {
polygon = this._newPolygon(points, stackedOnPoints)
} // If areaStyle is removed
else if (polygon) {
lineGroup.remove(polygon)
polygon = this._polygon = null
} // NOTE: Must update _endLabel before setClipPath.
if (!isCoordSysPolar) {
this._initOrUpdateEndLabel(
seriesModel,
coordSys,
convertToColorString(visualColor)
)
}
lineGroup.setClipPath(
createLineClipPath(this, coordSys, true, seriesModel)
)
} else {
if (isAreaChart && !polygon) {
// If areaStyle is added
polygon = this._newPolygon(points, stackedOnPoints)
} else if (polygon && !isAreaChart) {
// If areaStyle is removed
lineGroup.remove(polygon)
polygon = this._polygon = null
} // NOTE: Must update _endLabel before setClipPath.
if (!isCoordSysPolar) {
this._initOrUpdateEndLabel(
seriesModel,
coordSys,
convertToColorString(visualColor)
)
} // Update clipPath
var oldClipPath = lineGroup.getClipPath()
if (oldClipPath) {
var newClipPath = createLineClipPath(
this,
coordSys,
false,
seriesModel
)
initProps(
oldClipPath,
{ shape: newClipPath.shape },
seriesModel
)
} else {
lineGroup.setClipPath(
createLineClipPath(this, coordSys, true, seriesModel)
)
} // Always update, or it is wrong in the case turning on legend
// because points are not changed
showSymbol &&
symbolDraw.updateData(data, {
isIgnore: isIgnoreFunc,
clipShape: clipShapeForSymbol,
disableAnimation: true,
getSymbolPoint: function getSymbolPoint(idx) {
return [points[idx * 2], points[idx * 2 + 1]]
}
}) // In the case data zoom triggerred refreshing frequently
// Data may not change if line has a category axis. So it should animate nothing
if (
!isPointsSame(this._stackedOnPoints, stackedOnPoints) ||
!isPointsSame(this._points, points)
) {
if (hasAnimation) {
this._doUpdateAnimation(
data,
stackedOnPoints,
coordSys,
api,
step,
valueOrigin,
connectNulls
)
} else {
// Not do it in update with animation
if (step) {
// TODO If stacked series is not step
points = turnPointsIntoStep(
points,
coordSys,
step,
connectNulls
)
if (stackedOnPoints) {
stackedOnPoints = turnPointsIntoStep(
stackedOnPoints,
coordSys,
step,
connectNulls
)
}
}
polyline.setShape({ points: points })
polygon &&
polygon.setShape({
points: points,
stackedOnPoints: stackedOnPoints
})
}
}
}
var emphasisModel = seriesModel.getModel('emphasis')
var focus = emphasisModel.get('focus')
var blurScope = emphasisModel.get('blurScope')
var emphasisDisabled = emphasisModel.get('disabled')
polyline.useStyle(
defaults(
// Use color in lineStyle first
lineStyleModel.getLineStyle(),
{ fill: 'none', stroke: visualColor, lineJoin: 'bevel' }
)
)
setStatesStylesFromModel(polyline, seriesModel, 'lineStyle')
if (
polyline.style.lineWidth > 0 &&
seriesModel.get(['emphasis', 'lineStyle', 'width']) === 'bolder'
) {
var emphasisLineStyle = polyline.getState('emphasis').style
emphasisLineStyle.lineWidth = +polyline.style.lineWidth + 1
} // Needs seriesIndex for focus
getECData(polyline).seriesIndex = seriesModel.seriesIndex
toggleHoverEmphasis(polyline, focus, blurScope, emphasisDisabled)
var smooth = getSmooth(seriesModel.get('smooth'))
var smoothMonotone = seriesModel.get('smoothMonotone')
polyline.setShape({
smooth: smooth,
smoothMonotone: smoothMonotone,
connectNulls: connectNulls
})
if (polygon) {
var stackedOnSeries = data.getCalculationInfo('stackedOnSeries')
var stackedOnSmooth = 0
polygon.useStyle(
defaults(areaStyleModel.getAreaStyle(), {
fill: visualColor,
opacity: 0.7,
lineJoin: 'bevel',
decal: data.getVisual('style').decal
})
)
if (stackedOnSeries) {
stackedOnSmooth = getSmooth(stackedOnSeries.get('smooth'))
}
polygon.setShape({
smooth: smooth,
stackedOnSmooth: stackedOnSmooth,
smoothMonotone: smoothMonotone,
connectNulls: connectNulls
})
setStatesStylesFromModel(polygon, seriesModel, 'areaStyle') // Needs seriesIndex for focus
getECData(polygon).seriesIndex = seriesModel.seriesIndex
toggleHoverEmphasis(polygon, focus, blurScope, emphasisDisabled)
}
var changePolyState = function changePolyState(toState) {
_this._changePolyState(toState)
}
data.eachItemGraphicEl(function (el) {
// Switch polyline / polygon state if element changed its state.
el && (el.onHoverStateChange = changePolyState)
})
this._polyline.onHoverStateChange = changePolyState
this._data = data // Save the coordinate system for transition animation when data changed
this._coordSys = coordSys
this._stackedOnPoints = stackedOnPoints
this._points = points
this._step = step
this._valueOrigin = valueOrigin
if (seriesModel.get('triggerLineEvent')) {
this.packEventData(seriesModel, polyline)
polygon && this.packEventData(seriesModel, polygon)
}
}
LineView.prototype.packEventData = function (seriesModel, el) {
getECData(el).eventData = {
componentType: 'series',
componentSubType: 'line',
componentIndex: seriesModel.componentIndex,
seriesIndex: seriesModel.seriesIndex,
seriesName: seriesModel.name,
seriesType: 'line'
}
}
LineView.prototype.highlight = function (
seriesModel,
ecModel,
api,
payload
) {
var data = seriesModel.getData()
var dataIndex = queryDataIndex(data, payload)
this._changePolyState('emphasis')
if (
!(dataIndex instanceof Array) &&
dataIndex != null &&
dataIndex >= 0
) {
var points = data.getLayout('points')
var symbol = data.getItemGraphicEl(dataIndex)
if (!symbol) {
// Create a temporary symbol if it is not exists
var x = points[dataIndex * 2]
var y = points[dataIndex * 2 + 1]
if (isNaN(x) || isNaN(y)) {
// Null data
return
} // fix #11360: should't draw symbol outside clipShapeForSymbol
if (
this._clipShapeForSymbol &&
!this._clipShapeForSymbol.contain(x, y)
) {
return
}
var zlevel = seriesModel.get('zlevel')
var z = seriesModel.get('z')
symbol = new SymbolClz(data, dataIndex)
symbol.x = x
symbol.y = y
symbol.setZ(zlevel, z) // ensure label text of the temporary symbol is in front of line and area polygon
var symbolLabel = symbol.getSymbolPath().getTextContent()
if (symbolLabel) {
symbolLabel.zlevel = zlevel
symbolLabel.z = z
symbolLabel.z2 = this._polyline.z2 + 1
}
symbol.__temp = true
data.setItemGraphicEl(dataIndex, symbol) // Stop scale animation
symbol.stopSymbolAnimation(true)
this.group.add(symbol)
}
symbol.highlight()
} else {
// Highlight whole series
ChartView$1.prototype.highlight.call(
this,
seriesModel,
ecModel,
api,
payload
)
}
}
LineView.prototype.downplay = function (
seriesModel,
ecModel,
api,
payload
) {
var data = seriesModel.getData()
var dataIndex = queryDataIndex(data, payload)
this._changePolyState('normal')
if (dataIndex != null && dataIndex >= 0) {
var symbol = data.getItemGraphicEl(dataIndex)
if (symbol) {
if (symbol.__temp) {
data.setItemGraphicEl(dataIndex, null)
this.group.remove(symbol)
} else {
symbol.downplay()
}
}
} else {
// FIXME
// can not downplay completely.
// Downplay whole series
ChartView$1.prototype.downplay.call(
this,
seriesModel,
ecModel,
api,
payload
)
}
}
LineView.prototype._changePolyState = function (toState) {
var polygon = this._polygon
setStatesFlag(this._polyline, toState)
polygon && setStatesFlag(polygon, toState)
}
LineView.prototype._newPolyline = function (points) {
var polyline = this._polyline // Remove previous created polyline
if (polyline) {
this._lineGroup.remove(polyline)
}
polyline = new ECPolyline({
shape: { points: points },
segmentIgnoreThreshold: 2,
z2: 10
})
this._lineGroup.add(polyline)
this._polyline = polyline
return polyline
}
LineView.prototype._newPolygon = function (
points,
stackedOnPoints
) {
var polygon = this._polygon // Remove previous created polygon
if (polygon) {
this._lineGroup.remove(polygon)
}
polygon = new ECPolygon({
shape: { points: points, stackedOnPoints: stackedOnPoints },
segmentIgnoreThreshold: 2
})
this._lineGroup.add(polygon)
this._polygon = polygon
return polygon
}
LineView.prototype._initSymbolLabelAnimation = function (
data,
coordSys,
clipShape
) {
var isHorizontalOrRadial
var isCoordSysPolar
var baseAxis = coordSys.getBaseAxis()
var isAxisInverse = baseAxis.inverse
if (coordSys.type === 'cartesian2d') {
isHorizontalOrRadial = baseAxis.isHorizontal()
isCoordSysPolar = false
} else if (coordSys.type === 'polar') {
isHorizontalOrRadial = baseAxis.dim === 'angle'
isCoordSysPolar = true
}
var seriesModel = data.hostModel
var seriesDuration = seriesModel.get('animationDuration')
if (isFunction(seriesDuration)) {
seriesDuration = seriesDuration(null)
}
var seriesDalay = seriesModel.get('animationDelay') || 0
var seriesDalayValue = isFunction(seriesDalay)
? seriesDalay(null)
: seriesDalay
data.eachItemGraphicEl(function (symbol, idx) {
var el = symbol
if (el) {
var point = [symbol.x, symbol.y]
var start = void 0
var end = void 0
var current = void 0
if (clipShape) {
if (isCoordSysPolar) {
var polarClip = clipShape
var coord = coordSys.pointToCoord(point)
if (isHorizontalOrRadial) {
start = polarClip.startAngle
end = polarClip.endAngle
current = (-coord[1] / 180) * Math.PI
} else {
start = polarClip.r0
end = polarClip.r
current = coord[0]
}
} else {
var gridClip = clipShape
if (isHorizontalOrRadial) {
start = gridClip.x
end = gridClip.x + gridClip.width
current = symbol.x
} else {
start = gridClip.y + gridClip.height
end = gridClip.y
current = symbol.y
}
}
}
var ratio =
end === start ? 0 : (current - start) / (end - start)
if (isAxisInverse) {
ratio = 1 - ratio
}
var delay = isFunction(seriesDalay)
? seriesDalay(idx)
: seriesDuration * ratio + seriesDalayValue
var symbolPath = el.getSymbolPath()
var text = symbolPath.getTextContent()
el.attr({ scaleX: 0, scaleY: 0 })
el.animateTo(
{ scaleX: 1, scaleY: 1 },
{ duration: 200, setToFinal: true, delay: delay }
)
if (text) {
text.animateFrom(
{ style: { opacity: 0 } },
{ duration: 300, delay: delay }
)
}
symbolPath.disableLabelAnimation = true
}
})
}
LineView.prototype._initOrUpdateEndLabel = function (
seriesModel,
coordSys,
inheritColor
) {
var endLabelModel = seriesModel.getModel('endLabel')
if (anyStateShowEndLabel(seriesModel)) {
var data_2 = seriesModel.getData()
var polyline = this._polyline // series may be filtered.
var points = data_2.getLayout('points')
if (!points) {
polyline.removeTextContent()
this._endLabel = null
return
}
var endLabel = this._endLabel
if (!endLabel) {
endLabel = this._endLabel = new ZRText$1({
z2: 200 // should be higher than item symbol
})
endLabel.ignoreClip = true
polyline.setTextContent(this._endLabel)
polyline.disableLabelAnimation = true
} // Find last non-NaN data to display data
var dataIndex = getLastIndexNotNull(points)
if (dataIndex >= 0) {
setLabelStyle(
polyline,
getLabelStatesModels(seriesModel, 'endLabel'),
{
inheritColor: inheritColor,
labelFetcher: seriesModel,
labelDataIndex: dataIndex,
defaultText: function defaultText(
dataIndex,
opt,
interpolatedValue
) {
return interpolatedValue != null
? getDefaultInterpolatedLabel(
data_2,
interpolatedValue
)
: getDefaultLabel(data_2, dataIndex)
},
enableTextSetter: true
},
getEndLabelStateSpecified(endLabelModel, coordSys)
)
polyline.textConfig.position = null
}
} else if (this._endLabel) {
this._polyline.removeTextContent()
this._endLabel = null
}
}
LineView.prototype._endLabelOnDuring = function (
percent,
clipRect,
data,
animationRecord,
valueAnimation,
endLabelModel,
coordSys
) {
var endLabel = this._endLabel
var polyline = this._polyline
if (endLabel) {
// NOTE: Don't remove percent < 1. percent === 1 means the first frame during render.
// The label is not prepared at this time.
if (percent < 1 && animationRecord.originalX == null) {
animationRecord.originalX = endLabel.x
animationRecord.originalY = endLabel.y
}
var points = data.getLayout('points')
var seriesModel = data.hostModel
var connectNulls = seriesModel.get('connectNulls')
var precision = endLabelModel.get('precision')
var distance = endLabelModel.get('distance') || 0
var baseAxis = coordSys.getBaseAxis()
var isHorizontal = baseAxis.isHorizontal()
var isBaseInversed = baseAxis.inverse
var clipShape = clipRect.shape
var xOrY = isBaseInversed
? isHorizontal
? clipShape.x
: clipShape.y + clipShape.height
: isHorizontal
? clipShape.x + clipShape.width
: clipShape.y
var distanceX =
(isHorizontal ? distance : 0) * (isBaseInversed ? -1 : 1)
var distanceY =
(isHorizontal ? 0 : -distance) * (isBaseInversed ? -1 : 1)
var dim = isHorizontal ? 'x' : 'y'
var dataIndexRange = getIndexRange(points, xOrY, dim)
var indices = dataIndexRange.range
var diff = indices[1] - indices[0]
var value = void 0
if (diff >= 1) {
// diff > 1 && connectNulls, which is on the null data.
if (diff > 1 && !connectNulls) {
var pt = getPointAtIndex(points, indices[0])
endLabel.attr({
x: pt[0] + distanceX,
y: pt[1] + distanceY
})
valueAnimation &&
(value = seriesModel.getRawValue(indices[0]))
} else {
var pt = polyline.getPointOn(xOrY, dim)
pt &&
endLabel.attr({
x: pt[0] + distanceX,
y: pt[1] + distanceY
})
var startValue = seriesModel.getRawValue(indices[0])
var endValue = seriesModel.getRawValue(indices[1])
valueAnimation &&
(value = interpolateRawValues(
data,
precision,
startValue,
endValue,
dataIndexRange.t
))
}
animationRecord.lastFrameIndex = indices[0]
} else {
// If diff <= 0, which is the range is not found(Include NaN)
// Choose the first point or last point.
var idx =
percent === 1 || animationRecord.lastFrameIndex > 0
? indices[0]
: 0
var pt = getPointAtIndex(points, idx)
valueAnimation && (value = seriesModel.getRawValue(idx))
endLabel.attr({ x: pt[0] + distanceX, y: pt[1] + distanceY })
}
if (valueAnimation) {
labelInner(endLabel).setLabelText(value)
}
}
}
/**
* @private
*/ // FIXME Two value axis
LineView.prototype._doUpdateAnimation = function (
data,
stackedOnPoints,
coordSys,
api,
step,
valueOrigin,
connectNulls
) {
var polyline = this._polyline
var polygon = this._polygon
var seriesModel = data.hostModel
var diff = lineAnimationDiff(
this._data,
data,
this._stackedOnPoints,
stackedOnPoints,
this._coordSys,
coordSys,
this._valueOrigin
)
var current = diff.current
var stackedOnCurrent = diff.stackedOnCurrent
var next = diff.next
var stackedOnNext = diff.stackedOnNext
if (step) {
// TODO If stacked series is not step
current = turnPointsIntoStep(
diff.current,
coordSys,
step,
connectNulls
)
stackedOnCurrent = turnPointsIntoStep(
diff.stackedOnCurrent,
coordSys,
step,
connectNulls
)
next = turnPointsIntoStep(
diff.next,
coordSys,
step,
connectNulls
)
stackedOnNext = turnPointsIntoStep(
diff.stackedOnNext,
coordSys,
step,
connectNulls
)
} // Don't apply animation if diff is large.
// For better result and avoid memory explosion problems like
// https://github.com/apache/incubator-echarts/issues/12229
if (
getBoundingDiff(current, next) > 3000 ||
(polygon &&
getBoundingDiff(stackedOnCurrent, stackedOnNext) > 3000)
) {
polyline.stopAnimation()
polyline.setShape({ points: next })
if (polygon) {
polygon.stopAnimation()
polygon.setShape({
points: next,
stackedOnPoints: stackedOnNext
})
}
return
}
polyline.shape.__points = diff.current
polyline.shape.points = current
var target = { shape: { points: next } } // Also animate the original points.
// If points reference is changed when turning into step line.
if (diff.current !== current) {
target.shape.__points = diff.next
} // Stop previous animation.
polyline.stopAnimation()
updateProps$1(polyline, target, seriesModel)
if (polygon) {
polygon.setShape({
// Reuse the points with polyline.
points: current,
stackedOnPoints: stackedOnCurrent
})
polygon.stopAnimation()
updateProps$1(
polygon,
{ shape: { stackedOnPoints: stackedOnNext } },
seriesModel
) // If use attr directly in updateProps.
if (polyline.shape.points !== polygon.shape.points) {
polygon.shape.points = polyline.shape.points
}
}
var updatedDataInfo = []
var diffStatus = diff.status
for (var i = 0; i < diffStatus.length; i++) {
var cmd = diffStatus[i].cmd
if (cmd === '=') {
var el = data.getItemGraphicEl(diffStatus[i].idx1)
if (el) {
updatedDataInfo.push({
el: el,
ptIdx: i // Index of points
})
}
}
}
if (polyline.animators && polyline.animators.length) {
polyline.animators[0].during(function () {
polygon && polygon.dirtyShape()
var points = polyline.shape.__points
for (var i = 0; i < updatedDataInfo.length; i++) {
var el = updatedDataInfo[i].el
var offset = updatedDataInfo[i].ptIdx * 2
el.x = points[offset]
el.y = points[offset + 1]
el.markRedraw()
}
})
}
}
LineView.prototype.remove = function (ecModel) {
var group = this.group
var oldData = this._data
this._lineGroup.removeAll()
this._symbolDraw.remove(true) // Remove temporary created elements when highlighting
oldData &&
oldData.eachItemGraphicEl(function (el, idx) {
if (el.__temp) {
group.remove(el)
oldData.setItemGraphicEl(idx, null)
}
})
this._polyline =
this._polygon =
this._coordSys =
this._points =
this._stackedOnPoints =
this._endLabel =
this._data =
null
}
LineView.type = 'line'
return LineView
})(ChartView$1)
var LineView$1 = LineView
function pointsLayout(seriesType, forceStoreInTypedArray) {
return {
seriesType: seriesType,
plan: createRenderPlanner(),
reset: function reset(seriesModel) {
var data = seriesModel.getData()
var coordSys = seriesModel.coordinateSystem
var pipelineContext = seriesModel.pipelineContext
var useTypedArray =
forceStoreInTypedArray || pipelineContext.large
if (!coordSys) {
return
}
var dims = map$1(coordSys.dimensions, function (dim) {
return data.mapDimension(dim)
}).slice(0, 2)
var dimLen = dims.length
var stackResultDim = data.getCalculationInfo(
'stackResultDimension'
)
if (isDimensionStacked(data, dims[0])) {
dims[0] = stackResultDim
}
if (isDimensionStacked(data, dims[1])) {
dims[1] = stackResultDim
}
var store = data.getStore()
var dimIdx0 = data.getDimensionIndex(dims[0])
var dimIdx1 = data.getDimensionIndex(dims[1])
return (
dimLen && {
progress: function progress(params, data) {
var segCount = params.end - params.start
var points =
useTypedArray && createFloat32Array(segCount * dimLen)
var tmpIn = []
var tmpOut = []
for (
var i = params.start, offset = 0;
i < params.end;
i++
) {
var point = void 0
if (dimLen === 1) {
var x = store.get(dimIdx0, i) // NOTE: Make sure the second parameter is null to use default strategy.
point = coordSys.dataToPoint(x, null, tmpOut)
} else {
tmpIn[0] = store.get(dimIdx0, i)
tmpIn[1] = store.get(dimIdx1, i) // Let coordinate system to handle the NaN data.
point = coordSys.dataToPoint(tmpIn, null, tmpOut)
}
if (useTypedArray) {
points[offset++] = point[0]
points[offset++] = point[1]
} else {
data.setItemLayout(i, point.slice())
}
}
useTypedArray && data.setLayout('points', points)
}
}
)
}
}
}
var samplers = {
average: function average(frame) {
var sum = 0
var count = 0
for (var i = 0; i < frame.length; i++) {
if (!isNaN(frame[i])) {
sum += frame[i]
count++
}
} // Return NaN if count is 0
return count === 0 ? NaN : sum / count
},
sum: function sum(frame) {
var sum = 0
for (var i = 0; i < frame.length; i++) {
// Ignore NaN
sum += frame[i] || 0
}
return sum
},
max: function max(frame) {
var max = -Infinity
for (var i = 0; i < frame.length; i++) {
frame[i] > max && (max = frame[i])
} // NaN will cause illegal axis extent.
return isFinite(max) ? max : NaN
},
min: function min(frame) {
var min = Infinity
for (var i = 0; i < frame.length; i++) {
frame[i] < min && (min = frame[i])
} // NaN will cause illegal axis extent.
return isFinite(min) ? min : NaN
}, // TODO
// Median
nearest: function nearest(frame) {
return frame[0]
}
}
var indexSampler = function indexSampler(frame) {
return Math.round(frame.length / 2)
}
function dataSample(seriesType) {
return {
seriesType: seriesType, // FIXME:TS never used, so comment it
// modifyOutputEnd: true,
reset: function reset(seriesModel, ecModel, api) {
var data = seriesModel.getData()
var sampling = seriesModel.get('sampling')
var coordSys = seriesModel.coordinateSystem
var count = data.count() // Only cartesian2d support down sampling. Disable it when there is few data.
if (count > 10 && coordSys.type === 'cartesian2d' && sampling) {
var baseAxis = coordSys.getBaseAxis()
var valueAxis = coordSys.getOtherAxis(baseAxis)
var extent = baseAxis.getExtent()
var dpr = api.getDevicePixelRatio() // Coordinste system has been resized
var size = Math.abs(extent[1] - extent[0]) * (dpr || 1)
var rate = Math.round(count / size)
if (isFinite(rate) && rate > 1) {
if (sampling === 'lttb') {
seriesModel.setData(
data.lttbDownSample(
data.mapDimension(valueAxis.dim),
1 / rate
)
)
}
var sampler = void 0
if (isString(sampling)) {
sampler = samplers[sampling]
} else if (isFunction(sampling)) {
sampler = sampling
}
if (sampler) {
// Only support sample the first dim mapped from value axis.
seriesModel.setData(
data.downSample(
data.mapDimension(valueAxis.dim),
1 / rate,
sampler,
indexSampler
)
)
}
}
}
}
}
}
function install$i(registers) {
registers.registerChartView(LineView$1)
registers.registerSeriesModel(LineSeries)
registers.registerLayout(pointsLayout('line', true))
registers.registerVisual({
seriesType: 'line',
reset: function reset(seriesModel) {
var data = seriesModel.getData() // Visual coding for legend
var lineStyle = seriesModel.getModel('lineStyle').getLineStyle()
if (lineStyle && !lineStyle.stroke) {
// Fill in visual should be palette color if
// has color callback
lineStyle.stroke = data.getVisual('style').fill
}
data.setVisual('legendLineStyle', lineStyle)
}
}) // Down sample after filter
registers.registerProcessor(
registers.PRIORITY.PROCESSOR.STATISTIC,
dataSample('line')
)
}
var BaseBarSeriesModel = /** @class */ (function (_super) {
__extends(BaseBarSeriesModel, _super)
function BaseBarSeriesModel() {
var _this =
(_super !== null && _super.apply(this, arguments)) || this
_this.type = BaseBarSeriesModel.type
return _this
}
BaseBarSeriesModel.prototype.getInitialData = function (
option,
ecModel
) {
return createSeriesData(null, this, { useEncodeDefaulter: true })
}
BaseBarSeriesModel.prototype.getMarkerPosition = function (value) {
var coordSys = this.coordinateSystem
if (coordSys && coordSys.clampData) {
// PENDING if clamp ?
var pt = coordSys.dataToPoint(coordSys.clampData(value))
var data = this.getData()
var offset = data.getLayout('offset')
var size = data.getLayout('size')
var offsetIndex = coordSys.getBaseAxis().isHorizontal() ? 0 : 1
pt[offsetIndex] += offset + size / 2
return pt
}
return [NaN, NaN]
}
BaseBarSeriesModel.type = 'series.__base_bar__'
BaseBarSeriesModel.defaultOption = {
// zlevel: 0,
z: 2,
coordinateSystem: 'cartesian2d',
legendHoverLink: true, // stack: null
// Cartesian coordinate system
// xAxisIndex: 0,
// yAxisIndex: 0,
barMinHeight: 0,
barMinAngle: 0, // cursor: null,
large: false,
largeThreshold: 400,
progressive: 3e3,
progressiveChunkMode: 'mod'
}
return BaseBarSeriesModel
})(SeriesModel$1)
SeriesModel$1.registerClass(BaseBarSeriesModel)
var BaseBarSeriesModel$1 = BaseBarSeriesModel
var BarSeriesModel = /** @class */ (function (_super) {
__extends(BarSeriesModel, _super)
function BarSeriesModel() {
var _this =
(_super !== null && _super.apply(this, arguments)) || this
_this.type = BarSeriesModel.type
return _this
}
BarSeriesModel.prototype.getInitialData = function () {
return createSeriesData(null, this, {
useEncodeDefaulter: true,
createInvertedIndices: !!this.get('realtimeSort', true) || null
})
}
/**
* @override
*/ BarSeriesModel.prototype.getProgressive = function () {
// Do not support progressive in normal mode.
return this.get('large') ? this.get('progressive') : false
}
/**
* @override
*/ BarSeriesModel.prototype.getProgressiveThreshold = function () {
// Do not support progressive in normal mode.
var progressiveThreshold = this.get('progressiveThreshold')
var largeThreshold = this.get('largeThreshold')
if (largeThreshold > progressiveThreshold) {
progressiveThreshold = largeThreshold
}
return progressiveThreshold
}
BarSeriesModel.prototype.brushSelector = function (
dataIndex,
data,
selectors
) {
return selectors.rect(data.getItemLayout(dataIndex))
}
BarSeriesModel.type = 'series.bar'
BarSeriesModel.dependencies = ['grid', 'polar']
BarSeriesModel.defaultOption = inheritDefaultOption(
BaseBarSeriesModel$1.defaultOption,
{
// If clipped
// Only available on cartesian2d
clip: true,
roundCap: false,
showBackground: false,
backgroundStyle: {
color: 'rgba(180, 180, 180, 0.2)',
borderColor: null,
borderWidth: 0,
borderType: 'solid',
borderRadius: 0,
shadowBlur: 0,
shadowColor: null,
shadowOffsetX: 0,
shadowOffsetY: 0,
opacity: 1
},
select: { itemStyle: { borderColor: '#212121' } },
realtimeSort: false
}
)
return BarSeriesModel
})(BaseBarSeriesModel$1)
var BarSeries = BarSeriesModel
/**
* Sausage: similar to sector, but have half circle on both sides
*/ var SausageShape = /** @class */ (function () {
function SausageShape() {
this.cx = 0
this.cy = 0
this.r0 = 0
this.r = 0
this.startAngle = 0
this.endAngle = Math.PI * 2
this.clockwise = true
}
return SausageShape
})()
var SausagePath = /** @class */ (function (_super) {
__extends(SausagePath, _super)
function SausagePath(opts) {
var _this = _super.call(this, opts) || this
_this.type = 'sausage'
return _this
}
SausagePath.prototype.getDefaultShape = function () {
return new SausageShape()
}
SausagePath.prototype.buildPath = function (ctx, shape) {
var cx = shape.cx
var cy = shape.cy
var r0 = Math.max(shape.r0 || 0, 0)
var r = Math.max(shape.r, 0)
var dr = (r - r0) * 0.5
var rCenter = r0 + dr
var startAngle = shape.startAngle
var endAngle = shape.endAngle
var clockwise = shape.clockwise
var PI2 = Math.PI * 2
var lessThanCircle = clockwise
? endAngle - startAngle < PI2
: startAngle - endAngle < PI2
if (!lessThanCircle) {
// Normalize angles
startAngle = endAngle - (clockwise ? PI2 : -PI2)
}
var unitStartX = Math.cos(startAngle)
var unitStartY = Math.sin(startAngle)
var unitEndX = Math.cos(endAngle)
var unitEndY = Math.sin(endAngle)
if (lessThanCircle) {
ctx.moveTo(unitStartX * r0 + cx, unitStartY * r0 + cy)
ctx.arc(
unitStartX * rCenter + cx,
unitStartY * rCenter + cy,
dr,
-Math.PI + startAngle,
startAngle,
!clockwise
)
} else {
ctx.moveTo(unitStartX * r + cx, unitStartY * r + cy)
}
ctx.arc(cx, cy, r, startAngle, endAngle, !clockwise)
ctx.arc(
unitEndX * rCenter + cx,
unitEndY * rCenter + cy,
dr,
endAngle - Math.PI * 2,
endAngle - Math.PI,
!clockwise
)
if (r0 !== 0) {
ctx.arc(cx, cy, r0, endAngle, startAngle, clockwise)
} // ctx.closePath();
}
return SausagePath
})(Path$1)
var Sausage = SausagePath
function createSectorCalculateTextPosition(positionMapping, opts) {
opts = opts || {}
var isRoundCap = opts.isRoundCap
return function (out, opts, boundingRect) {
var textPosition = opts.position
if (!textPosition || textPosition instanceof Array) {
return _calculateTextPosition(out, opts, boundingRect)
}
var mappedSectorPosition = positionMapping(textPosition)
var distance = opts.distance != null ? opts.distance : 5
var sector = this.shape
var cx = sector.cx
var cy = sector.cy
var r = sector.r
var r0 = sector.r0
var middleR = (r + r0) / 2
var startAngle = sector.startAngle
var endAngle = sector.endAngle
var middleAngle = (startAngle + endAngle) / 2
var extraDist = isRoundCap ? Math.abs(r - r0) / 2 : 0
var mathCos = Math.cos
var mathSin = Math.sin // base position: top-left
var x = cx + r * mathCos(startAngle)
var y = cy + r * mathSin(startAngle)
var textAlign = 'left'
var textVerticalAlign = 'top'
switch (mappedSectorPosition) {
case 'startArc':
x = cx + (r0 - distance) * mathCos(middleAngle)
y = cy + (r0 - distance) * mathSin(middleAngle)
textAlign = 'center'
textVerticalAlign = 'top'
break
case 'insideStartArc':
x = cx + (r0 + distance) * mathCos(middleAngle)
y = cy + (r0 + distance) * mathSin(middleAngle)
textAlign = 'center'
textVerticalAlign = 'bottom'
break
case 'startAngle':
x =
cx +
middleR * mathCos(startAngle) +
adjustAngleDistanceX(
startAngle,
distance + extraDist,
false
)
y =
cy +
middleR * mathSin(startAngle) +
adjustAngleDistanceY(
startAngle,
distance + extraDist,
false
)
textAlign = 'right'
textVerticalAlign = 'middle'
break
case 'insideStartAngle':
x =
cx +
middleR * mathCos(startAngle) +
adjustAngleDistanceX(
startAngle,
-distance + extraDist,
false
)
y =
cy +
middleR * mathSin(startAngle) +
adjustAngleDistanceY(
startAngle,
-distance + extraDist,
false
)
textAlign = 'left'
textVerticalAlign = 'middle'
break
case 'middle':
x = cx + middleR * mathCos(middleAngle)
y = cy + middleR * mathSin(middleAngle)
textAlign = 'center'
textVerticalAlign = 'middle'
break
case 'endArc':
x = cx + (r + distance) * mathCos(middleAngle)
y = cy + (r + distance) * mathSin(middleAngle)
textAlign = 'center'
textVerticalAlign = 'bottom'
break
case 'insideEndArc':
x = cx + (r - distance) * mathCos(middleAngle)
y = cy + (r - distance) * mathSin(middleAngle)
textAlign = 'center'
textVerticalAlign = 'top'
break
case 'endAngle':
x =
cx +
middleR * mathCos(endAngle) +
adjustAngleDistanceX(endAngle, distance + extraDist, true)
y =
cy +
middleR * mathSin(endAngle) +
adjustAngleDistanceY(endAngle, distance + extraDist, true)
textAlign = 'left'
textVerticalAlign = 'middle'
break
case 'insideEndAngle':
x =
cx +
middleR * mathCos(endAngle) +
adjustAngleDistanceX(endAngle, -distance + extraDist, true)
y =
cy +
middleR * mathSin(endAngle) +
adjustAngleDistanceY(endAngle, -distance + extraDist, true)
textAlign = 'right'
textVerticalAlign = 'middle'
break
default:
return _calculateTextPosition(out, opts, boundingRect)
}
out = out || {}
out.x = x
out.y = y
out.align = textAlign
out.verticalAlign = textVerticalAlign
return out
}
}
function setSectorTextRotation(
sector,
textPosition,
positionMapping,
rotateType
) {
if (isNumber(rotateType)) {
// user-set rotation
sector.setTextConfig({ rotation: rotateType })
return
} else if (isArray(textPosition)) {
// user-set position, use 0 as auto rotation
sector.setTextConfig({ rotation: 0 })
return
}
var shape = sector.shape
var startAngle = shape.clockwise ? shape.startAngle : shape.endAngle
var endAngle = shape.clockwise ? shape.endAngle : shape.startAngle
var middleAngle = (startAngle + endAngle) / 2
var anchorAngle
var mappedSectorPosition = positionMapping(textPosition)
switch (mappedSectorPosition) {
case 'startArc':
case 'insideStartArc':
case 'middle':
case 'insideEndArc':
case 'endArc':
anchorAngle = middleAngle
break
case 'startAngle':
case 'insideStartAngle':
anchorAngle = startAngle
break
case 'endAngle':
case 'insideEndAngle':
anchorAngle = endAngle
break
default:
sector.setTextConfig({ rotation: 0 })
return
}
var rotate = Math.PI * 1.5 - anchorAngle
/**
* TODO: labels with rotate > Math.PI / 2 should be rotate another
* half round flipped to increase readability. However, only middle
* position supports this for now, because in other positions, the
* anchor point is not at the center of the text, so the positions
* after rotating is not as expected.
*/ if (
mappedSectorPosition === 'middle' &&
rotate > Math.PI / 2 &&
rotate < Math.PI * 1.5
) {
rotate -= Math.PI
}
sector.setTextConfig({ rotation: rotate })
}
function adjustAngleDistanceX(angle, distance, isEnd) {
return distance * Math.sin(angle) * (isEnd ? -1 : 1)
}
function adjustAngleDistanceY(angle, distance, isEnd) {
return distance * Math.cos(angle) * (isEnd ? 1 : -1)
}
var mathMax$1 = Math.max
var mathMin$1 = Math.min
function getClipArea(coord, data) {
var coordSysClipArea = coord.getArea && coord.getArea()
if (isCoordinateSystemType(coord, 'cartesian2d')) {
var baseAxis = coord.getBaseAxis() // When boundaryGap is false or using time axis. bar may exceed the grid.
// We should not clip this part.
// See test/bar2.html
if (baseAxis.type !== 'category' || !baseAxis.onBand) {
var expandWidth = data.getLayout('bandWidth')
if (baseAxis.isHorizontal()) {
coordSysClipArea.x -= expandWidth
coordSysClipArea.width += expandWidth * 2
} else {
coordSysClipArea.y -= expandWidth
coordSysClipArea.height += expandWidth * 2
}
}
}
return coordSysClipArea
}
var BarView = /** @class */ (function (_super) {
__extends(BarView, _super)
function BarView() {
var _this = _super.call(this) || this
_this.type = BarView.type
_this._isFirstFrame = true
return _this
}
BarView.prototype.render = function (
seriesModel,
ecModel,
api,
payload
) {
this._model = seriesModel
this._removeOnRenderedListener(api)
this._updateDrawMode(seriesModel)
var coordinateSystemType = seriesModel.get('coordinateSystem')
if (
coordinateSystemType === 'cartesian2d' ||
coordinateSystemType === 'polar'
) {
// Clear previously rendered progressive elements.
this._progressiveEls = null
this._isLargeDraw
? this._renderLarge(seriesModel, ecModel, api)
: this._renderNormal(seriesModel, ecModel, api, payload)
}
}
BarView.prototype.incrementalPrepareRender = function (
seriesModel
) {
this._clear()
this._updateDrawMode(seriesModel) // incremental also need to clip, otherwise might be overlow.
// But must not set clip in each frame, otherwise all of the children will be marked redraw.
this._updateLargeClip(seriesModel)
}
BarView.prototype.incrementalRender = function (
params,
seriesModel
) {
// Reset
this._progressiveEls = [] // Do not support progressive in normal mode.
this._incrementalRenderLarge(params, seriesModel)
}
BarView.prototype.eachRendered = function (cb) {
traverseElements(this._progressiveEls || this.group, cb)
}
BarView.prototype._updateDrawMode = function (seriesModel) {
var isLargeDraw = seriesModel.pipelineContext.large
if (
this._isLargeDraw == null ||
isLargeDraw !== this._isLargeDraw
) {
this._isLargeDraw = isLargeDraw
this._clear()
}
}
BarView.prototype._renderNormal = function (
seriesModel,
ecModel,
api,
payload
) {
var group = this.group
var data = seriesModel.getData()
var oldData = this._data
var coord = seriesModel.coordinateSystem
var baseAxis = coord.getBaseAxis()
var isHorizontalOrRadial
if (coord.type === 'cartesian2d') {
isHorizontalOrRadial = baseAxis.isHorizontal()
} else if (coord.type === 'polar') {
isHorizontalOrRadial = baseAxis.dim === 'angle'
}
var animationModel = seriesModel.isAnimationEnabled()
? seriesModel
: null
var realtimeSortCfg = shouldRealtimeSort(seriesModel, coord)
if (realtimeSortCfg) {
this._enableRealtimeSort(realtimeSortCfg, data, api)
}
var needsClip = seriesModel.get('clip', true) || realtimeSortCfg
var coordSysClipArea = getClipArea(coord, data) // If there is clipPath created in large mode. Remove it.
group.removeClipPath() // We don't use clipPath in normal mode because we needs a perfect animation
// And don't want the label are clipped.
var roundCap = seriesModel.get('roundCap', true)
var drawBackground = seriesModel.get('showBackground', true)
var backgroundModel = seriesModel.getModel('backgroundStyle')
var barBorderRadius = backgroundModel.get('borderRadius') || 0
var bgEls = []
var oldBgEls = this._backgroundEls
var isInitSort = payload && payload.isInitSort
var isChangeOrder = payload && payload.type === 'changeAxisOrder'
function createBackground(dataIndex) {
var bgLayout = getLayout[coord.type](data, dataIndex)
var bgEl = createBackgroundEl(
coord,
isHorizontalOrRadial,
bgLayout
)
bgEl.useStyle(backgroundModel.getItemStyle()) // Only cartesian2d support borderRadius.
if (coord.type === 'cartesian2d') {
bgEl.setShape('r', barBorderRadius)
}
bgEls[dataIndex] = bgEl
return bgEl
}
data
.diff(oldData)
.add(function (dataIndex) {
var itemModel = data.getItemModel(dataIndex)
var layout = getLayout[coord.type](data, dataIndex, itemModel)
if (drawBackground) {
createBackground(dataIndex)
} // If dataZoom in filteMode: 'empty', the baseValue can be set as NaN in "axisProxy".
if (
!data.hasValue(dataIndex) ||
!isValidLayout[coord.type](layout)
) {
return
}
var isClipped = false
if (needsClip) {
// Clip will modify the layout params.
// And return a boolean to determine if the shape are fully clipped.
isClipped = clip[coord.type](coordSysClipArea, layout)
}
var el = elementCreator[coord.type](
seriesModel,
data,
dataIndex,
layout,
isHorizontalOrRadial,
animationModel,
baseAxis.model,
false,
roundCap
)
if (realtimeSortCfg) {
/**
* Force label animation because even if the element is
* ignored because it's clipped, it may not be clipped after
* changing order. Then, if not using forceLabelAnimation,
* the label animation was never started, in which case,
* the label will be the final value and doesn't have label
* animation.
*/ el.forceLabelAnimation = true
}
updateStyle(
el,
data,
dataIndex,
itemModel,
layout,
seriesModel,
isHorizontalOrRadial,
coord.type === 'polar'
)
if (isInitSort) {
el.attr({ shape: layout })
} else if (realtimeSortCfg) {
updateRealtimeAnimation(
realtimeSortCfg,
animationModel,
el,
layout,
dataIndex,
isHorizontalOrRadial,
false,
false
)
} else {
initProps(el, { shape: layout }, seriesModel, dataIndex)
}
data.setItemGraphicEl(dataIndex, el)
group.add(el)
el.ignore = isClipped
})
.update(function (newIndex, oldIndex) {
var itemModel = data.getItemModel(newIndex)
var layout = getLayout[coord.type](data, newIndex, itemModel)
if (drawBackground) {
var bgEl = void 0
if (oldBgEls.length === 0) {
bgEl = createBackground(oldIndex)
} else {
bgEl = oldBgEls[oldIndex]
bgEl.useStyle(backgroundModel.getItemStyle()) // Only cartesian2d support borderRadius.
if (coord.type === 'cartesian2d') {
bgEl.setShape('r', barBorderRadius)
}
bgEls[newIndex] = bgEl
}
var bgLayout = getLayout[coord.type](data, newIndex)
var shape = createBackgroundShape(
isHorizontalOrRadial,
bgLayout,
coord
)
updateProps$1(
bgEl,
{ shape: shape },
animationModel,
newIndex
)
}
var el = oldData.getItemGraphicEl(oldIndex)
if (
!data.hasValue(newIndex) ||
!isValidLayout[coord.type](layout)
) {
group.remove(el)
return
}
var isClipped = false
if (needsClip) {
isClipped = clip[coord.type](coordSysClipArea, layout)
if (isClipped) {
group.remove(el)
}
}
if (!el) {
el = elementCreator[coord.type](
seriesModel,
data,
newIndex,
layout,
isHorizontalOrRadial,
animationModel,
baseAxis.model,
!!el,
roundCap
)
} else {
saveOldStyle(el)
}
if (realtimeSortCfg) {
el.forceLabelAnimation = true
}
if (isChangeOrder) {
var textEl = el.getTextContent()
if (textEl) {
var labelInnerStore = labelInner(textEl)
if (labelInnerStore.prevValue != null) {
/**
* Set preValue to be value so that no new label
* should be started, otherwise, it will take a full
* `animationDurationUpdate` time to finish the
* animation, which is not expected.
*/ labelInnerStore.prevValue = labelInnerStore.value
}
}
} // Not change anything if only order changed.
// Especially not change label.
else {
updateStyle(
el,
data,
newIndex,
itemModel,
layout,
seriesModel,
isHorizontalOrRadial,
coord.type === 'polar'
)
}
if (isInitSort) {
el.attr({ shape: layout })
} else if (realtimeSortCfg) {
updateRealtimeAnimation(
realtimeSortCfg,
animationModel,
el,
layout,
newIndex,
isHorizontalOrRadial,
true,
isChangeOrder
)
} else {
updateProps$1(
el,
{ shape: layout },
seriesModel,
newIndex,
null
)
}
data.setItemGraphicEl(newIndex, el)
el.ignore = isClipped
group.add(el)
})
.remove(function (dataIndex) {
var el = oldData.getItemGraphicEl(dataIndex)
el && removeElementWithFadeOut(el, seriesModel, dataIndex)
})
.execute()
var bgGroup =
this._backgroundGroup || (this._backgroundGroup = new Group$3())
bgGroup.removeAll()
for (var i = 0; i < bgEls.length; ++i) {
bgGroup.add(bgEls[i])
}
group.add(bgGroup)
this._backgroundEls = bgEls
this._data = data
}
BarView.prototype._renderLarge = function (
seriesModel,
ecModel,
api
) {
this._clear()
createLarge(seriesModel, this.group)
this._updateLargeClip(seriesModel)
}
BarView.prototype._incrementalRenderLarge = function (
params,
seriesModel
) {
this._removeBackground()
createLarge(seriesModel, this.group, this._progressiveEls, true)
}
BarView.prototype._updateLargeClip = function (seriesModel) {
// Use clipPath in large mode.
var clipPath =
seriesModel.get('clip', true) &&
createClipPath(seriesModel.coordinateSystem, false, seriesModel)
var group = this.group
if (clipPath) {
group.setClipPath(clipPath)
} else {
group.removeClipPath()
}
}
BarView.prototype._enableRealtimeSort = function (
realtimeSortCfg,
data,
api
) {
var _this = this // If no data in the first frame, wait for data to initSort
if (!data.count()) {
return
}
var baseAxis = realtimeSortCfg.baseAxis
if (this._isFirstFrame) {
this._dispatchInitSort(data, realtimeSortCfg, api)
this._isFirstFrame = false
} else {
var orderMapping_1 = function orderMapping_1(idx) {
var el = data.getItemGraphicEl(idx)
var shape = el && el.shape
return (
(shape && // The result should be consistent with the initial sort by data value.
// Do not support the case that both positive and negative exist.
Math.abs(
baseAxis.isHorizontal() ? shape.height : shape.width
)) || // If data is NaN, shape.xxx may be NaN, so use || 0 here in case
0
)
}
this._onRendered = function () {
_this._updateSortWithinSameData(
data,
orderMapping_1,
baseAxis,
api
)
}
api.getZr().on('rendered', this._onRendered)
}
}
BarView.prototype._dataSort = function (
data,
baseAxis,
orderMapping
) {
var info = []
data.each(
data.mapDimension(baseAxis.dim),
function (ordinalNumber, dataIdx) {
var mappedValue = orderMapping(dataIdx)
mappedValue = mappedValue == null ? NaN : mappedValue
info.push({
dataIndex: dataIdx,
mappedValue: mappedValue,
ordinalNumber: ordinalNumber
})
}
)
info.sort(function (a, b) {
// If NaN, it will be treated as min val.
return b.mappedValue - a.mappedValue
})
return {
ordinalNumbers: map$1(info, function (item) {
return item.ordinalNumber
})
}
}
BarView.prototype._isOrderChangedWithinSameData = function (
data,
orderMapping,
baseAxis
) {
var scale = baseAxis.scale
var ordinalDataDim = data.mapDimension(baseAxis.dim)
var lastValue = Number.MAX_VALUE
for (
var tickNum = 0, len = scale.getOrdinalMeta().categories.length;
tickNum < len;
++tickNum
) {
var rawIdx = data.rawIndexOf(
ordinalDataDim,
scale.getRawOrdinalNumber(tickNum)
)
var value =
rawIdx < 0 // If some tick have no bar, the tick will be treated as min.
? Number.MIN_VALUE // PENDING: if dataZoom on baseAxis exits, is it a performance issue?
: orderMapping(data.indexOfRawIndex(rawIdx))
if (value > lastValue) {
return true
}
lastValue = value
}
return false
}
/*
* Consider the case when A and B changed order, whose representing
* bars are both out of sight, we don't wish to trigger reorder action
* as long as the order in the view doesn't change.
*/ BarView.prototype._isOrderDifferentInView = function (
orderInfo,
baseAxis
) {
var scale = baseAxis.scale
var extent = scale.getExtent()
var tickNum = Math.max(0, extent[0])
var tickMax = Math.min(
extent[1],
scale.getOrdinalMeta().categories.length - 1
)
for (; tickNum <= tickMax; ++tickNum) {
if (
orderInfo.ordinalNumbers[tickNum] !==
scale.getRawOrdinalNumber(tickNum)
) {
return true
}
}
}
BarView.prototype._updateSortWithinSameData = function (
data,
orderMapping,
baseAxis,
api
) {
if (
!this._isOrderChangedWithinSameData(
data,
orderMapping,
baseAxis
)
) {
return
}
var sortInfo = this._dataSort(data, baseAxis, orderMapping)
if (this._isOrderDifferentInView(sortInfo, baseAxis)) {
this._removeOnRenderedListener(api)
api.dispatchAction({
type: 'changeAxisOrder',
componentType: baseAxis.dim + 'Axis',
axisId: baseAxis.index,
sortInfo: sortInfo
})
}
}
BarView.prototype._dispatchInitSort = function (
data,
realtimeSortCfg,
api
) {
var baseAxis = realtimeSortCfg.baseAxis
var sortResult = this._dataSort(
data,
baseAxis,
function (dataIdx) {
return data.get(
data.mapDimension(realtimeSortCfg.otherAxis.dim),
dataIdx
)
}
)
api.dispatchAction({
type: 'changeAxisOrder',
componentType: baseAxis.dim + 'Axis',
isInitSort: true,
axisId: baseAxis.index,
sortInfo: sortResult
})
}
BarView.prototype.remove = function (ecModel, api) {
this._clear(this._model)
this._removeOnRenderedListener(api)
}
BarView.prototype.dispose = function (ecModel, api) {
this._removeOnRenderedListener(api)
}
BarView.prototype._removeOnRenderedListener = function (api) {
if (this._onRendered) {
api.getZr().off('rendered', this._onRendered)
this._onRendered = null
}
}
BarView.prototype._clear = function (model) {
var group = this.group
var data = this._data
if (
model &&
model.isAnimationEnabled() &&
data &&
!this._isLargeDraw
) {
this._removeBackground()
this._backgroundEls = []
data.eachItemGraphicEl(function (el) {
removeElementWithFadeOut(el, model, getECData(el).dataIndex)
})
} else {
group.removeAll()
}
this._data = null
this._isFirstFrame = true
}
BarView.prototype._removeBackground = function () {
this.group.remove(this._backgroundGroup)
this._backgroundGroup = null
}
BarView.type = 'bar'
return BarView
})(ChartView$1)
var clip = {
cartesian2d: function cartesian2d(coordSysBoundingRect, layout) {
var signWidth = layout.width < 0 ? -1 : 1
var signHeight = layout.height < 0 ? -1 : 1 // Needs positive width and height
if (signWidth < 0) {
layout.x += layout.width
layout.width = -layout.width
}
if (signHeight < 0) {
layout.y += layout.height
layout.height = -layout.height
}
var coordSysX2 =
coordSysBoundingRect.x + coordSysBoundingRect.width
var coordSysY2 =
coordSysBoundingRect.y + coordSysBoundingRect.height
var x = mathMax$1(layout.x, coordSysBoundingRect.x)
var x2 = mathMin$1(layout.x + layout.width, coordSysX2)
var y = mathMax$1(layout.y, coordSysBoundingRect.y)
var y2 = mathMin$1(layout.y + layout.height, coordSysY2)
var xClipped = x2 < x
var yClipped = y2 < y // When xClipped or yClipped, the element will be marked as `ignore`.
// But we should also place the element at the edge of the coord sys bounding rect.
// Beause if data changed and the bar show again, its transition animaiton
// will begin at this place.
layout.x = xClipped && x > coordSysX2 ? x2 : x
layout.y = yClipped && y > coordSysY2 ? y2 : y
layout.width = xClipped ? 0 : x2 - x
layout.height = yClipped ? 0 : y2 - y // Reverse back
if (signWidth < 0) {
layout.x += layout.width
layout.width = -layout.width
}
if (signHeight < 0) {
layout.y += layout.height
layout.height = -layout.height
}
return xClipped || yClipped
},
polar: function polar(coordSysClipArea, layout) {
var signR = layout.r0 <= layout.r ? 1 : -1 // Make sure r is larger than r0
if (signR < 0) {
var tmp = layout.r
layout.r = layout.r0
layout.r0 = tmp
}
var r = mathMin$1(layout.r, coordSysClipArea.r)
var r0 = mathMax$1(layout.r0, coordSysClipArea.r0)
layout.r = r
layout.r0 = r0
var clipped = r - r0 < 0 // Reverse back
if (signR < 0) {
var tmp = layout.r
layout.r = layout.r0
layout.r0 = tmp
}
return clipped
}
}
var elementCreator = {
cartesian2d: function cartesian2d(
seriesModel,
data,
newIndex,
layout,
isHorizontal,
animationModel,
axisModel,
isUpdate,
roundCap
) {
var rect = new Rect$2({ shape: extend({}, layout), z2: 1 })
rect.__dataIndex = newIndex
rect.name = 'item'
if (animationModel) {
var rectShape = rect.shape
var animateProperty = isHorizontal ? 'height' : 'width'
rectShape[animateProperty] = 0
}
return rect
},
polar: function polar(
seriesModel,
data,
newIndex,
layout,
isRadial,
animationModel,
axisModel,
isUpdate,
roundCap
) {
var ShapeClass = !isRadial && roundCap ? Sausage : Sector$1
var sector = new ShapeClass({ shape: layout, z2: 1 })
sector.name = 'item'
var positionMap = createPolarPositionMapping(isRadial)
sector.calculateTextPosition = createSectorCalculateTextPosition(
positionMap,
{ isRoundCap: ShapeClass === Sausage }
) // Animation
if (animationModel) {
var sectorShape = sector.shape
var animateProperty = isRadial ? 'r' : 'endAngle'
var animateTarget = {}
sectorShape[animateProperty] = isRadial ? 0 : layout.startAngle
animateTarget[animateProperty] = layout[animateProperty]
;(isUpdate ? updateProps$1 : initProps)(
sector,
{
shape: animateTarget // __value: typeof dataValue === 'string' ? parseInt(dataValue, 10) : dataValue
},
animationModel
)
}
return sector
}
}
function shouldRealtimeSort(seriesModel, coordSys) {
var realtimeSortOption = seriesModel.get('realtimeSort', true)
var baseAxis = coordSys.getBaseAxis()
if (
realtimeSortOption &&
baseAxis.type === 'category' &&
coordSys.type === 'cartesian2d'
) {
return {
baseAxis: baseAxis,
otherAxis: coordSys.getOtherAxis(baseAxis)
}
}
}
function updateRealtimeAnimation(
realtimeSortCfg,
seriesAnimationModel,
el,
layout,
newIndex,
isHorizontal,
isUpdate,
isChangeOrder
) {
var seriesTarget
var axisTarget
if (isHorizontal) {
axisTarget = { x: layout.x, width: layout.width }
seriesTarget = { y: layout.y, height: layout.height }
} else {
axisTarget = { y: layout.y, height: layout.height }
seriesTarget = { x: layout.x, width: layout.width }
}
if (!isChangeOrder) {
// Keep the original growth animation if only axis order changed.
// Not start a new animation.
;(isUpdate ? updateProps$1 : initProps)(
el,
{ shape: seriesTarget },
seriesAnimationModel,
newIndex,
null
)
}
var axisAnimationModel = seriesAnimationModel
? realtimeSortCfg.baseAxis.model
: null
;(isUpdate ? updateProps$1 : initProps)(
el,
{ shape: axisTarget },
axisAnimationModel,
newIndex
)
}
function checkPropertiesNotValid(obj, props) {
for (var i = 0; i < props.length; i++) {
if (!isFinite(obj[props[i]])) {
return true
}
}
return false
}
var rectPropties = ['x', 'y', 'width', 'height']
var polarPropties = ['cx', 'cy', 'r', 'startAngle', 'endAngle']
var isValidLayout = {
cartesian2d: function cartesian2d(layout) {
return !checkPropertiesNotValid(layout, rectPropties)
},
polar: function polar(layout) {
return !checkPropertiesNotValid(layout, polarPropties)
}
}
var getLayout = {
// itemModel is only used to get borderWidth, which is not needed
// when calculating bar background layout.
cartesian2d: function cartesian2d(data, dataIndex, itemModel) {
var layout = data.getItemLayout(dataIndex)
var fixedLineWidth = itemModel
? getLineWidth(itemModel, layout)
: 0 // fix layout with lineWidth
var signX = layout.width > 0 ? 1 : -1
var signY = layout.height > 0 ? 1 : -1
return {
x: layout.x + (signX * fixedLineWidth) / 2,
y: layout.y + (signY * fixedLineWidth) / 2,
width: layout.width - signX * fixedLineWidth,
height: layout.height - signY * fixedLineWidth
}
},
polar: function polar(data, dataIndex, itemModel) {
var layout = data.getItemLayout(dataIndex)
return {
cx: layout.cx,
cy: layout.cy,
r0: layout.r0,
r: layout.r,
startAngle: layout.startAngle,
endAngle: layout.endAngle,
clockwise: layout.clockwise
}
}
}
function isZeroOnPolar(layout) {
return (
layout.startAngle != null &&
layout.endAngle != null &&
layout.startAngle === layout.endAngle
)
}
function createPolarPositionMapping(isRadial) {
return (function (isRadial) {
var arcOrAngle = isRadial ? 'Arc' : 'Angle'
return function (position) {
switch (position) {
case 'start':
case 'insideStart':
case 'end':
case 'insideEnd':
return position + arcOrAngle
default:
return position
}
}
})(isRadial)
}
function updateStyle(
el,
data,
dataIndex,
itemModel,
layout,
seriesModel,
isHorizontalOrRadial,
isPolar
) {
var style = data.getItemVisual(dataIndex, 'style')
if (!isPolar) {
el.setShape(
'r',
itemModel.get(['itemStyle', 'borderRadius']) || 0
)
}
el.useStyle(style)
var cursorStyle = itemModel.getShallow('cursor')
cursorStyle && el.attr('cursor', cursorStyle)
var labelPositionOutside = isPolar
? isHorizontalOrRadial
? layout.r >= layout.r0
? 'endArc'
: 'startArc'
: layout.endAngle >= layout.startAngle
? 'endAngle'
: 'startAngle'
: isHorizontalOrRadial
? layout.height >= 0
? 'bottom'
: 'top'
: layout.width >= 0
? 'right'
: 'left'
var labelStatesModels = getLabelStatesModels(itemModel)
setLabelStyle(el, labelStatesModels, {
labelFetcher: seriesModel,
labelDataIndex: dataIndex,
defaultText: getDefaultLabel(seriesModel.getData(), dataIndex),
inheritColor: style.fill,
defaultOpacity: style.opacity,
defaultOutsidePosition: labelPositionOutside
})
var label = el.getTextContent()
if (isPolar && label) {
var position = itemModel.get(['label', 'position'])
el.textConfig.inside = position === 'middle' ? true : null
setSectorTextRotation(
el,
position === 'outside' ? labelPositionOutside : position,
createPolarPositionMapping(isHorizontalOrRadial),
itemModel.get(['label', 'rotate'])
)
}
setLabelValueAnimation(
label,
labelStatesModels,
seriesModel.getRawValue(dataIndex),
function (value) {
return getDefaultInterpolatedLabel(data, value)
}
)
var emphasisModel = itemModel.getModel(['emphasis'])
toggleHoverEmphasis(
el,
emphasisModel.get('focus'),
emphasisModel.get('blurScope'),
emphasisModel.get('disabled')
)
setStatesStylesFromModel(el, itemModel)
if (isZeroOnPolar(layout)) {
el.style.fill = 'none'
el.style.stroke = 'none'
each$7(el.states, function (state) {
if (state.style) {
state.style.fill = state.style.stroke = 'none'
}
})
}
} // In case width or height are too small.
function getLineWidth(itemModel, rawLayout) {
// Has no border.
var borderColor = itemModel.get(['itemStyle', 'borderColor'])
if (!borderColor || borderColor === 'none') {
return 0
}
var lineWidth = itemModel.get(['itemStyle', 'borderWidth']) || 0 // width or height may be NaN for empty data
var width = isNaN(rawLayout.width)
? Number.MAX_VALUE
: Math.abs(rawLayout.width)
var height = isNaN(rawLayout.height)
? Number.MAX_VALUE
: Math.abs(rawLayout.height)
return Math.min(lineWidth, width, height)
}
var LagePathShape = /** @class */ (function () {
function LagePathShape() {}
return LagePathShape
})()
var LargePath = /** @class */ (function (_super) {
__extends(LargePath, _super)
function LargePath(opts) {
var _this = _super.call(this, opts) || this
_this.type = 'largeBar'
return _this
}
LargePath.prototype.getDefaultShape = function () {
return new LagePathShape()
}
LargePath.prototype.buildPath = function (ctx, shape) {
// Drawing lines is more efficient than drawing
// a whole line or drawing rects.
var points = shape.points
var baseDimIdx = this.baseDimIdx
var valueDimIdx = 1 - this.baseDimIdx
var startPoint = []
var size = []
var barWidth = this.barWidth
for (var i = 0; i < points.length; i += 3) {
size[baseDimIdx] = barWidth
size[valueDimIdx] = points[i + 2]
startPoint[baseDimIdx] = points[i + baseDimIdx]
startPoint[valueDimIdx] = points[i + valueDimIdx]
ctx.rect(startPoint[0], startPoint[1], size[0], size[1])
}
}
return LargePath
})(Path$1)
function createLarge(
seriesModel,
group,
progressiveEls,
incremental
) {
// TODO support polar
var data = seriesModel.getData()
var baseDimIdx = data.getLayout('valueAxisHorizontal') ? 1 : 0
var largeDataIndices = data.getLayout('largeDataIndices')
var barWidth = data.getLayout('size')
var backgroundModel = seriesModel.getModel('backgroundStyle')
var bgPoints = data.getLayout('largeBackgroundPoints')
if (bgPoints) {
var bgEl = new LargePath({
shape: { points: bgPoints },
incremental: !!incremental,
silent: true,
z2: 0
})
bgEl.baseDimIdx = baseDimIdx
bgEl.largeDataIndices = largeDataIndices
bgEl.barWidth = barWidth
bgEl.useStyle(backgroundModel.getItemStyle())
group.add(bgEl)
progressiveEls && progressiveEls.push(bgEl)
}
var el = new LargePath({
shape: { points: data.getLayout('largePoints') },
incremental: !!incremental,
z2: 1
})
el.baseDimIdx = baseDimIdx
el.largeDataIndices = largeDataIndices
el.barWidth = barWidth
group.add(el)
el.useStyle(data.getVisual('style')) // Enable tooltip and user mouse/touch event handlers.
getECData(el).seriesIndex = seriesModel.seriesIndex
if (!seriesModel.get('silent')) {
el.on('mousedown', largePathUpdateDataIndex)
el.on('mousemove', largePathUpdateDataIndex)
}
progressiveEls && progressiveEls.push(el)
} // Use throttle to avoid frequently traverse to find dataIndex.
var largePathUpdateDataIndex = throttle(
function (event) {
var largePath = this
var dataIndex = largePathFindDataIndex(
largePath,
event.offsetX,
event.offsetY
)
getECData(largePath).dataIndex = dataIndex >= 0 ? dataIndex : null
},
30,
false
)
function largePathFindDataIndex(largePath, x, y) {
var baseDimIdx = largePath.baseDimIdx
var valueDimIdx = 1 - baseDimIdx
var points = largePath.shape.points
var largeDataIndices = largePath.largeDataIndices
var startPoint = []
var size = []
var barWidth = largePath.barWidth
for (var i = 0, len = points.length / 3; i < len; i++) {
var ii = i * 3
size[baseDimIdx] = barWidth
size[valueDimIdx] = points[ii + 2]
startPoint[baseDimIdx] = points[ii + baseDimIdx]
startPoint[valueDimIdx] = points[ii + valueDimIdx]
if (size[valueDimIdx] < 0) {
startPoint[valueDimIdx] += size[valueDimIdx]
size[valueDimIdx] = -size[valueDimIdx]
}
if (
x >= startPoint[0] &&
x <= startPoint[0] + size[0] &&
y >= startPoint[1] &&
y <= startPoint[1] + size[1]
) {
return largeDataIndices[i]
}
}
return -1
}
function createBackgroundShape(isHorizontalOrRadial, layout, coord) {
if (isCoordinateSystemType(coord, 'cartesian2d')) {
var rectShape = layout
var coordLayout = coord.getArea()
return {
x: isHorizontalOrRadial ? rectShape.x : coordLayout.x,
y: isHorizontalOrRadial ? coordLayout.y : rectShape.y,
width: isHorizontalOrRadial
? rectShape.width
: coordLayout.width,
height: isHorizontalOrRadial
? coordLayout.height
: rectShape.height
}
} else {
var coordLayout = coord.getArea()
var sectorShape = layout
return {
cx: coordLayout.cx,
cy: coordLayout.cy,
r0: isHorizontalOrRadial ? coordLayout.r0 : sectorShape.r0,
r: isHorizontalOrRadial ? coordLayout.r : sectorShape.r,
startAngle: isHorizontalOrRadial ? sectorShape.startAngle : 0,
endAngle: isHorizontalOrRadial
? sectorShape.endAngle
: Math.PI * 2
}
}
}
function createBackgroundEl(coord, isHorizontalOrRadial, layout) {
var ElementClz = coord.type === 'polar' ? Sector$1 : Rect$2
return new ElementClz({
shape: createBackgroundShape(isHorizontalOrRadial, layout, coord),
silent: true,
z2: 0
})
}
var BarView$1 = BarView
function install$h(registers) {
registers.registerChartView(BarView$1)
registers.registerSeriesModel(BarSeries)
registers.registerLayout(
registers.PRIORITY.VISUAL.LAYOUT,
curry$1(layout$2, 'bar')
) // Do layout after other overall layout, which can preapre some informations.
registers.registerLayout(
registers.PRIORITY.VISUAL.PROGRESSIVE_LAYOUT,
createProgressiveLayout('bar')
) // Down sample after filter
registers.registerProcessor(
registers.PRIORITY.PROCESSOR.STATISTIC,
dataSample('bar')
)
/**
* @payload
* @property {string} [componentType=series]
* @property {number} [dx]
* @property {number} [dy]
* @property {number} [zoom]
* @property {number} [originX]
* @property {number} [originY]
*/ registers.registerAction(
{
type: 'changeAxisOrder',
event: 'changeAxisOrder',
update: 'update'
},
function (payload, ecModel) {
var componentType = payload.componentType || 'series'
ecModel.eachComponent(
{ mainType: componentType, query: payload },
function (componentModel) {
if (payload.sortInfo) {
componentModel.axis.setCategorySortInfo(payload.sortInfo)
}
}
)
}
)
}
var PI2 = Math.PI * 2
var RADIAN$1 = Math.PI / 180
function getViewRect(seriesModel, api) {
return getLayoutRect(seriesModel.getBoxLayoutParams(), {
width: api.getWidth(),
height: api.getHeight()
})
}
function getBasicPieLayout(seriesModel, api) {
var viewRect = getViewRect(seriesModel, api)
var center = seriesModel.get('center')
var radius = seriesModel.get('radius')
if (!isArray(radius)) {
radius = [0, radius]
}
if (!isArray(center)) {
center = [center, center]
}
var width = parsePercent(viewRect.width, api.getWidth())
var height = parsePercent(viewRect.height, api.getHeight())
var size = Math.min(width, height)
var cx = parsePercent(center[0], width) + viewRect.x
var cy = parsePercent(center[1], height) + viewRect.y
var r0 = parsePercent(radius[0], size / 2)
var r = parsePercent(radius[1], size / 2)
return { cx: cx, cy: cy, r0: r0, r: r }
}
function pieLayout(seriesType, ecModel, api) {
ecModel.eachSeriesByType(seriesType, function (seriesModel) {
var data = seriesModel.getData()
var valueDim = data.mapDimension('value')
var viewRect = getViewRect(seriesModel, api)
var _a = getBasicPieLayout(seriesModel, api),
cx = _a.cx,
cy = _a.cy,
r = _a.r,
r0 = _a.r0
var startAngle = -seriesModel.get('startAngle') * RADIAN$1
var minAngle = seriesModel.get('minAngle') * RADIAN$1
var validDataCount = 0
data.each(valueDim, function (value) {
!isNaN(value) && validDataCount++
})
var sum = data.getSum(valueDim) // Sum may be 0
var unitRadian = (Math.PI / (sum || validDataCount)) * 2
var clockwise = seriesModel.get('clockwise')
var roseType = seriesModel.get('roseType')
var stillShowZeroSum = seriesModel.get('stillShowZeroSum') // [0...max]
var extent = data.getDataExtent(valueDim)
extent[0] = 0 // In the case some sector angle is smaller than minAngle
var restAngle = PI2
var valueSumLargerThanMinAngle = 0
var currentAngle = startAngle
var dir = clockwise ? 1 : -1
data.setLayout({ viewRect: viewRect, r: r })
data.each(valueDim, function (value, idx) {
var angle
if (isNaN(value)) {
data.setItemLayout(idx, {
angle: NaN,
startAngle: NaN,
endAngle: NaN,
clockwise: clockwise,
cx: cx,
cy: cy,
r0: r0,
r: roseType ? NaN : r
})
return
} // FIXME 兼容 2.0 但是 roseType 是 area 的时候才是这样?
if (roseType !== 'area') {
angle =
sum === 0 && stillShowZeroSum
? unitRadian
: value * unitRadian
} else {
angle = PI2 / validDataCount
}
if (angle < minAngle) {
angle = minAngle
restAngle -= minAngle
} else {
valueSumLargerThanMinAngle += value
}
var endAngle = currentAngle + dir * angle
data.setItemLayout(idx, {
angle: angle,
startAngle: currentAngle,
endAngle: endAngle,
clockwise: clockwise,
cx: cx,
cy: cy,
r0: r0,
r: roseType ? linearMap(value, extent, [r0, r]) : r
})
currentAngle = endAngle
}) // Some sector is constrained by minAngle
// Rest sectors needs recalculate angle
if (restAngle < PI2 && validDataCount) {
// Average the angle if rest angle is not enough after all angles is
// Constrained by minAngle
if (restAngle <= 1e-3) {
var angle_1 = PI2 / validDataCount
data.each(valueDim, function (value, idx) {
if (!isNaN(value)) {
var layout_1 = data.getItemLayout(idx)
layout_1.angle = angle_1
layout_1.startAngle = startAngle + dir * idx * angle_1
layout_1.endAngle = startAngle + dir * (idx + 1) * angle_1
}
})
} else {
unitRadian = restAngle / valueSumLargerThanMinAngle
currentAngle = startAngle
data.each(valueDim, function (value, idx) {
if (!isNaN(value)) {
var layout_2 = data.getItemLayout(idx)
var angle =
layout_2.angle === minAngle
? minAngle
: value * unitRadian
layout_2.startAngle = currentAngle
layout_2.endAngle = currentAngle + dir * angle
currentAngle += dir * angle
}
})
}
}
})
}
/*
* 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 dataFilter(seriesType) {
return {
seriesType: seriesType,
reset: function reset(seriesModel, ecModel) {
var legendModels = ecModel.findComponents({
mainType: 'legend'
})
if (!legendModels || !legendModels.length) {
return
}
var data = seriesModel.getData()
data.filterSelf(function (idx) {
var name = data.getName(idx) // If in any legend component the status is not selected.
for (var i = 0; i < legendModels.length; i++) {
// @ts-ignore FIXME: LegendModel
if (!legendModels[i].isSelected(name)) {
return false
}
}
return true
})
}
}
}
var RADIAN = Math.PI / 180
function adjustSingleSide(
list,
cx,
cy,
r,
dir,
viewWidth,
viewHeight,
viewLeft,
viewTop,
farthestX
) {
if (list.length < 2) {
return
}
function recalculateXOnSemiToAlignOnEllipseCurve(semi) {
var rB = semi.rB
var rB2 = rB * rB
for (var i = 0; i < semi.list.length; i++) {
var item = semi.list[i]
var dy = Math.abs(item.label.y - cy) // horizontal r is always same with original r because x is not changed.
var rA = r + item.len
var rA2 = rA * rA // Use ellipse implicit function to calculate x
var dx = Math.sqrt((1 - Math.abs((dy * dy) / rB2)) * rA2)
var newX = cx + (dx + item.len2) * dir
var deltaX = newX - item.label.x
var newTargetWidth = item.targetTextWidth - deltaX * dir // text x is changed, so need to recalculate width.
constrainTextWidth(item, newTargetWidth, true)
item.label.x = newX
}
} // Adjust X based on the shifted y. Make tight labels aligned on an ellipse curve.
function recalculateX(items) {
// Extremes of
var topSemi = { list: [], maxY: 0 }
var bottomSemi = { list: [], maxY: 0 }
for (var i = 0; i < items.length; i++) {
if (items[i].labelAlignTo !== 'none') {
continue
}
var item = items[i]
var semi = item.label.y > cy ? bottomSemi : topSemi
var dy = Math.abs(item.label.y - cy)
if (dy >= semi.maxY) {
var dx = item.label.x - cx - item.len2 * dir // horizontal r is always same with original r because x is not changed.
var rA = r + item.len // Canculate rB based on the topest / bottemest label.
var rB =
Math.abs(dx) < rA
? Math.sqrt((dy * dy) / (1 - (dx * dx) / rA / rA))
: rA
semi.rB = rB
semi.maxY = dy
}
semi.list.push(item)
}
recalculateXOnSemiToAlignOnEllipseCurve(topSemi)
recalculateXOnSemiToAlignOnEllipseCurve(bottomSemi)
}
var len = list.length
for (var i = 0; i < len; i++) {
if (
list[i].position === 'outer' &&
list[i].labelAlignTo === 'labelLine'
) {
var dx = list[i].label.x - farthestX
list[i].linePoints[1][0] += dx
list[i].label.x = farthestX
}
}
if (shiftLayoutOnY(list, viewTop, viewTop + viewHeight)) {
recalculateX(list)
}
}
function avoidOverlap(
labelLayoutList,
cx,
cy,
r,
viewWidth,
viewHeight,
viewLeft,
viewTop
) {
var leftList = []
var rightList = []
var leftmostX = Number.MAX_VALUE
var rightmostX = -Number.MAX_VALUE
for (var i = 0; i < labelLayoutList.length; i++) {
var label = labelLayoutList[i].label
if (isPositionCenter(labelLayoutList[i])) {
continue
}
if (label.x < cx) {
leftmostX = Math.min(leftmostX, label.x)
leftList.push(labelLayoutList[i])
} else {
rightmostX = Math.max(rightmostX, label.x)
rightList.push(labelLayoutList[i])
}
}
for (var i = 0; i < labelLayoutList.length; i++) {
var layout = labelLayoutList[i]
if (!isPositionCenter(layout) && layout.linePoints) {
if (layout.labelStyleWidth != null) {
continue
}
var label = layout.label
var linePoints = layout.linePoints
var targetTextWidth = void 0
if (layout.labelAlignTo === 'edge') {
if (label.x < cx) {
targetTextWidth =
linePoints[2][0] -
layout.labelDistance -
viewLeft -
layout.edgeDistance
} else {
targetTextWidth =
viewLeft +
viewWidth -
layout.edgeDistance -
linePoints[2][0] -
layout.labelDistance
}
} else if (layout.labelAlignTo === 'labelLine') {
if (label.x < cx) {
targetTextWidth = leftmostX - viewLeft - layout.bleedMargin
} else {
targetTextWidth =
viewLeft + viewWidth - rightmostX - layout.bleedMargin
}
} else {
if (label.x < cx) {
targetTextWidth = label.x - viewLeft - layout.bleedMargin
} else {
targetTextWidth =
viewLeft + viewWidth - label.x - layout.bleedMargin
}
}
layout.targetTextWidth = targetTextWidth
constrainTextWidth(layout, targetTextWidth)
}
}
adjustSingleSide(
rightList,
cx,
cy,
r,
1,
viewWidth,
viewHeight,
viewLeft,
viewTop,
rightmostX
)
adjustSingleSide(
leftList,
cx,
cy,
r,
-1,
viewWidth,
viewHeight,
viewLeft,
viewTop,
leftmostX
)
for (var i = 0; i < labelLayoutList.length; i++) {
var layout = labelLayoutList[i]
if (!isPositionCenter(layout) && layout.linePoints) {
var label = layout.label
var linePoints = layout.linePoints
var isAlignToEdge = layout.labelAlignTo === 'edge'
var padding = label.style.padding
var paddingH = padding ? padding[1] + padding[3] : 0 // textRect.width already contains paddingH if bgColor is set
var extraPaddingH = label.style.backgroundColor ? 0 : paddingH
var realTextWidth = layout.rect.width + extraPaddingH
var dist = linePoints[1][0] - linePoints[2][0]
if (isAlignToEdge) {
if (label.x < cx) {
linePoints[2][0] =
viewLeft +
layout.edgeDistance +
realTextWidth +
layout.labelDistance
} else {
linePoints[2][0] =
viewLeft +
viewWidth -
layout.edgeDistance -
realTextWidth -
layout.labelDistance
}
} else {
if (label.x < cx) {
linePoints[2][0] = label.x + layout.labelDistance
} else {
linePoints[2][0] = label.x - layout.labelDistance
}
linePoints[1][0] = linePoints[2][0] + dist
}
linePoints[1][1] = linePoints[2][1] = label.y
}
}
}
/**
* Set max width of each label, and then wrap each label to the max width.
*
* @param layout label layout
* @param availableWidth max width for the label to display
* @param forceRecalculate recaculate the text layout even if the current width
* is smaller than `availableWidth`. This is useful when the text was previously
* wrapped by calling `constrainTextWidth` but now `availableWidth` changed, in
* which case, previous wrapping should be redo.
*/ function constrainTextWidth(
layout,
availableWidth,
forceRecalculate
) {
if (forceRecalculate === void 0) {
forceRecalculate = false
}
if (layout.labelStyleWidth != null) {
// User-defined style.width has the highest priority.
return
}
var label = layout.label
var style = label.style
var textRect = layout.rect
var bgColor = style.backgroundColor
var padding = style.padding
var paddingH = padding ? padding[1] + padding[3] : 0
var overflow = style.overflow // textRect.width already contains paddingH if bgColor is set
var oldOuterWidth = textRect.width + (bgColor ? 0 : paddingH)
if (availableWidth < oldOuterWidth || forceRecalculate) {
var oldHeight = textRect.height
if (overflow && overflow.match('break')) {
// Temporarily set background to be null to calculate
// the bounding box without backgroud.
label.setStyle('backgroundColor', null) // Set constraining width
label.setStyle('width', availableWidth - paddingH) // This is the real bounding box of the text without padding
var innerRect = label.getBoundingRect()
label.setStyle('width', Math.ceil(innerRect.width))
label.setStyle('backgroundColor', bgColor)
} else {
var availableInnerWidth = availableWidth - paddingH
var newWidth =
availableWidth < oldOuterWidth // Current text is too wide, use `availableWidth` as max width.
? availableInnerWidth // Current available width is enough, but the text may have
: // already been wrapped with a smaller available width.
forceRecalculate
? availableInnerWidth > layout.unconstrainedWidth // Current available is larger than text width,
? // so don't constrain width (otherwise it may have
// empty space in the background).
null // Current available is smaller than text width, so
: // use the current available width as constraining
// width.
availableInnerWidth // Current available width is enough, so no need to
: // constrain.
null
label.setStyle('width', newWidth)
}
var newRect = label.getBoundingRect()
textRect.width = newRect.width
var margin = (label.style.margin || 0) + 2.1
textRect.height = newRect.height + margin
textRect.y -= (textRect.height - oldHeight) / 2
}
}
function isPositionCenter(sectorShape) {
// Not change x for center label
return sectorShape.position === 'center'
}
function pieLabelLayout(seriesModel) {
var data = seriesModel.getData()
var labelLayoutList = []
var cx
var cy
var hasLabelRotate = false
var minShowLabelRadian =
(seriesModel.get('minShowLabelAngle') || 0) * RADIAN
var viewRect = data.getLayout('viewRect')
var r = data.getLayout('r')
var viewWidth = viewRect.width
var viewLeft = viewRect.x
var viewTop = viewRect.y
var viewHeight = viewRect.height
function setNotShow(el) {
el.ignore = true
}
function isLabelShown(label) {
if (!label.ignore) {
return true
}
for (var key in label.states) {
if (label.states[key].ignore === false) {
return true
}
}
return false
}
data.each(function (idx) {
var sector = data.getItemGraphicEl(idx)
var sectorShape = sector.shape
var label = sector.getTextContent()
var labelLine = sector.getTextGuideLine()
var itemModel = data.getItemModel(idx)
var labelModel = itemModel.getModel('label') // Use position in normal or emphasis
var labelPosition =
labelModel.get('position') ||
itemModel.get(['emphasis', 'label', 'position'])
var labelDistance = labelModel.get('distanceToLabelLine')
var labelAlignTo = labelModel.get('alignTo')
var edgeDistance = parsePercent(
labelModel.get('edgeDistance'),
viewWidth
)
var bleedMargin = labelModel.get('bleedMargin')
var labelLineModel = itemModel.getModel('labelLine')
var labelLineLen = labelLineModel.get('length')
labelLineLen = parsePercent(labelLineLen, viewWidth)
var labelLineLen2 = labelLineModel.get('length2')
labelLineLen2 = parsePercent(labelLineLen2, viewWidth)
if (
Math.abs(sectorShape.endAngle - sectorShape.startAngle) <
minShowLabelRadian
) {
each$7(label.states, setNotShow)
label.ignore = true
return
}
if (!isLabelShown(label)) {
return
}
var midAngle = (sectorShape.startAngle + sectorShape.endAngle) / 2
var nx = Math.cos(midAngle)
var ny = Math.sin(midAngle)
var textX
var textY
var linePoints
var textAlign
cx = sectorShape.cx
cy = sectorShape.cy
var isLabelInside =
labelPosition === 'inside' || labelPosition === 'inner'
if (labelPosition === 'center') {
textX = sectorShape.cx
textY = sectorShape.cy
textAlign = 'center'
} else {
var x1 =
(isLabelInside
? ((sectorShape.r + sectorShape.r0) / 2) * nx
: sectorShape.r * nx) + cx
var y1 =
(isLabelInside
? ((sectorShape.r + sectorShape.r0) / 2) * ny
: sectorShape.r * ny) + cy
textX = x1 + nx * 3
textY = y1 + ny * 3
if (!isLabelInside) {
// For roseType
var x2 = x1 + nx * (labelLineLen + r - sectorShape.r)
var y2 = y1 + ny * (labelLineLen + r - sectorShape.r)
var x3 = x2 + (nx < 0 ? -1 : 1) * labelLineLen2
var y3 = y2
if (labelAlignTo === 'edge') {
// Adjust textX because text align of edge is opposite
textX =
nx < 0
? viewLeft + edgeDistance
: viewLeft + viewWidth - edgeDistance
} else {
textX = x3 + (nx < 0 ? -labelDistance : labelDistance)
}
textY = y3
linePoints = [
[x1, y1],
[x2, y2],
[x3, y3]
]
}
textAlign = isLabelInside
? 'center'
: labelAlignTo === 'edge'
? nx > 0
? 'right'
: 'left'
: nx > 0
? 'left'
: 'right'
}
var PI = Math.PI
var labelRotate = 0
var rotate = labelModel.get('rotate')
if (isNumber(rotate)) {
labelRotate = rotate * (PI / 180)
} else if (labelPosition === 'center') {
labelRotate = 0
} else if (rotate === 'radial' || rotate === true) {
var radialAngle = nx < 0 ? -midAngle + PI : -midAngle
labelRotate = radialAngle
} else if (
rotate === 'tangential' &&
labelPosition !== 'outside' &&
labelPosition !== 'outer'
) {
var rad = Math.atan2(nx, ny)
if (rad < 0) {
rad = PI * 2 + rad
}
var isDown = ny > 0
if (isDown) {
rad = PI + rad
}
labelRotate = rad - PI
}
hasLabelRotate = !!labelRotate
label.x = textX
label.y = textY
label.rotation = labelRotate
label.setStyle({ verticalAlign: 'middle' }) // Not sectorShape the inside label
if (!isLabelInside) {
var textRect = label.getBoundingRect().clone()
textRect.applyTransform(label.getComputedTransform()) // Text has a default 1px stroke. Exclude this.
var margin = (label.style.margin || 0) + 2.1
textRect.y -= margin / 2
textRect.height += margin
labelLayoutList.push({
label: label,
labelLine: labelLine,
position: labelPosition,
len: labelLineLen,
len2: labelLineLen2,
minTurnAngle: labelLineModel.get('minTurnAngle'),
maxSurfaceAngle: labelLineModel.get('maxSurfaceAngle'),
surfaceNormal: new Point$1(nx, ny),
linePoints: linePoints,
textAlign: textAlign,
labelDistance: labelDistance,
labelAlignTo: labelAlignTo,
edgeDistance: edgeDistance,
bleedMargin: bleedMargin,
rect: textRect,
unconstrainedWidth: textRect.width,
labelStyleWidth: label.style.width
})
} else {
label.setStyle({ align: textAlign })
var selectState = label.states.select
if (selectState) {
selectState.x += label.x
selectState.y += label.y
}
}
sector.setTextConfig({ inside: isLabelInside })
})
if (!hasLabelRotate && seriesModel.get('avoidLabelOverlap')) {
avoidOverlap(
labelLayoutList,
cx,
cy,
r,
viewWidth,
viewHeight,
viewLeft,
viewTop
)
}
for (var i = 0; i < labelLayoutList.length; i++) {
var layout = labelLayoutList[i]
var label = layout.label
var labelLine = layout.labelLine
var notShowLabel = isNaN(label.x) || isNaN(label.y)
if (label) {
label.setStyle({ align: layout.textAlign })
if (notShowLabel) {
each$7(label.states, setNotShow)
label.ignore = true
}
var selectState = label.states.select
if (selectState) {
selectState.x += label.x
selectState.y += label.y
}
}
if (labelLine) {
var linePoints = layout.linePoints
if (notShowLabel || !linePoints) {
each$7(labelLine.states, setNotShow)
labelLine.ignore = true
} else {
limitTurnAngle(linePoints, layout.minTurnAngle)
limitSurfaceAngle(
linePoints,
layout.surfaceNormal,
layout.maxSurfaceAngle
)
labelLine.setShape({ points: linePoints }) // Set the anchor to the midpoint of sector
label.__hostTarget.textGuideLineConfig = {
anchor: new Point$1(linePoints[0][0], linePoints[0][1])
}
}
}
}
}
function getSectorCornerRadius(model, shape, zeroIfNull) {
var cornerRadius = model.get('borderRadius')
if (cornerRadius == null) {
return zeroIfNull ? { cornerRadius: 0 } : null
}
if (!isArray(cornerRadius)) {
cornerRadius = [
cornerRadius,
cornerRadius,
cornerRadius,
cornerRadius
]
}
var dr = Math.abs(shape.r || 0 - shape.r0 || 0)
return {
cornerRadius: map$1(cornerRadius, function (cr) {
return parsePercent$1(cr, dr)
})
}
}
/**
* Piece of pie including Sector, Label, LabelLine
*/ var PiePiece = /** @class */ (function (_super) {
__extends(PiePiece, _super)
function PiePiece(data, idx, startAngle) {
var _this = _super.call(this) || this
_this.z2 = 2
var text = new ZRText$1()
_this.setTextContent(text)
_this.updateData(data, idx, startAngle, true)
return _this
}
PiePiece.prototype.updateData = function (
data,
idx,
startAngle,
firstCreate
) {
var sector = this
var seriesModel = data.hostModel
var itemModel = data.getItemModel(idx)
var emphasisModel = itemModel.getModel('emphasis')
var layout = data.getItemLayout(idx) // cornerRadius & innerCornerRadius doesn't exist in the item layout. Use `0` if null value is specified.
// see `setItemLayout` in `pieLayout.ts`.
var sectorShape = extend(
getSectorCornerRadius(
itemModel.getModel('itemStyle'),
layout,
true
),
layout
) // Ignore NaN data.
if (isNaN(sectorShape.startAngle)) {
// Use NaN shape to avoid drawing shape.
sector.setShape(sectorShape)
return
}
if (firstCreate) {
sector.setShape(sectorShape)
var animationType = seriesModel.getShallow('animationType')
if (seriesModel.ecModel.ssr) {
// Use scale animation in SSR mode(opacity?)
// Because CSS SVG animation doesn't support very customized shape animation.
initProps(sector, { scaleX: 0, scaleY: 0 }, seriesModel, {
dataIndex: idx,
isFrom: true
})
sector.originX = sectorShape.cx
sector.originY = sectorShape.cy
} else if (animationType === 'scale') {
sector.shape.r = layout.r0
initProps(
sector,
{ shape: { r: layout.r } },
seriesModel,
idx
)
} // Expansion
else {
if (startAngle != null) {
sector.setShape({
startAngle: startAngle,
endAngle: startAngle
})
initProps(
sector,
{
shape: {
startAngle: layout.startAngle,
endAngle: layout.endAngle
}
},
seriesModel,
idx
)
} else {
sector.shape.endAngle = layout.startAngle
updateProps$1(
sector,
{ shape: { endAngle: layout.endAngle } },
seriesModel,
idx
)
}
}
} else {
saveOldStyle(sector) // Transition animation from the old shape
updateProps$1(sector, { shape: sectorShape }, seriesModel, idx)
}
sector.useStyle(data.getItemVisual(idx, 'style'))
setStatesStylesFromModel(sector, itemModel)
var midAngle = (layout.startAngle + layout.endAngle) / 2
var offset = seriesModel.get('selectedOffset')
var dx = Math.cos(midAngle) * offset
var dy = Math.sin(midAngle) * offset
var cursorStyle = itemModel.getShallow('cursor')
cursorStyle && sector.attr('cursor', cursorStyle)
this._updateLabel(seriesModel, data, idx)
sector.ensureState('emphasis').shape = extend(
{
r:
layout.r +
(emphasisModel.get('scale')
? emphasisModel.get('scaleSize') || 0
: 0)
},
getSectorCornerRadius(
emphasisModel.getModel('itemStyle'),
layout
)
)
extend(sector.ensureState('select'), {
x: dx,
y: dy,
shape: getSectorCornerRadius(
itemModel.getModel(['select', 'itemStyle']),
layout
)
})
extend(sector.ensureState('blur'), {
shape: getSectorCornerRadius(
itemModel.getModel(['blur', 'itemStyle']),
layout
)
})
var labelLine = sector.getTextGuideLine()
var labelText = sector.getTextContent()
labelLine &&
extend(labelLine.ensureState('select'), { x: dx, y: dy }) // TODO: needs dx, dy in zrender?
extend(labelText.ensureState('select'), { x: dx, y: dy })
toggleHoverEmphasis(
this,
emphasisModel.get('focus'),
emphasisModel.get('blurScope'),
emphasisModel.get('disabled')
)
}
PiePiece.prototype._updateLabel = function (
seriesModel,
data,
idx
) {
var sector = this
var itemModel = data.getItemModel(idx)
var labelLineModel = itemModel.getModel('labelLine')
var style = data.getItemVisual(idx, 'style')
var visualColor = style && style.fill
var visualOpacity = style && style.opacity
setLabelStyle(sector, getLabelStatesModels(itemModel), {
labelFetcher: data.hostModel,
labelDataIndex: idx,
inheritColor: visualColor,
defaultOpacity: visualOpacity,
defaultText:
seriesModel.getFormattedLabel(idx, 'normal') ||
data.getName(idx)
})
var labelText = sector.getTextContent() // Set textConfig on sector.
sector.setTextConfig({
// reset position, rotation
position: null,
rotation: null
}) // Make sure update style on labelText after setLabelStyle.
// Because setLabelStyle will replace a new style on it.
labelText.attr({ z2: 10 })
var labelPosition = seriesModel.get(['label', 'position'])
if (labelPosition !== 'outside' && labelPosition !== 'outer') {
sector.removeTextGuideLine()
} else {
var polyline = this.getTextGuideLine()
if (!polyline) {
polyline = new Polyline$1()
this.setTextGuideLine(polyline)
} // Default use item visual color
setLabelLineStyle(this, getLabelLineStatesModels(itemModel), {
stroke: visualColor,
opacity: retrieve3(
labelLineModel.get(['lineStyle', 'opacity']),
visualOpacity,
1
)
})
}
}
return PiePiece
})(Sector$1) // Pie view
var PieView = /** @class */ (function (_super) {
__extends(PieView, _super)
function PieView() {
var _this =
(_super !== null && _super.apply(this, arguments)) || this
_this.ignoreLabelLineUpdate = true
return _this
}
PieView.prototype.render = function (
seriesModel,
ecModel,
api,
payload
) {
var data = seriesModel.getData()
var oldData = this._data
var group = this.group
var startAngle // First render
if (!oldData && data.count() > 0) {
var shape = data.getItemLayout(0)
for (
var s = 1;
isNaN(shape && shape.startAngle) && s < data.count();
++s
) {
shape = data.getItemLayout(s)
}
if (shape) {
startAngle = shape.startAngle
}
} // remove empty-circle if it exists
if (this._emptyCircleSector) {
group.remove(this._emptyCircleSector)
} // when all data are filtered, show lightgray empty circle
if (data.count() === 0 && seriesModel.get('showEmptyCircle')) {
var sector = new Sector$1({
shape: getBasicPieLayout(seriesModel, api)
})
sector.useStyle(
seriesModel.getModel('emptyCircleStyle').getItemStyle()
)
this._emptyCircleSector = sector
group.add(sector)
}
data
.diff(oldData)
.add(function (idx) {
var piePiece = new PiePiece(data, idx, startAngle)
data.setItemGraphicEl(idx, piePiece)
group.add(piePiece)
})
.update(function (newIdx, oldIdx) {
var piePiece = oldData.getItemGraphicEl(oldIdx)
piePiece.updateData(data, newIdx, startAngle)
piePiece.off('click')
group.add(piePiece)
data.setItemGraphicEl(newIdx, piePiece)
})
.remove(function (idx) {
var piePiece = oldData.getItemGraphicEl(idx)
removeElementWithFadeOut(piePiece, seriesModel, idx)
})
.execute()
pieLabelLayout(seriesModel) // Always use initial animation.
if (seriesModel.get('animationTypeUpdate') !== 'expansion') {
this._data = data
}
}
PieView.prototype.dispose = function () {}
PieView.prototype.containPoint = function (point, seriesModel) {
var data = seriesModel.getData()
var itemLayout = data.getItemLayout(0)
if (itemLayout) {
var dx = point[0] - itemLayout.cx
var dy = point[1] - itemLayout.cy
var radius = Math.sqrt(dx * dx + dy * dy)
return radius <= itemLayout.r && radius >= itemLayout.r0
}
}
PieView.type = 'pie'
return PieView
})(ChartView$1)
var PieView$1 = PieView
/**
* [Usage]:
* (1)
* createListSimply(seriesModel, ['value']);
* (2)
* createListSimply(seriesModel, {
* coordDimensions: ['value'],
* dimensionsCount: 5
* });
*/ function createSeriesDataSimply(seriesModel, opt, nameList) {
opt =
(isArray(opt) && { coordDimensions: opt }) ||
extend({ encodeDefine: seriesModel.getEncode() }, opt)
var source = seriesModel.getSource()
var dimensions = prepareSeriesDataSchema(source, opt).dimensions
var list = new SeriesData$1(dimensions, seriesModel)
list.initData(source, nameList)
return list
}
/*
* 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.
*/ /**
* LegendVisualProvider is an bridge that pick encoded color from data and
* provide to the legend component.
*/ var LegendVisualProvider = /** @class */ (function () {
function LegendVisualProvider( // Function to get data after filtered. It stores all the encoding info
getDataWithEncodedVisual, // Function to get raw data before filtered.
getRawData
) {
this._getDataWithEncodedVisual = getDataWithEncodedVisual
this._getRawData = getRawData
}
LegendVisualProvider.prototype.getAllNames = function () {
var rawData = this._getRawData() // We find the name from the raw data. In case it's filtered by the legend component.
// Normally, the name can be found in rawData, but can't be found in filtered data will display as gray.
return rawData.mapArray(rawData.getName)
}
LegendVisualProvider.prototype.containName = function (name) {
var rawData = this._getRawData()
return rawData.indexOfName(name) >= 0
}
LegendVisualProvider.prototype.indexOfName = function (name) {
// Only get data when necessary.
// Because LegendVisualProvider constructor may be new in the stage that data is not prepared yet.
// Invoking Series#getData immediately will throw an error.
var dataWithEncodedVisual = this._getDataWithEncodedVisual()
return dataWithEncodedVisual.indexOfName(name)
}
LegendVisualProvider.prototype.getItemVisual = function (
dataIndex,
key
) {
// Get encoded visual properties from final filtered data.
var dataWithEncodedVisual = this._getDataWithEncodedVisual()
return dataWithEncodedVisual.getItemVisual(dataIndex, key)
}
return LegendVisualProvider
})()
var LegendVisualProvider$1 = LegendVisualProvider
var PieSeriesModel = /** @class */ (function (_super) {
__extends(PieSeriesModel, _super)
function PieSeriesModel() {
return (_super !== null && _super.apply(this, arguments)) || this
}
/**
* @overwrite
*/ PieSeriesModel.prototype.init = function (option) {
_super.prototype.init.apply(this, arguments) // Enable legend selection for each data item
// Use a function instead of direct access because data reference may changed
this.legendVisualProvider = new LegendVisualProvider$1(
bind$1(this.getData, this),
bind$1(this.getRawData, this)
)
this._defaultLabelLine(option)
}
/**
* @overwrite
*/ PieSeriesModel.prototype.mergeOption = function () {
_super.prototype.mergeOption.apply(this, arguments)
}
/**
* @overwrite
*/ PieSeriesModel.prototype.getInitialData = function () {
return createSeriesDataSimply(this, {
coordDimensions: ['value'],
encodeDefaulter: curry$1(makeSeriesEncodeForNameBased, this)
})
}
/**
* @overwrite
*/ PieSeriesModel.prototype.getDataParams = function (dataIndex) {
var data = this.getData()
var params = _super.prototype.getDataParams.call(this, dataIndex) // FIXME toFixed?
var valueList = []
data.each(data.mapDimension('value'), function (value) {
valueList.push(value)
})
params.percent = getPercentWithPrecision(
valueList,
dataIndex,
data.hostModel.get('percentPrecision')
)
params.$vars.push('percent')
return params
}
PieSeriesModel.prototype._defaultLabelLine = function (option) {
// Extend labelLine emphasis
defaultEmphasis(option, 'labelLine', ['show'])
var labelLineNormalOpt = option.labelLine
var labelLineEmphasisOpt = option.emphasis.labelLine // Not show label line if `label.normal.show = false`
labelLineNormalOpt.show =
labelLineNormalOpt.show && option.label.show
labelLineEmphasisOpt.show =
labelLineEmphasisOpt.show && option.emphasis.label.show
}
PieSeriesModel.type = 'series.pie'
PieSeriesModel.defaultOption = {
// zlevel: 0,
z: 2,
legendHoverLink: true,
colorBy: 'data', // 默认全局居中
center: ['50%', '50%'],
radius: [0, '75%'], // 默认顺时针
clockwise: true,
startAngle: 90, // 最小角度改为0
minAngle: 0, // If the angle of a sector less than `minShowLabelAngle`,
// the label will not be displayed.
minShowLabelAngle: 0, // 选中时扇区偏移量
selectedOffset: 10, // 选择模式,默认关闭,可选single,multiple
// selectedMode: false,
// 南丁格尔玫瑰图模式,'radius'(半径) | 'area'(面积)
// roseType: null,
percentPrecision: 2, // If still show when all data zero.
stillShowZeroSum: true, // cursor: null,
left: 0,
top: 0,
right: 0,
bottom: 0,
width: null,
height: null,
label: {
// color: 'inherit',
// If rotate around circle
rotate: 0,
show: true,
overflow: 'truncate', // 'outer', 'inside', 'center'
position: 'outer', // 'none', 'labelLine', 'edge'. Works only when position is 'outer'
alignTo: 'none', // Closest distance between label and chart edge.
// Works only position is 'outer' and alignTo is 'edge'.
edgeDistance: '25%', // Works only position is 'outer' and alignTo is not 'edge'.
bleedMargin: 10, // Distance between text and label line.
distanceToLabelLine: 5 // formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调
// 默认使用全局文本样式,详见TEXTSTYLE
// distance: 当position为inner时有效,为label位置到圆心的距离与圆半径(环状图为内外半径和)的比例系数
}, // Enabled when label.normal.position is 'outer'
labelLine: {
show: true, // 引导线两段中的第一段长度
length: 15, // 引导线两段中的第二段长度
length2: 15,
smooth: false,
minTurnAngle: 90,
maxSurfaceAngle: 90,
lineStyle: {
// color: 各异,
width: 1,
type: 'solid'
}
},
itemStyle: { borderWidth: 1, borderJoin: 'round' },
showEmptyCircle: true,
emptyCircleStyle: { color: 'lightgray', opacity: 1 },
labelLayout: {
// Hide the overlapped label.
hideOverlap: true
},
emphasis: { scale: true, scaleSize: 5 }, // If use strategy to avoid label overlapping
avoidLabelOverlap: true, // Animation type. Valid values: expansion, scale
animationType: 'expansion',
animationDuration: 1000, // Animation type when update. Valid values: transition, expansion
animationTypeUpdate: 'transition',
animationEasingUpdate: 'cubicInOut',
animationDurationUpdate: 500,
animationEasing: 'cubicInOut'
}
return PieSeriesModel
})(SeriesModel$1)
var PieSeriesModel$1 = PieSeriesModel
function negativeDataFilter(seriesType) {
return {
seriesType: seriesType,
reset: function reset(seriesModel, ecModel) {
var data = seriesModel.getData()
data.filterSelf(function (idx) {
// handle negative value condition
var valueDim = data.mapDimension('value')
var curValue = data.get(valueDim, idx)
if (isNumber(curValue) && !isNaN(curValue) && curValue < 0) {
return false
}
return true
})
}
}
}
function install$g(registers) {
registers.registerChartView(PieView$1)
registers.registerSeriesModel(PieSeriesModel$1)
createLegacyDataSelectAction('pie', registers.registerAction)
registers.registerLayout(curry$1(pieLayout, 'pie'))
registers.registerProcessor(dataFilter('pie'))
registers.registerProcessor(negativeDataFilter('pie'))
}
var GridModel = /** @class */ (function (_super) {
__extends(GridModel, _super)
function GridModel() {
return (_super !== null && _super.apply(this, arguments)) || this
}
GridModel.type = 'grid'
GridModel.dependencies = ['xAxis', 'yAxis']
GridModel.layoutMode = 'box'
GridModel.defaultOption = {
show: false, // zlevel: 0,
z: 0,
left: '10%',
top: 60,
right: '10%',
bottom: 70, // If grid size contain label
containLabel: false, // width: {totalWidth} - left - right,
// height: {totalHeight} - top - bottom,
backgroundColor: 'rgba(0,0,0,0)',
borderWidth: 1,
borderColor: '#ccc'
}
return GridModel
})(ComponentModel$1)
var GridModel$1 = GridModel
var CartesianAxisModel = /** @class */ (function (_super) {
__extends(CartesianAxisModel, _super)
function CartesianAxisModel() {
return (_super !== null && _super.apply(this, arguments)) || this
}
CartesianAxisModel.prototype.getCoordSysModel = function () {
return this.getReferringComponents('grid', SINGLE_REFERRING)
.models[0]
}
CartesianAxisModel.type = 'cartesian2dAxis'
return CartesianAxisModel
})(ComponentModel$1)
mixin(CartesianAxisModel, AxisModelCommonMixin)
var defaultOption = {
show: true, // zlevel: 0,
z: 0, // Inverse the axis.
inverse: false, // Axis name displayed.
name: '', // 'start' | 'middle' | 'end'
nameLocation: 'end', // By degree. By default auto rotate by nameLocation.
nameRotate: null,
nameTruncate: { maxWidth: null, ellipsis: '...', placeholder: '.' }, // Use global text style by default.
nameTextStyle: {}, // The gap between axisName and axisLine.
nameGap: 15, // Default `false` to support tooltip.
silent: false, // Default `false` to avoid legacy user event listener fail.
triggerEvent: false,
tooltip: { show: false },
axisPointer: {},
axisLine: {
show: true,
onZero: true,
onZeroAxisIndex: null,
lineStyle: { color: '#6E7079', width: 1, type: 'solid' }, // The arrow at both ends the the axis.
symbol: ['none', 'none'],
symbolSize: [10, 15]
},
axisTick: {
show: true, // Whether axisTick is inside the grid or outside the grid.
inside: false, // The length of axisTick.
length: 5,
lineStyle: { width: 1 }
},
axisLabel: {
show: true, // Whether axisLabel is inside the grid or outside the grid.
inside: false,
rotate: 0, // true | false | null/undefined (auto)
showMinLabel: null, // true | false | null/undefined (auto)
showMaxLabel: null,
margin: 8, // formatter: null,
fontSize: 12
},
splitLine: {
show: true,
lineStyle: { color: ['#E0E6F1'], width: 1, type: 'solid' }
},
splitArea: {
show: false,
areaStyle: {
color: ['rgba(250,250,250,0.2)', 'rgba(210,219,238,0.2)']
}
}
}
var categoryAxis = merge(
{
// The gap at both ends of the axis. For categoryAxis, boolean.
boundaryGap: true, // Set false to faster category collection.
deduplication: null, // splitArea: {
// show: false
// },
splitLine: { show: false },
axisTick: {
// If tick is align with label when boundaryGap is true
alignWithLabel: false,
interval: 'auto'
},
axisLabel: { interval: 'auto' }
},
defaultOption
)
var valueAxis = merge(
{
boundaryGap: [0, 0],
axisLine: {
// Not shown when other axis is categoryAxis in cartesian
show: 'auto'
},
axisTick: {
// Not shown when other axis is categoryAxis in cartesian
show: 'auto'
}, // TODO
// min/max: [30, datamin, 60] or [20, datamin] or [datamin, 60]
splitNumber: 5,
minorTick: {
// Minor tick, not available for cateogry axis.
show: false, // Split number of minor ticks. The value should be in range of (0, 100)
splitNumber: 5, // Lenght of minor tick
length: 3, // Line style
lineStyle: {
// Default to be same with axisTick
}
},
minorSplitLine: {
show: false,
lineStyle: { color: '#F4F7FD', width: 1 }
}
},
defaultOption
)
var timeAxis = merge(
{
splitNumber: 6,
axisLabel: {
// To eliminate labels that are not nice
showMinLabel: false,
showMaxLabel: false,
rich: { primary: { fontWeight: 'bold' } }
},
splitLine: { show: false }
},
valueAxis
)
var logAxis = defaults({ logBase: 10 }, valueAxis)
var axisDefault = {
category: categoryAxis,
value: valueAxis,
time: timeAxis,
log: logAxis
}
/*
* 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 AXIS_TYPES = { value: 1, category: 1, time: 1, log: 1 }
/**
* Generate sub axis model class
* @param axisName 'x' 'y' 'radius' 'angle' 'parallel' ...
*/ function axisModelCreator(
registers,
axisName,
BaseAxisModelClass,
extraDefaultOption
) {
each$7(AXIS_TYPES, function (v, axisType) {
var defaultOption = merge(
merge({}, axisDefault[axisType], true),
extraDefaultOption,
true
)
var AxisModel = /** @class */ (function (_super) {
__extends(AxisModel, _super)
function AxisModel() {
var _this =
(_super !== null && _super.apply(this, arguments)) || this
_this.type = axisName + 'Axis.' + axisType
return _this
}
AxisModel.prototype.mergeDefaultAndTheme = function (
option,
ecModel
) {
var layoutMode = fetchLayoutMode(this)
var inputPositionParams = layoutMode
? getLayoutParams(option)
: {}
var themeModel = ecModel.getTheme()
merge(option, themeModel.get(axisType + 'Axis'))
merge(option, this.getDefaultOption())
option.type = getAxisType(option)
if (layoutMode) {
mergeLayoutParam(option, inputPositionParams, layoutMode)
}
}
AxisModel.prototype.optionUpdated = function () {
var thisOption = this.option
if (thisOption.type === 'category') {
this.__ordinalMeta = OrdinalMeta$1.createByAxisModel(this)
}
}
/**
* Should not be called before all of 'getInitailData' finished.
* Because categories are collected during initializing data.
*/ AxisModel.prototype.getCategories = function (rawData) {
var option = this.option // FIXME
// warning if called before all of 'getInitailData' finished.
if (option.type === 'category') {
if (rawData) {
return option.data
}
return this.__ordinalMeta.categories
}
}
AxisModel.prototype.getOrdinalMeta = function () {
return this.__ordinalMeta
}
AxisModel.type = axisName + 'Axis.' + axisType
AxisModel.defaultOption = defaultOption
return AxisModel
})(BaseAxisModelClass)
registers.registerComponentModel(AxisModel)
})
registers.registerSubTypeDefaulter(axisName + 'Axis', getAxisType)
}
function getAxisType(option) {
// Default axis with data is category axis
return option.type || (option.data ? 'category' : 'value')
}
var Cartesian = /** @class */ (function () {
function Cartesian(name) {
this.type = 'cartesian'
this._dimList = []
this._axes = {}
this.name = name || ''
}
Cartesian.prototype.getAxis = function (dim) {
return this._axes[dim]
}
Cartesian.prototype.getAxes = function () {
return map$1(
this._dimList,
function (dim) {
return this._axes[dim]
},
this
)
}
Cartesian.prototype.getAxesByScale = function (scaleType) {
scaleType = scaleType.toLowerCase()
return filter(this.getAxes(), function (axis) {
return axis.scale.type === scaleType
})
}
Cartesian.prototype.addAxis = function (axis) {
var dim = axis.dim
this._axes[dim] = axis
this._dimList.push(dim)
}
return Cartesian
})()
var Cartesian$1 = Cartesian
var cartesian2DDimensions = ['x', 'y']
function canCalculateAffineTransform(scale) {
return scale.type === 'interval' || scale.type === 'time'
}
var Cartesian2D = /** @class */ (function (_super) {
__extends(Cartesian2D, _super)
function Cartesian2D() {
var _this =
(_super !== null && _super.apply(this, arguments)) || this
_this.type = 'cartesian2d'
_this.dimensions = cartesian2DDimensions
return _this
}
/**
* Calculate an affine transform matrix if two axes are time or value.
* It's mainly for accelartion on the large time series data.
*/ Cartesian2D.prototype.calcAffineTransform = function () {
this._transform = this._invTransform = null
var xAxisScale = this.getAxis('x').scale
var yAxisScale = this.getAxis('y').scale
if (
!canCalculateAffineTransform(xAxisScale) ||
!canCalculateAffineTransform(yAxisScale)
) {
return
}
var xScaleExtent = xAxisScale.getExtent()
var yScaleExtent = yAxisScale.getExtent()
var start = this.dataToPoint([xScaleExtent[0], yScaleExtent[0]])
var end = this.dataToPoint([xScaleExtent[1], yScaleExtent[1]])
var xScaleSpan = xScaleExtent[1] - xScaleExtent[0]
var yScaleSpan = yScaleExtent[1] - yScaleExtent[0]
if (!xScaleSpan || !yScaleSpan) {
return
} // Accelerate data to point calculation on the special large time series data.
var scaleX = (end[0] - start[0]) / xScaleSpan
var scaleY = (end[1] - start[1]) / yScaleSpan
var translateX = start[0] - xScaleExtent[0] * scaleX
var translateY = start[1] - yScaleExtent[0] * scaleY
var m = (this._transform = [
scaleX,
0,
0,
scaleY,
translateX,
translateY
])
this._invTransform = invert([], m)
}
/**
* Base axis will be used on stacking.
*/ Cartesian2D.prototype.getBaseAxis = function () {
return (
this.getAxesByScale('ordinal')[0] ||
this.getAxesByScale('time')[0] ||
this.getAxis('x')
)
}
Cartesian2D.prototype.containPoint = function (point) {
var axisX = this.getAxis('x')
var axisY = this.getAxis('y')
return (
axisX.contain(axisX.toLocalCoord(point[0])) &&
axisY.contain(axisY.toLocalCoord(point[1]))
)
}
Cartesian2D.prototype.containData = function (data) {
return (
this.getAxis('x').containData(data[0]) &&
this.getAxis('y').containData(data[1])
)
}
Cartesian2D.prototype.containZone = function (data1, data2) {
var zoneDiag1 = this.dataToPoint(data1)
var zoneDiag2 = this.dataToPoint(data2)
var area = this.getArea()
var zone = new BoundingRect$1(
zoneDiag1[0],
zoneDiag1[1],
zoneDiag2[0] - zoneDiag1[0],
zoneDiag2[1] - zoneDiag1[1]
)
return area.intersect(zone)
}
Cartesian2D.prototype.dataToPoint = function (data, clamp, out) {
out = out || []
var xVal = data[0]
var yVal = data[1] // Fast path
if (
this._transform && // It's supported that if data is like `[Inifity, 123]`, where only Y pixel calculated.
xVal != null &&
isFinite(xVal) &&
yVal != null &&
isFinite(yVal)
) {
return applyTransform$1(out, data, this._transform)
}
var xAxis = this.getAxis('x')
var yAxis = this.getAxis('y')
out[0] = xAxis.toGlobalCoord(xAxis.dataToCoord(xVal, clamp))
out[1] = yAxis.toGlobalCoord(yAxis.dataToCoord(yVal, clamp))
return out
}
Cartesian2D.prototype.clampData = function (data, out) {
var xScale = this.getAxis('x').scale
var yScale = this.getAxis('y').scale
var xAxisExtent = xScale.getExtent()
var yAxisExtent = yScale.getExtent()
var x = xScale.parse(data[0])
var y = yScale.parse(data[1])
out = out || []
out[0] = Math.min(
Math.max(Math.min(xAxisExtent[0], xAxisExtent[1]), x),
Math.max(xAxisExtent[0], xAxisExtent[1])
)
out[1] = Math.min(
Math.max(Math.min(yAxisExtent[0], yAxisExtent[1]), y),
Math.max(yAxisExtent[0], yAxisExtent[1])
)
return out
}
Cartesian2D.prototype.pointToData = function (point, clamp) {
var out = []
if (this._invTransform) {
return applyTransform$1(out, point, this._invTransform)
}
var xAxis = this.getAxis('x')
var yAxis = this.getAxis('y')
out[0] = xAxis.coordToData(xAxis.toLocalCoord(point[0]), clamp)
out[1] = yAxis.coordToData(yAxis.toLocalCoord(point[1]), clamp)
return out
}
Cartesian2D.prototype.getOtherAxis = function (axis) {
return this.getAxis(axis.dim === 'x' ? 'y' : 'x')
}
/**
* Get rect area of cartesian.
* Area will have a contain function to determine if a point is in the coordinate system.
*/ Cartesian2D.prototype.getArea = function () {
var xExtent = this.getAxis('x').getGlobalExtent()
var yExtent = this.getAxis('y').getGlobalExtent()
var x = Math.min(xExtent[0], xExtent[1])
var y = Math.min(yExtent[0], yExtent[1])
var width = Math.max(xExtent[0], xExtent[1]) - x
var height = Math.max(yExtent[0], yExtent[1]) - y
return new BoundingRect$1(x, y, width, height)
}
return Cartesian2D
})(Cartesian$1)
var Cartesian2D$1 = Cartesian2D
var Axis2D = /** @class */ (function (_super) {
__extends(Axis2D, _super)
function Axis2D(dim, scale, coordExtent, axisType, position) {
var _this = _super.call(this, dim, scale, coordExtent) || this
/**
* Index of axis, can be used as key
* Injected outside.
*/ _this.index = 0
_this.type = axisType || 'value'
_this.position = position || 'bottom'
return _this
}
Axis2D.prototype.isHorizontal = function () {
var position = this.position
return position === 'top' || position === 'bottom'
}
/**
* Each item cooresponds to this.getExtent(), which
* means globalExtent[0] may greater than globalExtent[1],
* unless `asc` is input.
*
* @param {boolean} [asc]
* @return {Array.}
*/ Axis2D.prototype.getGlobalExtent = function (asc) {
var ret = this.getExtent()
ret[0] = this.toGlobalCoord(ret[0])
ret[1] = this.toGlobalCoord(ret[1])
asc && ret[0] > ret[1] && ret.reverse()
return ret
}
Axis2D.prototype.pointToData = function (point, clamp) {
return this.coordToData(
this.toLocalCoord(point[this.dim === 'x' ? 0 : 1]),
clamp
)
}
/**
* Set ordinalSortInfo
* @param info new OrdinalSortInfo
*/ Axis2D.prototype.setCategorySortInfo = function (info) {
if (this.type !== 'category') {
return false
}
this.model.option.categorySortInfo = info
this.scale.setSortInfo(info)
}
return Axis2D
})(Axis$1)
var Axis2D$1 = Axis2D
/**
* Can only be called after coordinate system creation stage.
* (Can be called before coordinate system update stage).
*/ function layout$1(gridModel, axisModel, opt) {
opt = opt || {}
var grid = gridModel.coordinateSystem
var axis = axisModel.axis
var layout = {}
var otherAxisOnZeroOf = axis.getAxesOnZeroOf()[0]
var rawAxisPosition = axis.position
var axisPosition = otherAxisOnZeroOf ? 'onZero' : rawAxisPosition
var axisDim = axis.dim
var rect = grid.getRect()
var rectBound = [
rect.x,
rect.x + rect.width,
rect.y,
rect.y + rect.height
]
var idx = { left: 0, right: 1, top: 0, bottom: 1, onZero: 2 }
var axisOffset = axisModel.get('offset') || 0
var posBound =
axisDim === 'x'
? [rectBound[2] - axisOffset, rectBound[3] + axisOffset]
: [rectBound[0] - axisOffset, rectBound[1] + axisOffset]
if (otherAxisOnZeroOf) {
var onZeroCoord = otherAxisOnZeroOf.toGlobalCoord(
otherAxisOnZeroOf.dataToCoord(0)
)
posBound[idx.onZero] = Math.max(
Math.min(onZeroCoord, posBound[1]),
posBound[0]
)
} // Axis position
layout.position = [
axisDim === 'y' ? posBound[idx[axisPosition]] : rectBound[0],
axisDim === 'x' ? posBound[idx[axisPosition]] : rectBound[3]
] // Axis rotation
layout.rotation = (Math.PI / 2) * (axisDim === 'x' ? 0 : 1) // Tick and label direction, x y is axisDim
var dirMap = { top: -1, bottom: 1, left: -1, right: 1 }
layout.labelDirection =
layout.tickDirection =
layout.nameDirection =
dirMap[rawAxisPosition]
layout.labelOffset = otherAxisOnZeroOf
? posBound[idx[rawAxisPosition]] - posBound[idx.onZero]
: 0
if (axisModel.get(['axisTick', 'inside'])) {
layout.tickDirection = -layout.tickDirection
}
if (
retrieve(opt.labelInside, axisModel.get(['axisLabel', 'inside']))
) {
layout.labelDirection = -layout.labelDirection
} // Special label rotation
var labelRotate = axisModel.get(['axisLabel', 'rotate'])
layout.labelRotate =
axisPosition === 'top' ? -labelRotate : labelRotate // Over splitLine and splitArea
layout.z2 = 1
return layout
}
function isCartesian2DSeries(seriesModel) {
return seriesModel.get('coordinateSystem') === 'cartesian2d'
}
function findAxisModels(seriesModel) {
var axisModelMap = { xAxisModel: null, yAxisModel: null }
each$7(axisModelMap, function (v, key) {
var axisType = key.replace(/Model$/, '')
var axisModel = seriesModel.getReferringComponents(
axisType,
SINGLE_REFERRING
).models[0]
axisModelMap[key] = axisModel
})
return axisModelMap
}
var mathLog = Math.log
function alignScaleTicks(scale, axisModel, alignToScale) {
var intervalScaleProto = IntervalScale$1.prototype // NOTE: There is a precondition for log scale here:
// In log scale we store _interval and _extent of exponent value.
// So if we use the method of InternalScale to set/get these data.
// It process the exponent value, which is linear and what we want here.
var alignToTicks = intervalScaleProto.getTicks.call(alignToScale)
var alignToNicedTicks = intervalScaleProto.getTicks.call(
alignToScale,
true
)
var alignToSplitNumber = alignToTicks.length - 1
var alignToInterval =
intervalScaleProto.getInterval.call(alignToScale)
var scaleExtent = getScaleExtent(scale, axisModel)
var rawExtent = scaleExtent.extent
var isMinFixed = scaleExtent.fixMin
var isMaxFixed = scaleExtent.fixMax
if (scale.type === 'log') {
var logBase = mathLog(scale.base)
rawExtent = [
mathLog(rawExtent[0]) / logBase,
mathLog(rawExtent[1]) / logBase
]
}
scale.setExtent(rawExtent[0], rawExtent[1])
scale.calcNiceExtent({
splitNumber: alignToSplitNumber,
fixMin: isMinFixed,
fixMax: isMaxFixed
})
var extent = intervalScaleProto.getExtent.call(scale) // Need to update the rawExtent.
// Because value in rawExtent may be not parsed. e.g. 'dataMin', 'dataMax'
if (isMinFixed) {
rawExtent[0] = extent[0]
}
if (isMaxFixed) {
rawExtent[1] = extent[1]
}
var interval = intervalScaleProto.getInterval.call(scale)
var min = rawExtent[0]
var max = rawExtent[1]
if (isMinFixed && isMaxFixed) {
// User set min, max, divide to get new interval
interval = (max - min) / alignToSplitNumber
} else if (isMinFixed) {
max = rawExtent[0] + interval * alignToSplitNumber // User set min, expand extent on the other side
while (
max < rawExtent[1] &&
isFinite(max) &&
isFinite(rawExtent[1])
) {
interval = increaseInterval(interval)
max = rawExtent[0] + interval * alignToSplitNumber
}
} else if (isMaxFixed) {
// User set max, expand extent on the other side
min = rawExtent[1] - interval * alignToSplitNumber
while (
min > rawExtent[0] &&
isFinite(min) &&
isFinite(rawExtent[0])
) {
interval = increaseInterval(interval)
min = rawExtent[1] - interval * alignToSplitNumber
}
} else {
var nicedSplitNumber = scale.getTicks().length - 1
if (nicedSplitNumber > alignToSplitNumber) {
interval = increaseInterval(interval)
}
var range = interval * alignToSplitNumber
max = Math.ceil(rawExtent[1] / interval) * interval
min = round$1(max - range) // Not change the result that crossing zero.
if (min < 0 && rawExtent[0] >= 0) {
min = 0
max = round$1(range)
} else if (max > 0 && rawExtent[1] <= 0) {
max = 0
min = -round$1(range)
}
} // Adjust min, max based on the extent of alignTo. When min or max is set in alignTo scale
var t0 =
(alignToTicks[0].value - alignToNicedTicks[0].value) /
alignToInterval
var t1 =
(alignToTicks[alignToSplitNumber].value -
alignToNicedTicks[alignToSplitNumber].value) /
alignToInterval // NOTE: Must in setExtent -> setInterval -> setNiceExtent order.
intervalScaleProto.setExtent.call(
scale,
min + interval * t0,
max + interval * t1
)
intervalScaleProto.setInterval.call(scale, interval)
if (t0 || t1) {
intervalScaleProto.setNiceExtent.call(
scale,
min + interval,
max - interval
)
}
}
var Grid = /** @class */ (function () {
function Grid(gridModel, ecModel, api) {
// FIXME:TS where used (different from registered type 'cartesian2d')?
this.type = 'grid'
this._coordsMap = {}
this._coordsList = []
this._axesMap = {}
this._axesList = []
this.axisPointerEnabled = true
this.dimensions = cartesian2DDimensions
this._initCartesian(gridModel, ecModel, api)
this.model = gridModel
}
Grid.prototype.getRect = function () {
return this._rect
}
Grid.prototype.update = function (ecModel, api) {
var axesMap = this._axesMap
this._updateScale(ecModel, this.model)
function updateAxisTicks(axes) {
var alignTo // Axis is added in order of axisIndex.
var axesIndices = keys(axes)
var len = axesIndices.length
if (!len) {
return
}
var axisNeedsAlign = [] // Process once and calculate the ticks for those don't use alignTicks.
for (var i = len - 1; i >= 0; i--) {
var idx = +axesIndices[i] // Convert to number.
var axis = axes[idx]
var model = axis.model
var scale = axis.scale
if (
// Only value and log axis without interval support alignTicks.
isIntervalOrLogScale(scale) &&
model.get('alignTicks') &&
model.get('interval') == null
) {
axisNeedsAlign.push(axis)
} else {
niceScaleExtent(scale, model)
if (isIntervalOrLogScale(scale)) {
// Can only align to interval or log axis.
alignTo = axis
}
}
} // PENDING. Should we find the axis that both set interval, min, max and align to this one?
if (axisNeedsAlign.length) {
if (!alignTo) {
alignTo = axisNeedsAlign.pop()
niceScaleExtent(alignTo.scale, alignTo.model)
}
each$7(axisNeedsAlign, function (axis) {
alignScaleTicks(axis.scale, axis.model, alignTo.scale)
})
}
}
updateAxisTicks(axesMap.x)
updateAxisTicks(axesMap.y) // Key: axisDim_axisIndex, value: boolean, whether onZero target.
var onZeroRecords = {}
each$7(axesMap.x, function (xAxis) {
fixAxisOnZero(axesMap, 'y', xAxis, onZeroRecords)
})
each$7(axesMap.y, function (yAxis) {
fixAxisOnZero(axesMap, 'x', yAxis, onZeroRecords)
}) // Resize again if containLabel is enabled
// FIXME It may cause getting wrong grid size in data processing stage
this.resize(this.model, api)
}
/**
* Resize the grid
*/ Grid.prototype.resize = function (
gridModel,
api,
ignoreContainLabel
) {
var boxLayoutParams = gridModel.getBoxLayoutParams()
var isContainLabel =
!ignoreContainLabel && gridModel.get('containLabel')
var gridRect = getLayoutRect(boxLayoutParams, {
width: api.getWidth(),
height: api.getHeight()
})
this._rect = gridRect
var axesList = this._axesList
adjustAxes() // Minus label size
if (isContainLabel) {
each$7(axesList, function (axis) {
if (!axis.model.get(['axisLabel', 'inside'])) {
var labelUnionRect = estimateLabelUnionRect(axis)
if (labelUnionRect) {
var dim = axis.isHorizontal() ? 'height' : 'width'
var margin = axis.model.get(['axisLabel', 'margin'])
gridRect[dim] -= labelUnionRect[dim] + margin
if (axis.position === 'top') {
gridRect.y += labelUnionRect.height + margin
} else if (axis.position === 'left') {
gridRect.x += labelUnionRect.width + margin
}
}
}
})
adjustAxes()
}
each$7(this._coordsList, function (coord) {
// Calculate affine matrix to accelerate the data to point transform.
// If all the axes scales are time or value.
coord.calcAffineTransform()
})
function adjustAxes() {
each$7(axesList, function (axis) {
var isHorizontal = axis.isHorizontal()
var extent = isHorizontal
? [0, gridRect.width]
: [0, gridRect.height]
var idx = axis.inverse ? 1 : 0
axis.setExtent(extent[idx], extent[1 - idx])
updateAxisTransform(
axis,
isHorizontal ? gridRect.x : gridRect.y
)
})
}
}
Grid.prototype.getAxis = function (dim, axisIndex) {
var axesMapOnDim = this._axesMap[dim]
if (axesMapOnDim != null) {
return axesMapOnDim[axisIndex || 0]
}
}
Grid.prototype.getAxes = function () {
return this._axesList.slice()
}
Grid.prototype.getCartesian = function (xAxisIndex, yAxisIndex) {
if (xAxisIndex != null && yAxisIndex != null) {
var key = 'x' + xAxisIndex + 'y' + yAxisIndex
return this._coordsMap[key]
}
if (isObject$2(xAxisIndex)) {
yAxisIndex = xAxisIndex.yAxisIndex
xAxisIndex = xAxisIndex.xAxisIndex
}
for (
var i = 0, coordList = this._coordsList;
i < coordList.length;
i++
) {
if (
coordList[i].getAxis('x').index === xAxisIndex ||
coordList[i].getAxis('y').index === yAxisIndex
) {
return coordList[i]
}
}
}
Grid.prototype.getCartesians = function () {
return this._coordsList.slice()
}
/**
* @implements
*/ Grid.prototype.convertToPixel = function (
ecModel,
finder,
value
) {
var target = this._findConvertTarget(finder)
return target.cartesian
? target.cartesian.dataToPoint(value)
: target.axis
? target.axis.toGlobalCoord(target.axis.dataToCoord(value))
: null
}
/**
* @implements
*/ Grid.prototype.convertFromPixel = function (
ecModel,
finder,
value
) {
var target = this._findConvertTarget(finder)
return target.cartesian
? target.cartesian.pointToData(value)
: target.axis
? target.axis.coordToData(target.axis.toLocalCoord(value))
: null
}
Grid.prototype._findConvertTarget = function (finder) {
var seriesModel = finder.seriesModel
var xAxisModel =
finder.xAxisModel ||
(seriesModel &&
seriesModel.getReferringComponents('xAxis', SINGLE_REFERRING)
.models[0])
var yAxisModel =
finder.yAxisModel ||
(seriesModel &&
seriesModel.getReferringComponents('yAxis', SINGLE_REFERRING)
.models[0])
var gridModel = finder.gridModel
var coordsList = this._coordsList
var cartesian
var axis
if (seriesModel) {
cartesian = seriesModel.coordinateSystem
indexOf(coordsList, cartesian) < 0 && (cartesian = null)
} else if (xAxisModel && yAxisModel) {
cartesian = this.getCartesian(
xAxisModel.componentIndex,
yAxisModel.componentIndex
)
} else if (xAxisModel) {
axis = this.getAxis('x', xAxisModel.componentIndex)
} else if (yAxisModel) {
axis = this.getAxis('y', yAxisModel.componentIndex)
} // Lowest priority.
else if (gridModel) {
var grid = gridModel.coordinateSystem
if (grid === this) {
cartesian = this._coordsList[0]
}
}
return { cartesian: cartesian, axis: axis }
}
/**
* @implements
*/ Grid.prototype.containPoint = function (point) {
var coord = this._coordsList[0]
if (coord) {
return coord.containPoint(point)
}
}
/**
* Initialize cartesian coordinate systems
*/ Grid.prototype._initCartesian = function (
gridModel,
ecModel,
api
) {
var _this = this
var grid = this
var axisPositionUsed = {
left: false,
right: false,
top: false,
bottom: false
}
var axesMap = { x: {}, y: {} }
var axesCount = { x: 0, y: 0 } /// Create axis
ecModel.eachComponent('xAxis', createAxisCreator('x'), this)
ecModel.eachComponent('yAxis', createAxisCreator('y'), this)
if (!axesCount.x || !axesCount.y) {
// Roll back when there no either x or y axis
this._axesMap = {}
this._axesList = []
return
}
this._axesMap = axesMap /// Create cartesian2d
each$7(axesMap.x, function (xAxis, xAxisIndex) {
each$7(axesMap.y, function (yAxis, yAxisIndex) {
var key = 'x' + xAxisIndex + 'y' + yAxisIndex
var cartesian = new Cartesian2D$1(key)
cartesian.master = _this
cartesian.model = gridModel
_this._coordsMap[key] = cartesian
_this._coordsList.push(cartesian)
cartesian.addAxis(xAxis)
cartesian.addAxis(yAxis)
})
})
function createAxisCreator(dimName) {
return function (axisModel, idx) {
if (!isAxisUsedInTheGrid(axisModel, gridModel)) {
return
}
var axisPosition = axisModel.get('position')
if (dimName === 'x') {
// Fix position
if (axisPosition !== 'top' && axisPosition !== 'bottom') {
// Default bottom of X
axisPosition = axisPositionUsed.bottom ? 'top' : 'bottom'
}
} else {
// Fix position
if (axisPosition !== 'left' && axisPosition !== 'right') {
// Default left of Y
axisPosition = axisPositionUsed.left ? 'right' : 'left'
}
}
axisPositionUsed[axisPosition] = true
var axis = new Axis2D$1(
dimName,
createScaleByModel(axisModel),
[0, 0],
axisModel.get('type'),
axisPosition
)
var isCategory = axis.type === 'category'
axis.onBand = isCategory && axisModel.get('boundaryGap')
axis.inverse = axisModel.get('inverse') // Inject axis into axisModel
axisModel.axis = axis // Inject axisModel into axis
axis.model = axisModel // Inject grid info axis
axis.grid = grid // Index of axis, can be used as key
axis.index = idx
grid._axesList.push(axis)
axesMap[dimName][idx] = axis
axesCount[dimName]++
}
}
}
/**
* Update cartesian properties from series.
*/ Grid.prototype._updateScale = function (ecModel, gridModel) {
// Reset scale
each$7(this._axesList, function (axis) {
axis.scale.setExtent(Infinity, -Infinity)
if (axis.type === 'category') {
var categorySortInfo = axis.model.get('categorySortInfo')
axis.scale.setSortInfo(categorySortInfo)
}
})
ecModel.eachSeries(function (seriesModel) {
if (isCartesian2DSeries(seriesModel)) {
var axesModelMap = findAxisModels(seriesModel)
var xAxisModel = axesModelMap.xAxisModel
var yAxisModel = axesModelMap.yAxisModel
if (
!isAxisUsedInTheGrid(xAxisModel, gridModel) ||
!isAxisUsedInTheGrid(yAxisModel, gridModel)
) {
return
}
var cartesian = this.getCartesian(
xAxisModel.componentIndex,
yAxisModel.componentIndex
)
var data = seriesModel.getData()
var xAxis = cartesian.getAxis('x')
var yAxis = cartesian.getAxis('y')
unionExtent(data, xAxis)
unionExtent(data, yAxis)
}
}, this)
function unionExtent(data, axis) {
each$7(getDataDimensionsOnAxis(data, axis.dim), function (dim) {
axis.scale.unionExtentFromData(data, dim)
})
}
}
/**
* @param dim 'x' or 'y' or 'auto' or null/undefined
*/ Grid.prototype.getTooltipAxes = function (dim) {
var baseAxes = []
var otherAxes = []
each$7(this.getCartesians(), function (cartesian) {
var baseAxis =
dim != null && dim !== 'auto'
? cartesian.getAxis(dim)
: cartesian.getBaseAxis()
var otherAxis = cartesian.getOtherAxis(baseAxis)
indexOf(baseAxes, baseAxis) < 0 && baseAxes.push(baseAxis)
indexOf(otherAxes, otherAxis) < 0 && otherAxes.push(otherAxis)
})
return { baseAxes: baseAxes, otherAxes: otherAxes }
}
Grid.create = function (ecModel, api) {
var grids = []
ecModel.eachComponent('grid', function (gridModel, idx) {
var grid = new Grid(gridModel, ecModel, api)
grid.name = 'grid_' + idx // dataSampling requires axis extent, so resize
// should be performed in create stage.
grid.resize(gridModel, api, true)
gridModel.coordinateSystem = grid
grids.push(grid)
}) // Inject the coordinateSystems into seriesModel
ecModel.eachSeries(function (seriesModel) {
if (!isCartesian2DSeries(seriesModel)) {
return
}
var axesModelMap = findAxisModels(seriesModel)
var xAxisModel = axesModelMap.xAxisModel
var yAxisModel = axesModelMap.yAxisModel
var gridModel = xAxisModel.getCoordSysModel()
var grid = gridModel.coordinateSystem
seriesModel.coordinateSystem = grid.getCartesian(
xAxisModel.componentIndex,
yAxisModel.componentIndex
)
})
return grids
} // For deciding which dimensions to use when creating list data
Grid.dimensions = cartesian2DDimensions
return Grid
})()
/**
* Check if the axis is used in the specified grid.
*/ function isAxisUsedInTheGrid(axisModel, gridModel) {
return axisModel.getCoordSysModel() === gridModel
}
function fixAxisOnZero(
axesMap,
otherAxisDim,
axis, // Key: see `getOnZeroRecordKey`
onZeroRecords
) {
axis.getAxesOnZeroOf = function () {
// TODO: onZero of multiple axes.
return otherAxisOnZeroOf ? [otherAxisOnZeroOf] : []
} // onZero can not be enabled in these two situations:
// 1. When any other axis is a category axis.
// 2. When no axis is cross 0 point.
var otherAxes = axesMap[otherAxisDim]
var otherAxisOnZeroOf
var axisModel = axis.model
var onZero = axisModel.get(['axisLine', 'onZero'])
var onZeroAxisIndex = axisModel.get(['axisLine', 'onZeroAxisIndex'])
if (!onZero) {
return
} // If target axis is specified.
if (onZeroAxisIndex != null) {
if (canOnZeroToAxis(otherAxes[onZeroAxisIndex])) {
otherAxisOnZeroOf = otherAxes[onZeroAxisIndex]
}
} else {
// Find the first available other axis.
for (var idx in otherAxes) {
if (
otherAxes.hasOwnProperty(idx) &&
canOnZeroToAxis(otherAxes[idx]) && // Consider that two Y axes on one value axis,
// if both onZero, the two Y axes overlap.
!onZeroRecords[getOnZeroRecordKey(otherAxes[idx])]
) {
otherAxisOnZeroOf = otherAxes[idx]
break
}
}
}
if (otherAxisOnZeroOf) {
onZeroRecords[getOnZeroRecordKey(otherAxisOnZeroOf)] = true
}
function getOnZeroRecordKey(axis) {
return axis.dim + '_' + axis.index
}
}
function canOnZeroToAxis(axis) {
return (
axis &&
axis.type !== 'category' &&
axis.type !== 'time' &&
ifAxisCrossZero(axis)
)
}
function updateAxisTransform(axis, coordBase) {
var axisExtent = axis.getExtent()
var axisExtentSum = axisExtent[0] + axisExtent[1] // Fast transform
axis.toGlobalCoord =
axis.dim === 'x'
? function (coord) {
return coord + coordBase
}
: function (coord) {
return axisExtentSum - coord + coordBase
}
axis.toLocalCoord =
axis.dim === 'x'
? function (coord) {
return coord - coordBase
}
: function (coord) {
return axisExtentSum - coord + coordBase
}
}
var Grid$1 = Grid
var PI = Math.PI
/**
* A final axis is translated and rotated from a "standard axis".
* So opt.position and opt.rotation is required.
*
* A standard axis is and axis from [0, 0] to [0, axisExtent[1]],
* for example: (0, 0) ------------> (0, 50)
*
* nameDirection or tickDirection or labelDirection is 1 means tick
* or label is below the standard axis, whereas is -1 means above
* the standard axis. labelOffset means offset between label and axis,
* which is useful when 'onZero', where axisLabel is in the grid and
* label in outside grid.
*
* Tips: like always,
* positive rotation represents anticlockwise, and negative rotation
* represents clockwise.
* The direction of position coordinate is the same as the direction
* of screen coordinate.
*
* Do not need to consider axis 'inverse', which is auto processed by
* axis extent.
*/ var AxisBuilder = /** @class */ (function () {
function AxisBuilder(axisModel, opt) {
this.group = new Group$3()
this.opt = opt
this.axisModel = axisModel // Default value
defaults(opt, {
labelOffset: 0,
nameDirection: 1,
tickDirection: 1,
labelDirection: 1,
silent: true,
handleAutoShown: function handleAutoShown() {
return true
}
}) // FIXME Not use a seperate text group?
var transformGroup = new Group$3({
x: opt.position[0],
y: opt.position[1],
rotation: opt.rotation
}) // this.group.add(transformGroup);
// this._transformGroup = transformGroup;
transformGroup.updateTransform()
this._transformGroup = transformGroup
}
AxisBuilder.prototype.hasBuilder = function (name) {
return !!builders[name]
}
AxisBuilder.prototype.add = function (name) {
builders[name](
this.opt,
this.axisModel,
this.group,
this._transformGroup
)
}
AxisBuilder.prototype.getGroup = function () {
return this.group
}
AxisBuilder.innerTextLayout = function (
axisRotation,
textRotation,
direction
) {
var rotationDiff = remRadian(textRotation - axisRotation)
var textAlign
var textVerticalAlign
if (isRadianAroundZero(rotationDiff)) {
// Label is parallel with axis line.
textVerticalAlign = direction > 0 ? 'top' : 'bottom'
textAlign = 'center'
} else if (isRadianAroundZero(rotationDiff - PI)) {
// Label is inverse parallel with axis line.
textVerticalAlign = direction > 0 ? 'bottom' : 'top'
textAlign = 'center'
} else {
textVerticalAlign = 'middle'
if (rotationDiff > 0 && rotationDiff < PI) {
textAlign = direction > 0 ? 'right' : 'left'
} else {
textAlign = direction > 0 ? 'left' : 'right'
}
}
return {
rotation: rotationDiff,
textAlign: textAlign,
textVerticalAlign: textVerticalAlign
}
}
AxisBuilder.makeAxisEventDataBase = function (axisModel) {
var eventData = {
componentType: axisModel.mainType,
componentIndex: axisModel.componentIndex
}
eventData[axisModel.mainType + 'Index'] = axisModel.componentIndex
return eventData
}
AxisBuilder.isLabelSilent = function (axisModel) {
var tooltipOpt = axisModel.get('tooltip')
return (
axisModel.get('silent') || // Consider mouse cursor, add these restrictions.
!(
axisModel.get('triggerEvent') ||
(tooltipOpt && tooltipOpt.show)
)
)
}
return AxisBuilder
})()
var builders = {
axisLine: function axisLine(opt, axisModel, group, transformGroup) {
var shown = axisModel.get(['axisLine', 'show'])
if (shown === 'auto' && opt.handleAutoShown) {
shown = opt.handleAutoShown('axisLine')
}
if (!shown) {
return
}
var extent = axisModel.axis.getExtent()
var matrix = transformGroup.transform
var pt1 = [extent[0], 0]
var pt2 = [extent[1], 0]
if (matrix) {
applyTransform$1(pt1, pt1, matrix)
applyTransform$1(pt2, pt2, matrix)
}
var lineStyle = extend(
{ lineCap: 'round' },
axisModel.getModel(['axisLine', 'lineStyle']).getLineStyle()
)
var line = new Line$1({
// Id for animation
subPixelOptimize: true,
shape: { x1: pt1[0], y1: pt1[1], x2: pt2[0], y2: pt2[1] },
style: lineStyle,
strokeContainThreshold: opt.strokeContainThreshold || 5,
silent: true,
z2: 1
})
line.anid = 'line'
group.add(line)
var arrows = axisModel.get(['axisLine', 'symbol'])
if (arrows != null) {
var arrowSize = axisModel.get(['axisLine', 'symbolSize'])
if (isString(arrows)) {
// Use the same arrow for start and end point
arrows = [arrows, arrows]
}
if (isString(arrowSize) || isNumber(arrowSize)) {
// Use the same size for width and height
arrowSize = [arrowSize, arrowSize]
}
var arrowOffset = normalizeSymbolOffset(
axisModel.get(['axisLine', 'symbolOffset']) || 0,
arrowSize
)
var symbolWidth_1 = arrowSize[0]
var symbolHeight_1 = arrowSize[1]
each$7(
[
{
rotate: opt.rotation + Math.PI / 2,
offset: arrowOffset[0],
r: 0
},
{
rotate: opt.rotation - Math.PI / 2,
offset: arrowOffset[1],
r: Math.sqrt(
(pt1[0] - pt2[0]) * (pt1[0] - pt2[0]) +
(pt1[1] - pt2[1]) * (pt1[1] - pt2[1])
)
}
],
function (point, index) {
if (arrows[index] !== 'none' && arrows[index] != null) {
var symbol = createSymbol(
arrows[index],
-symbolWidth_1 / 2,
-symbolHeight_1 / 2,
symbolWidth_1,
symbolHeight_1,
lineStyle.stroke,
true
) // Calculate arrow position with offset
var r = point.r + point.offset
symbol.attr({
rotation: point.rotate,
x: pt1[0] + r * Math.cos(opt.rotation),
y: pt1[1] - r * Math.sin(opt.rotation),
silent: true,
z2: 11
})
group.add(symbol)
}
}
)
}
},
axisTickLabel: function axisTickLabel(
opt,
axisModel,
group,
transformGroup
) {
var ticksEls = buildAxisMajorTicks(
group,
transformGroup,
axisModel,
opt
)
var labelEls = buildAxisLabel(
group,
transformGroup,
axisModel,
opt
)
fixMinMaxLabelShow(axisModel, labelEls, ticksEls)
buildAxisMinorTicks(
group,
transformGroup,
axisModel,
opt.tickDirection
) // This bit fixes the label overlap issue for the time chart.
// See https://github.com/apache/echarts/issues/14266 for more.
if (axisModel.get(['axisLabel', 'hideOverlap'])) {
var labelList = prepareLayoutList(
map$1(labelEls, function (label) {
return {
label: label,
priority: label.z2,
defaultAttr: { ignore: label.ignore }
}
})
)
hideOverlap(labelList)
}
},
axisName: function axisName(opt, axisModel, group, transformGroup) {
var name = retrieve(opt.axisName, axisModel.get('name'))
if (!name) {
return
}
var nameLocation = axisModel.get('nameLocation')
var nameDirection = opt.nameDirection
var textStyleModel = axisModel.getModel('nameTextStyle')
var gap = axisModel.get('nameGap') || 0
var extent = axisModel.axis.getExtent()
var gapSignal = extent[0] > extent[1] ? -1 : 1
var pos = [
nameLocation === 'start'
? extent[0] - gapSignal * gap
: nameLocation === 'end'
? extent[1] + gapSignal * gap
: (extent[0] + extent[1]) / 2, // Reuse labelOffset.
isNameLocationCenter(nameLocation)
? opt.labelOffset + nameDirection * gap
: 0
]
var labelLayout
var nameRotation = axisModel.get('nameRotate')
if (nameRotation != null) {
nameRotation = (nameRotation * PI) / 180 // To radian.
}
var axisNameAvailableWidth
if (isNameLocationCenter(nameLocation)) {
labelLayout = AxisBuilder.innerTextLayout(
opt.rotation,
nameRotation != null ? nameRotation : opt.rotation, // Adapt to axis.
nameDirection
)
} else {
labelLayout = endTextLayout(
opt.rotation,
nameLocation,
nameRotation || 0,
extent
)
axisNameAvailableWidth = opt.axisNameAvailableWidth
if (axisNameAvailableWidth != null) {
axisNameAvailableWidth = Math.abs(
axisNameAvailableWidth / Math.sin(labelLayout.rotation)
)
!isFinite(axisNameAvailableWidth) &&
(axisNameAvailableWidth = null)
}
}
var textFont = textStyleModel.getFont()
var truncateOpt = axisModel.get('nameTruncate', true) || {}
var ellipsis = truncateOpt.ellipsis
var maxWidth = retrieve(
opt.nameTruncateMaxWidth,
truncateOpt.maxWidth,
axisNameAvailableWidth
)
var textEl = new ZRText$1({
x: pos[0],
y: pos[1],
rotation: labelLayout.rotation,
silent: AxisBuilder.isLabelSilent(axisModel),
style: createTextStyle(textStyleModel, {
text: name,
font: textFont,
overflow: 'truncate',
width: maxWidth,
ellipsis: ellipsis,
fill:
textStyleModel.getTextColor() ||
axisModel.get(['axisLine', 'lineStyle', 'color']),
align: textStyleModel.get('align') || labelLayout.textAlign,
verticalAlign:
textStyleModel.get('verticalAlign') ||
labelLayout.textVerticalAlign
}),
z2: 1
})
setTooltipConfig({
el: textEl,
componentModel: axisModel,
itemName: name
})
textEl.__fullText = name // Id for animation
textEl.anid = 'name'
if (axisModel.get('triggerEvent')) {
var eventData = AxisBuilder.makeAxisEventDataBase(axisModel)
eventData.targetType = 'axisName'
eventData.name = name
getECData(textEl).eventData = eventData
} // FIXME
transformGroup.add(textEl)
textEl.updateTransform()
group.add(textEl)
textEl.decomposeTransform()
}
}
function endTextLayout(rotation, textPosition, textRotate, extent) {
var rotationDiff = remRadian(textRotate - rotation)
var textAlign
var textVerticalAlign
var inverse = extent[0] > extent[1]
var onLeft =
(textPosition === 'start' && !inverse) ||
(textPosition !== 'start' && inverse)
if (isRadianAroundZero(rotationDiff - PI / 2)) {
textVerticalAlign = onLeft ? 'bottom' : 'top'
textAlign = 'center'
} else if (isRadianAroundZero(rotationDiff - PI * 1.5)) {
textVerticalAlign = onLeft ? 'top' : 'bottom'
textAlign = 'center'
} else {
textVerticalAlign = 'middle'
if (rotationDiff < PI * 1.5 && rotationDiff > PI / 2) {
textAlign = onLeft ? 'left' : 'right'
} else {
textAlign = onLeft ? 'right' : 'left'
}
}
return {
rotation: rotationDiff,
textAlign: textAlign,
textVerticalAlign: textVerticalAlign
}
}
function fixMinMaxLabelShow(axisModel, labelEls, tickEls) {
if (shouldShowAllLabels(axisModel.axis)) {
return
} // If min or max are user set, we need to check
// If the tick on min(max) are overlap on their neighbour tick
// If they are overlapped, we need to hide the min(max) tick label
var showMinLabel = axisModel.get(['axisLabel', 'showMinLabel'])
var showMaxLabel = axisModel.get(['axisLabel', 'showMaxLabel']) // FIXME
// Have not consider onBand yet, where tick els is more than label els.
labelEls = labelEls || []
tickEls = tickEls || []
var firstLabel = labelEls[0]
var nextLabel = labelEls[1]
var lastLabel = labelEls[labelEls.length - 1]
var prevLabel = labelEls[labelEls.length - 2]
var firstTick = tickEls[0]
var nextTick = tickEls[1]
var lastTick = tickEls[tickEls.length - 1]
var prevTick = tickEls[tickEls.length - 2]
if (showMinLabel === false) {
ignoreEl(firstLabel)
ignoreEl(firstTick)
} else if (isTwoLabelOverlapped(firstLabel, nextLabel)) {
if (showMinLabel) {
ignoreEl(nextLabel)
ignoreEl(nextTick)
} else {
ignoreEl(firstLabel)
ignoreEl(firstTick)
}
}
if (showMaxLabel === false) {
ignoreEl(lastLabel)
ignoreEl(lastTick)
} else if (isTwoLabelOverlapped(prevLabel, lastLabel)) {
if (showMaxLabel) {
ignoreEl(prevLabel)
ignoreEl(prevTick)
} else {
ignoreEl(lastLabel)
ignoreEl(lastTick)
}
}
}
function ignoreEl(el) {
el && (el.ignore = true)
}
function isTwoLabelOverlapped(current, next) {
// current and next has the same rotation.
var firstRect = current && current.getBoundingRect().clone()
var nextRect = next && next.getBoundingRect().clone()
if (!firstRect || !nextRect) {
return
} // When checking intersect of two rotated labels, we use mRotationBack
// to avoid that boundingRect is enlarge when using `boundingRect.applyTransform`.
var mRotationBack = identity([])
rotate(mRotationBack, mRotationBack, -current.rotation)
firstRect.applyTransform(
mul([], mRotationBack, current.getLocalTransform())
)
nextRect.applyTransform(
mul([], mRotationBack, next.getLocalTransform())
)
return firstRect.intersect(nextRect)
}
function isNameLocationCenter(nameLocation) {
return nameLocation === 'middle' || nameLocation === 'center'
}
function createTicks(
ticksCoords,
tickTransform,
tickEndCoord,
tickLineStyle,
anidPrefix
) {
var tickEls = []
var pt1 = []
var pt2 = []
for (var i = 0; i < ticksCoords.length; i++) {
var tickCoord = ticksCoords[i].coord
pt1[0] = tickCoord
pt1[1] = 0
pt2[0] = tickCoord
pt2[1] = tickEndCoord
if (tickTransform) {
applyTransform$1(pt1, pt1, tickTransform)
applyTransform$1(pt2, pt2, tickTransform)
} // Tick line, Not use group transform to have better line draw
var tickEl = new Line$1({
subPixelOptimize: true,
shape: { x1: pt1[0], y1: pt1[1], x2: pt2[0], y2: pt2[1] },
style: tickLineStyle,
z2: 2,
autoBatch: true,
silent: true
})
tickEl.anid = anidPrefix + '_' + ticksCoords[i].tickValue
tickEls.push(tickEl)
}
return tickEls
}
function buildAxisMajorTicks(group, transformGroup, axisModel, opt) {
var axis = axisModel.axis
var tickModel = axisModel.getModel('axisTick')
var shown = tickModel.get('show')
if (shown === 'auto' && opt.handleAutoShown) {
shown = opt.handleAutoShown('axisTick')
}
if (!shown || axis.scale.isBlank()) {
return
}
var lineStyleModel = tickModel.getModel('lineStyle')
var tickEndCoord = opt.tickDirection * tickModel.get('length')
var ticksCoords = axis.getTicksCoords()
var ticksEls = createTicks(
ticksCoords,
transformGroup.transform,
tickEndCoord,
defaults(lineStyleModel.getLineStyle(), {
stroke: axisModel.get(['axisLine', 'lineStyle', 'color'])
}),
'ticks'
)
for (var i = 0; i < ticksEls.length; i++) {
group.add(ticksEls[i])
}
return ticksEls
}
function buildAxisMinorTicks(
group,
transformGroup,
axisModel,
tickDirection
) {
var axis = axisModel.axis
var minorTickModel = axisModel.getModel('minorTick')
if (!minorTickModel.get('show') || axis.scale.isBlank()) {
return
}
var minorTicksCoords = axis.getMinorTicksCoords()
if (!minorTicksCoords.length) {
return
}
var lineStyleModel = minorTickModel.getModel('lineStyle')
var tickEndCoord = tickDirection * minorTickModel.get('length')
var minorTickLineStyle = defaults(
lineStyleModel.getLineStyle(),
defaults(axisModel.getModel('axisTick').getLineStyle(), {
stroke: axisModel.get(['axisLine', 'lineStyle', 'color'])
})
)
for (var i = 0; i < minorTicksCoords.length; i++) {
var minorTicksEls = createTicks(
minorTicksCoords[i],
transformGroup.transform,
tickEndCoord,
minorTickLineStyle,
'minorticks_' + i
)
for (var k = 0; k < minorTicksEls.length; k++) {
group.add(minorTicksEls[k])
}
}
}
function buildAxisLabel(group, transformGroup, axisModel, opt) {
var axis = axisModel.axis
var show = retrieve(
opt.axisLabelShow,
axisModel.get(['axisLabel', 'show'])
)
if (!show || axis.scale.isBlank()) {
return
}
var labelModel = axisModel.getModel('axisLabel')
var labelMargin = labelModel.get('margin')
var labels = axis.getViewLabels() // Special label rotate.
var labelRotation =
((retrieve(opt.labelRotate, labelModel.get('rotate')) || 0) *
PI) /
180
var labelLayout = AxisBuilder.innerTextLayout(
opt.rotation,
labelRotation,
opt.labelDirection
)
var rawCategoryData =
axisModel.getCategories && axisModel.getCategories(true)
var labelEls = []
var silent = AxisBuilder.isLabelSilent(axisModel)
var triggerEvent = axisModel.get('triggerEvent')
each$7(labels, function (labelItem, index) {
var tickValue =
axis.scale.type === 'ordinal'
? axis.scale.getRawOrdinalNumber(labelItem.tickValue)
: labelItem.tickValue
var formattedLabel = labelItem.formattedLabel
var rawLabel = labelItem.rawLabel
var itemLabelModel = labelModel
if (rawCategoryData && rawCategoryData[tickValue]) {
var rawCategoryItem = rawCategoryData[tickValue]
if (isObject$2(rawCategoryItem) && rawCategoryItem.textStyle) {
itemLabelModel = new Model$1(
rawCategoryItem.textStyle,
labelModel,
axisModel.ecModel
)
}
}
var textColor =
itemLabelModel.getTextColor() ||
axisModel.get(['axisLine', 'lineStyle', 'color'])
var tickCoord = axis.dataToCoord(tickValue)
var textEl = new ZRText$1({
x: tickCoord,
y: opt.labelOffset + opt.labelDirection * labelMargin,
rotation: labelLayout.rotation,
silent: silent,
z2: 10 + (labelItem.level || 0),
style: createTextStyle(itemLabelModel, {
text: formattedLabel,
align:
itemLabelModel.getShallow('align', true) ||
labelLayout.textAlign,
verticalAlign:
itemLabelModel.getShallow('verticalAlign', true) ||
itemLabelModel.getShallow('baseline', true) ||
labelLayout.textVerticalAlign,
fill: isFunction(textColor)
? textColor(
// (1) In category axis with data zoom, tick is not the original
// index of axis.data. So tick should not be exposed to user
// in category axis.
// (2) Compatible with previous version, which always use formatted label as
// input. But in interval scale the formatted label is like '223,445', which
// maked user repalce ','. So we modify it to return original val but remain
// it as 'string' to avoid error in replacing.
axis.type === 'category'
? rawLabel
: axis.type === 'value'
? tickValue + ''
: tickValue,
index
)
: textColor
})
})
textEl.anid = 'label_' + tickValue // Pack data for mouse event
if (triggerEvent) {
var eventData = AxisBuilder.makeAxisEventDataBase(axisModel)
eventData.targetType = 'axisLabel'
eventData.value = rawLabel
eventData.tickIndex = index
if (axis.type === 'category') {
eventData.dataIndex = tickValue
}
getECData(textEl).eventData = eventData
} // FIXME
transformGroup.add(textEl)
textEl.updateTransform()
labelEls.push(textEl)
group.add(textEl)
textEl.decomposeTransform()
})
return labelEls
}
var AxisBuilder$1 = AxisBuilder // allAxesInfo should be updated when setOption performed.
function collect(ecModel, api) {
var result = {
/**
* key: makeKey(axis.model)
* value: {
* axis,
* coordSys,
* axisPointerModel,
* triggerTooltip,
* involveSeries,
* snap,
* seriesModels,
* seriesDataCount
* }
*/ axesInfo: {},
seriesInvolved: false,
/**
* key: makeKey(coordSys.model)
* value: Object: key makeKey(axis.model), value: axisInfo
*/ coordSysAxesInfo: {},
coordSysMap: {}
}
collectAxesInfo(result, ecModel, api) // Check seriesInvolved for performance, in case too many series in some chart.
result.seriesInvolved && collectSeriesInfo(result, ecModel)
return result
}
function collectAxesInfo(result, ecModel, api) {
var globalTooltipModel = ecModel.getComponent('tooltip')
var globalAxisPointerModel = ecModel.getComponent('axisPointer') // links can only be set on global.
var linksOption = globalAxisPointerModel.get('link', true) || []
var linkGroups = [] // Collect axes info.
each$7(api.getCoordinateSystems(), function (coordSys) {
// Some coordinate system do not support axes, like geo.
if (!coordSys.axisPointerEnabled) {
return
}
var coordSysKey = makeKey(coordSys.model)
var axesInfoInCoordSys = (result.coordSysAxesInfo[coordSysKey] =
{})
result.coordSysMap[coordSysKey] = coordSys // Set tooltip (like 'cross') is a convienent way to show axisPointer
// for user. So we enable seting tooltip on coordSys model.
var coordSysModel = coordSys.model
var baseTooltipModel = coordSysModel.getModel(
'tooltip',
globalTooltipModel
)
each$7(
coordSys.getAxes(),
curry$1(saveTooltipAxisInfo, false, null)
) // If axis tooltip used, choose tooltip axis for each coordSys.
// Notice this case: coordSys is `grid` but not `cartesian2D` here.
if (
coordSys.getTooltipAxes &&
globalTooltipModel && // If tooltip.showContent is set as false, tooltip will not
// show but axisPointer will show as normal.
baseTooltipModel.get('show')
) {
// Compatible with previous logic. But series.tooltip.trigger: 'axis'
// or series.data[n].tooltip.trigger: 'axis' are not support any more.
var triggerAxis = baseTooltipModel.get('trigger') === 'axis'
var cross =
baseTooltipModel.get(['axisPointer', 'type']) === 'cross'
var tooltipAxes = coordSys.getTooltipAxes(
baseTooltipModel.get(['axisPointer', 'axis'])
)
if (triggerAxis || cross) {
each$7(
tooltipAxes.baseAxes,
curry$1(
saveTooltipAxisInfo,
cross ? 'cross' : true,
triggerAxis
)
)
}
if (cross) {
each$7(
tooltipAxes.otherAxes,
curry$1(saveTooltipAxisInfo, 'cross', false)
)
}
} // fromTooltip: true | false | 'cross'
// triggerTooltip: true | false | null
function saveTooltipAxisInfo(fromTooltip, triggerTooltip, axis) {
var axisPointerModel = axis.model.getModel(
'axisPointer',
globalAxisPointerModel
)
var axisPointerShow = axisPointerModel.get('show')
if (
!axisPointerShow ||
(axisPointerShow === 'auto' &&
!fromTooltip &&
!isHandleTrigger(axisPointerModel))
) {
return
}
if (triggerTooltip == null) {
triggerTooltip = axisPointerModel.get('triggerTooltip')
}
axisPointerModel = fromTooltip
? makeAxisPointerModel(
axis,
baseTooltipModel,
globalAxisPointerModel,
ecModel,
fromTooltip,
triggerTooltip
)
: axisPointerModel
var snap = axisPointerModel.get('snap')
var axisKey = makeKey(axis.model)
var involveSeries =
triggerTooltip || snap || axis.type === 'category' // If result.axesInfo[key] exist, override it (tooltip has higher priority).
var axisInfo = (result.axesInfo[axisKey] = {
key: axisKey,
axis: axis,
coordSys: coordSys,
axisPointerModel: axisPointerModel,
triggerTooltip: triggerTooltip,
involveSeries: involveSeries,
snap: snap,
useHandle: isHandleTrigger(axisPointerModel),
seriesModels: [],
linkGroup: null
})
axesInfoInCoordSys[axisKey] = axisInfo
result.seriesInvolved = result.seriesInvolved || involveSeries
var groupIndex = getLinkGroupIndex(linksOption, axis)
if (groupIndex != null) {
var linkGroup =
linkGroups[groupIndex] ||
(linkGroups[groupIndex] = { axesInfo: {} })
linkGroup.axesInfo[axisKey] = axisInfo
linkGroup.mapper = linksOption[groupIndex].mapper
axisInfo.linkGroup = linkGroup
}
}
})
}
function makeAxisPointerModel(
axis,
baseTooltipModel,
globalAxisPointerModel,
ecModel,
fromTooltip,
triggerTooltip
) {
var tooltipAxisPointerModel =
baseTooltipModel.getModel('axisPointer')
var fields = [
'type',
'snap',
'lineStyle',
'shadowStyle',
'label',
'animation',
'animationDurationUpdate',
'animationEasingUpdate',
'z'
]
var volatileOption = {}
each$7(fields, function (field) {
volatileOption[field] = clone$3(
tooltipAxisPointerModel.get(field)
)
}) // category axis do not auto snap, otherwise some tick that do not
// has value can not be hovered. value/time/log axis default snap if
// triggered from tooltip and trigger tooltip.
volatileOption.snap = axis.type !== 'category' && !!triggerTooltip // Compatibel with previous behavior, tooltip axis do not show label by default.
// Only these properties can be overrided from tooltip to axisPointer.
if (tooltipAxisPointerModel.get('type') === 'cross') {
volatileOption.type = 'line'
}
var labelOption =
volatileOption.label || (volatileOption.label = {}) // Follow the convention, do not show label when triggered by tooltip by default.
labelOption.show == null && (labelOption.show = false)
if (fromTooltip === 'cross') {
// When 'cross', both axes show labels.
var tooltipAxisPointerLabelShow = tooltipAxisPointerModel.get([
'label',
'show'
])
labelOption.show =
tooltipAxisPointerLabelShow != null
? tooltipAxisPointerLabelShow
: true // If triggerTooltip, this is a base axis, which should better not use cross style
// (cross style is dashed by default)
if (!triggerTooltip) {
var crossStyle = (volatileOption.lineStyle =
tooltipAxisPointerModel.get('crossStyle'))
crossStyle && defaults(labelOption, crossStyle.textStyle)
}
}
return axis.model.getModel(
'axisPointer',
new Model$1(volatileOption, globalAxisPointerModel, ecModel)
)
}
function collectSeriesInfo(result, ecModel) {
// Prepare data for axis trigger
ecModel.eachSeries(function (seriesModel) {
// Notice this case: this coordSys is `cartesian2D` but not `grid`.
var coordSys = seriesModel.coordinateSystem
var seriesTooltipTrigger = seriesModel.get(
['tooltip', 'trigger'],
true
)
var seriesTooltipShow = seriesModel.get(['tooltip', 'show'], true)
if (
!coordSys ||
seriesTooltipTrigger === 'none' ||
seriesTooltipTrigger === false ||
seriesTooltipTrigger === 'item' ||
seriesTooltipShow === false ||
seriesModel.get(['axisPointer', 'show'], true) === false
) {
return
}
each$7(
result.coordSysAxesInfo[makeKey(coordSys.model)],
function (axisInfo) {
var axis = axisInfo.axis
if (coordSys.getAxis(axis.dim) === axis) {
axisInfo.seriesModels.push(seriesModel)
axisInfo.seriesDataCount == null &&
(axisInfo.seriesDataCount = 0)
axisInfo.seriesDataCount += seriesModel.getData().count()
}
}
)
})
}
/**
* For example:
* {
* axisPointer: {
* links: [{
* xAxisIndex: [2, 4],
* yAxisIndex: 'all'
* }, {
* xAxisId: ['a5', 'a7'],
* xAxisName: 'xxx'
* }]
* }
* }
*/ function getLinkGroupIndex(linksOption, axis) {
var axisModel = axis.model
var dim = axis.dim
for (var i = 0; i < linksOption.length; i++) {
var linkOption = linksOption[i] || {}
if (
checkPropInLink(linkOption[dim + 'AxisId'], axisModel.id) ||
checkPropInLink(
linkOption[dim + 'AxisIndex'],
axisModel.componentIndex
) ||
checkPropInLink(linkOption[dim + 'AxisName'], axisModel.name)
) {
return i
}
}
}
function checkPropInLink(linkPropValue, axisPropValue) {
return (
linkPropValue === 'all' ||
(isArray(linkPropValue) &&
indexOf(linkPropValue, axisPropValue) >= 0) ||
linkPropValue === axisPropValue
)
}
function fixValue(axisModel) {
var axisInfo = getAxisInfo(axisModel)
if (!axisInfo) {
return
}
var axisPointerModel = axisInfo.axisPointerModel
var scale = axisInfo.axis.scale
var option = axisPointerModel.option
var status = axisPointerModel.get('status')
var value = axisPointerModel.get('value') // Parse init value for category and time axis.
if (value != null) {
value = scale.parse(value)
}
var useHandle = isHandleTrigger(axisPointerModel) // If `handle` used, `axisPointer` will always be displayed, so value
// and status should be initialized.
if (status == null) {
option.status = useHandle ? 'show' : 'hide'
}
var extent = scale.getExtent().slice()
extent[0] > extent[1] && extent.reverse()
if (
// Pick a value on axis when initializing.
value == null || // If both `handle` and `dataZoom` are used, value may be out of axis extent,
// where we should re-pick a value to keep `handle` displaying normally.
value > extent[1]
) {
// Make handle displayed on the end of the axis when init, which looks better.
value = extent[1]
}
if (value < extent[0]) {
value = extent[0]
}
option.value = value
if (useHandle) {
option.status = axisInfo.axis.scale.isBlank() ? 'hide' : 'show'
}
}
function getAxisInfo(axisModel) {
var coordSysAxesInfo = (
axisModel.ecModel.getComponent('axisPointer') || {}
).coordSysAxesInfo
return (
coordSysAxesInfo && coordSysAxesInfo.axesInfo[makeKey(axisModel)]
)
}
function getAxisPointerModel(axisModel) {
var axisInfo = getAxisInfo(axisModel)
return axisInfo && axisInfo.axisPointerModel
}
function isHandleTrigger(axisPointerModel) {
return !!axisPointerModel.get(['handle', 'show'])
}
/**
* @param {module:echarts/model/Model} model
* @return {string} unique key
*/ function makeKey(model) {
return model.type + '||' + model.id
}
var axisPointerClazz = {}
/**
* Base class of AxisView.
*/ var AxisView = /** @class */ (function (_super) {
__extends(AxisView, _super)
function AxisView() {
var _this =
(_super !== null && _super.apply(this, arguments)) || this
_this.type = AxisView.type
return _this
}
/**
* @override
*/ AxisView.prototype.render = function (
axisModel,
ecModel,
api,
payload
) {
// FIXME
// This process should proformed after coordinate systems updated
// (axis scale updated), and should be performed each time update.
// So put it here temporarily, although it is not appropriate to
// put a model-writing procedure in `view`.
this.axisPointerClass && fixValue(axisModel)
_super.prototype.render.apply(this, arguments)
this._doUpdateAxisPointerClass(axisModel, api, true)
}
/**
* Action handler.
*/ AxisView.prototype.updateAxisPointer = function (
axisModel,
ecModel,
api,
payload
) {
this._doUpdateAxisPointerClass(axisModel, api, false)
}
/**
* @override
*/ AxisView.prototype.remove = function (ecModel, api) {
var axisPointer = this._axisPointer
axisPointer && axisPointer.remove(api)
}
/**
* @override
*/ AxisView.prototype.dispose = function (ecModel, api) {
this._disposeAxisPointer(api)
_super.prototype.dispose.apply(this, arguments)
}
AxisView.prototype._doUpdateAxisPointerClass = function (
axisModel,
api,
forceRender
) {
var Clazz = AxisView.getAxisPointerClass(this.axisPointerClass)
if (!Clazz) {
return
}
var axisPointerModel = getAxisPointerModel(axisModel)
axisPointerModel
? (
this._axisPointer || (this._axisPointer = new Clazz())
).render(axisModel, axisPointerModel, api, forceRender)
: this._disposeAxisPointer(api)
}
AxisView.prototype._disposeAxisPointer = function (api) {
this._axisPointer && this._axisPointer.dispose(api)
this._axisPointer = null
}
AxisView.registerAxisPointerClass = function (type, clazz) {
axisPointerClazz[type] = clazz
}
AxisView.getAxisPointerClass = function (type) {
return type && axisPointerClazz[type]
}
AxisView.type = 'axis'
return AxisView
})(ComponentView$1)
var AxisView$1 = AxisView
var inner$5 = makeInner()
function rectCoordAxisBuildSplitArea(
axisView,
axisGroup,
axisModel,
gridModel
) {
var axis = axisModel.axis
if (axis.scale.isBlank()) {
return
} // TODO: TYPE
var splitAreaModel = axisModel.getModel('splitArea')
var areaStyleModel = splitAreaModel.getModel('areaStyle')
var areaColors = areaStyleModel.get('color')
var gridRect = gridModel.coordinateSystem.getRect()
var ticksCoords = axis.getTicksCoords({
tickModel: splitAreaModel,
clamp: true
})
if (!ticksCoords.length) {
return
} // For Making appropriate splitArea animation, the color and anid
// should be corresponding to previous one if possible.
var areaColorsLen = areaColors.length
var lastSplitAreaColors = inner$5(axisView).splitAreaColors
var newSplitAreaColors = createHashMap()
var colorIndex = 0
if (lastSplitAreaColors) {
for (var i = 0; i < ticksCoords.length; i++) {
var cIndex = lastSplitAreaColors.get(ticksCoords[i].tickValue)
if (cIndex != null) {
colorIndex =
(cIndex + (areaColorsLen - 1) * i) % areaColorsLen
break
}
}
}
var prev = axis.toGlobalCoord(ticksCoords[0].coord)
var areaStyle = areaStyleModel.getAreaStyle()
areaColors = isArray(areaColors) ? areaColors : [areaColors]
for (var i = 1; i < ticksCoords.length; i++) {
var tickCoord = axis.toGlobalCoord(ticksCoords[i].coord)
var x = void 0
var y = void 0
var width = void 0
var height = void 0
if (axis.isHorizontal()) {
x = prev
y = gridRect.y
width = tickCoord - x
height = gridRect.height
prev = x + width
} else {
x = gridRect.x
y = prev
width = gridRect.width
height = tickCoord - y
prev = y + height
}
var tickValue = ticksCoords[i - 1].tickValue
tickValue != null && newSplitAreaColors.set(tickValue, colorIndex)
axisGroup.add(
new Rect$2({
anid: tickValue != null ? 'area_' + tickValue : null,
shape: { x: x, y: y, width: width, height: height },
style: defaults({ fill: areaColors[colorIndex] }, areaStyle),
autoBatch: true,
silent: true
})
)
colorIndex = (colorIndex + 1) % areaColorsLen
}
inner$5(axisView).splitAreaColors = newSplitAreaColors
}
function rectCoordAxisHandleRemove(axisView) {
inner$5(axisView).splitAreaColors = null
}
var axisBuilderAttrs = ['axisLine', 'axisTickLabel', 'axisName']
var selfBuilderAttrs = ['splitArea', 'splitLine', 'minorSplitLine']
var CartesianAxisView = /** @class */ (function (_super) {
__extends(CartesianAxisView, _super)
function CartesianAxisView() {
var _this =
(_super !== null && _super.apply(this, arguments)) || this
_this.type = CartesianAxisView.type
_this.axisPointerClass = 'CartesianAxisPointer'
return _this
}
/**
* @override
*/ CartesianAxisView.prototype.render = function (
axisModel,
ecModel,
api,
payload
) {
this.group.removeAll()
var oldAxisGroup = this._axisGroup
this._axisGroup = new Group$3()
this.group.add(this._axisGroup)
if (!axisModel.get('show')) {
return
}
var gridModel = axisModel.getCoordSysModel()
var layout = layout$1(gridModel, axisModel)
var axisBuilder = new AxisBuilder$1(
axisModel,
extend(
{
handleAutoShown: function handleAutoShown(elementType) {
var cartesians =
gridModel.coordinateSystem.getCartesians()
for (var i = 0; i < cartesians.length; i++) {
if (
isIntervalOrLogScale(
cartesians[i].getOtherAxis(axisModel.axis).scale
)
) {
// Still show axis tick or axisLine if other axis is value / log
return true
}
} // Not show axisTick or axisLine if other axis is category / time
return false
}
},
layout
)
)
each$7(axisBuilderAttrs, axisBuilder.add, axisBuilder)
this._axisGroup.add(axisBuilder.getGroup())
each$7(
selfBuilderAttrs,
function (name) {
if (axisModel.get([name, 'show'])) {
axisElementBuilders[name](
this,
this._axisGroup,
axisModel,
gridModel
)
}
},
this
) // THIS is a special case for bar racing chart.
// Update the axis label from the natural initial layout to
// sorted layout should has no animation.
var isInitialSortFromBarRacing =
payload &&
payload.type === 'changeAxisOrder' &&
payload.isInitSort
if (!isInitialSortFromBarRacing) {
groupTransition(oldAxisGroup, this._axisGroup, axisModel)
}
_super.prototype.render.call(
this,
axisModel,
ecModel,
api,
payload
)
}
CartesianAxisView.prototype.remove = function () {
rectCoordAxisHandleRemove(this)
}
CartesianAxisView.type = 'cartesianAxis'
return CartesianAxisView
})(AxisView$1)
var axisElementBuilders = {
splitLine: function splitLine(
axisView,
axisGroup,
axisModel,
gridModel
) {
var axis = axisModel.axis
if (axis.scale.isBlank()) {
return
}
var splitLineModel = axisModel.getModel('splitLine')
var lineStyleModel = splitLineModel.getModel('lineStyle')
var lineColors = lineStyleModel.get('color')
lineColors = isArray(lineColors) ? lineColors : [lineColors]
var gridRect = gridModel.coordinateSystem.getRect()
var isHorizontal = axis.isHorizontal()
var lineCount = 0
var ticksCoords = axis.getTicksCoords({
tickModel: splitLineModel
})
var p1 = []
var p2 = []
var lineStyle = lineStyleModel.getLineStyle()
for (var i = 0; i < ticksCoords.length; i++) {
var tickCoord = axis.toGlobalCoord(ticksCoords[i].coord)
if (isHorizontal) {
p1[0] = tickCoord
p1[1] = gridRect.y
p2[0] = tickCoord
p2[1] = gridRect.y + gridRect.height
} else {
p1[0] = gridRect.x
p1[1] = tickCoord
p2[0] = gridRect.x + gridRect.width
p2[1] = tickCoord
}
var colorIndex = lineCount++ % lineColors.length
var tickValue = ticksCoords[i].tickValue
axisGroup.add(
new Line$1({
anid:
tickValue != null
? 'line_' + ticksCoords[i].tickValue
: null,
subPixelOptimize: true,
autoBatch: true,
shape: { x1: p1[0], y1: p1[1], x2: p2[0], y2: p2[1] },
style: defaults(
{ stroke: lineColors[colorIndex] },
lineStyle
),
silent: true
})
)
}
},
minorSplitLine: function minorSplitLine(
axisView,
axisGroup,
axisModel,
gridModel
) {
var axis = axisModel.axis
var minorSplitLineModel = axisModel.getModel('minorSplitLine')
var lineStyleModel = minorSplitLineModel.getModel('lineStyle')
var gridRect = gridModel.coordinateSystem.getRect()
var isHorizontal = axis.isHorizontal()
var minorTicksCoords = axis.getMinorTicksCoords()
if (!minorTicksCoords.length) {
return
}
var p1 = []
var p2 = []
var lineStyle = lineStyleModel.getLineStyle()
for (var i = 0; i < minorTicksCoords.length; i++) {
for (var k = 0; k < minorTicksCoords[i].length; k++) {
var tickCoord = axis.toGlobalCoord(
minorTicksCoords[i][k].coord
)
if (isHorizontal) {
p1[0] = tickCoord
p1[1] = gridRect.y
p2[0] = tickCoord
p2[1] = gridRect.y + gridRect.height
} else {
p1[0] = gridRect.x
p1[1] = tickCoord
p2[0] = gridRect.x + gridRect.width
p2[1] = tickCoord
}
axisGroup.add(
new Line$1({
anid: 'minor_line_' + minorTicksCoords[i][k].tickValue,
subPixelOptimize: true,
autoBatch: true,
shape: { x1: p1[0], y1: p1[1], x2: p2[0], y2: p2[1] },
style: lineStyle,
silent: true
})
)
}
}
},
splitArea: function splitArea(
axisView,
axisGroup,
axisModel,
gridModel
) {
rectCoordAxisBuildSplitArea(
axisView,
axisGroup,
axisModel,
gridModel
)
}
}
var CartesianXAxisView = /** @class */ (function (_super) {
__extends(CartesianXAxisView, _super)
function CartesianXAxisView() {
var _this =
(_super !== null && _super.apply(this, arguments)) || this
_this.type = CartesianXAxisView.type
return _this
}
CartesianXAxisView.type = 'xAxis'
return CartesianXAxisView
})(CartesianAxisView)
var CartesianYAxisView = /** @class */ (function (_super) {
__extends(CartesianYAxisView, _super)
function CartesianYAxisView() {
var _this =
(_super !== null && _super.apply(this, arguments)) || this
_this.type = CartesianXAxisView.type
return _this
}
CartesianYAxisView.type = 'yAxis'
return CartesianYAxisView
})(CartesianAxisView)
var GridView = /** @class */ (function (_super) {
__extends(GridView, _super)
function GridView() {
var _this =
(_super !== null && _super.apply(this, arguments)) || this
_this.type = 'grid'
return _this
}
GridView.prototype.render = function (gridModel, ecModel) {
this.group.removeAll()
if (gridModel.get('show')) {
this.group.add(
new Rect$2({
shape: gridModel.coordinateSystem.getRect(),
style: defaults(
{ fill: gridModel.get('backgroundColor') },
gridModel.getItemStyle()
),
silent: true,
z2: -1
})
)
}
}
GridView.type = 'grid'
return GridView
})(ComponentView$1)
var extraOption = {
// gridIndex: 0,
// gridId: '',
offset: 0
}
function install$f(registers) {
registers.registerComponentView(GridView)
registers.registerComponentModel(GridModel$1)
registers.registerCoordinateSystem('cartesian2d', Grid$1)
axisModelCreator(registers, 'x', CartesianAxisModel, extraOption)
axisModelCreator(registers, 'y', CartesianAxisModel, extraOption)
registers.registerComponentView(CartesianXAxisView)
registers.registerComponentView(CartesianYAxisView)
registers.registerPreprocessor(function (option) {
// Only create grid when need
if (option.xAxis && option.yAxis && !option.grid) {
option.grid = {}
}
})
}
var ATTR = '\0_ec_interaction_mutex'
function take(zr, resourceKey, userKey) {
var store = getStore(zr)
store[resourceKey] = userKey
}
function release(zr, resourceKey, userKey) {
var store = getStore(zr)
var uKey = store[resourceKey]
if (uKey === userKey) {
store[resourceKey] = null
}
}
function isTaken(zr, resourceKey) {
return !!getStore(zr)[resourceKey]
}
function getStore(zr) {
return zr[ATTR] || (zr[ATTR] = {})
}
/**
* payload: {
* type: 'takeGlobalCursor',
* key: 'dataZoomSelect', or 'brush', or ...,
* If no userKey, release global cursor.
* }
*/ // TODO: SELF REGISTERED.
registerAction(
{
type: 'takeGlobalCursor',
event: 'globalCursorTaken',
update: 'update'
},
noop
)
var RoamController = /** @class */ (function (_super) {
__extends(RoamController, _super)
function RoamController(zr) {
var _this = _super.call(this) || this
_this._zr = zr // Avoid two roamController bind the same handler
var mousedownHandler = bind$1(_this._mousedownHandler, _this)
var mousemoveHandler = bind$1(_this._mousemoveHandler, _this)
var mouseupHandler = bind$1(_this._mouseupHandler, _this)
var mousewheelHandler = bind$1(_this._mousewheelHandler, _this)
var pinchHandler = bind$1(_this._pinchHandler, _this)
/**
* Notice: only enable needed types. For example, if 'zoom'
* is not needed, 'zoom' should not be enabled, otherwise
* default mousewheel behaviour (scroll page) will be disabled.
*/ _this.enable = function (controlType, opt) {
// Disable previous first
this.disable()
this._opt = defaults(clone$3(opt) || {}, {
zoomOnMouseWheel: true,
moveOnMouseMove: true, // By default, wheel do not trigger move.
moveOnMouseWheel: false,
preventDefaultMouseMove: true
})
if (controlType == null) {
controlType = true
}
if (
controlType === true ||
controlType === 'move' ||
controlType === 'pan'
) {
zr.on('mousedown', mousedownHandler)
zr.on('mousemove', mousemoveHandler)
zr.on('mouseup', mouseupHandler)
}
if (
controlType === true ||
controlType === 'scale' ||
controlType === 'zoom'
) {
zr.on('mousewheel', mousewheelHandler)
zr.on('pinch', pinchHandler)
}
}
_this.disable = function () {
zr.off('mousedown', mousedownHandler)
zr.off('mousemove', mousemoveHandler)
zr.off('mouseup', mouseupHandler)
zr.off('mousewheel', mousewheelHandler)
zr.off('pinch', pinchHandler)
}
return _this
}
RoamController.prototype.isDragging = function () {
return this._dragging
}
RoamController.prototype.isPinching = function () {
return this._pinching
}
RoamController.prototype.setPointerChecker = function (
pointerChecker
) {
this.pointerChecker = pointerChecker
}
RoamController.prototype.dispose = function () {
this.disable()
}
RoamController.prototype._mousedownHandler = function (e) {
if (
isMiddleOrRightButtonOnMouseUpDown(e) ||
(e.target && e.target.draggable)
) {
return
}
var x = e.offsetX
var y = e.offsetY // Only check on mosedown, but not mousemove.
// Mouse can be out of target when mouse moving.
if (this.pointerChecker && this.pointerChecker(e, x, y)) {
this._x = x
this._y = y
this._dragging = true
}
}
RoamController.prototype._mousemoveHandler = function (e) {
if (
!this._dragging ||
!isAvailableBehavior('moveOnMouseMove', e, this._opt) ||
e.gestureEvent === 'pinch' ||
isTaken(this._zr, 'globalPan')
) {
return
}
var x = e.offsetX
var y = e.offsetY
var oldX = this._x
var oldY = this._y
var dx = x - oldX
var dy = y - oldY
this._x = x
this._y = y
this._opt.preventDefaultMouseMove && stop(e.event)
trigger$1(this, 'pan', 'moveOnMouseMove', e, {
dx: dx,
dy: dy,
oldX: oldX,
oldY: oldY,
newX: x,
newY: y,
isAvailableBehavior: null
})
}
RoamController.prototype._mouseupHandler = function (e) {
if (!isMiddleOrRightButtonOnMouseUpDown(e)) {
this._dragging = false
}
}
RoamController.prototype._mousewheelHandler = function (e) {
var shouldZoom = isAvailableBehavior(
'zoomOnMouseWheel',
e,
this._opt
)
var shouldMove = isAvailableBehavior(
'moveOnMouseWheel',
e,
this._opt
)
var wheelDelta = e.wheelDelta
var absWheelDeltaDelta = Math.abs(wheelDelta)
var originX = e.offsetX
var originY = e.offsetY // wheelDelta maybe -0 in chrome mac.
if (wheelDelta === 0 || (!shouldZoom && !shouldMove)) {
return
} // If both `shouldZoom` and `shouldMove` is true, trigger
// their event both, and the final behavior is determined
// by event listener themselves.
if (shouldZoom) {
// Convenience:
// Mac and VM Windows on Mac: scroll up: zoom out.
// Windows: scroll up: zoom in.
// FIXME: Should do more test in different environment.
// wheelDelta is too complicated in difference nvironment
// (https://developer.mozilla.org/en-US/docs/Web/Events/mousewheel),
// although it has been normallized by zrender.
// wheelDelta of mouse wheel is bigger than touch pad.
var factor =
absWheelDeltaDelta > 3
? 1.4
: absWheelDeltaDelta > 1
? 1.2
: 1.1
var scale = wheelDelta > 0 ? factor : 1 / factor
checkPointerAndTrigger(this, 'zoom', 'zoomOnMouseWheel', e, {
scale: scale,
originX: originX,
originY: originY,
isAvailableBehavior: null
})
}
if (shouldMove) {
// FIXME: Should do more test in different environment.
var absDelta = Math.abs(wheelDelta) // wheelDelta of mouse wheel is bigger than touch pad.
var scrollDelta =
(wheelDelta > 0 ? 1 : -1) *
(absDelta > 3 ? 0.4 : absDelta > 1 ? 0.15 : 0.05)
checkPointerAndTrigger(
this,
'scrollMove',
'moveOnMouseWheel',
e,
{
scrollDelta: scrollDelta,
originX: originX,
originY: originY,
isAvailableBehavior: null
}
)
}
}
RoamController.prototype._pinchHandler = function (e) {
if (isTaken(this._zr, 'globalPan')) {
return
}
var scale = e.pinchScale > 1 ? 1.1 : 1 / 1.1
checkPointerAndTrigger(this, 'zoom', null, e, {
scale: scale,
originX: e.pinchX,
originY: e.pinchY,
isAvailableBehavior: null
})
}
return RoamController
})(Eventful$1)
function checkPointerAndTrigger(
controller,
eventName,
behaviorToCheck,
e,
contollerEvent
) {
if (
controller.pointerChecker &&
controller.pointerChecker(
e,
contollerEvent.originX,
contollerEvent.originY
)
) {
// When mouse is out of roamController rect,
// default befavoius should not be be disabled, otherwise
// page sliding is disabled, contrary to expectation.
stop(e.event)
trigger$1(
controller,
eventName,
behaviorToCheck,
e,
contollerEvent
)
}
}
function trigger$1(
controller,
eventName,
behaviorToCheck,
e,
contollerEvent
) {
// Also provide behavior checker for event listener, for some case that
// multiple components share one listener.
contollerEvent.isAvailableBehavior = bind$1(
isAvailableBehavior,
null,
behaviorToCheck,
e
) // TODO should not have type issue.
controller.trigger(eventName, contollerEvent)
} // settings: {
// zoomOnMouseWheel
// moveOnMouseMove
// moveOnMouseWheel
// }
// The value can be: true / false / 'shift' / 'ctrl' / 'alt'.
function isAvailableBehavior(behaviorToCheck, e, settings) {
var setting = settings[behaviorToCheck]
return (
!behaviorToCheck ||
(setting && (!isString(setting) || e.event[setting + 'Key']))
)
}
var RoamController$1 = RoamController
/*
* 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 IRRELEVANT_EXCLUDES = { axisPointer: 1, tooltip: 1, brush: 1 }
/**
* Avoid that: mouse click on a elements that is over geo or graph,
* but roam is triggered.
*/ function onIrrelevantElement(e, api, targetCoordSysModel) {
var model = api.getComponentByElement(e.topTarget) // If model is axisModel, it works only if it is injected with coordinateSystem.
var coordSys = model && model.coordinateSystem
return (
model &&
model !== targetCoordSysModel &&
!IRRELEVANT_EXCLUDES.hasOwnProperty(model.mainType) &&
coordSys &&
coordSys.model !== targetCoordSysModel
)
}
/*
* 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.
*/ /**
* Calculate slider move result.
* Usage:
* (1) If both handle0 and handle1 are needed to be moved, set minSpan the same as
* maxSpan and the same as `Math.abs(handleEnd[1] - handleEnds[0])`.
* (2) If handle0 is forbidden to cross handle1, set minSpan as `0`.
*
* @param delta Move length.
* @param handleEnds handleEnds[0] can be bigger then handleEnds[1].
* handleEnds will be modified in this method.
* @param extent handleEnds is restricted by extent.
* extent[0] should less or equals than extent[1].
* @param handleIndex Can be 'all', means that both move the two handleEnds.
* @param minSpan The range of dataZoom can not be smaller than that.
* If not set, handle0 and cross handle1. If set as a non-negative
* number (including `0`), handles will push each other when reaching
* the minSpan.
* @param maxSpan The range of dataZoom can not be larger than that.
* @return The input handleEnds.
*/ function sliderMove(
delta,
handleEnds,
extent,
handleIndex,
minSpan,
maxSpan
) {
delta = delta || 0
var extentSpan = extent[1] - extent[0] // Notice maxSpan and minSpan can be null/undefined.
if (minSpan != null) {
minSpan = restrict(minSpan, [0, extentSpan])
}
if (maxSpan != null) {
maxSpan = Math.max(maxSpan, minSpan != null ? minSpan : 0)
}
if (handleIndex === 'all') {
var handleSpan = Math.abs(handleEnds[1] - handleEnds[0])
handleSpan = restrict(handleSpan, [0, extentSpan])
minSpan = maxSpan = restrict(handleSpan, [minSpan, maxSpan])
handleIndex = 0
}
handleEnds[0] = restrict(handleEnds[0], extent)
handleEnds[1] = restrict(handleEnds[1], extent)
var originalDistSign = getSpanSign(handleEnds, handleIndex)
handleEnds[handleIndex] += delta // Restrict in extent.
var extentMinSpan = minSpan || 0
var realExtent = extent.slice()
originalDistSign.sign < 0
? (realExtent[0] += extentMinSpan)
: (realExtent[1] -= extentMinSpan)
handleEnds[handleIndex] = restrict(
handleEnds[handleIndex],
realExtent
) // Expand span.
var currDistSign
currDistSign = getSpanSign(handleEnds, handleIndex)
if (
minSpan != null &&
(currDistSign.sign !== originalDistSign.sign ||
currDistSign.span < minSpan)
) {
// If minSpan exists, 'cross' is forbidden.
handleEnds[1 - handleIndex] =
handleEnds[handleIndex] + originalDistSign.sign * minSpan
} // Shrink span.
currDistSign = getSpanSign(handleEnds, handleIndex)
if (maxSpan != null && currDistSign.span > maxSpan) {
handleEnds[1 - handleIndex] =
handleEnds[handleIndex] + currDistSign.sign * maxSpan
}
return handleEnds
}
function getSpanSign(handleEnds, handleIndex) {
var dist = handleEnds[handleIndex] - handleEnds[1 - handleIndex] // If `handleEnds[0] === handleEnds[1]`, always believe that handleEnd[0]
// is at left of handleEnds[1] for non-cross case.
return {
span: Math.abs(dist),
sign: dist > 0 ? -1 : dist < 0 ? 1 : handleIndex ? -1 : 1
}
}
function restrict(value, extend) {
return Math.min(
extend[1] != null ? extend[1] : Infinity,
Math.max(extend[0] != null ? extend[0] : -Infinity, value)
)
}
var BRUSH_PANEL_GLOBAL = true
var mathMin = Math.min
var mathMax = Math.max
var mathPow = Math.pow
var COVER_Z = 10000
var UNSELECT_THRESHOLD = 6
var MIN_RESIZE_LINE_WIDTH = 6
var MUTEX_RESOURCE_KEY = 'globalPan'
var DIRECTION_MAP = { w: [0, 0], e: [0, 1], n: [1, 0], s: [1, 1] }
var CURSOR_MAP = {
w: 'ew',
e: 'ew',
n: 'ns',
s: 'ns',
ne: 'nesw',
sw: 'nesw',
nw: 'nwse',
se: 'nwse'
}
var DEFAULT_BRUSH_OPT = {
brushStyle: {
lineWidth: 2,
stroke: 'rgba(210,219,238,0.3)',
fill: '#D2DBEE'
},
transformable: true,
brushMode: 'single',
removeOnClick: false
}
var baseUID = 0
/**
* params:
* areas: Array., coord relates to container group,
* If no container specified, to global.
* opt {
* isEnd: boolean,
* removeOnClick: boolean
* }
*/ var BrushController = /** @class */ (function (_super) {
__extends(BrushController, _super)
function BrushController(zr) {
var _this = _super.call(this) || this
/**
* @internal
*/ _this._track = []
/**
* @internal
*/ _this._covers = []
_this._handlers = {}
_this._zr = zr
_this.group = new Group$3()
_this._uid = 'brushController_' + baseUID++
each$7(
pointerHandlers,
function (handler, eventName) {
this._handlers[eventName] = bind$1(handler, this)
},
_this
)
return _this
}
/**
* If set to `false`, select disabled.
*/ BrushController.prototype.enableBrush = function (brushOption) {
this._brushType && this._doDisableBrush()
brushOption.brushType && this._doEnableBrush(brushOption)
return this
}
BrushController.prototype._doEnableBrush = function (brushOption) {
var zr = this._zr // Consider roam, which takes globalPan too.
if (!this._enableGlobalPan) {
take(zr, MUTEX_RESOURCE_KEY, this._uid)
}
each$7(this._handlers, function (handler, eventName) {
zr.on(eventName, handler)
})
this._brushType = brushOption.brushType
this._brushOption = merge(
clone$3(DEFAULT_BRUSH_OPT),
brushOption,
true
)
}
BrushController.prototype._doDisableBrush = function () {
var zr = this._zr
release(zr, MUTEX_RESOURCE_KEY, this._uid)
each$7(this._handlers, function (handler, eventName) {
zr.off(eventName, handler)
})
this._brushType = this._brushOption = null
}
/**
* @param panelOpts If not pass, it is global brush.
*/ BrushController.prototype.setPanels = function (panelOpts) {
if (panelOpts && panelOpts.length) {
var panels_1 = (this._panels = {})
each$7(panelOpts, function (panelOpts) {
panels_1[panelOpts.panelId] = clone$3(panelOpts)
})
} else {
this._panels = null
}
return this
}
BrushController.prototype.mount = function (opt) {
opt = opt || {}
this._enableGlobalPan = opt.enableGlobalPan
var thisGroup = this.group
this._zr.add(thisGroup)
thisGroup.attr({
x: opt.x || 0,
y: opt.y || 0,
rotation: opt.rotation || 0,
scaleX: opt.scaleX || 1,
scaleY: opt.scaleY || 1
})
this._transform = thisGroup.getLocalTransform()
return this
} // eachCover(cb, context): void {
// each(this._covers, cb, context);
// }
/**
* Update covers.
* @param coverConfigList
* If coverConfigList is null/undefined, all covers removed.
*/ BrushController.prototype.updateCovers = function (
coverConfigList
) {
coverConfigList = map$1(coverConfigList, function (coverConfig) {
return merge(clone$3(DEFAULT_BRUSH_OPT), coverConfig, true)
})
var tmpIdPrefix = '\0-brush-index-'
var oldCovers = this._covers
var newCovers = (this._covers = [])
var controller = this
var creatingCover = this._creatingCover
new DataDiffer$1(oldCovers, coverConfigList, oldGetKey, getKey)
.add(addOrUpdate)
.update(addOrUpdate)
.remove(remove)
.execute()
return this
function getKey(brushOption, index) {
return (
(brushOption.id != null
? brushOption.id
: tmpIdPrefix + index) +
'-' +
brushOption.brushType
)
}
function oldGetKey(cover, index) {
return getKey(cover.__brushOption, index)
}
function addOrUpdate(newIndex, oldIndex) {
var newBrushInternal = coverConfigList[newIndex] // Consider setOption in event listener of brushSelect,
// where updating cover when creating should be forbiden.
if (oldIndex != null && oldCovers[oldIndex] === creatingCover) {
newCovers[newIndex] = oldCovers[oldIndex]
} else {
var cover = (newCovers[newIndex] =
oldIndex != null
? ((oldCovers[oldIndex].__brushOption = newBrushInternal),
oldCovers[oldIndex])
: endCreating(
controller,
createCover(controller, newBrushInternal)
))
updateCoverAfterCreation(controller, cover)
}
}
function remove(oldIndex) {
if (oldCovers[oldIndex] !== creatingCover) {
controller.group.remove(oldCovers[oldIndex])
}
}
}
BrushController.prototype.unmount = function () {
this.enableBrush(false) // container may 'removeAll' outside.
clearCovers(this)
this._zr.remove(this.group)
return this
}
BrushController.prototype.dispose = function () {
this.unmount()
this.off()
}
return BrushController
})(Eventful$1)
function createCover(controller, brushOption) {
var cover = coverRenderers[brushOption.brushType].createCover(
controller,
brushOption
)
cover.__brushOption = brushOption
updateZ(cover, brushOption)
controller.group.add(cover)
return cover
}
function endCreating(controller, creatingCover) {
var coverRenderer = getCoverRenderer(creatingCover)
if (coverRenderer.endCreating) {
coverRenderer.endCreating(controller, creatingCover)
updateZ(creatingCover, creatingCover.__brushOption)
}
return creatingCover
}
function updateCoverShape(controller, cover) {
var brushOption = cover.__brushOption
getCoverRenderer(cover).updateCoverShape(
controller,
cover,
brushOption.range,
brushOption
)
}
function updateZ(cover, brushOption) {
var z = brushOption.z
z == null && (z = COVER_Z)
cover.traverse(function (el) {
el.z = z
el.z2 = z // Consider in given container.
})
}
function updateCoverAfterCreation(controller, cover) {
getCoverRenderer(cover).updateCommon(controller, cover)
updateCoverShape(controller, cover)
}
function getCoverRenderer(cover) {
return coverRenderers[cover.__brushOption.brushType]
} // return target panel or `true` (means global panel)
function getPanelByPoint(controller, e, localCursorPoint) {
var panels = controller._panels
if (!panels) {
return BRUSH_PANEL_GLOBAL // Global panel
}
var panel
var transform = controller._transform
each$7(panels, function (pn) {
pn.isTargetByCursor(e, localCursorPoint, transform) &&
(panel = pn)
})
return panel
} // Return a panel or true
function getPanelByCover(controller, cover) {
var panels = controller._panels
if (!panels) {
return BRUSH_PANEL_GLOBAL // Global panel
}
var panelId = cover.__brushOption.panelId // User may give cover without coord sys info,
// which is then treated as global panel.
return panelId != null ? panels[panelId] : BRUSH_PANEL_GLOBAL
}
function clearCovers(controller) {
var covers = controller._covers
var originalLength = covers.length
each$7(
covers,
function (cover) {
controller.group.remove(cover)
},
controller
)
covers.length = 0
return !!originalLength
}
function trigger(controller, opt) {
var areas = map$1(controller._covers, function (cover) {
var brushOption = cover.__brushOption
var range = clone$3(brushOption.range)
return {
brushType: brushOption.brushType,
panelId: brushOption.panelId,
range: range
}
})
controller.trigger('brush', {
areas: areas,
isEnd: !!opt.isEnd,
removeOnClick: !!opt.removeOnClick
})
}
function shouldShowCover(controller) {
var track = controller._track
if (!track.length) {
return false
}
var p2 = track[track.length - 1]
var p1 = track[0]
var dx = p2[0] - p1[0]
var dy = p2[1] - p1[1]
var dist = mathPow(dx * dx + dy * dy, 0.5)
return dist > UNSELECT_THRESHOLD
}
function getTrackEnds(track) {
var tail = track.length - 1
tail < 0 && (tail = 0)
return [track[0], track[tail]]
}
function createBaseRectCover(
rectRangeConverter,
controller,
brushOption,
edgeNameSequences
) {
var cover = new Group$3()
cover.add(
new Rect$2({
name: 'main',
style: makeStyle(brushOption),
silent: true,
draggable: true,
cursor: 'move',
drift: curry$1(
driftRect,
rectRangeConverter,
controller,
cover,
['n', 's', 'w', 'e']
),
ondragend: curry$1(trigger, controller, { isEnd: true })
})
)
each$7(edgeNameSequences, function (nameSequence) {
cover.add(
new Rect$2({
name: nameSequence.join(''),
style: { opacity: 0 },
draggable: true,
silent: true,
invisible: true,
drift: curry$1(
driftRect,
rectRangeConverter,
controller,
cover,
nameSequence
),
ondragend: curry$1(trigger, controller, { isEnd: true })
})
)
})
return cover
}
function updateBaseRect(controller, cover, localRange, brushOption) {
var lineWidth = brushOption.brushStyle.lineWidth || 0
var handleSize = mathMax(lineWidth, MIN_RESIZE_LINE_WIDTH)
var x = localRange[0][0]
var y = localRange[1][0]
var xa = x - lineWidth / 2
var ya = y - lineWidth / 2
var x2 = localRange[0][1]
var y2 = localRange[1][1]
var x2a = x2 - handleSize + lineWidth / 2
var y2a = y2 - handleSize + lineWidth / 2
var width = x2 - x
var height = y2 - y
var widtha = width + lineWidth
var heighta = height + lineWidth
updateRectShape(controller, cover, 'main', x, y, width, height)
if (brushOption.transformable) {
updateRectShape(
controller,
cover,
'w',
xa,
ya,
handleSize,
heighta
)
updateRectShape(
controller,
cover,
'e',
x2a,
ya,
handleSize,
heighta
)
updateRectShape(
controller,
cover,
'n',
xa,
ya,
widtha,
handleSize
)
updateRectShape(
controller,
cover,
's',
xa,
y2a,
widtha,
handleSize
)
updateRectShape(
controller,
cover,
'nw',
xa,
ya,
handleSize,
handleSize
)
updateRectShape(
controller,
cover,
'ne',
x2a,
ya,
handleSize,
handleSize
)
updateRectShape(
controller,
cover,
'sw',
xa,
y2a,
handleSize,
handleSize
)
updateRectShape(
controller,
cover,
'se',
x2a,
y2a,
handleSize,
handleSize
)
}
}
function updateCommon(controller, cover) {
var brushOption = cover.__brushOption
var transformable = brushOption.transformable
var mainEl = cover.childAt(0)
mainEl.useStyle(makeStyle(brushOption))
mainEl.attr({
silent: !transformable,
cursor: transformable ? 'move' : 'default'
})
each$7(
[
['w'],
['e'],
['n'],
['s'],
['s', 'e'],
['s', 'w'],
['n', 'e'],
['n', 'w']
],
function (nameSequence) {
var el = cover.childOfName(nameSequence.join(''))
var globalDir =
nameSequence.length === 1
? getGlobalDirection1(controller, nameSequence[0])
: getGlobalDirection2(controller, nameSequence)
el &&
el.attr({
silent: !transformable,
invisible: !transformable,
cursor: transformable
? CURSOR_MAP[globalDir] + '-resize'
: null
})
}
)
}
function updateRectShape(controller, cover, name, x, y, w, h) {
var el = cover.childOfName(name)
el &&
el.setShape(
pointsToRect(
clipByPanel(controller, cover, [
[x, y],
[x + w, y + h]
])
)
)
}
function makeStyle(brushOption) {
return defaults({ strokeNoScale: true }, brushOption.brushStyle)
}
function formatRectRange(x, y, x2, y2) {
var min = [mathMin(x, x2), mathMin(y, y2)]
var max = [mathMax(x, x2), mathMax(y, y2)]
return [
[min[0], max[0]],
[min[1], max[1]] // y range
]
}
function getTransform(controller) {
return getTransform$1(controller.group)
}
function getGlobalDirection1(controller, localDirName) {
var map = { w: 'left', e: 'right', n: 'top', s: 'bottom' }
var inverseMap = { left: 'w', right: 'e', top: 'n', bottom: 's' }
var dir = transformDirection(
map[localDirName],
getTransform(controller)
)
return inverseMap[dir]
}
function getGlobalDirection2(controller, localDirNameSeq) {
var globalDir = [
getGlobalDirection1(controller, localDirNameSeq[0]),
getGlobalDirection1(controller, localDirNameSeq[1])
]
;(globalDir[0] === 'e' || globalDir[0] === 'w') &&
globalDir.reverse()
return globalDir.join('')
}
function driftRect(
rectRangeConverter,
controller,
cover,
dirNameSequence,
dx,
dy
) {
var brushOption = cover.__brushOption
var rectRange = rectRangeConverter.toRectRange(brushOption.range)
var localDelta = toLocalDelta(controller, dx, dy)
each$7(dirNameSequence, function (dirName) {
var ind = DIRECTION_MAP[dirName]
rectRange[ind[0]][ind[1]] += localDelta[ind[0]]
})
brushOption.range = rectRangeConverter.fromRectRange(
formatRectRange(
rectRange[0][0],
rectRange[1][0],
rectRange[0][1],
rectRange[1][1]
)
)
updateCoverAfterCreation(controller, cover)
trigger(controller, { isEnd: false })
}
function driftPolygon(controller, cover, dx, dy) {
var range = cover.__brushOption.range
var localDelta = toLocalDelta(controller, dx, dy)
each$7(range, function (point) {
point[0] += localDelta[0]
point[1] += localDelta[1]
})
updateCoverAfterCreation(controller, cover)
trigger(controller, { isEnd: false })
}
function toLocalDelta(controller, dx, dy) {
var thisGroup = controller.group
var localD = thisGroup.transformCoordToLocal(dx, dy)
var localZero = thisGroup.transformCoordToLocal(0, 0)
return [localD[0] - localZero[0], localD[1] - localZero[1]]
}
function clipByPanel(controller, cover, data) {
var panel = getPanelByCover(controller, cover)
return panel && panel !== BRUSH_PANEL_GLOBAL
? panel.clipPath(data, controller._transform)
: clone$3(data)
}
function pointsToRect(points) {
var xmin = mathMin(points[0][0], points[1][0])
var ymin = mathMin(points[0][1], points[1][1])
var xmax = mathMax(points[0][0], points[1][0])
var ymax = mathMax(points[0][1], points[1][1])
return { x: xmin, y: ymin, width: xmax - xmin, height: ymax - ymin }
}
function resetCursor(controller, e, localCursorPoint) {
if (
// Check active
!controller._brushType || // resetCursor should be always called when mouse is in zr area,
// but not called when mouse is out of zr area to avoid bad influence
// if `mousemove`, `mouseup` are triggered from `document` event.
isOutsideZrArea(controller, e.offsetX, e.offsetY)
) {
return
}
var zr = controller._zr
var covers = controller._covers
var currPanel = getPanelByPoint(controller, e, localCursorPoint) // Check whether in covers.
if (!controller._dragging) {
for (var i = 0; i < covers.length; i++) {
var brushOption = covers[i].__brushOption
if (
currPanel &&
(currPanel === BRUSH_PANEL_GLOBAL ||
brushOption.panelId === currPanel.panelId) &&
coverRenderers[brushOption.brushType].contain(
covers[i],
localCursorPoint[0],
localCursorPoint[1]
)
) {
// Use cursor style set on cover.
return
}
}
}
currPanel && zr.setCursorStyle('crosshair')
}
function preventDefault(e) {
var rawE = e.event
rawE.preventDefault && rawE.preventDefault()
}
function mainShapeContain(cover, x, y) {
return cover.childOfName('main').contain(x, y)
}
function updateCoverByMouse(controller, e, localCursorPoint, isEnd) {
var creatingCover = controller._creatingCover
var panel = controller._creatingPanel
var thisBrushOption = controller._brushOption
var eventParams
controller._track.push(localCursorPoint.slice())
if (shouldShowCover(controller) || creatingCover) {
if (panel && !creatingCover) {
thisBrushOption.brushMode === 'single' &&
clearCovers(controller)
var brushOption = clone$3(thisBrushOption)
brushOption.brushType = determineBrushType(
brushOption.brushType,
panel
)
brushOption.panelId =
panel === BRUSH_PANEL_GLOBAL ? null : panel.panelId
creatingCover = controller._creatingCover = createCover(
controller,
brushOption
)
controller._covers.push(creatingCover)
}
if (creatingCover) {
var coverRenderer =
coverRenderers[
determineBrushType(controller._brushType, panel)
]
var coverBrushOption = creatingCover.__brushOption
coverBrushOption.range = coverRenderer.getCreatingRange(
clipByPanel(controller, creatingCover, controller._track)
)
if (isEnd) {
endCreating(controller, creatingCover)
coverRenderer.updateCommon(controller, creatingCover)
}
updateCoverShape(controller, creatingCover)
eventParams = { isEnd: isEnd }
}
} else if (
isEnd &&
thisBrushOption.brushMode === 'single' &&
thisBrushOption.removeOnClick
) {
// Help user to remove covers easily, only by a tiny drag, in 'single' mode.
// But a single click do not clear covers, because user may have casual
// clicks (for example, click on other component and do not expect covers
// disappear).
// Only some cover removed, trigger action, but not every click trigger action.
if (
getPanelByPoint(controller, e, localCursorPoint) &&
clearCovers(controller)
) {
eventParams = { isEnd: isEnd, removeOnClick: true }
}
}
return eventParams
}
function determineBrushType(brushType, panel) {
if (brushType === 'auto') {
return panel.defaultBrushType
}
return brushType
}
var pointerHandlers = {
mousedown: function mousedown(e) {
if (this._dragging) {
// In case some browser do not support globalOut,
// and release mouse out side the browser.
handleDragEnd(this, e)
} else if (!e.target || !e.target.draggable) {
preventDefault(e)
var localCursorPoint = this.group.transformCoordToLocal(
e.offsetX,
e.offsetY
)
this._creatingCover = null
var panel = (this._creatingPanel = getPanelByPoint(
this,
e,
localCursorPoint
))
if (panel) {
this._dragging = true
this._track = [localCursorPoint.slice()]
}
}
},
mousemove: function mousemove(e) {
var x = e.offsetX
var y = e.offsetY
var localCursorPoint = this.group.transformCoordToLocal(x, y)
resetCursor(this, e, localCursorPoint)
if (this._dragging) {
preventDefault(e)
var eventParams = updateCoverByMouse(
this,
e,
localCursorPoint,
false
)
eventParams && trigger(this, eventParams)
}
},
mouseup: function mouseup(e) {
handleDragEnd(this, e)
}
}
function handleDragEnd(controller, e) {
if (controller._dragging) {
preventDefault(e)
var x = e.offsetX
var y = e.offsetY
var localCursorPoint = controller.group.transformCoordToLocal(
x,
y
)
var eventParams = updateCoverByMouse(
controller,
e,
localCursorPoint,
true
)
controller._dragging = false
controller._track = []
controller._creatingCover = null // trigger event shoule be at final, after procedure will be nested.
eventParams && trigger(controller, eventParams)
}
}
function isOutsideZrArea(controller, x, y) {
var zr = controller._zr
return x < 0 || x > zr.getWidth() || y < 0 || y > zr.getHeight()
}
/**
* key: brushType
*/ var coverRenderers = {
lineX: getLineRenderer(0),
lineY: getLineRenderer(1),
rect: {
createCover: function createCover(controller, brushOption) {
function returnInput(range) {
return range
}
return createBaseRectCover(
{ toRectRange: returnInput, fromRectRange: returnInput },
controller,
brushOption,
[
['w'],
['e'],
['n'],
['s'],
['s', 'e'],
['s', 'w'],
['n', 'e'],
['n', 'w']
]
)
},
getCreatingRange: function getCreatingRange(localTrack) {
var ends = getTrackEnds(localTrack)
return formatRectRange(
ends[1][0],
ends[1][1],
ends[0][0],
ends[0][1]
)
},
updateCoverShape: function updateCoverShape(
controller,
cover,
localRange,
brushOption
) {
updateBaseRect(controller, cover, localRange, brushOption)
},
updateCommon: updateCommon,
contain: mainShapeContain
},
polygon: {
createCover: function createCover(controller, brushOption) {
var cover = new Group$3() // Do not use graphic.Polygon because graphic.Polyline do not close the
// border of the shape when drawing, which is a better experience for user.
cover.add(
new Polyline$1({
name: 'main',
style: makeStyle(brushOption),
silent: true
})
)
return cover
},
getCreatingRange: function getCreatingRange(localTrack) {
return localTrack
},
endCreating: function endCreating(controller, cover) {
cover.remove(cover.childAt(0)) // Use graphic.Polygon close the shape.
cover.add(
new Polygon$1({
name: 'main',
draggable: true,
drift: curry$1(driftPolygon, controller, cover),
ondragend: curry$1(trigger, controller, { isEnd: true })
})
)
},
updateCoverShape: function updateCoverShape(
controller,
cover,
localRange,
brushOption
) {
cover
.childAt(0)
.setShape({
points: clipByPanel(controller, cover, localRange)
})
},
updateCommon: updateCommon,
contain: mainShapeContain
}
}
function getLineRenderer(xyIndex) {
return {
createCover: function createCover(controller, brushOption) {
return createBaseRectCover(
{
toRectRange: function toRectRange(range) {
var rectRange = [range, [0, 100]]
xyIndex && rectRange.reverse()
return rectRange
},
fromRectRange: function fromRectRange(rectRange) {
return rectRange[xyIndex]
}
},
controller,
brushOption,
[
[['w'], ['e']],
[['n'], ['s']]
][xyIndex]
)
},
getCreatingRange: function getCreatingRange(localTrack) {
var ends = getTrackEnds(localTrack)
var min = mathMin(ends[0][xyIndex], ends[1][xyIndex])
var max = mathMax(ends[0][xyIndex], ends[1][xyIndex])
return [min, max]
},
updateCoverShape: function updateCoverShape(
controller,
cover,
localRange,
brushOption
) {
var otherExtent // If brushWidth not specified, fit the panel.
var panel = getPanelByCover(controller, cover)
if (
panel !== BRUSH_PANEL_GLOBAL &&
panel.getLinearBrushOtherExtent
) {
otherExtent = panel.getLinearBrushOtherExtent(xyIndex)
} else {
var zr = controller._zr
otherExtent = [
0,
[zr.getWidth(), zr.getHeight()][1 - xyIndex]
]
}
var rectRange = [localRange, otherExtent]
xyIndex && rectRange.reverse()
updateBaseRect(controller, cover, rectRange, brushOption)
},
updateCommon: updateCommon,
contain: mainShapeContain
}
}
var BrushController$1 = BrushController
function makeRectPanelClipPath(rect) {
rect = normalizeRect(rect)
return function (localPoints) {
return clipPointsByRect(localPoints, rect)
}
}
function makeLinearBrushOtherExtent(rect, specifiedXYIndex) {
rect = normalizeRect(rect)
return function (xyIndex) {
var idx = specifiedXYIndex != null ? specifiedXYIndex : xyIndex
var brushWidth = idx ? rect.width : rect.height
var base = idx ? rect.x : rect.y
return [base, base + (brushWidth || 0)]
}
}
function makeRectIsTargetByCursor(rect, api, targetModel) {
var boundingRect = normalizeRect(rect)
return function (e, localCursorPoint) {
return (
boundingRect.contain(
localCursorPoint[0],
localCursorPoint[1]
) && !onIrrelevantElement(e, api, targetModel)
)
}
} // Consider width/height is negative.
function normalizeRect(rect) {
return BoundingRect$1.create(rect)
}
var inner$4 = makeInner()
var clone$1 = clone$3
var bind = bind$1
/**
* Base axis pointer class in 2D.
*/ var BaseAxisPointer = /** @class */ (function () {
function BaseAxisPointer() {
this._dragging = false
/**
* In px, arbitrary value. Do not set too small,
* no animation is ok for most cases.
*/ this.animationThreshold = 15
}
/**
* @implement
*/ BaseAxisPointer.prototype.render = function (
axisModel,
axisPointerModel,
api,
forceRender
) {
var value = axisPointerModel.get('value')
var status = axisPointerModel.get('status') // Bind them to `this`, not in closure, otherwise they will not
// be replaced when user calling setOption in not merge mode.
this._axisModel = axisModel
this._axisPointerModel = axisPointerModel
this._api = api // Optimize: `render` will be called repeatly during mouse move.
// So it is power consuming if performing `render` each time,
// especially on mobile device.
if (
!forceRender &&
this._lastValue === value &&
this._lastStatus === status
) {
return
}
this._lastValue = value
this._lastStatus = status
var group = this._group
var handle = this._handle
if (!status || status === 'hide') {
// Do not clear here, for animation better.
group && group.hide()
handle && handle.hide()
return
}
group && group.show()
handle && handle.show() // Otherwise status is 'show'
var elOption = {}
this.makeElOption(
elOption,
value,
axisModel,
axisPointerModel,
api
) // Enable change axis pointer type.
var graphicKey = elOption.graphicKey
if (graphicKey !== this._lastGraphicKey) {
this.clear(api)
}
this._lastGraphicKey = graphicKey
var moveAnimation = (this._moveAnimation =
this.determineAnimation(axisModel, axisPointerModel))
if (!group) {
group = this._group = new Group$3()
this.createPointerEl(
group,
elOption,
axisModel,
axisPointerModel
)
this.createLabelEl(group, elOption, axisModel, axisPointerModel)
api.getZr().add(group)
} else {
var doUpdateProps = curry$1(
updateProps,
axisPointerModel,
moveAnimation
)
this.updatePointerEl(group, elOption, doUpdateProps)
this.updateLabelEl(
group,
elOption,
doUpdateProps,
axisPointerModel
)
}
updateMandatoryProps(group, axisPointerModel, true)
this._renderHandle(value)
}
/**
* @implement
*/ BaseAxisPointer.prototype.remove = function (api) {
this.clear(api)
}
/**
* @implement
*/ BaseAxisPointer.prototype.dispose = function (api) {
this.clear(api)
}
/**
* @protected
*/ BaseAxisPointer.prototype.determineAnimation = function (
axisModel,
axisPointerModel
) {
var animation = axisPointerModel.get('animation')
var axis = axisModel.axis
var isCategoryAxis = axis.type === 'category'
var useSnap = axisPointerModel.get('snap') // Value axis without snap always do not snap.
if (!useSnap && !isCategoryAxis) {
return false
}
if (animation === 'auto' || animation == null) {
var animationThreshold = this.animationThreshold
if (
isCategoryAxis &&
axis.getBandWidth() > animationThreshold
) {
return true
} // It is important to auto animation when snap used. Consider if there is
// a dataZoom, animation will be disabled when too many points exist, while
// it will be enabled for better visual effect when little points exist.
if (useSnap) {
var seriesDataCount = getAxisInfo(axisModel).seriesDataCount
var axisExtent = axis.getExtent() // Approximate band width
return (
Math.abs(axisExtent[0] - axisExtent[1]) / seriesDataCount >
animationThreshold
)
}
return false
}
return animation === true
}
/**
* add {pointer, label, graphicKey} to elOption
* @protected
*/ BaseAxisPointer.prototype.makeElOption = function (
elOption,
value,
axisModel,
axisPointerModel,
api
) {
// Shoule be implemenented by sub-class.
}
/**
* @protected
*/ BaseAxisPointer.prototype.createPointerEl = function (
group,
elOption,
axisModel,
axisPointerModel
) {
var pointerOption = elOption.pointer
if (pointerOption) {
var pointerEl = (inner$4(group).pointerEl = new graphic[
pointerOption.type
](clone$1(elOption.pointer)))
group.add(pointerEl)
}
}
/**
* @protected
*/ BaseAxisPointer.prototype.createLabelEl = function (
group,
elOption,
axisModel,
axisPointerModel
) {
if (elOption.label) {
var labelEl = (inner$4(group).labelEl = new ZRText$1(
clone$1(elOption.label)
))
group.add(labelEl)
updateLabelShowHide(labelEl, axisPointerModel)
}
}
/**
* @protected
*/ BaseAxisPointer.prototype.updatePointerEl = function (
group,
elOption,
updateProps
) {
var pointerEl = inner$4(group).pointerEl
if (pointerEl && elOption.pointer) {
pointerEl.setStyle(elOption.pointer.style)
updateProps(pointerEl, { shape: elOption.pointer.shape })
}
}
/**
* @protected
*/ BaseAxisPointer.prototype.updateLabelEl = function (
group,
elOption,
updateProps,
axisPointerModel
) {
var labelEl = inner$4(group).labelEl
if (labelEl) {
labelEl.setStyle(elOption.label.style)
updateProps(labelEl, {
// Consider text length change in vertical axis, animation should
// be used on shape, otherwise the effect will be weird.
// TODOTODO
// shape: elOption.label.shape,
x: elOption.label.x,
y: elOption.label.y
})
updateLabelShowHide(labelEl, axisPointerModel)
}
}
/**
* @private
*/ BaseAxisPointer.prototype._renderHandle = function (value) {
if (this._dragging || !this.updateHandleTransform) {
return
}
var axisPointerModel = this._axisPointerModel
var zr = this._api.getZr()
var handle = this._handle
var handleModel = axisPointerModel.getModel('handle')
var status = axisPointerModel.get('status')
if (!handleModel.get('show') || !status || status === 'hide') {
handle && zr.remove(handle)
this._handle = null
return
}
var isInit
if (!this._handle) {
isInit = true
handle = this._handle = createIcon(handleModel.get('icon'), {
cursor: 'move',
draggable: true,
onmousemove: function onmousemove(e) {
// Fot mobile devicem, prevent screen slider on the button.
stop(e.event)
},
onmousedown: bind(this._onHandleDragMove, this, 0, 0),
drift: bind(this._onHandleDragMove, this),
ondragend: bind(this._onHandleDragEnd, this)
})
zr.add(handle)
}
updateMandatoryProps(handle, axisPointerModel, false) // update style
handle.setStyle(
handleModel.getItemStyle(null, [
'color',
'borderColor',
'borderWidth',
'opacity',
'shadowColor',
'shadowBlur',
'shadowOffsetX',
'shadowOffsetY'
])
) // update position
var handleSize = handleModel.get('size')
if (!isArray(handleSize)) {
handleSize = [handleSize, handleSize]
}
handle.scaleX = handleSize[0] / 2
handle.scaleY = handleSize[1] / 2
createOrUpdate(
this,
'_doDispatchAxisPointer',
handleModel.get('throttle') || 0,
'fixRate'
)
this._moveHandleToValue(value, isInit)
}
BaseAxisPointer.prototype._moveHandleToValue = function (
value,
isInit
) {
updateProps(
this._axisPointerModel,
!isInit && this._moveAnimation,
this._handle,
getHandleTransProps(
this.getHandleTransform(
value,
this._axisModel,
this._axisPointerModel
)
)
)
}
BaseAxisPointer.prototype._onHandleDragMove = function (dx, dy) {
var handle = this._handle
if (!handle) {
return
}
this._dragging = true // Persistent for throttle.
var trans = this.updateHandleTransform(
getHandleTransProps(handle),
[dx, dy],
this._axisModel,
this._axisPointerModel
)
this._payloadInfo = trans
handle.stopAnimation()
handle.attr(getHandleTransProps(trans))
inner$4(handle).lastProp = null
this._doDispatchAxisPointer()
}
/**
* Throttled method.
*/ BaseAxisPointer.prototype._doDispatchAxisPointer = function () {
var handle = this._handle
if (!handle) {
return
}
var payloadInfo = this._payloadInfo
var axisModel = this._axisModel
this._api.dispatchAction({
type: 'updateAxisPointer',
x: payloadInfo.cursorPoint[0],
y: payloadInfo.cursorPoint[1],
tooltipOption: payloadInfo.tooltipOption,
axesInfo: [
{
axisDim: axisModel.axis.dim,
axisIndex: axisModel.componentIndex
}
]
})
}
BaseAxisPointer.prototype._onHandleDragEnd = function () {
this._dragging = false
var handle = this._handle
if (!handle) {
return
}
var value = this._axisPointerModel.get('value') // Consider snap or categroy axis, handle may be not consistent with
// axisPointer. So move handle to align the exact value position when
// drag ended.
this._moveHandleToValue(value) // For the effect: tooltip will be shown when finger holding on handle
// button, and will be hidden after finger left handle button.
this._api.dispatchAction({ type: 'hideTip' })
}
/**
* @private
*/ BaseAxisPointer.prototype.clear = function (api) {
this._lastValue = null
this._lastStatus = null
var zr = api.getZr()
var group = this._group
var handle = this._handle
if (zr && group) {
this._lastGraphicKey = null
group && zr.remove(group)
handle && zr.remove(handle)
this._group = null
this._handle = null
this._payloadInfo = null
}
clear$1(this, '_doDispatchAxisPointer')
}
/**
* @protected
*/ BaseAxisPointer.prototype.doClear = function () {
// Implemented by sub-class if necessary.
}
BaseAxisPointer.prototype.buildLabel = function (
xy,
wh,
xDimIndex
) {
xDimIndex = xDimIndex || 0
return {
x: xy[xDimIndex],
y: xy[1 - xDimIndex],
width: wh[xDimIndex],
height: wh[1 - xDimIndex]
}
}
return BaseAxisPointer
})()
function updateProps(animationModel, moveAnimation, el, props) {
// Animation optimize.
if (!propsEqual(inner$4(el).lastProp, props)) {
inner$4(el).lastProp = props
moveAnimation
? updateProps$1(el, props, animationModel)
: (el.stopAnimation(), el.attr(props))
}
}
function propsEqual(lastProps, newProps) {
if (isObject$2(lastProps) && isObject$2(newProps)) {
var equals_1 = true
each$7(newProps, function (item, key) {
equals_1 = equals_1 && propsEqual(lastProps[key], item)
})
return !!equals_1
} else {
return lastProps === newProps
}
}
function updateLabelShowHide(labelEl, axisPointerModel) {
labelEl[axisPointerModel.get(['label', 'show']) ? 'show' : 'hide']()
}
function getHandleTransProps(trans) {
return {
x: trans.x || 0,
y: trans.y || 0,
rotation: trans.rotation || 0
}
}
function updateMandatoryProps(group, axisPointerModel, silent) {
var z = axisPointerModel.get('z')
var zlevel = axisPointerModel.get('zlevel')
group &&
group.traverse(function (el) {
if (el.type !== 'group') {
z != null && (el.z = z)
zlevel != null && (el.zlevel = zlevel)
el.silent = silent
}
})
}
var BaseAxisPointer$1 = BaseAxisPointer
function buildElStyle(axisPointerModel) {
var axisPointerType = axisPointerModel.get('type')
var styleModel = axisPointerModel.getModel(
axisPointerType + 'Style'
)
var style
if (axisPointerType === 'line') {
style = styleModel.getLineStyle()
style.fill = null
} else if (axisPointerType === 'shadow') {
style = styleModel.getAreaStyle()
style.stroke = null
}
return style
}
/**
* @param {Function} labelPos {align, verticalAlign, position}
*/ function buildLabelElOption(
elOption,
axisModel,
axisPointerModel,
api,
labelPos
) {
var value = axisPointerModel.get('value')
var text = getValueLabel(
value,
axisModel.axis,
axisModel.ecModel,
axisPointerModel.get('seriesDataIndices'),
{
precision: axisPointerModel.get(['label', 'precision']),
formatter: axisPointerModel.get(['label', 'formatter'])
}
)
var labelModel = axisPointerModel.getModel('label')
var paddings = normalizeCssArray(labelModel.get('padding') || 0)
var font = labelModel.getFont()
var textRect = getBoundingRect(text, font)
var position = labelPos.position
var width = textRect.width + paddings[1] + paddings[3]
var height = textRect.height + paddings[0] + paddings[2] // Adjust by align.
var align = labelPos.align
align === 'right' && (position[0] -= width)
align === 'center' && (position[0] -= width / 2)
var verticalAlign = labelPos.verticalAlign
verticalAlign === 'bottom' && (position[1] -= height)
verticalAlign === 'middle' && (position[1] -= height / 2) // Not overflow ec container
confineInContainer(position, width, height, api)
var bgColor = labelModel.get('backgroundColor')
if (!bgColor || bgColor === 'auto') {
bgColor = axisModel.get(['axisLine', 'lineStyle', 'color'])
}
elOption.label = {
// shape: {x: 0, y: 0, width: width, height: height, r: labelModel.get('borderRadius')},
x: position[0],
y: position[1],
style: createTextStyle(labelModel, {
text: text,
font: font,
fill: labelModel.getTextColor(),
padding: paddings,
backgroundColor: bgColor
}), // Lable should be over axisPointer.
z2: 10
}
} // Do not overflow ec container
function confineInContainer(position, width, height, api) {
var viewWidth = api.getWidth()
var viewHeight = api.getHeight()
position[0] = Math.min(position[0] + width, viewWidth) - width
position[1] = Math.min(position[1] + height, viewHeight) - height
position[0] = Math.max(position[0], 0)
position[1] = Math.max(position[1], 0)
}
function getValueLabel(value, axis, ecModel, seriesDataIndices, opt) {
value = axis.scale.parse(value)
var text = axis.scale.getLabel(
{ value: value },
{
// If `precision` is set, width can be fixed (like '12.00500'), which
// helps to debounce when when moving label.
precision: opt.precision
}
)
var formatter = opt.formatter
if (formatter) {
var params_1 = {
value: getAxisRawValue(axis, { value: value }),
axisDimension: axis.dim,
axisIndex: axis.index,
seriesData: []
}
each$7(seriesDataIndices, function (idxItem) {
var series = ecModel.getSeriesByIndex(idxItem.seriesIndex)
var dataIndex = idxItem.dataIndexInside
var dataParams = series && series.getDataParams(dataIndex)
dataParams && params_1.seriesData.push(dataParams)
})
if (isString(formatter)) {
text = formatter.replace('{value}', text)
} else if (isFunction(formatter)) {
text = formatter(params_1)
}
}
return text
}
function getTransformedPosition(axis, value, layoutInfo) {
var transform = create()
rotate(transform, transform, layoutInfo.rotation)
translate(transform, transform, layoutInfo.position)
return applyTransform(
[
axis.dataToCoord(value),
(layoutInfo.labelOffset || 0) +
(layoutInfo.labelDirection || 1) *
(layoutInfo.labelMargin || 0)
],
transform
)
}
function buildCartesianSingleLabelElOption(
value,
elOption,
layoutInfo,
axisModel,
axisPointerModel,
api
) {
// @ts-ignore
var textLayout = AxisBuilder$1.innerTextLayout(
layoutInfo.rotation,
0,
layoutInfo.labelDirection
)
layoutInfo.labelMargin = axisPointerModel.get(['label', 'margin'])
buildLabelElOption(elOption, axisModel, axisPointerModel, api, {
position: getTransformedPosition(
axisModel.axis,
value,
layoutInfo
),
align: textLayout.textAlign,
verticalAlign: textLayout.textVerticalAlign
})
}
function makeLineShape(p1, p2, xDimIndex) {
xDimIndex = xDimIndex || 0
return {
x1: p1[xDimIndex],
y1: p1[1 - xDimIndex],
x2: p2[xDimIndex],
y2: p2[1 - xDimIndex]
}
}
function makeRectShape(xy, wh, xDimIndex) {
xDimIndex = xDimIndex || 0
return {
x: xy[xDimIndex],
y: xy[1 - xDimIndex],
width: wh[xDimIndex],
height: wh[1 - xDimIndex]
}
}
var CartesianAxisPointer = /** @class */ (function (_super) {
__extends(CartesianAxisPointer, _super)
function CartesianAxisPointer() {
return (_super !== null && _super.apply(this, arguments)) || this
}
/**
* @override
*/ CartesianAxisPointer.prototype.makeElOption = function (
elOption,
value,
axisModel,
axisPointerModel,
api
) {
var axis = axisModel.axis
var grid = axis.grid
var axisPointerType = axisPointerModel.get('type')
var otherExtent = getCartesian(grid, axis)
.getOtherAxis(axis)
.getGlobalExtent()
var pixelValue = axis.toGlobalCoord(axis.dataToCoord(value, true))
if (axisPointerType && axisPointerType !== 'none') {
var elStyle = buildElStyle(axisPointerModel)
var pointerOption = pointerShapeBuilder[axisPointerType](
axis,
pixelValue,
otherExtent
)
pointerOption.style = elStyle
elOption.graphicKey = pointerOption.type
elOption.pointer = pointerOption
}
var layoutInfo = layout$1(grid.model, axisModel)
buildCartesianSingleLabelElOption(
// @ts-ignore
value,
elOption,
layoutInfo,
axisModel,
axisPointerModel,
api
)
}
/**
* @override
*/ CartesianAxisPointer.prototype.getHandleTransform = function (
value,
axisModel,
axisPointerModel
) {
var layoutInfo = layout$1(axisModel.axis.grid.model, axisModel, {
labelInside: false
}) // @ts-ignore
layoutInfo.labelMargin = axisPointerModel.get([
'handle',
'margin'
])
var pos = getTransformedPosition(
axisModel.axis,
value,
layoutInfo
)
return {
x: pos[0],
y: pos[1],
rotation:
layoutInfo.rotation +
(layoutInfo.labelDirection < 0 ? Math.PI : 0)
}
}
/**
* @override
*/ CartesianAxisPointer.prototype.updateHandleTransform =
function (transform, delta, axisModel, axisPointerModel) {
var axis = axisModel.axis
var grid = axis.grid
var axisExtent = axis.getGlobalExtent(true)
var otherExtent = getCartesian(grid, axis)
.getOtherAxis(axis)
.getGlobalExtent()
var dimIndex = axis.dim === 'x' ? 0 : 1
var currPosition = [transform.x, transform.y]
currPosition[dimIndex] += delta[dimIndex]
currPosition[dimIndex] = Math.min(
axisExtent[1],
currPosition[dimIndex]
)
currPosition[dimIndex] = Math.max(
axisExtent[0],
currPosition[dimIndex]
)
var cursorOtherValue = (otherExtent[1] + otherExtent[0]) / 2
var cursorPoint = [cursorOtherValue, cursorOtherValue]
cursorPoint[dimIndex] = currPosition[dimIndex] // Make tooltip do not overlap axisPointer and in the middle of the grid.
var tooltipOptions = [
{ verticalAlign: 'middle' },
{ align: 'center' }
]
return {
x: currPosition[0],
y: currPosition[1],
rotation: transform.rotation,
cursorPoint: cursorPoint,
tooltipOption: tooltipOptions[dimIndex]
}
}
return CartesianAxisPointer
})(BaseAxisPointer$1)
function getCartesian(grid, axis) {
var opt = {}
opt[axis.dim + 'AxisIndex'] = axis.index
return grid.getCartesian(opt)
}
var pointerShapeBuilder = {
line: function line(axis, pixelValue, otherExtent) {
var targetShape = makeLineShape(
[pixelValue, otherExtent[0]],
[pixelValue, otherExtent[1]],
getAxisDimIndex(axis)
)
return {
type: 'Line',
subPixelOptimize: true,
shape: targetShape
}
},
shadow: function shadow(axis, pixelValue, otherExtent) {
var bandWidth = Math.max(1, axis.getBandWidth())
var span = otherExtent[1] - otherExtent[0]
return {
type: 'Rect',
shape: makeRectShape(
[pixelValue - bandWidth / 2, otherExtent[0]],
[bandWidth, span],
getAxisDimIndex(axis)
)
}
}
}
function getAxisDimIndex(axis) {
return axis.dim === 'x' ? 0 : 1
}
var CartesianAxisPointer$1 = CartesianAxisPointer
var AxisPointerModel = /** @class */ (function (_super) {
__extends(AxisPointerModel, _super)
function AxisPointerModel() {
var _this =
(_super !== null && _super.apply(this, arguments)) || this
_this.type = AxisPointerModel.type
return _this
}
AxisPointerModel.type = 'axisPointer'
AxisPointerModel.defaultOption = {
// 'auto' means that show when triggered by tooltip or handle.
show: 'auto', // zlevel: 0,
z: 50,
type: 'line', // axispointer triggered by tootip determine snap automatically,
// see `modelHelper`.
snap: false,
triggerTooltip: true,
value: null,
status: null,
link: [], // Do not set 'auto' here, otherwise global animation: false
// will not effect at this axispointer.
animation: null,
animationDurationUpdate: 200,
lineStyle: { color: '#B9BEC9', width: 1, type: 'dashed' },
shadowStyle: { color: 'rgba(210,219,238,0.2)' },
label: {
show: true,
formatter: null,
precision: 'auto',
margin: 3,
color: '#fff',
padding: [5, 7, 5, 7],
backgroundColor: 'auto',
borderColor: null,
borderWidth: 0,
borderRadius: 3
},
handle: {
show: false, // eslint-disable-next-line
icon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4h1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7v-1.2h6.6z M13.3,22H6.7v-1.2h6.6z M13.3,19.6H6.7v-1.2h6.6z',
size: 45, // handle margin is from symbol center to axis, which is stable when circular move.
margin: 50, // color: '#1b8bbd'
// color: '#2f4554'
color: '#333',
shadowBlur: 3,
shadowColor: '#aaa',
shadowOffsetX: 0,
shadowOffsetY: 2, // For mobile performance
throttle: 40
}
}
return AxisPointerModel
})(ComponentModel$1)
var AxisPointerModel$1 = AxisPointerModel
var inner$3 = makeInner()
var each$4 = each$7
/**
* @param {string} key
* @param {module:echarts/ExtensionAPI} api
* @param {Function} handler
* param: {string} currTrigger
* param: {Array.} point
*/ function register(key, api, handler) {
if (env$1.node) {
return
}
var zr = api.getZr()
inner$3(zr).records || (inner$3(zr).records = {})
initGlobalListeners(zr, api)
var record =
inner$3(zr).records[key] || (inner$3(zr).records[key] = {})
record.handler = handler
}
function initGlobalListeners(zr, api) {
if (inner$3(zr).initialized) {
return
}
inner$3(zr).initialized = true
useHandler('click', curry$1(doEnter, 'click'))
useHandler('mousemove', curry$1(doEnter, 'mousemove')) // useHandler('mouseout', onLeave);
useHandler('globalout', onLeave)
function useHandler(eventType, cb) {
zr.on(eventType, function (e) {
var dis = makeDispatchAction$1(api)
each$4(inner$3(zr).records, function (record) {
record && cb(record, e, dis.dispatchAction)
})
dispatchTooltipFinally(dis.pendings, api)
})
}
}
function dispatchTooltipFinally(pendings, api) {
var showLen = pendings.showTip.length
var hideLen = pendings.hideTip.length
var actuallyPayload
if (showLen) {
actuallyPayload = pendings.showTip[showLen - 1]
} else if (hideLen) {
actuallyPayload = pendings.hideTip[hideLen - 1]
}
if (actuallyPayload) {
actuallyPayload.dispatchAction = null
api.dispatchAction(actuallyPayload)
}
}
function onLeave(record, e, dispatchAction) {
record.handler('leave', null, dispatchAction)
}
function doEnter(currTrigger, record, e, dispatchAction) {
record.handler(currTrigger, e, dispatchAction)
}
function makeDispatchAction$1(api) {
var pendings = { showTip: [], hideTip: [] } // FIXME
// better approach?
// 'showTip' and 'hideTip' can be triggered by axisPointer and tooltip,
// which may be conflict, (axisPointer call showTip but tooltip call hideTip);
// So we have to add "final stage" to merge those dispatched actions.
var dispatchAction = function dispatchAction(payload) {
var pendingList = pendings[payload.type]
if (pendingList) {
pendingList.push(payload)
} else {
payload.dispatchAction = dispatchAction
api.dispatchAction(payload)
}
}
return { dispatchAction: dispatchAction, pendings: pendings }
}
function unregister(key, api) {
if (env$1.node) {
return
}
var zr = api.getZr()
var record = (inner$3(zr).records || {})[key]
if (record) {
inner$3(zr).records[key] = null
}
}
var AxisPointerView = /** @class */ (function (_super) {
__extends(AxisPointerView, _super)
function AxisPointerView() {
var _this =
(_super !== null && _super.apply(this, arguments)) || this
_this.type = AxisPointerView.type
return _this
}
AxisPointerView.prototype.render = function (
globalAxisPointerModel,
ecModel,
api
) {
var globalTooltipModel = ecModel.getComponent('tooltip')
var triggerOn =
globalAxisPointerModel.get('triggerOn') ||
(globalTooltipModel && globalTooltipModel.get('triggerOn')) ||
'mousemove|click' // Register global listener in AxisPointerView to enable
// AxisPointerView to be independent to Tooltip.
register(
'axisPointer',
api,
function (currTrigger, e, dispatchAction) {
// If 'none', it is not controlled by mouse totally.
if (
triggerOn !== 'none' &&
(currTrigger === 'leave' ||
triggerOn.indexOf(currTrigger) >= 0)
) {
dispatchAction({
type: 'updateAxisPointer',
currTrigger: currTrigger,
x: e && e.offsetX,
y: e && e.offsetY
})
}
}
)
}
AxisPointerView.prototype.remove = function (ecModel, api) {
unregister('axisPointer', api)
}
AxisPointerView.prototype.dispose = function (ecModel, api) {
unregister('axisPointer', api)
}
AxisPointerView.type = 'axisPointer'
return AxisPointerView
})(ComponentView$1)
var AxisPointerView$1 = AxisPointerView
/**
* @param finder contains {seriesIndex, dataIndex, dataIndexInside}
* @param ecModel
* @return {point: [x, y], el: ...} point Will not be null.
*/ function findPointFromSeries(finder, ecModel) {
var point = []
var seriesIndex = finder.seriesIndex
var seriesModel
if (
seriesIndex == null ||
!(seriesModel = ecModel.getSeriesByIndex(seriesIndex))
) {
return { point: [] }
}
var data = seriesModel.getData()
var dataIndex = queryDataIndex(data, finder)
if (dataIndex == null || dataIndex < 0 || isArray(dataIndex)) {
return { point: [] }
}
var el = data.getItemGraphicEl(dataIndex)
var coordSys = seriesModel.coordinateSystem
if (seriesModel.getTooltipPosition) {
point = seriesModel.getTooltipPosition(dataIndex) || []
} else if (coordSys && coordSys.dataToPoint) {
if (finder.isStacked) {
var baseAxis = coordSys.getBaseAxis()
var valueAxis = coordSys.getOtherAxis(baseAxis)
var valueAxisDim = valueAxis.dim
var baseAxisDim = baseAxis.dim
var baseDataOffset =
valueAxisDim === 'x' || valueAxisDim === 'radius' ? 1 : 0
var baseDim = data.mapDimension(baseAxisDim)
var stackedData = []
stackedData[baseDataOffset] = data.get(baseDim, dataIndex)
stackedData[1 - baseDataOffset] = data.get(
data.getCalculationInfo('stackResultDimension'),
dataIndex
)
point = coordSys.dataToPoint(stackedData) || []
} else {
point =
coordSys.dataToPoint(
data.getValues(
map$1(coordSys.dimensions, function (dim) {
return data.mapDimension(dim)
}),
dataIndex
)
) || []
}
} else if (el) {
// Use graphic bounding rect
var rect = el.getBoundingRect().clone()
rect.applyTransform(el.transform)
point = [rect.x + rect.width / 2, rect.y + rect.height / 2]
}
return { point: point, el: el }
}
var inner$2 = makeInner()
/**
* Basic logic: check all axis, if they do not demand show/highlight,
* then hide/downplay them.
*
* @return content of event obj for echarts.connect.
*/ function axisTrigger(payload, ecModel, api) {
var currTrigger = payload.currTrigger
var point = [payload.x, payload.y]
var finder = payload
var dispatchAction =
payload.dispatchAction || bind$1(api.dispatchAction, api)
var coordSysAxesInfo =
ecModel.getComponent('axisPointer').coordSysAxesInfo // Pending
// See #6121. But we are not able to reproduce it yet.
if (!coordSysAxesInfo) {
return
}
if (illegalPoint(point)) {
// Used in the default behavior of `connection`: use the sample seriesIndex
// and dataIndex. And also used in the tooltipView trigger.
point = findPointFromSeries(
{
seriesIndex: finder.seriesIndex, // Do not use dataIndexInside from other ec instance.
// FIXME: auto detect it?
dataIndex: finder.dataIndex
},
ecModel
).point
}
var isIllegalPoint = illegalPoint(point) // Axis and value can be specified when calling dispatchAction({type: 'updateAxisPointer'}).
// Notice: In this case, it is difficult to get the `point` (which is necessary to show
// tooltip, so if point is not given, we just use the point found by sample seriesIndex
// and dataIndex.
var inputAxesInfo = finder.axesInfo
var axesInfo = coordSysAxesInfo.axesInfo
var shouldHide = currTrigger === 'leave' || illegalPoint(point)
var outputPayload = {}
var showValueMap = {}
var dataByCoordSys = { list: [], map: {} }
var updaters = {
showPointer: curry$1(showPointer, showValueMap),
showTooltip: curry$1(showTooltip, dataByCoordSys)
} // Process for triggered axes.
each$7(
coordSysAxesInfo.coordSysMap,
function (coordSys, coordSysKey) {
// If a point given, it must be contained by the coordinate system.
var coordSysContainsPoint =
isIllegalPoint || coordSys.containPoint(point)
each$7(
coordSysAxesInfo.coordSysAxesInfo[coordSysKey],
function (axisInfo, key) {
var axis = axisInfo.axis
var inputAxisInfo = findInputAxisInfo(
inputAxesInfo,
axisInfo
) // If no inputAxesInfo, no axis is restricted.
if (
!shouldHide &&
coordSysContainsPoint &&
(!inputAxesInfo || inputAxisInfo)
) {
var val = inputAxisInfo && inputAxisInfo.value
if (val == null && !isIllegalPoint) {
val = axis.pointToData(point)
}
val != null &&
processOnAxis(
axisInfo,
val,
updaters,
false,
outputPayload
)
}
}
)
}
) // Process for linked axes.
var linkTriggers = {}
each$7(axesInfo, function (tarAxisInfo, tarKey) {
var linkGroup = tarAxisInfo.linkGroup // If axis has been triggered in the previous stage, it should not be triggered by link.
if (linkGroup && !showValueMap[tarKey]) {
each$7(linkGroup.axesInfo, function (srcAxisInfo, srcKey) {
var srcValItem = showValueMap[srcKey] // If srcValItem exist, source axis is triggered, so link to target axis.
if (srcAxisInfo !== tarAxisInfo && srcValItem) {
var val = srcValItem.value
linkGroup.mapper &&
(val = tarAxisInfo.axis.scale.parse(
linkGroup.mapper(
val,
makeMapperParam(srcAxisInfo),
makeMapperParam(tarAxisInfo)
)
))
linkTriggers[tarAxisInfo.key] = val
}
})
}
})
each$7(linkTriggers, function (val, tarKey) {
processOnAxis(
axesInfo[tarKey],
val,
updaters,
true,
outputPayload
)
})
updateModelActually(showValueMap, axesInfo, outputPayload)
dispatchTooltipActually(
dataByCoordSys,
point,
payload,
dispatchAction
)
dispatchHighDownActually(axesInfo, dispatchAction, api)
return outputPayload
}
function processOnAxis(
axisInfo,
newValue,
updaters,
noSnap,
outputFinder
) {
var axis = axisInfo.axis
if (axis.scale.isBlank() || !axis.containData(newValue)) {
return
}
if (!axisInfo.involveSeries) {
updaters.showPointer(axisInfo, newValue)
return
} // Heavy calculation. So put it after axis.containData checking.
var payloadInfo = buildPayloadsBySeries(newValue, axisInfo)
var payloadBatch = payloadInfo.payloadBatch
var snapToValue = payloadInfo.snapToValue // Fill content of event obj for echarts.connect.
// By default use the first involved series data as a sample to connect.
if (payloadBatch[0] && outputFinder.seriesIndex == null) {
extend(outputFinder, payloadBatch[0])
} // If no linkSource input, this process is for collecting link
// target, where snap should not be accepted.
if (!noSnap && axisInfo.snap) {
if (axis.containData(snapToValue) && snapToValue != null) {
newValue = snapToValue
}
}
updaters.showPointer(axisInfo, newValue, payloadBatch) // Tooltip should always be snapToValue, otherwise there will be
// incorrect "axis value ~ series value" mapping displayed in tooltip.
updaters.showTooltip(axisInfo, payloadInfo, snapToValue)
}
function buildPayloadsBySeries(value, axisInfo) {
var axis = axisInfo.axis
var dim = axis.dim
var snapToValue = value
var payloadBatch = []
var minDist = Number.MAX_VALUE
var minDiff = -1
each$7(axisInfo.seriesModels, function (series, idx) {
var dataDim = series.getData().mapDimensionsAll(dim)
var seriesNestestValue
var dataIndices
if (series.getAxisTooltipData) {
var result = series.getAxisTooltipData(dataDim, value, axis)
dataIndices = result.dataIndices
seriesNestestValue = result.nestestValue
} else {
dataIndices = series.getData().indicesOfNearest(
dataDim[0],
value, // Add a threshold to avoid find the wrong dataIndex
// when data length is not same.
// false,
axis.type === 'category' ? 0.5 : null
)
if (!dataIndices.length) {
return
}
seriesNestestValue = series
.getData()
.get(dataDim[0], dataIndices[0])
}
if (seriesNestestValue == null || !isFinite(seriesNestestValue)) {
return
}
var diff = value - seriesNestestValue
var dist = Math.abs(diff) // Consider category case
if (dist <= minDist) {
if (dist < minDist || (diff >= 0 && minDiff < 0)) {
minDist = dist
minDiff = diff
snapToValue = seriesNestestValue
payloadBatch.length = 0
}
each$7(dataIndices, function (dataIndex) {
payloadBatch.push({
seriesIndex: series.seriesIndex,
dataIndexInside: dataIndex,
dataIndex: series.getData().getRawIndex(dataIndex)
})
})
}
})
return { payloadBatch: payloadBatch, snapToValue: snapToValue }
}
function showPointer(showValueMap, axisInfo, value, payloadBatch) {
showValueMap[axisInfo.key] = {
value: value,
payloadBatch: payloadBatch
}
}
function showTooltip(dataByCoordSys, axisInfo, payloadInfo, value) {
var payloadBatch = payloadInfo.payloadBatch
var axis = axisInfo.axis
var axisModel = axis.model
var axisPointerModel = axisInfo.axisPointerModel // If no data, do not create anything in dataByCoordSys,
// whose length will be used to judge whether dispatch action.
if (!axisInfo.triggerTooltip || !payloadBatch.length) {
return
}
var coordSysModel = axisInfo.coordSys.model
var coordSysKey = makeKey(coordSysModel)
var coordSysItem = dataByCoordSys.map[coordSysKey]
if (!coordSysItem) {
coordSysItem = dataByCoordSys.map[coordSysKey] = {
coordSysId: coordSysModel.id,
coordSysIndex: coordSysModel.componentIndex,
coordSysType: coordSysModel.type,
coordSysMainType: coordSysModel.mainType,
dataByAxis: []
}
dataByCoordSys.list.push(coordSysItem)
}
coordSysItem.dataByAxis.push({
axisDim: axis.dim,
axisIndex: axisModel.componentIndex,
axisType: axisModel.type,
axisId: axisModel.id,
value: value, // Caustion: viewHelper.getValueLabel is actually on "view stage", which
// depends that all models have been updated. So it should not be performed
// here. Considering axisPointerModel used here is volatile, which is hard
// to be retrieve in TooltipView, we prepare parameters here.
valueLabelOpt: {
precision: axisPointerModel.get(['label', 'precision']),
formatter: axisPointerModel.get(['label', 'formatter'])
},
seriesDataIndices: payloadBatch.slice()
})
}
function updateModelActually(showValueMap, axesInfo, outputPayload) {
var outputAxesInfo = (outputPayload.axesInfo = []) // Basic logic: If no 'show' required, 'hide' this axisPointer.
each$7(axesInfo, function (axisInfo, key) {
var option = axisInfo.axisPointerModel.option
var valItem = showValueMap[key]
if (valItem) {
!axisInfo.useHandle && (option.status = 'show')
option.value = valItem.value // For label formatter param and highlight.
option.seriesDataIndices = (valItem.payloadBatch || []).slice()
} // When always show (e.g., handle used), remain
// original value and status.
else {
// If hide, value still need to be set, consider
// click legend to toggle axis blank.
!axisInfo.useHandle && (option.status = 'hide')
} // If status is 'hide', should be no info in payload.
option.status === 'show' &&
outputAxesInfo.push({
axisDim: axisInfo.axis.dim,
axisIndex: axisInfo.axis.model.componentIndex,
value: option.value
})
})
}
function dispatchTooltipActually(
dataByCoordSys,
point,
payload,
dispatchAction
) {
// Basic logic: If no showTip required, hideTip will be dispatched.
if (illegalPoint(point) || !dataByCoordSys.list.length) {
dispatchAction({ type: 'hideTip' })
return
} // In most case only one axis (or event one series is used). It is
// convinient to fetch payload.seriesIndex and payload.dataIndex
// dirtectly. So put the first seriesIndex and dataIndex of the first
// axis on the payload.
var sampleItem =
((dataByCoordSys.list[0].dataByAxis[0] || {}).seriesDataIndices ||
[])[0] || {}
dispatchAction({
type: 'showTip',
escapeConnect: true,
x: point[0],
y: point[1],
tooltipOption: payload.tooltipOption,
position: payload.position,
dataIndexInside: sampleItem.dataIndexInside,
dataIndex: sampleItem.dataIndex,
seriesIndex: sampleItem.seriesIndex,
dataByCoordSys: dataByCoordSys.list
})
}
function dispatchHighDownActually(axesInfo, dispatchAction, api) {
// FIXME
// highlight status modification shoule be a stage of main process?
// (Consider confilct (e.g., legend and axisPointer) and setOption)
var zr = api.getZr()
var highDownKey = 'axisPointerLastHighlights'
var lastHighlights = inner$2(zr)[highDownKey] || {}
var newHighlights = (inner$2(zr)[highDownKey] = {}) // Update highlight/downplay status according to axisPointer model.
// Build hash map and remove duplicate incidentally.
each$7(axesInfo, function (axisInfo, key) {
var option = axisInfo.axisPointerModel.option
option.status === 'show' &&
each$7(option.seriesDataIndices, function (batchItem) {
var key = batchItem.seriesIndex + ' | ' + batchItem.dataIndex
newHighlights[key] = batchItem
})
}) // Diff.
var toHighlight = []
var toDownplay = []
each$7(lastHighlights, function (batchItem, key) {
!newHighlights[key] && toDownplay.push(batchItem)
})
each$7(newHighlights, function (batchItem, key) {
!lastHighlights[key] && toHighlight.push(batchItem)
})
toDownplay.length &&
api.dispatchAction({
type: 'downplay',
escapeConnect: true, // Not blur others when highlight in axisPointer.
notBlur: true,
batch: toDownplay
})
toHighlight.length &&
api.dispatchAction({
type: 'highlight',
escapeConnect: true, // Not blur others when highlight in axisPointer.
notBlur: true,
batch: toHighlight
})
}
function findInputAxisInfo(inputAxesInfo, axisInfo) {
for (var i = 0; i < (inputAxesInfo || []).length; i++) {
var inputAxisInfo = inputAxesInfo[i]
if (
axisInfo.axis.dim === inputAxisInfo.axisDim &&
axisInfo.axis.model.componentIndex === inputAxisInfo.axisIndex
) {
return inputAxisInfo
}
}
}
function makeMapperParam(axisInfo) {
var axisModel = axisInfo.axis.model
var item = {}
var dim = (item.axisDim = axisInfo.axis.dim)
item.axisIndex = item[dim + 'AxisIndex'] = axisModel.componentIndex
item.axisName = item[dim + 'AxisName'] = axisModel.name
item.axisId = item[dim + 'AxisId'] = axisModel.id
return item
}
function illegalPoint(point) {
return (
!point ||
point[0] == null ||
isNaN(point[0]) ||
point[1] == null ||
isNaN(point[1])
)
}
function install$e(registers) {
// CartesianAxisPointer is not supposed to be required here. But consider
// echarts.simple.js and online build tooltip, which only require gridSimple,
// CartesianAxisPointer should be able to required somewhere.
AxisView$1.registerAxisPointerClass(
'CartesianAxisPointer',
CartesianAxisPointer$1
)
registers.registerComponentModel(AxisPointerModel$1)
registers.registerComponentView(AxisPointerView$1)
registers.registerPreprocessor(function (option) {
// Always has a global axisPointerModel for default setting.
if (option) {
;(!option.axisPointer || option.axisPointer.length === 0) &&
(option.axisPointer = {})
var link = option.axisPointer.link // Normalize to array to avoid object mergin. But if link
// is not set, remain null/undefined, otherwise it will
// override existent link setting.
if (link && !isArray(link)) {
option.axisPointer.link = [link]
}
}
}) // This process should proformed after coordinate systems created
// and series data processed. So put it on statistic processing stage.
registers.registerProcessor(
registers.PRIORITY.PROCESSOR.STATISTIC,
function (ecModel, api) {
// Build axisPointerModel, mergin tooltip.axisPointer model for each axis.
// allAxesInfo should be updated when setOption performed.
ecModel.getComponent('axisPointer').coordSysAxesInfo = collect(
ecModel,
api
)
}
) // Broadcast to all views.
registers.registerAction(
{
type: 'updateAxisPointer',
event: 'updateAxisPointer',
update: ':updateAxisPointer'
},
axisTrigger
)
}
function install$d(registers) {
use(install$f)
use(install$e)
}
var DATA_ZOOM_AXIS_DIMENSIONS = [
'x',
'y',
'radius',
'angle',
'single'
] // Supported coords.
// FIXME: polar has been broken (but rarely used).
var SERIES_COORDS = ['cartesian2d', 'polar', 'singleAxis']
function isCoordSupported(seriesModel) {
var coordType = seriesModel.get('coordinateSystem')
return indexOf(SERIES_COORDS, coordType) >= 0
}
function getAxisMainType(axisDim) {
return axisDim + 'Axis'
}
/**
* If two dataZoomModels has the same axis controlled, we say that they are 'linked'.
* This function finds all linked dataZoomModels start from the given payload.
*/ function findEffectedDataZooms(ecModel, payload) {
// Key: `DataZoomAxisDimension`
var axisRecords = createHashMap()
var effectedModels = [] // Key: uid of dataZoomModel
var effectedModelMap = createHashMap() // Find the dataZooms specified by payload.
ecModel.eachComponent(
{ mainType: 'dataZoom', query: payload },
function (dataZoomModel) {
if (!effectedModelMap.get(dataZoomModel.uid)) {
addToEffected(dataZoomModel)
}
}
) // Start from the given dataZoomModels, travel the graph to find
// all of the linked dataZoom models.
var foundNewLink
do {
foundNewLink = false
ecModel.eachComponent('dataZoom', processSingle)
} while (foundNewLink)
function processSingle(dataZoomModel) {
if (
!effectedModelMap.get(dataZoomModel.uid) &&
isLinked(dataZoomModel)
) {
addToEffected(dataZoomModel)
foundNewLink = true
}
}
function addToEffected(dataZoom) {
effectedModelMap.set(dataZoom.uid, true)
effectedModels.push(dataZoom)
markAxisControlled(dataZoom)
}
function isLinked(dataZoomModel) {
var isLink = false
dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) {
var axisIdxArr = axisRecords.get(axisDim)
if (axisIdxArr && axisIdxArr[axisIndex]) {
isLink = true
}
})
return isLink
}
function markAxisControlled(dataZoomModel) {
dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) {
;(axisRecords.get(axisDim) || axisRecords.set(axisDim, []))[
axisIndex
] = true
})
}
return effectedModels
}
/**
* Find the first target coordinate system.
* Available after model built.
*
* @return Like {
* grid: [
* {model: coord0, axisModels: [axis1, axis3], coordIndex: 1},
* {model: coord1, axisModels: [axis0, axis2], coordIndex: 0},
* ...
* ], // cartesians must not be null/undefined.
* polar: [
* {model: coord0, axisModels: [axis4], coordIndex: 0},
* ...
* ], // polars must not be null/undefined.
* singleAxis: [
* {model: coord0, axisModels: [], coordIndex: 0}
* ]
* }
*/ function collectReferCoordSysModelInfo(dataZoomModel) {
var ecModel = dataZoomModel.ecModel
var coordSysInfoWrap = { infoList: [], infoMap: createHashMap() }
dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) {
var axisModel = ecModel.getComponent(
getAxisMainType(axisDim),
axisIndex
)
if (!axisModel) {
return
}
var coordSysModel = axisModel.getCoordSysModel()
if (!coordSysModel) {
return
}
var coordSysUid = coordSysModel.uid
var coordSysInfo = coordSysInfoWrap.infoMap.get(coordSysUid)
if (!coordSysInfo) {
coordSysInfo = { model: coordSysModel, axisModels: [] }
coordSysInfoWrap.infoList.push(coordSysInfo)
coordSysInfoWrap.infoMap.set(coordSysUid, coordSysInfo)
}
coordSysInfo.axisModels.push(axisModel)
})
return coordSysInfoWrap
}
var DataZoomAxisInfo = /** @class */ (function () {
function DataZoomAxisInfo() {
this.indexList = []
this.indexMap = []
}
DataZoomAxisInfo.prototype.add = function (axisCmptIdx) {
// Remove duplication.
if (!this.indexMap[axisCmptIdx]) {
this.indexList.push(axisCmptIdx)
this.indexMap[axisCmptIdx] = true
}
}
return DataZoomAxisInfo
})()
var DataZoomModel = /** @class */ (function (_super) {
__extends(DataZoomModel, _super)
function DataZoomModel() {
var _this =
(_super !== null && _super.apply(this, arguments)) || this
_this.type = DataZoomModel.type
_this._autoThrottle = true
_this._noTarget = true
/**
* It is `[rangeModeForMin, rangeModeForMax]`.
* The optional values for `rangeMode`:
* + `'value'` mode: the axis extent will always be determined by
* `dataZoom.startValue` and `dataZoom.endValue`, despite
* how data like and how `axis.min` and `axis.max` are.
* + `'percent'` mode: `100` represents 100% of the `[dMin, dMax]`,
* where `dMin` is `axis.min` if `axis.min` specified, otherwise `data.extent[0]`,
* and `dMax` is `axis.max` if `axis.max` specified, otherwise `data.extent[1]`.
* Axis extent will be determined by the result of the percent of `[dMin, dMax]`.
*
* For example, when users are using dynamic data (update data periodically via `setOption`),
* if in `'value`' mode, the window will be kept in a fixed value range despite how
* data are appended, while if in `'percent'` mode, whe window range will be changed alone with
* the appended data (suppose `axis.min` and `axis.max` are not specified).
*/ _this._rangePropMode = ['percent', 'percent']
return _this
}
DataZoomModel.prototype.init = function (
option,
parentModel,
ecModel
) {
var inputRawOption = retrieveRawOption(option)
/**
* Suppose a "main process" start at the point that model prepared (that is,
* model initialized or merged or method called in `action`).
* We should keep the `main process` idempotent, that is, given a set of values
* on `option`, we get the same result.
*
* But sometimes, values on `option` will be updated for providing users
* a "final calculated value" (`dataZoomProcessor` will do that). Those value
* should not be the base/input of the `main process`.
*
* So in that case we should save and keep the input of the `main process`
* separately, called `settledOption`.
*
* For example, consider the case:
* (Step_1) brush zoom the grid by `toolbox.dataZoom`,
* where the original input `option.startValue`, `option.endValue` are earsed by
* calculated value.
* (Step)2) click the legend to hide and show a series,
* where the new range is calculated by the earsed `startValue` and `endValue`,
* which brings incorrect result.
*/ this.settledOption = inputRawOption
this.mergeDefaultAndTheme(option, ecModel)
this._doInit(inputRawOption)
}
DataZoomModel.prototype.mergeOption = function (newOption) {
var inputRawOption = retrieveRawOption(newOption) //FIX #2591
merge(this.option, newOption, true)
merge(this.settledOption, inputRawOption, true)
this._doInit(inputRawOption)
}
DataZoomModel.prototype._doInit = function (inputRawOption) {
var thisOption = this.option
this._setDefaultThrottle(inputRawOption)
this._updateRangeUse(inputRawOption)
var settledOption = this.settledOption
each$7(
[
['start', 'startValue'],
['end', 'endValue']
],
function (names, index) {
// start/end has higher priority over startValue/endValue if they
// both set, but we should make chart.setOption({endValue: 1000})
// effective, rather than chart.setOption({endValue: 1000, end: null}).
if (this._rangePropMode[index] === 'value') {
thisOption[names[0]] = settledOption[names[0]] = null
} // Otherwise do nothing and use the merge result.
},
this
)
this._resetTarget()
}
DataZoomModel.prototype._resetTarget = function () {
var optionOrient = this.get('orient', true)
var targetAxisIndexMap = (this._targetAxisInfoMap =
createHashMap())
var hasAxisSpecified =
this._fillSpecifiedTargetAxis(targetAxisIndexMap)
if (hasAxisSpecified) {
this._orient =
optionOrient || this._makeAutoOrientByTargetAxis()
} else {
this._orient = optionOrient || 'horizontal'
this._fillAutoTargetAxisByOrient(
targetAxisIndexMap,
this._orient
)
}
this._noTarget = true
targetAxisIndexMap.each(function (axisInfo) {
if (axisInfo.indexList.length) {
this._noTarget = false
}
}, this)
}
DataZoomModel.prototype._fillSpecifiedTargetAxis = function (
targetAxisIndexMap
) {
var hasAxisSpecified = false
each$7(
DATA_ZOOM_AXIS_DIMENSIONS,
function (axisDim) {
var refering = this.getReferringComponents(
getAxisMainType(axisDim),
MULTIPLE_REFERRING
) // When user set axisIndex as a empty array, we think that user specify axisIndex
// but do not want use auto mode. Because empty array may be encountered when
// some error occured.
if (!refering.specified) {
return
}
hasAxisSpecified = true
var axisInfo = new DataZoomAxisInfo()
each$7(refering.models, function (axisModel) {
axisInfo.add(axisModel.componentIndex)
})
targetAxisIndexMap.set(axisDim, axisInfo)
},
this
)
return hasAxisSpecified
}
DataZoomModel.prototype._fillAutoTargetAxisByOrient = function (
targetAxisIndexMap,
orient
) {
var ecModel = this.ecModel
var needAuto = true // Find axis that parallel to dataZoom as default.
if (needAuto) {
var axisDim = orient === 'vertical' ? 'y' : 'x'
var axisModels = ecModel.findComponents({
mainType: axisDim + 'Axis'
})
setParallelAxis(axisModels, axisDim)
} // Find axis that parallel to dataZoom as default.
if (needAuto) {
var axisModels = ecModel.findComponents({
mainType: 'singleAxis',
filter: function filter(axisModel) {
return axisModel.get('orient', true) === orient
}
})
setParallelAxis(axisModels, 'single')
}
function setParallelAxis(axisModels, axisDim) {
// At least use the first parallel axis as the target axis.
var axisModel = axisModels[0]
if (!axisModel) {
return
}
var axisInfo = new DataZoomAxisInfo()
axisInfo.add(axisModel.componentIndex)
targetAxisIndexMap.set(axisDim, axisInfo)
needAuto = false // Find parallel axes in the same grid.
if (axisDim === 'x' || axisDim === 'y') {
var gridModel_1 = axisModel.getReferringComponents(
'grid',
SINGLE_REFERRING
).models[0]
gridModel_1 &&
each$7(axisModels, function (axModel) {
if (
axisModel.componentIndex !== axModel.componentIndex &&
gridModel_1 ===
axModel.getReferringComponents(
'grid',
SINGLE_REFERRING
).models[0]
) {
axisInfo.add(axModel.componentIndex)
}
})
}
}
if (needAuto) {
// If no parallel axis, find the first category axis as default. (Also consider polar).
each$7(
DATA_ZOOM_AXIS_DIMENSIONS,
function (axisDim) {
if (!needAuto) {
return
}
var axisModels = ecModel.findComponents({
mainType: getAxisMainType(axisDim),
filter: function filter(axisModel) {
return axisModel.get('type', true) === 'category'
}
})
if (axisModels[0]) {
var axisInfo = new DataZoomAxisInfo()
axisInfo.add(axisModels[0].componentIndex)
targetAxisIndexMap.set(axisDim, axisInfo)
needAuto = false
}
},
this
)
}
}
DataZoomModel.prototype._makeAutoOrientByTargetAxis = function () {
var dim // Find the first axis
this.eachTargetAxis(function (axisDim) {
!dim && (dim = axisDim)
}, this)
return dim === 'y' ? 'vertical' : 'horizontal'
}
DataZoomModel.prototype._setDefaultThrottle = function (
inputRawOption
) {
// When first time user set throttle, auto throttle ends.
if (inputRawOption.hasOwnProperty('throttle')) {
this._autoThrottle = false
}
if (this._autoThrottle) {
var globalOption = this.ecModel.option
this.option.throttle =
globalOption.animation &&
globalOption.animationDurationUpdate > 0
? 100
: 20
}
}
DataZoomModel.prototype._updateRangeUse = function (
inputRawOption
) {
var rangePropMode = this._rangePropMode
var rangeModeInOption = this.get('rangeMode')
each$7(
[
['start', 'startValue'],
['end', 'endValue']
],
function (names, index) {
var percentSpecified = inputRawOption[names[0]] != null
var valueSpecified = inputRawOption[names[1]] != null
if (percentSpecified && !valueSpecified) {
rangePropMode[index] = 'percent'
} else if (!percentSpecified && valueSpecified) {
rangePropMode[index] = 'value'
} else if (rangeModeInOption) {
rangePropMode[index] = rangeModeInOption[index]
} else if (percentSpecified) {
// percentSpecified && valueSpecified
rangePropMode[index] = 'percent'
} // else remain its original setting.
}
)
}
DataZoomModel.prototype.noTarget = function () {
return this._noTarget
}
DataZoomModel.prototype.getFirstTargetAxisModel = function () {
var firstAxisModel
this.eachTargetAxis(function (axisDim, axisIndex) {
if (firstAxisModel == null) {
firstAxisModel = this.ecModel.getComponent(
getAxisMainType(axisDim),
axisIndex
)
}
}, this)
return firstAxisModel
}
/**
* @param {Function} callback param: axisModel, dimNames, axisIndex, dataZoomModel, ecModel
*/ DataZoomModel.prototype.eachTargetAxis = function (
callback,
context
) {
this._targetAxisInfoMap.each(function (axisInfo, axisDim) {
each$7(axisInfo.indexList, function (axisIndex) {
callback.call(context, axisDim, axisIndex)
})
})
}
/**
* @return If not found, return null/undefined.
*/ DataZoomModel.prototype.getAxisProxy = function (
axisDim,
axisIndex
) {
var axisModel = this.getAxisModel(axisDim, axisIndex)
if (axisModel) {
return axisModel.__dzAxisProxy
}
}
/**
* @return If not found, return null/undefined.
*/ DataZoomModel.prototype.getAxisModel = function (
axisDim,
axisIndex
) {
var axisInfo = this._targetAxisInfoMap.get(axisDim)
if (axisInfo && axisInfo.indexMap[axisIndex]) {
return this.ecModel.getComponent(
getAxisMainType(axisDim),
axisIndex
)
}
}
/**
* If not specified, set to undefined.
*/ DataZoomModel.prototype.setRawRange = function (opt) {
var thisOption = this.option
var settledOption = this.settledOption
each$7(
[
['start', 'startValue'],
['end', 'endValue']
],
function (names) {
// Consider the pair :
// If one has value and the other one is `null/undefined`, we both set them
// to `settledOption`. This strategy enables the feature to clear the original
// value in `settledOption` to `null/undefined`.
// But if both of them are `null/undefined`, we do not set them to `settledOption`
// and keep `settledOption` with the original value. This strategy enables users to
// only set but not set when calling
// `dispatchAction`.
// The pair is treated in the same way.
if (opt[names[0]] != null || opt[names[1]] != null) {
thisOption[names[0]] = settledOption[names[0]] =
opt[names[0]]
thisOption[names[1]] = settledOption[names[1]] =
opt[names[1]]
}
},
this
)
this._updateRangeUse(opt)
}
DataZoomModel.prototype.setCalculatedRange = function (opt) {
var option = this.option
each$7(
['start', 'startValue', 'end', 'endValue'],
function (name) {
option[name] = opt[name]
}
)
}
DataZoomModel.prototype.getPercentRange = function () {
var axisProxy = this.findRepresentativeAxisProxy()
if (axisProxy) {
return axisProxy.getDataPercentWindow()
}
}
/**
* For example, chart.getModel().getComponent('dataZoom').getValueRange('y', 0);
*
* @return [startValue, endValue] value can only be '-' or finite number.
*/ DataZoomModel.prototype.getValueRange = function (
axisDim,
axisIndex
) {
if (axisDim == null && axisIndex == null) {
var axisProxy = this.findRepresentativeAxisProxy()
if (axisProxy) {
return axisProxy.getDataValueWindow()
}
} else {
return this.getAxisProxy(
axisDim,
axisIndex
).getDataValueWindow()
}
}
/**
* @param axisModel If axisModel given, find axisProxy
* corresponding to the axisModel
*/ DataZoomModel.prototype.findRepresentativeAxisProxy = function (
axisModel
) {
if (axisModel) {
return axisModel.__dzAxisProxy
} // Find the first hosted axisProxy
var firstProxy
var axisDimList = this._targetAxisInfoMap.keys()
for (var i = 0; i < axisDimList.length; i++) {
var axisDim = axisDimList[i]
var axisInfo = this._targetAxisInfoMap.get(axisDim)
for (var j = 0; j < axisInfo.indexList.length; j++) {
var proxy = this.getAxisProxy(axisDim, axisInfo.indexList[j])
if (proxy.hostedBy(this)) {
return proxy
}
if (!firstProxy) {
firstProxy = proxy
}
}
} // If no hosted proxy found, still need to return a proxy.
// This case always happens in toolbox dataZoom, where axes are all hosted by
// other dataZooms.
return firstProxy
}
DataZoomModel.prototype.getRangePropMode = function () {
return this._rangePropMode.slice()
}
DataZoomModel.prototype.getOrient = function () {
return this._orient
}
DataZoomModel.type = 'dataZoom'
DataZoomModel.dependencies = [
'xAxis',
'yAxis',
'radiusAxis',
'angleAxis',
'singleAxis',
'series',
'toolbox'
]
DataZoomModel.defaultOption = {
// zlevel: 0,
z: 4,
filterMode: 'filter',
start: 0,
end: 100
}
return DataZoomModel
})(ComponentModel$1)
/**
* Retrieve the those raw params from option, which will be cached separately.
* becasue they will be overwritten by normalized/calculated values in the main
* process.
*/ function retrieveRawOption(option) {
var ret = {}
each$7(
['start', 'end', 'startValue', 'endValue', 'throttle'],
function (name) {
option.hasOwnProperty(name) && (ret[name] = option[name])
}
)
return ret
}
var DataZoomModel$1 = DataZoomModel
var SelectDataZoomModel = /** @class */ (function (_super) {
__extends(SelectDataZoomModel, _super)
function SelectDataZoomModel() {
var _this =
(_super !== null && _super.apply(this, arguments)) || this
_this.type = SelectDataZoomModel.type
return _this
}
SelectDataZoomModel.type = 'dataZoom.select'
return SelectDataZoomModel
})(DataZoomModel$1)
var SelectZoomModel = SelectDataZoomModel
var DataZoomView = /** @class */ (function (_super) {
__extends(DataZoomView, _super)
function DataZoomView() {
var _this =
(_super !== null && _super.apply(this, arguments)) || this
_this.type = DataZoomView.type
return _this
}
DataZoomView.prototype.render = function (
dataZoomModel,
ecModel,
api,
payload
) {
this.dataZoomModel = dataZoomModel
this.ecModel = ecModel
this.api = api
}
DataZoomView.type = 'dataZoom'
return DataZoomView
})(ComponentView$1)
var DataZoomView$1 = DataZoomView
var SelectDataZoomView = /** @class */ (function (_super) {
__extends(SelectDataZoomView, _super)
function SelectDataZoomView() {
var _this =
(_super !== null && _super.apply(this, arguments)) || this
_this.type = SelectDataZoomView.type
return _this
}
SelectDataZoomView.type = 'dataZoom.select'
return SelectDataZoomView
})(DataZoomView$1)
var SelectZoomView = SelectDataZoomView
var each$3 = each$7
var asc = asc$1
/**
* Operate single axis.
* One axis can only operated by one axis operator.
* Different dataZoomModels may be defined to operate the same axis.
* (i.e. 'inside' data zoom and 'slider' data zoom components)
* So dataZoomModels share one axisProxy in that case.
*/ var AxisProxy = /** @class */ (function () {
function AxisProxy(dimName, axisIndex, dataZoomModel, ecModel) {
this._dimName = dimName
this._axisIndex = axisIndex
this.ecModel = ecModel
this._dataZoomModel = dataZoomModel // /**
// * @readOnly
// * @private
// */
// this.hasSeriesStacked;
}
/**
* Whether the axisProxy is hosted by dataZoomModel.
*/ AxisProxy.prototype.hostedBy = function (dataZoomModel) {
return this._dataZoomModel === dataZoomModel
}
/**
* @return Value can only be NaN or finite value.
*/ AxisProxy.prototype.getDataValueWindow = function () {
return this._valueWindow.slice()
}
/**
* @return {Array.}
*/ AxisProxy.prototype.getDataPercentWindow = function () {
return this._percentWindow.slice()
}
AxisProxy.prototype.getTargetSeriesModels = function () {
var seriesModels = []
this.ecModel.eachSeries(function (seriesModel) {
if (isCoordSupported(seriesModel)) {
var axisMainType = getAxisMainType(this._dimName)
var axisModel = seriesModel.getReferringComponents(
axisMainType,
SINGLE_REFERRING
).models[0]
if (
axisModel &&
this._axisIndex === axisModel.componentIndex
) {
seriesModels.push(seriesModel)
}
}
}, this)
return seriesModels
}
AxisProxy.prototype.getAxisModel = function () {
return this.ecModel.getComponent(
this._dimName + 'Axis',
this._axisIndex
)
}
AxisProxy.prototype.getMinMaxSpan = function () {
return clone$3(this._minMaxSpan)
}
/**
* Only calculate by given range and this._dataExtent, do not change anything.
*/ AxisProxy.prototype.calculateDataWindow = function (opt) {
var dataExtent = this._dataExtent
var axisModel = this.getAxisModel()
var scale = axisModel.axis.scale
var rangePropMode = this._dataZoomModel.getRangePropMode()
var percentExtent = [0, 100]
var percentWindow = []
var valueWindow = []
var hasPropModeValue
each$3(['start', 'end'], function (prop, idx) {
var boundPercent = opt[prop]
var boundValue = opt[prop + 'Value'] // Notice: dataZoom is based either on `percentProp` ('start', 'end') or
// on `valueProp` ('startValue', 'endValue'). (They are based on the data extent
// but not min/max of axis, which will be calculated by data window then).
// The former one is suitable for cases that a dataZoom component controls multiple
// axes with different unit or extent, and the latter one is suitable for accurate
// zoom by pixel (e.g., in dataZoomSelect).
// we use `getRangePropMode()` to mark which prop is used. `rangePropMode` is updated
// only when setOption or dispatchAction, otherwise it remains its original value.
// (Why not only record `percentProp` and always map to `valueProp`? Because
// the map `valueProp` -> `percentProp` -> `valueProp` probably not the original
// `valueProp`. consider two axes constrolled by one dataZoom. They have different
// data extent. All of values that are overflow the `dataExtent` will be calculated
// to percent '100%').
if (rangePropMode[idx] === 'percent') {
boundPercent == null && (boundPercent = percentExtent[idx]) // Use scale.parse to math round for category or time axis.
boundValue = scale.parse(
linearMap(boundPercent, percentExtent, dataExtent)
)
} else {
hasPropModeValue = true
boundValue =
boundValue == null
? dataExtent[idx]
: scale.parse(boundValue) // Calculating `percent` from `value` may be not accurate, because
// This calculation can not be inversed, because all of values that
// are overflow the `dataExtent` will be calculated to percent '100%'
boundPercent = linearMap(
boundValue,
dataExtent,
percentExtent
)
} // valueWindow[idx] = round(boundValue);
// percentWindow[idx] = round(boundPercent);
valueWindow[idx] = boundValue
percentWindow[idx] = boundPercent
})
asc(valueWindow)
asc(percentWindow) // The windows from user calling of `dispatchAction` might be out of the extent,
// or do not obey the `min/maxSpan`, `min/maxValueSpan`. But we dont restrict window
// by `zoomLock` here, because we see `zoomLock` just as a interaction constraint,
// where API is able to initialize/modify the window size even though `zoomLock`
// specified.
var spans = this._minMaxSpan
hasPropModeValue
? restrictSet(
valueWindow,
percentWindow,
dataExtent,
percentExtent,
false
)
: restrictSet(
percentWindow,
valueWindow,
percentExtent,
dataExtent,
true
)
function restrictSet(
fromWindow,
toWindow,
fromExtent,
toExtent,
toValue
) {
var suffix = toValue ? 'Span' : 'ValueSpan'
sliderMove(
0,
fromWindow,
fromExtent,
'all',
spans['min' + suffix],
spans['max' + suffix]
)
for (var i = 0; i < 2; i++) {
toWindow[i] = linearMap(
fromWindow[i],
fromExtent,
toExtent,
true
)
toValue && (toWindow[i] = scale.parse(toWindow[i]))
}
}
return { valueWindow: valueWindow, percentWindow: percentWindow }
}
/**
* Notice: reset should not be called before series.restoreData() called,
* so it is recommanded to be called in "process stage" but not "model init
* stage".
*/ AxisProxy.prototype.reset = function (dataZoomModel) {
if (dataZoomModel !== this._dataZoomModel) {
return
}
var targetSeries = this.getTargetSeriesModels() // Culculate data window and data extent, and record them.
this._dataExtent = calculateDataExtent(
this,
this._dimName,
targetSeries
) // `calculateDataWindow` uses min/maxSpan.
this._updateMinMaxSpan()
var dataWindow = this.calculateDataWindow(
dataZoomModel.settledOption
)
this._valueWindow = dataWindow.valueWindow
this._percentWindow = dataWindow.percentWindow // Update axis setting then.
this._setAxisModel()
}
AxisProxy.prototype.filterData = function (dataZoomModel, api) {
if (dataZoomModel !== this._dataZoomModel) {
return
}
var axisDim = this._dimName
var seriesModels = this.getTargetSeriesModels()
var filterMode = dataZoomModel.get('filterMode')
var valueWindow = this._valueWindow
if (filterMode === 'none') {
return
} // FIXME
// Toolbox may has dataZoom injected. And if there are stacked bar chart
// with NaN data, NaN will be filtered and stack will be wrong.
// So we need to force the mode to be set empty.
// In fect, it is not a big deal that do not support filterMode-'filter'
// when using toolbox#dataZoom, utill tooltip#dataZoom support "single axis
// selection" some day, which might need "adapt to data extent on the
// otherAxis", which is disabled by filterMode-'empty'.
// But currently, stack has been fixed to based on value but not index,
// so this is not an issue any more.
// let otherAxisModel = this.getOtherAxisModel();
// if (dataZoomModel.get('$fromToolbox')
// && otherAxisModel
// && otherAxisModel.hasSeriesStacked
// ) {
// filterMode = 'empty';
// }
// TODO
// filterMode 'weakFilter' and 'empty' is not optimized for huge data yet.
each$3(seriesModels, function (seriesModel) {
var seriesData = seriesModel.getData()
var dataDims = seriesData.mapDimensionsAll(axisDim)
if (!dataDims.length) {
return
}
if (filterMode === 'weakFilter') {
var store_1 = seriesData.getStore()
var dataDimIndices_1 = map$1(
dataDims,
function (dim) {
return seriesData.getDimensionIndex(dim)
},
seriesData
)
seriesData.filterSelf(function (dataIndex) {
var leftOut
var rightOut
var hasValue
for (var i = 0; i < dataDims.length; i++) {
var value = store_1.get(dataDimIndices_1[i], dataIndex)
var thisHasValue = !isNaN(value)
var thisLeftOut = value < valueWindow[0]
var thisRightOut = value > valueWindow[1]
if (thisHasValue && !thisLeftOut && !thisRightOut) {
return true
}
thisHasValue && (hasValue = true)
thisLeftOut && (leftOut = true)
thisRightOut && (rightOut = true)
} // If both left out and right out, do not filter.
return hasValue && leftOut && rightOut
})
} else {
each$3(dataDims, function (dim) {
if (filterMode === 'empty') {
seriesModel.setData(
(seriesData = seriesData.map(dim, function (value) {
return !isInWindow(value) ? NaN : value
}))
)
} else {
var range = {}
range[dim] = valueWindow // console.time('select');
seriesData.selectRange(range) // console.timeEnd('select');
}
})
}
each$3(dataDims, function (dim) {
seriesData.setApproximateExtent(valueWindow, dim)
})
})
function isInWindow(value) {
return value >= valueWindow[0] && value <= valueWindow[1]
}
}
AxisProxy.prototype._updateMinMaxSpan = function () {
var minMaxSpan = (this._minMaxSpan = {})
var dataZoomModel = this._dataZoomModel
var dataExtent = this._dataExtent
each$3(
['min', 'max'],
function (minMax) {
var percentSpan = dataZoomModel.get(minMax + 'Span')
var valueSpan = dataZoomModel.get(minMax + 'ValueSpan')
valueSpan != null &&
(valueSpan =
this.getAxisModel().axis.scale.parse(valueSpan)) // minValueSpan and maxValueSpan has higher priority than minSpan and maxSpan
if (valueSpan != null) {
percentSpan = linearMap(
dataExtent[0] + valueSpan,
dataExtent,
[0, 100],
true
)
} else if (percentSpan != null) {
valueSpan =
linearMap(percentSpan, [0, 100], dataExtent, true) -
dataExtent[0]
}
minMaxSpan[minMax + 'Span'] = percentSpan
minMaxSpan[minMax + 'ValueSpan'] = valueSpan
},
this
)
}
AxisProxy.prototype._setAxisModel = function () {
var axisModel = this.getAxisModel()
var percentWindow = this._percentWindow
var valueWindow = this._valueWindow
if (!percentWindow) {
return
} // [0, 500]: arbitrary value, guess axis extent.
var precision = getPixelPrecision(valueWindow, [0, 500])
precision = Math.min(precision, 20) // For value axis, if min/max/scale are not set, we just use the extent obtained
// by series data, which may be a little different from the extent calculated by
// `axisHelper.getScaleExtent`. But the different just affects the experience a
// little when zooming. So it will not be fixed until some users require it strongly.
var rawExtentInfo = axisModel.axis.scale.rawExtentInfo
if (percentWindow[0] !== 0) {
rawExtentInfo.setDeterminedMinMax(
'min',
+valueWindow[0].toFixed(precision)
)
}
if (percentWindow[1] !== 100) {
rawExtentInfo.setDeterminedMinMax(
'max',
+valueWindow[1].toFixed(precision)
)
}
rawExtentInfo.freeze()
}
return AxisProxy
})()
function calculateDataExtent(axisProxy, axisDim, seriesModels) {
var dataExtent = [Infinity, -Infinity]
each$3(seriesModels, function (seriesModel) {
unionAxisExtentFromData(
dataExtent,
seriesModel.getData(),
axisDim
)
}) // It is important to get "consistent" extent when more then one axes is
// controlled by a `dataZoom`, otherwise those axes will not be synchronized
// when zooming. But it is difficult to know what is "consistent", considering
// axes have different type or even different meanings (For example, two
// time axes are used to compare data of the same date in different years).
// So basically dataZoom just obtains extent by series.data (in category axis
// extent can be obtained from axis.data).
// Nevertheless, user can set min/max/scale on axes to make extent of axes
// consistent.
var axisModel = axisProxy.getAxisModel()
var rawExtentResult = ensureScaleRawExtentInfo(
axisModel.axis.scale,
axisModel,
dataExtent
).calculate()
return [rawExtentResult.min, rawExtentResult.max]
}
var AxisProxy$1 = AxisProxy
var dataZoomProcessor = {
// `dataZoomProcessor` will only be performed in needed series. Consider if
// there is a line series and a pie series, it is better not to update the
// line series if only pie series is needed to be updated.
getTargetSeries: function getTargetSeries(ecModel) {
function eachAxisModel(cb) {
ecModel.eachComponent('dataZoom', function (dataZoomModel) {
dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) {
var axisModel = ecModel.getComponent(
getAxisMainType(axisDim),
axisIndex
)
cb(axisDim, axisIndex, axisModel, dataZoomModel)
})
})
} // FIXME: it brings side-effect to `getTargetSeries`.
// Prepare axis proxies.
eachAxisModel(function (
axisDim,
axisIndex,
axisModel,
dataZoomModel
) {
// dispose all last axis proxy, in case that some axis are deleted.
axisModel.__dzAxisProxy = null
})
var proxyList = []
eachAxisModel(function (
axisDim,
axisIndex,
axisModel,
dataZoomModel
) {
// Different dataZooms may constrol the same axis. In that case,
// an axisProxy serves both of them.
if (!axisModel.__dzAxisProxy) {
// Use the first dataZoomModel as the main model of axisProxy.
axisModel.__dzAxisProxy = new AxisProxy$1(
axisDim,
axisIndex,
dataZoomModel,
ecModel
)
proxyList.push(axisModel.__dzAxisProxy)
}
})
var seriesModelMap = createHashMap()
each$7(proxyList, function (axisProxy) {
each$7(
axisProxy.getTargetSeriesModels(),
function (seriesModel) {
seriesModelMap.set(seriesModel.uid, seriesModel)
}
)
})
return seriesModelMap
}, // Consider appendData, where filter should be performed. Because data process is
// in block mode currently, it is not need to worry about that the overallProgress
// execute every frame.
overallReset: function overallReset(ecModel, api) {
ecModel.eachComponent('dataZoom', function (dataZoomModel) {
// We calculate window and reset axis here but not in model
// init stage and not after action dispatch handler, because
// reset should be called after seriesData.restoreData.
dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) {
dataZoomModel
.getAxisProxy(axisDim, axisIndex)
.reset(dataZoomModel)
}) // Caution: data zoom filtering is order sensitive when using
// percent range and no min/max/scale set on axis.
// For example, we have dataZoom definition:
// [
// {xAxisIndex: 0, start: 30, end: 70},
// {yAxisIndex: 0, start: 20, end: 80}
// ]
// In this case, [20, 80] of y-dataZoom should be based on data
// that have filtered by x-dataZoom using range of [30, 70],
// but should not be based on full raw data. Thus sliding
// x-dataZoom will change both ranges of xAxis and yAxis,
// while sliding y-dataZoom will only change the range of yAxis.
// So we should filter x-axis after reset x-axis immediately,
// and then reset y-axis and filter y-axis.
dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) {
dataZoomModel
.getAxisProxy(axisDim, axisIndex)
.filterData(dataZoomModel, api)
})
})
ecModel.eachComponent('dataZoom', function (dataZoomModel) {
// Fullfill all of the range props so that user
// is able to get them from chart.getOption().
var axisProxy = dataZoomModel.findRepresentativeAxisProxy()
if (axisProxy) {
var percentRange = axisProxy.getDataPercentWindow()
var valueRange = axisProxy.getDataValueWindow()
dataZoomModel.setCalculatedRange({
start: percentRange[0],
end: percentRange[1],
startValue: valueRange[0],
endValue: valueRange[1]
})
}
})
}
}
var dataZoomProcessor$1 = dataZoomProcessor
function installDataZoomAction(registers) {
registers.registerAction('dataZoom', function (payload, ecModel) {
var effectedModels = findEffectedDataZooms(ecModel, payload)
each$7(effectedModels, function (dataZoomModel) {
dataZoomModel.setRawRange({
start: payload.start,
end: payload.end,
startValue: payload.startValue,
endValue: payload.endValue
})
})
})
}
var installed = false
function installCommon(registers) {
if (installed) {
return
}
installed = true
registers.registerProcessor(
registers.PRIORITY.PROCESSOR.FILTER,
dataZoomProcessor$1
)
installDataZoomAction(registers)
registers.registerSubTypeDefaulter('dataZoom', function () {
// Default 'slider' when no type specified.
return 'slider'
})
}
function install$c(registers) {
registers.registerComponentModel(SelectZoomModel)
registers.registerComponentView(SelectZoomView)
installCommon(registers)
}
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/ /**
* AUTO-GENERATED FILE. DO NOT MODIFY.
*/ var ToolboxFeature = /** @class */ (function () {
function ToolboxFeature() {}
return ToolboxFeature
})()
var features = {}
function registerFeature(name, ctor) {
features[name] = ctor
}
function getFeature(name) {
return features[name]
}
var ToolboxModel = /** @class */ (function (_super) {
__extends(ToolboxModel, _super)
function ToolboxModel() {
var _this =
(_super !== null && _super.apply(this, arguments)) || this
_this.type = ToolboxModel.type
return _this
}
ToolboxModel.prototype.optionUpdated = function () {
_super.prototype.optionUpdated.apply(this, arguments)
var ecModel = this.ecModel
each$7(this.option.feature, function (featureOpt, featureName) {
var Feature = getFeature(featureName)
if (Feature) {
if (Feature.getDefaultOption) {
Feature.defaultOption = Feature.getDefaultOption(ecModel)
}
merge(featureOpt, Feature.defaultOption)
}
})
}
ToolboxModel.type = 'toolbox'
ToolboxModel.layoutMode = { type: 'box', ignoreSize: true }
ToolboxModel.defaultOption = {
show: true,
z: 6, // zlevel: 0,
orient: 'horizontal',
left: 'right',
top: 'top', // right
// bottom
backgroundColor: 'transparent',
borderColor: '#ccc',
borderRadius: 0,
borderWidth: 0,
padding: 5,
itemSize: 15,
itemGap: 8,
showTitle: true,
iconStyle: { borderColor: '#666', color: 'none' },
emphasis: { iconStyle: { borderColor: '#3E98C5' } }, // textStyle: {},
// feature
tooltip: { show: false, position: 'bottom' }
}
return ToolboxModel
})(ComponentModel$1)
var ToolboxModel$1 = ToolboxModel
/**
* Layout list like component.
* It will box layout each items in group of component and then position the whole group in the viewport
* @param {module:zrender/group/Group} group
* @param {module:echarts/model/Component} componentModel
* @param {module:echarts/ExtensionAPI}
*/ function layout(group, componentModel, api) {
var boxLayoutParams = componentModel.getBoxLayoutParams()
var padding = componentModel.get('padding')
var viewportSize = {
width: api.getWidth(),
height: api.getHeight()
}
var rect = getLayoutRect(boxLayoutParams, viewportSize, padding)
box(
componentModel.get('orient'),
group,
componentModel.get('itemGap'),
rect.width,
rect.height
)
positionElement(group, boxLayoutParams, viewportSize, padding)
}
function makeBackground(rect, componentModel) {
var padding = normalizeCssArray(componentModel.get('padding'))
var style = componentModel.getItemStyle(['color', 'opacity'])
style.fill = componentModel.get('backgroundColor')
rect = new Rect$2({
shape: {
x: rect.x - padding[3],
y: rect.y - padding[0],
width: rect.width + padding[1] + padding[3],
height: rect.height + padding[0] + padding[2],
r: componentModel.get('borderRadius')
},
style: style,
silent: true,
z2: -1
}) // FIXME
// `subPixelOptimizeRect` may bring some gap between edge of viewpart
// and background rect when setting like `left: 0`, `top: 0`.
// graphic.subPixelOptimizeRect(rect);
return rect
}
var ToolboxView = /** @class */ (function (_super) {
__extends(ToolboxView, _super)
function ToolboxView() {
return (_super !== null && _super.apply(this, arguments)) || this
}
ToolboxView.prototype.render = function (
toolboxModel,
ecModel,
api,
payload
) {
var group = this.group
group.removeAll()
if (!toolboxModel.get('show')) {
return
}
var itemSize = +toolboxModel.get('itemSize')
var isVertical = toolboxModel.get('orient') === 'vertical'
var featureOpts = toolboxModel.get('feature') || {}
var features = this._features || (this._features = {})
var featureNames = []
each$7(featureOpts, function (opt, name) {
featureNames.push(name)
})
new DataDiffer$1(this._featureNames || [], featureNames)
.add(processFeature)
.update(processFeature)
.remove(curry$1(processFeature, null))
.execute() // Keep for diff.
this._featureNames = featureNames
function processFeature(newIndex, oldIndex) {
var featureName = featureNames[newIndex]
var oldName = featureNames[oldIndex]
var featureOpt = featureOpts[featureName]
var featureModel = new Model$1(
featureOpt,
toolboxModel,
toolboxModel.ecModel
)
var feature // FIX#11236, merge feature title from MagicType newOption. TODO: consider seriesIndex ?
if (
payload &&
payload.newTitle != null &&
payload.featureName === featureName
) {
featureOpt.title = payload.newTitle
}
if (featureName && !oldName) {
// Create
if (isUserFeatureName(featureName)) {
feature = {
onclick: featureModel.option.onclick,
featureName: featureName
}
} else {
var Feature = getFeature(featureName)
if (!Feature) {
return
}
feature = new Feature()
}
features[featureName] = feature
} else {
feature = features[oldName] // If feature does not exsit.
if (!feature) {
return
}
}
feature.uid = getUID('toolbox-feature')
feature.model = featureModel
feature.ecModel = ecModel
feature.api = api
var isToolboxFeature = feature instanceof ToolboxFeature
if (!featureName && oldName) {
isToolboxFeature &&
feature.dispose &&
feature.dispose(ecModel, api)
return
}
if (
!featureModel.get('show') ||
(isToolboxFeature && feature.unusable)
) {
isToolboxFeature &&
feature.remove &&
feature.remove(ecModel, api)
return
}
createIconPaths(featureModel, feature, featureName)
featureModel.setIconStatus = function (iconName, status) {
var option = this.option
var iconPaths = this.iconPaths
option.iconStatus = option.iconStatus || {}
option.iconStatus[iconName] = status
if (iconPaths[iconName]) {
;(status === 'emphasis' ? enterEmphasis : leaveEmphasis)(
iconPaths[iconName]
)
}
}
if (feature instanceof ToolboxFeature) {
if (feature.render) {
feature.render(featureModel, ecModel, api, payload)
}
}
}
function createIconPaths(featureModel, feature, featureName) {
var iconStyleModel = featureModel.getModel('iconStyle')
var iconStyleEmphasisModel = featureModel.getModel([
'emphasis',
'iconStyle'
]) // If one feature has mutiple icon. they are orginaized as
// {
// icon: {
// foo: '',
// bar: ''
// },
// title: {
// foo: '',
// bar: ''
// }
// }
var icons =
feature instanceof ToolboxFeature && feature.getIcons
? feature.getIcons()
: featureModel.get('icon')
var titles = featureModel.get('title') || {}
var iconsMap
var titlesMap
if (isString(icons)) {
iconsMap = {}
iconsMap[featureName] = icons
} else {
iconsMap = icons
}
if (isString(titles)) {
titlesMap = {}
titlesMap[featureName] = titles
} else {
titlesMap = titles
}
var iconPaths = (featureModel.iconPaths = {})
each$7(iconsMap, function (iconStr, iconName) {
var path = createIcon(
iconStr,
{},
{
x: -itemSize / 2,
y: -itemSize / 2,
width: itemSize,
height: itemSize
}
) // TODO handling image
path.setStyle(iconStyleModel.getItemStyle())
var pathEmphasisState = path.ensureState('emphasis')
pathEmphasisState.style =
iconStyleEmphasisModel.getItemStyle() // Text position calculation
var textContent = new ZRText$1({
style: {
text: titlesMap[iconName],
align: iconStyleEmphasisModel.get('textAlign'),
borderRadius:
iconStyleEmphasisModel.get('textBorderRadius'),
padding: iconStyleEmphasisModel.get('textPadding'),
fill: null
},
ignore: true
})
path.setTextContent(textContent)
setTooltipConfig({
el: path,
componentModel: toolboxModel,
itemName: iconName,
formatterParamsExtra: { title: titlesMap[iconName] }
})
path.__title = titlesMap[iconName]
path
.on('mouseover', function () {
// Should not reuse above hoverStyle, which might be modified.
var hoverStyle = iconStyleEmphasisModel.getItemStyle()
var defaultTextPosition = isVertical
? toolboxModel.get('right') == null &&
toolboxModel.get('left') !== 'right'
? 'right'
: 'left'
: toolboxModel.get('bottom') == null &&
toolboxModel.get('top') !== 'bottom'
? 'bottom'
: 'top'
textContent.setStyle({
fill:
iconStyleEmphasisModel.get('textFill') ||
hoverStyle.fill ||
hoverStyle.stroke ||
'#000',
backgroundColor: iconStyleEmphasisModel.get(
'textBackgroundColor'
)
})
path.setTextConfig({
position:
iconStyleEmphasisModel.get('textPosition') ||
defaultTextPosition
})
textContent.ignore = !toolboxModel.get('showTitle') // Use enterEmphasis and leaveEmphasis provide by ec.
// There are flags managed by the echarts.
api.enterEmphasis(this)
})
.on('mouseout', function () {
if (
featureModel.get(['iconStatus', iconName]) !==
'emphasis'
) {
api.leaveEmphasis(this)
}
textContent.hide()
})
;(featureModel.get(['iconStatus', iconName]) === 'emphasis'
? enterEmphasis
: leaveEmphasis)(path)
group.add(path)
path.on(
'click',
bind$1(feature.onclick, feature, ecModel, api, iconName)
)
iconPaths[iconName] = path
})
}
layout(group, toolboxModel, api) // Render background after group is layout
// FIXME
group.add(makeBackground(group.getBoundingRect(), toolboxModel)) // Adjust icon title positions to avoid them out of screen
isVertical ||
group.eachChild(function (icon) {
var titleText = icon.__title // const hoverStyle = icon.hoverStyle;
// TODO simplify code?
var emphasisState = icon.ensureState('emphasis')
var emphasisTextConfig =
emphasisState.textConfig || (emphasisState.textConfig = {})
var textContent = icon.getTextContent()
var emphasisTextState =
textContent && textContent.ensureState('emphasis') // May be background element
if (
emphasisTextState &&
!isFunction(emphasisTextState) &&
titleText
) {
var emphasisTextStyle =
emphasisTextState.style || (emphasisTextState.style = {})
var rect = getBoundingRect(
titleText,
ZRText$1.makeFont(emphasisTextStyle)
)
var offsetX = icon.x + group.x
var offsetY = icon.y + group.y + itemSize
var needPutOnTop = false
if (offsetY + rect.height > api.getHeight()) {
emphasisTextConfig.position = 'top'
needPutOnTop = true
}
var topOffset = needPutOnTop
? -5 - rect.height
: itemSize + 10
if (offsetX + rect.width / 2 > api.getWidth()) {
emphasisTextConfig.position = ['100%', topOffset]
emphasisTextStyle.align = 'right'
} else if (offsetX - rect.width / 2 < 0) {
emphasisTextConfig.position = [0, topOffset]
emphasisTextStyle.align = 'left'
}
}
})
}
ToolboxView.prototype.updateView = function (
toolboxModel,
ecModel,
api,
payload
) {
each$7(this._features, function (feature) {
feature instanceof ToolboxFeature &&
feature.updateView &&
feature.updateView(feature.model, ecModel, api, payload)
})
} // updateLayout(toolboxModel, ecModel, api, payload) {
// zrUtil.each(this._features, function (feature) {
// feature.updateLayout && feature.updateLayout(feature.model, ecModel, api, payload);
// });
// },
ToolboxView.prototype.remove = function (ecModel, api) {
each$7(this._features, function (feature) {
feature instanceof ToolboxFeature &&
feature.remove &&
feature.remove(ecModel, api)
})
this.group.removeAll()
}
ToolboxView.prototype.dispose = function (ecModel, api) {
each$7(this._features, function (feature) {
feature instanceof ToolboxFeature &&
feature.dispose &&
feature.dispose(ecModel, api)
})
}
ToolboxView.type = 'toolbox'
return ToolboxView
})(ComponentView$1)
function isUserFeatureName(featureName) {
return featureName.indexOf('my') === 0
}
var ToolboxView$1 = ToolboxView
/* global window, document */ var SaveAsImage =
/** @class */ (function (_super) {
__extends(SaveAsImage, _super)
function SaveAsImage() {
return (
(_super !== null && _super.apply(this, arguments)) || this
)
}
SaveAsImage.prototype.onclick = function (ecModel, api) {
var model = this.model
var title =
model.get('name') || ecModel.get('title.0.text') || 'echarts'
var isSvg = api.getZr().painter.getType() === 'svg'
var type = isSvg ? 'svg' : model.get('type', true) || 'png'
var url = api.getConnectedDataURL({
type: type,
backgroundColor:
model.get('backgroundColor', true) ||
ecModel.get('backgroundColor') ||
'#fff',
connectedBackgroundColor: model.get(
'connectedBackgroundColor'
),
excludeComponents: model.get('excludeComponents'),
pixelRatio: model.get('pixelRatio')
})
var browser = env$1.browser // Chrome, Firefox, New Edge
if (
isFunction(MouseEvent) &&
(browser.newEdge || (!browser.ie && !browser.edge))
) {
var $a = document.createElement('a')
$a.download = title + '.' + type
$a.target = '_blank'
$a.href = url
var evt = new MouseEvent('click', {
// some micro front-end framework, window maybe is a Proxy
view: document.defaultView,
bubbles: true,
cancelable: false
})
$a.dispatchEvent(evt)
} // IE or old Edge
else {
// @ts-ignore
if (window.navigator.msSaveOrOpenBlob || isSvg) {
var parts = url.split(',') // data:[][;charset=][;base64],
var base64Encoded = parts[0].indexOf('base64') > -1
var bstr = isSvg // should decode the svg data uri first
? decodeURIComponent(parts[1])
: parts[1] // only `atob` when the data uri is encoded with base64
// otherwise, like `svg` data uri exported by zrender,
// there will be an error, for it's not encoded with base64.
// (just a url-encoded string through `encodeURIComponent`)
base64Encoded && (bstr = window.atob(bstr))
var filename = title + '.' + type // @ts-ignore
if (window.navigator.msSaveOrOpenBlob) {
var n = bstr.length
var u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
var blob = new Blob([u8arr]) // @ts-ignore
window.navigator.msSaveOrOpenBlob(blob, filename)
} else {
var frame = document.createElement('iframe')
document.body.appendChild(frame)
var cw = frame.contentWindow
var doc = cw.document
doc.open('image/svg+xml', 'replace')
doc.write(bstr)
doc.close()
cw.focus()
doc.execCommand('SaveAs', true, filename)
document.body.removeChild(frame)
}
} else {
var lang = model.get('lang')
var html =
'' +
'' +
'
' +
''
var tab = window.open()
tab.document.write(html)
tab.document.title = title
}
}
}
SaveAsImage.getDefaultOption = function (ecModel) {
var defaultOption = {
show: true,
icon: 'M4.7,22.9L29.3,45.5L54.7,23.4M4.6,43.6L4.6,58L53.8,58L53.8,43.6M29.2,45.1L29.2,0',
title: ecModel
.getLocaleModel()
.get(['toolbox', 'saveAsImage', 'title']),
type: 'png', // Default use option.backgroundColor
// backgroundColor: '#fff',
connectedBackgroundColor: '#fff',
name: '',
excludeComponents: ['toolbox'], // use current pixel ratio of device by default
// pixelRatio: 1,
lang: ecModel
.getLocaleModel()
.get(['toolbox', 'saveAsImage', 'lang'])
}
return defaultOption
}
return SaveAsImage
})(ToolboxFeature)
var SaveAsImage$1 = SaveAsImage
var INNER_STACK_KEYWORD = '__ec_magicType_stack__'
var radioTypes = [['line', 'bar'], ['stack']]
var MagicType = /** @class */ (function (_super) {
__extends(MagicType, _super)
function MagicType() {
return (_super !== null && _super.apply(this, arguments)) || this
}
MagicType.prototype.getIcons = function () {
var model = this.model
var availableIcons = model.get('icon')
var icons = {}
each$7(model.get('type'), function (type) {
if (availableIcons[type]) {
icons[type] = availableIcons[type]
}
})
return icons
}
MagicType.getDefaultOption = function (ecModel) {
var defaultOption = {
show: true,
type: [], // Icon group
icon: {
line: 'M4.1,28.9h7.1l9.3-22l7.4,38l9.7-19.7l3,12.8h14.9M4.1,58h51.4',
bar: 'M6.7,22.9h10V48h-10V22.9zM24.9,13h10v35h-10V13zM43.2,2h10v46h-10V2zM3.1,58h53.7', // eslint-disable-next-line
stack:
'M8.2,38.4l-8.4,4.1l30.6,15.3L60,42.5l-8.1-4.1l-21.5,11L8.2,38.4z M51.9,30l-8.1,4.2l-13.4,6.9l-13.9-6.9L8.2,30l-8.4,4.2l8.4,4.2l22.2,11l21.5-11l8.1-4.2L51.9,30z M51.9,21.7l-8.1,4.2L35.7,30l-5.3,2.8L24.9,30l-8.4-4.1l-8.3-4.2l-8.4,4.2L8.2,30l8.3,4.2l13.9,6.9l13.4-6.9l8.1-4.2l8.1-4.1L51.9,21.7zM30.4,2.2L-0.2,17.5l8.4,4.1l8.3,4.2l8.4,4.2l5.5,2.7l5.3-2.7l8.1-4.2l8.1-4.2l8.1-4.1L30.4,2.2z' // jshint ignore:line
}, // `line`, `bar`, `stack`, `tiled`
title: ecModel
.getLocaleModel()
.get(['toolbox', 'magicType', 'title']),
option: {},
seriesIndex: {}
}
return defaultOption
}
MagicType.prototype.onclick = function (ecModel, api, type) {
var model = this.model
var seriesIndex = model.get(['seriesIndex', type]) // Not supported magicType
if (!seriesOptGenreator[type]) {
return
}
var newOption = { series: [] }
var generateNewSeriesTypes = function generateNewSeriesTypes(
seriesModel
) {
var seriesType = seriesModel.subType
var seriesId = seriesModel.id
var newSeriesOpt = seriesOptGenreator[type](
seriesType,
seriesId,
seriesModel,
model
)
if (newSeriesOpt) {
// PENDING If merge original option?
defaults(newSeriesOpt, seriesModel.option)
newOption.series.push(newSeriesOpt)
} // Modify boundaryGap
var coordSys = seriesModel.coordinateSystem
if (
coordSys &&
coordSys.type === 'cartesian2d' &&
(type === 'line' || type === 'bar')
) {
var categoryAxis = coordSys.getAxesByScale('ordinal')[0]
if (categoryAxis) {
var axisDim = categoryAxis.dim
var axisType = axisDim + 'Axis'
var axisModel = seriesModel.getReferringComponents(
axisType,
SINGLE_REFERRING
).models[0]
var axisIndex = axisModel.componentIndex
newOption[axisType] = newOption[axisType] || []
for (var i = 0; i <= axisIndex; i++) {
newOption[axisType][axisIndex] =
newOption[axisType][axisIndex] || {}
}
newOption[axisType][axisIndex].boundaryGap = type === 'bar'
}
}
}
each$7(radioTypes, function (radio) {
if (indexOf(radio, type) >= 0) {
each$7(radio, function (item) {
model.setIconStatus(item, 'normal')
})
}
})
model.setIconStatus(type, 'emphasis')
ecModel.eachComponent(
{
mainType: 'series',
query:
seriesIndex == null ? null : { seriesIndex: seriesIndex }
},
generateNewSeriesTypes
)
var newTitle
var currentType = type // Change title of stack
if (type === 'stack') {
// use titles in model instead of ecModel
// as stack and tiled appears in pair, just flip them
// no need of checking stack state
newTitle = merge(
{
stack: model.option.title.tiled,
tiled: model.option.title.stack
},
model.option.title
)
if (model.get(['iconStatus', type]) !== 'emphasis') {
currentType = 'tiled'
}
}
api.dispatchAction({
type: 'changeMagicType',
currentType: currentType,
newOption: newOption,
newTitle: newTitle,
featureName: 'magicType'
})
}
return MagicType
})(ToolboxFeature)
var seriesOptGenreator = {
line: function line(seriesType, seriesId, seriesModel, model) {
if (seriesType === 'bar') {
return merge(
{
id: seriesId,
type: 'line', // Preserve data related option
data: seriesModel.get('data'),
stack: seriesModel.get('stack'),
markPoint: seriesModel.get('markPoint'),
markLine: seriesModel.get('markLine')
},
model.get(['option', 'line']) || {},
true
)
}
},
bar: function bar(seriesType, seriesId, seriesModel, model) {
if (seriesType === 'line') {
return merge(
{
id: seriesId,
type: 'bar', // Preserve data related option
data: seriesModel.get('data'),
stack: seriesModel.get('stack'),
markPoint: seriesModel.get('markPoint'),
markLine: seriesModel.get('markLine')
},
model.get(['option', 'bar']) || {},
true
)
}
},
stack: function stack(seriesType, seriesId, seriesModel, model) {
var isStack = seriesModel.get('stack') === INNER_STACK_KEYWORD
if (seriesType === 'line' || seriesType === 'bar') {
model.setIconStatus('stack', isStack ? 'normal' : 'emphasis')
return merge(
{ id: seriesId, stack: isStack ? '' : INNER_STACK_KEYWORD },
model.get(['option', 'stack']) || {},
true
)
}
}
} // TODO: SELF REGISTERED.
registerAction(
{
type: 'changeMagicType',
event: 'magicTypeChanged',
update: 'prepareAndUpdate'
},
function (payload, ecModel) {
ecModel.mergeOption(payload.newOption)
}
)
var MagicType$1 = MagicType
/* global document */ var BLOCK_SPLITER = new Array(60).join('-')
var ITEM_SPLITER = '\t'
/**
* Group series into two types
* 1. on category axis, like line, bar
* 2. others, like scatter, pie
*/ function groupSeries(ecModel) {
var seriesGroupByCategoryAxis = {}
var otherSeries = []
var meta = []
ecModel.eachRawSeries(function (seriesModel) {
var coordSys = seriesModel.coordinateSystem
if (
coordSys &&
(coordSys.type === 'cartesian2d' || coordSys.type === 'polar')
) {
// TODO: TYPE Consider polar? Include polar may increase unecessary bundle size.
var baseAxis = coordSys.getBaseAxis()
if (baseAxis.type === 'category') {
var key = baseAxis.dim + '_' + baseAxis.index
if (!seriesGroupByCategoryAxis[key]) {
seriesGroupByCategoryAxis[key] = {
categoryAxis: baseAxis,
valueAxis: coordSys.getOtherAxis(baseAxis),
series: []
}
meta.push({
axisDim: baseAxis.dim,
axisIndex: baseAxis.index
})
}
seriesGroupByCategoryAxis[key].series.push(seriesModel)
} else {
otherSeries.push(seriesModel)
}
} else {
otherSeries.push(seriesModel)
}
})
return {
seriesGroupByCategoryAxis: seriesGroupByCategoryAxis,
other: otherSeries,
meta: meta
}
}
/**
* Assemble content of series on cateogory axis
* @inner
*/ function assembleSeriesWithCategoryAxis(groups) {
var tables = []
each$7(groups, function (group, key) {
var categoryAxis = group.categoryAxis
var valueAxis = group.valueAxis
var valueAxisDim = valueAxis.dim
var headers = [' '].concat(
map$1(group.series, function (series) {
return series.name
})
) // @ts-ignore TODO Polar
var columns = [categoryAxis.model.getCategories()]
each$7(group.series, function (series) {
var rawData = series.getRawData()
columns.push(
series
.getRawData()
.mapArray(
rawData.mapDimension(valueAxisDim),
function (val) {
return val
}
)
)
}) // Assemble table content
var lines = [headers.join(ITEM_SPLITER)]
for (var i = 0; i < columns[0].length; i++) {
var items = []
for (var j = 0; j < columns.length; j++) {
items.push(columns[j][i])
}
lines.push(items.join(ITEM_SPLITER))
}
tables.push(lines.join('\n'))
})
return tables.join('\n\n' + BLOCK_SPLITER + '\n\n')
}
/**
* Assemble content of other series
*/ function assembleOtherSeries(series) {
return map$1(series, function (series) {
var data = series.getRawData()
var lines = [series.name]
var vals = []
data.each(data.dimensions, function () {
var argLen = arguments.length
var dataIndex = arguments[argLen - 1]
var name = data.getName(dataIndex)
for (var i = 0; i < argLen - 1; i++) {
vals[i] = arguments[i]
}
lines.push(
(name ? name + ITEM_SPLITER : '') + vals.join(ITEM_SPLITER)
)
})
return lines.join('\n')
}).join('\n\n' + BLOCK_SPLITER + '\n\n')
}
function getContentFromModel(ecModel) {
var result = groupSeries(ecModel)
return {
value: filter(
[
assembleSeriesWithCategoryAxis(
result.seriesGroupByCategoryAxis
),
assembleOtherSeries(result.other)
],
function (str) {
return !!str.replace(/[\n\t\s]/g, '')
}
).join('\n\n' + BLOCK_SPLITER + '\n\n'),
meta: result.meta
}
}
function trim(str) {
return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '')
}
/**
* If a block is tsv format
*/ function isTSVFormat(block) {
// Simple method to find out if a block is tsv format
var firstLine = block.slice(0, block.indexOf('\n'))
if (firstLine.indexOf(ITEM_SPLITER) >= 0) {
return true
}
}
var itemSplitRegex = new RegExp('[' + ITEM_SPLITER + ']+', 'g')
/**
* @param {string} tsv
* @return {Object}
*/ function parseTSVContents(tsv) {
var tsvLines = tsv.split(/\n+/g)
var headers = trim(tsvLines.shift()).split(itemSplitRegex)
var categories = []
var series = map$1(headers, function (header) {
return { name: header, data: [] }
})
for (var i = 0; i < tsvLines.length; i++) {
var items = trim(tsvLines[i]).split(itemSplitRegex)
categories.push(items.shift())
for (var j = 0; j < items.length; j++) {
series[j] && (series[j].data[i] = items[j])
}
}
return { series: series, categories: categories }
}
function parseListContents(str) {
var lines = str.split(/\n+/g)
var seriesName = trim(lines.shift())
var data = []
for (var i = 0; i < lines.length; i++) {
// if line is empty, ignore it.
// there is a case that a user forgot to delete `\n`.
var line = trim(lines[i])
if (!line) {
continue
}
var items = line.split(itemSplitRegex)
var name_1 = ''
var value = void 0
var hasName = false
if (isNaN(items[0])) {
// First item is name
hasName = true
name_1 = items[0]
items = items.slice(1)
data[i] = { name: name_1, value: [] }
value = data[i].value
} else {
value = data[i] = []
}
for (var j = 0; j < items.length; j++) {
value.push(+items[j])
}
if (value.length === 1) {
hasName ? (data[i].value = value[0]) : (data[i] = value[0])
}
}
return { name: seriesName, data: data }
}
function parseContents(str, blockMetaList) {
var blocks = str.split(
new RegExp('\n*' + BLOCK_SPLITER + '\n*', 'g')
)
var newOption = { series: [] }
each$7(blocks, function (block, idx) {
if (isTSVFormat(block)) {
var result = parseTSVContents(block)
var blockMeta = blockMetaList[idx]
var axisKey = blockMeta.axisDim + 'Axis'
if (blockMeta) {
newOption[axisKey] = newOption[axisKey] || []
newOption[axisKey][blockMeta.axisIndex] = {
data: result.categories
}
newOption.series = newOption.series.concat(result.series)
}
} else {
var result = parseListContents(block)
newOption.series.push(result)
}
})
return newOption
}
var DataView = /** @class */ (function (_super) {
__extends(DataView, _super)
function DataView() {
return (_super !== null && _super.apply(this, arguments)) || this
}
DataView.prototype.onclick = function (ecModel, api) {
// FIXME: better way?
setTimeout(function () {
api.dispatchAction({ type: 'hideTip' })
})
var container = api.getDom()
var model = this.model
if (this._dom) {
container.removeChild(this._dom)
}
var root = document.createElement('div') // use padding to avoid 5px whitespace
root.style.cssText =
'position:absolute;top:0;bottom:0;left:0;right:0;padding:5px'
root.style.backgroundColor =
model.get('backgroundColor') || '#fff' // Create elements
var header = document.createElement('h4')
var lang = model.get('lang') || []
header.innerHTML = lang[0] || model.get('title')
header.style.cssText = 'margin:10px 20px'
header.style.color = model.get('textColor')
var viewMain = document.createElement('div')
var textarea = document.createElement('textarea')
viewMain.style.cssText = 'overflow:auto'
var optionToContent = model.get('optionToContent')
var contentToOption = model.get('contentToOption')
var result = getContentFromModel(ecModel)
if (isFunction(optionToContent)) {
var htmlOrDom = optionToContent(api.getOption())
if (isString(htmlOrDom)) {
viewMain.innerHTML = htmlOrDom
} else if (isDom(htmlOrDom)) {
viewMain.appendChild(htmlOrDom)
}
} else {
// Use default textarea
textarea.readOnly = model.get('readOnly')
var style = textarea.style // eslint-disable-next-line max-len
style.cssText =
'display:block;width:100%;height:100%;font-family:monospace;font-size:14px;line-height:1.6rem;resize:none;box-sizing:border-box;outline:none'
style.color = model.get('textColor')
style.borderColor = model.get('textareaBorderColor')
style.backgroundColor = model.get('textareaColor')
textarea.value = result.value
viewMain.appendChild(textarea)
}
var blockMetaList = result.meta
var buttonContainer = document.createElement('div')
buttonContainer.style.cssText =
'position:absolute;bottom:5px;left:0;right:0' // eslint-disable-next-line max-len
var buttonStyle =
'float:right;margin-right:20px;border:none;cursor:pointer;padding:2px 5px;font-size:12px;border-radius:3px'
var closeButton = document.createElement('div')
var refreshButton = document.createElement('div')
buttonStyle += ';background-color:' + model.get('buttonColor')
buttonStyle += ';color:' + model.get('buttonTextColor')
var self = this
function close() {
container.removeChild(root)
self._dom = null
}
addEventListener(closeButton, 'click', close)
addEventListener(refreshButton, 'click', function () {
if (
(contentToOption == null && optionToContent != null) ||
(contentToOption != null && optionToContent == null)
) {
close()
return
}
var newOption
try {
if (isFunction(contentToOption)) {
newOption = contentToOption(viewMain, api.getOption())
} else {
newOption = parseContents(textarea.value, blockMetaList)
}
} catch (e) {
close()
throw new Error('Data view format error ' + e)
}
if (newOption) {
api.dispatchAction({
type: 'changeDataView',
newOption: newOption
})
}
close()
})
closeButton.innerHTML = lang[1]
refreshButton.innerHTML = lang[2]
refreshButton.style.cssText = closeButton.style.cssText =
buttonStyle
!model.get('readOnly') &&
buttonContainer.appendChild(refreshButton)
buttonContainer.appendChild(closeButton)
root.appendChild(header)
root.appendChild(viewMain)
root.appendChild(buttonContainer)
viewMain.style.height = container.clientHeight - 80 + 'px'
container.appendChild(root)
this._dom = root
}
DataView.prototype.remove = function (ecModel, api) {
this._dom && api.getDom().removeChild(this._dom)
}
DataView.prototype.dispose = function (ecModel, api) {
this.remove(ecModel, api)
}
DataView.getDefaultOption = function (ecModel) {
var defaultOption = {
show: true,
readOnly: false,
optionToContent: null,
contentToOption: null, // eslint-disable-next-line
icon: 'M17.5,17.3H33 M17.5,17.3H33 M45.4,29.5h-28 M11.5,2v56H51V14.8L38.4,2H11.5z M38.4,2.2v12.7H51 M45.4,41.7h-28',
title: ecModel
.getLocaleModel()
.get(['toolbox', 'dataView', 'title']),
lang: ecModel
.getLocaleModel()
.get(['toolbox', 'dataView', 'lang']),
backgroundColor: '#fff',
textColor: '#000',
textareaColor: '#fff',
textareaBorderColor: '#333',
buttonColor: '#c23531',
buttonTextColor: '#fff'
}
return defaultOption
}
return DataView
})(ToolboxFeature)
/**
* @inner
*/ function tryMergeDataOption(newData, originalData) {
return map$1(newData, function (newVal, idx) {
var original = originalData && originalData[idx]
if (isObject$2(original) && !isArray(original)) {
var newValIsObject = isObject$2(newVal) && !isArray(newVal)
if (!newValIsObject) {
newVal = { value: newVal }
} // original data has name but new data has no name
var shouldDeleteName =
original.name != null && newVal.name == null // Original data has option
newVal = defaults(newVal, original)
shouldDeleteName && delete newVal.name
return newVal
} else {
return newVal
}
})
} // TODO: SELF REGISTERED.
registerAction(
{
type: 'changeDataView',
event: 'dataViewChanged',
update: 'prepareAndUpdate'
},
function (payload, ecModel) {
var newSeriesOptList = []
each$7(payload.newOption.series, function (seriesOpt) {
var seriesModel = ecModel.getSeriesByName(seriesOpt.name)[0]
if (!seriesModel) {
// New created series
// Geuss the series type
newSeriesOptList.push(
extend(
{
// Default is scatter
type: 'scatter'
},
seriesOpt
)
)
} else {
var originalData = seriesModel.get('data')
newSeriesOptList.push({
name: seriesOpt.name,
data: tryMergeDataOption(seriesOpt.data, originalData)
})
}
})
ecModel.mergeOption(
defaults({ series: newSeriesOptList }, payload.newOption)
)
}
)
var DataView$1 = DataView
var each$2 = each$7
var inner$1 = makeInner()
/**
* @param ecModel
* @param newSnapshot key is dataZoomId
*/ function push(ecModel, newSnapshot) {
var storedSnapshots = getStoreSnapshots(ecModel) // If previous dataZoom can not be found,
// complete an range with current range.
each$2(newSnapshot, function (batchItem, dataZoomId) {
var i = storedSnapshots.length - 1
for (; i >= 0; i--) {
var snapshot = storedSnapshots[i]
if (snapshot[dataZoomId]) {
break
}
}
if (i < 0) {
// No origin range set, create one by current range.
var dataZoomModel = ecModel.queryComponents({
mainType: 'dataZoom',
subType: 'select',
id: dataZoomId
})[0]
if (dataZoomModel) {
var percentRange = dataZoomModel.getPercentRange()
storedSnapshots[0][dataZoomId] = {
dataZoomId: dataZoomId,
start: percentRange[0],
end: percentRange[1]
}
}
}
})
storedSnapshots.push(newSnapshot)
}
function pop(ecModel) {
var storedSnapshots = getStoreSnapshots(ecModel)
var head = storedSnapshots[storedSnapshots.length - 1]
storedSnapshots.length > 1 && storedSnapshots.pop() // Find top for all dataZoom.
var snapshot = {}
each$2(head, function (batchItem, dataZoomId) {
for (var i = storedSnapshots.length - 1; i >= 0; i--) {
batchItem = storedSnapshots[i][dataZoomId]
if (batchItem) {
snapshot[dataZoomId] = batchItem
break
}
}
})
return snapshot
}
function clear(ecModel) {
inner$1(ecModel).snapshots = null
}
function count(ecModel) {
return getStoreSnapshots(ecModel).length
}
/**
* History length of each dataZoom may be different.
* this._history[0] is used to store origin range.
*/ function getStoreSnapshots(ecModel) {
var store = inner$1(ecModel)
if (!store.snapshots) {
store.snapshots = [{}]
}
return store.snapshots
}
var RestoreOption = /** @class */ (function (_super) {
__extends(RestoreOption, _super)
function RestoreOption() {
return (_super !== null && _super.apply(this, arguments)) || this
}
RestoreOption.prototype.onclick = function (ecModel, api) {
clear(ecModel)
api.dispatchAction({ type: 'restore', from: this.uid })
}
RestoreOption.getDefaultOption = function (ecModel) {
var defaultOption = {
show: true, // eslint-disable-next-line
icon: 'M3.8,33.4 M47,18.9h9.8V8.7 M56.3,20.1 C52.1,9,40.5,0.6,26.8,2.1C12.6,3.7,1.6,16.2,2.1,30.6 M13,41.1H3.1v10.2 M3.7,39.9c4.2,11.1,15.8,19.5,29.5,18 c14.2-1.6,25.2-14.1,24.7-28.5',
title: ecModel
.getLocaleModel()
.get(['toolbox', 'restore', 'title'])
}
return defaultOption
}
return RestoreOption
})(ToolboxFeature) // TODO: SELF REGISTERED.
registerAction(
{ type: 'restore', event: 'restore', update: 'prepareAndUpdate' },
function (payload, ecModel) {
ecModel.resetOption('recreate')
}
)
var Restore = RestoreOption // how to genarialize to more coordinate systems.
var INCLUDE_FINDER_MAIN_TYPES = [
'grid',
'xAxis',
'yAxis',
'geo',
'graph',
'polar',
'radiusAxis',
'angleAxis',
'bmap'
]
var BrushTargetManager = /** @class */ (function () {
/**
* @param finder contains Index/Id/Name of xAxis/yAxis/geo/grid
* Each can be {number|Array.}. like: {xAxisIndex: [3, 4]}
* @param opt.include include coordinate system types.
*/ function BrushTargetManager(finder, ecModel, opt) {
var _this = this
this._targetInfoList = []
var foundCpts = parseFinder(ecModel, finder)
each$7(targetInfoBuilders, function (builder, type) {
if (!opt || !opt.include || indexOf(opt.include, type) >= 0) {
builder(foundCpts, _this._targetInfoList)
}
})
}
BrushTargetManager.prototype.setOutputRanges = function (
areas,
ecModel
) {
this.matchOutputRanges(
areas,
ecModel,
function (area, coordRange, coordSys) {
;(area.coordRanges || (area.coordRanges = [])).push(
coordRange
) // area.coordRange is the first of area.coordRanges
if (!area.coordRange) {
area.coordRange = coordRange // In 'category' axis, coord to pixel is not reversible, so we can not
// rebuild range by coordRange accrately, which may bring trouble when
// brushing only one item. So we use __rangeOffset to rebuilding range
// by coordRange. And this it only used in brush component so it is no
// need to be adapted to coordRanges.
var result = coordConvert[area.brushType](
0,
coordSys,
coordRange
)
area.__rangeOffset = {
offset: diffProcessor[area.brushType](
result.values,
area.range,
[1, 1]
),
xyMinMax: result.xyMinMax
}
}
}
)
return areas
}
BrushTargetManager.prototype.matchOutputRanges = function (
areas,
ecModel,
cb
) {
each$7(
areas,
function (area) {
var targetInfo = this.findTargetInfo(area, ecModel)
if (targetInfo && targetInfo !== true) {
each$7(targetInfo.coordSyses, function (coordSys) {
var result = coordConvert[area.brushType](
1,
coordSys,
area.range,
true
)
cb(area, result.values, coordSys, ecModel)
})
}
},
this
)
}
/**
* the `areas` is `BrushModel.areas`.
* Called in layout stage.
* convert `area.coordRange` to global range and set panelId to `area.range`.
*/ BrushTargetManager.prototype.setInputRanges = function (
areas,
ecModel
) {
each$7(
areas,
function (area) {
var targetInfo = this.findTargetInfo(area, ecModel)
area.range = area.range || [] // convert coordRange to global range and set panelId.
if (targetInfo && targetInfo !== true) {
area.panelId = targetInfo.panelId // (1) area.range shoule always be calculate from coordRange but does
// not keep its original value, for the sake of the dataZoom scenario,
// where area.coordRange remains unchanged but area.range may be changed.
// (2) Only support converting one coordRange to pixel range in brush
// component. So do not consider `coordRanges`.
// (3) About __rangeOffset, see comment above.
var result = coordConvert[area.brushType](
0,
targetInfo.coordSys,
area.coordRange
)
var rangeOffset = area.__rangeOffset
area.range = rangeOffset
? diffProcessor[area.brushType](
result.values,
rangeOffset.offset,
getScales(result.xyMinMax, rangeOffset.xyMinMax)
)
: result.values
}
},
this
)
}
BrushTargetManager.prototype.makePanelOpts = function (
api,
getDefaultBrushType
) {
return map$1(this._targetInfoList, function (targetInfo) {
var rect = targetInfo.getPanelRect()
return {
panelId: targetInfo.panelId,
defaultBrushType: getDefaultBrushType
? getDefaultBrushType(targetInfo)
: null,
clipPath: makeRectPanelClipPath(rect),
isTargetByCursor: makeRectIsTargetByCursor(
rect,
api,
targetInfo.coordSysModel
),
getLinearBrushOtherExtent: makeLinearBrushOtherExtent(rect)
}
})
}
BrushTargetManager.prototype.controlSeries = function (
area,
seriesModel,
ecModel
) {
// Check whether area is bound in coord, and series do not belong to that coord.
// If do not do this check, some brush (like lineX) will controll all axes.
var targetInfo = this.findTargetInfo(area, ecModel)
return (
targetInfo === true ||
(targetInfo &&
indexOf(
targetInfo.coordSyses,
seriesModel.coordinateSystem
) >= 0)
)
}
/**
* If return Object, a coord found.
* If reutrn true, global found.
* Otherwise nothing found.
*/ BrushTargetManager.prototype.findTargetInfo = function (
area,
ecModel
) {
var targetInfoList = this._targetInfoList
var foundCpts = parseFinder(ecModel, area)
for (var i = 0; i < targetInfoList.length; i++) {
var targetInfo = targetInfoList[i]
var areaPanelId = area.panelId
if (areaPanelId) {
if (targetInfo.panelId === areaPanelId) {
return targetInfo
}
} else {
for (var j = 0; j < targetInfoMatchers.length; j++) {
if (targetInfoMatchers[j](foundCpts, targetInfo)) {
return targetInfo
}
}
}
}
return true
}
return BrushTargetManager
})()
function formatMinMax(minMax) {
minMax[0] > minMax[1] && minMax.reverse()
return minMax
}
function parseFinder(ecModel, finder) {
return parseFinder$1(ecModel, finder, {
includeMainTypes: INCLUDE_FINDER_MAIN_TYPES
})
}
var targetInfoBuilders = {
grid: function grid(foundCpts, targetInfoList) {
var xAxisModels = foundCpts.xAxisModels
var yAxisModels = foundCpts.yAxisModels
var gridModels = foundCpts.gridModels // Remove duplicated.
var gridModelMap = createHashMap()
var xAxesHas = {}
var yAxesHas = {}
if (!xAxisModels && !yAxisModels && !gridModels) {
return
}
each$7(xAxisModels, function (axisModel) {
var gridModel = axisModel.axis.grid.model
gridModelMap.set(gridModel.id, gridModel)
xAxesHas[gridModel.id] = true
})
each$7(yAxisModels, function (axisModel) {
var gridModel = axisModel.axis.grid.model
gridModelMap.set(gridModel.id, gridModel)
yAxesHas[gridModel.id] = true
})
each$7(gridModels, function (gridModel) {
gridModelMap.set(gridModel.id, gridModel)
xAxesHas[gridModel.id] = true
yAxesHas[gridModel.id] = true
})
gridModelMap.each(function (gridModel) {
var grid = gridModel.coordinateSystem
var cartesians = []
each$7(grid.getCartesians(), function (cartesian, index) {
if (
indexOf(xAxisModels, cartesian.getAxis('x').model) >= 0 ||
indexOf(yAxisModels, cartesian.getAxis('y').model) >= 0
) {
cartesians.push(cartesian)
}
})
targetInfoList.push({
panelId: 'grid--' + gridModel.id,
gridModel: gridModel,
coordSysModel: gridModel, // Use the first one as the representitive coordSys.
coordSys: cartesians[0],
coordSyses: cartesians,
getPanelRect: panelRectBuilders.grid,
xAxisDeclared: xAxesHas[gridModel.id],
yAxisDeclared: yAxesHas[gridModel.id]
})
})
},
geo: function geo(foundCpts, targetInfoList) {
each$7(foundCpts.geoModels, function (geoModel) {
var coordSys = geoModel.coordinateSystem
targetInfoList.push({
panelId: 'geo--' + geoModel.id,
geoModel: geoModel,
coordSysModel: geoModel,
coordSys: coordSys,
coordSyses: [coordSys],
getPanelRect: panelRectBuilders.geo
})
})
}
}
var targetInfoMatchers = [
// grid
function (foundCpts, targetInfo) {
var xAxisModel = foundCpts.xAxisModel
var yAxisModel = foundCpts.yAxisModel
var gridModel = foundCpts.gridModel
!gridModel &&
xAxisModel &&
(gridModel = xAxisModel.axis.grid.model)
!gridModel &&
yAxisModel &&
(gridModel = yAxisModel.axis.grid.model)
return gridModel && gridModel === targetInfo.gridModel
}, // geo
function (foundCpts, targetInfo) {
var geoModel = foundCpts.geoModel
return geoModel && geoModel === targetInfo.geoModel
}
]
var panelRectBuilders = {
grid: function grid() {
// grid is not Transformable.
return this.coordSys.master.getRect().clone()
},
geo: function geo() {
var coordSys = this.coordSys
var rect = coordSys.getBoundingRect().clone() // geo roam and zoom transform
rect.applyTransform(getTransform$1(coordSys))
return rect
}
}
var coordConvert = {
lineX: curry$1(axisConvert, 0),
lineY: curry$1(axisConvert, 1),
rect: function rect(to, coordSys, rangeOrCoordRange, clamp) {
var xminymin = to
? coordSys.pointToData(
[rangeOrCoordRange[0][0], rangeOrCoordRange[1][0]],
clamp
)
: coordSys.dataToPoint(
[rangeOrCoordRange[0][0], rangeOrCoordRange[1][0]],
clamp
)
var xmaxymax = to
? coordSys.pointToData(
[rangeOrCoordRange[0][1], rangeOrCoordRange[1][1]],
clamp
)
: coordSys.dataToPoint(
[rangeOrCoordRange[0][1], rangeOrCoordRange[1][1]],
clamp
)
var values = [
formatMinMax([xminymin[0], xmaxymax[0]]),
formatMinMax([xminymin[1], xmaxymax[1]])
]
return { values: values, xyMinMax: values }
},
polygon: function polygon(to, coordSys, rangeOrCoordRange, clamp) {
var xyMinMax = [
[Infinity, -Infinity],
[Infinity, -Infinity]
]
var values = map$1(rangeOrCoordRange, function (item) {
var p = to
? coordSys.pointToData(item, clamp)
: coordSys.dataToPoint(item, clamp)
xyMinMax[0][0] = Math.min(xyMinMax[0][0], p[0])
xyMinMax[1][0] = Math.min(xyMinMax[1][0], p[1])
xyMinMax[0][1] = Math.max(xyMinMax[0][1], p[0])
xyMinMax[1][1] = Math.max(xyMinMax[1][1], p[1])
return p
})
return { values: values, xyMinMax: xyMinMax }
}
}
function axisConvert(axisNameIndex, to, coordSys, rangeOrCoordRange) {
var axis = coordSys.getAxis(['x', 'y'][axisNameIndex])
var values = formatMinMax(
map$1([0, 1], function (i) {
return to
? axis.coordToData(
axis.toLocalCoord(rangeOrCoordRange[i]),
true
)
: axis.toGlobalCoord(axis.dataToCoord(rangeOrCoordRange[i]))
})
)
var xyMinMax = []
xyMinMax[axisNameIndex] = values
xyMinMax[1 - axisNameIndex] = [NaN, NaN]
return { values: values, xyMinMax: xyMinMax }
}
var diffProcessor = {
lineX: curry$1(axisDiffProcessor, 0),
lineY: curry$1(axisDiffProcessor, 1),
rect: function rect(values, refer, scales) {
return [
[
values[0][0] - scales[0] * refer[0][0],
values[0][1] - scales[0] * refer[0][1]
],
[
values[1][0] - scales[1] * refer[1][0],
values[1][1] - scales[1] * refer[1][1]
]
]
},
polygon: function polygon(values, refer, scales) {
return map$1(values, function (item, idx) {
return [
item[0] - scales[0] * refer[idx][0],
item[1] - scales[1] * refer[idx][1]
]
})
}
}
function axisDiffProcessor(axisNameIndex, values, refer, scales) {
return [
values[0] - scales[axisNameIndex] * refer[0],
values[1] - scales[axisNameIndex] * refer[1]
]
} // We have to process scale caused by dataZoom manually,
// although it might be not accurate.
// Return [0~1, 0~1]
function getScales(xyMinMaxCurr, xyMinMaxOrigin) {
var sizeCurr = getSize(xyMinMaxCurr)
var sizeOrigin = getSize(xyMinMaxOrigin)
var scales = [
sizeCurr[0] / sizeOrigin[0],
sizeCurr[1] / sizeOrigin[1]
]
isNaN(scales[0]) && (scales[0] = 1)
isNaN(scales[1]) && (scales[1] = 1)
return scales
}
function getSize(xyMinMax) {
return xyMinMax
? [
xyMinMax[0][1] - xyMinMax[0][0],
xyMinMax[1][1] - xyMinMax[1][0]
]
: [NaN, NaN]
}
var BrushTargetManager$1 = BrushTargetManager
var each$1 = each$7
var DATA_ZOOM_ID_BASE = makeInternalComponentId('toolbox-dataZoom_')
var DataZoomFeature = /** @class */ (function (_super) {
__extends(DataZoomFeature, _super)
function DataZoomFeature() {
return (_super !== null && _super.apply(this, arguments)) || this
}
DataZoomFeature.prototype.render = function (
featureModel,
ecModel,
api,
payload
) {
if (!this._brushController) {
this._brushController = new BrushController$1(api.getZr())
this._brushController
.on('brush', bind$1(this._onBrush, this))
.mount()
}
updateZoomBtnStatus(featureModel, ecModel, this, payload, api)
updateBackBtnStatus(featureModel, ecModel)
}
DataZoomFeature.prototype.onclick = function (ecModel, api, type) {
handlers[type].call(this)
}
DataZoomFeature.prototype.remove = function (ecModel, api) {
this._brushController && this._brushController.unmount()
}
DataZoomFeature.prototype.dispose = function (ecModel, api) {
this._brushController && this._brushController.dispose()
}
DataZoomFeature.prototype._onBrush = function (eventParam) {
var areas = eventParam.areas
if (!eventParam.isEnd || !areas.length) {
return
}
var snapshot = {}
var ecModel = this.ecModel
this._brushController.updateCovers([]) // remove cover
var brushTargetManager = new BrushTargetManager$1(
makeAxisFinder(this.model),
ecModel,
{ include: ['grid'] }
)
brushTargetManager.matchOutputRanges(
areas,
ecModel,
function (area, coordRange, coordSys) {
if (coordSys.type !== 'cartesian2d') {
return
}
var brushType = area.brushType
if (brushType === 'rect') {
setBatch('x', coordSys, coordRange[0])
setBatch('y', coordSys, coordRange[1])
} else {
setBatch(
{ lineX: 'x', lineY: 'y' }[brushType],
coordSys,
coordRange
)
}
}
)
push(ecModel, snapshot)
this._dispatchZoomAction(snapshot)
function setBatch(dimName, coordSys, minMax) {
var axis = coordSys.getAxis(dimName)
var axisModel = axis.model
var dataZoomModel = findDataZoom(dimName, axisModel, ecModel) // Restrict range.
var minMaxSpan = dataZoomModel
.findRepresentativeAxisProxy(axisModel)
.getMinMaxSpan()
if (
minMaxSpan.minValueSpan != null ||
minMaxSpan.maxValueSpan != null
) {
minMax = sliderMove(
0,
minMax.slice(),
axis.scale.getExtent(),
0,
minMaxSpan.minValueSpan,
minMaxSpan.maxValueSpan
)
}
dataZoomModel &&
(snapshot[dataZoomModel.id] = {
dataZoomId: dataZoomModel.id,
startValue: minMax[0],
endValue: minMax[1]
})
}
function findDataZoom(dimName, axisModel, ecModel) {
var found
ecModel.eachComponent(
{ mainType: 'dataZoom', subType: 'select' },
function (dzModel) {
var has = dzModel.getAxisModel(
dimName,
axisModel.componentIndex
)
has && (found = dzModel)
}
)
return found
}
}
DataZoomFeature.prototype._dispatchZoomAction = function (
snapshot
) {
var batch = [] // Convert from hash map to array.
each$1(snapshot, function (batchItem, dataZoomId) {
batch.push(clone$3(batchItem))
})
batch.length &&
this.api.dispatchAction({
type: 'dataZoom',
from: this.uid,
batch: batch
})
}
DataZoomFeature.getDefaultOption = function (ecModel) {
var defaultOption = {
show: true,
filterMode: 'filter', // Icon group
icon: {
zoom: 'M0,13.5h26.9 M13.5,26.9V0 M32.1,13.5H58V58H13.5 V32.1',
back: 'M22,1.4L9.9,13.5l12.3,12.3 M10.3,13.5H54.9v44.6 H10.3v-26'
}, // `zoom`, `back`
title: ecModel
.getLocaleModel()
.get(['toolbox', 'dataZoom', 'title']),
brushStyle: { borderWidth: 0, color: 'rgba(210,219,238,0.2)' }
}
return defaultOption
}
return DataZoomFeature
})(ToolboxFeature)
var handlers = {
zoom: function zoom() {
var nextActive = !this._isZoomActive
this.api.dispatchAction({
type: 'takeGlobalCursor',
key: 'dataZoomSelect',
dataZoomSelectActive: nextActive
})
},
back: function back() {
this._dispatchZoomAction(pop(this.ecModel))
}
}
function makeAxisFinder(dzFeatureModel) {
var setting = {
xAxisIndex: dzFeatureModel.get('xAxisIndex', true),
yAxisIndex: dzFeatureModel.get('yAxisIndex', true),
xAxisId: dzFeatureModel.get('xAxisId', true),
yAxisId: dzFeatureModel.get('yAxisId', true)
} // If both `xAxisIndex` `xAxisId` not set, it means 'all'.
// If both `yAxisIndex` `yAxisId` not set, it means 'all'.
// Some old cases set like this below to close yAxis control but leave xAxis control:
// `{ feature: { dataZoom: { yAxisIndex: false } }`.
if (setting.xAxisIndex == null && setting.xAxisId == null) {
setting.xAxisIndex = 'all'
}
if (setting.yAxisIndex == null && setting.yAxisId == null) {
setting.yAxisIndex = 'all'
}
return setting
}
function updateBackBtnStatus(featureModel, ecModel) {
featureModel.setIconStatus(
'back',
count(ecModel) > 1 ? 'emphasis' : 'normal'
)
}
function updateZoomBtnStatus(
featureModel,
ecModel,
view,
payload,
api
) {
var zoomActive = view._isZoomActive
if (payload && payload.type === 'takeGlobalCursor') {
zoomActive =
payload.key === 'dataZoomSelect'
? payload.dataZoomSelectActive
: false
}
view._isZoomActive = zoomActive
featureModel.setIconStatus(
'zoom',
zoomActive ? 'emphasis' : 'normal'
)
var brushTargetManager = new BrushTargetManager$1(
makeAxisFinder(featureModel),
ecModel,
{ include: ['grid'] }
)
var panels = brushTargetManager.makePanelOpts(
api,
function (targetInfo) {
return targetInfo.xAxisDeclared && !targetInfo.yAxisDeclared
? 'lineX'
: !targetInfo.xAxisDeclared && targetInfo.yAxisDeclared
? 'lineY'
: 'rect'
}
)
view._brushController
.setPanels(panels)
.enableBrush(
zoomActive && panels.length
? {
brushType: 'auto',
brushStyle: featureModel
.getModel('brushStyle')
.getItemStyle()
}
: false
)
}
registerInternalOptionCreator('dataZoom', function (ecModel) {
var toolboxModel = ecModel.getComponent('toolbox', 0)
var featureDataZoomPath = ['feature', 'dataZoom']
if (
!toolboxModel ||
toolboxModel.get(featureDataZoomPath) == null
) {
return
}
var dzFeatureModel = toolboxModel.getModel(featureDataZoomPath)
var dzOptions = []
var finder = makeAxisFinder(dzFeatureModel)
var finderResult = parseFinder$1(ecModel, finder)
each$1(finderResult.xAxisModels, function (axisModel) {
return buildInternalOptions(axisModel, 'xAxis', 'xAxisIndex')
})
each$1(finderResult.yAxisModels, function (axisModel) {
return buildInternalOptions(axisModel, 'yAxis', 'yAxisIndex')
})
function buildInternalOptions(
axisModel,
axisMainType,
axisIndexPropName
) {
var axisIndex = axisModel.componentIndex
var newOpt = {
type: 'select',
$fromToolbox: true, // Default to be filter
filterMode: dzFeatureModel.get('filterMode', true) || 'filter', // Id for merge mapping.
id: DATA_ZOOM_ID_BASE + axisMainType + axisIndex
}
newOpt[axisIndexPropName] = axisIndex
dzOptions.push(newOpt)
}
return dzOptions
})
var DataZoom = DataZoomFeature
function install$b(registers) {
registers.registerComponentModel(ToolboxModel$1)
registers.registerComponentView(ToolboxView$1)
registerFeature('saveAsImage', SaveAsImage$1)
registerFeature('magicType', MagicType$1)
registerFeature('dataView', DataView$1)
registerFeature('dataZoom', DataZoom)
registerFeature('restore', Restore)
use(install$c)
}
var TooltipModel = /** @class */ (function (_super) {
__extends(TooltipModel, _super)
function TooltipModel() {
var _this =
(_super !== null && _super.apply(this, arguments)) || this
_this.type = TooltipModel.type
return _this
}
TooltipModel.type = 'tooltip'
TooltipModel.dependencies = ['axisPointer']
TooltipModel.defaultOption = {
// zlevel: 0,
z: 60,
show: true, // tooltip main content
showContent: true, // 'trigger' only works on coordinate system.
// 'item' | 'axis' | 'none'
trigger: 'item', // 'click' | 'mousemove' | 'none'
triggerOn: 'mousemove|click',
alwaysShowContent: false,
displayMode: 'single',
renderMode: 'auto', // whether restraint content inside viewRect.
// If renderMode: 'richText', default true.
// If renderMode: 'html', defaut false (for backward compat).
confine: null,
showDelay: 0,
hideDelay: 100, // Animation transition time, unit is second
transitionDuration: 0.4,
enterable: false,
backgroundColor: '#fff', // box shadow
shadowBlur: 10,
shadowColor: 'rgba(0, 0, 0, .2)',
shadowOffsetX: 1,
shadowOffsetY: 2, // tooltip border radius, unit is px, default is 4
borderRadius: 4, // tooltip border width, unit is px, default is 0 (no border)
borderWidth: 1, // Tooltip inside padding, default is 5 for all direction
// Array is allowed to set up, right, bottom, left, same with css
// The default value: See `tooltip/tooltipMarkup.ts#getPaddingFromTooltipModel`.
padding: null, // Extra css text
extraCssText: '', // axis indicator, trigger by axis
axisPointer: {
// default is line
// legal values: 'line' | 'shadow' | 'cross'
type: 'line', // Valid when type is line, appoint tooltip line locate on which line. Optional
// legal values: 'x' | 'y' | 'angle' | 'radius' | 'auto'
// default is 'auto', chose the axis which type is category.
// for multiply y axis, cartesian coord chose x axis, polar chose angle axis
axis: 'auto',
animation: 'auto',
animationDurationUpdate: 200,
animationEasingUpdate: 'exponentialOut',
crossStyle: {
color: '#999',
width: 1,
type: 'dashed', // TODO formatter
textStyle: {}
} // lineStyle and shadowStyle should not be specified here,
// otherwise it will always override those styles on option.axisPointer.
},
textStyle: { color: '#666', fontSize: 14 }
}
return TooltipModel
})(ComponentModel$1)
var TooltipModel$1 = TooltipModel
/* global document */ function shouldTooltipConfine(tooltipModel) {
var confineOption = tooltipModel.get('confine')
return confineOption != null
? !!confineOption // In richText mode, the outside part can not be visible.
: tooltipModel.get('renderMode') === 'richText'
}
function testStyle(styleProps) {
if (!env$1.domSupported) {
return
}
var style = document.documentElement.style
for (var i = 0, len = styleProps.length; i < len; i++) {
if (styleProps[i] in style) {
return styleProps[i]
}
}
}
var TRANSFORM_VENDOR = testStyle([
'transform',
'webkitTransform',
'OTransform',
'MozTransform',
'msTransform'
])
var TRANSITION_VENDOR = testStyle([
'webkitTransition',
'transition',
'OTransition',
'MozTransition',
'msTransition'
])
function toCSSVendorPrefix(styleVendor, styleProp) {
if (!styleVendor) {
return styleProp
}
styleProp = toCamelCase(styleProp, true)
var idx = styleVendor.indexOf(styleProp)
styleVendor =
idx === -1
? styleProp
: '-' + styleVendor.slice(0, idx) + '-' + styleProp
return styleVendor.toLowerCase()
}
function getComputedStyle(el, style) {
var stl =
el.currentStyle ||
(document.defaultView &&
document.defaultView.getComputedStyle(el))
return stl ? (style ? stl[style] : stl) : null
}
/* global document, window */ var CSS_TRANSITION_VENDOR =
toCSSVendorPrefix(TRANSITION_VENDOR, 'transition')
var CSS_TRANSFORM_VENDOR = toCSSVendorPrefix(
TRANSFORM_VENDOR,
'transform'
) // eslint-disable-next-line
var gCssText =
'position:absolute;display:block;border-style:solid;white-space:nowrap;z-index:9999999;' +
(env$1.transform3dSupported ? 'will-change:transform;' : '')
function mirrorPos(pos) {
pos =
pos === 'left'
? 'right'
: pos === 'right'
? 'left'
: pos === 'top'
? 'bottom'
: 'top'
return pos
}
function assembleArrow(tooltipModel, borderColor, arrowPosition) {
if (!isString(arrowPosition) || arrowPosition === 'inside') {
return ''
}
var backgroundColor = tooltipModel.get('backgroundColor')
var borderWidth = tooltipModel.get('borderWidth')
borderColor = convertToColorString(borderColor)
var arrowPos = mirrorPos(arrowPosition)
var arrowSize = Math.max(Math.round(borderWidth) * 1.5, 6)
var positionStyle = ''
var transformStyle = CSS_TRANSFORM_VENDOR + ':'
var rotateDeg
if (indexOf(['left', 'right'], arrowPos) > -1) {
positionStyle += 'top:50%'
transformStyle +=
'translateY(-50%) rotate(' +
(rotateDeg = arrowPos === 'left' ? -225 : -45) +
'deg)'
} else {
positionStyle += 'left:50%'
transformStyle +=
'translateX(-50%) rotate(' +
(rotateDeg = arrowPos === 'top' ? 225 : 45) +
'deg)'
}
var rotateRadian = (rotateDeg * Math.PI) / 180
var arrowWH = arrowSize + borderWidth
var rotatedWH =
arrowWH * Math.abs(Math.cos(rotateRadian)) +
arrowWH * Math.abs(Math.sin(rotateRadian))
var arrowOffset =
Math.round(
((rotatedWH - Math.SQRT2 * borderWidth) / 2 +
Math.SQRT2 * borderWidth -
(rotatedWH - arrowWH) / 2) *
100
) / 100
positionStyle += ';' + arrowPos + ':-' + arrowOffset + 'px'
var borderStyle = borderColor + ' solid ' + borderWidth + 'px;'
var styleCss = [
'position:absolute;width:' +
arrowSize +
'px;height:' +
arrowSize +
'px;',
positionStyle + ';' + transformStyle + ';',
'border-bottom:' + borderStyle,
'border-right:' + borderStyle,
'background-color:' + backgroundColor + ';'
]
return ''
}
function assembleTransition(duration, onlyFade) {
var transitionCurve = 'cubic-bezier(0.23,1,0.32,1)'
var transitionOption = ' ' + duration / 2 + 's ' + transitionCurve
var transitionText =
'opacity' + transitionOption + ',visibility' + transitionOption
if (!onlyFade) {
transitionOption = ' ' + duration + 's ' + transitionCurve
transitionText += env$1.transformSupported
? ',' + CSS_TRANSFORM_VENDOR + transitionOption
: ',left' + transitionOption + ',top' + transitionOption
}
return CSS_TRANSITION_VENDOR + ':' + transitionText
}
function assembleTransform(x, y, toString) {
// If using float on style, the final width of the dom might
// keep changing slightly while mouse move. So `toFixed(0)` them.
var x0 = x.toFixed(0) + 'px'
var y0 = y.toFixed(0) + 'px' // not support transform, use `left` and `top` instead.
if (!env$1.transformSupported) {
return toString
? 'top:' + y0 + ';left:' + x0 + ';'
: [
['top', y0],
['left', x0]
]
} // support transform
var is3d = env$1.transform3dSupported
var translate =
'translate' +
(is3d ? '3d' : '') +
'(' +
x0 +
',' +
y0 +
(is3d ? ',0' : '') +
')'
return toString
? 'top:0;left:0;' + CSS_TRANSFORM_VENDOR + ':' + translate + ';'
: [
['top', 0],
['left', 0],
[TRANSFORM_VENDOR, translate]
]
}
/**
* @param {Object} textStyle
* @return {string}
* @inner
*/ function assembleFont(textStyleModel) {
var cssText = []
var fontSize = textStyleModel.get('fontSize')
var color = textStyleModel.getTextColor()
color && cssText.push('color:' + color)
cssText.push('font:' + textStyleModel.getFont())
fontSize && // @ts-ignore, leave it to the tooltip refactor.
cssText.push(
'line-height:' + Math.round((fontSize * 3) / 2) + 'px'
)
var shadowColor = textStyleModel.get('textShadowColor')
var shadowBlur = textStyleModel.get('textShadowBlur') || 0
var shadowOffsetX = textStyleModel.get('textShadowOffsetX') || 0
var shadowOffsetY = textStyleModel.get('textShadowOffsetY') || 0
shadowColor &&
shadowBlur &&
cssText.push(
'text-shadow:' +
shadowOffsetX +
'px ' +
shadowOffsetY +
'px ' +
shadowBlur +
'px ' +
shadowColor
)
each$7(['decoration', 'align'], function (name) {
var val = textStyleModel.get(name)
val && cssText.push('text-' + name + ':' + val)
})
return cssText.join(';')
}
function assembleCssText(tooltipModel, enableTransition, onlyFade) {
var cssText = []
var transitionDuration = tooltipModel.get('transitionDuration')
var backgroundColor = tooltipModel.get('backgroundColor')
var shadowBlur = tooltipModel.get('shadowBlur')
var shadowColor = tooltipModel.get('shadowColor')
var shadowOffsetX = tooltipModel.get('shadowOffsetX')
var shadowOffsetY = tooltipModel.get('shadowOffsetY')
var textStyleModel = tooltipModel.getModel('textStyle')
var padding = getPaddingFromTooltipModel(tooltipModel, 'html')
var boxShadow =
shadowOffsetX +
'px ' +
shadowOffsetY +
'px ' +
shadowBlur +
'px ' +
shadowColor
cssText.push('box-shadow:' + boxShadow) // Animation transition. Do not animate when transitionDuration is 0.
enableTransition &&
transitionDuration &&
cssText.push(assembleTransition(transitionDuration, onlyFade))
if (backgroundColor) {
cssText.push('background-color:' + backgroundColor)
} // Border style
each$7(['width', 'color', 'radius'], function (name) {
var borderName = 'border-' + name
var camelCase = toCamelCase(borderName)
var val = tooltipModel.get(camelCase)
val != null &&
cssText.push(
borderName + ':' + val + (name === 'color' ? '' : 'px')
)
}) // Text style
cssText.push(assembleFont(textStyleModel)) // Padding
if (padding != null) {
cssText.push(
'padding:' + normalizeCssArray(padding).join('px ') + 'px'
)
}
return cssText.join(';') + ';'
} // If not able to make, do not modify the input `out`.
function makeStyleCoord$1(out, zr, appendToBody, zrX, zrY) {
var zrPainter = zr && zr.painter
if (appendToBody) {
var zrViewportRoot = zrPainter && zrPainter.getViewportRoot()
if (zrViewportRoot) {
// Some APPs might use scale on body, so we support CSS transform here.
transformLocalCoord(
out,
zrViewportRoot,
document.body,
zrX,
zrY
)
}
} else {
out[0] = zrX
out[1] = zrY // xy should be based on canvas root. But tooltipContent is
// the sibling of canvas root. So padding of ec container
// should be considered here.
var viewportRootOffset =
zrPainter && zrPainter.getViewportRootOffset()
if (viewportRootOffset) {
out[0] += viewportRootOffset.offsetLeft
out[1] += viewportRootOffset.offsetTop
}
}
out[2] = out[0] / zr.getWidth()
out[3] = out[1] / zr.getHeight()
}
var TooltipHTMLContent = /** @class */ (function () {
function TooltipHTMLContent(container, api, opt) {
this._show = false
this._styleCoord = [0, 0, 0, 0]
this._enterable = true
this._firstShow = true
this._longHide = true
if (env$1.wxa) {
return null
}
var el = document.createElement('div') // TODO: TYPE
el.domBelongToZr = true
this.el = el
var zr = (this._zr = api.getZr())
var appendToBody = (this._appendToBody = opt && opt.appendToBody)
makeStyleCoord$1(
this._styleCoord,
zr,
appendToBody,
api.getWidth() / 2,
api.getHeight() / 2
)
if (appendToBody) {
document.body.appendChild(el)
} else {
container.appendChild(el)
}
this._container = container // FIXME
// Is it needed to trigger zr event manually if
// the browser do not support `pointer-events: none`.
var self = this
el.onmouseenter = function () {
// clear the timeout in hideLater and keep showing tooltip
if (self._enterable) {
clearTimeout(self._hideTimeout)
self._show = true
}
self._inContent = true
}
el.onmousemove = function (e) {
e = e || window.event
if (!self._enterable) {
// `pointer-events: none` is set to tooltip content div
// if `enterable` is set as `false`, and `el.onmousemove`
// can not be triggered. But in browser that do not
// support `pointer-events`, we need to do this:
// Try trigger zrender event to avoid mouse
// in and out shape too frequently
var handler = zr.handler
var zrViewportRoot = zr.painter.getViewportRoot()
normalizeEvent(zrViewportRoot, e, true)
handler.dispatch('mousemove', e)
}
}
el.onmouseleave = function () {
// set `_inContent` to `false` before `hideLater`
self._inContent = false
if (self._enterable) {
if (self._show) {
self.hideLater(self._hideDelay)
}
}
}
}
/**
* Update when tooltip is rendered
*/ TooltipHTMLContent.prototype.update = function (tooltipModel) {
// FIXME
// Move this logic to ec main?
var container = this._container
var position = getComputedStyle(container, 'position')
var domStyle = container.style
if (domStyle.position !== 'absolute' && position !== 'absolute') {
domStyle.position = 'relative'
} // move tooltip if chart resized
var alwaysShowContent = tooltipModel.get('alwaysShowContent')
alwaysShowContent && this._moveIfResized() // update className
this.el.className = tooltipModel.get('className') || '' // Hide the tooltip
// PENDING
// this.hide();
}
TooltipHTMLContent.prototype.show = function (
tooltipModel,
nearPointColor
) {
clearTimeout(this._hideTimeout)
clearTimeout(this._longHideTimeout)
var el = this.el
var style = el.style
var styleCoord = this._styleCoord
if (!el.innerHTML) {
style.display = 'none'
} else {
style.cssText =
gCssText +
assembleCssText(
tooltipModel,
!this._firstShow,
this._longHide
) + // initial transform
assembleTransform(styleCoord[0], styleCoord[1], true) +
('border-color:' +
convertToColorString(nearPointColor) +
';') +
(tooltipModel.get('extraCssText') || '') + // If mouse occasionally move over the tooltip, a mouseout event will be
// triggered by canvas, and cause some unexpectable result like dragging
// stop, "unfocusAdjacency". Here `pointer-events: none` is used to solve
// it. Although it is not supported by IE8~IE10, fortunately it is a rare
// scenario.
(';pointer-events:' + (this._enterable ? 'auto' : 'none'))
}
this._show = true
this._firstShow = false
this._longHide = false
}
TooltipHTMLContent.prototype.setContent = function (
content,
markers,
tooltipModel,
borderColor,
arrowPosition
) {
var el = this.el
if (content == null) {
el.innerHTML = ''
return
}
var arrow = ''
if (
isString(arrowPosition) &&
tooltipModel.get('trigger') === 'item' &&
!shouldTooltipConfine(tooltipModel)
) {
arrow = assembleArrow(tooltipModel, borderColor, arrowPosition)
}
if (isString(content)) {
el.innerHTML = content + arrow
} else if (content) {
// Clear previous
el.innerHTML = ''
if (!isArray(content)) {
content = [content]
}
for (var i = 0; i < content.length; i++) {
if (isDom(content[i]) && content[i].parentNode !== el) {
el.appendChild(content[i])
}
} // no arrow if empty
if (arrow && el.childNodes.length) {
// no need to create a new parent element, but it's not supported by IE 10 and older.
// const arrowEl = document.createRange().createContextualFragment(arrow);
var arrowEl = document.createElement('div')
arrowEl.innerHTML = arrow
el.appendChild(arrowEl)
}
}
}
TooltipHTMLContent.prototype.setEnterable = function (enterable) {
this._enterable = enterable
}
TooltipHTMLContent.prototype.getSize = function () {
var el = this.el
return [el.offsetWidth, el.offsetHeight]
}
TooltipHTMLContent.prototype.moveTo = function (zrX, zrY) {
var styleCoord = this._styleCoord
makeStyleCoord$1(
styleCoord,
this._zr,
this._appendToBody,
zrX,
zrY
)
if (styleCoord[0] != null && styleCoord[1] != null) {
var style_1 = this.el.style
var transforms = assembleTransform(styleCoord[0], styleCoord[1])
each$7(transforms, function (transform) {
style_1[transform[0]] = transform[1]
})
}
}
/**
* when `alwaysShowContent` is true,
* move the tooltip after chart resized
*/ TooltipHTMLContent.prototype._moveIfResized = function () {
// The ratio of left to width
var ratioX = this._styleCoord[2] // The ratio of top to height
var ratioY = this._styleCoord[3]
this.moveTo(
ratioX * this._zr.getWidth(),
ratioY * this._zr.getHeight()
)
}
TooltipHTMLContent.prototype.hide = function () {
var _this = this
var style = this.el.style
style.visibility = 'hidden'
style.opacity = '0'
env$1.transform3dSupported && (style.willChange = '')
this._show = false
this._longHideTimeout = setTimeout(function () {
return (_this._longHide = true)
}, 500)
}
TooltipHTMLContent.prototype.hideLater = function (time) {
if (this._show && !(this._inContent && this._enterable)) {
if (time) {
this._hideDelay = time // Set show false to avoid invoke hideLater multiple times
this._show = false
this._hideTimeout = setTimeout(bind$1(this.hide, this), time)
} else {
this.hide()
}
}
}
TooltipHTMLContent.prototype.isShow = function () {
return this._show
}
TooltipHTMLContent.prototype.dispose = function () {
this.el.parentNode.removeChild(this.el)
}
return TooltipHTMLContent
})()
var TooltipHTMLContent$1 = TooltipHTMLContent
var TooltipRichContent = /** @class */ (function () {
function TooltipRichContent(api) {
this._show = false
this._styleCoord = [0, 0, 0, 0]
this._enterable = true
this._zr = api.getZr()
makeStyleCoord(
this._styleCoord,
this._zr,
api.getWidth() / 2,
api.getHeight() / 2
)
}
/**
* Update when tooltip is rendered
*/ TooltipRichContent.prototype.update = function (tooltipModel) {
var alwaysShowContent = tooltipModel.get('alwaysShowContent')
alwaysShowContent && this._moveIfResized()
}
TooltipRichContent.prototype.show = function () {
if (this._hideTimeout) {
clearTimeout(this._hideTimeout)
}
this.el.show()
this._show = true
}
/**
* Set tooltip content
*/ TooltipRichContent.prototype.setContent = function (
content,
markupStyleCreator,
tooltipModel,
borderColor,
arrowPosition
) {
var _this = this
if (isObject$2(content)) {
throwError('')
}
if (this.el) {
this._zr.remove(this.el)
}
var textStyleModel = tooltipModel.getModel('textStyle')
this.el = new ZRText$1({
style: {
rich: markupStyleCreator.richTextStyles,
text: content,
lineHeight: 22,
borderWidth: 1,
borderColor: borderColor,
textShadowColor: textStyleModel.get('textShadowColor'),
fill: tooltipModel.get(['textStyle', 'color']),
padding: getPaddingFromTooltipModel(tooltipModel, 'richText'),
verticalAlign: 'top',
align: 'left'
},
z: tooltipModel.get('z')
})
each$7(
[
'backgroundColor',
'borderRadius',
'shadowColor',
'shadowBlur',
'shadowOffsetX',
'shadowOffsetY'
],
function (propName) {
_this.el.style[propName] = tooltipModel.get(propName)
}
)
each$7(
['textShadowBlur', 'textShadowOffsetX', 'textShadowOffsetY'],
function (propName) {
_this.el.style[propName] = textStyleModel.get(propName) || 0
}
)
this._zr.add(this.el)
var self = this
this.el.on('mouseover', function () {
// clear the timeout in hideLater and keep showing tooltip
if (self._enterable) {
clearTimeout(self._hideTimeout)
self._show = true
}
self._inContent = true
})
this.el.on('mouseout', function () {
if (self._enterable) {
if (self._show) {
self.hideLater(self._hideDelay)
}
}
self._inContent = false
})
}
TooltipRichContent.prototype.setEnterable = function (enterable) {
this._enterable = enterable
}
TooltipRichContent.prototype.getSize = function () {
var el = this.el
var bounding = this.el.getBoundingRect() // bounding rect does not include shadow. For renderMode richText,
// if overflow, it will be cut. So calculate them accurately.
var shadowOuterSize = calcShadowOuterSize(el.style)
return [
bounding.width + shadowOuterSize.left + shadowOuterSize.right,
bounding.height + shadowOuterSize.top + shadowOuterSize.bottom
]
}
TooltipRichContent.prototype.moveTo = function (x, y) {
var el = this.el
if (el) {
var styleCoord = this._styleCoord
makeStyleCoord(styleCoord, this._zr, x, y)
x = styleCoord[0]
y = styleCoord[1]
var style = el.style
var borderWidth = mathMaxWith0(style.borderWidth || 0)
var shadowOuterSize = calcShadowOuterSize(style) // rich text x, y do not include border.
el.x = x + borderWidth + shadowOuterSize.left
el.y = y + borderWidth + shadowOuterSize.top
el.markRedraw()
}
}
/**
* when `alwaysShowContent` is true,
* move the tooltip after chart resized
*/ TooltipRichContent.prototype._moveIfResized = function () {
// The ratio of left to width
var ratioX = this._styleCoord[2] // The ratio of top to height
var ratioY = this._styleCoord[3]
this.moveTo(
ratioX * this._zr.getWidth(),
ratioY * this._zr.getHeight()
)
}
TooltipRichContent.prototype.hide = function () {
if (this.el) {
this.el.hide()
}
this._show = false
}
TooltipRichContent.prototype.hideLater = function (time) {
if (this._show && !(this._inContent && this._enterable)) {
if (time) {
this._hideDelay = time // Set show false to avoid invoke hideLater multiple times
this._show = false
this._hideTimeout = setTimeout(bind$1(this.hide, this), time)
} else {
this.hide()
}
}
}
TooltipRichContent.prototype.isShow = function () {
return this._show
}
TooltipRichContent.prototype.dispose = function () {
this._zr.remove(this.el)
}
return TooltipRichContent
})()
function mathMaxWith0(val) {
return Math.max(0, val)
}
function calcShadowOuterSize(style) {
var shadowBlur = mathMaxWith0(style.shadowBlur || 0)
var shadowOffsetX = mathMaxWith0(style.shadowOffsetX || 0)
var shadowOffsetY = mathMaxWith0(style.shadowOffsetY || 0)
return {
left: mathMaxWith0(shadowBlur - shadowOffsetX),
right: mathMaxWith0(shadowBlur + shadowOffsetX),
top: mathMaxWith0(shadowBlur - shadowOffsetY),
bottom: mathMaxWith0(shadowBlur + shadowOffsetY)
}
}
function makeStyleCoord(out, zr, zrX, zrY) {
out[0] = zrX
out[1] = zrY
out[2] = out[0] / zr.getWidth()
out[3] = out[1] / zr.getHeight()
}
var TooltipRichContent$1 = TooltipRichContent
var proxyRect = new Rect$2({
shape: { x: -1, y: -1, width: 2, height: 2 }
})
var TooltipView = /** @class */ (function (_super) {
__extends(TooltipView, _super)
function TooltipView() {
var _this =
(_super !== null && _super.apply(this, arguments)) || this
_this.type = TooltipView.type
return _this
}
TooltipView.prototype.init = function (ecModel, api) {
if (env$1.node || !api.getDom()) {
return
}
var tooltipModel = ecModel.getComponent('tooltip')
var renderMode = (this._renderMode = getTooltipRenderMode(
tooltipModel.get('renderMode')
))
this._tooltipContent =
renderMode === 'richText'
? new TooltipRichContent$1(api)
: new TooltipHTMLContent$1(api.getDom(), api, {
appendToBody: tooltipModel.get('appendToBody', true)
})
}
TooltipView.prototype.render = function (
tooltipModel,
ecModel,
api
) {
if (env$1.node || !api.getDom()) {
return
} // Reset
this.group.removeAll()
this._tooltipModel = tooltipModel
this._ecModel = ecModel
this._api = api
/**
* @private
* @type {boolean}
*/ this._alwaysShowContent =
tooltipModel.get('alwaysShowContent')
var tooltipContent = this._tooltipContent
tooltipContent.update(tooltipModel)
tooltipContent.setEnterable(tooltipModel.get('enterable'))
this._initGlobalListener()
this._keepShow() // PENDING
// `mousemove` event will be triggered very frequently when the mouse moves fast,
// which causes that the `updatePosition` function was also called frequently.
// In Chrome with devtools open and Firefox, tooltip looks laggy and shakes. See #14695 #16101
// To avoid frequent triggering,
// consider throttling it in 50ms when transition is enabled
if (
this._renderMode !== 'richText' &&
tooltipModel.get('transitionDuration')
) {
createOrUpdate(this, '_updatePosition', 50, 'fixRate')
} else {
clear$1(this, '_updatePosition')
}
}
TooltipView.prototype._initGlobalListener = function () {
var tooltipModel = this._tooltipModel
var triggerOn = tooltipModel.get('triggerOn')
register(
'itemTooltip',
this._api,
bind$1(function (currTrigger, e, dispatchAction) {
// If 'none', it is not controlled by mouse totally.
if (triggerOn !== 'none') {
if (triggerOn.indexOf(currTrigger) >= 0) {
this._tryShow(e, dispatchAction)
} else if (currTrigger === 'leave') {
this._hide(dispatchAction)
}
}
}, this)
)
}
TooltipView.prototype._keepShow = function () {
var tooltipModel = this._tooltipModel
var ecModel = this._ecModel
var api = this._api
var triggerOn = tooltipModel.get('triggerOn') // Try to keep the tooltip show when refreshing
if (
this._lastX != null &&
this._lastY != null && // When user is willing to control tooltip totally using API,
// self.manuallyShowTip({x, y}) might cause tooltip hide,
// which is not expected.
triggerOn !== 'none' &&
triggerOn !== 'click'
) {
var self_1 = this
clearTimeout(this._refreshUpdateTimeout)
this._refreshUpdateTimeout = setTimeout(function () {
// Show tip next tick after other charts are rendered
// In case highlight action has wrong result
// FIXME
!api.isDisposed() &&
self_1.manuallyShowTip(tooltipModel, ecModel, api, {
x: self_1._lastX,
y: self_1._lastY,
dataByCoordSys: self_1._lastDataByCoordSys
})
})
}
}
/**
* Show tip manually by
* dispatchAction({
* type: 'showTip',
* x: 10,
* y: 10
* });
* Or
* dispatchAction({
* type: 'showTip',
* seriesIndex: 0,
* dataIndex or dataIndexInside or name
* });
*
* TODO Batch
*/ TooltipView.prototype.manuallyShowTip = function (
tooltipModel,
ecModel,
api,
payload
) {
if (payload.from === this.uid || env$1.node || !api.getDom()) {
return
}
var dispatchAction = makeDispatchAction(payload, api) // Reset ticket
this._ticket = '' // When triggered from axisPointer.
var dataByCoordSys = payload.dataByCoordSys
var cmptRef = findComponentReference(payload, ecModel, api)
if (cmptRef) {
var rect = cmptRef.el.getBoundingRect().clone()
rect.applyTransform(cmptRef.el.transform)
this._tryShow(
{
offsetX: rect.x + rect.width / 2,
offsetY: rect.y + rect.height / 2,
target: cmptRef.el,
position: payload.position, // When manully trigger, the mouse is not on the el, so we'd better to
// position tooltip on the bottom of the el and display arrow is possible.
positionDefault: 'bottom'
},
dispatchAction
)
} else if (
payload.tooltip &&
payload.x != null &&
payload.y != null
) {
var el = proxyRect
el.x = payload.x
el.y = payload.y
el.update()
getECData(el).tooltipConfig = {
name: null,
option: payload.tooltip
} // Manually show tooltip while view is not using zrender elements.
this._tryShow(
{ offsetX: payload.x, offsetY: payload.y, target: el },
dispatchAction
)
} else if (dataByCoordSys) {
this._tryShow(
{
offsetX: payload.x,
offsetY: payload.y,
position: payload.position,
dataByCoordSys: dataByCoordSys,
tooltipOption: payload.tooltipOption
},
dispatchAction
)
} else if (payload.seriesIndex != null) {
if (
this._manuallyAxisShowTip(tooltipModel, ecModel, api, payload)
) {
return
}
var pointInfo = findPointFromSeries(payload, ecModel)
var cx = pointInfo.point[0]
var cy = pointInfo.point[1]
if (cx != null && cy != null) {
this._tryShow(
{
offsetX: cx,
offsetY: cy,
target: pointInfo.el,
position: payload.position, // When manully trigger, the mouse is not on the el, so we'd better to
// position tooltip on the bottom of the el and display arrow is possible.
positionDefault: 'bottom'
},
dispatchAction
)
}
} else if (payload.x != null && payload.y != null) {
// FIXME
// should wrap dispatchAction like `axisPointer/globalListener` ?
api.dispatchAction({
type: 'updateAxisPointer',
x: payload.x,
y: payload.y
})
this._tryShow(
{
offsetX: payload.x,
offsetY: payload.y,
position: payload.position,
target: api.getZr().findHover(payload.x, payload.y).target
},
dispatchAction
)
}
}
TooltipView.prototype.manuallyHideTip = function (
tooltipModel,
ecModel,
api,
payload
) {
var tooltipContent = this._tooltipContent
if (!this._alwaysShowContent && this._tooltipModel) {
tooltipContent.hideLater(this._tooltipModel.get('hideDelay'))
}
this._lastX = this._lastY = this._lastDataByCoordSys = null
if (payload.from !== this.uid) {
this._hide(makeDispatchAction(payload, api))
}
} // Be compatible with previous design, that is, when tooltip.type is 'axis' and
// dispatchAction 'showTip' with seriesIndex and dataIndex will trigger axis pointer
// and tooltip.
TooltipView.prototype._manuallyAxisShowTip = function (
tooltipModel,
ecModel,
api,
payload
) {
var seriesIndex = payload.seriesIndex
var dataIndex = payload.dataIndex // @ts-ignore
var coordSysAxesInfo =
ecModel.getComponent('axisPointer').coordSysAxesInfo
if (
seriesIndex == null ||
dataIndex == null ||
coordSysAxesInfo == null
) {
return
}
var seriesModel = ecModel.getSeriesByIndex(seriesIndex)
if (!seriesModel) {
return
}
var data = seriesModel.getData()
var tooltipCascadedModel = buildTooltipModel(
[
data.getItemModel(dataIndex),
seriesModel,
(seriesModel.coordinateSystem || {}).model
],
this._tooltipModel
)
if (tooltipCascadedModel.get('trigger') !== 'axis') {
return
}
api.dispatchAction({
type: 'updateAxisPointer',
seriesIndex: seriesIndex,
dataIndex: dataIndex,
position: payload.position
})
return true
}
TooltipView.prototype._tryShow = function (e, dispatchAction) {
var el = e.target
var tooltipModel = this._tooltipModel
if (!tooltipModel) {
return
} // Save mouse x, mouse y. So we can try to keep showing the tip if chart is refreshed
this._lastX = e.offsetX
this._lastY = e.offsetY
var dataByCoordSys = e.dataByCoordSys
if (dataByCoordSys && dataByCoordSys.length) {
this._showAxisTooltip(dataByCoordSys, e)
} else if (el) {
this._lastDataByCoordSys = null
var seriesDispatcher_1
var cmptDispatcher_1
findEventDispatcher(
el,
function (target) {
// Always show item tooltip if mouse is on the element with dataIndex
if (getECData(target).dataIndex != null) {
seriesDispatcher_1 = target
return true
} // Tooltip provided directly. Like legend.
if (getECData(target).tooltipConfig != null) {
cmptDispatcher_1 = target
return true
}
},
true
)
if (seriesDispatcher_1) {
this._showSeriesItemTooltip(
e,
seriesDispatcher_1,
dispatchAction
)
} else if (cmptDispatcher_1) {
this._showComponentItemTooltip(
e,
cmptDispatcher_1,
dispatchAction
)
} else {
this._hide(dispatchAction)
}
} else {
this._lastDataByCoordSys = null
this._hide(dispatchAction)
}
}
TooltipView.prototype._showOrMove = function (tooltipModel, cb) {
// showDelay is used in this case: tooltip.enterable is set
// as true. User intent to move mouse into tooltip and click
// something. `showDelay` makes it easier to enter the content
// but tooltip do not move immediately.
var delay = tooltipModel.get('showDelay')
cb = bind$1(cb, this)
clearTimeout(this._showTimout)
delay > 0 ? (this._showTimout = setTimeout(cb, delay)) : cb()
}
TooltipView.prototype._showAxisTooltip = function (
dataByCoordSys,
e
) {
var ecModel = this._ecModel
var globalTooltipModel = this._tooltipModel
var point = [e.offsetX, e.offsetY]
var singleTooltipModel = buildTooltipModel(
[e.tooltipOption],
globalTooltipModel
)
var renderMode = this._renderMode
var cbParamsList = []
var articleMarkup = createTooltipMarkup('section', {
blocks: [],
noHeader: true
}) // Only for legacy: `Serise['formatTooltip']` returns a string.
var markupTextArrLegacy = []
var markupStyleCreator = new TooltipMarkupStyleCreator()
each$7(dataByCoordSys, function (itemCoordSys) {
each$7(itemCoordSys.dataByAxis, function (axisItem) {
var axisModel = ecModel.getComponent(
axisItem.axisDim + 'Axis',
axisItem.axisIndex
)
var axisValue = axisItem.value
if (!axisModel || axisValue == null) {
return
}
var axisValueLabel = getValueLabel(
axisValue,
axisModel.axis,
ecModel,
axisItem.seriesDataIndices,
axisItem.valueLabelOpt
)
var axisSectionMarkup = createTooltipMarkup('section', {
header: axisValueLabel,
noHeader: !trim$1(axisValueLabel),
sortBlocks: true,
blocks: []
})
articleMarkup.blocks.push(axisSectionMarkup)
each$7(axisItem.seriesDataIndices, function (idxItem) {
var series = ecModel.getSeriesByIndex(idxItem.seriesIndex)
var dataIndex = idxItem.dataIndexInside
var cbParams = series.getDataParams(dataIndex) // Can't find data.
if (cbParams.dataIndex < 0) {
return
}
cbParams.axisDim = axisItem.axisDim
cbParams.axisIndex = axisItem.axisIndex
cbParams.axisType = axisItem.axisType
cbParams.axisId = axisItem.axisId
cbParams.axisValue = getAxisRawValue(axisModel.axis, {
value: axisValue
})
cbParams.axisValueLabel = axisValueLabel // Pre-create marker style for makers. Users can assemble richText
// text in `formatter` callback and use those markers style.
cbParams.marker = markupStyleCreator.makeTooltipMarker(
'item',
convertToColorString(cbParams.color),
renderMode
)
var seriesTooltipResult = normalizeTooltipFormatResult(
series.formatTooltip(dataIndex, true, null)
)
var frag = seriesTooltipResult.frag
if (frag) {
var valueFormatter = buildTooltipModel(
[series],
globalTooltipModel
).get('valueFormatter')
axisSectionMarkup.blocks.push(
valueFormatter
? extend({ valueFormatter: valueFormatter }, frag)
: frag
)
}
if (seriesTooltipResult.text) {
markupTextArrLegacy.push(seriesTooltipResult.text)
}
cbParamsList.push(cbParams)
})
})
}) // In most cases, the second axis is displays upper on the first one.
// So we reverse it to look better.
articleMarkup.blocks.reverse()
markupTextArrLegacy.reverse()
var positionExpr = e.position
var orderMode = singleTooltipModel.get('order')
var builtMarkupText = buildTooltipMarkup(
articleMarkup,
markupStyleCreator,
renderMode,
orderMode,
ecModel.get('useUTC'),
singleTooltipModel.get('textStyle')
)
builtMarkupText && markupTextArrLegacy.unshift(builtMarkupText)
var blockBreak = renderMode === 'richText' ? '\n\n' : '
'
var allMarkupText = markupTextArrLegacy.join(blockBreak)
this._showOrMove(singleTooltipModel, function () {
if (
this._updateContentNotChangedOnAxis(
dataByCoordSys,
cbParamsList
)
) {
this._updatePosition(
singleTooltipModel,
positionExpr,
point[0],
point[1],
this._tooltipContent,
cbParamsList
)
} else {
this._showTooltipContent(
singleTooltipModel,
allMarkupText,
cbParamsList,
Math.random() + '',
point[0],
point[1],
positionExpr,
null,
markupStyleCreator
)
}
}) // Do not trigger events here, because this branch only be entered
// from dispatchAction.
}
TooltipView.prototype._showSeriesItemTooltip = function (
e,
dispatcher,
dispatchAction
) {
var ecModel = this._ecModel
var ecData = getECData(dispatcher) // Use dataModel in element if possible
// Used when mouseover on a element like markPoint or edge
// In which case, the data is not main data in series.
var seriesIndex = ecData.seriesIndex
var seriesModel = ecModel.getSeriesByIndex(seriesIndex) // For example, graph link.
var dataModel = ecData.dataModel || seriesModel
var dataIndex = ecData.dataIndex
var dataType = ecData.dataType
var data = dataModel.getData(dataType)
var renderMode = this._renderMode
var positionDefault = e.positionDefault
var tooltipModel = buildTooltipModel(
[
data.getItemModel(dataIndex),
dataModel,
seriesModel && (seriesModel.coordinateSystem || {}).model
],
this._tooltipModel,
positionDefault ? { position: positionDefault } : null
)
var tooltipTrigger = tooltipModel.get('trigger')
if (tooltipTrigger != null && tooltipTrigger !== 'item') {
return
}
var params = dataModel.getDataParams(dataIndex, dataType)
var markupStyleCreator = new TooltipMarkupStyleCreator() // Pre-create marker style for makers. Users can assemble richText
// text in `formatter` callback and use those markers style.
params.marker = markupStyleCreator.makeTooltipMarker(
'item',
convertToColorString(params.color),
renderMode
)
var seriesTooltipResult = normalizeTooltipFormatResult(
dataModel.formatTooltip(dataIndex, false, dataType)
)
var orderMode = tooltipModel.get('order')
var valueFormatter = tooltipModel.get('valueFormatter')
var frag = seriesTooltipResult.frag
var markupText = frag
? buildTooltipMarkup(
valueFormatter
? extend({ valueFormatter: valueFormatter }, frag)
: frag,
markupStyleCreator,
renderMode,
orderMode,
ecModel.get('useUTC'),
tooltipModel.get('textStyle')
)
: seriesTooltipResult.text
var asyncTicket = 'item_' + dataModel.name + '_' + dataIndex
this._showOrMove(tooltipModel, function () {
this._showTooltipContent(
tooltipModel,
markupText,
params,
asyncTicket,
e.offsetX,
e.offsetY,
e.position,
e.target,
markupStyleCreator
)
}) // FIXME
// duplicated showtip if manuallyShowTip is called from dispatchAction.
dispatchAction({
type: 'showTip',
dataIndexInside: dataIndex,
dataIndex: data.getRawIndex(dataIndex),
seriesIndex: seriesIndex,
from: this.uid
})
}
TooltipView.prototype._showComponentItemTooltip = function (
e,
el,
dispatchAction
) {
var ecData = getECData(el)
var tooltipConfig = ecData.tooltipConfig
var tooltipOpt = tooltipConfig.option || {}
if (isString(tooltipOpt)) {
var content = tooltipOpt
tooltipOpt = {
content: content, // Fixed formatter
formatter: content
}
}
var tooltipModelCascade = [tooltipOpt]
var cmpt = this._ecModel.getComponent(
ecData.componentMainType,
ecData.componentIndex
)
if (cmpt) {
tooltipModelCascade.push(cmpt)
} // In most cases, component tooltip formatter has different params with series tooltip formatter,
// so that they can not share the same formatter. Since the global tooltip formatter is used for series
// by convension, we do not use it as the default formatter for component.
tooltipModelCascade.push({ formatter: tooltipOpt.content })
var positionDefault = e.positionDefault
var subTooltipModel = buildTooltipModel(
tooltipModelCascade,
this._tooltipModel,
positionDefault ? { position: positionDefault } : null
)
var defaultHtml = subTooltipModel.get('content')
var asyncTicket = Math.random() + '' // PENDING: this case do not support richText style yet.
var markupStyleCreator = new TooltipMarkupStyleCreator() // Do not check whether `trigger` is 'none' here, because `trigger`
// only works on coordinate system. In fact, we have not found case
// that requires setting `trigger` nothing on component yet.
this._showOrMove(subTooltipModel, function () {
// Use formatterParams from element defined in component
// Avoid users modify it.
var formatterParams = clone$3(
subTooltipModel.get('formatterParams') || {}
)
this._showTooltipContent(
subTooltipModel,
defaultHtml,
formatterParams,
asyncTicket,
e.offsetX,
e.offsetY,
e.position,
el,
markupStyleCreator
)
}) // If not dispatch showTip, tip may be hide triggered by axis.
dispatchAction({ type: 'showTip', from: this.uid })
}
TooltipView.prototype._showTooltipContent = function (
// Use Model insteadof TooltipModel because this model may be from series or other options.
// Instead of top level tooltip.
tooltipModel,
defaultHtml,
params,
asyncTicket,
x,
y,
positionExpr,
el,
markupStyleCreator
) {
// Reset ticket
this._ticket = ''
if (
!tooltipModel.get('showContent') ||
!tooltipModel.get('show')
) {
return
}
var tooltipContent = this._tooltipContent
tooltipContent.setEnterable(tooltipModel.get('enterable'))
var formatter = tooltipModel.get('formatter')
positionExpr = positionExpr || tooltipModel.get('position')
var html = defaultHtml
var nearPoint = this._getNearestPoint(
[x, y],
params,
tooltipModel.get('trigger'),
tooltipModel.get('borderColor')
)
var nearPointColor = nearPoint.color
if (formatter) {
if (isString(formatter)) {
var useUTC = tooltipModel.ecModel.get('useUTC')
var params0 = isArray(params) ? params[0] : params
var isTimeAxis =
params0 &&
params0.axisType &&
params0.axisType.indexOf('time') >= 0
html = formatter
if (isTimeAxis) {
html = format(params0.axisValue, html, useUTC)
}
html = formatTpl(html, params, true)
} else if (isFunction(formatter)) {
var callback = bind$1(function (cbTicket, html) {
if (cbTicket === this._ticket) {
tooltipContent.setContent(
html,
markupStyleCreator,
tooltipModel,
nearPointColor,
positionExpr
)
this._updatePosition(
tooltipModel,
positionExpr,
x,
y,
tooltipContent,
params,
el
)
}
}, this)
this._ticket = asyncTicket
html = formatter(params, asyncTicket, callback)
} else {
html = formatter
}
}
tooltipContent.setContent(
html,
markupStyleCreator,
tooltipModel,
nearPointColor,
positionExpr
)
tooltipContent.show(tooltipModel, nearPointColor)
this._updatePosition(
tooltipModel,
positionExpr,
x,
y,
tooltipContent,
params,
el
)
}
TooltipView.prototype._getNearestPoint = function (
point,
tooltipDataParams,
trigger,
borderColor
) {
if (trigger === 'axis' || isArray(tooltipDataParams)) {
return {
color:
borderColor ||
(this._renderMode === 'html' ? '#fff' : 'none')
}
}
if (!isArray(tooltipDataParams)) {
return {
color:
borderColor ||
tooltipDataParams.color ||
tooltipDataParams.borderColor
}
}
}
TooltipView.prototype._updatePosition = function (
tooltipModel,
positionExpr,
x, // Mouse x
y, // Mouse y
content,
params,
el
) {
var viewWidth = this._api.getWidth()
var viewHeight = this._api.getHeight()
positionExpr = positionExpr || tooltipModel.get('position')
var contentSize = content.getSize()
var align = tooltipModel.get('align')
var vAlign = tooltipModel.get('verticalAlign')
var rect = el && el.getBoundingRect().clone()
el && rect.applyTransform(el.transform)
if (isFunction(positionExpr)) {
// Callback of position can be an array or a string specify the position
positionExpr = positionExpr([x, y], params, content.el, rect, {
viewSize: [viewWidth, viewHeight],
contentSize: contentSize.slice()
})
}
if (isArray(positionExpr)) {
x = parsePercent(positionExpr[0], viewWidth)
y = parsePercent(positionExpr[1], viewHeight)
} else if (isObject$2(positionExpr)) {
var boxLayoutPosition = positionExpr
boxLayoutPosition.width = contentSize[0]
boxLayoutPosition.height = contentSize[1]
var layoutRect = getLayoutRect(boxLayoutPosition, {
width: viewWidth,
height: viewHeight
})
x = layoutRect.x
y = layoutRect.y
align = null // When positionExpr is left/top/right/bottom,
// align and verticalAlign will not work.
vAlign = null
} // Specify tooltip position by string 'top' 'bottom' 'left' 'right' around graphic element
else if (isString(positionExpr) && el) {
var pos = calcTooltipPosition(
positionExpr,
rect,
contentSize,
tooltipModel.get('borderWidth')
)
x = pos[0]
y = pos[1]
} else {
var pos = refixTooltipPosition(
x,
y,
content,
viewWidth,
viewHeight,
align ? null : 20,
vAlign ? null : 20
)
x = pos[0]
y = pos[1]
}
align &&
(x -= isCenterAlign(align)
? contentSize[0] / 2
: align === 'right'
? contentSize[0]
: 0)
vAlign &&
(y -= isCenterAlign(vAlign)
? contentSize[1] / 2
: vAlign === 'bottom'
? contentSize[1]
: 0)
if (shouldTooltipConfine(tooltipModel)) {
var pos = confineTooltipPosition(
x,
y,
content,
viewWidth,
viewHeight
)
x = pos[0]
y = pos[1]
}
content.moveTo(x, y)
} // FIXME
// Should we remove this but leave this to user?
TooltipView.prototype._updateContentNotChangedOnAxis = function (
dataByCoordSys,
cbParamsList
) {
var lastCoordSys = this._lastDataByCoordSys
var lastCbParamsList = this._cbParamsList
var contentNotChanged =
!!lastCoordSys && lastCoordSys.length === dataByCoordSys.length
contentNotChanged &&
each$7(
lastCoordSys,
function (lastItemCoordSys, indexCoordSys) {
var lastDataByAxis = lastItemCoordSys.dataByAxis || []
var thisItemCoordSys = dataByCoordSys[indexCoordSys] || {}
var thisDataByAxis = thisItemCoordSys.dataByAxis || []
contentNotChanged =
contentNotChanged &&
lastDataByAxis.length === thisDataByAxis.length
contentNotChanged &&
each$7(lastDataByAxis, function (lastItem, indexAxis) {
var thisItem = thisDataByAxis[indexAxis] || {}
var lastIndices = lastItem.seriesDataIndices || []
var newIndices = thisItem.seriesDataIndices || []
contentNotChanged =
contentNotChanged &&
lastItem.value === thisItem.value &&
lastItem.axisType === thisItem.axisType &&
lastItem.axisId === thisItem.axisId &&
lastIndices.length === newIndices.length
contentNotChanged &&
each$7(lastIndices, function (lastIdxItem, j) {
var newIdxItem = newIndices[j]
contentNotChanged =
contentNotChanged &&
lastIdxItem.seriesIndex ===
newIdxItem.seriesIndex &&
lastIdxItem.dataIndex === newIdxItem.dataIndex
}) // check is cbParams data value changed
lastCbParamsList &&
each$7(
lastItem.seriesDataIndices,
function (idxItem) {
var seriesIdx = idxItem.seriesIndex
var cbParams = cbParamsList[seriesIdx]
var lastCbParams = lastCbParamsList[seriesIdx]
if (
cbParams &&
lastCbParams &&
lastCbParams.data !== cbParams.data
) {
contentNotChanged = false
}
}
)
})
}
)
this._lastDataByCoordSys = dataByCoordSys
this._cbParamsList = cbParamsList
return !!contentNotChanged
}
TooltipView.prototype._hide = function (dispatchAction) {
// Do not directly hideLater here, because this behavior may be prevented
// in dispatchAction when showTip is dispatched.
// FIXME
// duplicated hideTip if manuallyHideTip is called from dispatchAction.
this._lastDataByCoordSys = null
dispatchAction({ type: 'hideTip', from: this.uid })
}
TooltipView.prototype.dispose = function (ecModel, api) {
if (env$1.node || !api.getDom()) {
return
}
clear$1(this, '_updatePosition')
this._tooltipContent.dispose()
unregister('itemTooltip', api)
}
TooltipView.type = 'tooltip'
return TooltipView
})(ComponentView$1)
/**
* From top to bottom. (the last one should be globalTooltipModel);
*/ function buildTooltipModel(
modelCascade,
globalTooltipModel,
defaultTooltipOption
) {
// Last is always tooltip model.
var ecModel = globalTooltipModel.ecModel
var resultModel
if (defaultTooltipOption) {
resultModel = new Model$1(defaultTooltipOption, ecModel, ecModel)
resultModel = new Model$1(
globalTooltipModel.option,
resultModel,
ecModel
)
} else {
resultModel = globalTooltipModel
}
for (var i = modelCascade.length - 1; i >= 0; i--) {
var tooltipOpt = modelCascade[i]
if (tooltipOpt) {
if (tooltipOpt instanceof Model$1) {
tooltipOpt = tooltipOpt.get('tooltip', true)
} // In each data item tooltip can be simply write:
// {
// value: 10,
// tooltip: 'Something you need to know'
// }
if (isString(tooltipOpt)) {
tooltipOpt = { formatter: tooltipOpt }
}
if (tooltipOpt) {
resultModel = new Model$1(tooltipOpt, resultModel, ecModel)
}
}
}
return resultModel
}
function makeDispatchAction(payload, api) {
return payload.dispatchAction || bind$1(api.dispatchAction, api)
}
function refixTooltipPosition(
x,
y,
content,
viewWidth,
viewHeight,
gapH,
gapV
) {
var size = content.getSize()
var width = size[0]
var height = size[1]
if (gapH != null) {
// Add extra 2 pixels for this case:
// At present the "values" in defaut tooltip are using CSS `float: right`.
// When the right edge of the tooltip box is on the right side of the
// viewport, the `float` layout might push the "values" to the second line.
if (x + width + gapH + 2 > viewWidth) {
x -= width + gapH
} else {
x += gapH
}
}
if (gapV != null) {
if (y + height + gapV > viewHeight) {
y -= height + gapV
} else {
y += gapV
}
}
return [x, y]
}
function confineTooltipPosition(
x,
y,
content,
viewWidth,
viewHeight
) {
var size = content.getSize()
var width = size[0]
var height = size[1]
x = Math.min(x + width, viewWidth) - width
y = Math.min(y + height, viewHeight) - height
x = Math.max(x, 0)
y = Math.max(y, 0)
return [x, y]
}
function calcTooltipPosition(
position,
rect,
contentSize,
borderWidth
) {
var domWidth = contentSize[0]
var domHeight = contentSize[1]
var offset = Math.ceil(Math.SQRT2 * borderWidth) + 8
var x = 0
var y = 0
var rectWidth = rect.width
var rectHeight = rect.height
switch (position) {
case 'inside':
x = rect.x + rectWidth / 2 - domWidth / 2
y = rect.y + rectHeight / 2 - domHeight / 2
break
case 'top':
x = rect.x + rectWidth / 2 - domWidth / 2
y = rect.y - domHeight - offset
break
case 'bottom':
x = rect.x + rectWidth / 2 - domWidth / 2
y = rect.y + rectHeight + offset
break
case 'left':
x = rect.x - domWidth - offset
y = rect.y + rectHeight / 2 - domHeight / 2
break
case 'right':
x = rect.x + rectWidth + offset
y = rect.y + rectHeight / 2 - domHeight / 2
}
return [x, y]
}
function isCenterAlign(align) {
return align === 'center' || align === 'middle'
}
/**
* Find target component by payload like:
* ```js
* { legendId: 'some_id', name: 'xxx' }
* { toolboxIndex: 1, name: 'xxx' }
* { geoName: 'some_name', name: 'xxx' }
* ```
* PENDING: at present only
*
* If not found, return null/undefined.
*/ function findComponentReference(payload, ecModel, api) {
var queryOptionMap = preParseFinder(payload).queryOptionMap
var componentMainType = queryOptionMap.keys()[0]
if (!componentMainType || componentMainType === 'series') {
return
}
var queryResult = queryReferringComponents(
ecModel,
componentMainType,
queryOptionMap.get(componentMainType),
{ useDefault: false, enableAll: false, enableNone: false }
)
var model = queryResult.models[0]
if (!model) {
return
}
var view = api.getViewOfComponentModel(model)
var el
view.group.traverse(function (subEl) {
var tooltipConfig = getECData(subEl).tooltipConfig
if (tooltipConfig && tooltipConfig.name === payload.name) {
el = subEl
return true // stop
}
})
if (el) {
return {
componentMainType: componentMainType,
componentIndex: model.componentIndex,
el: el
}
}
}
var TooltipView$1 = TooltipView
function install$a(registers) {
use(install$e)
registers.registerComponentModel(TooltipModel$1)
registers.registerComponentView(TooltipView$1)
/**
* @action
* @property {string} type
* @property {number} seriesIndex
* @property {number} dataIndex
* @property {number} [x]
* @property {number} [y]
*/ registers.registerAction(
{
type: 'showTip',
event: 'showTip',
update: 'tooltip:manuallyShowTip'
},
noop
)
registers.registerAction(
{
type: 'hideTip',
event: 'hideTip',
update: 'tooltip:manuallyHideTip'
},
noop
)
}
var TitleModel = /** @class */ (function (_super) {
__extends(TitleModel, _super)
function TitleModel() {
var _this =
(_super !== null && _super.apply(this, arguments)) || this
_this.type = TitleModel.type
_this.layoutMode = { type: 'box', ignoreSize: true }
return _this
}
TitleModel.type = 'title'
TitleModel.defaultOption = {
// zlevel: 0,
z: 6,
show: true,
text: '',
target: 'blank',
subtext: '',
subtarget: 'blank',
left: 0,
top: 0,
backgroundColor: 'rgba(0,0,0,0)',
borderColor: '#ccc',
borderWidth: 0,
padding: 5,
itemGap: 10,
textStyle: { fontSize: 18, fontWeight: 'bold', color: '#464646' },
subtextStyle: { fontSize: 12, color: '#6E7079' }
}
return TitleModel
})(ComponentModel$1) // View
var TitleView = /** @class */ (function (_super) {
__extends(TitleView, _super)
function TitleView() {
var _this =
(_super !== null && _super.apply(this, arguments)) || this
_this.type = TitleView.type
return _this
}
TitleView.prototype.render = function (titleModel, ecModel, api) {
this.group.removeAll()
if (!titleModel.get('show')) {
return
}
var group = this.group
var textStyleModel = titleModel.getModel('textStyle')
var subtextStyleModel = titleModel.getModel('subtextStyle')
var textAlign = titleModel.get('textAlign')
var textVerticalAlign = retrieve2(
titleModel.get('textBaseline'),
titleModel.get('textVerticalAlign')
)
var textEl = new ZRText$1({
style: createTextStyle(
textStyleModel,
{
text: titleModel.get('text'),
fill: textStyleModel.getTextColor()
},
{ disableBox: true }
),
z2: 10
})
var textRect = textEl.getBoundingRect()
var subText = titleModel.get('subtext')
var subTextEl = new ZRText$1({
style: createTextStyle(
subtextStyleModel,
{
text: subText,
fill: subtextStyleModel.getTextColor(),
y: textRect.height + titleModel.get('itemGap'),
verticalAlign: 'top'
},
{ disableBox: true }
),
z2: 10
})
var link = titleModel.get('link')
var sublink = titleModel.get('sublink')
var triggerEvent = titleModel.get('triggerEvent', true)
textEl.silent = !link && !triggerEvent
subTextEl.silent = !sublink && !triggerEvent
if (link) {
textEl.on('click', function () {
windowOpen(link, '_' + titleModel.get('target'))
})
}
if (sublink) {
subTextEl.on('click', function () {
windowOpen(sublink, '_' + titleModel.get('subtarget'))
})
}
getECData(textEl).eventData = getECData(subTextEl).eventData =
triggerEvent
? {
componentType: 'title',
componentIndex: titleModel.componentIndex
}
: null
group.add(textEl)
subText && group.add(subTextEl) // If no subText, but add subTextEl, there will be an empty line.
var groupRect = group.getBoundingRect()
var layoutOption = titleModel.getBoxLayoutParams()
layoutOption.width = groupRect.width
layoutOption.height = groupRect.height
var layoutRect = getLayoutRect(
layoutOption,
{ width: api.getWidth(), height: api.getHeight() },
titleModel.get('padding')
) // Adjust text align based on position
if (!textAlign) {
// Align left if title is on the left. center and right is same
textAlign = titleModel.get('left') || titleModel.get('right') // @ts-ignore
if (textAlign === 'middle') {
textAlign = 'center'
} // Adjust layout by text align
if (textAlign === 'right') {
layoutRect.x += layoutRect.width
} else if (textAlign === 'center') {
layoutRect.x += layoutRect.width / 2
}
}
if (!textVerticalAlign) {
textVerticalAlign =
titleModel.get('top') || titleModel.get('bottom') // @ts-ignore
if (textVerticalAlign === 'center') {
textVerticalAlign = 'middle'
}
if (textVerticalAlign === 'bottom') {
layoutRect.y += layoutRect.height
} else if (textVerticalAlign === 'middle') {
layoutRect.y += layoutRect.height / 2
}
textVerticalAlign = textVerticalAlign || 'top'
}
group.x = layoutRect.x
group.y = layoutRect.y
group.markRedraw()
var alignStyle = {
align: textAlign,
verticalAlign: textVerticalAlign
}
textEl.setStyle(alignStyle)
subTextEl.setStyle(alignStyle) // Render background
// Get groupRect again because textAlign has been changed
groupRect = group.getBoundingRect()
var padding = layoutRect.margin
var style = titleModel.getItemStyle(['color', 'opacity'])
style.fill = titleModel.get('backgroundColor')
var rect = new Rect$2({
shape: {
x: groupRect.x - padding[3],
y: groupRect.y - padding[0],
width: groupRect.width + padding[1] + padding[3],
height: groupRect.height + padding[0] + padding[2],
r: titleModel.get('borderRadius')
},
style: style,
subPixelOptimize: true,
silent: true
})
group.add(rect)
}
TitleView.type = 'title'
return TitleView
})(ComponentView$1)
function install$9(registers) {
registers.registerComponentModel(TitleModel)
registers.registerComponentView(TitleView)
}
var getDefaultSelectorOptions = function getDefaultSelectorOptions(
ecModel,
type
) {
if (type === 'all') {
return {
type: 'all',
title: ecModel
.getLocaleModel()
.get(['legend', 'selector', 'all'])
}
} else if (type === 'inverse') {
return {
type: 'inverse',
title: ecModel
.getLocaleModel()
.get(['legend', 'selector', 'inverse'])
}
}
}
var LegendModel = /** @class */ (function (_super) {
__extends(LegendModel, _super)
function LegendModel() {
var _this =
(_super !== null && _super.apply(this, arguments)) || this
_this.type = LegendModel.type
_this.layoutMode = {
type: 'box', // legend.width/height are maxWidth/maxHeight actually,
// whereas realy width/height is calculated by its content.
// (Setting {left: 10, right: 10} does not make sense).
// So consider the case:
// `setOption({legend: {left: 10});`
// then `setOption({legend: {right: 10});`
// The previous `left` should be cleared by setting `ignoreSize`.
ignoreSize: true
}
return _this
}
LegendModel.prototype.init = function (
option,
parentModel,
ecModel
) {
this.mergeDefaultAndTheme(option, ecModel)
option.selected = option.selected || {}
this._updateSelector(option)
}
LegendModel.prototype.mergeOption = function (option, ecModel) {
_super.prototype.mergeOption.call(this, option, ecModel)
this._updateSelector(option)
}
LegendModel.prototype._updateSelector = function (option) {
var selector = option.selector
var ecModel = this.ecModel
if (selector === true) {
selector = option.selector = ['all', 'inverse']
}
if (isArray(selector)) {
each$7(selector, function (item, index) {
isString(item) && (item = { type: item })
selector[index] = merge(
item,
getDefaultSelectorOptions(ecModel, item.type)
)
})
}
}
LegendModel.prototype.optionUpdated = function () {
this._updateData(this.ecModel)
var legendData = this._data // If selectedMode is single, try to select one
if (legendData[0] && this.get('selectedMode') === 'single') {
var hasSelected = false // If has any selected in option.selected
for (var i = 0; i < legendData.length; i++) {
var name_1 = legendData[i].get('name')
if (this.isSelected(name_1)) {
// Force to unselect others
this.select(name_1)
hasSelected = true
break
}
} // Try select the first if selectedMode is single
!hasSelected && this.select(legendData[0].get('name'))
}
}
LegendModel.prototype._updateData = function (ecModel) {
var potentialData = []
var availableNames = []
ecModel.eachRawSeries(function (seriesModel) {
var seriesName = seriesModel.name
availableNames.push(seriesName)
var isPotential
if (seriesModel.legendVisualProvider) {
var provider = seriesModel.legendVisualProvider
var names = provider.getAllNames()
if (!ecModel.isSeriesFiltered(seriesModel)) {
availableNames = availableNames.concat(names)
}
if (names.length) {
potentialData = potentialData.concat(names)
} else {
isPotential = true
}
} else {
isPotential = true
}
if (isPotential && isNameSpecified(seriesModel)) {
potentialData.push(seriesModel.name)
}
})
/**
* @type {Array.}
* @private
*/ this._availableNames = availableNames // If legend.data not specified in option, use availableNames as data,
// which is convinient for user preparing option.
var rawData = this.get('data') || potentialData
var legendData = map$1(
rawData,
function (dataItem) {
// Can be string or number
if (isString(dataItem) || isNumber(dataItem)) {
dataItem = { name: dataItem }
}
return new Model$1(dataItem, this, this.ecModel)
},
this
)
/**
* @type {Array.}
* @private
*/ this._data = legendData
}
LegendModel.prototype.getData = function () {
return this._data
}
LegendModel.prototype.select = function (name) {
var selected = this.option.selected
var selectedMode = this.get('selectedMode')
if (selectedMode === 'single') {
var data = this._data
each$7(data, function (dataItem) {
selected[dataItem.get('name')] = false
})
}
selected[name] = true
}
LegendModel.prototype.unSelect = function (name) {
if (this.get('selectedMode') !== 'single') {
this.option.selected[name] = false
}
}
LegendModel.prototype.toggleSelected = function (name) {
var selected = this.option.selected // Default is true
if (!selected.hasOwnProperty(name)) {
selected[name] = true
}
this[selected[name] ? 'unSelect' : 'select'](name)
}
LegendModel.prototype.allSelect = function () {
var data = this._data
var selected = this.option.selected
each$7(data, function (dataItem) {
selected[dataItem.get('name', true)] = true
})
}
LegendModel.prototype.inverseSelect = function () {
var data = this._data
var selected = this.option.selected
each$7(data, function (dataItem) {
var name = dataItem.get('name', true) // Initially, default value is true
if (!selected.hasOwnProperty(name)) {
selected[name] = true
}
selected[name] = !selected[name]
})
}
LegendModel.prototype.isSelected = function (name) {
var selected = this.option.selected
return (
!(selected.hasOwnProperty(name) && !selected[name]) &&
indexOf(this._availableNames, name) >= 0
)
}
LegendModel.prototype.getOrient = function () {
return this.get('orient') === 'vertical'
? { index: 1, name: 'vertical' }
: { index: 0, name: 'horizontal' }
}
LegendModel.type = 'legend.plain'
LegendModel.dependencies = ['series']
LegendModel.defaultOption = {
// zlevel: 0,
z: 4,
show: true,
orient: 'horizontal',
left: 'center', // right: 'center',
top: 0, // bottom: null,
align: 'auto',
backgroundColor: 'rgba(0,0,0,0)',
borderColor: '#ccc',
borderRadius: 0,
borderWidth: 0,
padding: 5,
itemGap: 10,
itemWidth: 25,
itemHeight: 14,
symbolRotate: 'inherit',
symbolKeepAspect: true,
inactiveColor: '#ccc',
inactiveBorderColor: '#ccc',
inactiveBorderWidth: 'auto',
itemStyle: {
color: 'inherit',
opacity: 'inherit',
borderColor: 'inherit',
borderWidth: 'auto',
borderCap: 'inherit',
borderJoin: 'inherit',
borderDashOffset: 'inherit',
borderMiterLimit: 'inherit'
},
lineStyle: {
width: 'auto',
color: 'inherit',
inactiveColor: '#ccc',
inactiveWidth: 2,
opacity: 'inherit',
type: 'inherit',
cap: 'inherit',
join: 'inherit',
dashOffset: 'inherit',
miterLimit: 'inherit'
},
textStyle: { color: '#333' },
selectedMode: true,
selector: false,
selectorLabel: {
show: true,
borderRadius: 10,
padding: [3, 5, 3, 5],
fontSize: 12,
fontFamily: 'sans-serif',
color: '#666',
borderWidth: 1,
borderColor: '#666'
},
emphasis: {
selectorLabel: {
show: true,
color: '#eee',
backgroundColor: '#666'
}
},
selectorPosition: 'auto',
selectorItemGap: 7,
selectorButtonGap: 10,
tooltip: { show: false }
}
return LegendModel
})(ComponentModel$1)
var LegendModel$1 = LegendModel
var curry = curry$1
var each = each$7
var Group$1 = Group$3
var LegendView = /** @class */ (function (_super) {
__extends(LegendView, _super)
function LegendView() {
var _this =
(_super !== null && _super.apply(this, arguments)) || this
_this.type = LegendView.type
_this.newlineDisabled = false
return _this
}
LegendView.prototype.init = function () {
this.group.add((this._contentGroup = new Group$1()))
this.group.add((this._selectorGroup = new Group$1()))
this._isFirstRender = true
}
/**
* @protected
*/ LegendView.prototype.getContentGroup = function () {
return this._contentGroup
}
/**
* @protected
*/ LegendView.prototype.getSelectorGroup = function () {
return this._selectorGroup
}
/**
* @override
*/ LegendView.prototype.render = function (
legendModel,
ecModel,
api
) {
var isFirstRender = this._isFirstRender
this._isFirstRender = false
this.resetInner()
if (!legendModel.get('show', true)) {
return
}
var itemAlign = legendModel.get('align')
var orient = legendModel.get('orient')
if (!itemAlign || itemAlign === 'auto') {
itemAlign =
legendModel.get('left') === 'right' && orient === 'vertical'
? 'right'
: 'left'
} // selector has been normalized to an array in model
var selector = legendModel.get('selector', true)
var selectorPosition = legendModel.get('selectorPosition', true)
if (
selector &&
(!selectorPosition || selectorPosition === 'auto')
) {
selectorPosition = orient === 'horizontal' ? 'end' : 'start'
}
this.renderInner(
itemAlign,
legendModel,
ecModel,
api,
selector,
orient,
selectorPosition
) // Perform layout.
var positionInfo = legendModel.getBoxLayoutParams()
var viewportSize = {
width: api.getWidth(),
height: api.getHeight()
}
var padding = legendModel.get('padding')
var maxSize = getLayoutRect(positionInfo, viewportSize, padding)
var mainRect = this.layoutInner(
legendModel,
itemAlign,
maxSize,
isFirstRender,
selector,
selectorPosition
) // Place mainGroup, based on the calculated `mainRect`.
var layoutRect = getLayoutRect(
defaults(
{ width: mainRect.width, height: mainRect.height },
positionInfo
),
viewportSize,
padding
)
this.group.x = layoutRect.x - mainRect.x
this.group.y = layoutRect.y - mainRect.y
this.group.markRedraw() // Render background after group is layout.
this.group.add(
(this._backgroundEl = makeBackground(mainRect, legendModel))
)
}
LegendView.prototype.resetInner = function () {
this.getContentGroup().removeAll()
this._backgroundEl && this.group.remove(this._backgroundEl)
this.getSelectorGroup().removeAll()
}
LegendView.prototype.renderInner = function (
itemAlign,
legendModel,
ecModel,
api,
selector,
orient,
selectorPosition
) {
var contentGroup = this.getContentGroup()
var legendDrawnMap = createHashMap()
var selectMode = legendModel.get('selectedMode')
var excludeSeriesId = []
ecModel.eachRawSeries(function (seriesModel) {
!seriesModel.get('legendHoverLink') &&
excludeSeriesId.push(seriesModel.id)
})
each(
legendModel.getData(),
function (legendItemModel, dataIndex) {
var name = legendItemModel.get('name') // Use empty string or \n as a newline string
if (!this.newlineDisabled && (name === '' || name === '\n')) {
var g = new Group$1() // @ts-ignore
g.newline = true
contentGroup.add(g)
return
} // Representitive series.
var seriesModel = ecModel.getSeriesByName(name)[0]
if (legendDrawnMap.get(name)) {
// Have been drawed
return
} // Legend to control series.
if (seriesModel) {
var data = seriesModel.getData()
var lineVisualStyle =
data.getVisual('legendLineStyle') || {}
var legendIcon = data.getVisual('legendIcon')
/**
* `data.getVisual('style')` may be the color from the register
* in series. For example, for line series,
*/ var style = data.getVisual('style')
var itemGroup = this._createItem(
seriesModel,
name,
dataIndex,
legendItemModel,
legendModel,
itemAlign,
lineVisualStyle,
style,
legendIcon,
selectMode,
api
)
itemGroup
.on(
'click',
curry(
dispatchSelectAction,
name,
null,
api,
excludeSeriesId
)
)
.on(
'mouseover',
curry(
dispatchHighlightAction,
seriesModel.name,
null,
api,
excludeSeriesId
)
)
.on(
'mouseout',
curry(
dispatchDownplayAction,
seriesModel.name,
null,
api,
excludeSeriesId
)
)
legendDrawnMap.set(name, true)
} else {
// Legend to control data. In pie and funnel.
ecModel.eachRawSeries(function (seriesModel) {
// In case multiple series has same data name
if (legendDrawnMap.get(name)) {
return
}
if (seriesModel.legendVisualProvider) {
var provider = seriesModel.legendVisualProvider
if (!provider.containName(name)) {
return
}
var idx = provider.indexOfName(name)
var style = provider.getItemVisual(idx, 'style')
var legendIcon = provider.getItemVisual(
idx,
'legendIcon'
)
var colorArr = parse(style.fill) // Color may be set to transparent in visualMap when data is out of range.
// Do not show nothing.
if (colorArr && colorArr[3] === 0) {
colorArr[3] = 0.2 // TODO color is set to 0, 0, 0, 0. Should show correct RGBA
style = extend(extend({}, style), {
fill: stringify(colorArr, 'rgba')
})
}
var itemGroup = this._createItem(
seriesModel,
name,
dataIndex,
legendItemModel,
legendModel,
itemAlign,
{},
style,
legendIcon,
selectMode,
api
) // FIXME: consider different series has items with the same name.
itemGroup
.on(
'click',
curry(
dispatchSelectAction,
null,
name,
api,
excludeSeriesId
)
) // Should not specify the series name, consider legend controls
// more than one pie series.
.on(
'mouseover',
curry(
dispatchHighlightAction,
null,
name,
api,
excludeSeriesId
)
)
.on(
'mouseout',
curry(
dispatchDownplayAction,
null,
name,
api,
excludeSeriesId
)
)
legendDrawnMap.set(name, true)
}
}, this)
}
},
this
)
if (selector) {
this._createSelector(
selector,
legendModel,
api,
orient,
selectorPosition
)
}
}
LegendView.prototype._createSelector = function (
selector,
legendModel,
api,
orient,
selectorPosition
) {
var selectorGroup = this.getSelectorGroup()
each(selector, function createSelectorButton(selectorItem) {
var type = selectorItem.type
var labelText = new ZRText$1({
style: {
x: 0,
y: 0,
align: 'center',
verticalAlign: 'middle'
},
onclick: function onclick() {
api.dispatchAction({
type:
type === 'all'
? 'legendAllSelect'
: 'legendInverseSelect'
})
}
})
selectorGroup.add(labelText)
var labelModel = legendModel.getModel('selectorLabel')
var emphasisLabelModel = legendModel.getModel([
'emphasis',
'selectorLabel'
])
setLabelStyle(
labelText,
{ normal: labelModel, emphasis: emphasisLabelModel },
{ defaultText: selectorItem.title }
)
enableHoverEmphasis(labelText)
})
}
LegendView.prototype._createItem = function (
seriesModel,
name,
dataIndex,
legendItemModel,
legendModel,
itemAlign,
lineVisualStyle,
itemVisualStyle,
legendIcon,
selectMode,
api
) {
var drawType = seriesModel.visualDrawType
var itemWidth = legendModel.get('itemWidth')
var itemHeight = legendModel.get('itemHeight')
var isSelected = legendModel.isSelected(name)
var iconRotate = legendItemModel.get('symbolRotate')
var symbolKeepAspect = legendItemModel.get('symbolKeepAspect')
var legendIconType = legendItemModel.get('icon')
legendIcon = legendIconType || legendIcon || 'roundRect'
var style = getLegendStyle(
legendIcon,
legendItemModel,
lineVisualStyle,
itemVisualStyle,
drawType,
isSelected,
api
)
var itemGroup = new Group$1()
var textStyleModel = legendItemModel.getModel('textStyle')
if (
isFunction(seriesModel.getLegendIcon) &&
(!legendIconType || legendIconType === 'inherit')
) {
// Series has specific way to define legend icon
itemGroup.add(
seriesModel.getLegendIcon({
itemWidth: itemWidth,
itemHeight: itemHeight,
icon: legendIcon,
iconRotate: iconRotate,
itemStyle: style.itemStyle,
lineStyle: style.lineStyle,
symbolKeepAspect: symbolKeepAspect
})
)
} else {
// Use default legend icon policy for most series
var rotate =
legendIconType === 'inherit' &&
seriesModel.getData().getVisual('symbol')
? iconRotate === 'inherit'
? seriesModel.getData().getVisual('symbolRotate')
: iconRotate
: 0 // No rotation for no icon
itemGroup.add(
getDefaultLegendIcon({
itemWidth: itemWidth,
itemHeight: itemHeight,
icon: legendIcon,
iconRotate: rotate,
itemStyle: style.itemStyle,
lineStyle: style.lineStyle,
symbolKeepAspect: symbolKeepAspect
})
)
}
var textX = itemAlign === 'left' ? itemWidth + 5 : -5
var textAlign = itemAlign
var formatter = legendModel.get('formatter')
var content = name
if (isString(formatter) && formatter) {
content = formatter.replace('{name}', name != null ? name : '')
} else if (isFunction(formatter)) {
content = formatter(name)
}
var inactiveColor = legendItemModel.get('inactiveColor')
itemGroup.add(
new ZRText$1({
style: createTextStyle(textStyleModel, {
text: content,
x: textX,
y: itemHeight / 2,
fill: isSelected
? textStyleModel.getTextColor()
: inactiveColor,
align: textAlign,
verticalAlign: 'middle'
})
})
) // Add a invisible rect to increase the area of mouse hover
var hitRect = new Rect$2({
shape: itemGroup.getBoundingRect(),
invisible: true
})
var tooltipModel = legendItemModel.getModel('tooltip')
if (tooltipModel.get('show')) {
setTooltipConfig({
el: hitRect,
componentModel: legendModel,
itemName: name,
itemTooltipOption: tooltipModel.option
})
}
itemGroup.add(hitRect)
itemGroup.eachChild(function (child) {
child.silent = true
})
hitRect.silent = !selectMode
this.getContentGroup().add(itemGroup)
enableHoverEmphasis(itemGroup) // @ts-ignore
itemGroup.__legendDataIndex = dataIndex
return itemGroup
}
LegendView.prototype.layoutInner = function (
legendModel,
itemAlign,
maxSize,
isFirstRender,
selector,
selectorPosition
) {
var contentGroup = this.getContentGroup()
var selectorGroup = this.getSelectorGroup() // Place items in contentGroup.
box(
legendModel.get('orient'),
contentGroup,
legendModel.get('itemGap'),
maxSize.width,
maxSize.height
)
var contentRect = contentGroup.getBoundingRect()
var contentPos = [-contentRect.x, -contentRect.y]
selectorGroup.markRedraw()
contentGroup.markRedraw()
if (selector) {
// Place buttons in selectorGroup
box(
// Buttons in selectorGroup always layout horizontally
'horizontal',
selectorGroup,
legendModel.get('selectorItemGap', true)
)
var selectorRect = selectorGroup.getBoundingRect()
var selectorPos = [-selectorRect.x, -selectorRect.y]
var selectorButtonGap = legendModel.get(
'selectorButtonGap',
true
)
var orientIdx = legendModel.getOrient().index
var wh = orientIdx === 0 ? 'width' : 'height'
var hw = orientIdx === 0 ? 'height' : 'width'
var yx = orientIdx === 0 ? 'y' : 'x'
if (selectorPosition === 'end') {
selectorPos[orientIdx] += contentRect[wh] + selectorButtonGap
} else {
contentPos[orientIdx] += selectorRect[wh] + selectorButtonGap
} //Always align selector to content as 'middle'
selectorPos[1 - orientIdx] +=
contentRect[hw] / 2 - selectorRect[hw] / 2
selectorGroup.x = selectorPos[0]
selectorGroup.y = selectorPos[1]
contentGroup.x = contentPos[0]
contentGroup.y = contentPos[1]
var mainRect = { x: 0, y: 0 }
mainRect[wh] =
contentRect[wh] + selectorButtonGap + selectorRect[wh]
mainRect[hw] = Math.max(contentRect[hw], selectorRect[hw])
mainRect[yx] = Math.min(
0,
selectorRect[yx] + selectorPos[1 - orientIdx]
)
return mainRect
} else {
contentGroup.x = contentPos[0]
contentGroup.y = contentPos[1]
return this.group.getBoundingRect()
}
}
/**
* @protected
*/ LegendView.prototype.remove = function () {
this.getContentGroup().removeAll()
this._isFirstRender = true
}
LegendView.type = 'legend.plain'
return LegendView
})(ComponentView$1)
function getLegendStyle(
iconType,
legendItemModel,
lineVisualStyle,
itemVisualStyle,
drawType,
isSelected,
api
) {
/**
* Use series style if is inherit;
* elsewise, use legend style
*/ function handleCommonProps(style, visualStyle) {
// If lineStyle.width is 'auto', it is set to be 2 if series has border
if (style.lineWidth === 'auto') {
style.lineWidth = visualStyle.lineWidth > 0 ? 2 : 0
}
each(style, function (propVal, propName) {
style[propName] === 'inherit' &&
(style[propName] = visualStyle[propName])
})
} // itemStyle
var itemStyleModel = legendItemModel.getModel('itemStyle')
var itemStyle = itemStyleModel.getItemStyle()
var iconBrushType =
iconType.lastIndexOf('empty', 0) === 0 ? 'fill' : 'stroke'
var decalStyle = itemStyleModel.getShallow('decal')
itemStyle.decal =
!decalStyle || decalStyle === 'inherit'
? itemVisualStyle.decal
: createOrUpdatePatternFromDecal(decalStyle, api)
if (itemStyle.fill === 'inherit') {
/**
* Series with visualDrawType as 'stroke' should have
* series stroke as legend fill
*/ itemStyle.fill = itemVisualStyle[drawType]
}
if (itemStyle.stroke === 'inherit') {
/**
* icon type with "emptyXXX" should use fill color
* in visual style
*/ itemStyle.stroke = itemVisualStyle[iconBrushType]
}
if (itemStyle.opacity === 'inherit') {
/**
* Use lineStyle.opacity if drawType is stroke
*/ itemStyle.opacity = (
drawType === 'fill' ? itemVisualStyle : lineVisualStyle
).opacity
}
handleCommonProps(itemStyle, itemVisualStyle) // lineStyle
var legendLineModel = legendItemModel.getModel('lineStyle')
var lineStyle = legendLineModel.getLineStyle()
handleCommonProps(lineStyle, lineVisualStyle) // Fix auto color to real color
itemStyle.fill === 'auto' && (itemStyle.fill = itemVisualStyle.fill)
itemStyle.stroke === 'auto' &&
(itemStyle.stroke = itemVisualStyle.fill)
lineStyle.stroke === 'auto' &&
(lineStyle.stroke = itemVisualStyle.fill)
if (!isSelected) {
var borderWidth = legendItemModel.get('inactiveBorderWidth')
/**
* Since stroke is set to be inactiveBorderColor, it may occur that
* there is no border in series but border in legend, so we need to
* use border only when series has border if is set to be auto
*/ var visualHasBorder = itemStyle[iconBrushType]
itemStyle.lineWidth =
borderWidth === 'auto'
? itemVisualStyle.lineWidth > 0 && visualHasBorder
? 2
: 0
: itemStyle.lineWidth
itemStyle.fill = legendItemModel.get('inactiveColor')
itemStyle.stroke = legendItemModel.get('inactiveBorderColor')
lineStyle.stroke = legendLineModel.get('inactiveColor')
lineStyle.lineWidth = legendLineModel.get('inactiveWidth')
}
return { itemStyle: itemStyle, lineStyle: lineStyle }
}
function getDefaultLegendIcon(opt) {
var symboType = opt.icon || 'roundRect'
var icon = createSymbol(
symboType,
0,
0,
opt.itemWidth,
opt.itemHeight,
opt.itemStyle.fill,
opt.symbolKeepAspect
)
icon.setStyle(opt.itemStyle)
icon.rotation = ((opt.iconRotate || 0) * Math.PI) / 180
icon.setOrigin([opt.itemWidth / 2, opt.itemHeight / 2])
if (symboType.indexOf('empty') > -1) {
icon.style.stroke = icon.style.fill
icon.style.fill = '#fff'
icon.style.lineWidth = 2
}
return icon
}
function dispatchSelectAction(
seriesName,
dataName,
api,
excludeSeriesId
) {
// downplay before unselect
dispatchDownplayAction(seriesName, dataName, api, excludeSeriesId)
api.dispatchAction({
type: 'legendToggleSelect',
name: seriesName != null ? seriesName : dataName
}) // highlight after select
// TODO higlight immediately may cause animation loss.
dispatchHighlightAction(seriesName, dataName, api, excludeSeriesId)
}
function isUseHoverLayer(api) {
var list = api.getZr().storage.getDisplayList()
var emphasisState
var i = 0
var len = list.length
while (i < len && !(emphasisState = list[i].states.emphasis)) {
i++
}
return emphasisState && emphasisState.hoverLayer
}
function dispatchHighlightAction(
seriesName,
dataName,
api,
excludeSeriesId
) {
// If element hover will move to a hoverLayer.
if (!isUseHoverLayer(api)) {
api.dispatchAction({
type: 'highlight',
seriesName: seriesName,
name: dataName,
excludeSeriesId: excludeSeriesId
})
}
}
function dispatchDownplayAction(
seriesName,
dataName,
api,
excludeSeriesId
) {
// If element hover will move to a hoverLayer.
if (!isUseHoverLayer(api)) {
api.dispatchAction({
type: 'downplay',
seriesName: seriesName,
name: dataName,
excludeSeriesId: excludeSeriesId
})
}
}
var LegendView$1 = LegendView
/*
* 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 legendFilter(ecModel) {
var legendModels = ecModel.findComponents({ mainType: 'legend' })
if (legendModels && legendModels.length) {
ecModel.filterSeries(function (series) {
// If in any legend component the status is not selected.
// Because in legend series is assumed selected when it is not in the legend data.
for (var i = 0; i < legendModels.length; i++) {
if (!legendModels[i].isSelected(series.name)) {
return false
}
}
return true
})
}
}
function legendSelectActionHandler(methodName, payload, ecModel) {
var selectedMap = {}
var isToggleSelect = methodName === 'toggleSelected'
var isSelected // Update all legend components
ecModel.eachComponent('legend', function (legendModel) {
if (isToggleSelect && isSelected != null) {
// Force other legend has same selected status
// Or the first is toggled to true and other are toggled to false
// In the case one legend has some item unSelected in option. And if other legend
// doesn't has the item, they will assume it is selected.
legendModel[isSelected ? 'select' : 'unSelect'](payload.name)
} else if (
methodName === 'allSelect' ||
methodName === 'inverseSelect'
) {
legendModel[methodName]()
} else {
legendModel[methodName](payload.name)
isSelected = legendModel.isSelected(payload.name)
}
var legendData = legendModel.getData()
each$7(legendData, function (model) {
var name = model.get('name') // Wrap element
if (name === '\n' || name === '') {
return
}
var isItemSelected = legendModel.isSelected(name)
if (selectedMap.hasOwnProperty(name)) {
// Unselected if any legend is unselected
selectedMap[name] = selectedMap[name] && isItemSelected
} else {
selectedMap[name] = isItemSelected
}
})
}) // Return the event explicitly
return methodName === 'allSelect' || methodName === 'inverseSelect'
? { selected: selectedMap }
: { name: payload.name, selected: selectedMap }
}
function installLegendAction(registers) {
/**
* @event legendToggleSelect
* @type {Object}
* @property {string} type 'legendToggleSelect'
* @property {string} [from]
* @property {string} name Series name or data item name
*/ registers.registerAction(
'legendToggleSelect',
'legendselectchanged',
curry$1(legendSelectActionHandler, 'toggleSelected')
)
registers.registerAction(
'legendAllSelect',
'legendselectall',
curry$1(legendSelectActionHandler, 'allSelect')
)
registers.registerAction(
'legendInverseSelect',
'legendinverseselect',
curry$1(legendSelectActionHandler, 'inverseSelect')
)
/**
* @event legendSelect
* @type {Object}
* @property {string} type 'legendSelect'
* @property {string} name Series name or data item name
*/ registers.registerAction(
'legendSelect',
'legendselected',
curry$1(legendSelectActionHandler, 'select')
)
/**
* @event legendUnSelect
* @type {Object}
* @property {string} type 'legendUnSelect'
* @property {string} name Series name or data item name
*/ registers.registerAction(
'legendUnSelect',
'legendunselected',
curry$1(legendSelectActionHandler, 'unSelect')
)
}
function install$8(registers) {
registers.registerComponentModel(LegendModel$1)
registers.registerComponentView(LegendView$1)
registers.registerProcessor(
registers.PRIORITY.PROCESSOR.SERIES_FILTER,
legendFilter
)
registers.registerSubTypeDefaulter('legend', function () {
return 'plain'
})
installLegendAction(registers)
}
var ScrollableLegendModel = /** @class */ (function (_super) {
__extends(ScrollableLegendModel, _super)
function ScrollableLegendModel() {
var _this =
(_super !== null && _super.apply(this, arguments)) || this
_this.type = ScrollableLegendModel.type
return _this
}
/**
* @param {number} scrollDataIndex
*/ ScrollableLegendModel.prototype.setScrollDataIndex = function (
scrollDataIndex
) {
this.option.scrollDataIndex = scrollDataIndex
}
ScrollableLegendModel.prototype.init = function (
option,
parentModel,
ecModel
) {
var inputPositionParams = getLayoutParams(option)
_super.prototype.init.call(this, option, parentModel, ecModel)
mergeAndNormalizeLayoutParams(this, option, inputPositionParams)
}
/**
* @override
*/ ScrollableLegendModel.prototype.mergeOption = function (
option,
ecModel
) {
_super.prototype.mergeOption.call(this, option, ecModel)
mergeAndNormalizeLayoutParams(this, this.option, option)
}
ScrollableLegendModel.type = 'legend.scroll'
ScrollableLegendModel.defaultOption = inheritDefaultOption(
LegendModel$1.defaultOption,
{
scrollDataIndex: 0,
pageButtonItemGap: 5,
pageButtonGap: null,
pageButtonPosition: 'end',
pageFormatter: '{current}/{total}',
pageIcons: {
horizontal: ['M0,0L12,-10L12,10z', 'M0,0L-12,-10L-12,10z'],
vertical: ['M0,0L20,0L10,-20z', 'M0,0L20,0L10,20z']
},
pageIconColor: '#2f4554',
pageIconInactiveColor: '#aaa',
pageIconSize: 15,
pageTextStyle: { color: '#333' },
animationDurationUpdate: 800
}
)
return ScrollableLegendModel
})(LegendModel$1)
function mergeAndNormalizeLayoutParams(legendModel, target, raw) {
var orient = legendModel.getOrient()
var ignoreSize = [1, 1]
ignoreSize[orient.index] = 0
mergeLayoutParam(target, raw, {
type: 'box',
ignoreSize: !!ignoreSize
})
}
var ScrollableLegendModel$1 = ScrollableLegendModel
var Group = Group$3
var WH = ['width', 'height']
var XY = ['x', 'y']
var ScrollableLegendView = /** @class */ (function (_super) {
__extends(ScrollableLegendView, _super)
function ScrollableLegendView() {
var _this =
(_super !== null && _super.apply(this, arguments)) || this
_this.type = ScrollableLegendView.type
_this.newlineDisabled = true
_this._currentIndex = 0
return _this
}
ScrollableLegendView.prototype.init = function () {
_super.prototype.init.call(this)
this.group.add((this._containerGroup = new Group()))
this._containerGroup.add(this.getContentGroup())
this.group.add((this._controllerGroup = new Group()))
}
/**
* @override
*/ ScrollableLegendView.prototype.resetInner = function () {
_super.prototype.resetInner.call(this)
this._controllerGroup.removeAll()
this._containerGroup.removeClipPath()
this._containerGroup.__rectSize = null
}
/**
* @override
*/ ScrollableLegendView.prototype.renderInner = function (
itemAlign,
legendModel,
ecModel,
api,
selector,
orient,
selectorPosition
) {
var self = this // Render content items.
_super.prototype.renderInner.call(
this,
itemAlign,
legendModel,
ecModel,
api,
selector,
orient,
selectorPosition
)
var controllerGroup = this._controllerGroup // FIXME: support be 'auto' adapt to size number text length,
// e.g., '3/12345' should not overlap with the control arrow button.
var pageIconSize = legendModel.get('pageIconSize', true)
var pageIconSizeArr = isArray(pageIconSize)
? pageIconSize
: [pageIconSize, pageIconSize]
createPageButton('pagePrev', 0)
var pageTextStyleModel = legendModel.getModel('pageTextStyle')
controllerGroup.add(
new ZRText$1({
name: 'pageText',
style: {
// Placeholder to calculate a proper layout.
text: 'xx/xx',
fill: pageTextStyleModel.getTextColor(),
font: pageTextStyleModel.getFont(),
verticalAlign: 'middle',
align: 'center'
},
silent: true
})
)
createPageButton('pageNext', 1)
function createPageButton(name, iconIdx) {
var pageDataIndexName = name + 'DataIndex'
var icon = createIcon(
legendModel.get('pageIcons', true)[
legendModel.getOrient().name
][iconIdx],
{
// Buttons will be created in each render, so we do not need
// to worry about avoiding using legendModel kept in scope.
onclick: bind$1(
self._pageGo,
self,
pageDataIndexName,
legendModel,
api
)
},
{
x: -pageIconSizeArr[0] / 2,
y: -pageIconSizeArr[1] / 2,
width: pageIconSizeArr[0],
height: pageIconSizeArr[1]
}
)
icon.name = name
controllerGroup.add(icon)
}
}
/**
* @override
*/ ScrollableLegendView.prototype.layoutInner = function (
legendModel,
itemAlign,
maxSize,
isFirstRender,
selector,
selectorPosition
) {
var selectorGroup = this.getSelectorGroup()
var orientIdx = legendModel.getOrient().index
var wh = WH[orientIdx]
var xy = XY[orientIdx]
var hw = WH[1 - orientIdx]
var yx = XY[1 - orientIdx]
selector &&
box(
// Buttons in selectorGroup always layout horizontally
'horizontal',
selectorGroup,
legendModel.get('selectorItemGap', true)
)
var selectorButtonGap = legendModel.get('selectorButtonGap', true)
var selectorRect = selectorGroup.getBoundingRect()
var selectorPos = [-selectorRect.x, -selectorRect.y]
var processMaxSize = clone$3(maxSize)
selector &&
(processMaxSize[wh] =
maxSize[wh] - selectorRect[wh] - selectorButtonGap)
var mainRect = this._layoutContentAndController(
legendModel,
isFirstRender,
processMaxSize,
orientIdx,
wh,
hw,
yx,
xy
)
if (selector) {
if (selectorPosition === 'end') {
selectorPos[orientIdx] += mainRect[wh] + selectorButtonGap
} else {
var offset = selectorRect[wh] + selectorButtonGap
selectorPos[orientIdx] -= offset
mainRect[xy] -= offset
}
mainRect[wh] += selectorRect[wh] + selectorButtonGap
selectorPos[1 - orientIdx] +=
mainRect[yx] + mainRect[hw] / 2 - selectorRect[hw] / 2
mainRect[hw] = Math.max(mainRect[hw], selectorRect[hw])
mainRect[yx] = Math.min(
mainRect[yx],
selectorRect[yx] + selectorPos[1 - orientIdx]
)
selectorGroup.x = selectorPos[0]
selectorGroup.y = selectorPos[1]
selectorGroup.markRedraw()
}
return mainRect
}
ScrollableLegendView.prototype._layoutContentAndController =
function (
legendModel,
isFirstRender,
maxSize,
orientIdx,
wh,
hw,
yx,
xy
) {
var contentGroup = this.getContentGroup()
var containerGroup = this._containerGroup
var controllerGroup = this._controllerGroup // Place items in contentGroup.
box(
legendModel.get('orient'),
contentGroup,
legendModel.get('itemGap'),
!orientIdx ? null : maxSize.width,
orientIdx ? null : maxSize.height
)
box(
// Buttons in controller are layout always horizontally.
'horizontal',
controllerGroup,
legendModel.get('pageButtonItemGap', true)
)
var contentRect = contentGroup.getBoundingRect()
var controllerRect = controllerGroup.getBoundingRect()
var showController = (this._showController =
contentRect[wh] > maxSize[wh]) // In case that the inner elements of contentGroup layout do not based on [0, 0]
var contentPos = [-contentRect.x, -contentRect.y] // Remain contentPos when scroll animation perfroming.
// If first rendering, `contentGroup.position` is [0, 0], which
// does not make sense and may cause unexepcted animation if adopted.
if (!isFirstRender) {
contentPos[orientIdx] = contentGroup[xy]
} // Layout container group based on 0.
var containerPos = [0, 0]
var controllerPos = [-controllerRect.x, -controllerRect.y]
var pageButtonGap = retrieve2(
legendModel.get('pageButtonGap', true),
legendModel.get('itemGap', true)
) // Place containerGroup and controllerGroup and contentGroup.
if (showController) {
var pageButtonPosition = legendModel.get(
'pageButtonPosition',
true
) // controller is on the right / bottom.
if (pageButtonPosition === 'end') {
controllerPos[orientIdx] += maxSize[wh] - controllerRect[wh]
} // controller is on the left / top.
else {
containerPos[orientIdx] +=
controllerRect[wh] + pageButtonGap
}
} // Always align controller to content as 'middle'.
controllerPos[1 - orientIdx] +=
contentRect[hw] / 2 - controllerRect[hw] / 2
contentGroup.setPosition(contentPos)
containerGroup.setPosition(containerPos)
controllerGroup.setPosition(controllerPos) // Calculate `mainRect` and set `clipPath`.
// mainRect should not be calculated by `this.group.getBoundingRect()`
// for sake of the overflow.
var mainRect = { x: 0, y: 0 } // Consider content may be overflow (should be clipped).
mainRect[wh] = showController ? maxSize[wh] : contentRect[wh]
mainRect[hw] = Math.max(contentRect[hw], controllerRect[hw]) // `containerRect[yx] + containerPos[1 - orientIdx]` is 0.
mainRect[yx] = Math.min(
0,
controllerRect[yx] + controllerPos[1 - orientIdx]
)
containerGroup.__rectSize = maxSize[wh]
if (showController) {
var clipShape = { x: 0, y: 0 }
clipShape[wh] = Math.max(
maxSize[wh] - controllerRect[wh] - pageButtonGap,
0
)
clipShape[hw] = mainRect[hw]
containerGroup.setClipPath(new Rect$2({ shape: clipShape })) // Consider content may be larger than container, container rect
// can not be obtained from `containerGroup.getBoundingRect()`.
containerGroup.__rectSize = clipShape[wh]
} else {
// Do not remove or ignore controller. Keep them set as placeholders.
controllerGroup.eachChild(function (child) {
child.attr({ invisible: true, silent: true })
})
} // Content translate animation.
var pageInfo = this._getPageInfo(legendModel)
pageInfo.pageIndex != null &&
updateProps$1(
contentGroup,
{
x: pageInfo.contentPosition[0],
y: pageInfo.contentPosition[1]
}, // When switch from "show controller" to "not show controller", view should be
// updated immediately without animation, otherwise causes weird effect.
showController ? legendModel : null
)
this._updatePageInfoView(legendModel, pageInfo)
return mainRect
}
ScrollableLegendView.prototype._pageGo = function (
to,
legendModel,
api
) {
var scrollDataIndex = this._getPageInfo(legendModel)[to]
scrollDataIndex != null &&
api.dispatchAction({
type: 'legendScroll',
scrollDataIndex: scrollDataIndex,
legendId: legendModel.id
})
}
ScrollableLegendView.prototype._updatePageInfoView = function (
legendModel,
pageInfo
) {
var controllerGroup = this._controllerGroup
each$7(['pagePrev', 'pageNext'], function (name) {
var key = name + 'DataIndex'
var canJump = pageInfo[key] != null
var icon = controllerGroup.childOfName(name)
if (icon) {
icon.setStyle(
'fill',
canJump
? legendModel.get('pageIconColor', true)
: legendModel.get('pageIconInactiveColor', true)
)
icon.cursor = canJump ? 'pointer' : 'default'
}
})
var pageText = controllerGroup.childOfName('pageText')
var pageFormatter = legendModel.get('pageFormatter')
var pageIndex = pageInfo.pageIndex
var current = pageIndex != null ? pageIndex + 1 : 0
var total = pageInfo.pageCount
pageText &&
pageFormatter &&
pageText.setStyle(
'text',
isString(pageFormatter)
? pageFormatter
.replace(
'{current}',
current == null ? '' : current + ''
)
.replace('{total}', total == null ? '' : total + '')
: pageFormatter({ current: current, total: total })
)
}
/**
* contentPosition: Array., null when data item not found.
* pageIndex: number, null when data item not found.
* pageCount: number, always be a number, can be 0.
* pagePrevDataIndex: number, null when no previous page.
* pageNextDataIndex: number, null when no next page.
* }
*/ ScrollableLegendView.prototype._getPageInfo = function (
legendModel
) {
var scrollDataIndex = legendModel.get('scrollDataIndex', true)
var contentGroup = this.getContentGroup()
var containerRectSize = this._containerGroup.__rectSize
var orientIdx = legendModel.getOrient().index
var wh = WH[orientIdx]
var xy = XY[orientIdx]
var targetItemIndex = this._findTargetItemIndex(scrollDataIndex)
var children = contentGroup.children()
var targetItem = children[targetItemIndex]
var itemCount = children.length
var pCount = !itemCount ? 0 : 1
var result = {
contentPosition: [contentGroup.x, contentGroup.y],
pageCount: pCount,
pageIndex: pCount - 1,
pagePrevDataIndex: null,
pageNextDataIndex: null
}
if (!targetItem) {
return result
}
var targetItemInfo = getItemInfo(targetItem)
result.contentPosition[orientIdx] = -targetItemInfo.s // Strategy:
// (1) Always align based on the left/top most item.
// (2) It is user-friendly that the last item shown in the
// current window is shown at the begining of next window.
// Otherwise if half of the last item is cut by the window,
// it will have no chance to display entirely.
// (3) Consider that item size probably be different, we
// have calculate pageIndex by size rather than item index,
// and we can not get page index directly by division.
// (4) The window is to narrow to contain more than
// one item, we should make sure that the page can be fliped.
for (
var i = targetItemIndex + 1,
winStartItemInfo = targetItemInfo,
winEndItemInfo = targetItemInfo,
currItemInfo = null;
i <= itemCount;
++i
) {
currItemInfo = getItemInfo(children[i])
if (
// Half of the last item is out of the window.
(!currItemInfo &&
winEndItemInfo.e >
winStartItemInfo.s + containerRectSize) || // If the current item does not intersect with the window, the new page
// can be started at the current item or the last item.
(currItemInfo && !intersect(currItemInfo, winStartItemInfo.s))
) {
if (winEndItemInfo.i > winStartItemInfo.i) {
winStartItemInfo = winEndItemInfo
} else {
// e.g., when page size is smaller than item size.
winStartItemInfo = currItemInfo
}
if (winStartItemInfo) {
if (result.pageNextDataIndex == null) {
result.pageNextDataIndex = winStartItemInfo.i
}
++result.pageCount
}
}
winEndItemInfo = currItemInfo
}
for (
var i = targetItemIndex - 1,
winStartItemInfo = targetItemInfo,
winEndItemInfo = targetItemInfo,
currItemInfo = null;
i >= -1;
--i
) {
currItemInfo = getItemInfo(children[i])
if (
// If the the end item does not intersect with the window started
// from the current item, a page can be settled.
(!currItemInfo ||
!intersect(winEndItemInfo, currItemInfo.s)) && // e.g., when page size is smaller than item size.
winStartItemInfo.i < winEndItemInfo.i
) {
winEndItemInfo = winStartItemInfo
if (result.pagePrevDataIndex == null) {
result.pagePrevDataIndex = winStartItemInfo.i
}
++result.pageCount
++result.pageIndex
}
winStartItemInfo = currItemInfo
}
return result
function getItemInfo(el) {
if (el) {
var itemRect = el.getBoundingRect()
var start = itemRect[xy] + el[xy]
return {
s: start,
e: start + itemRect[wh],
i: el.__legendDataIndex
}
}
}
function intersect(itemInfo, winStart) {
return (
itemInfo.e >= winStart &&
itemInfo.s <= winStart + containerRectSize
)
}
}
ScrollableLegendView.prototype._findTargetItemIndex = function (
targetDataIndex
) {
if (!this._showController) {
return 0
}
var index
var contentGroup = this.getContentGroup()
var defaultIndex
contentGroup.eachChild(function (child, idx) {
var legendDataIdx = child.__legendDataIndex // FIXME
// If the given targetDataIndex (from model) is illegal,
// we use defaultIndex. But the index on the legend model and
// action payload is still illegal. That case will not be
// changed until some scenario requires.
if (defaultIndex == null && legendDataIdx != null) {
defaultIndex = idx
}
if (legendDataIdx === targetDataIndex) {
index = idx
}
})
return index != null ? index : defaultIndex
}
ScrollableLegendView.type = 'legend.scroll'
return ScrollableLegendView
})(LegendView$1)
var ScrollableLegendView$1 = ScrollableLegendView
/*
* 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 installScrollableLegendAction(registers) {
/**
* @event legendScroll
* @type {Object}
* @property {string} type 'legendScroll'
* @property {string} scrollDataIndex
*/ registers.registerAction(
'legendScroll',
'legendscroll',
function (payload, ecModel) {
var scrollDataIndex = payload.scrollDataIndex
scrollDataIndex != null &&
ecModel.eachComponent(
{ mainType: 'legend', subType: 'scroll', query: payload },
function (legendModel) {
legendModel.setScrollDataIndex(scrollDataIndex)
}
)
}
)
}
function install$7(registers) {
use(install$8)
registers.registerComponentModel(ScrollableLegendModel$1)
registers.registerComponentView(ScrollableLegendView$1)
installScrollableLegendAction(registers)
}
function install$6(registers) {
use(install$8)
use(install$7)
}
var InsideZoomModel = /** @class */ (function (_super) {
__extends(InsideZoomModel, _super)
function InsideZoomModel() {
var _this =
(_super !== null && _super.apply(this, arguments)) || this
_this.type = InsideZoomModel.type
return _this
}
InsideZoomModel.type = 'dataZoom.inside'
InsideZoomModel.defaultOption = inheritDefaultOption(
DataZoomModel$1.defaultOption,
{
disabled: false,
zoomLock: false,
zoomOnMouseWheel: true,
moveOnMouseMove: true,
moveOnMouseWheel: false,
preventDefaultMouseMove: true
}
)
return InsideZoomModel
})(DataZoomModel$1)
var InsideZoomModel$1 = InsideZoomModel
var inner = makeInner()
function setViewInfoToCoordSysRecord(api, dataZoomModel, getRange) {
inner(api).coordSysRecordMap.each(function (coordSysRecord) {
var dzInfo = coordSysRecord.dataZoomInfoMap.get(dataZoomModel.uid)
if (dzInfo) {
dzInfo.getRange = getRange
}
})
}
function disposeCoordSysRecordIfNeeded(api, dataZoomModel) {
var coordSysRecordMap = inner(api).coordSysRecordMap
var coordSysKeyArr = coordSysRecordMap.keys()
for (var i = 0; i < coordSysKeyArr.length; i++) {
var coordSysKey = coordSysKeyArr[i]
var coordSysRecord = coordSysRecordMap.get(coordSysKey)
var dataZoomInfoMap = coordSysRecord.dataZoomInfoMap
if (dataZoomInfoMap) {
var dzUid = dataZoomModel.uid
var dzInfo = dataZoomInfoMap.get(dzUid)
if (dzInfo) {
dataZoomInfoMap.removeKey(dzUid)
if (!dataZoomInfoMap.keys().length) {
disposeCoordSysRecord(coordSysRecordMap, coordSysRecord)
}
}
}
}
}
function disposeCoordSysRecord(coordSysRecordMap, coordSysRecord) {
if (coordSysRecord) {
coordSysRecordMap.removeKey(coordSysRecord.model.uid)
var controller = coordSysRecord.controller
controller && controller.dispose()
}
}
function createCoordSysRecord(api, coordSysModel) {
// These init props will never change after record created.
var coordSysRecord = {
model: coordSysModel,
containsPoint: curry$1(containsPoint, coordSysModel),
dispatchAction: curry$1(dispatchAction, api),
dataZoomInfoMap: null,
controller: null
} // Must not do anything depends on coordSysRecord outside the event handler here,
// because coordSysRecord not completed yet.
var controller = (coordSysRecord.controller = new RoamController$1(
api.getZr()
))
each$7(['pan', 'zoom', 'scrollMove'], function (eventName) {
controller.on(eventName, function (event) {
var batch = []
coordSysRecord.dataZoomInfoMap.each(function (dzInfo) {
// Check whether the behaviors (zoomOnMouseWheel, moveOnMouseMove,
// moveOnMouseWheel, ...) enabled.
if (!event.isAvailableBehavior(dzInfo.model.option)) {
return
}
var method = (dzInfo.getRange || {})[eventName]
var range =
method &&
method(
dzInfo.dzReferCoordSysInfo,
coordSysRecord.model.mainType,
coordSysRecord.controller,
event
)
!dzInfo.model.get('disabled', true) &&
range &&
batch.push({
dataZoomId: dzInfo.model.id,
start: range[0],
end: range[1]
})
})
batch.length && coordSysRecord.dispatchAction(batch)
})
})
return coordSysRecord
}
/**
* This action will be throttled.
*/ function dispatchAction(api, batch) {
if (!api.isDisposed()) {
api.dispatchAction({
type: 'dataZoom',
animation: { easing: 'cubicOut', duration: 100 },
batch: batch
})
}
}
function containsPoint(coordSysModel, e, x, y) {
return coordSysModel.coordinateSystem.containPoint([x, y])
}
/**
* Merge roamController settings when multiple dataZooms share one roamController.
*/ function mergeControllerParams(dataZoomInfoMap) {
var controlType // DO NOT use reserved word (true, false, undefined) as key literally. Even if encapsulated
// as string, it is probably revert to reserved word by compress tool. See #7411.
var prefix = 'type_'
var typePriority = {
type_true: 2,
type_move: 1,
type_false: 0,
type_undefined: -1
}
var preventDefaultMouseMove = true
dataZoomInfoMap.each(function (dataZoomInfo) {
var dataZoomModel = dataZoomInfo.model
var oneType = dataZoomModel.get('disabled', true)
? false
: dataZoomModel.get('zoomLock', true)
? 'move'
: true
if (
typePriority[prefix + oneType] >
typePriority[prefix + controlType]
) {
controlType = oneType
} // Prevent default move event by default. If one false, do not prevent. Otherwise
// users may be confused why it does not work when multiple insideZooms exist.
preventDefaultMouseMove =
preventDefaultMouseMove &&
dataZoomModel.get('preventDefaultMouseMove', true)
})
return {
controlType: controlType,
opt: {
// RoamController will enable all of these functionalities,
// and the final behavior is determined by its event listener
// provided by each inside zoom.
zoomOnMouseWheel: true,
moveOnMouseMove: true,
moveOnMouseWheel: true,
preventDefaultMouseMove: !!preventDefaultMouseMove
}
}
}
function installDataZoomRoamProcessor(registers) {
registers.registerProcessor(
registers.PRIORITY.PROCESSOR.FILTER,
function (ecModel, api) {
var apiInner = inner(api)
var coordSysRecordMap =
apiInner.coordSysRecordMap ||
(apiInner.coordSysRecordMap = createHashMap())
coordSysRecordMap.each(function (coordSysRecord) {
// `coordSysRecordMap` always exists (becuase it hold the `roam controller`, which should
// better not re-create each time), but clear `dataZoomInfoMap` each round of the workflow.
coordSysRecord.dataZoomInfoMap = null
})
ecModel.eachComponent(
{ mainType: 'dataZoom', subType: 'inside' },
function (dataZoomModel) {
var dzReferCoordSysWrap =
collectReferCoordSysModelInfo(dataZoomModel)
each$7(
dzReferCoordSysWrap.infoList,
function (dzCoordSysInfo) {
var coordSysUid = dzCoordSysInfo.model.uid
var coordSysRecord =
coordSysRecordMap.get(coordSysUid) ||
coordSysRecordMap.set(
coordSysUid,
createCoordSysRecord(api, dzCoordSysInfo.model)
)
var dataZoomInfoMap =
coordSysRecord.dataZoomInfoMap ||
(coordSysRecord.dataZoomInfoMap = createHashMap()) // Notice these props might be changed each time for a single dataZoomModel.
dataZoomInfoMap.set(dataZoomModel.uid, {
dzReferCoordSysInfo: dzCoordSysInfo,
model: dataZoomModel,
getRange: null
})
}
)
}
) // (1) Merge dataZoom settings for each coord sys and set to the roam controller.
// (2) Clear coord sys if not refered by any dataZoom.
coordSysRecordMap.each(function (coordSysRecord) {
var controller = coordSysRecord.controller
var firstDzInfo
var dataZoomInfoMap = coordSysRecord.dataZoomInfoMap
if (dataZoomInfoMap) {
var firstDzKey = dataZoomInfoMap.keys()[0]
if (firstDzKey != null) {
firstDzInfo = dataZoomInfoMap.get(firstDzKey)
}
}
if (!firstDzInfo) {
disposeCoordSysRecord(coordSysRecordMap, coordSysRecord)
return
}
var controllerParams = mergeControllerParams(dataZoomInfoMap)
controller.enable(
controllerParams.controlType,
controllerParams.opt
)
controller.setPointerChecker(coordSysRecord.containsPoint)
createOrUpdate(
coordSysRecord,
'dispatchAction',
firstDzInfo.model.get('throttle', true),
'fixRate'
)
})
}
)
}
var InsideZoomView = /** @class */ (function (_super) {
__extends(InsideZoomView, _super)
function InsideZoomView() {
var _this =
(_super !== null && _super.apply(this, arguments)) || this
_this.type = 'dataZoom.inside'
return _this
}
InsideZoomView.prototype.render = function (
dataZoomModel,
ecModel,
api
) {
_super.prototype.render.apply(this, arguments)
if (dataZoomModel.noTarget()) {
this._clear()
return
} // Hence the `throttle` util ensures to preserve command order,
// here simply updating range all the time will not cause missing
// any of the the roam change.
this.range = dataZoomModel.getPercentRange() // Reset controllers.
setViewInfoToCoordSysRecord(api, dataZoomModel, {
pan: bind$1(getRangeHandlers.pan, this),
zoom: bind$1(getRangeHandlers.zoom, this),
scrollMove: bind$1(getRangeHandlers.scrollMove, this)
})
}
InsideZoomView.prototype.dispose = function () {
this._clear()
_super.prototype.dispose.apply(this, arguments)
}
InsideZoomView.prototype._clear = function () {
disposeCoordSysRecordIfNeeded(this.api, this.dataZoomModel)
this.range = null
}
InsideZoomView.type = 'dataZoom.inside'
return InsideZoomView
})(DataZoomView$1)
var getRangeHandlers = {
zoom: function zoom(coordSysInfo, coordSysMainType, controller, e) {
var lastRange = this.range
var range = lastRange.slice() // Calculate transform by the first axis.
var axisModel = coordSysInfo.axisModels[0]
if (!axisModel) {
return
}
var directionInfo = getDirectionInfo[coordSysMainType](
null,
[e.originX, e.originY],
axisModel,
controller,
coordSysInfo
)
var percentPoint =
((directionInfo.signal > 0
? directionInfo.pixelStart +
directionInfo.pixelLength -
directionInfo.pixel
: directionInfo.pixel - directionInfo.pixelStart) /
directionInfo.pixelLength) *
(range[1] - range[0]) +
range[0]
var scale = Math.max(1 / e.scale, 0)
range[0] = (range[0] - percentPoint) * scale + percentPoint
range[1] = (range[1] - percentPoint) * scale + percentPoint // Restrict range.
var minMaxSpan = this.dataZoomModel
.findRepresentativeAxisProxy()
.getMinMaxSpan()
sliderMove(
0,
range,
[0, 100],
0,
minMaxSpan.minSpan,
minMaxSpan.maxSpan
)
this.range = range
if (lastRange[0] !== range[0] || lastRange[1] !== range[1]) {
return range
}
},
pan: makeMover(function (
range,
axisModel,
coordSysInfo,
coordSysMainType,
controller,
e
) {
var directionInfo = getDirectionInfo[coordSysMainType](
[e.oldX, e.oldY],
[e.newX, e.newY],
axisModel,
controller,
coordSysInfo
)
return (
(directionInfo.signal *
(range[1] - range[0]) *
directionInfo.pixel) /
directionInfo.pixelLength
)
}),
scrollMove: makeMover(function (
range,
axisModel,
coordSysInfo,
coordSysMainType,
controller,
e
) {
var directionInfo = getDirectionInfo[coordSysMainType](
[0, 0],
[e.scrollDelta, e.scrollDelta],
axisModel,
controller,
coordSysInfo
)
return (
directionInfo.signal * (range[1] - range[0]) * e.scrollDelta
)
})
}
function makeMover(getPercentDelta) {
return function (coordSysInfo, coordSysMainType, controller, e) {
var lastRange = this.range
var range = lastRange.slice() // Calculate transform by the first axis.
var axisModel = coordSysInfo.axisModels[0]
if (!axisModel) {
return
}
var percentDelta = getPercentDelta(
range,
axisModel,
coordSysInfo,
coordSysMainType,
controller,
e
)
sliderMove(percentDelta, range, [0, 100], 'all')
this.range = range
if (lastRange[0] !== range[0] || lastRange[1] !== range[1]) {
return range
}
}
}
var getDirectionInfo = {
grid: function grid(
oldPoint,
newPoint,
axisModel,
controller,
coordSysInfo
) {
var axis = axisModel.axis
var ret = {}
var rect = coordSysInfo.model.coordinateSystem.getRect()
oldPoint = oldPoint || [0, 0]
if (axis.dim === 'x') {
ret.pixel = newPoint[0] - oldPoint[0]
ret.pixelLength = rect.width
ret.pixelStart = rect.x
ret.signal = axis.inverse ? 1 : -1
} else {
// axis.dim === 'y'
ret.pixel = newPoint[1] - oldPoint[1]
ret.pixelLength = rect.height
ret.pixelStart = rect.y
ret.signal = axis.inverse ? -1 : 1
}
return ret
},
polar: function polar(
oldPoint,
newPoint,
axisModel,
controller,
coordSysInfo
) {
var axis = axisModel.axis
var ret = {}
var polar = coordSysInfo.model.coordinateSystem
var radiusExtent = polar.getRadiusAxis().getExtent()
var angleExtent = polar.getAngleAxis().getExtent()
oldPoint = oldPoint ? polar.pointToCoord(oldPoint) : [0, 0]
newPoint = polar.pointToCoord(newPoint)
if (axisModel.mainType === 'radiusAxis') {
ret.pixel = newPoint[0] - oldPoint[0] // ret.pixelLength = Math.abs(radiusExtent[1] - radiusExtent[0]);
// ret.pixelStart = Math.min(radiusExtent[0], radiusExtent[1]);
ret.pixelLength = radiusExtent[1] - radiusExtent[0]
ret.pixelStart = radiusExtent[0]
ret.signal = axis.inverse ? 1 : -1
} else {
// 'angleAxis'
ret.pixel = newPoint[1] - oldPoint[1] // ret.pixelLength = Math.abs(angleExtent[1] - angleExtent[0]);
// ret.pixelStart = Math.min(angleExtent[0], angleExtent[1]);
ret.pixelLength = angleExtent[1] - angleExtent[0]
ret.pixelStart = angleExtent[0]
ret.signal = axis.inverse ? -1 : 1
}
return ret
},
singleAxis: function singleAxis(
oldPoint,
newPoint,
axisModel,
controller,
coordSysInfo
) {
var axis = axisModel.axis
var rect = coordSysInfo.model.coordinateSystem.getRect()
var ret = {}
oldPoint = oldPoint || [0, 0]
if (axis.orient === 'horizontal') {
ret.pixel = newPoint[0] - oldPoint[0]
ret.pixelLength = rect.width
ret.pixelStart = rect.x
ret.signal = axis.inverse ? 1 : -1
} else {
// 'vertical'
ret.pixel = newPoint[1] - oldPoint[1]
ret.pixelLength = rect.height
ret.pixelStart = rect.y
ret.signal = axis.inverse ? -1 : 1
}
return ret
}
}
var InsideZoomView$1 = InsideZoomView
function install$5(registers) {
installCommon(registers)
registers.registerComponentModel(InsideZoomModel$1)
registers.registerComponentView(InsideZoomView$1)
installDataZoomRoamProcessor(registers)
}
var SliderZoomModel = /** @class */ (function (_super) {
__extends(SliderZoomModel, _super)
function SliderZoomModel() {
var _this =
(_super !== null && _super.apply(this, arguments)) || this
_this.type = SliderZoomModel.type
return _this
}
SliderZoomModel.type = 'dataZoom.slider'
SliderZoomModel.layoutMode = 'box'
SliderZoomModel.defaultOption = inheritDefaultOption(
DataZoomModel$1.defaultOption,
{
show: true, // deault value can only be drived in view stage.
right: 'ph',
top: 'ph',
width: 'ph',
height: 'ph',
left: null,
bottom: null,
borderColor: '#d2dbee',
borderRadius: 3,
backgroundColor: 'rgba(47,69,84,0)', // dataBackgroundColor: '#ddd',
dataBackground: {
lineStyle: { color: '#d2dbee', width: 0.5 },
areaStyle: { color: '#d2dbee', opacity: 0.2 }
},
selectedDataBackground: {
lineStyle: { color: '#8fb0f7', width: 0.5 },
areaStyle: { color: '#8fb0f7', opacity: 0.2 }
}, // Color of selected window.
fillerColor: 'rgba(135,175,274,0.2)',
handleIcon:
'path://M-9.35,34.56V42m0-40V9.5m-2,0h4a2,2,0,0,1,2,2v21a2,2,0,0,1-2,2h-4a2,2,0,0,1-2-2v-21A2,2,0,0,1-11.35,9.5Z', // Percent of the slider height
handleSize: '100%',
handleStyle: { color: '#fff', borderColor: '#ACB8D1' },
moveHandleSize: 7,
moveHandleIcon:
'path://M-320.9-50L-320.9-50c18.1,0,27.1,9,27.1,27.1V85.7c0,18.1-9,27.1-27.1,27.1l0,0c-18.1,0-27.1-9-27.1-27.1V-22.9C-348-41-339-50-320.9-50z M-212.3-50L-212.3-50c18.1,0,27.1,9,27.1,27.1V85.7c0,18.1-9,27.1-27.1,27.1l0,0c-18.1,0-27.1-9-27.1-27.1V-22.9C-239.4-41-230.4-50-212.3-50z M-103.7-50L-103.7-50c18.1,0,27.1,9,27.1,27.1V85.7c0,18.1-9,27.1-27.1,27.1l0,0c-18.1,0-27.1-9-27.1-27.1V-22.9C-130.9-41-121.8-50-103.7-50z',
moveHandleStyle: { color: '#D2DBEE', opacity: 0.7 },
showDetail: true,
showDataShadow: 'auto',
realtime: true,
zoomLock: false,
textStyle: { color: '#6E7079' },
brushSelect: true,
brushStyle: { color: 'rgba(135,175,274,0.15)' },
emphasis: {
handleStyle: { borderColor: '#8FB0F7' },
moveHandleStyle: { color: '#8FB0F7' }
}
}
)
return SliderZoomModel
})(DataZoomModel$1)
var SliderZoomModel$1 = SliderZoomModel
var Rect = Rect$2 // Constants
var DEFAULT_LOCATION_EDGE_GAP = 7
var DEFAULT_FRAME_BORDER_WIDTH = 1
var DEFAULT_FILLER_SIZE = 30
var DEFAULT_MOVE_HANDLE_SIZE = 7
var HORIZONTAL = 'horizontal'
var VERTICAL = 'vertical'
var LABEL_GAP = 5
var SHOW_DATA_SHADOW_SERIES_TYPE = [
'line',
'bar',
'candlestick',
'scatter'
]
var REALTIME_ANIMATION_CONFIG = {
easing: 'cubicOut',
duration: 100,
delay: 0
}
var SliderZoomView = /** @class */ (function (_super) {
__extends(SliderZoomView, _super)
function SliderZoomView() {
var _this =
(_super !== null && _super.apply(this, arguments)) || this
_this.type = SliderZoomView.type
_this._displayables = {}
return _this
}
SliderZoomView.prototype.init = function (ecModel, api) {
this.api = api // A unique handler for each dataZoom component
this._onBrush = bind$1(this._onBrush, this)
this._onBrushEnd = bind$1(this._onBrushEnd, this)
}
SliderZoomView.prototype.render = function (
dataZoomModel,
ecModel,
api,
payload
) {
_super.prototype.render.apply(this, arguments)
createOrUpdate(
this,
'_dispatchZoomAction',
dataZoomModel.get('throttle'),
'fixRate'
)
this._orient = dataZoomModel.getOrient()
if (dataZoomModel.get('show') === false) {
this.group.removeAll()
return
}
if (dataZoomModel.noTarget()) {
this._clear()
this.group.removeAll()
return
} // Notice: this._resetInterval() should not be executed when payload.type
// is 'dataZoom', origin this._range should be maintained, otherwise 'pan'
// or 'zoom' info will be missed because of 'throttle' of this.dispatchAction,
if (
!payload ||
payload.type !== 'dataZoom' ||
payload.from !== this.uid
) {
this._buildView()
}
this._updateView()
}
SliderZoomView.prototype.dispose = function () {
this._clear()
_super.prototype.dispose.apply(this, arguments)
}
SliderZoomView.prototype._clear = function () {
clear$1(this, '_dispatchZoomAction')
var zr = this.api.getZr()
zr.off('mousemove', this._onBrush)
zr.off('mouseup', this._onBrushEnd)
}
SliderZoomView.prototype._buildView = function () {
var thisGroup = this.group
thisGroup.removeAll()
this._brushing = false
this._displayables.brushRect = null
this._resetLocation()
this._resetInterval()
var barGroup = (this._displayables.sliderGroup = new Group$3())
this._renderBackground()
this._renderHandle()
this._renderDataShadow()
thisGroup.add(barGroup)
this._positionGroup()
}
SliderZoomView.prototype._resetLocation = function () {
var dataZoomModel = this.dataZoomModel
var api = this.api
var showMoveHandle = dataZoomModel.get('brushSelect')
var moveHandleSize = showMoveHandle ? DEFAULT_MOVE_HANDLE_SIZE : 0 // If some of x/y/width/height are not specified,
// auto-adapt according to target grid.
var coordRect = this._findCoordRect()
var ecSize = { width: api.getWidth(), height: api.getHeight() } // Default align by coordinate system rect.
var positionInfo =
this._orient === HORIZONTAL
? {
// Why using 'right', because right should be used in vertical,
// and it is better to be consistent for dealing with position param merge.
right: ecSize.width - coordRect.x - coordRect.width,
top:
ecSize.height -
DEFAULT_FILLER_SIZE -
DEFAULT_LOCATION_EDGE_GAP -
moveHandleSize,
width: coordRect.width,
height: DEFAULT_FILLER_SIZE
}
: {
right: DEFAULT_LOCATION_EDGE_GAP,
top: coordRect.y,
width: DEFAULT_FILLER_SIZE,
height: coordRect.height
} // Do not write back to option and replace value 'ph', because
// the 'ph' value should be recalculated when resize.
var layoutParams = getLayoutParams(dataZoomModel.option) // Replace the placeholder value.
each$7(['right', 'top', 'width', 'height'], function (name) {
if (layoutParams[name] === 'ph') {
layoutParams[name] = positionInfo[name]
}
})
var layoutRect = getLayoutRect(layoutParams, ecSize)
this._location = { x: layoutRect.x, y: layoutRect.y }
this._size = [layoutRect.width, layoutRect.height]
this._orient === VERTICAL && this._size.reverse()
}
SliderZoomView.prototype._positionGroup = function () {
var thisGroup = this.group
var location = this._location
var orient = this._orient // Just use the first axis to determine mapping.
var targetAxisModel = this.dataZoomModel.getFirstTargetAxisModel()
var inverse = targetAxisModel && targetAxisModel.get('inverse')
var sliderGroup = this._displayables.sliderGroup
var otherAxisInverse = (this._dataShadowInfo || {})
.otherAxisInverse // Transform barGroup.
sliderGroup.attr(
orient === HORIZONTAL && !inverse
? { scaleY: otherAxisInverse ? 1 : -1, scaleX: 1 }
: orient === HORIZONTAL && inverse
? { scaleY: otherAxisInverse ? 1 : -1, scaleX: -1 }
: orient === VERTICAL && !inverse
? {
scaleY: otherAxisInverse ? -1 : 1,
scaleX: 1,
rotation: Math.PI / 2
} // Dont use Math.PI, considering shadow direction.
: {
scaleY: otherAxisInverse ? -1 : 1,
scaleX: -1,
rotation: Math.PI / 2
}
) // Position barGroup
var rect = thisGroup.getBoundingRect([sliderGroup])
thisGroup.x = location.x - rect.x
thisGroup.y = location.y - rect.y
thisGroup.markRedraw()
}
SliderZoomView.prototype._getViewExtent = function () {
return [0, this._size[0]]
}
SliderZoomView.prototype._renderBackground = function () {
var dataZoomModel = this.dataZoomModel
var size = this._size
var barGroup = this._displayables.sliderGroup
var brushSelect = dataZoomModel.get('brushSelect')
barGroup.add(
new Rect({
silent: true,
shape: { x: 0, y: 0, width: size[0], height: size[1] },
style: { fill: dataZoomModel.get('backgroundColor') },
z2: -40
})
) // Click panel, over shadow, below handles.
var clickPanel = new Rect({
shape: { x: 0, y: 0, width: size[0], height: size[1] },
style: { fill: 'transparent' },
z2: 0,
onclick: bind$1(this._onClickPanel, this)
})
var zr = this.api.getZr()
if (brushSelect) {
clickPanel.on('mousedown', this._onBrushStart, this)
clickPanel.cursor = 'crosshair'
zr.on('mousemove', this._onBrush)
zr.on('mouseup', this._onBrushEnd)
} else {
zr.off('mousemove', this._onBrush)
zr.off('mouseup', this._onBrushEnd)
}
barGroup.add(clickPanel)
}
SliderZoomView.prototype._renderDataShadow = function () {
var info = (this._dataShadowInfo = this._prepareDataShadowInfo())
this._displayables.dataShadowSegs = []
if (!info) {
return
}
var size = this._size
var oldSize = this._shadowSize || []
var seriesModel = info.series
var data = seriesModel.getRawData()
var otherDim = seriesModel.getShadowDim
? seriesModel.getShadowDim() // @see candlestick
: info.otherDim
if (otherDim == null) {
return
}
var polygonPts = this._shadowPolygonPts
var polylinePts = this._shadowPolylinePts // Not re-render if data doesn't change.
if (
data !== this._shadowData ||
otherDim !== this._shadowDim ||
size[0] !== oldSize[0] ||
size[1] !== oldSize[1]
) {
var otherDataExtent_1 = data.getDataExtent(otherDim) // Nice extent.
var otherOffset =
(otherDataExtent_1[1] - otherDataExtent_1[0]) * 0.3
otherDataExtent_1 = [
otherDataExtent_1[0] - otherOffset,
otherDataExtent_1[1] + otherOffset
]
var otherShadowExtent_1 = [0, size[1]]
var thisShadowExtent = [0, size[0]]
var areaPoints_1 = [
[size[0], 0],
[0, 0]
]
var linePoints_1 = []
var step_1 = thisShadowExtent[1] / (data.count() - 1)
var thisCoord_1 = 0 // Optimize for large data shadow
var stride_1 = Math.round(data.count() / size[0])
var lastIsEmpty_1
data.each([otherDim], function (value, index) {
if (stride_1 > 0 && index % stride_1) {
thisCoord_1 += step_1
return
} // FIXME
// Should consider axis.min/axis.max when drawing dataShadow.
// FIXME
// 应该使用统一的空判断?还是在list里进行空判断?
var isEmpty = value == null || isNaN(value) || value === '' // See #4235.
var otherCoord = isEmpty
? 0
: linearMap(
value,
otherDataExtent_1,
otherShadowExtent_1,
true
) // Attempt to draw data shadow precisely when there are empty value.
if (isEmpty && !lastIsEmpty_1 && index) {
areaPoints_1.push([
areaPoints_1[areaPoints_1.length - 1][0],
0
])
linePoints_1.push([
linePoints_1[linePoints_1.length - 1][0],
0
])
} else if (!isEmpty && lastIsEmpty_1) {
areaPoints_1.push([thisCoord_1, 0])
linePoints_1.push([thisCoord_1, 0])
}
areaPoints_1.push([thisCoord_1, otherCoord])
linePoints_1.push([thisCoord_1, otherCoord])
thisCoord_1 += step_1
lastIsEmpty_1 = isEmpty
})
polygonPts = this._shadowPolygonPts = areaPoints_1
polylinePts = this._shadowPolylinePts = linePoints_1
}
this._shadowData = data
this._shadowDim = otherDim
this._shadowSize = [size[0], size[1]]
var dataZoomModel = this.dataZoomModel
function createDataShadowGroup(isSelectedArea) {
var model = dataZoomModel.getModel(
isSelectedArea ? 'selectedDataBackground' : 'dataBackground'
)
var group = new Group$3()
var polygon = new Polygon$1({
shape: { points: polygonPts },
segmentIgnoreThreshold: 1,
style: model.getModel('areaStyle').getAreaStyle(),
silent: true,
z2: -20
})
var polyline = new Polyline$1({
shape: { points: polylinePts },
segmentIgnoreThreshold: 1,
style: model.getModel('lineStyle').getLineStyle(),
silent: true,
z2: -19
})
group.add(polygon)
group.add(polyline)
return group
} // let dataBackgroundModel = dataZoomModel.getModel('dataBackground');
for (var i = 0; i < 3; i++) {
var group = createDataShadowGroup(i === 1)
this._displayables.sliderGroup.add(group)
this._displayables.dataShadowSegs.push(group)
}
}
SliderZoomView.prototype._prepareDataShadowInfo = function () {
var dataZoomModel = this.dataZoomModel
var showDataShadow = dataZoomModel.get('showDataShadow')
if (showDataShadow === false) {
return
} // Find a representative series.
var result
var ecModel = this.ecModel
dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) {
var seriesModels = dataZoomModel
.getAxisProxy(axisDim, axisIndex)
.getTargetSeriesModels()
each$7(
seriesModels,
function (seriesModel) {
if (result) {
return
}
if (
showDataShadow !== true &&
indexOf(
SHOW_DATA_SHADOW_SERIES_TYPE,
seriesModel.get('type')
) < 0
) {
return
}
var thisAxis = ecModel.getComponent(
getAxisMainType(axisDim),
axisIndex
).axis
var otherDim = getOtherDim(axisDim)
var otherAxisInverse
var coordSys = seriesModel.coordinateSystem
if (otherDim != null && coordSys.getOtherAxis) {
otherAxisInverse = coordSys.getOtherAxis(thisAxis).inverse
}
otherDim = seriesModel.getData().mapDimension(otherDim)
result = {
thisAxis: thisAxis,
series: seriesModel,
thisDim: axisDim,
otherDim: otherDim,
otherAxisInverse: otherAxisInverse
}
},
this
)
}, this)
return result
}
SliderZoomView.prototype._renderHandle = function () {
var thisGroup = this.group
var displayables = this._displayables
var handles = (displayables.handles = [null, null])
var handleLabels = (displayables.handleLabels = [null, null])
var sliderGroup = this._displayables.sliderGroup
var size = this._size
var dataZoomModel = this.dataZoomModel
var api = this.api
var borderRadius = dataZoomModel.get('borderRadius') || 0
var brushSelect = dataZoomModel.get('brushSelect')
var filler = (displayables.filler = new Rect({
silent: brushSelect,
style: { fill: dataZoomModel.get('fillerColor') },
textConfig: { position: 'inside' }
}))
sliderGroup.add(filler) // Frame border.
sliderGroup.add(
new Rect({
silent: true,
subPixelOptimize: true,
shape: {
x: 0,
y: 0,
width: size[0],
height: size[1],
r: borderRadius
},
style: {
// deprecated option
stroke:
dataZoomModel.get('dataBackgroundColor') ||
dataZoomModel.get('borderColor'),
lineWidth: DEFAULT_FRAME_BORDER_WIDTH,
fill: 'rgba(0,0,0,0)'
}
})
) // Left and right handle to resize
each$7(
[0, 1],
function (handleIndex) {
var iconStr = dataZoomModel.get('handleIcon')
if (
!symbolBuildProxies[iconStr] &&
iconStr.indexOf('path://') < 0 &&
iconStr.indexOf('image://') < 0
) {
// Compatitable with the old icon parsers. Which can use a path string without path://
iconStr = 'path://' + iconStr
}
var path = createSymbol(iconStr, -1, 0, 2, 2, null, true)
path.attr({
cursor: getCursor(this._orient),
draggable: true,
drift: bind$1(this._onDragMove, this, handleIndex),
ondragend: bind$1(this._onDragEnd, this),
onmouseover: bind$1(this._showDataInfo, this, true),
onmouseout: bind$1(this._showDataInfo, this, false),
z2: 5
})
var bRect = path.getBoundingRect()
var handleSize = dataZoomModel.get('handleSize')
this._handleHeight = parsePercent(handleSize, this._size[1])
this._handleWidth =
(bRect.width / bRect.height) * this._handleHeight
path.setStyle(
dataZoomModel.getModel('handleStyle').getItemStyle()
)
path.style.strokeNoScale = true
path.rectHover = true
path.ensureState('emphasis').style = dataZoomModel
.getModel(['emphasis', 'handleStyle'])
.getItemStyle()
enableHoverEmphasis(path)
var handleColor = dataZoomModel.get('handleColor') // deprecated option
// Compatitable with previous version
if (handleColor != null) {
path.style.fill = handleColor
}
sliderGroup.add((handles[handleIndex] = path))
var textStyleModel = dataZoomModel.getModel('textStyle')
thisGroup.add(
(handleLabels[handleIndex] = new ZRText$1({
silent: true,
invisible: true,
style: createTextStyle(textStyleModel, {
x: 0,
y: 0,
text: '',
verticalAlign: 'middle',
align: 'center',
fill: textStyleModel.getTextColor(),
font: textStyleModel.getFont()
}),
z2: 10
}))
)
},
this
) // Handle to move. Only visible when brushSelect is set true.
var actualMoveZone = filler
if (brushSelect) {
var moveHandleHeight = parsePercent(
dataZoomModel.get('moveHandleSize'),
size[1]
)
var moveHandle_1 = (displayables.moveHandle = new Rect$2({
style: dataZoomModel
.getModel('moveHandleStyle')
.getItemStyle(),
silent: true,
shape: {
r: [0, 0, 2, 2],
y: size[1] - 0.5,
height: moveHandleHeight
}
}))
var iconSize = moveHandleHeight * 0.8
var moveHandleIcon = (displayables.moveHandleIcon =
createSymbol(
dataZoomModel.get('moveHandleIcon'),
-iconSize / 2,
-iconSize / 2,
iconSize,
iconSize,
'#fff',
true
))
moveHandleIcon.silent = true
moveHandleIcon.y = size[1] + moveHandleHeight / 2 - 0.5
moveHandle_1.ensureState('emphasis').style = dataZoomModel
.getModel(['emphasis', 'moveHandleStyle'])
.getItemStyle()
var moveZoneExpandSize = Math.min(
size[1] / 2,
Math.max(moveHandleHeight, 10)
)
actualMoveZone = displayables.moveZone = new Rect$2({
invisible: true,
shape: {
y: size[1] - moveZoneExpandSize,
height: moveHandleHeight + moveZoneExpandSize
}
})
actualMoveZone
.on('mouseover', function () {
api.enterEmphasis(moveHandle_1)
})
.on('mouseout', function () {
api.leaveEmphasis(moveHandle_1)
})
sliderGroup.add(moveHandle_1)
sliderGroup.add(moveHandleIcon)
sliderGroup.add(actualMoveZone)
}
actualMoveZone.attr({
draggable: true,
cursor: getCursor(this._orient),
drift: bind$1(this._onDragMove, this, 'all'),
ondragstart: bind$1(this._showDataInfo, this, true),
ondragend: bind$1(this._onDragEnd, this),
onmouseover: bind$1(this._showDataInfo, this, true),
onmouseout: bind$1(this._showDataInfo, this, false)
})
}
SliderZoomView.prototype._resetInterval = function () {
var range = (this._range = this.dataZoomModel.getPercentRange())
var viewExtent = this._getViewExtent()
this._handleEnds = [
linearMap(range[0], [0, 100], viewExtent, true),
linearMap(range[1], [0, 100], viewExtent, true)
]
}
SliderZoomView.prototype._updateInterval = function (
handleIndex,
delta
) {
var dataZoomModel = this.dataZoomModel
var handleEnds = this._handleEnds
var viewExtend = this._getViewExtent()
var minMaxSpan = dataZoomModel
.findRepresentativeAxisProxy()
.getMinMaxSpan()
var percentExtent = [0, 100]
sliderMove(
delta,
handleEnds,
viewExtend,
dataZoomModel.get('zoomLock') ? 'all' : handleIndex,
minMaxSpan.minSpan != null
? linearMap(
minMaxSpan.minSpan,
percentExtent,
viewExtend,
true
)
: null,
minMaxSpan.maxSpan != null
? linearMap(
minMaxSpan.maxSpan,
percentExtent,
viewExtend,
true
)
: null
)
var lastRange = this._range
var range = (this._range = asc$1([
linearMap(handleEnds[0], viewExtend, percentExtent, true),
linearMap(handleEnds[1], viewExtend, percentExtent, true)
]))
return (
!lastRange ||
lastRange[0] !== range[0] ||
lastRange[1] !== range[1]
)
}
SliderZoomView.prototype._updateView = function (nonRealtime) {
var displaybles = this._displayables
var handleEnds = this._handleEnds
var handleInterval = asc$1(handleEnds.slice())
var size = this._size
each$7(
[0, 1],
function (handleIndex) {
// Handles
var handle = displaybles.handles[handleIndex]
var handleHeight = this._handleHeight
handle.attr({
scaleX: handleHeight / 2,
scaleY: handleHeight / 2, // This is a trick, by adding an extra tiny offset to let the default handle's end point align to the drag window.
// NOTE: It may affect some custom shapes a bit. But we prefer to have better result by default.
x: handleEnds[handleIndex] + (handleIndex ? -1 : 1),
y: size[1] / 2 - handleHeight / 2
})
},
this
) // Filler
displaybles.filler.setShape({
x: handleInterval[0],
y: 0,
width: handleInterval[1] - handleInterval[0],
height: size[1]
})
var viewExtent = {
x: handleInterval[0],
width: handleInterval[1] - handleInterval[0]
} // Move handle
if (displaybles.moveHandle) {
displaybles.moveHandle.setShape(viewExtent)
displaybles.moveZone.setShape(viewExtent) // Force update path on the invisible object
displaybles.moveZone.getBoundingRect()
displaybles.moveHandleIcon &&
displaybles.moveHandleIcon.attr(
'x',
viewExtent.x + viewExtent.width / 2
)
} // update clip path of shadow.
var dataShadowSegs = displaybles.dataShadowSegs
var segIntervals = [
0,
handleInterval[0],
handleInterval[1],
size[0]
]
for (var i = 0; i < dataShadowSegs.length; i++) {
var segGroup = dataShadowSegs[i]
var clipPath = segGroup.getClipPath()
if (!clipPath) {
clipPath = new Rect$2()
segGroup.setClipPath(clipPath)
}
clipPath.setShape({
x: segIntervals[i],
y: 0,
width: segIntervals[i + 1] - segIntervals[i],
height: size[1]
})
}
this._updateDataInfo(nonRealtime)
}
SliderZoomView.prototype._updateDataInfo = function (nonRealtime) {
var dataZoomModel = this.dataZoomModel
var displaybles = this._displayables
var handleLabels = displaybles.handleLabels
var orient = this._orient
var labelTexts = ['', ''] // FIXME
// date型,支持formatter,autoformatter(ec2 date.getAutoFormatter)
if (dataZoomModel.get('showDetail')) {
var axisProxy = dataZoomModel.findRepresentativeAxisProxy()
if (axisProxy) {
var axis = axisProxy.getAxisModel().axis
var range = this._range
var dataInterval = nonRealtime // See #4434, data and axis are not processed and reset yet in non-realtime mode.
? axisProxy.calculateDataWindow({
start: range[0],
end: range[1]
}).valueWindow
: axisProxy.getDataValueWindow()
labelTexts = [
this._formatLabel(dataInterval[0], axis),
this._formatLabel(dataInterval[1], axis)
]
}
}
var orderedHandleEnds = asc$1(this._handleEnds.slice())
setLabel.call(this, 0)
setLabel.call(this, 1)
function setLabel(handleIndex) {
// Label
// Text should not transform by barGroup.
// Ignore handlers transform
var barTransform = getTransform$1(
displaybles.handles[handleIndex].parent,
this.group
)
var direction = transformDirection(
handleIndex === 0 ? 'right' : 'left',
barTransform
)
var offset = this._handleWidth / 2 + LABEL_GAP
var textPoint = applyTransform(
[
orderedHandleEnds[handleIndex] +
(handleIndex === 0 ? -offset : offset),
this._size[1] / 2
],
barTransform
)
handleLabels[handleIndex].setStyle({
x: textPoint[0],
y: textPoint[1],
verticalAlign: orient === HORIZONTAL ? 'middle' : direction,
align: orient === HORIZONTAL ? direction : 'center',
text: labelTexts[handleIndex]
})
}
}
SliderZoomView.prototype._formatLabel = function (value, axis) {
var dataZoomModel = this.dataZoomModel
var labelFormatter = dataZoomModel.get('labelFormatter')
var labelPrecision = dataZoomModel.get('labelPrecision')
if (labelPrecision == null || labelPrecision === 'auto') {
labelPrecision = axis.getPixelPrecision()
}
var valueStr =
value == null || isNaN(value)
? '' // FIXME Glue code
: axis.type === 'category' || axis.type === 'time'
? axis.scale.getLabel({ value: Math.round(value) }) // param of toFixed should less then 20.
: value.toFixed(Math.min(labelPrecision, 20))
return isFunction(labelFormatter)
? labelFormatter(value, valueStr)
: isString(labelFormatter)
? labelFormatter.replace('{value}', valueStr)
: valueStr
}
/**
* @param showOrHide true: show, false: hide
*/ SliderZoomView.prototype._showDataInfo = function (showOrHide) {
// Always show when drgging.
showOrHide = this._dragging || showOrHide
var displayables = this._displayables
var handleLabels = displayables.handleLabels
handleLabels[0].attr('invisible', !showOrHide)
handleLabels[1].attr('invisible', !showOrHide) // Highlight move handle
displayables.moveHandle &&
this.api[showOrHide ? 'enterEmphasis' : 'leaveEmphasis'](
displayables.moveHandle,
1
)
}
SliderZoomView.prototype._onDragMove = function (
handleIndex,
dx,
dy,
event
) {
this._dragging = true // For mobile device, prevent screen slider on the button.
stop(event.event) // Transform dx, dy to bar coordination.
var barTransform =
this._displayables.sliderGroup.getLocalTransform()
var vertex = applyTransform([dx, dy], barTransform, true)
var changed = this._updateInterval(handleIndex, vertex[0])
var realtime = this.dataZoomModel.get('realtime')
this._updateView(!realtime) // Avoid dispatch dataZoom repeatly but range not changed,
// which cause bad visual effect when progressive enabled.
changed && realtime && this._dispatchZoomAction(true)
}
SliderZoomView.prototype._onDragEnd = function () {
this._dragging = false
this._showDataInfo(false) // While in realtime mode and stream mode, dispatch action when
// drag end will cause the whole view rerender, which is unnecessary.
var realtime = this.dataZoomModel.get('realtime')
!realtime && this._dispatchZoomAction(false)
}
SliderZoomView.prototype._onClickPanel = function (e) {
var size = this._size
var localPoint =
this._displayables.sliderGroup.transformCoordToLocal(
e.offsetX,
e.offsetY
)
if (
localPoint[0] < 0 ||
localPoint[0] > size[0] ||
localPoint[1] < 0 ||
localPoint[1] > size[1]
) {
return
}
var handleEnds = this._handleEnds
var center = (handleEnds[0] + handleEnds[1]) / 2
var changed = this._updateInterval('all', localPoint[0] - center)
this._updateView()
changed && this._dispatchZoomAction(false)
}
SliderZoomView.prototype._onBrushStart = function (e) {
var x = e.offsetX
var y = e.offsetY
this._brushStart = new Point$1(x, y)
this._brushing = true
this._brushStartTime = +new Date() // this._updateBrushRect(x, y);
}
SliderZoomView.prototype._onBrushEnd = function (e) {
if (!this._brushing) {
return
}
var brushRect = this._displayables.brushRect
this._brushing = false
if (!brushRect) {
return
}
brushRect.attr('ignore', true)
var brushShape = brushRect.shape
var brushEndTime = +new Date() // console.log(brushEndTime - this._brushStartTime);
if (
brushEndTime - this._brushStartTime < 200 &&
Math.abs(brushShape.width) < 5
) {
// Will treat it as a click
return
}
var viewExtend = this._getViewExtent()
var percentExtent = [0, 100]
this._range = asc$1([
linearMap(brushShape.x, viewExtend, percentExtent, true),
linearMap(
brushShape.x + brushShape.width,
viewExtend,
percentExtent,
true
)
])
this._handleEnds = [brushShape.x, brushShape.x + brushShape.width]
this._updateView()
this._dispatchZoomAction(false)
}
SliderZoomView.prototype._onBrush = function (e) {
if (this._brushing) {
// For mobile device, prevent screen slider on the button.
stop(e.event)
this._updateBrushRect(e.offsetX, e.offsetY)
}
}
SliderZoomView.prototype._updateBrushRect = function (
mouseX,
mouseY
) {
var displayables = this._displayables
var dataZoomModel = this.dataZoomModel
var brushRect = displayables.brushRect
if (!brushRect) {
brushRect = displayables.brushRect = new Rect({
silent: true,
style: dataZoomModel.getModel('brushStyle').getItemStyle()
})
displayables.sliderGroup.add(brushRect)
}
brushRect.attr('ignore', false)
var brushStart = this._brushStart
var sliderGroup = this._displayables.sliderGroup
var endPoint = sliderGroup.transformCoordToLocal(mouseX, mouseY)
var startPoint = sliderGroup.transformCoordToLocal(
brushStart.x,
brushStart.y
)
var size = this._size
endPoint[0] = Math.max(Math.min(size[0], endPoint[0]), 0)
brushRect.setShape({
x: startPoint[0],
y: 0,
width: endPoint[0] - startPoint[0],
height: size[1]
})
}
/**
* This action will be throttled.
*/ SliderZoomView.prototype._dispatchZoomAction = function (
realtime
) {
var range = this._range
this.api.dispatchAction({
type: 'dataZoom',
from: this.uid,
dataZoomId: this.dataZoomModel.id,
animation: realtime ? REALTIME_ANIMATION_CONFIG : null,
start: range[0],
end: range[1]
})
}
SliderZoomView.prototype._findCoordRect = function () {
// Find the grid coresponding to the first axis referred by dataZoom.
var rect
var coordSysInfoList = collectReferCoordSysModelInfo(
this.dataZoomModel
).infoList
if (!rect && coordSysInfoList.length) {
var coordSys = coordSysInfoList[0].model.coordinateSystem
rect = coordSys.getRect && coordSys.getRect()
}
if (!rect) {
var width = this.api.getWidth()
var height = this.api.getHeight()
rect = {
x: width * 0.2,
y: height * 0.2,
width: width * 0.6,
height: height * 0.6
}
}
return rect
}
SliderZoomView.type = 'dataZoom.slider'
return SliderZoomView
})(DataZoomView$1)
function getOtherDim(thisDim) {
// FIXME
// 这个逻辑和getOtherAxis里一致,但是写在这里是否不好
var map = { x: 'y', y: 'x', radius: 'angle', angle: 'radius' }
return map[thisDim]
}
function getCursor(orient) {
return orient === 'vertical' ? 'ns-resize' : 'ew-resize'
}
var SliderZoomView$1 = SliderZoomView
function install$4(registers) {
registers.registerComponentModel(SliderZoomModel$1)
registers.registerComponentView(SliderZoomView$1)
installCommon(registers)
}
function install$3(registers) {
use(install$5)
use(install$4) // Do not install './dataZoomSelect',
// since it only work for toolbox dataZoom.
}
var RELATIONAL_EXPRESSION_OP_ALIAS_MAP = {
value: 'eq', // PENDING: not good for literal semantic?
'<': 'lt',
'<=': 'lte',
'>': 'gt',
'>=': 'gte',
'=': 'eq',
'!=': 'ne',
'<>': 'ne' // Might mileading for sake of the different between '==' and '===',
// So dont support them.
// '==': 'eq',
// '===': 'seq',
// '!==': 'sne'
// PENDING: Whether support some common alias "ge", "le", "neq"?
// ge: 'gte',
// le: 'lte',
// neq: 'ne',
} // type RelationalExpressionOpEvaluate = (tarVal: unknown, condVal: unknown) => boolean;
var RegExpEvaluator = /** @class */ (function () {
function RegExpEvaluator(rVal) {
// Support condVal: RegExp | string
var condValue = (this._condVal = isString(rVal)
? new RegExp(rVal)
: isRegExp(rVal)
? rVal
: null)
if (condValue == null) {
var errMsg = ''
throwError(errMsg)
}
}
RegExpEvaluator.prototype.evaluate = function (lVal) {
var type = _typeof(lVal)
return isString(type)
? this._condVal.test(lVal)
: isNumber(type)
? this._condVal.test(lVal + '')
: false
}
return RegExpEvaluator
})()
var ConstConditionInternal = /** @class */ (function () {
function ConstConditionInternal() {}
ConstConditionInternal.prototype.evaluate = function () {
return this.value
}
return ConstConditionInternal
})()
var AndConditionInternal = /** @class */ (function () {
function AndConditionInternal() {}
AndConditionInternal.prototype.evaluate = function () {
var children = this.children
for (var i = 0; i < children.length; i++) {
if (!children[i].evaluate()) {
return false
}
}
return true
}
return AndConditionInternal
})()
var OrConditionInternal = /** @class */ (function () {
function OrConditionInternal() {}
OrConditionInternal.prototype.evaluate = function () {
var children = this.children
for (var i = 0; i < children.length; i++) {
if (children[i].evaluate()) {
return true
}
}
return false
}
return OrConditionInternal
})()
var NotConditionInternal = /** @class */ (function () {
function NotConditionInternal() {}
NotConditionInternal.prototype.evaluate = function () {
return !this.child.evaluate()
}
return NotConditionInternal
})()
var RelationalConditionInternal = /** @class */ (function () {
function RelationalConditionInternal() {}
RelationalConditionInternal.prototype.evaluate = function () {
var needParse = !!this.valueParser // Call getValue with no `this`.
var getValue = this.getValue
var tarValRaw = getValue(this.valueGetterParam)
var tarValParsed = needParse ? this.valueParser(tarValRaw) : null // Relational cond follow "and" logic internally.
for (var i = 0; i < this.subCondList.length; i++) {
if (
!this.subCondList[i].evaluate(
needParse ? tarValParsed : tarValRaw
)
) {
return false
}
}
return true
}
return RelationalConditionInternal
})()
function parseOption(exprOption, getters) {
if (exprOption === true || exprOption === false) {
var cond = new ConstConditionInternal()
cond.value = exprOption
return cond
}
var errMsg = ''
if (!isObjectNotArray(exprOption)) {
throwError(errMsg)
}
if (exprOption.and) {
return parseAndOrOption('and', exprOption, getters)
} else if (exprOption.or) {
return parseAndOrOption('or', exprOption, getters)
} else if (exprOption.not) {
return parseNotOption(exprOption, getters)
}
return parseRelationalOption(exprOption, getters)
}
function parseAndOrOption(op, exprOption, getters) {
var subOptionArr = exprOption[op]
var errMsg = ''
if (!isArray(subOptionArr)) {
throwError(errMsg)
}
if (!subOptionArr.length) {
throwError(errMsg)
}
var cond =
op === 'and'
? new AndConditionInternal()
: new OrConditionInternal()
cond.children = map$1(subOptionArr, function (subOption) {
return parseOption(subOption, getters)
})
if (!cond.children.length) {
throwError(errMsg)
}
return cond
}
function parseNotOption(exprOption, getters) {
var subOption = exprOption.not
var errMsg = ''
if (!isObjectNotArray(subOption)) {
throwError(errMsg)
}
var cond = new NotConditionInternal()
cond.child = parseOption(subOption, getters)
if (!cond.child) {
throwError(errMsg)
}
return cond
}
function parseRelationalOption(exprOption, getters) {
var errMsg = ''
var valueGetterParam = getters.prepareGetValue(exprOption)
var subCondList = []
var exprKeys = keys(exprOption)
var parserName = exprOption.parser
var valueParser = parserName ? getRawValueParser(parserName) : null
for (var i = 0; i < exprKeys.length; i++) {
var keyRaw = exprKeys[i]
if (
keyRaw === 'parser' ||
getters.valueGetterAttrMap.get(keyRaw)
) {
continue
}
var op = hasOwn(RELATIONAL_EXPRESSION_OP_ALIAS_MAP, keyRaw)
? RELATIONAL_EXPRESSION_OP_ALIAS_MAP[keyRaw]
: keyRaw
var condValueRaw = exprOption[keyRaw]
var condValueParsed = valueParser
? valueParser(condValueRaw)
: condValueRaw
var evaluator =
createFilterComparator(op, condValueParsed) ||
(op === 'reg' && new RegExpEvaluator(condValueParsed))
if (!evaluator) {
throwError(errMsg)
}
subCondList.push(evaluator)
}
if (!subCondList.length) {
throwError(errMsg)
}
var cond = new RelationalConditionInternal()
cond.valueGetterParam = valueGetterParam
cond.valueParser = valueParser
cond.getValue = getters.getValue
cond.subCondList = subCondList
return cond
}
function isObjectNotArray(val) {
return isObject$2(val) && !isArrayLike(val)
}
var ConditionalExpressionParsed = /** @class */ (function () {
function ConditionalExpressionParsed(exprOption, getters) {
this._cond = parseOption(exprOption, getters)
}
ConditionalExpressionParsed.prototype.evaluate = function () {
return this._cond.evaluate()
}
return ConditionalExpressionParsed
})()
function parseConditionalExpression(exprOption, getters) {
return new ConditionalExpressionParsed(exprOption, getters)
}
var filterTransform = {
type: 'echarts:filter', // PEDING: enhance to filter by index rather than create new data
transform: function transform(params) {
// [Caveat] Fail-Fast:
// Do not return the whole dataset unless user config indicate it explicitly.
// For example, if no condition specified by mistake, return an empty result
// is better than return the entire raw soruce for user to find the mistake.
var upstream = params.upstream
var rawItem
var condition = parseConditionalExpression(params.config, {
valueGetterAttrMap: createHashMap({ dimension: true }),
prepareGetValue: function prepareGetValue(exprOption) {
var errMsg = ''
var dimLoose = exprOption.dimension
if (!hasOwn(exprOption, 'dimension')) {
throwError(errMsg)
}
var dimInfo = upstream.getDimensionInfo(dimLoose)
if (!dimInfo) {
throwError(errMsg)
}
return { dimIdx: dimInfo.index }
},
getValue: function getValue(param) {
return upstream.retrieveValueFromItem(rawItem, param.dimIdx)
}
})
var resultData = []
for (var i = 0, len = upstream.count(); i < len; i++) {
rawItem = upstream.getRawDataItem(i)
if (condition.evaluate()) {
resultData.push(rawItem)
}
}
return { data: resultData }
}
}
var sortTransform = {
type: 'echarts:sort',
transform: function transform(params) {
var upstream = params.upstream
var config = params.config
var errMsg = '' // Normalize
// const orderExprList: OrderExpression[] = isArray(config[0])
// ? config as OrderExpression[]
// : [config as OrderExpression];
var orderExprList = normalizeToArray(config)
if (!orderExprList.length) {
throwError(errMsg)
}
var orderDefList = []
each$7(orderExprList, function (orderExpr) {
var dimLoose = orderExpr.dimension
var order = orderExpr.order
var parserName = orderExpr.parser
var incomparable = orderExpr.incomparable
if (dimLoose == null) {
throwError(errMsg)
}
if (order !== 'asc' && order !== 'desc') {
throwError(errMsg)
}
if (
incomparable &&
incomparable !== 'min' &&
incomparable !== 'max'
) {
var errMsg_1 = ''
throwError(errMsg_1)
}
if (order !== 'asc' && order !== 'desc') {
var errMsg_2 = ''
throwError(errMsg_2)
}
var dimInfo = upstream.getDimensionInfo(dimLoose)
if (!dimInfo) {
throwError(errMsg)
}
var parser = parserName ? getRawValueParser(parserName) : null
if (parserName && !parser) {
throwError(errMsg)
}
orderDefList.push({
dimIdx: dimInfo.index,
parser: parser,
comparator: new SortOrderComparator(order, incomparable)
})
}) // TODO: support it?
var sourceFormat = upstream.sourceFormat
if (
sourceFormat !== SOURCE_FORMAT_ARRAY_ROWS &&
sourceFormat !== SOURCE_FORMAT_OBJECT_ROWS
) {
throwError(errMsg)
} // Other upstream format are all array.
var resultData = []
for (var i = 0, len = upstream.count(); i < len; i++) {
resultData.push(upstream.getRawDataItem(i))
}
resultData.sort(function (item0, item1) {
for (var i = 0; i < orderDefList.length; i++) {
var orderDef = orderDefList[i]
var val0 = upstream.retrieveValueFromItem(
item0,
orderDef.dimIdx
)
var val1 = upstream.retrieveValueFromItem(
item1,
orderDef.dimIdx
)
if (orderDef.parser) {
val0 = orderDef.parser(val0)
val1 = orderDef.parser(val1)
}
var result = orderDef.comparator.evaluate(val0, val1)
if (result !== 0) {
return result
}
}
return 0
})
return { data: resultData }
}
}
function install$2(registers) {
registers.registerTransform(filterTransform)
registers.registerTransform(sortTransform)
}
var DatasetModel = /** @class */ (function (_super) {
__extends(DatasetModel, _super)
function DatasetModel() {
var _this =
(_super !== null && _super.apply(this, arguments)) || this
_this.type = 'dataset'
return _this
}
DatasetModel.prototype.init = function (
option,
parentModel,
ecModel
) {
_super.prototype.init.call(this, option, parentModel, ecModel)
this._sourceManager = new SourceManager(this)
disableTransformOptionMerge(this)
}
DatasetModel.prototype.mergeOption = function (newOption, ecModel) {
_super.prototype.mergeOption.call(this, newOption, ecModel)
disableTransformOptionMerge(this)
}
DatasetModel.prototype.optionUpdated = function () {
this._sourceManager.dirty()
}
DatasetModel.prototype.getSourceManager = function () {
return this._sourceManager
}
DatasetModel.type = 'dataset'
DatasetModel.defaultOption = {
seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN
}
return DatasetModel
})(ComponentModel$1)
var DatasetView = /** @class */ (function (_super) {
__extends(DatasetView, _super)
function DatasetView() {
var _this =
(_super !== null && _super.apply(this, arguments)) || this
_this.type = 'dataset'
return _this
}
DatasetView.type = 'dataset'
return DatasetView
})(ComponentView$1)
function install$1(registers) {
registers.registerComponentModel(DatasetModel)
registers.registerComponentView(DatasetView)
}
var CMD = PathProxy$1.CMD
function aroundEqual(a, b) {
return Math.abs(a - b) < 1e-5
}
function pathToBezierCurves(path) {
var data = path.data
var len = path.len()
var bezierArrayGroups = []
var currentSubpath
var xi = 0
var yi = 0
var x0 = 0
var y0 = 0
function createNewSubpath(x, y) {
if (currentSubpath && currentSubpath.length > 2) {
bezierArrayGroups.push(currentSubpath)
}
currentSubpath = [x, y]
}
function addLine(x0, y0, x1, y1) {
if (!(aroundEqual(x0, x1) && aroundEqual(y0, y1))) {
currentSubpath.push(x0, y0, x1, y1, x1, y1)
}
}
function addArc(startAngle, endAngle, cx, cy, rx, ry) {
var delta = Math.abs(endAngle - startAngle)
var len = (Math.tan(delta / 4) * 4) / 3
var dir = endAngle < startAngle ? -1 : 1
var c1 = Math.cos(startAngle)
var s1 = Math.sin(startAngle)
var c2 = Math.cos(endAngle)
var s2 = Math.sin(endAngle)
var x1 = c1 * rx + cx
var y1 = s1 * ry + cy
var x4 = c2 * rx + cx
var y4 = s2 * ry + cy
var hx = rx * len * dir
var hy = ry * len * dir
currentSubpath.push(
x1 - hx * s1,
y1 + hy * c1,
x4 + hx * s2,
y4 - hy * c2,
x4,
y4
)
}
var x1
var y1
var x2
var y2
for (var i = 0; i < len; ) {
var cmd = data[i++]
var isFirst = i === 1
if (isFirst) {
xi = data[i]
yi = data[i + 1]
x0 = xi
y0 = yi
if (cmd === CMD.L || cmd === CMD.C || cmd === CMD.Q) {
currentSubpath = [x0, y0]
}
}
switch (cmd) {
case CMD.M:
xi = x0 = data[i++]
yi = y0 = data[i++]
createNewSubpath(x0, y0)
break
case CMD.L:
x1 = data[i++]
y1 = data[i++]
addLine(xi, yi, x1, y1)
xi = x1
yi = y1
break
case CMD.C:
currentSubpath.push(
data[i++],
data[i++],
data[i++],
data[i++],
(xi = data[i++]),
(yi = data[i++])
)
break
case CMD.Q:
x1 = data[i++]
y1 = data[i++]
x2 = data[i++]
y2 = data[i++]
currentSubpath.push(
xi + (2 / 3) * (x1 - xi),
yi + (2 / 3) * (y1 - yi),
x2 + (2 / 3) * (x1 - x2),
y2 + (2 / 3) * (y1 - y2),
x2,
y2
)
xi = x2
yi = y2
break
case CMD.A:
var cx = data[i++]
var cy = data[i++]
var rx = data[i++]
var ry = data[i++]
var startAngle = data[i++]
var endAngle = data[i++] + startAngle
i += 1
var anticlockwise = !data[i++]
x1 = Math.cos(startAngle) * rx + cx
y1 = Math.sin(startAngle) * ry + cy
if (isFirst) {
x0 = x1
y0 = y1
createNewSubpath(x0, y0)
} else {
addLine(xi, yi, x1, y1)
}
xi = Math.cos(endAngle) * rx + cx
yi = Math.sin(endAngle) * ry + cy
var step = ((anticlockwise ? -1 : 1) * Math.PI) / 2
for (
var angle = startAngle;
anticlockwise ? angle > endAngle : angle < endAngle;
angle += step
) {
var nextAngle = anticlockwise
? Math.max(angle + step, endAngle)
: Math.min(angle + step, endAngle)
addArc(angle, nextAngle, cx, cy, rx, ry)
}
break
case CMD.R:
x0 = xi = data[i++]
y0 = yi = data[i++]
x1 = x0 + data[i++]
y1 = y0 + data[i++]
createNewSubpath(x1, y0)
addLine(x1, y0, x1, y1)
addLine(x1, y1, x0, y1)
addLine(x0, y1, x0, y0)
addLine(x0, y0, x1, y0)
break
case CMD.Z:
currentSubpath && addLine(xi, yi, x0, y0)
xi = x0
yi = y0
break
}
}
if (currentSubpath && currentSubpath.length > 2) {
bezierArrayGroups.push(currentSubpath)
}
return bezierArrayGroups
}
function adpativeBezier(x0, y0, x1, y1, x2, y2, x3, y3, out, scale) {
if (
aroundEqual(x0, x1) &&
aroundEqual(y0, y1) &&
aroundEqual(x2, x3) &&
aroundEqual(y2, y3)
) {
out.push(x3, y3)
return
}
var PIXEL_DISTANCE = 2 / scale
var PIXEL_DISTANCE_SQR = PIXEL_DISTANCE * PIXEL_DISTANCE
var dx = x3 - x0
var dy = y3 - y0
var d = Math.sqrt(dx * dx + dy * dy)
dx /= d
dy /= d
var dx1 = x1 - x0
var dy1 = y1 - y0
var dx2 = x2 - x3
var dy2 = y2 - y3
var cp1LenSqr = dx1 * dx1 + dy1 * dy1
var cp2LenSqr = dx2 * dx2 + dy2 * dy2
if (
cp1LenSqr < PIXEL_DISTANCE_SQR &&
cp2LenSqr < PIXEL_DISTANCE_SQR
) {
out.push(x3, y3)
return
}
var projLen1 = dx * dx1 + dy * dy1
var projLen2 = -dx * dx2 - dy * dy2
var d1Sqr = cp1LenSqr - projLen1 * projLen1
var d2Sqr = cp2LenSqr - projLen2 * projLen2
if (
d1Sqr < PIXEL_DISTANCE_SQR &&
projLen1 >= 0 &&
d2Sqr < PIXEL_DISTANCE_SQR &&
projLen2 >= 0
) {
out.push(x3, y3)
return
}
var tmpSegX = []
var tmpSegY = []
cubicSubdivide(x0, x1, x2, x3, 0.5, tmpSegX)
cubicSubdivide(y0, y1, y2, y3, 0.5, tmpSegY)
adpativeBezier(
tmpSegX[0],
tmpSegY[0],
tmpSegX[1],
tmpSegY[1],
tmpSegX[2],
tmpSegY[2],
tmpSegX[3],
tmpSegY[3],
out,
scale
)
adpativeBezier(
tmpSegX[4],
tmpSegY[4],
tmpSegX[5],
tmpSegY[5],
tmpSegX[6],
tmpSegY[6],
tmpSegX[7],
tmpSegY[7],
out,
scale
)
}
function pathToPolygons(path, scale) {
var bezierArrayGroups = pathToBezierCurves(path)
var polygons = []
scale = scale || 1
for (var i = 0; i < bezierArrayGroups.length; i++) {
var beziers = bezierArrayGroups[i]
var polygon = []
var x0 = beziers[0]
var y0 = beziers[1]
polygon.push(x0, y0)
for (var k = 2; k < beziers.length; ) {
var x1 = beziers[k++]
var y1 = beziers[k++]
var x2 = beziers[k++]
var y2 = beziers[k++]
var x3 = beziers[k++]
var y3 = beziers[k++]
adpativeBezier(x0, y0, x1, y1, x2, y2, x3, y3, polygon, scale)
x0 = x3
y0 = y3
}
polygons.push(polygon)
}
return polygons
}
function getDividingGrids(dimSize, rowDim, count) {
var rowSize = dimSize[rowDim]
var columnSize = dimSize[1 - rowDim]
var ratio = Math.abs(rowSize / columnSize)
var rowCount = Math.ceil(Math.sqrt(ratio * count))
var columnCount = Math.floor(count / rowCount)
if (columnCount === 0) {
columnCount = 1
rowCount = count
}
var grids = []
for (var i = 0; i < rowCount; i++) {
grids.push(columnCount)
}
var currentCount = rowCount * columnCount
var remained = count - currentCount
if (remained > 0) {
for (var i = 0; i < remained; i++) {
grids[i % rowCount] += 1
}
}
return grids
}
function divideSector(sectorShape, count, outShapes) {
var r0 = sectorShape.r0
var r = sectorShape.r
var startAngle = sectorShape.startAngle
var endAngle = sectorShape.endAngle
var angle = Math.abs(endAngle - startAngle)
var arcLen = angle * r
var deltaR = r - r0
var isAngleRow = arcLen > Math.abs(deltaR)
var grids = getDividingGrids(
[arcLen, deltaR],
isAngleRow ? 0 : 1,
count
)
var rowSize = (isAngleRow ? angle : deltaR) / grids.length
for (var row = 0; row < grids.length; row++) {
var columnSize = (isAngleRow ? deltaR : angle) / grids[row]
for (var column = 0; column < grids[row]; column++) {
var newShape = {}
if (isAngleRow) {
newShape.startAngle = startAngle + rowSize * row
newShape.endAngle = startAngle + rowSize * (row + 1)
newShape.r0 = r0 + columnSize * column
newShape.r = r0 + columnSize * (column + 1)
} else {
newShape.startAngle = startAngle + columnSize * column
newShape.endAngle = startAngle + columnSize * (column + 1)
newShape.r0 = r0 + rowSize * row
newShape.r = r0 + rowSize * (row + 1)
}
newShape.clockwise = sectorShape.clockwise
newShape.cx = sectorShape.cx
newShape.cy = sectorShape.cy
outShapes.push(newShape)
}
}
}
function divideRect(rectShape, count, outShapes) {
var width = rectShape.width
var height = rectShape.height
var isHorizontalRow = width > height
var grids = getDividingGrids(
[width, height],
isHorizontalRow ? 0 : 1,
count
)
var rowSizeDim = isHorizontalRow ? 'width' : 'height'
var columnSizeDim = isHorizontalRow ? 'height' : 'width'
var rowDim = isHorizontalRow ? 'x' : 'y'
var columnDim = isHorizontalRow ? 'y' : 'x'
var rowSize = rectShape[rowSizeDim] / grids.length
for (var row = 0; row < grids.length; row++) {
var columnSize = rectShape[columnSizeDim] / grids[row]
for (var column = 0; column < grids[row]; column++) {
var newShape = {}
newShape[rowDim] = row * rowSize
newShape[columnDim] = column * columnSize
newShape[rowSizeDim] = rowSize
newShape[columnSizeDim] = columnSize
newShape.x += rectShape.x
newShape.y += rectShape.y
outShapes.push(newShape)
}
}
}
function crossProduct2d(x1, y1, x2, y2) {
return x1 * y2 - x2 * y1
}
function lineLineIntersect(a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y) {
var mx = a2x - a1x
var my = a2y - a1y
var nx = b2x - b1x
var ny = b2y - b1y
var nmCrossProduct = crossProduct2d(nx, ny, mx, my)
if (Math.abs(nmCrossProduct) < 1e-6) {
return null
}
var b1a1x = a1x - b1x
var b1a1y = a1y - b1y
var p = crossProduct2d(b1a1x, b1a1y, nx, ny) / nmCrossProduct
if (p < 0 || p > 1) {
return null
}
return new Point$1(p * mx + a1x, p * my + a1y)
}
function projPtOnLine(pt, lineA, lineB) {
var dir = new Point$1()
Point$1.sub(dir, lineB, lineA)
dir.normalize()
var dir2 = new Point$1()
Point$1.sub(dir2, pt, lineA)
var len = dir2.dot(dir)
return len
}
function addToPoly(poly, pt) {
var last = poly[poly.length - 1]
if (last && last[0] === pt[0] && last[1] === pt[1]) {
return
}
poly.push(pt)
}
function splitPolygonByLine(points, lineA, lineB) {
var len = points.length
var intersections = []
for (var i = 0; i < len; i++) {
var p0 = points[i]
var p1 = points[(i + 1) % len]
var intersectionPt = lineLineIntersect(
p0[0],
p0[1],
p1[0],
p1[1],
lineA.x,
lineA.y,
lineB.x,
lineB.y
)
if (intersectionPt) {
intersections.push({
projPt: projPtOnLine(intersectionPt, lineA, lineB),
pt: intersectionPt,
idx: i
})
}
}
if (intersections.length < 2) {
return [{ points: points }, { points: points }]
}
intersections.sort(function (a, b) {
return a.projPt - b.projPt
})
var splitPt0 = intersections[0]
var splitPt1 = intersections[intersections.length - 1]
if (splitPt1.idx < splitPt0.idx) {
var tmp = splitPt0
splitPt0 = splitPt1
splitPt1 = tmp
}
var splitPt0Arr = [splitPt0.pt.x, splitPt0.pt.y]
var splitPt1Arr = [splitPt1.pt.x, splitPt1.pt.y]
var newPolyA = [splitPt0Arr]
var newPolyB = [splitPt1Arr]
for (var i = splitPt0.idx + 1; i <= splitPt1.idx; i++) {
addToPoly(newPolyA, points[i].slice())
}
addToPoly(newPolyA, splitPt1Arr)
addToPoly(newPolyA, splitPt0Arr)
for (var i = splitPt1.idx + 1; i <= splitPt0.idx + len; i++) {
addToPoly(newPolyB, points[i % len].slice())
}
addToPoly(newPolyB, splitPt0Arr)
addToPoly(newPolyB, splitPt1Arr)
return [{ points: newPolyA }, { points: newPolyB }]
}
function binaryDividePolygon(polygonShape) {
var points = polygonShape.points
var min = []
var max = []
fromPoints(points, min, max)
var boundingRect = new BoundingRect$1(
min[0],
min[1],
max[0] - min[0],
max[1] - min[1]
)
var width = boundingRect.width
var height = boundingRect.height
var x = boundingRect.x
var y = boundingRect.y
var pt0 = new Point$1()
var pt1 = new Point$1()
if (width > height) {
pt0.x = pt1.x = x + width / 2
pt0.y = y
pt1.y = y + height
} else {
pt0.y = pt1.y = y + height / 2
pt0.x = x
pt1.x = x + width
}
return splitPolygonByLine(points, pt0, pt1)
}
function binaryDivideRecursive(divider, shape, count, out) {
if (count === 1) {
out.push(shape)
} else {
var mid = Math.floor(count / 2)
var sub = divider(shape)
binaryDivideRecursive(divider, sub[0], mid, out)
binaryDivideRecursive(divider, sub[1], count - mid, out)
}
return out
}
function clone(path, count) {
var paths = []
for (var i = 0; i < count; i++) {
paths.push(clonePath(path))
}
return paths
}
function copyPathProps(source, target) {
target.setStyle(source.style)
target.z = source.z
target.z2 = source.z2
target.zlevel = source.zlevel
}
function polygonConvert(points) {
var out = []
for (var i = 0; i < points.length; ) {
out.push([points[i++], points[i++]])
}
return out
}
function split(path, count) {
var outShapes = []
var shape = path.shape
var OutShapeCtor
switch (path.type) {
case 'rect':
divideRect(shape, count, outShapes)
OutShapeCtor = Rect$2
break
case 'sector':
divideSector(shape, count, outShapes)
OutShapeCtor = Sector$1
break
case 'circle':
divideSector(
{
r0: 0,
r: shape.r,
startAngle: 0,
endAngle: Math.PI * 2,
cx: shape.cx,
cy: shape.cy
},
count,
outShapes
)
OutShapeCtor = Sector$1
break
default:
var m = path.getComputedTransform()
var scale = m
? Math.sqrt(
Math.max(
m[0] * m[0] + m[1] * m[1],
m[2] * m[2] + m[3] * m[3]
)
)
: 1
var polygons = map$1(
pathToPolygons(path.getUpdatedPathProxy(), scale),
function (poly) {
return polygonConvert(poly)
}
)
var polygonCount = polygons.length
if (polygonCount === 0) {
binaryDivideRecursive(
binaryDividePolygon,
{ points: polygons[0] },
count,
outShapes
)
} else if (polygonCount === count) {
for (var i = 0; i < polygonCount; i++) {
outShapes.push({ points: polygons[i] })
}
} else {
var totalArea_1 = 0
var items = map$1(polygons, function (poly) {
var min = []
var max = []
fromPoints(poly, min, max)
var area = (max[1] - min[1]) * (max[0] - min[0])
totalArea_1 += area
return { poly: poly, area: area }
})
items.sort(function (a, b) {
return b.area - a.area
})
var left = count
for (var i = 0; i < polygonCount; i++) {
var item = items[i]
if (left <= 0) {
break
}
var selfCount =
i === polygonCount - 1
? left
: Math.ceil((item.area / totalArea_1) * count)
if (selfCount < 0) {
continue
}
binaryDivideRecursive(
binaryDividePolygon,
{ points: item.poly },
selfCount,
outShapes
)
left -= selfCount
}
}
OutShapeCtor = Polygon$1
break
}
if (!OutShapeCtor) {
return clone(path, count)
}
var out = []
for (var i = 0; i < outShapes.length; i++) {
var subPath = new OutShapeCtor()
subPath.setShape(outShapes[i])
copyPathProps(path, subPath)
out.push(subPath)
}
return out
}
function alignSubpath(subpath1, subpath2) {
var len1 = subpath1.length
var len2 = subpath2.length
if (len1 === len2) {
return [subpath1, subpath2]
}
var tmpSegX = []
var tmpSegY = []
var shorterPath = len1 < len2 ? subpath1 : subpath2
var shorterLen = Math.min(len1, len2)
var diff = Math.abs(len2 - len1) / 6
var shorterBezierCount = (shorterLen - 2) / 6
var eachCurveSubDivCount = Math.ceil(diff / shorterBezierCount) + 1
var newSubpath = [shorterPath[0], shorterPath[1]]
var remained = diff
for (var i = 2; i < shorterLen; ) {
var x0 = shorterPath[i - 2]
var y0 = shorterPath[i - 1]
var x1 = shorterPath[i++]
var y1 = shorterPath[i++]
var x2 = shorterPath[i++]
var y2 = shorterPath[i++]
var x3 = shorterPath[i++]
var y3 = shorterPath[i++]
if (remained <= 0) {
newSubpath.push(x1, y1, x2, y2, x3, y3)
continue
}
var actualSubDivCount =
Math.min(remained, eachCurveSubDivCount - 1) + 1
for (var k = 1; k <= actualSubDivCount; k++) {
var p = k / actualSubDivCount
cubicSubdivide(x0, x1, x2, x3, p, tmpSegX)
cubicSubdivide(y0, y1, y2, y3, p, tmpSegY)
x0 = tmpSegX[3]
y0 = tmpSegY[3]
newSubpath.push(
tmpSegX[1],
tmpSegY[1],
tmpSegX[2],
tmpSegY[2],
x0,
y0
)
x1 = tmpSegX[5]
y1 = tmpSegY[5]
x2 = tmpSegX[6]
y2 = tmpSegY[6]
}
remained -= actualSubDivCount - 1
}
return shorterPath === subpath1
? [newSubpath, subpath2]
: [subpath1, newSubpath]
}
function createSubpath(lastSubpathSubpath, otherSubpath) {
var len = lastSubpathSubpath.length
var lastX = lastSubpathSubpath[len - 2]
var lastY = lastSubpathSubpath[len - 1]
var newSubpath = []
for (var i = 0; i < otherSubpath.length; ) {
newSubpath[i++] = lastX
newSubpath[i++] = lastY
}
return newSubpath
}
function alignBezierCurves(array1, array2) {
var _a
var lastSubpath1
var lastSubpath2
var newArray1 = []
var newArray2 = []
for (var i = 0; i < Math.max(array1.length, array2.length); i++) {
var subpath1 = array1[i]
var subpath2 = array2[i]
var newSubpath1 = void 0
var newSubpath2 = void 0
if (!subpath1) {
newSubpath1 = createSubpath(lastSubpath1 || subpath2, subpath2)
newSubpath2 = subpath2
} else if (!subpath2) {
newSubpath2 = createSubpath(lastSubpath2 || subpath1, subpath1)
newSubpath1 = subpath1
} else {
;(_a = alignSubpath(subpath1, subpath2)),
(newSubpath1 = _a[0]),
(newSubpath2 = _a[1])
lastSubpath1 = newSubpath1
lastSubpath2 = newSubpath2
}
newArray1.push(newSubpath1)
newArray2.push(newSubpath2)
}
return [newArray1, newArray2]
}
function centroid(array) {
var signedArea = 0
var cx = 0
var cy = 0
var len = array.length
for (var i = 0, j = len - 2; i < len; j = i, i += 2) {
var x0 = array[j]
var y0 = array[j + 1]
var x1 = array[i]
var y1 = array[i + 1]
var a = x0 * y1 - x1 * y0
signedArea += a
cx += (x0 + x1) * a
cy += (y0 + y1) * a
}
if (signedArea === 0) {
return [array[0] || 0, array[1] || 0]
}
return [cx / signedArea / 3, cy / signedArea / 3, signedArea]
}
function findBestRingOffset(
fromSubBeziers,
toSubBeziers,
fromCp,
toCp
) {
var bezierCount = (fromSubBeziers.length - 2) / 6
var bestScore = Infinity
var bestOffset = 0
var len = fromSubBeziers.length
var len2 = len - 2
for (var offset = 0; offset < bezierCount; offset++) {
var cursorOffset = offset * 6
var score = 0
for (var k = 0; k < len; k += 2) {
var idx =
k === 0 ? cursorOffset : ((cursorOffset + k - 2) % len2) + 2
var x0 = fromSubBeziers[idx] - fromCp[0]
var y0 = fromSubBeziers[idx + 1] - fromCp[1]
var x1 = toSubBeziers[k] - toCp[0]
var y1 = toSubBeziers[k + 1] - toCp[1]
var dx = x1 - x0
var dy = y1 - y0
score += dx * dx + dy * dy
}
if (score < bestScore) {
bestScore = score
bestOffset = offset
}
}
return bestOffset
}
function reverse(array) {
var newArr = []
var len = array.length
for (var i = 0; i < len; i += 2) {
newArr[i] = array[len - i - 2]
newArr[i + 1] = array[len - i - 1]
}
return newArr
}
function findBestMorphingRotation(
fromArr,
toArr,
searchAngleIteration,
searchAngleRange
) {
var result = []
var fromNeedsReverse
for (var i = 0; i < fromArr.length; i++) {
var fromSubpathBezier = fromArr[i]
var toSubpathBezier = toArr[i]
var fromCp = centroid(fromSubpathBezier)
var toCp = centroid(toSubpathBezier)
if (fromNeedsReverse == null) {
fromNeedsReverse = fromCp[2] < 0 !== toCp[2] < 0
}
var newFromSubpathBezier = []
var newToSubpathBezier = []
var bestAngle = 0
var bestScore = Infinity
var tmpArr = []
var len = fromSubpathBezier.length
if (fromNeedsReverse) {
fromSubpathBezier = reverse(fromSubpathBezier)
}
var offset =
findBestRingOffset(
fromSubpathBezier,
toSubpathBezier,
fromCp,
toCp
) * 6
var len2 = len - 2
for (var k = 0; k < len2; k += 2) {
var idx = ((offset + k) % len2) + 2
newFromSubpathBezier[k + 2] = fromSubpathBezier[idx] - fromCp[0]
newFromSubpathBezier[k + 3] =
fromSubpathBezier[idx + 1] - fromCp[1]
}
newFromSubpathBezier[0] = fromSubpathBezier[offset] - fromCp[0]
newFromSubpathBezier[1] =
fromSubpathBezier[offset + 1] - fromCp[1]
if (searchAngleIteration > 0) {
var step = searchAngleRange / searchAngleIteration
for (
var angle = -searchAngleRange / 2;
angle <= searchAngleRange / 2;
angle += step
) {
var sa = Math.sin(angle)
var ca = Math.cos(angle)
var score = 0
for (var k = 0; k < fromSubpathBezier.length; k += 2) {
var x0 = newFromSubpathBezier[k]
var y0 = newFromSubpathBezier[k + 1]
var x1 = toSubpathBezier[k] - toCp[0]
var y1 = toSubpathBezier[k + 1] - toCp[1]
var newX1 = x1 * ca - y1 * sa
var newY1 = x1 * sa + y1 * ca
tmpArr[k] = newX1
tmpArr[k + 1] = newY1
var dx = newX1 - x0
var dy = newY1 - y0
score += dx * dx + dy * dy
}
if (score < bestScore) {
bestScore = score
bestAngle = angle
for (var m = 0; m < tmpArr.length; m++) {
newToSubpathBezier[m] = tmpArr[m]
}
}
}
} else {
for (var i_1 = 0; i_1 < len; i_1 += 2) {
newToSubpathBezier[i_1] = toSubpathBezier[i_1] - toCp[0]
newToSubpathBezier[i_1 + 1] =
toSubpathBezier[i_1 + 1] - toCp[1]
}
}
result.push({
from: newFromSubpathBezier,
to: newToSubpathBezier,
fromCp: fromCp,
toCp: toCp,
rotation: -bestAngle
})
}
return result
}
function isCombineMorphing(path) {
return path.__isCombineMorphing
}
var SAVED_METHOD_PREFIX = '__mOriginal_'
function saveAndModifyMethod(obj, methodName, modifiers) {
var savedMethodName = SAVED_METHOD_PREFIX + methodName
var originalMethod = obj[savedMethodName] || obj[methodName]
if (!obj[savedMethodName]) {
obj[savedMethodName] = obj[methodName]
}
var replace = modifiers.replace
var after = modifiers.after
var before = modifiers.before
obj[methodName] = function () {
var args = arguments
var res
before && before.apply(this, args)
if (replace) {
res = replace.apply(this, args)
} else {
res = originalMethod.apply(this, args)
}
after && after.apply(this, args)
return res
}
}
function restoreMethod(obj, methodName) {
var savedMethodName = SAVED_METHOD_PREFIX + methodName
if (obj[savedMethodName]) {
obj[methodName] = obj[savedMethodName]
obj[savedMethodName] = null
}
}
function applyTransformOnBeziers(bezierCurves, mm) {
for (var i = 0; i < bezierCurves.length; i++) {
var subBeziers = bezierCurves[i]
for (var k = 0; k < subBeziers.length; ) {
var x = subBeziers[k]
var y = subBeziers[k + 1]
subBeziers[k++] = mm[0] * x + mm[2] * y + mm[4]
subBeziers[k++] = mm[1] * x + mm[3] * y + mm[5]
}
}
}
function prepareMorphPath(fromPath, toPath) {
var fromPathProxy = fromPath.getUpdatedPathProxy()
var toPathProxy = toPath.getUpdatedPathProxy()
var _a = alignBezierCurves(
pathToBezierCurves(fromPathProxy),
pathToBezierCurves(toPathProxy)
),
fromBezierCurves = _a[0],
toBezierCurves = _a[1]
var fromPathTransform = fromPath.getComputedTransform()
var toPathTransform = toPath.getComputedTransform()
function updateIdentityTransform() {
this.transform = null
}
fromPathTransform &&
applyTransformOnBeziers(fromBezierCurves, fromPathTransform)
toPathTransform &&
applyTransformOnBeziers(toBezierCurves, toPathTransform)
saveAndModifyMethod(toPath, 'updateTransform', {
replace: updateIdentityTransform
})
toPath.transform = null
var morphingData = findBestMorphingRotation(
fromBezierCurves,
toBezierCurves,
10,
Math.PI
)
var tmpArr = []
saveAndModifyMethod(toPath, 'buildPath', {
replace: function replace(path) {
var t = toPath.__morphT
var onet = 1 - t
var newCp = []
for (var i = 0; i < morphingData.length; i++) {
var item = morphingData[i]
var from = item.from
var to = item.to
var angle = item.rotation * t
var fromCp = item.fromCp
var toCp = item.toCp
var sa = Math.sin(angle)
var ca = Math.cos(angle)
lerp$1(newCp, fromCp, toCp, t)
for (var m = 0; m < from.length; m += 2) {
var x0_1 = from[m]
var y0_1 = from[m + 1]
var x1 = to[m]
var y1 = to[m + 1]
var x = x0_1 * onet + x1 * t
var y = y0_1 * onet + y1 * t
tmpArr[m] = x * ca - y * sa + newCp[0]
tmpArr[m + 1] = x * sa + y * ca + newCp[1]
}
var x0 = tmpArr[0]
var y0 = tmpArr[1]
path.moveTo(x0, y0)
for (var m = 2; m < from.length; ) {
var x1 = tmpArr[m++]
var y1 = tmpArr[m++]
var x2 = tmpArr[m++]
var y2 = tmpArr[m++]
var x3 = tmpArr[m++]
var y3 = tmpArr[m++]
if (x0 === x1 && y0 === y1 && x2 === x3 && y2 === y3) {
path.lineTo(x3, y3)
} else {
path.bezierCurveTo(x1, y1, x2, y2, x3, y3)
}
x0 = x3
y0 = y3
}
}
}
})
}
function morphPath(fromPath, toPath, animationOpts) {
if (!fromPath || !toPath) {
return toPath
}
var oldDone = animationOpts.done
var oldDuring = animationOpts.during
prepareMorphPath(fromPath, toPath)
toPath.__morphT = 0
function restoreToPath() {
restoreMethod(toPath, 'buildPath')
restoreMethod(toPath, 'updateTransform')
toPath.__morphT = -1
toPath.createPathProxy()
toPath.dirtyShape()
}
toPath.animateTo(
{ __morphT: 1 },
defaults(
{
during: function during(p) {
toPath.dirtyShape()
oldDuring && oldDuring(p)
},
done: function done() {
restoreToPath()
oldDone && oldDone()
}
},
animationOpts
)
)
return toPath
}
function hilbert(x, y, minX, minY, maxX, maxY) {
var bits = 16
x =
maxX === minX
? 0
: Math.round((32767 * (x - minX)) / (maxX - minX))
y =
maxY === minY
? 0
: Math.round((32767 * (y - minY)) / (maxY - minY))
var d = 0
var tmp
for (var s = (1 << bits) / 2; s > 0; s /= 2) {
var rx = 0
var ry = 0
if ((x & s) > 0) {
rx = 1
}
if ((y & s) > 0) {
ry = 1
}
d += s * s * ((3 * rx) ^ ry)
if (ry === 0) {
if (rx === 1) {
x = s - 1 - x
y = s - 1 - y
}
tmp = x
x = y
y = tmp
}
}
return d
}
function sortPaths(pathList) {
var xMin = Infinity
var yMin = Infinity
var xMax = -Infinity
var yMax = -Infinity
var cps = map$1(pathList, function (path) {
var rect = path.getBoundingRect()
var m = path.getComputedTransform()
var x = rect.x + rect.width / 2 + (m ? m[4] : 0)
var y = rect.y + rect.height / 2 + (m ? m[5] : 0)
xMin = Math.min(x, xMin)
yMin = Math.min(y, yMin)
xMax = Math.max(x, xMax)
yMax = Math.max(y, yMax)
return [x, y]
})
var items = map$1(cps, function (cp, idx) {
return {
cp: cp,
z: hilbert(cp[0], cp[1], xMin, yMin, xMax, yMax),
path: pathList[idx]
}
})
return items
.sort(function (a, b) {
return a.z - b.z
})
.map(function (item) {
return item.path
})
}
function defaultDividePath(param) {
return split(param.path, param.count)
}
function createEmptyReturn() {
return { fromIndividuals: [], toIndividuals: [], count: 0 }
}
function combineMorph(fromList, toPath, animationOpts) {
var fromPathList = []
function addFromPath(fromList) {
for (var i = 0; i < fromList.length; i++) {
var from = fromList[i]
if (isCombineMorphing(from)) {
addFromPath(from.childrenRef())
} else if (from instanceof Path$1) {
fromPathList.push(from)
}
}
}
addFromPath(fromList)
var separateCount = fromPathList.length
if (!separateCount) {
return createEmptyReturn()
}
var dividePath = animationOpts.dividePath || defaultDividePath
var toSubPathList = dividePath({
path: toPath,
count: separateCount
})
if (toSubPathList.length !== separateCount) {
console.error('Invalid morphing: unmatched splitted path')
return createEmptyReturn()
}
fromPathList = sortPaths(fromPathList)
toSubPathList = sortPaths(toSubPathList)
var oldDone = animationOpts.done
var oldDuring = animationOpts.during
var individualDelay = animationOpts.individualDelay
var identityTransform = new Transformable()
for (var i = 0; i < separateCount; i++) {
var from = fromPathList[i]
var to = toSubPathList[i]
to.parent = toPath
to.copyTransform(identityTransform)
if (!individualDelay) {
prepareMorphPath(from, to)
}
}
toPath.__isCombineMorphing = true
toPath.childrenRef = function () {
return toSubPathList
}
function addToSubPathListToZr(zr) {
for (var i = 0; i < toSubPathList.length; i++) {
toSubPathList[i].addSelfToZr(zr)
}
}
saveAndModifyMethod(toPath, 'addSelfToZr', {
after: function after(zr) {
addToSubPathListToZr(zr)
}
})
saveAndModifyMethod(toPath, 'removeSelfFromZr', {
after: function after(zr) {
for (var i = 0; i < toSubPathList.length; i++) {
toSubPathList[i].removeSelfFromZr(zr)
}
}
})
function restoreToPath() {
toPath.__isCombineMorphing = false
toPath.__morphT = -1
toPath.childrenRef = null
restoreMethod(toPath, 'addSelfToZr')
restoreMethod(toPath, 'removeSelfFromZr')
}
var toLen = toSubPathList.length
if (individualDelay) {
var animating_1 = toLen
var eachDone = function eachDone() {
animating_1--
if (animating_1 === 0) {
restoreToPath()
oldDone && oldDone()
}
}
for (var i = 0; i < toLen; i++) {
var indivdualAnimationOpts = individualDelay
? defaults(
{
delay:
(animationOpts.delay || 0) +
individualDelay(
i,
toLen,
fromPathList[i],
toSubPathList[i]
),
done: eachDone
},
animationOpts
)
: animationOpts
morphPath(
fromPathList[i],
toSubPathList[i],
indivdualAnimationOpts
)
}
} else {
toPath.__morphT = 0
toPath.animateTo(
{ __morphT: 1 },
defaults(
{
during: function during(p) {
for (var i = 0; i < toLen; i++) {
var child = toSubPathList[i]
child.__morphT = toPath.__morphT
child.dirtyShape()
}
oldDuring && oldDuring(p)
},
done: function done() {
restoreToPath()
for (var i = 0; i < fromList.length; i++) {
restoreMethod(fromList[i], 'updateTransform')
}
oldDone && oldDone()
}
},
animationOpts
)
)
}
if (toPath.__zr) {
addToSubPathListToZr(toPath.__zr)
}
return {
fromIndividuals: fromPathList,
toIndividuals: toSubPathList,
count: toLen
}
}
function separateMorph(fromPath, toPathList, animationOpts) {
var toLen = toPathList.length
var fromPathList = []
var dividePath = animationOpts.dividePath || defaultDividePath
function addFromPath(fromList) {
for (var i = 0; i < fromList.length; i++) {
var from = fromList[i]
if (isCombineMorphing(from)) {
addFromPath(from.childrenRef())
} else if (from instanceof Path$1) {
fromPathList.push(from)
}
}
}
if (isCombineMorphing(fromPath)) {
addFromPath(fromPath.childrenRef())
var fromLen = fromPathList.length
if (fromLen < toLen) {
var k = 0
for (var i = fromLen; i < toLen; i++) {
fromPathList.push(clonePath(fromPathList[k++ % fromLen]))
}
}
fromPathList.length = toLen
} else {
fromPathList = dividePath({ path: fromPath, count: toLen })
var fromPathTransform = fromPath.getComputedTransform()
for (var i = 0; i < fromPathList.length; i++) {
fromPathList[i].setLocalTransform(fromPathTransform)
}
if (fromPathList.length !== toLen) {
console.error('Invalid morphing: unmatched splitted path')
return createEmptyReturn()
}
}
fromPathList = sortPaths(fromPathList)
toPathList = sortPaths(toPathList)
var individualDelay = animationOpts.individualDelay
for (var i = 0; i < toLen; i++) {
var indivdualAnimationOpts = individualDelay
? defaults(
{
delay:
(animationOpts.delay || 0) +
individualDelay(
i,
toLen,
fromPathList[i],
toPathList[i]
)
},
animationOpts
)
: animationOpts
morphPath(fromPathList[i], toPathList[i], indivdualAnimationOpts)
}
return {
fromIndividuals: fromPathList,
toIndividuals: toPathList,
count: toPathList.length
}
}
function isMultiple(elements) {
return isArray(elements[0])
}
function prepareMorphBatches(one, many) {
var batches = []
var batchCount = one.length
for (var i = 0; i < batchCount; i++) {
batches.push({ one: one[i], many: [] })
}
for (var i = 0; i < many.length; i++) {
var len = many[i].length
var k = void 0
for (k = 0; k < len; k++) {
batches[k % batchCount].many.push(many[i][k])
}
}
var off = 0 // If one has more paths than each one of many. average them.
for (var i = batchCount - 1; i >= 0; i--) {
if (!batches[i].many.length) {
var moveFrom = batches[off].many
if (moveFrom.length <= 1) {
// Not enough
// Start from the first one.
if (off) {
off = 0
} else {
return batches
}
}
var len = moveFrom.length
var mid = Math.ceil(len / 2)
batches[i].many = moveFrom.slice(mid, len)
batches[off].many = moveFrom.slice(0, mid)
off++
}
}
return batches
}
var pathDividers = {
clone: function clone(params) {
var ret = [] // Fitting the alpha
var approxOpacity =
1 - Math.pow(1 - params.path.style.opacity, 1 / params.count)
for (var i = 0; i < params.count; i++) {
var cloned = clonePath(params.path)
cloned.setStyle('opacity', approxOpacity)
ret.push(cloned)
}
return ret
}, // Use the default divider
split: null
}
function applyMorphAnimation(
from,
to,
divideShape,
seriesModel,
dataIndex,
animateOtherProps
) {
if (!from.length || !to.length) {
return
}
var updateAnimationCfg = getAnimationConfig(
'update',
seriesModel,
dataIndex
)
if (!(updateAnimationCfg && updateAnimationCfg.duration > 0)) {
return
}
var animationDelay = seriesModel
.getModel('universalTransition')
.get('delay')
var animationCfg = Object.assign(
{
// Need to setToFinal so the further calculation based on the style can be correct.
// Like emphasis color.
setToFinal: true
},
updateAnimationCfg
)
var many
var one
if (isMultiple(from)) {
// manyToOne
many = from
one = to
}
if (isMultiple(to)) {
// oneToMany
many = to
one = from
}
function morphOneBatch(
batch,
fromIsMany,
animateIndex,
animateCount,
forceManyOne
) {
var batchMany = batch.many
var batchOne = batch.one
if (batchMany.length === 1 && !forceManyOne) {
// Is one to one
var batchFrom = fromIsMany ? batchMany[0] : batchOne
var batchTo = fromIsMany ? batchOne : batchMany[0]
if (isCombineMorphing(batchFrom)) {
// Keep doing combine animation.
morphOneBatch(
{ many: [batchFrom], one: batchTo },
true,
animateIndex,
animateCount,
true
)
} else {
var individualAnimationCfg = animationDelay
? defaults(
{ delay: animationDelay(animateIndex, animateCount) },
animationCfg
)
: animationCfg
morphPath(batchFrom, batchTo, individualAnimationCfg)
animateOtherProps(
batchFrom,
batchTo,
batchFrom,
batchTo,
individualAnimationCfg
)
}
} else {
var separateAnimationCfg = defaults(
{
dividePath: pathDividers[divideShape],
individualDelay:
animationDelay &&
function (idx, count, fromPath, toPath) {
return animationDelay(idx + animateIndex, animateCount)
}
},
animationCfg
)
var _a = fromIsMany
? combineMorph(batchMany, batchOne, separateAnimationCfg)
: separateMorph(batchOne, batchMany, separateAnimationCfg),
fromIndividuals = _a.fromIndividuals,
toIndividuals = _a.toIndividuals
var count = fromIndividuals.length
for (var k = 0; k < count; k++) {
var individualAnimationCfg = animationDelay
? defaults(
{ delay: animationDelay(k, count) },
animationCfg
)
: animationCfg
animateOtherProps(
fromIndividuals[k],
toIndividuals[k],
fromIsMany ? batchMany[k] : batch.one,
fromIsMany ? batch.one : batchMany[k],
individualAnimationCfg
)
}
}
}
var fromIsMany = many
? many === from // Is one to one. If the path number not match. also needs do merge and separate morphing.
: from.length > to.length
var morphBatches = many
? prepareMorphBatches(one, many)
: prepareMorphBatches(fromIsMany ? to : from, [
fromIsMany ? from : to
])
var animateCount = 0
for (var i = 0; i < morphBatches.length; i++) {
animateCount += morphBatches[i].many.length
}
var animateIndex = 0
for (var i = 0; i < morphBatches.length; i++) {
morphOneBatch(
morphBatches[i],
fromIsMany,
animateIndex,
animateCount
)
animateIndex += morphBatches[i].many.length
}
}
function getPathList(elements) {
if (!elements) {
return []
}
if (isArray(elements)) {
var pathList_1 = []
for (var i = 0; i < elements.length; i++) {
pathList_1.push(getPathList(elements[i]))
}
return pathList_1
}
var pathList = []
elements.traverse(function (el) {
if (
el instanceof Path$1 &&
!el.disableMorphing &&
!el.invisible &&
!el.ignore
) {
pathList.push(el)
}
})
return pathList
}
var DATA_COUNT_THRESHOLD = 1e4
var getUniversalTransitionGlobalStore = makeInner()
function getGroupIdDimension(data) {
var dimensions = data.dimensions
for (var i = 0; i < dimensions.length; i++) {
var dimInfo = data.getDimensionInfo(dimensions[i])
if (dimInfo && dimInfo.otherDims.itemGroupId === 0) {
return dimensions[i]
}
}
}
function flattenDataDiffItems(list) {
var items = []
each$7(list, function (seriesInfo) {
var data = seriesInfo.data
if (data.count() > DATA_COUNT_THRESHOLD) {
return
}
var indices = data.getIndices()
var groupDim = getGroupIdDimension(data)
for (var dataIndex = 0; dataIndex < indices.length; dataIndex++) {
items.push({
data: data,
dim: seriesInfo.dim || groupDim,
divide: seriesInfo.divide,
dataIndex: dataIndex
})
}
})
return items
}
function fadeInElement(newEl, newSeries, newIndex) {
newEl.traverse(function (el) {
if (el instanceof Path$1) {
// TODO use fade in animation for target element.
initProps(el, { style: { opacity: 0 } }, newSeries, {
dataIndex: newIndex,
isFrom: true
})
}
})
}
function removeEl(el) {
if (el.parent) {
// Bake parent transform to element.
// So it can still have proper transform to transition after it's removed.
var computedTransform = el.getComputedTransform()
el.setLocalTransform(computedTransform)
el.parent.remove(el)
}
}
function stopAnimation(el) {
el.stopAnimation()
if (el.isGroup) {
el.traverse(function (child) {
child.stopAnimation()
})
}
}
function animateElementStyles(el, dataIndex, seriesModel) {
var animationConfig = getAnimationConfig(
'update',
seriesModel,
dataIndex
)
animationConfig &&
el.traverse(function (child) {
if (child instanceof Displayable$1) {
var oldStyle = getOldStyle(child)
if (oldStyle) {
child.animateFrom({ style: oldStyle }, animationConfig)
}
}
})
}
function isAllIdSame(oldDiffItems, newDiffItems) {
var len = oldDiffItems.length
if (len !== newDiffItems.length) {
return false
}
for (var i = 0; i < len; i++) {
var oldItem = oldDiffItems[i]
var newItem = newDiffItems[i]
if (
oldItem.data.getId(oldItem.dataIndex) !==
newItem.data.getId(newItem.dataIndex)
) {
return false
}
}
return true
}
function transitionBetween(oldList, newList, api) {
var oldDiffItems = flattenDataDiffItems(oldList)
var newDiffItems = flattenDataDiffItems(newList)
function updateMorphingPathProps(
from,
to,
rawFrom,
rawTo,
animationCfg
) {
if (rawFrom || from) {
to.animateFrom(
{
style:
rawFrom && rawFrom !== from // dividingMethod like clone may override the style(opacity)
? // So extend it to raw style.
extend(extend({}, rawFrom.style), from.style)
: from.style
},
animationCfg
)
}
}
function findKeyDim(items) {
for (var i = 0; i < items.length; i++) {
if (items[i].dim) {
return items[i].dim
}
}
}
var oldKeyDim = findKeyDim(oldDiffItems)
var newKeyDim = findKeyDim(newDiffItems)
var hasMorphAnimation = false
function createKeyGetter(isOld, onlyGetId) {
return function (diffItem) {
var data = diffItem.data
var dataIndex = diffItem.dataIndex // TODO if specified dim
if (onlyGetId) {
return data.getId(dataIndex)
} // Use group id as transition key by default.
// So we can achieve multiple to multiple animation like drilldown / up naturally.
// If group id not exits. Use id instead. If so, only one to one transition will be applied.
var dataGroupId =
data.hostModel && data.hostModel.get('dataGroupId') // If specified key dimension(itemGroupId by default). Use this same dimension from other data.
// PENDING: If only use key dimension of newData.
var keyDim = isOld
? oldKeyDim || newKeyDim
: newKeyDim || oldKeyDim
var dimInfo = keyDim && data.getDimensionInfo(keyDim)
var dimOrdinalMeta = dimInfo && dimInfo.ordinalMeta
if (dimInfo) {
// Get from encode.itemGroupId.
var key = data.get(dimInfo.name, dataIndex)
if (dimOrdinalMeta) {
return dimOrdinalMeta.categories[key] || key + ''
}
return key + ''
} // Get groupId from raw item. { groupId: '' }
var itemVal = data.getRawDataItem(dataIndex)
if (itemVal && itemVal.groupId) {
return itemVal.groupId + ''
}
return dataGroupId || data.getId(dataIndex)
}
} // Use id if it's very likely to be an one to one animation
// It's more robust than groupId
// TODO Check if key dimension is specified.
var useId = isAllIdSame(oldDiffItems, newDiffItems)
var isElementStillInChart = {}
if (!useId) {
// We may have different diff strategy with basicTransition if we use other dimension as key.
// If so, we can't simply check if oldEl is same with newEl. We need a map to check if oldEl is still being used in the new chart.
// We can't use the elements that already being morphed. Let it keep it's original basic transition.
for (var i = 0; i < newDiffItems.length; i++) {
var newItem = newDiffItems[i]
var el = newItem.data.getItemGraphicEl(newItem.dataIndex)
if (el) {
isElementStillInChart[el.id] = true
}
}
}
function updateOneToOne(newIndex, oldIndex) {
var oldItem = oldDiffItems[oldIndex]
var newItem = newDiffItems[newIndex]
var newSeries = newItem.data.hostModel // TODO Mark this elements is morphed and don't morph them anymore
var oldEl = oldItem.data.getItemGraphicEl(oldItem.dataIndex)
var newEl = newItem.data.getItemGraphicEl(newItem.dataIndex) // Can't handle same elements.
if (oldEl === newEl) {
newEl &&
animateElementStyles(newEl, newItem.dataIndex, newSeries)
return
}
if (
// We can't use the elements that already being morphed
oldEl &&
isElementStillInChart[oldEl.id]
) {
return
}
if (newEl) {
// TODO: If keep animating the group in case
// some of the elements don't want to be morphed.
// TODO Label?
stopAnimation(newEl)
if (oldEl) {
stopAnimation(oldEl) // If old element is doing leaving animation. stop it and remove it immediately.
removeEl(oldEl)
hasMorphAnimation = true
applyMorphAnimation(
getPathList(oldEl),
getPathList(newEl),
newItem.divide,
newSeries,
newIndex,
updateMorphingPathProps
)
} else {
fadeInElement(newEl, newSeries, newIndex)
}
} // else keep oldEl leaving animation.
}
new DataDiffer$1(
oldDiffItems,
newDiffItems,
createKeyGetter(true, useId),
createKeyGetter(false, useId),
null,
'multiple'
)
.update(updateOneToOne)
.updateManyToOne(function (newIndex, oldIndices) {
var newItem = newDiffItems[newIndex]
var newData = newItem.data
var newSeries = newData.hostModel
var newEl = newData.getItemGraphicEl(newItem.dataIndex)
var oldElsList = filter(
map$1(oldIndices, function (idx) {
return oldDiffItems[idx].data.getItemGraphicEl(
oldDiffItems[idx].dataIndex
)
}),
function (oldEl) {
return (
oldEl &&
oldEl !== newEl &&
!isElementStillInChart[oldEl.id]
)
}
)
if (newEl) {
stopAnimation(newEl)
if (oldElsList.length) {
// If old element is doing leaving animation. stop it and remove it immediately.
each$7(oldElsList, function (oldEl) {
stopAnimation(oldEl)
removeEl(oldEl)
})
hasMorphAnimation = true
applyMorphAnimation(
getPathList(oldElsList),
getPathList(newEl),
newItem.divide,
newSeries,
newIndex,
updateMorphingPathProps
)
} else {
fadeInElement(newEl, newSeries, newItem.dataIndex)
}
} // else keep oldEl leaving animation.
})
.updateOneToMany(function (newIndices, oldIndex) {
var oldItem = oldDiffItems[oldIndex]
var oldEl = oldItem.data.getItemGraphicEl(oldItem.dataIndex) // We can't use the elements that already being morphed
if (oldEl && isElementStillInChart[oldEl.id]) {
return
}
var newElsList = filter(
map$1(newIndices, function (idx) {
return newDiffItems[idx].data.getItemGraphicEl(
newDiffItems[idx].dataIndex
)
}),
function (el) {
return el && el !== oldEl
}
)
var newSeris = newDiffItems[newIndices[0]].data.hostModel
if (newElsList.length) {
each$7(newElsList, function (newEl) {
return stopAnimation(newEl)
})
if (oldEl) {
stopAnimation(oldEl) // If old element is doing leaving animation. stop it and remove it immediately.
removeEl(oldEl)
hasMorphAnimation = true
applyMorphAnimation(
getPathList(oldEl),
getPathList(newElsList),
oldItem.divide, // Use divide on old.
newSeris,
newIndices[0],
updateMorphingPathProps
)
} else {
each$7(newElsList, function (newEl) {
return fadeInElement(newEl, newSeris, newIndices[0])
})
}
} // else keep oldEl leaving animation.
})
.updateManyToMany(function (newIndices, oldIndices) {
// If two data are same and both have groupId.
// Normally they should be diff by id.
new DataDiffer$1(
oldIndices,
newIndices,
function (rawIdx) {
return oldDiffItems[rawIdx].data.getId(
oldDiffItems[rawIdx].dataIndex
)
},
function (rawIdx) {
return newDiffItems[rawIdx].data.getId(
newDiffItems[rawIdx].dataIndex
)
}
)
.update(function (newIndex, oldIndex) {
// Use the original index
updateOneToOne(newIndices[newIndex], oldIndices[oldIndex])
})
.execute()
})
.execute()
if (hasMorphAnimation) {
each$7(newList, function (_a) {
var data = _a.data
var seriesModel = data.hostModel
var view = seriesModel && api.getViewOfSeriesModel(seriesModel)
var animationCfg = getAnimationConfig('update', seriesModel, 0) // use 0 index.
if (
view &&
seriesModel.isAnimationEnabled() &&
animationCfg &&
animationCfg.duration > 0
) {
view.group.traverse(function (el) {
if (el instanceof Path$1 && !el.animators.length) {
// We can't accept there still exists element that has no animation
// if universalTransition is enabled
el.animateFrom({ style: { opacity: 0 } }, animationCfg)
}
})
}
})
}
}
function getSeriesTransitionKey(series) {
var seriesKey = series
.getModel('universalTransition')
.get('seriesKey')
if (!seriesKey) {
// Use series id by default.
return series.id
}
return seriesKey
}
function convertArraySeriesKeyToString(seriesKey) {
if (isArray(seriesKey)) {
// Order independent.
return seriesKey.sort().join(',')
}
return seriesKey
}
function getDivideShapeFromData(data) {
if (data.hostModel) {
return data.hostModel
.getModel('universalTransition')
.get('divideShape')
}
}
function findTransitionSeriesBatches(globalStore, params) {
var updateBatches = createHashMap()
var oldDataMap = createHashMap() // Map that only store key in array seriesKey.
// Which is used to query the old data when transition from one to multiple series.
var oldDataMapForSplit = createHashMap()
each$7(globalStore.oldSeries, function (series, idx) {
var oldData = globalStore.oldData[idx]
var transitionKey = getSeriesTransitionKey(series)
var transitionKeyStr =
convertArraySeriesKeyToString(transitionKey)
oldDataMap.set(transitionKeyStr, oldData)
if (isArray(transitionKey)) {
// Same key can't in different array seriesKey.
each$7(transitionKey, function (key) {
oldDataMapForSplit.set(key, {
data: oldData,
key: transitionKeyStr
})
})
}
})
each$7(params.updatedSeries, function (series) {
if (
series.isUniversalTransitionEnabled() &&
series.isAnimationEnabled()
) {
var newData = series.getData()
var transitionKey = getSeriesTransitionKey(series)
var transitionKeyStr =
convertArraySeriesKeyToString(transitionKey) // Only transition between series with same id.
var oldData = oldDataMap.get(transitionKeyStr) // string transition key is the best match.
if (oldData) {
updateBatches.set(transitionKeyStr, {
oldSeries: [
{ divide: getDivideShapeFromData(oldData), data: oldData }
],
newSeries: [
{ divide: getDivideShapeFromData(newData), data: newData }
]
})
} else {
// Transition from multiple series.
if (isArray(transitionKey)) {
var oldSeries_1 = []
each$7(transitionKey, function (key) {
var oldData = oldDataMap.get(key)
if (oldData) {
oldSeries_1.push({
divide: getDivideShapeFromData(oldData),
data: oldData
})
}
})
if (oldSeries_1.length) {
updateBatches.set(transitionKeyStr, {
oldSeries: oldSeries_1,
newSeries: [
{
data: newData,
divide: getDivideShapeFromData(newData)
}
]
})
}
} else {
// Try transition to multiple series.
var oldData_1 = oldDataMapForSplit.get(transitionKey)
if (oldData_1) {
var batch = updateBatches.get(oldData_1.key)
if (!batch) {
batch = {
oldSeries: [
{
data: oldData_1.data,
divide: getDivideShapeFromData(oldData_1.data)
}
],
newSeries: []
}
updateBatches.set(oldData_1.key, batch)
}
batch.newSeries.push({
data: newData,
divide: getDivideShapeFromData(newData)
})
}
}
}
}
})
return updateBatches
}
function querySeries(series, finder) {
for (var i = 0; i < series.length; i++) {
var found =
(finder.seriesIndex != null &&
finder.seriesIndex === series[i].seriesIndex) ||
(finder.seriesId != null && finder.seriesId === series[i].id)
if (found) {
return i
}
}
}
function transitionSeriesFromOpt(
transitionOpt,
globalStore,
params,
api
) {
var from = []
var to = []
each$7(normalizeToArray(transitionOpt.from), function (finder) {
var idx = querySeries(globalStore.oldSeries, finder)
if (idx >= 0) {
from.push({
data: globalStore.oldData[idx], // TODO can specify divideShape in transition.
divide: getDivideShapeFromData(globalStore.oldData[idx]),
dim: finder.dimension
})
}
})
each$7(normalizeToArray(transitionOpt.to), function (finder) {
var idx = querySeries(params.updatedSeries, finder)
if (idx >= 0) {
var data = params.updatedSeries[idx].getData()
to.push({
data: data,
divide: getDivideShapeFromData(data),
dim: finder.dimension
})
}
})
if (from.length > 0 && to.length > 0) {
transitionBetween(from, to, api)
}
}
function installUniversalTransition(registers) {
registers.registerUpdateLifecycle(
'series:beforeupdate',
function (ecMOdel, api, params) {
each$7(
normalizeToArray(params.seriesTransition),
function (transOpt) {
each$7(normalizeToArray(transOpt.to), function (finder) {
var series = params.updatedSeries
for (var i = 0; i < series.length; i++) {
if (
(finder.seriesIndex != null &&
finder.seriesIndex === series[i].seriesIndex) ||
(finder.seriesId != null &&
finder.seriesId === series[i].id)
) {
series[i][SERIES_UNIVERSAL_TRANSITION_PROP] = true
}
}
})
}
)
}
)
registers.registerUpdateLifecycle(
'series:transition',
function (ecModel, api, params) {
// TODO api provide an namespace that can save stuff per instance
var globalStore = getUniversalTransitionGlobalStore(api) // TODO multiple to multiple series.
if (
globalStore.oldSeries &&
params.updatedSeries &&
params.optionChanged
) {
// Use give transition config if its' give;
var transitionOpt = params.seriesTransition
if (transitionOpt) {
each$7(normalizeToArray(transitionOpt), function (opt) {
transitionSeriesFromOpt(opt, globalStore, params, api)
})
} else {
// Else guess from series based on transition series key.
var updateBatches_1 = findTransitionSeriesBatches(
globalStore,
params
)
each$7(updateBatches_1.keys(), function (key) {
var batch = updateBatches_1.get(key)
transitionBetween(batch.oldSeries, batch.newSeries, api)
})
} // Reset
each$7(params.updatedSeries, function (series) {
// Reset;
if (series[SERIES_UNIVERSAL_TRANSITION_PROP]) {
series[SERIES_UNIVERSAL_TRANSITION_PROP] = false
}
})
} // Save all series of current update. Not only the updated one.
var allSeries = ecModel.getSeries()
var savedSeries = (globalStore.oldSeries = [])
var savedData = (globalStore.oldData = [])
for (var i = 0; i < allSeries.length; i++) {
var data = allSeries[i].getData() // Only save the data that can have transition.
// Avoid large data costing too much extra memory
if (data.count() < DATA_COUNT_THRESHOLD) {
savedSeries.push(allSeries[i])
savedData.push(data)
}
}
}
)
}
function createDom(id, painter, dpr) {
var newDom = platformApi.createCanvas()
var width = painter.getWidth()
var height = painter.getHeight()
var newDomStyle = newDom.style
if (newDomStyle) {
newDomStyle.position = 'absolute'
newDomStyle.left = '0'
newDomStyle.top = '0'
newDomStyle.width = width + 'px'
newDomStyle.height = height + 'px'
newDom.setAttribute('data-zr-dom-id', id)
}
newDom.width = width * dpr
newDom.height = height * dpr
return newDom
}
var Layer = (function (_super) {
__extends(Layer, _super)
function Layer(id, painter, dpr) {
var _this = _super.call(this) || this
_this.motionBlur = false
_this.lastFrameAlpha = 0.7
_this.dpr = 1
_this.virtual = false
_this.config = {}
_this.incremental = false
_this.zlevel = 0
_this.maxRepaintRectCount = 5
_this.__dirty = true
_this.__firstTimePaint = true
_this.__used = false
_this.__drawIndex = 0
_this.__startIndex = 0
_this.__endIndex = 0
_this.__prevStartIndex = null
_this.__prevEndIndex = null
var dom
dpr = dpr || devicePixelRatio
if (typeof id === 'string') {
dom = createDom(id, painter, dpr)
} else if (isObject$2(id)) {
dom = id
id = dom.id
}
_this.id = id
_this.dom = dom
var domStyle = dom.style
if (domStyle) {
disableUserSelect(dom)
dom.onselectstart = function () {
return false
}
domStyle.padding = '0'
domStyle.margin = '0'
domStyle.borderWidth = '0'
}
_this.painter = painter
_this.dpr = dpr
return _this
}
Layer.prototype.getElementCount = function () {
return this.__endIndex - this.__startIndex
}
Layer.prototype.afterBrush = function () {
this.__prevStartIndex = this.__startIndex
this.__prevEndIndex = this.__endIndex
}
Layer.prototype.initContext = function () {
this.ctx = this.dom.getContext('2d')
this.ctx.dpr = this.dpr
}
Layer.prototype.setUnpainted = function () {
this.__firstTimePaint = true
}
Layer.prototype.createBackBuffer = function () {
var dpr = this.dpr
this.domBack = createDom('back-' + this.id, this.painter, dpr)
this.ctxBack = this.domBack.getContext('2d')
if (dpr !== 1) {
this.ctxBack.scale(dpr, dpr)
}
}
Layer.prototype.createRepaintRects = function (
displayList,
prevList,
viewWidth,
viewHeight
) {
if (this.__firstTimePaint) {
this.__firstTimePaint = false
return null
}
var mergedRepaintRects = []
var maxRepaintRectCount = this.maxRepaintRectCount
var full = false
var pendingRect = new BoundingRect$1(0, 0, 0, 0)
function addRectToMergePool(rect) {
if (!rect.isFinite() || rect.isZero()) {
return
}
if (mergedRepaintRects.length === 0) {
var boundingRect = new BoundingRect$1(0, 0, 0, 0)
boundingRect.copy(rect)
mergedRepaintRects.push(boundingRect)
} else {
var isMerged = false
var minDeltaArea = Infinity
var bestRectToMergeIdx = 0
for (var i = 0; i < mergedRepaintRects.length; ++i) {
var mergedRect = mergedRepaintRects[i]
if (mergedRect.intersect(rect)) {
var pendingRect_1 = new BoundingRect$1(0, 0, 0, 0)
pendingRect_1.copy(mergedRect)
pendingRect_1.union(rect)
mergedRepaintRects[i] = pendingRect_1
isMerged = true
break
} else if (full) {
pendingRect.copy(rect)
pendingRect.union(mergedRect)
var aArea = rect.width * rect.height
var bArea = mergedRect.width * mergedRect.height
var pendingArea = pendingRect.width * pendingRect.height
var deltaArea = pendingArea - aArea - bArea
if (deltaArea < minDeltaArea) {
minDeltaArea = deltaArea
bestRectToMergeIdx = i
}
}
}
if (full) {
mergedRepaintRects[bestRectToMergeIdx].union(rect)
isMerged = true
}
if (!isMerged) {
var boundingRect = new BoundingRect$1(0, 0, 0, 0)
boundingRect.copy(rect)
mergedRepaintRects.push(boundingRect)
}
if (!full) {
full = mergedRepaintRects.length >= maxRepaintRectCount
}
}
}
for (var i = this.__startIndex; i < this.__endIndex; ++i) {
var el = displayList[i]
if (el) {
var shouldPaint = el.shouldBePainted(
viewWidth,
viewHeight,
true,
true
)
var prevRect =
el.__isRendered && (el.__dirty & REDRAW_BIT || !shouldPaint)
? el.getPrevPaintRect()
: null
if (prevRect) {
addRectToMergePool(prevRect)
}
var curRect =
shouldPaint && (el.__dirty & REDRAW_BIT || !el.__isRendered)
? el.getPaintRect()
: null
if (curRect) {
addRectToMergePool(curRect)
}
}
}
for (
var i = this.__prevStartIndex;
i < this.__prevEndIndex;
++i
) {
var el = prevList[i]
var shouldPaint = el.shouldBePainted(
viewWidth,
viewHeight,
true,
true
)
if (el && (!shouldPaint || !el.__zr) && el.__isRendered) {
var prevRect = el.getPrevPaintRect()
if (prevRect) {
addRectToMergePool(prevRect)
}
}
}
var hasIntersections
do {
hasIntersections = false
for (var i = 0; i < mergedRepaintRects.length; ) {
if (mergedRepaintRects[i].isZero()) {
mergedRepaintRects.splice(i, 1)
continue
}
for (var j = i + 1; j < mergedRepaintRects.length; ) {
if (
mergedRepaintRects[i].intersect(mergedRepaintRects[j])
) {
hasIntersections = true
mergedRepaintRects[i].union(mergedRepaintRects[j])
mergedRepaintRects.splice(j, 1)
} else {
j++
}
}
i++
}
} while (hasIntersections)
this._paintRects = mergedRepaintRects
return mergedRepaintRects
}
Layer.prototype.debugGetPaintRects = function () {
return (this._paintRects || []).slice()
}
Layer.prototype.resize = function (width, height) {
var dpr = this.dpr
var dom = this.dom
var domStyle = dom.style
var domBack = this.domBack
if (domStyle) {
domStyle.width = width + 'px'
domStyle.height = height + 'px'
}
dom.width = width * dpr
dom.height = height * dpr
if (domBack) {
domBack.width = width * dpr
domBack.height = height * dpr
if (dpr !== 1) {
this.ctxBack.scale(dpr, dpr)
}
}
}
Layer.prototype.clear = function (
clearAll,
clearColor,
repaintRects
) {
var dom = this.dom
var ctx = this.ctx
var width = dom.width
var height = dom.height
clearColor = clearColor || this.clearColor
var haveMotionBLur = this.motionBlur && !clearAll
var lastFrameAlpha = this.lastFrameAlpha
var dpr = this.dpr
var self = this
if (haveMotionBLur) {
if (!this.domBack) {
this.createBackBuffer()
}
this.ctxBack.globalCompositeOperation = 'copy'
this.ctxBack.drawImage(dom, 0, 0, width / dpr, height / dpr)
}
var domBack = this.domBack
function doClear(x, y, width, height) {
ctx.clearRect(x, y, width, height)
if (clearColor && clearColor !== 'transparent') {
var clearColorGradientOrPattern = void 0
if (isGradientObject(clearColor)) {
clearColorGradientOrPattern =
clearColor.__canvasGradient ||
getCanvasGradient(ctx, clearColor, {
x: 0,
y: 0,
width: width,
height: height
})
clearColor.__canvasGradient = clearColorGradientOrPattern
} else if (isImagePatternObject(clearColor)) {
clearColorGradientOrPattern = createCanvasPattern(
ctx,
clearColor,
{
dirty: function dirty() {
self.setUnpainted()
self.__painter.refresh()
}
}
)
}
ctx.save()
ctx.fillStyle = clearColorGradientOrPattern || clearColor
ctx.fillRect(x, y, width, height)
ctx.restore()
}
if (haveMotionBLur) {
ctx.save()
ctx.globalAlpha = lastFrameAlpha
ctx.drawImage(domBack, x, y, width, height)
ctx.restore()
}
}
if (!repaintRects || haveMotionBLur) {
doClear(0, 0, width, height)
} else if (repaintRects.length) {
each$7(repaintRects, function (rect) {
doClear(
rect.x * dpr,
rect.y * dpr,
rect.width * dpr,
rect.height * dpr
)
})
}
}
return Layer
})(Eventful$1)
var Layer$1 = Layer
var HOVER_LAYER_ZLEVEL = 1e5
var CANVAS_ZLEVEL = 314159
var EL_AFTER_INCREMENTAL_INC = 0.01
var INCREMENTAL_INC = 0.001
function isLayerValid(layer) {
if (!layer) {
return false
}
if (layer.__builtin__) {
return true
}
if (
typeof layer.resize !== 'function' ||
typeof layer.refresh !== 'function'
) {
return false
}
return true
}
function createRoot(width, height) {
var domRoot = document.createElement('div')
domRoot.style.cssText =
[
'position:relative',
'width:' + width + 'px',
'height:' + height + 'px',
'padding:0',
'margin:0',
'border-width:0'
].join(';') + ';'
return domRoot
}
var CanvasPainter = (function () {
function CanvasPainter(root, storage, opts, id) {
this.type = 'canvas'
this._zlevelList = []
this._prevDisplayList = []
this._layers = {}
this._layerConfig = {}
this._needsManuallyCompositing = false
this.type = 'canvas'
var singleCanvas =
!root.nodeName || root.nodeName.toUpperCase() === 'CANVAS'
this._opts = opts = extend({}, opts || {})
this.dpr = opts.devicePixelRatio || devicePixelRatio
this._singleCanvas = singleCanvas
this.root = root
var rootStyle = root.style
if (rootStyle) {
disableUserSelect(root)
root.innerHTML = ''
}
this.storage = storage
var zlevelList = this._zlevelList
this._prevDisplayList = []
var layers = this._layers
if (!singleCanvas) {
this._width = getSize$1(root, 0, opts)
this._height = getSize$1(root, 1, opts)
var domRoot = (this._domRoot = createRoot(
this._width,
this._height
))
root.appendChild(domRoot)
} else {
var rootCanvas = root
var width = rootCanvas.width
var height = rootCanvas.height
if (opts.width != null) {
width = opts.width
}
if (opts.height != null) {
height = opts.height
}
this.dpr = opts.devicePixelRatio || 1
rootCanvas.width = width * this.dpr
rootCanvas.height = height * this.dpr
this._width = width
this._height = height
var mainLayer = new Layer$1(rootCanvas, this, this.dpr)
mainLayer.__builtin__ = true
mainLayer.initContext()
layers[CANVAS_ZLEVEL] = mainLayer
mainLayer.zlevel = CANVAS_ZLEVEL
zlevelList.push(CANVAS_ZLEVEL)
this._domRoot = root
}
}
CanvasPainter.prototype.getType = function () {
return 'canvas'
}
CanvasPainter.prototype.isSingleCanvas = function () {
return this._singleCanvas
}
CanvasPainter.prototype.getViewportRoot = function () {
return this._domRoot
}
CanvasPainter.prototype.getViewportRootOffset = function () {
var viewportRoot = this.getViewportRoot()
if (viewportRoot) {
return {
offsetLeft: viewportRoot.offsetLeft || 0,
offsetTop: viewportRoot.offsetTop || 0
}
}
}
CanvasPainter.prototype.refresh = function (paintAll) {
var list = this.storage.getDisplayList(true)
var prevList = this._prevDisplayList
var zlevelList = this._zlevelList
this._redrawId = Math.random()
this._paintList(list, prevList, paintAll, this._redrawId)
for (var i = 0; i < zlevelList.length; i++) {
var z = zlevelList[i]
var layer = this._layers[z]
if (!layer.__builtin__ && layer.refresh) {
var clearColor = i === 0 ? this._backgroundColor : null
layer.refresh(clearColor)
}
}
if (this._opts.useDirtyRect) {
this._prevDisplayList = list.slice()
}
return this
}
CanvasPainter.prototype.refreshHover = function () {
this._paintHoverList(this.storage.getDisplayList(false))
}
CanvasPainter.prototype._paintHoverList = function (list) {
var len = list.length
var hoverLayer = this._hoverlayer
hoverLayer && hoverLayer.clear()
if (!len) {
return
}
var scope = {
inHover: true,
viewWidth: this._width,
viewHeight: this._height
}
var ctx
for (var i = 0; i < len; i++) {
var el = list[i]
if (el.__inHover) {
if (!hoverLayer) {
hoverLayer = this._hoverlayer =
this.getLayer(HOVER_LAYER_ZLEVEL)
}
if (!ctx) {
ctx = hoverLayer.ctx
ctx.save()
}
brush(ctx, el, scope, i === len - 1)
}
}
if (ctx) {
ctx.restore()
}
}
CanvasPainter.prototype.getHoverLayer = function () {
return this.getLayer(HOVER_LAYER_ZLEVEL)
}
CanvasPainter.prototype.paintOne = function (ctx, el) {
brushSingle(ctx, el)
}
CanvasPainter.prototype._paintList = function (
list,
prevList,
paintAll,
redrawId
) {
if (this._redrawId !== redrawId) {
return
}
paintAll = paintAll || false
this._updateLayerStatus(list)
var _a = this._doPaintList(list, prevList, paintAll),
finished = _a.finished,
needsRefreshHover = _a.needsRefreshHover
if (this._needsManuallyCompositing) {
this._compositeManually()
}
if (needsRefreshHover) {
this._paintHoverList(list)
}
if (!finished) {
var self_1 = this
requestAnimationFrame$1(function () {
self_1._paintList(list, prevList, paintAll, redrawId)
})
} else {
this.eachLayer(function (layer) {
layer.afterBrush && layer.afterBrush()
})
}
}
CanvasPainter.prototype._compositeManually = function () {
var ctx = this.getLayer(CANVAS_ZLEVEL).ctx
var width = this._domRoot.width
var height = this._domRoot.height
ctx.clearRect(0, 0, width, height)
this.eachBuiltinLayer(function (layer) {
if (layer.virtual) {
ctx.drawImage(layer.dom, 0, 0, width, height)
}
})
}
CanvasPainter.prototype._doPaintList = function (
list,
prevList,
paintAll
) {
var _this = this
var layerList = []
var useDirtyRect = this._opts.useDirtyRect
for (var zi = 0; zi < this._zlevelList.length; zi++) {
var zlevel = this._zlevelList[zi]
var layer = this._layers[zlevel]
if (
layer.__builtin__ &&
layer !== this._hoverlayer &&
(layer.__dirty || paintAll)
) {
layerList.push(layer)
}
}
var finished = true
var needsRefreshHover = false
var _loop_1 = function _loop_1(k) {
var layer = layerList[k]
var ctx = layer.ctx
var repaintRects =
useDirtyRect &&
layer.createRepaintRects(
list,
prevList,
this_1._width,
this_1._height
)
var start = paintAll ? layer.__startIndex : layer.__drawIndex
var useTimer = !paintAll && layer.incremental && Date.now
var startTime = useTimer && Date.now()
var clearColor =
layer.zlevel === this_1._zlevelList[0]
? this_1._backgroundColor
: null
if (layer.__startIndex === layer.__endIndex) {
layer.clear(false, clearColor, repaintRects)
} else if (start === layer.__startIndex) {
var firstEl = list[start]
if (!firstEl.incremental || !firstEl.notClear || paintAll) {
layer.clear(false, clearColor, repaintRects)
}
}
if (start === -1) {
console.error('For some unknown reason. drawIndex is -1')
start = layer.__startIndex
}
var i
var repaint = function repaint(repaintRect) {
var scope = {
inHover: false,
allClipped: false,
prevEl: null,
viewWidth: _this._width,
viewHeight: _this._height
}
for (i = start; i < layer.__endIndex; i++) {
var el = list[i]
if (el.__inHover) {
needsRefreshHover = true
}
_this._doPaintEl(
el,
layer,
useDirtyRect,
repaintRect,
scope,
i === layer.__endIndex - 1
)
if (useTimer) {
var dTime = Date.now() - startTime
if (dTime > 15) {
break
}
}
}
if (scope.prevElClipPaths) {
ctx.restore()
}
}
if (repaintRects) {
if (repaintRects.length === 0) {
i = layer.__endIndex
} else {
var dpr = this_1.dpr
for (var r = 0; r < repaintRects.length; ++r) {
var rect = repaintRects[r]
ctx.save()
ctx.beginPath()
ctx.rect(
rect.x * dpr,
rect.y * dpr,
rect.width * dpr,
rect.height * dpr
)
ctx.clip()
repaint(rect)
ctx.restore()
}
}
} else {
ctx.save()
repaint()
ctx.restore()
}
layer.__drawIndex = i
if (layer.__drawIndex < layer.__endIndex) {
finished = false
}
}
var this_1 = this
for (var k = 0; k < layerList.length; k++) {
_loop_1(k)
}
if (env$1.wxa) {
each$7(this._layers, function (layer) {
if (layer && layer.ctx && layer.ctx.draw) {
layer.ctx.draw()
}
})
}
return {
finished: finished,
needsRefreshHover: needsRefreshHover
}
}
CanvasPainter.prototype._doPaintEl = function (
el,
currentLayer,
useDirtyRect,
repaintRect,
scope,
isLast
) {
var ctx = currentLayer.ctx
if (useDirtyRect) {
var paintRect = el.getPaintRect()
if (
!repaintRect ||
(paintRect && paintRect.intersect(repaintRect))
) {
brush(ctx, el, scope, isLast)
el.setPrevPaintRect(paintRect)
}
} else {
brush(ctx, el, scope, isLast)
}
}
CanvasPainter.prototype.getLayer = function (zlevel, virtual) {
if (this._singleCanvas && !this._needsManuallyCompositing) {
zlevel = CANVAS_ZLEVEL
}
var layer = this._layers[zlevel]
if (!layer) {
layer = new Layer$1('zr_' + zlevel, this, this.dpr)
layer.zlevel = zlevel
layer.__builtin__ = true
if (this._layerConfig[zlevel]) {
merge(layer, this._layerConfig[zlevel], true)
} else if (
this._layerConfig[zlevel - EL_AFTER_INCREMENTAL_INC]
) {
merge(
layer,
this._layerConfig[zlevel - EL_AFTER_INCREMENTAL_INC],
true
)
}
if (virtual) {
layer.virtual = virtual
}
this.insertLayer(zlevel, layer)
layer.initContext()
}
return layer
}
CanvasPainter.prototype.insertLayer = function (zlevel, layer) {
var layersMap = this._layers
var zlevelList = this._zlevelList
var len = zlevelList.length
var domRoot = this._domRoot
var prevLayer = null
var i = -1
if (layersMap[zlevel]) {
return
}
if (!isLayerValid(layer)) {
return
}
if (len > 0 && zlevel > zlevelList[0]) {
for (i = 0; i < len - 1; i++) {
if (zlevelList[i] < zlevel && zlevelList[i + 1] > zlevel) {
break
}
}
prevLayer = layersMap[zlevelList[i]]
}
zlevelList.splice(i + 1, 0, zlevel)
layersMap[zlevel] = layer
if (!layer.virtual) {
if (prevLayer) {
var prevDom = prevLayer.dom
if (prevDom.nextSibling) {
domRoot.insertBefore(layer.dom, prevDom.nextSibling)
} else {
domRoot.appendChild(layer.dom)
}
} else {
if (domRoot.firstChild) {
domRoot.insertBefore(layer.dom, domRoot.firstChild)
} else {
domRoot.appendChild(layer.dom)
}
}
}
layer.__painter = this
}
CanvasPainter.prototype.eachLayer = function (cb, context) {
var zlevelList = this._zlevelList
for (var i = 0; i < zlevelList.length; i++) {
var z = zlevelList[i]
cb.call(context, this._layers[z], z)
}
}
CanvasPainter.prototype.eachBuiltinLayer = function (cb, context) {
var zlevelList = this._zlevelList
for (var i = 0; i < zlevelList.length; i++) {
var z = zlevelList[i]
var layer = this._layers[z]
if (layer.__builtin__) {
cb.call(context, layer, z)
}
}
}
CanvasPainter.prototype.eachOtherLayer = function (cb, context) {
var zlevelList = this._zlevelList
for (var i = 0; i < zlevelList.length; i++) {
var z = zlevelList[i]
var layer = this._layers[z]
if (!layer.__builtin__) {
cb.call(context, layer, z)
}
}
}
CanvasPainter.prototype.getLayers = function () {
return this._layers
}
CanvasPainter.prototype._updateLayerStatus = function (list) {
this.eachBuiltinLayer(function (layer, z) {
layer.__dirty = layer.__used = false
})
function updatePrevLayer(idx) {
if (prevLayer) {
if (prevLayer.__endIndex !== idx) {
prevLayer.__dirty = true
}
prevLayer.__endIndex = idx
}
}
if (this._singleCanvas) {
for (var i_1 = 1; i_1 < list.length; i_1++) {
var el = list[i_1]
if (el.zlevel !== list[i_1 - 1].zlevel || el.incremental) {
this._needsManuallyCompositing = true
break
}
}
}
var prevLayer = null
var incrementalLayerCount = 0
var prevZlevel
var i
for (i = 0; i < list.length; i++) {
var el = list[i]
var zlevel = el.zlevel
var layer = void 0
if (prevZlevel !== zlevel) {
prevZlevel = zlevel
incrementalLayerCount = 0
}
if (el.incremental) {
layer = this.getLayer(
zlevel + INCREMENTAL_INC,
this._needsManuallyCompositing
)
layer.incremental = true
incrementalLayerCount = 1
} else {
layer = this.getLayer(
zlevel +
(incrementalLayerCount > 0
? EL_AFTER_INCREMENTAL_INC
: 0),
this._needsManuallyCompositing
)
}
if (!layer.__builtin__) {
logError(
'ZLevel ' +
zlevel +
' has been used by unkown layer ' +
layer.id
)
}
if (layer !== prevLayer) {
layer.__used = true
if (layer.__startIndex !== i) {
layer.__dirty = true
}
layer.__startIndex = i
if (!layer.incremental) {
layer.__drawIndex = i
} else {
layer.__drawIndex = -1
}
updatePrevLayer(i)
prevLayer = layer
}
if (el.__dirty & REDRAW_BIT && !el.__inHover) {
layer.__dirty = true
if (layer.incremental && layer.__drawIndex < 0) {
layer.__drawIndex = i
}
}
}
updatePrevLayer(i)
this.eachBuiltinLayer(function (layer, z) {
if (!layer.__used && layer.getElementCount() > 0) {
layer.__dirty = true
layer.__startIndex = layer.__endIndex = layer.__drawIndex = 0
}
if (layer.__dirty && layer.__drawIndex < 0) {
layer.__drawIndex = layer.__startIndex
}
})
}
CanvasPainter.prototype.clear = function () {
this.eachBuiltinLayer(this._clearLayer)
return this
}
CanvasPainter.prototype._clearLayer = function (layer) {
layer.clear()
}
CanvasPainter.prototype.setBackgroundColor = function (
backgroundColor
) {
this._backgroundColor = backgroundColor
each$7(this._layers, function (layer) {
layer.setUnpainted()
})
}
CanvasPainter.prototype.configLayer = function (zlevel, config) {
if (config) {
var layerConfig = this._layerConfig
if (!layerConfig[zlevel]) {
layerConfig[zlevel] = config
} else {
merge(layerConfig[zlevel], config, true)
}
for (var i = 0; i < this._zlevelList.length; i++) {
var _zlevel = this._zlevelList[i]
if (
_zlevel === zlevel ||
_zlevel === zlevel + EL_AFTER_INCREMENTAL_INC
) {
var layer = this._layers[_zlevel]
merge(layer, layerConfig[zlevel], true)
}
}
}
}
CanvasPainter.prototype.delLayer = function (zlevel) {
var layers = this._layers
var zlevelList = this._zlevelList
var layer = layers[zlevel]
if (!layer) {
return
}
layer.dom.parentNode.removeChild(layer.dom)
delete layers[zlevel]
zlevelList.splice(indexOf(zlevelList, zlevel), 1)
}
CanvasPainter.prototype.resize = function (width, height) {
if (!this._domRoot.style) {
if (width == null || height == null) {
return
}
this._width = width
this._height = height
this.getLayer(CANVAS_ZLEVEL).resize(width, height)
} else {
var domRoot = this._domRoot
domRoot.style.display = 'none'
var opts = this._opts
var root = this.root
width != null && (opts.width = width)
height != null && (opts.height = height)
width = getSize$1(root, 0, opts)
height = getSize$1(root, 1, opts)
domRoot.style.display = ''
if (this._width !== width || height !== this._height) {
domRoot.style.width = width + 'px'
domRoot.style.height = height + 'px'
for (var id in this._layers) {
if (this._layers.hasOwnProperty(id)) {
this._layers[id].resize(width, height)
}
}
this.refresh(true)
}
this._width = width
this._height = height
}
return this
}
CanvasPainter.prototype.clearLayer = function (zlevel) {
var layer = this._layers[zlevel]
if (layer) {
layer.clear()
}
}
CanvasPainter.prototype.dispose = function () {
this.root.innerHTML = ''
this.root = this.storage = this._domRoot = this._layers = null
}
CanvasPainter.prototype.getRenderedCanvas = function (opts) {
opts = opts || {}
if (this._singleCanvas && !this._compositeManually) {
return this._layers[CANVAS_ZLEVEL].dom
}
var imageLayer = new Layer$1(
'image',
this,
opts.pixelRatio || this.dpr
)
imageLayer.initContext()
imageLayer.clear(
false,
opts.backgroundColor || this._backgroundColor
)
var ctx = imageLayer.ctx
if (opts.pixelRatio <= this.dpr) {
this.refresh()
var width_1 = imageLayer.dom.width
var height_1 = imageLayer.dom.height
this.eachLayer(function (layer) {
if (layer.__builtin__) {
ctx.drawImage(layer.dom, 0, 0, width_1, height_1)
} else if (layer.renderToCanvas) {
ctx.save()
layer.renderToCanvas(ctx)
ctx.restore()
}
})
} else {
var scope = {
inHover: false,
viewWidth: this._width,
viewHeight: this._height
}
var displayList = this.storage.getDisplayList(true)
for (var i = 0, len = displayList.length; i < len; i++) {
var el = displayList[i]
brush(ctx, el, scope, i === len - 1)
}
}
return imageLayer.dom
}
CanvasPainter.prototype.getWidth = function () {
return this._width
}
CanvasPainter.prototype.getHeight = function () {
return this._height
}
return CanvasPainter
})()
var CanvasPainter$1 = CanvasPainter
function install(registers) {
registers.registerPainter('canvas', CanvasPainter$1)
}
var lineChartOption = {
legend: { show: false },
emphasis: { lineStyle: { width: 2 } },
xAxis: {
boundaryGap: false,
data: [
'01\u6708',
'02\u6708',
'03\u6708',
'04\u6708',
'05\u6708',
'06\u6708',
'07\u6708',
'08\u6708',
'09\u6708',
'10\u6708',
'11\u6708',
'12\u6708'
],
type: 'category',
axisLine: { lineStyle: { color: '#8C8C8C' } }
},
color: [
'#5B8FF9',
'#01C1B5',
'#91DD1C',
'#FFA92C',
'#BE7E2E',
'#1C96DD',
'#D22CFF',
'#FF3C3C',
'#1AEE3E',
'#7c47ff'
],
series: [
{
lineStyle: { width: 1 },
data: [
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00'
],
symbol: 'circle',
name: '\u966A\u7EC3\u8BFE',
type: 'line',
emphasis: { lineStyle: { width: 1 } }
},
{
lineStyle: { width: 1 },
data: [
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00'
],
symbol: 'circle',
name: '\u76F4\u64AD\u8BFE',
type: 'line',
emphasis: { lineStyle: { width: 1 } }
},
{
lineStyle: { width: 1 },
data: [
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00'
],
symbol: 'circle',
name: '\u89C6\u9891\u8BFE',
type: 'line',
emphasis: { lineStyle: { width: 1 } }
},
{
lineStyle: { width: 1 },
data: [
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00'
],
symbol: 'circle',
name: '\u4E50\u8C31',
type: 'line',
emphasis: { lineStyle: { width: 1 } }
},
{
lineStyle: { width: 1 },
data: [
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00'
],
symbol: 'circle',
name: '\u5C0F\u9177Ai\u63A8\u5E7F',
type: 'line',
emphasis: { lineStyle: { width: 1 } }
},
{
lineStyle: { width: 1 },
data: [
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00'
],
symbol: 'circle',
name: '\u76F4\u64AD\u8BFE\u63A8\u8350',
type: 'line',
emphasis: { lineStyle: { width: 1 } }
},
{
lineStyle: { width: 1 },
data: [
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00'
],
symbol: 'circle',
name: '\u89C6\u9891\u8BFE\u63A8\u8350',
type: 'line',
emphasis: { lineStyle: { width: 1 } }
},
{
lineStyle: { width: 1 },
data: [
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00'
],
symbol: 'circle',
name: '\u5546\u54C1\u63A8\u8350',
type: 'line',
emphasis: { lineStyle: { width: 1 } }
},
{
lineStyle: { width: 1 },
data: [
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00'
],
symbol: 'circle',
name: '\u4E50\u8C31\u63A8\u8350',
type: 'line',
emphasis: { lineStyle: { width: 1 } }
},
{
lineStyle: { width: 1 },
data: [
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00',
'0.00'
],
symbol: 'circle',
name: '\u6D3B\u52A8\u62A5\u540D',
type: 'line',
emphasis: { lineStyle: { width: 1 } }
}
],
title: { show: false },
grid: {
bottom: '3%',
containLabel: true,
left: '3%',
right: '5%',
top: '7%'
},
tooltip: { trigger: 'axis', confine: true },
yAxis: {
type: 'value',
splitLine: {
axisLine: { lineStyle: { color: '#8C8C8C' } },
lineStyle: { color: ['#E2E2E2'] }
}
},
dataZoom: [{ type: 'inside', throttle: 100 }],
toolbox: { feature: { saveAsImage: { show: false } } }
}
var pieChartOption = {
tooltip: {
position: ['30%', '30%'],
trigger: 'item',
padding: 3,
textStyle: { fontSize: 12 },
borderWidth: 0,
formatter: '{b} : {c} ({d}%)'
},
series: [
{
avoidLabelOverlap: false,
label: { show: false },
data: [
{ name: '\u966A\u7EC3\u8BFE', value: '0.00' },
{ name: '\u76F4\u64AD\u8BFE', value: '0.00' },
{ name: '\u89C6\u9891\u8BFE', value: '0.00' },
{ name: '\u4E50\u8C31', value: '0.00' },
{ name: '\u5C0F\u9177Ai\u63A8\u5E7F', value: '0.00' },
{ name: '\u76F4\u64AD\u8BFE\u63A8\u8350', value: '0.00' },
{ name: '\u89C6\u9891\u8BFE\u63A8\u8350', value: '0.00' },
{ name: '\u5546\u54C1\u63A8\u8350', value: '0.00' },
{ name: '\u4E50\u8C31\u63A8\u8350', value: '0.00' },
{ name: '\u6D3B\u52A8\u62A5\u540D', value: '0.00' }
],
type: 'pie',
radius: ['50%', '80%']
}
],
grid: {
bottom: '0%',
containLabel: true,
left: '0%',
right: '0%',
top: '0%'
},
toolbox: { feature: { saveAsImage: { show: false } } },
color: [
'#5B8FF9',
'#01C1B5',
'#91DD1C',
'#FFA92C',
'#BE7E2E',
'#1C96DD',
'#D22CFF',
'#FF3C3C',
'#1AEE3E',
'#7c47ff'
]
} // 注册必须的组件
use([
install$9,
install$a,
install$d,
install$1,
install$2,
install$h,
installLabelLayout,
installUniversalTransition,
install,
install$g,
install$b,
install$6,
install$3,
install$i
])
var getAssetsHomeFile = exports(
'getAssetsHomeFile',
function (fileName) {
var path = './images/'.concat(fileName)
var modules = {
'./images/icon_fly.png': __glob_21_0,
'./images/icon_user.png': __glob_21_1
}
return modules[path].default
}
)
var yearColumns = []
var year = dayjs().year()
var defaultIndex = 10
for (var i = year - 10; i <= year + 10; i++) {
yearColumns.push({ text: ''.concat(i, '\u5E74'), value: i })
}
var index = exports(
'default',
defineComponent({
name: 'IncomeConsus',
data: function data() {
return {
moneyInfo: {
totalSingleRate: 0,
totalShareRate: 0,
totalInAmount: 0,
practiceAmount: 0,
practiceRate: 0,
liveAmount: 0,
liveRate: 0,
videoAmount: 0,
videoRate: 0,
musicAmount: 0,
musicRate: 0,
vipShareAmount: 0,
vipShareRate: 0,
liveShareAmount: 0,
liveShareRate: 0,
videoShareAmount: 0,
videoShareRate: 0,
mallShareAmount: 0,
mallShareRate: 0,
musicShareAmount: 0,
musicShareRate: 0,
actiRegistShareAmount: 0,
actiRegistShareRate: 0
},
params: { timeType: 'YEAR', dateTime: ''.concat(year) },
dateTimeStr: ''.concat(year, '\u5E74'),
myChart: null,
myChart2: null,
timerStatus: false,
currentDate: new Date()
}
},
mounted: function mounted() {
var _this2 = this
return _asyncToGenerator(
/*#__PURE__*/ regeneratorRuntime.mark(function _callee() {
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch ((_context.prev = _context.next)) {
case 0:
_this2.myChart = markRaw(
init(document.getElementById('incomeClass'))
)
_this2.myChart2 = markRaw(
init(document.getElementById('structrueClass'))
)
_this2.getList()
case 3:
case 'end':
return _context.stop()
}
}
}, _callee)
})
)()
},
methods: {
getList: function getList() {
var _this3 = this
return _asyncToGenerator(
/*#__PURE__*/ regeneratorRuntime.mark(function _callee2() {
var params, res, result, lineData
return regeneratorRuntime.wrap(
function _callee2$(_context2) {
while (1) {
switch ((_context2.prev = _context2.next)) {
case 0:
_context2.prev = 0
params = _this3.params
_context2.next = 4
return request.post(
'/api-teacher/userAccount/accountTotal',
{ data: params }
)
case 4:
res = _context2.sent
result = res.data || {}
_this3.moneyInfo = {
totalSingleRate:
result.practiceRate +
result.liveRate +
result.videoRate +
result.musicRate,
totalShareRate:
result.vipShareRate +
result.liveShareRate +
result.videoShareRate +
result.musicShareRate +
result.mallShareRate +
result.actiRegistShareRate,
totalInAmount: result.totalInAmount || 0,
practiceAmount: result.practiceAmount || 0,
practiceRate: result.practiceRate || 0,
liveAmount: result.liveAmount || 0,
liveRate: result.liveRate || 0,
videoAmount: result.videoAmount || 0,
videoRate: result.videoRate || 0,
musicAmount: result.musicAmount || 0,
musicRate: result.musicRate || 0,
vipShareAmount: result.vipShareAmount || 0,
vipShareRate: result.vipShareRate || 0,
liveShareAmount: result.liveShareAmount || 0,
liveShareRate: result.liveShareRate || 0,
videoShareAmount:
result.videoShareAmount || 0,
videoShareRate: result.videoShareRate || 0,
mallShareAmount: result.mallShareAmount || 0,
mallShareRate: result.mallShareRate || 0,
musicShareAmount:
result.musicShareAmount || 0,
musicShareRate: result.musicShareRate || 0,
actiRegistShareAmount:
result.actiRegistShareAmount || 0,
actiRegistShareRate:
result.actiRegistShareRate || 0
} // 处理折线图数据
lineData = {
xAxis: [],
practiceAmount: [],
liveAmount: [],
videoAmount: [],
musicAmount: [],
vipShareAmount: [],
liveShareAmount: [],
videoShareAmount: [],
mallShareAmount: [],
musicShareAmount: [],
actiRegistShareAmount: []
}
;(result.infoList || []).forEach(function (
item
) {
if (params.timeType === 'YEAR') {
lineData.xAxis.push(
dayjs(item.timeStr).format('MM月')
)
} else if (params.timeType === 'MONTH') {
lineData.xAxis.push(
dayjs(item.timeStr).format('DD日')
)
}
lineData.practiceAmount.push(
item.practiceAmount
)
lineData.liveAmount.push(item.liveAmount)
lineData.videoAmount.push(item.videoAmount)
lineData.musicAmount.push(item.musicAmount)
lineData.vipShareAmount.push(
item.vipShareAmount
) // 小酷Ai
lineData.liveShareAmount.push(
item.liveShareAmount
)
lineData.videoShareAmount.push(
item.videoShareAmount
)
lineData.mallShareAmount.push(
item.mallShareAmount
)
lineData.musicShareAmount.push(
item.musicShareAmount
)
lineData.actiRegistShareAmount.push(
item.actiRegistShareAmount
)
}) // 初始化折线图
lineChartOption.xAxis.data = lineData.xAxis
lineChartOption.series[0].data =
lineData.practiceAmount
lineChartOption.series[1].data =
lineData.liveAmount
lineChartOption.series[2].data =
lineData.videoAmount
lineChartOption.series[3].data =
lineData.musicAmount
lineChartOption.series[4].data =
lineData.vipShareAmount
lineChartOption.series[5].data =
lineData.liveShareAmount
lineChartOption.series[6].data =
lineData.videoShareAmount
lineChartOption.series[7].data =
lineData.mallShareAmount
lineChartOption.series[8].data =
lineData.musicShareAmount
lineChartOption.series[9].data =
lineData.actiRegistShareAmount // console.log(lineChartOption)
_this3.myChart.clear()
_this3.myChart.setOption(lineChartOption) // 处理饼图数据
pieChartOption.series[0].data[0].value =
result.practiceAmount
pieChartOption.series[0].data[1].value =
result.liveAmount
pieChartOption.series[0].data[2].value =
result.videoAmount
pieChartOption.series[0].data[3].value =
result.musicAmount
pieChartOption.series[0].data[4].value =
result.vipShareAmount
pieChartOption.series[0].data[5].value =
result.liveShareAmount
pieChartOption.series[0].data[6].value =
result.videoShareAmount
pieChartOption.series[0].data[7].value =
result.mallShareAmount
pieChartOption.series[0].data[8].value =
result.musicShareAmount
pieChartOption.series[0].data[9].value =
result.actiRegistShareAmount
_this3.myChart2.clear()
_this3.myChart2.setOption(pieChartOption)
_context2.next = 38
break
case 36:
_context2.prev = 36
_context2.t0 = _context2['catch'](0)
case 38:
case 'end':
return _context2.stop()
}
}
},
_callee2,
null,
[[0, 36]]
)
})
)()
}
},
render: function render() {
var _this4 = this
return createVNode('div', { style: { overflow: 'hidden' } }, [
createVNode('div', { class: styles.incomeConsus }, [
createVNode(
Cell,
{ class: styles.income, title: '总收入(元)' },
{
label: function label() {
return createVNode(
'span',
{ class: styles.countPrice },
[moneyFormat(_this4.moneyInfo.totalInAmount)]
)
},
value: function value() {
return createVNode(
'span',
{
class: styles.searchTime,
onClick: function onClick() {
return (_this4.timerStatus = true)
}
},
[_this4.dateTimeStr]
)
}
}
),
createVNode('div', { class: styles.section }, [
createVNode(
Row,
{ class: styles.numberCount },
{
default: function _default() {
return [
createVNode(
Col,
{ span: 6 },
{
default: function _default() {
return [
createVNode('i', null, null),
createVNode(
'div',
{ class: styles.type },
[
createVNode('span', null, [
createTextVNode(
'\u966A\u7EC3\u8BFE'
)
]),
createVNode(
'span',
{ class: styles.price },
[
moneyFormat(
_this4.moneyInfo.practiceAmount
)
]
)
]
)
]
}
}
),
createVNode(
Col,
{ span: 6 },
{
default: function _default() {
return [
createVNode(
'i',
{ class: styles.color1 },
null
),
createVNode(
'div',
{ class: styles.type },
[
createVNode('span', null, [
createTextVNode(
'\u76F4\u64AD\u8BFE'
)
]),
createVNode(
'span',
{ class: styles.price },
[
moneyFormat(
_this4.moneyInfo.liveAmount
)
]
)
]
)
]
}
}
),
createVNode(
Col,
{ span: 6 },
{
default: function _default() {
return [
createVNode(
'i',
{ class: styles.color2 },
null
),
createVNode(
'div',
{ class: styles.type },
[
createVNode('span', null, [
createTextVNode(
'\u89C6\u9891\u8BFE'
)
]),
createVNode(
'span',
{ class: styles.price },
[
moneyFormat(
_this4.moneyInfo.videoAmount
)
]
)
]
)
]
}
}
),
createVNode(
Col,
{ span: 6 },
{
default: function _default() {
return [
createVNode(
'i',
{ class: styles.color3 },
null
),
createVNode(
'div',
{ class: styles.type },
[
createVNode('span', null, [
createTextVNode('\u4E50\u8C31')
]),
createVNode(
'span',
{ class: styles.price },
[
moneyFormat(
_this4.moneyInfo.musicAmount
)
]
)
]
)
]
}
}
),
createVNode(
Col,
{ span: 6 },
{
default: function _default() {
return [
createVNode(
'i',
{ class: styles.color4 },
null
),
createVNode(
'div',
{ class: styles.type },
[
createVNode('span', null, [
createTextVNode(
'\u5C0F\u9177Ai\u63A8\u5E7F'
)
]),
createVNode(
'span',
{ class: styles.price },
[
moneyFormat(
_this4.moneyInfo.vipShareAmount
)
]
)
]
)
]
}
}
),
createVNode(
Col,
{ span: 6 },
{
default: function _default() {
return [
createVNode(
'i',
{ class: styles.color5 },
null
),
createVNode(
'div',
{ class: styles.type },
[
createVNode('span', null, [
createTextVNode(
'\u76F4\u64AD\u8BFE\u63A8\u8350'
)
]),
createVNode(
'span',
{ class: styles.price },
[
moneyFormat(
_this4.moneyInfo.liveShareAmount
)
]
)
]
)
]
}
}
),
createVNode(
Col,
{ span: 6 },
{
default: function _default() {
return [
createVNode(
'i',
{ class: styles.color6 },
null
),
createVNode(
'div',
{ class: styles.type },
[
createVNode('span', null, [
createTextVNode(
'\u89C6\u9891\u8BFE\u63A8\u8350'
)
]),
createVNode(
'span',
{ class: styles.price },
[
moneyFormat(
_this4.moneyInfo
.videoShareAmount
)
]
)
]
)
]
}
}
),
createVNode(
Col,
{ span: 6 },
{
default: function _default() {
return [
createVNode(
'i',
{ class: styles.color7 },
null
),
createVNode(
'div',
{ class: styles.type },
[
createVNode('span', null, [
createTextVNode(
'\u5546\u54C1\u63A8\u8350'
)
]),
createVNode(
'span',
{ class: styles.price },
[
moneyFormat(
_this4.moneyInfo.mallShareAmount
)
]
)
]
)
]
}
}
),
createVNode(
Col,
{ span: 6 },
{
default: function _default() {
return [
createVNode(
'i',
{ class: styles.color8 },
null
),
createVNode(
'div',
{ class: styles.type },
[
createVNode('span', null, [
createTextVNode(
'\u4E50\u8C31\u63A8\u8350'
)
]),
createVNode(
'span',
{ class: styles.price },
[
moneyFormat(
_this4.moneyInfo
.musicShareAmount
)
]
)
]
)
]
}
}
),
createVNode(
Col,
{ span: 6 },
{
default: function _default() {
return [
createVNode(
'i',
{ class: styles.color9 },
null
),
createVNode(
'div',
{ class: styles.type },
[
createVNode('span', null, [
createTextVNode(
'\u6D3B\u52A8\u62A5\u540D'
)
]),
createVNode(
'span',
{ class: styles.price },
[
moneyFormat(
_this4.moneyInfo
.actiRegistShareAmount
)
]
)
]
)
]
}
}
)
]
}
}
)
]),
createVNode(
'div',
{ id: 'incomeClass', class: styles.incomeLine },
null
),
createVNode('div', { class: styles.incomeTitle }, [
createVNode('i', null, null),
createTextVNode('\u6536\u5165\u7ED3\u6784')
]),
createVNode(
'div',
{ class: [styles.pieSection, 'van-hairline--bottom'] },
[
createVNode(
'div',
{ id: 'structrueClass', class: styles.pieIncome },
null
),
createVNode('div', { class: styles.rateAll }, [
createVNode('div', null, [
createVNode(
'img',
{ src: getAssetsHomeFile('icon_user.png') },
null
),
createVNode('span', null, [
createTextVNode(
'\u4E2A\u4EBA\u6536\u5165\u603B\u5360\u6BD4'
)
]),
createVNode('span', { class: styles.rate }, [
this.moneyInfo.totalSingleRate,
createTextVNode('%')
])
]),
createVNode('div', null, [
createVNode(
'img',
{ src: getAssetsHomeFile('icon_fly.png') },
null
),
createVNode('span', null, [
createTextVNode(
'\u63A8\u5E7F\u6536\u76CA\u603B\u5360\u6BD4'
)
]),
createVNode('span', { class: styles.rate }, [
this.moneyInfo.totalShareRate,
createTextVNode('%')
])
])
])
]
),
createVNode('div', { class: styles.pieData }, [
createVNode('div', null, [
createVNode('i', { class: styles.piePractice }, null),
createVNode('span', { class: styles.pieTitle }, [
createTextVNode('\u966A\u7EC3\u8BFE')
]),
createVNode('span', null, [
this.moneyInfo.practiceRate,
createTextVNode('%')
])
]),
createVNode('div', null, [
createVNode('i', { class: styles.pie1 }, null),
createVNode('span', { class: styles.pieTitle }, [
createTextVNode('\u5C0F\u9177Ai\u63A8\u5E7F')
]),
createVNode('span', null, [
this.moneyInfo.vipShareRate,
createTextVNode('%')
])
]),
createVNode('div', null, [
createVNode('i', { class: styles.pieLive }, null),
createVNode('span', { class: styles.pieTitle }, [
createTextVNode('\u76F4\u64AD\u8BFE')
]),
createVNode('span', null, [
this.moneyInfo.liveRate,
createTextVNode('%')
])
]),
createVNode('div', null, [
createVNode('i', { class: styles.pie2 }, null),
createVNode('span', { class: styles.pieTitle }, [
createTextVNode('\u76F4\u64AD\u8BFE\u63A8\u8350')
]),
createVNode('span', null, [
this.moneyInfo.liveShareRate,
createTextVNode('%')
])
]),
createVNode('div', null, [
createVNode('i', { class: styles.pieVideo }, null),
createVNode('span', { class: styles.pieTitle }, [
createTextVNode('\u89C6\u9891\u8BFE')
]),
createVNode('span', null, [
this.moneyInfo.videoRate,
createTextVNode('%')
])
]),
createVNode('div', null, [
createVNode('i', { class: styles.pie3 }, null),
createVNode('span', { class: styles.pieTitle }, [
createTextVNode('\u89C6\u9891\u8BFE\u63A8\u8350')
]),
createVNode('span', null, [
this.moneyInfo.videoShareRate,
createTextVNode('%')
])
]),
createVNode('div', null, [
createVNode('i', { class: styles.pieMusic }, null),
createVNode('span', { class: styles.pieTitle }, [
createTextVNode('\u4E50\u8C31')
]),
createVNode('span', null, [
this.moneyInfo.musicRate,
createTextVNode('%')
])
]),
createVNode('div', null, [
createVNode('i', { class: styles.pie5 }, null),
createVNode('span', { class: styles.pieTitle }, [
createTextVNode('\u4E50\u8C31\u63A8\u8350')
]),
createVNode('span', null, [
this.moneyInfo.musicShareRate,
createTextVNode('%')
])
]),
createVNode('div', null, [
createVNode('i', { class: styles.pie6 }, null),
createVNode('span', { class: styles.pieTitle }, [
createTextVNode('\u6D3B\u52A8\u62A5\u540D')
]),
createVNode('span', null, [
this.moneyInfo.actiRegistShareRate,
createTextVNode('%')
])
]),
createVNode('div', null, [
createVNode('i', { class: styles.pie4 }, null),
createVNode('span', { class: styles.pieTitle }, [
createTextVNode('\u5546\u54C1\u63A8\u8350')
]),
createVNode('span', null, [
this.moneyInfo.mallShareRate,
createTextVNode('%')
])
])
])
]),
createVNode(
Popup,
{
show: this.timerStatus,
'onUpdate:show': function onUpdateShow($event) {
return (_this4.timerStatus = $event)
},
position: 'bottom',
round: true,
zIndex: 99999999
},
{
default: function _default() {
return [
_this4.params.timeType === 'MONTH' &&
createVNode(
DatetimePicker,
{
modelValue: _this4.currentDate,
'onUpdate:modelValue':
function onUpdateModelValue($event) {
return (_this4.currentDate = $event)
},
type: 'year-month',
title: '选择时间',
formatter: formatterDate,
onCancle: function onCancle() {
return (_this4.timerStatus = false)
},
onConfirm: function onConfirm(value) {
_this4.params.dateTime =
dayjs(value).format('YYYY-MM')
_this4.dateTimeStr =
dayjs(value).format('YYYY年MM月')
_this4.timerStatus = false
_this4.getList()
}
},
{
'columns-top': function columnsTop() {
return createVNode(
'div',
{ class: styles.timePopup },
[
createVNode(
Button,
{
type: 'primary',
plain:
_this4.params.timeType !== 'MONTH',
size: 'mini',
class: styles.timeMonth,
onClick: function onClick() {
return (_this4.params.timeType =
'MONTH')
}
},
{
default: function _default() {
return [
createTextVNode('\u6309\u6708')
]
}
}
),
createVNode(
Button,
{
type: 'primary',
plain:
_this4.params.timeType !== 'YEAR',
size: 'mini',
class: styles.timeYear,
onClick: function onClick() {
return (_this4.params.timeType =
'YEAR')
}
},
{
default: function _default() {
return [
createTextVNode('\u6309\u5E74')
]
}
}
)
]
)
}
}
),
_this4.params.timeType === 'YEAR' &&
createVNode(
Picker,
{
modelValue: _this4.currentDate,
'onUpdate:modelValue':
function onUpdateModelValue($event) {
return (_this4.currentDate = $event)
},
title: '选择时间',
columns: yearColumns,
defaultIndex: defaultIndex,
onCancel: function onCancel() {
return (_this4.timerStatus = false)
},
onConfirm: function onConfirm(obj, index) {
_this4.params.dateTime = obj.value
defaultIndex = index // 记录当前选择的年份
_this4.dateTimeStr = obj.text
_this4.timerStatus = false
_this4.getList()
}
},
{
'columns-top': function columnsTop() {
return createVNode(
'div',
{ class: styles.timePopup },
[
createVNode(
Button,
{
type: 'primary',
plain:
_this4.params.timeType !== 'MONTH',
size: 'mini',
class: styles.timeMonth,
onClick: function onClick() {
return (_this4.params.timeType =
'MONTH')
}
},
{
default: function _default() {
return [
createTextVNode('\u6309\u6708')
]
}
}
),
createVNode(
Button,
{
type: 'primary',
plain:
_this4.params.timeType !== 'YEAR',
onClick: function onClick() {
return (_this4.params.timeType =
'YEAR')
},
size: 'mini',
class: styles.timeYear
},
{
default: function _default() {
return [
createTextVNode('\u6309\u5E74')
]
}
}
)
]
)
}
}
)
]
}
}
)
])
}
})
)
}
}
}
)
})()