
/*
* 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, hasOwn, retrieve2 } from 'zrender/lib/core/util.js';
import env from 'zrender/lib/core/env.js';
import { isNumeric, getRandomIdBase, getPrecision, round } from './number.js';
import { error, 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);
  }
}
/**
 * [CAVEAT]:
 *  DO NOT use it in performance-sensitive scenarios.
 *  Likely a hash map lookup; not inline-cache friendly.
 *
 * Enable property storage to any host object.
 * Notice: Serialization is not supported.
 *
 * For example:
 * let inner = makeInner();
 *
 * function some1(hostObj) {
 *      inner(hostObj).someProperty = 1212;
 *      ...
 * }
 * function some2() {
 *      let fields = inner(this);
 *      fields.someProperty1 = 1212;
 *      fields.someProperty2 = 'xx';
 *      ...
 * }
 */
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 illegal 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) {
    if (opt.enableNone) {
      result.models = [];
      return result;
    } else {
      // Do not throw; consider if some component previously does not use this method,
      // and start to use it, need to be fault-tolerant for backward compatibility.
      if (process.env.NODE_ENV !== 'production') {
        error('`"none"` or `false` is not a valid value on index option.');
      }
      indexOption = -1; // Can not query by index but may still query by id/name if specified.
    }
  }
  // `queryComponents` will return all components if
  // both all of index/id/name are null/undefined.
  if (indexOption === 'all') {
    if (opt.enableAll) {
      indexOption = idOption = nameOption = null;
    } else {
      if (process.env.NODE_ENV !== 'production') {
        error('`"all"` is not a valid value on index option.');
      }
      indexOption = -1;
    }
  }
  result.models = ecModel.queryComponents({
    mainType: mainType,
    index: indexOption,
    id: idOption,
    name: nameOption
  });
  return result;
}
/**
 * `{{mainType}Id, {mainType}Index, {mainType}Name}` takes precedence if provided in `payload`;
 * otherwise, query by `mainType`.
 * `subType` performs futher filtering if provided.
 */
export function makeQueryConditionKindA(payload,
// `mainType` is mandatory to restrict the range of query,
// since `payload` in an user input.
mainType,
// subType may be '' by `parseClassType`
subType) {
  if (process.env.NODE_ENV !== 'production') {
    assert(mainType);
  }
  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 is determined by `hasOwnProperty`.
  return condition;
}
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;
  }
}
/**
 * Use an iterator to avoid exposing the internal list or duplicating it
 * for the outside traveller, and no extra heap allocation.
 * @usage
 *  for (const it = resetIterator(); it.next();) {
 *      const item = it.item;
 *      const key = it.key;
 *      const itIdx = it.itIdx;
 *      // ...
 *  }
 * @usage
 *  const it = resetIterator();
 *  while (it.next()) { ... }
 * @usage
 *  for (resetIterator(it); it.next();) { ... }
 */
var ListIterator = /** @class */function () {
  function ListIterator() {}
  /**
   * The loop condition is `idx < end` if `step > 0`;
   * The loop condition is `idx >= end` if `step < 0`.
   *
   * @param end By default `list.length` if `step > 0`; `0` if `step < 0`.
   * @param step By default `1`.
   */
  ListIterator.prototype.reset = function (list, start, end, step) {
    this._list = list;
    this._step = step = step || 1;
    this._idx = start;
    this._end = end != null ? end : step > 0 ? list.length : 0;
    this.item = null;
    this.key = NaN;
    return this;
  };
  ListIterator.prototype.next = function () {
    if (this._step > 0 ? this._idx < this._end : this._idx >= this._end) {
      this.item = this._list[this._idx];
      this.key = this._idx = this._idx + this._step;
      return true;
    }
    return false;
  };
  return ListIterator;
}();
export { ListIterator };
export function clearTmpModel(model) {
  // Clear to avoid memory leak.
  model.option = model.parentModel = model.ecModel = null;
}
export function initExtentForUnion() {
  return [Infinity, -Infinity];
}
/**
 * NOTICE:
 *  - The input `val` must be a number - type checking is not performed.
 *  - `extent` should be initialized as `initExtentForUnion()`.
 */
export function unionExtentFromNumber(extent, val) {
  if (isValidNumberForExtent(val)) {
    val < extent[0] && (extent[0] = val);
    val > extent[1] && (extent[1] = val);
  }
}
/**
 * NOTICE:
 *  - The input `val` must be a number - type checking is not performed.
 *  - `extent` should be initialized as `initExtentForUnion()`.
 */
export function unionExtentStartFromNumber(extent, val) {
  if (isValidNumberForExtent(val) && val < extent[0]) {
    extent[0] = val;
  }
}
/**
 * NOTICE:
 *  - The input `val` must be a number - type checking is not performed.
 *  - `extent` should be initialized as `initExtentForUnion()`.
 */
export function unionExtentEndFromNumber(extent, val) {
  if (isValidNumberForExtent(val) && val > extent[1]) {
    extent[1] = val;
  }
}
/**
 * NOTICE:
 *  - `extent` should be initialized as `initExtentForUnion()`.
 */
export function unionExtentFromExtent(tarExtent, srcExtent) {
  // Accept both or neither.
  if (isValidBoundsForExtent(srcExtent[0], srcExtent[1])) {
    srcExtent[0] < tarExtent[0] && (tarExtent[0] = srcExtent[0]);
    srcExtent[1] > tarExtent[1] && (tarExtent[1] = srcExtent[1]);
  }
}
/**
 * PENDING: `Infinity` from user data is not necessarily meaningless, but visualizing it requires
 * special handling and it will not be supported until required. So we simply ignore it here.
 */
export function isValidNumberForExtent(val) {
  // Considered that number could be `NaN` and `Infinity` and should not write into the extent.
  // Note that `Infinity` is the initialized value from `initExtentForUnion` and may be inputted.
  return val != null && isFinite(val);
}
export function isValidBoundsForExtent(start, end) {
  return isValidNumberForExtent(start) && isValidNumberForExtent(end) && start <= end;
}
/**
 * `extent` should be initialized by `initExtentForUnion()`, and unioned by `unionExtent()`.
 * `extent` may contain `Infinity` / `NaN`, but assume no `null`/`undefined`.
 */
export function extentHasValue(extent) {
  // Also considered extent may have `NaN` and `Infinity`.
  var span = extent[1] - extent[0];
  return isFinite(span) && span >= 0;
}
/**
 * NOTE: considered items are null/undefined/NaN - do nothing for this case.
 */
export function ensureExtentAscSimply(extent) {
  if (isValidBoundsForExtent(extent[0], extent[1]) && extent[0] > extent[1]) {
    extent[0] = extent[1];
  }
}
/**
 * A util for ensuring the callback is called only once.
 * @usage
 *  const callOnlyOnce = makeCallOnlyOnce(); // Should be static (ESM top level).
 *  function someFunc(registers: EChartsExtensionInstallRegisters): void {
 *      callOnlyOnce(registers, function () {
 *          // Do something immediately and only once per registers.
 *      }
 *  }
 */
export function makeCallOnlyOnce() {
  var hiddenKey = '__ec_once_' + onceUniqueIndex++;
  return function (hostObj, cb) {
    if (process.env.NODE_ENV !== 'production') {
      assert(hostObj);
    }
    if (!hasOwn(hostObj, hiddenKey)) {
      hostObj[hiddenKey] = 1;
      cb();
    }
  };
}
var onceUniqueIndex = getRandomIdBase();
/**
 * @usage
 *  - The earlier item takes precedence for duplicate items.
 *  - The input `arr` will be modified if `resolve` is null/undefined.
 *  - Callers can use `resolve` to manually modify the `currItem`.
 *    The input `arr` will not be modified if `resolve` is passed.
 *    `resolve` will be called on every item.
 *  - Callers need to handle null/undefined (if existing) in `getKey`.
 */
export function removeDuplicates(arr, getKey,
// `existingCount`: the count before this item is added.
resolve) {
  var dupMap = createHashMap();
  var writeIdx = 0;
  each(arr, function (item) {
    var key = getKey(item);
    if (process.env.NODE_ENV !== 'production') {
      assert(isString(key));
    }
    var count = dupMap.get(key) || 0;
    if (resolve) {
      resolve(item, count);
    }
    if (!count && !resolve) {
      arr[writeIdx++] = item;
    }
    dupMap.set(key, count + 1);
  });
  if (!resolve) {
    arr.length = writeIdx;
  }
}
export function removeDuplicatesGetKeyFromValueProp(item) {
  if (process.env.NODE_ENV !== 'production') {
    assert(item.value != null);
  }
  return item.value + '';
}
export function removeDuplicatesGetKeyFromItemItself(item) {
  if (process.env.NODE_ENV !== 'production') {
    assert(item != null);
  }
  return item + '';
}
export function getIncrementalId(seriesModel, useIncremental) {
  // 0 means disable incremental.
  // 1 is preserved for backward compatibility.
  return retrieve2(useIncremental, true) ? seriesModel.seriesIndex + 2 : 0;
}
export function preparePipelineContext(seriesModel, view, pipeline) {
  var dataLen = seriesModel.getData().count();
  return {
    progressiveRender: pipeline.progressiveEnabled && view.incrementalPrepareRender && dataLen >= pipeline.threshold,
    large: seriesModel.get('large') && dataLen >= seriesModel.get('largeThreshold'),
    // TODO: modDataCount should not updated if `appendData`, otherwise cause whole repaint.
    // see `test/candlestick-large3.html`
    modDataCount: seriesModel.get('progressiveChunkMode') === 'mod' ? seriesModel.getData().count() : null
  };
}
/**
 * When some task "blocks" the upstream part of the pipeline, the upstream output (typically, a TypedArray
 * to carry layout points, e.g., `data.getLayout('points')`) range is from the start to the final end.
 * A downstream comsumer should either properly record cursors when reading from the upstream output,
 * or fail fast on that usage.
 * Otherwise, take the `data.getLayout('points')` as an example, repeatedly merging it with the existing
 * shape path can create a big path, which degrades performance but is hard to detect.
 *
 * This method provides an assertion for that.
 */
export function validateUpstreamOutputRange(
// null/undefined is not allowed, otherwise bug-prone.
upstreamOutputRange, thisTaskPlannedRange) {
  assert(upstreamOutputRange.start === thisTaskPlannedRange.start && upstreamOutputRange.end === thisTaskPlannedRange.end);
}
export function createSimpleOverallStageHandler(seriesType, overallReset) {
  return {
    seriesType: seriesType,
    overallReset: overallReset
  };
}
export function createSimpleOverallStageHandler2(overallReset) {
  return {
    overallReset: overallReset
  };
}