|   | 
| /* | 
| * 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. | 
| */ | 
| import { each, isObject, isArray, createHashMap, map, assert, isString, indexOf, isStringSafe, isNumber } from 'zrender/lib/core/util.js'; | 
| import env from 'zrender/lib/core/env.js'; | 
| import { isNumeric, getRandomIdBase, getPrecision, round } from './number.js'; | 
| import { warn } from './log.js'; | 
| function interpolateNumber(p0, p1, percent) { | 
|   return (p1 - p0) * percent + p0; | 
| } | 
| /** | 
|  * Make the name displayable. But we should | 
|  * make sure it is not duplicated with user | 
|  * specified name, so use '\0'; | 
|  */ | 
| var DUMMY_COMPONENT_NAME_PREFIX = 'series\0'; | 
| var INTERNAL_COMPONENT_ID_PREFIX = '\0_ec_\0'; | 
| /** | 
|  * If value is not array, then translate it to array. | 
|  * @param  {*} value | 
|  * @return {Array} [value] or value | 
|  */ | 
| export function normalizeToArray(value) { | 
|   return value instanceof Array ? value : value == null ? [] : [value]; | 
| } | 
| /** | 
|  * Sync default option between normal and emphasis like `position` and `show` | 
|  * In case some one will write code like | 
|  *     label: { | 
|  *          show: false, | 
|  *          position: 'outside', | 
|  *          fontSize: 18 | 
|  *     }, | 
|  *     emphasis: { | 
|  *          label: { show: true } | 
|  *     } | 
|  */ | 
| export function defaultEmphasis(opt, key, subOpts) { | 
|   // Caution: performance sensitive. | 
|   if (opt) { | 
|     opt[key] = opt[key] || {}; | 
|     opt.emphasis = opt.emphasis || {}; | 
|     opt.emphasis[key] = opt.emphasis[key] || {}; | 
|     // Default emphasis option from normal | 
|     for (var i = 0, len = subOpts.length; i < len; i++) { | 
|       var subOptName = subOpts[i]; | 
|       if (!opt.emphasis[key].hasOwnProperty(subOptName) && opt[key].hasOwnProperty(subOptName)) { | 
|         opt.emphasis[key][subOptName] = opt[key][subOptName]; | 
|       } | 
|     } | 
|   } | 
| } | 
| export var TEXT_STYLE_OPTIONS = ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily', 'rich', 'tag', 'color', 'textBorderColor', 'textBorderWidth', 'width', 'height', 'lineHeight', 'align', 'verticalAlign', 'baseline', 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY', 'textShadowColor', 'textShadowBlur', 'textShadowOffsetX', 'textShadowOffsetY', 'backgroundColor', 'borderColor', 'borderWidth', 'borderRadius', 'padding']; | 
| // modelUtil.LABEL_OPTIONS = modelUtil.TEXT_STYLE_OPTIONS.concat([ | 
| //     'position', 'offset', 'rotate', 'origin', 'show', 'distance', 'formatter', | 
| //     'fontStyle', 'fontWeight', 'fontSize', 'fontFamily', | 
| //     // FIXME: deprecated, check and remove it. | 
| //     'textStyle' | 
| // ]); | 
| /** | 
|  * The method does not ensure performance. | 
|  * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}] | 
|  * This helper method retrieves value from data. | 
|  */ | 
| export function getDataItemValue(dataItem) { | 
|   return isObject(dataItem) && !isArray(dataItem) && !(dataItem instanceof Date) ? dataItem.value : dataItem; | 
| } | 
| /** | 
|  * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}] | 
|  * This helper method determine if dataItem has extra option besides value | 
|  */ | 
| export function isDataItemOption(dataItem) { | 
|   return isObject(dataItem) && !(dataItem instanceof Array); | 
|   // // markLine data can be array | 
|   // && !(dataItem[0] && isObject(dataItem[0]) && !(dataItem[0] instanceof Array)); | 
| } | 
| ; | 
| /** | 
|  * Mapping to existings for merge. | 
|  * | 
|  * Mode "normalMege": | 
|  *     The mapping result (merge result) will keep the order of the existing | 
|  *     component, rather than the order of new option. Because we should ensure | 
|  *     some specified index reference (like xAxisIndex) keep work. | 
|  *     And in most cases, "merge option" is used to update partial option but not | 
|  *     be expected to change the order. | 
|  * | 
|  * Mode "replaceMege": | 
|  *     (1) Only the id mapped components will be merged. | 
|  *     (2) Other existing components (except internal components) will be removed. | 
|  *     (3) Other new options will be used to create new component. | 
|  *     (4) The index of the existing components will not be modified. | 
|  *     That means their might be "hole" after the removal. | 
|  *     The new components are created first at those available index. | 
|  * | 
|  * Mode "replaceAll": | 
|  *     This mode try to support that reproduce an echarts instance from another | 
|  *     echarts instance (via `getOption`) in some simple cases. | 
|  *     In this scenario, the `result` index are exactly the consistent with the `newCmptOptions`, | 
|  *     which ensures the component index referring (like `xAxisIndex: ?`) corrent. That is, | 
|  *     the "hole" in `newCmptOptions` will also be kept. | 
|  *     On the contrary, other modes try best to eliminate holes. | 
|  *     PENDING: This is an experimental mode yet. | 
|  * | 
|  * @return See the comment of <MappingResult>. | 
|  */ | 
| export function mappingToExists(existings, newCmptOptions, mode) { | 
|   var isNormalMergeMode = mode === 'normalMerge'; | 
|   var isReplaceMergeMode = mode === 'replaceMerge'; | 
|   var isReplaceAllMode = mode === 'replaceAll'; | 
|   existings = existings || []; | 
|   newCmptOptions = (newCmptOptions || []).slice(); | 
|   var existingIdIdxMap = createHashMap(); | 
|   // Validate id and name on user input option. | 
|   each(newCmptOptions, function (cmptOption, index) { | 
|     if (!isObject(cmptOption)) { | 
|       newCmptOptions[index] = null; | 
|       return; | 
|     } | 
|     if (process.env.NODE_ENV !== 'production') { | 
|       // There is some legacy case that name is set as `false`. | 
|       // But should work normally rather than throw error. | 
|       if (cmptOption.id != null && !isValidIdOrName(cmptOption.id)) { | 
|         warnInvalidateIdOrName(cmptOption.id); | 
|       } | 
|       if (cmptOption.name != null && !isValidIdOrName(cmptOption.name)) { | 
|         warnInvalidateIdOrName(cmptOption.name); | 
|       } | 
|     } | 
|   }); | 
|   var result = prepareResult(existings, existingIdIdxMap, mode); | 
|   if (isNormalMergeMode || isReplaceMergeMode) { | 
|     mappingById(result, existings, existingIdIdxMap, newCmptOptions); | 
|   } | 
|   if (isNormalMergeMode) { | 
|     mappingByName(result, newCmptOptions); | 
|   } | 
|   if (isNormalMergeMode || isReplaceMergeMode) { | 
|     mappingByIndex(result, newCmptOptions, isReplaceMergeMode); | 
|   } else if (isReplaceAllMode) { | 
|     mappingInReplaceAllMode(result, newCmptOptions); | 
|   } | 
|   makeIdAndName(result); | 
|   // The array `result` MUST NOT contain elided items, otherwise the | 
|   // forEach will omit those items and result in incorrect result. | 
|   return result; | 
| } | 
| function prepareResult(existings, existingIdIdxMap, mode) { | 
|   var result = []; | 
|   if (mode === 'replaceAll') { | 
|     return result; | 
|   } | 
|   // Do not use native `map` to in case that the array `existings` | 
|   // contains elided items, which will be omitted. | 
|   for (var index = 0; index < existings.length; index++) { | 
|     var existing = existings[index]; | 
|     // Because of replaceMerge, `existing` may be null/undefined. | 
|     if (existing && existing.id != null) { | 
|       existingIdIdxMap.set(existing.id, index); | 
|     } | 
|     // For non-internal-componnets: | 
|     //     Mode "normalMerge": all existings kept. | 
|     //     Mode "replaceMerge": all existing removed unless mapped by id. | 
|     // For internal-components: | 
|     //     go with "replaceMerge" approach in both mode. | 
|     result.push({ | 
|       existing: mode === 'replaceMerge' || isComponentIdInternal(existing) ? null : existing, | 
|       newOption: null, | 
|       keyInfo: null, | 
|       brandNew: null | 
|     }); | 
|   } | 
|   return result; | 
| } | 
| function mappingById(result, existings, existingIdIdxMap, newCmptOptions) { | 
|   // Mapping by id if specified. | 
|   each(newCmptOptions, function (cmptOption, index) { | 
|     if (!cmptOption || cmptOption.id == null) { | 
|       return; | 
|     } | 
|     var optionId = makeComparableKey(cmptOption.id); | 
|     var existingIdx = existingIdIdxMap.get(optionId); | 
|     if (existingIdx != null) { | 
|       var resultItem = result[existingIdx]; | 
|       assert(!resultItem.newOption, 'Duplicated option on id "' + optionId + '".'); | 
|       resultItem.newOption = cmptOption; | 
|       // In both mode, if id matched, new option will be merged to | 
|       // the existings rather than creating new component model. | 
|       resultItem.existing = existings[existingIdx]; | 
|       newCmptOptions[index] = null; | 
|     } | 
|   }); | 
| } | 
| function mappingByName(result, newCmptOptions) { | 
|   // Mapping by name if specified. | 
|   each(newCmptOptions, function (cmptOption, index) { | 
|     if (!cmptOption || cmptOption.name == null) { | 
|       return; | 
|     } | 
|     for (var i = 0; i < result.length; i++) { | 
|       var existing = result[i].existing; | 
|       if (!result[i].newOption // Consider name: two map to one. | 
|       // Can not match when both ids existing but different. | 
|       && existing && (existing.id == null || cmptOption.id == null) && !isComponentIdInternal(cmptOption) && !isComponentIdInternal(existing) && keyExistAndEqual('name', existing, cmptOption)) { | 
|         result[i].newOption = cmptOption; | 
|         newCmptOptions[index] = null; | 
|         return; | 
|       } | 
|     } | 
|   }); | 
| } | 
| function mappingByIndex(result, newCmptOptions, brandNew) { | 
|   each(newCmptOptions, function (cmptOption) { | 
|     if (!cmptOption) { | 
|       return; | 
|     } | 
|     // Find the first place that not mapped by id and not internal component (consider the "hole"). | 
|     var resultItem; | 
|     var nextIdx = 0; | 
|     while ( | 
|     // Be `!resultItem` only when `nextIdx >= result.length`. | 
|     (resultItem = result[nextIdx] | 
|     // (1) Existing models that already have id should be able to mapped to. Because | 
|     // after mapping performed, model will always be assigned with an id if user not given. | 
|     // After that all models have id. | 
|     // (2) If new option has id, it can only set to a hole or append to the last. It should | 
|     // not be merged to the existings with different id. Because id should not be overwritten. | 
|     // (3) Name can be overwritten, because axis use name as 'show label text'. | 
|     ) && (resultItem.newOption || isComponentIdInternal(resultItem.existing) || | 
|     // In mode "replaceMerge", here no not-mapped-non-internal-existing. | 
|     resultItem.existing && cmptOption.id != null && !keyExistAndEqual('id', cmptOption, resultItem.existing))) { | 
|       nextIdx++; | 
|     } | 
|     if (resultItem) { | 
|       resultItem.newOption = cmptOption; | 
|       resultItem.brandNew = brandNew; | 
|     } else { | 
|       result.push({ | 
|         newOption: cmptOption, | 
|         brandNew: brandNew, | 
|         existing: null, | 
|         keyInfo: null | 
|       }); | 
|     } | 
|     nextIdx++; | 
|   }); | 
| } | 
| function mappingInReplaceAllMode(result, newCmptOptions) { | 
|   each(newCmptOptions, function (cmptOption) { | 
|     // The feature "reproduce" requires "hole" will also reproduced | 
|     // in case that component index referring are broken. | 
|     result.push({ | 
|       newOption: cmptOption, | 
|       brandNew: true, | 
|       existing: null, | 
|       keyInfo: null | 
|     }); | 
|   }); | 
| } | 
| /** | 
|  * Make id and name for mapping result (result of mappingToExists) | 
|  * into `keyInfo` field. | 
|  */ | 
| function makeIdAndName(mapResult) { | 
|   // We use this id to hash component models and view instances | 
|   // in echarts. id can be specified by user, or auto generated. | 
|   // The id generation rule ensures new view instance are able | 
|   // to mapped to old instance when setOption are called in | 
|   // no-merge mode. So we generate model id by name and plus | 
|   // type in view id. | 
|   // name can be duplicated among components, which is convenient | 
|   // to specify multi components (like series) by one name. | 
|   // Ensure that each id is distinct. | 
|   var idMap = createHashMap(); | 
|   each(mapResult, function (item) { | 
|     var existing = item.existing; | 
|     existing && idMap.set(existing.id, item); | 
|   }); | 
|   each(mapResult, function (item) { | 
|     var opt = item.newOption; | 
|     // Force ensure id not duplicated. | 
|     assert(!opt || opt.id == null || !idMap.get(opt.id) || idMap.get(opt.id) === item, 'id duplicates: ' + (opt && opt.id)); | 
|     opt && opt.id != null && idMap.set(opt.id, item); | 
|     !item.keyInfo && (item.keyInfo = {}); | 
|   }); | 
|   // Make name and id. | 
|   each(mapResult, function (item, index) { | 
|     var existing = item.existing; | 
|     var opt = item.newOption; | 
|     var keyInfo = item.keyInfo; | 
|     if (!isObject(opt)) { | 
|       return; | 
|     } | 
|     // Name can be overwritten. Consider case: axis.name = '20km'. | 
|     // But id generated by name will not be changed, which affect | 
|     // only in that case: setOption with 'not merge mode' and view | 
|     // instance will be recreated, which can be accepted. | 
|     keyInfo.name = opt.name != null ? makeComparableKey(opt.name) : existing ? existing.name | 
|     // Avoid that different series has the same name, | 
|     // because name may be used like in color pallet. | 
|     : DUMMY_COMPONENT_NAME_PREFIX + index; | 
|     if (existing) { | 
|       keyInfo.id = makeComparableKey(existing.id); | 
|     } else if (opt.id != null) { | 
|       keyInfo.id = makeComparableKey(opt.id); | 
|     } else { | 
|       // Consider this situatoin: | 
|       //  optionA: [{name: 'a'}, {name: 'a'}, {..}] | 
|       //  optionB [{..}, {name: 'a'}, {name: 'a'}] | 
|       // Series with the same name between optionA and optionB | 
|       // should be mapped. | 
|       var idNum = 0; | 
|       do { | 
|         keyInfo.id = '\0' + keyInfo.name + '\0' + idNum++; | 
|       } while (idMap.get(keyInfo.id)); | 
|     } | 
|     idMap.set(keyInfo.id, item); | 
|   }); | 
| } | 
| function keyExistAndEqual(attr, obj1, obj2) { | 
|   var key1 = convertOptionIdName(obj1[attr], null); | 
|   var key2 = convertOptionIdName(obj2[attr], null); | 
|   // See `MappingExistingItem`. `id` and `name` trade string equals to number. | 
|   return key1 != null && key2 != null && key1 === key2; | 
| } | 
| /** | 
|  * @return return null if not exist. | 
|  */ | 
| function makeComparableKey(val) { | 
|   if (process.env.NODE_ENV !== 'production') { | 
|     if (val == null) { | 
|       throw new Error(); | 
|     } | 
|   } | 
|   return convertOptionIdName(val, ''); | 
| } | 
| export function convertOptionIdName(idOrName, defaultValue) { | 
|   if (idOrName == null) { | 
|     return defaultValue; | 
|   } | 
|   return isString(idOrName) ? idOrName : isNumber(idOrName) || isStringSafe(idOrName) ? idOrName + '' : defaultValue; | 
| } | 
| function warnInvalidateIdOrName(idOrName) { | 
|   if (process.env.NODE_ENV !== 'production') { | 
|     warn('`' + idOrName + '` is invalid id or name. Must be a string or number.'); | 
|   } | 
| } | 
| function isValidIdOrName(idOrName) { | 
|   return isStringSafe(idOrName) || isNumeric(idOrName); | 
| } | 
| export function isNameSpecified(componentModel) { | 
|   var name = componentModel.name; | 
|   // Is specified when `indexOf` get -1 or > 0. | 
|   return !!(name && name.indexOf(DUMMY_COMPONENT_NAME_PREFIX)); | 
| } | 
| /** | 
|  * @public | 
|  * @param {Object} cmptOption | 
|  * @return {boolean} | 
|  */ | 
| export function isComponentIdInternal(cmptOption) { | 
|   return cmptOption && cmptOption.id != null && makeComparableKey(cmptOption.id).indexOf(INTERNAL_COMPONENT_ID_PREFIX) === 0; | 
| } | 
| export function makeInternalComponentId(idSuffix) { | 
|   return INTERNAL_COMPONENT_ID_PREFIX + idSuffix; | 
| } | 
| export function setComponentTypeToKeyInfo(mappingResult, mainType, componentModelCtor) { | 
|   // Set mainType and complete subType. | 
|   each(mappingResult, function (item) { | 
|     var newOption = item.newOption; | 
|     if (isObject(newOption)) { | 
|       item.keyInfo.mainType = mainType; | 
|       item.keyInfo.subType = determineSubType(mainType, newOption, item.existing, componentModelCtor); | 
|     } | 
|   }); | 
| } | 
| function determineSubType(mainType, newCmptOption, existComponent, componentModelCtor) { | 
|   var subType = newCmptOption.type ? newCmptOption.type : existComponent ? existComponent.subType | 
|   // Use determineSubType only when there is no existComponent. | 
|   : componentModelCtor.determineSubType(mainType, newCmptOption); | 
|   // tooltip, markline, markpoint may always has no subType | 
|   return subType; | 
| } | 
| /** | 
|  * A helper for removing duplicate items between batchA and batchB, | 
|  * and in themselves, and categorize by series. | 
|  * | 
|  * @param batchA Like: [{seriesId: 2, dataIndex: [32, 4, 5]}, ...] | 
|  * @param batchB Like: [{seriesId: 2, dataIndex: [32, 4, 5]}, ...] | 
|  * @return result: [resultBatchA, resultBatchB] | 
|  */ | 
| export function compressBatches(batchA, batchB) { | 
|   var mapA = {}; | 
|   var mapB = {}; | 
|   makeMap(batchA || [], mapA); | 
|   makeMap(batchB || [], mapB, mapA); | 
|   return [mapToArray(mapA), mapToArray(mapB)]; | 
|   function makeMap(sourceBatch, map, otherMap) { | 
|     for (var i = 0, len = sourceBatch.length; i < len; i++) { | 
|       var seriesId = convertOptionIdName(sourceBatch[i].seriesId, null); | 
|       if (seriesId == null) { | 
|         return; | 
|       } | 
|       var dataIndices = normalizeToArray(sourceBatch[i].dataIndex); | 
|       var otherDataIndices = otherMap && otherMap[seriesId]; | 
|       for (var j = 0, lenj = dataIndices.length; j < lenj; j++) { | 
|         var dataIndex = dataIndices[j]; | 
|         if (otherDataIndices && otherDataIndices[dataIndex]) { | 
|           otherDataIndices[dataIndex] = null; | 
|         } else { | 
|           (map[seriesId] || (map[seriesId] = {}))[dataIndex] = 1; | 
|         } | 
|       } | 
|     } | 
|   } | 
|   function mapToArray(map, isData) { | 
|     var result = []; | 
|     for (var i in map) { | 
|       if (map.hasOwnProperty(i) && map[i] != null) { | 
|         if (isData) { | 
|           result.push(+i); | 
|         } else { | 
|           var dataIndices = mapToArray(map[i], true); | 
|           dataIndices.length && result.push({ | 
|             seriesId: i, | 
|             dataIndex: dataIndices | 
|           }); | 
|         } | 
|       } | 
|     } | 
|     return result; | 
|   } | 
| } | 
| /** | 
|  * @param payload Contains dataIndex (means rawIndex) / dataIndexInside / name | 
|  *                         each of which can be Array or primary type. | 
|  * @return dataIndex If not found, return undefined/null. | 
|  */ | 
| export function queryDataIndex(data, payload) { | 
|   if (payload.dataIndexInside != null) { | 
|     return payload.dataIndexInside; | 
|   } else if (payload.dataIndex != null) { | 
|     return isArray(payload.dataIndex) ? map(payload.dataIndex, function (value) { | 
|       return data.indexOfRawIndex(value); | 
|     }) : data.indexOfRawIndex(payload.dataIndex); | 
|   } else if (payload.name != null) { | 
|     return isArray(payload.name) ? map(payload.name, function (value) { | 
|       return data.indexOfName(value); | 
|     }) : data.indexOfName(payload.name); | 
|   } | 
| } | 
| /** | 
|  * Enable property storage to any host object. | 
|  * Notice: Serialization is not supported. | 
|  * | 
|  * For example: | 
|  * let inner = zrUitl.makeInner(); | 
|  * | 
|  * function some1(hostObj) { | 
|  *      inner(hostObj).someProperty = 1212; | 
|  *      ... | 
|  * } | 
|  * function some2() { | 
|  *      let fields = inner(this); | 
|  *      fields.someProperty1 = 1212; | 
|  *      fields.someProperty2 = 'xx'; | 
|  *      ... | 
|  * } | 
|  * | 
|  * @return {Function} | 
|  */ | 
| export function makeInner() { | 
|   var key = '__ec_inner_' + innerUniqueIndex++; | 
|   return function (hostObj) { | 
|     return hostObj[key] || (hostObj[key] = {}); | 
|   }; | 
| } | 
| var innerUniqueIndex = getRandomIdBase(); | 
| /** | 
|  * The same behavior as `component.getReferringComponents`. | 
|  */ | 
| export function parseFinder(ecModel, finderInput, opt) { | 
|   var _a = preParseFinder(finderInput, opt), | 
|     mainTypeSpecified = _a.mainTypeSpecified, | 
|     queryOptionMap = _a.queryOptionMap, | 
|     others = _a.others; | 
|   var result = others; | 
|   var defaultMainType = opt ? opt.defaultMainType : null; | 
|   if (!mainTypeSpecified && defaultMainType) { | 
|     queryOptionMap.set(defaultMainType, {}); | 
|   } | 
|   queryOptionMap.each(function (queryOption, mainType) { | 
|     var queryResult = queryReferringComponents(ecModel, mainType, queryOption, { | 
|       useDefault: defaultMainType === mainType, | 
|       enableAll: opt && opt.enableAll != null ? opt.enableAll : true, | 
|       enableNone: opt && opt.enableNone != null ? opt.enableNone : true | 
|     }); | 
|     result[mainType + 'Models'] = queryResult.models; | 
|     result[mainType + 'Model'] = queryResult.models[0]; | 
|   }); | 
|   return result; | 
| } | 
| export function preParseFinder(finderInput, opt) { | 
|   var finder; | 
|   if (isString(finderInput)) { | 
|     var obj = {}; | 
|     obj[finderInput + 'Index'] = 0; | 
|     finder = obj; | 
|   } else { | 
|     finder = finderInput; | 
|   } | 
|   var queryOptionMap = createHashMap(); | 
|   var others = {}; | 
|   var mainTypeSpecified = false; | 
|   each(finder, function (value, key) { | 
|     // Exclude 'dataIndex' and other illgal keys. | 
|     if (key === 'dataIndex' || key === 'dataIndexInside') { | 
|       others[key] = value; | 
|       return; | 
|     } | 
|     var parsedKey = key.match(/^(\w+)(Index|Id|Name)$/) || []; | 
|     var mainType = parsedKey[1]; | 
|     var queryType = (parsedKey[2] || '').toLowerCase(); | 
|     if (!mainType || !queryType || opt && opt.includeMainTypes && indexOf(opt.includeMainTypes, mainType) < 0) { | 
|       return; | 
|     } | 
|     mainTypeSpecified = mainTypeSpecified || !!mainType; | 
|     var queryOption = queryOptionMap.get(mainType) || queryOptionMap.set(mainType, {}); | 
|     queryOption[queryType] = value; | 
|   }); | 
|   return { | 
|     mainTypeSpecified: mainTypeSpecified, | 
|     queryOptionMap: queryOptionMap, | 
|     others: others | 
|   }; | 
| } | 
| export var SINGLE_REFERRING = { | 
|   useDefault: true, | 
|   enableAll: false, | 
|   enableNone: false | 
| }; | 
| export var MULTIPLE_REFERRING = { | 
|   useDefault: false, | 
|   enableAll: true, | 
|   enableNone: true | 
| }; | 
| export function queryReferringComponents(ecModel, mainType, userOption, opt) { | 
|   opt = opt || SINGLE_REFERRING; | 
|   var indexOption = userOption.index; | 
|   var idOption = userOption.id; | 
|   var nameOption = userOption.name; | 
|   var result = { | 
|     models: null, | 
|     specified: indexOption != null || idOption != null || nameOption != null | 
|   }; | 
|   if (!result.specified) { | 
|     // Use the first as default if `useDefault`. | 
|     var firstCmpt = void 0; | 
|     result.models = opt.useDefault && (firstCmpt = ecModel.getComponent(mainType)) ? [firstCmpt] : []; | 
|     return result; | 
|   } | 
|   if (indexOption === 'none' || indexOption === false) { | 
|     assert(opt.enableNone, '`"none"` or `false` is not a valid value on index option.'); | 
|     result.models = []; | 
|     return result; | 
|   } | 
|   // `queryComponents` will return all components if | 
|   // both all of index/id/name are null/undefined. | 
|   if (indexOption === 'all') { | 
|     assert(opt.enableAll, '`"all"` is not a valid value on index option.'); | 
|     indexOption = idOption = nameOption = null; | 
|   } | 
|   result.models = ecModel.queryComponents({ | 
|     mainType: mainType, | 
|     index: indexOption, | 
|     id: idOption, | 
|     name: nameOption | 
|   }); | 
|   return result; | 
| } | 
| export function setAttribute(dom, key, value) { | 
|   dom.setAttribute ? dom.setAttribute(key, value) : dom[key] = value; | 
| } | 
| export function getAttribute(dom, key) { | 
|   return dom.getAttribute ? dom.getAttribute(key) : dom[key]; | 
| } | 
| export function getTooltipRenderMode(renderModeOption) { | 
|   if (renderModeOption === 'auto') { | 
|     // Using html when `document` exists, use richText otherwise | 
|     return env.domSupported ? 'html' : 'richText'; | 
|   } else { | 
|     return renderModeOption || 'html'; | 
|   } | 
| } | 
| /** | 
|  * Group a list by key. | 
|  */ | 
| export function groupData(array, getKey // return key | 
| ) { | 
|   var buckets = createHashMap(); | 
|   var keys = []; | 
|   each(array, function (item) { | 
|     var key = getKey(item); | 
|     (buckets.get(key) || (keys.push(key), buckets.set(key, []))).push(item); | 
|   }); | 
|   return { | 
|     keys: keys, | 
|     buckets: buckets | 
|   }; | 
| } | 
| /** | 
|  * Interpolate raw values of a series with percent | 
|  * | 
|  * @param data         data | 
|  * @param labelModel   label model of the text element | 
|  * @param sourceValue  start value. May be null/undefined when init. | 
|  * @param targetValue  end value | 
|  * @param percent      0~1 percentage; 0 uses start value while 1 uses end value | 
|  * @return             interpolated values | 
|  *                     If `sourceValue` and `targetValue` are `number`, return `number`. | 
|  *                     If `sourceValue` and `targetValue` are `string`, return `string`. | 
|  *                     If `sourceValue` and `targetValue` are `(string | number)[]`, return `(string | number)[]`. | 
|  *                     Other cases do not supported. | 
|  */ | 
| export function interpolateRawValues(data, precision, sourceValue, targetValue, percent) { | 
|   var isAutoPrecision = precision == null || precision === 'auto'; | 
|   if (targetValue == null) { | 
|     return targetValue; | 
|   } | 
|   if (isNumber(targetValue)) { | 
|     var value = interpolateNumber(sourceValue || 0, targetValue, percent); | 
|     return round(value, isAutoPrecision ? Math.max(getPrecision(sourceValue || 0), getPrecision(targetValue)) : precision); | 
|   } else if (isString(targetValue)) { | 
|     return percent < 1 ? sourceValue : targetValue; | 
|   } else { | 
|     var interpolated = []; | 
|     var leftArr = sourceValue; | 
|     var rightArr = targetValue; | 
|     var length_1 = Math.max(leftArr ? leftArr.length : 0, rightArr.length); | 
|     for (var i = 0; i < length_1; ++i) { | 
|       var info = data.getDimensionInfo(i); | 
|       // Don't interpolate ordinal dims | 
|       if (info && info.type === 'ordinal') { | 
|         // In init, there is no `sourceValue`, but should better not to get undefined result. | 
|         interpolated[i] = (percent < 1 && leftArr ? leftArr : rightArr)[i]; | 
|       } else { | 
|         var leftVal = leftArr && leftArr[i] ? leftArr[i] : 0; | 
|         var rightVal = rightArr[i]; | 
|         var value = interpolateNumber(leftVal, rightVal, percent); | 
|         interpolated[i] = round(value, isAutoPrecision ? Math.max(getPrecision(leftVal), getPrecision(rightVal)) : precision); | 
|       } | 
|     } | 
|     return interpolated; | 
|   } | 
| } |