/* global: defineProperty */ 
 | 
import { Dictionary, ArrayLike, KeyOfDistributive } from './types'; 
 | 
import { GradientObject } from '../graphic/Gradient'; 
 | 
import { ImagePatternObject } from '../graphic/Pattern'; 
 | 
import { platformApi } from './platform'; 
 | 
  
 | 
// 用于处理merge时无法遍历Date等对象的问题 
 | 
const BUILTIN_OBJECT: Record<string, boolean> = reduce([ 
 | 
    'Function', 
 | 
    'RegExp', 
 | 
    'Date', 
 | 
    'Error', 
 | 
    'CanvasGradient', 
 | 
    'CanvasPattern', 
 | 
    // For node-canvas 
 | 
    'Image', 
 | 
    'Canvas' 
 | 
], (obj, val) => { 
 | 
    obj['[object ' + val + ']'] = true; 
 | 
    return obj; 
 | 
}, {} as Record<string, boolean>); 
 | 
  
 | 
const TYPED_ARRAY: Record<string, boolean> = reduce([ 
 | 
    'Int8', 
 | 
    'Uint8', 
 | 
    'Uint8Clamped', 
 | 
    'Int16', 
 | 
    'Uint16', 
 | 
    'Int32', 
 | 
    'Uint32', 
 | 
    'Float32', 
 | 
    'Float64' 
 | 
], (obj, val) => { 
 | 
    obj['[object ' + val + 'Array]'] = true; 
 | 
    return obj; 
 | 
}, {} as Record<string, boolean>); 
 | 
  
 | 
const objToString = Object.prototype.toString; 
 | 
  
 | 
const arrayProto = Array.prototype; 
 | 
const nativeForEach = arrayProto.forEach; 
 | 
const nativeFilter = arrayProto.filter; 
 | 
const nativeSlice = arrayProto.slice; 
 | 
const nativeMap = arrayProto.map; 
 | 
// In case some env may redefine the global variable `Function`. 
 | 
const ctorFunction = function () {}.constructor; 
 | 
const protoFunction = ctorFunction ? ctorFunction.prototype : null; 
 | 
const protoKey = '__proto__'; 
 | 
  
 | 
  
 | 
let idStart = 0x0907; 
 | 
  
 | 
/** 
 | 
 * Generate unique id 
 | 
 */ 
 | 
export function guid(): number { 
 | 
    return idStart++; 
 | 
} 
 | 
  
 | 
export function logError(...args: any[]) { 
 | 
    if (typeof console !== 'undefined') { 
 | 
        console.error.apply(console, args); 
 | 
    } 
 | 
} 
 | 
/** 
 | 
 * Those data types can be cloned: 
 | 
 *     Plain object, Array, TypedArray, number, string, null, undefined. 
 | 
 * Those data types will be assigned using the original data: 
 | 
 *     BUILTIN_OBJECT 
 | 
 * Instance of user defined class will be cloned to a plain object, without 
 | 
 * properties in prototype. 
 | 
 * Other data types is not supported (not sure what will happen). 
 | 
 * 
 | 
 * Caution: do not support clone Date, for performance consideration. 
 | 
 * (There might be a large number of date in `series.data`). 
 | 
 * So date should not be modified in and out of echarts. 
 | 
 */ 
 | 
export function clone<T extends any>(source: T): T { 
 | 
    if (source == null || typeof source !== 'object') { 
 | 
        return source; 
 | 
    } 
 | 
  
 | 
    let result = source as any; 
 | 
    const typeStr = <string>objToString.call(source); 
 | 
  
 | 
    if (typeStr === '[object Array]') { 
 | 
        if (!isPrimitive(source)) { 
 | 
            result = [] as any; 
 | 
            for (let i = 0, len = (source as any[]).length; i < len; i++) { 
 | 
                result[i] = clone((source as any[])[i]); 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
    else if (TYPED_ARRAY[typeStr]) { 
 | 
        if (!isPrimitive(source)) { 
 | 
            /* eslint-disable-next-line */ 
 | 
            const Ctor = source.constructor as typeof Float32Array; 
 | 
            if (Ctor.from) { 
 | 
                result = Ctor.from(source as Float32Array); 
 | 
            } 
 | 
            else { 
 | 
                result = new Ctor((source as Float32Array).length); 
 | 
                for (let i = 0, len = (source as Float32Array).length; i < len; i++) { 
 | 
                    result[i] = (source as Float32Array)[i]; 
 | 
                } 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
    else if (!BUILTIN_OBJECT[typeStr] && !isPrimitive(source) && !isDom(source)) { 
 | 
        result = {} as any; 
 | 
        for (let key in source) { 
 | 
            // Check if key is __proto__ to avoid prototype pollution 
 | 
            if (source.hasOwnProperty(key) && key !== protoKey) { 
 | 
                result[key] = clone(source[key]); 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
  
 | 
    return result; 
 | 
} 
 | 
  
 | 
export function merge< 
 | 
    T extends Dictionary<any>, 
 | 
    S extends Dictionary<any> 
 | 
>(target: T, source: S, overwrite?: boolean): T & S; 
 | 
export function merge< 
 | 
    T extends any, 
 | 
    S extends any 
 | 
>(target: T, source: S, overwrite?: boolean): T | S; 
 | 
export function merge(target: any, source: any, overwrite?: boolean): any { 
 | 
    // We should escapse that source is string 
 | 
    // and enter for ... in ... 
 | 
    if (!isObject(source) || !isObject(target)) { 
 | 
        return overwrite ? clone(source) : target; 
 | 
    } 
 | 
  
 | 
    for (let key in source) { 
 | 
        // Check if key is __proto__ to avoid prototype pollution 
 | 
        if (source.hasOwnProperty(key) && key !== protoKey) { 
 | 
            const targetProp = target[key]; 
 | 
            const sourceProp = source[key]; 
 | 
  
 | 
            if (isObject(sourceProp) 
 | 
                && isObject(targetProp) 
 | 
                && !isArray(sourceProp) 
 | 
                && !isArray(targetProp) 
 | 
                && !isDom(sourceProp) 
 | 
                && !isDom(targetProp) 
 | 
                && !isBuiltInObject(sourceProp) 
 | 
                && !isBuiltInObject(targetProp) 
 | 
                && !isPrimitive(sourceProp) 
 | 
                && !isPrimitive(targetProp) 
 | 
            ) { 
 | 
                // 如果需要递归覆盖,就递归调用merge 
 | 
                merge(targetProp, sourceProp, overwrite); 
 | 
            } 
 | 
            else if (overwrite || !(key in target)) { 
 | 
                // 否则只处理overwrite为true,或者在目标对象中没有此属性的情况 
 | 
                // NOTE,在 target[key] 不存在的时候也是直接覆盖 
 | 
                target[key] = clone(source[key]); 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
  
 | 
    return target; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * @param targetAndSources The first item is target, and the rests are source. 
 | 
 * @param overwrite 
 | 
 * @return Merged result 
 | 
 */ 
 | 
export function mergeAll(targetAndSources: any[], overwrite?: boolean): any { 
 | 
    let result = targetAndSources[0]; 
 | 
    for (let i = 1, len = targetAndSources.length; i < len; i++) { 
 | 
        result = merge(result, targetAndSources[i], overwrite); 
 | 
    } 
 | 
    return result; 
 | 
} 
 | 
  
 | 
export function extend< 
 | 
    T extends Dictionary<any>, 
 | 
    S extends Dictionary<any> 
 | 
>(target: T, source: S): T & S { 
 | 
    // @ts-ignore 
 | 
    if (Object.assign) { 
 | 
        // @ts-ignore 
 | 
        Object.assign(target, source); 
 | 
    } 
 | 
    else { 
 | 
        for (let key in source) { 
 | 
            // Check if key is __proto__ to avoid prototype pollution 
 | 
            if (source.hasOwnProperty(key) && key !== protoKey) { 
 | 
                (target as S & T)[key] = (source as T & S)[key]; 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
    return target as T & S; 
 | 
} 
 | 
  
 | 
export function defaults< 
 | 
    T extends Dictionary<any>, 
 | 
    S extends Dictionary<any> 
 | 
>(target: T, source: S, overlay?: boolean): T & S { 
 | 
    const keysArr = keys(source); 
 | 
    for (let i = 0, len = keysArr.length; i < len; i++) { 
 | 
        let key = keysArr[i]; 
 | 
        if ((overlay ? source[key] != null : (target as T & S)[key] == null)) { 
 | 
            (target as S & T)[key] = (source as T & S)[key]; 
 | 
        } 
 | 
    } 
 | 
    return target as T & S; 
 | 
} 
 | 
  
 | 
// Expose createCanvas in util for compatibility 
 | 
export const createCanvas = platformApi.createCanvas; 
 | 
  
 | 
/** 
 | 
 * 查询数组中元素的index 
 | 
 */ 
 | 
export function indexOf<T>(array: T[] | readonly T[] | ArrayLike<T>, value: T): number { 
 | 
    if (array) { 
 | 
        if ((array as T[]).indexOf) { 
 | 
            return (array as T[]).indexOf(value); 
 | 
        } 
 | 
        for (let i = 0, len = array.length; i < len; i++) { 
 | 
            if (array[i] === value) { 
 | 
                return i; 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
    return -1; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * 构造类继承关系 
 | 
 * 
 | 
 * @param clazz 源类 
 | 
 * @param baseClazz 基类 
 | 
 */ 
 | 
export function inherits(clazz: Function, baseClazz: Function) { 
 | 
    const clazzPrototype = clazz.prototype; 
 | 
    function F() {} 
 | 
    F.prototype = baseClazz.prototype; 
 | 
    clazz.prototype = new (F as any)(); 
 | 
  
 | 
    for (let prop in clazzPrototype) { 
 | 
        if (clazzPrototype.hasOwnProperty(prop)) { 
 | 
            clazz.prototype[prop] = clazzPrototype[prop]; 
 | 
        } 
 | 
    } 
 | 
    clazz.prototype.constructor = clazz; 
 | 
    (clazz as any).superClass = baseClazz; 
 | 
} 
 | 
  
 | 
export function mixin<T, S>(target: T | Function, source: S | Function, override?: boolean) { 
 | 
    target = 'prototype' in target ? target.prototype : target; 
 | 
    source = 'prototype' in source ? source.prototype : source; 
 | 
    // If build target is ES6 class. prototype methods is not enumerable. Use getOwnPropertyNames instead 
 | 
    // TODO: Determine if source is ES6 class? 
 | 
    if (Object.getOwnPropertyNames) { 
 | 
        const keyList = Object.getOwnPropertyNames(source); 
 | 
        for (let i = 0; i < keyList.length; i++) { 
 | 
            const key = keyList[i]; 
 | 
            if (key !== 'constructor') { 
 | 
                if ((override ? (source as any)[key] != null : (target as any)[key] == null)) { 
 | 
                    (target as any)[key] = (source as any)[key]; 
 | 
                } 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
    else { 
 | 
        defaults(target, source, override); 
 | 
    } 
 | 
} 
 | 
  
 | 
/** 
 | 
 * Consider typed array. 
 | 
 * @param data 
 | 
 */ 
 | 
export function isArrayLike(data: any): data is ArrayLike<any> { 
 | 
    if (!data) { 
 | 
        return false; 
 | 
    } 
 | 
    if (typeof data === 'string') { 
 | 
        return false; 
 | 
    } 
 | 
    return typeof data.length === 'number'; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * 数组或对象遍历 
 | 
 */ 
 | 
export function each<I extends Dictionary<any> | any[] | readonly any[] | ArrayLike<any>, Context>( 
 | 
    arr: I, 
 | 
    cb: ( 
 | 
        this: Context, 
 | 
        // Use unknown to avoid to infer to "any", which may disable typo check. 
 | 
        value: I extends (infer T)[] | readonly (infer T)[] | ArrayLike<infer T> ? T 
 | 
            // Use Dictionary<infer T> may cause infer fail when I is an interface. 
 | 
            // So here use a Record to infer type. 
 | 
            : I extends Dictionary<any> ? I extends Record<infer K, infer T> ? T : unknown : unknown, 
 | 
        index?: I extends any[] | readonly any[] | ArrayLike<any> ? number : keyof I & string,  // keyof Dictionary will return number | string 
 | 
        arr?: I 
 | 
    ) => void, 
 | 
    context?: Context 
 | 
) { 
 | 
    if (!(arr && cb)) { 
 | 
        return; 
 | 
    } 
 | 
    if ((arr as any).forEach && (arr as any).forEach === nativeForEach) { 
 | 
        (arr as any).forEach(cb, context); 
 | 
    } 
 | 
    else if (arr.length === +arr.length) { 
 | 
        for (let i = 0, len = arr.length; i < len; i++) { 
 | 
            // FIXME: should the elided item be travelled? like `[33,,55]`. 
 | 
            cb.call(context, (arr as any[])[i], i as any, arr); 
 | 
        } 
 | 
    } 
 | 
    else { 
 | 
        for (let key in arr) { 
 | 
            if (arr.hasOwnProperty(key)) { 
 | 
                cb.call(context, (arr as Dictionary<any>)[key], key as any, arr); 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
} 
 | 
  
 | 
/** 
 | 
 * Array mapping. 
 | 
 * @typeparam T Type in Array 
 | 
 * @typeparam R Type Returned 
 | 
 * @return Must be an array. 
 | 
 */ 
 | 
export function map<T, R, Context>( 
 | 
    arr: readonly T[], 
 | 
    cb: (this: Context, val: T, index?: number, arr?: readonly T[]) => R, 
 | 
    context?: Context 
 | 
): R[] { 
 | 
    // Take the same behavior with lodash when !arr and !cb, 
 | 
    // which might be some common sense. 
 | 
    if (!arr) { 
 | 
        return []; 
 | 
    } 
 | 
    if (!cb) { 
 | 
        return slice(arr) as unknown[] as R[]; 
 | 
    } 
 | 
    if (arr.map && arr.map === nativeMap) { 
 | 
        return arr.map(cb, context); 
 | 
    } 
 | 
    else { 
 | 
        const result = []; 
 | 
        for (let i = 0, len = arr.length; i < len; i++) { 
 | 
            // FIXME: should the elided item be travelled, like `[33,,55]`. 
 | 
            result.push(cb.call(context, arr[i], i, arr)); 
 | 
        } 
 | 
        return result; 
 | 
    } 
 | 
} 
 | 
  
 | 
export function reduce<T, S, Context>( 
 | 
    arr: readonly T[], 
 | 
    cb: (this: Context, previousValue: S, currentValue: T, currentIndex?: number, arr?: readonly T[]) => S, 
 | 
    memo?: S, 
 | 
    context?: Context 
 | 
): S { 
 | 
    if (!(arr && cb)) { 
 | 
        return; 
 | 
    } 
 | 
    for (let i = 0, len = arr.length; i < len; i++) { 
 | 
        memo = cb.call(context, memo, arr[i], i, arr); 
 | 
    } 
 | 
    return memo; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * Array filtering. 
 | 
 * @return Must be an array. 
 | 
 */ 
 | 
export function filter<T, Context>( 
 | 
    arr: readonly T[], 
 | 
    cb: (this: Context, value: T, index: number, arr: readonly T[]) => boolean, 
 | 
    context?: Context 
 | 
): T[] { 
 | 
    // Take the same behavior with lodash when !arr and !cb, 
 | 
    // which might be some common sense. 
 | 
    if (!arr) { 
 | 
        return []; 
 | 
    } 
 | 
    if (!cb) { 
 | 
        return slice(arr); 
 | 
    } 
 | 
    if (arr.filter && arr.filter === nativeFilter) { 
 | 
        return arr.filter(cb, context); 
 | 
    } 
 | 
    else { 
 | 
        const result = []; 
 | 
        for (let i = 0, len = arr.length; i < len; i++) { 
 | 
            // FIXME: should the elided items be travelled? like `[33,,55]`. 
 | 
            if (cb.call(context, arr[i], i, arr)) { 
 | 
                result.push(arr[i]); 
 | 
            } 
 | 
        } 
 | 
        return result; 
 | 
    } 
 | 
} 
 | 
  
 | 
/** 
 | 
 * 数组项查找 
 | 
 */ 
 | 
export function find<T, Context>( 
 | 
    arr: readonly T[], 
 | 
    cb: (this: Context, value: T, index?: number, arr?: readonly T[]) => boolean, 
 | 
    context?: Context 
 | 
): T { 
 | 
    if (!(arr && cb)) { 
 | 
        return; 
 | 
    } 
 | 
    for (let i = 0, len = arr.length; i < len; i++) { 
 | 
        if (cb.call(context, arr[i], i, arr)) { 
 | 
            return arr[i]; 
 | 
        } 
 | 
    } 
 | 
} 
 | 
  
 | 
/** 
 | 
 * Get all object keys 
 | 
 * 
 | 
 * Will return an empty array if obj is null/undefined 
 | 
 */ 
 | 
export function keys<T extends object>(obj: T): (KeyOfDistributive<T> & string)[] { 
 | 
    if (!obj) { 
 | 
        return []; 
 | 
    } 
 | 
    // Return type should be `keyof T` but exclude `number`, becuase 
 | 
    // `Object.keys` only return string rather than `number | string`. 
 | 
    type TKeys = KeyOfDistributive<T> & string; 
 | 
    if (Object.keys) { 
 | 
        return Object.keys(obj) as TKeys[]; 
 | 
    } 
 | 
    let keyList: TKeys[] = []; 
 | 
    for (let key in obj) { 
 | 
        if (obj.hasOwnProperty(key)) { 
 | 
            keyList.push(key as any); 
 | 
        } 
 | 
    } 
 | 
    return keyList; 
 | 
} 
 | 
  
 | 
// Remove this type in returned function. Or it will conflicts wicth callback with given context. Like Eventful. 
 | 
// According to lib.es5.d.ts 
 | 
/* eslint-disable max-len*/ 
 | 
export type Bind1<F, Ctx> = F extends (this: Ctx, ...args: infer A) => infer R ? (...args: A) => R : unknown; 
 | 
export type Bind2<F, Ctx, T1> = F extends (this: Ctx, a: T1, ...args: infer A) => infer R ? (...args: A) => R : unknown; 
 | 
export type Bind3<F, Ctx, T1, T2> = F extends (this: Ctx, a: T1, b: T2, ...args: infer A) => infer R ? (...args: A) => R : unknown; 
 | 
export type Bind4<F, Ctx, T1, T2, T3> = F extends (this: Ctx, a: T1, b: T2, c: T3, ...args: infer A) => infer R ? (...args: A) => R : unknown; 
 | 
export type Bind5<F, Ctx, T1, T2, T3, T4> = F extends (this: Ctx, a: T1, b: T2, c: T3, d: T4, ...args: infer A) => infer R ? (...args: A) => R : unknown; 
 | 
type BindFunc<Ctx> = (this: Ctx, ...arg: any[]) => any 
 | 
  
 | 
interface FunctionBind { 
 | 
    <F extends BindFunc<Ctx>, Ctx>(func: F, ctx: Ctx): Bind1<F, Ctx> 
 | 
    <F extends BindFunc<Ctx>, Ctx, T1 extends Parameters<F>[0]>(func: F, ctx: Ctx, a: T1): Bind2<F, Ctx, T1> 
 | 
    <F extends BindFunc<Ctx>, Ctx, T1 extends Parameters<F>[0], T2 extends Parameters<F>[1]>(func: F, ctx: Ctx, a: T1, b: T2): Bind3<F, Ctx, T1, T2> 
 | 
    <F extends BindFunc<Ctx>, Ctx, T1 extends Parameters<F>[0], T2 extends Parameters<F>[1], T3 extends Parameters<F>[2]>(func: F, ctx: Ctx, a: T1, b: T2, c: T3): Bind4<F, Ctx, T1, T2, T3> 
 | 
    <F extends BindFunc<Ctx>, Ctx, T1 extends Parameters<F>[0], T2 extends Parameters<F>[1], T3 extends Parameters<F>[2], T4 extends Parameters<F>[3]>(func: F, ctx: Ctx, a: T1, b: T2, c: T3, d: T4): Bind5<F, Ctx, T1, T2, T3, T4> 
 | 
} 
 | 
function bindPolyfill<Ctx, Fn extends(...args: any) => any>( 
 | 
    func: Fn, context: Ctx, ...args: any[] 
 | 
): (...args: Parameters<Fn>) => ReturnType<Fn> { 
 | 
    return function (this: Ctx) { 
 | 
        return func.apply(context, args.concat(nativeSlice.call(arguments))); 
 | 
    }; 
 | 
} 
 | 
export const bind: FunctionBind = (protoFunction && isFunction(protoFunction.bind)) 
 | 
    ? protoFunction.call.bind(protoFunction.bind) 
 | 
    : bindPolyfill; 
 | 
  
 | 
export type Curry1<F, T1> = F extends (a: T1, ...args: infer A) => infer R ? (...args: A) => R : unknown; 
 | 
export type Curry2<F, T1, T2> = F extends (a: T1, b: T2, ...args: infer A) => infer R ? (...args: A) => R : unknown; 
 | 
export type Curry3<F, T1, T2, T3> = F extends (a: T1, b: T2, c: T3, ...args: infer A) => infer R ? (...args: A) => R : unknown; 
 | 
export type Curry4<F, T1, T2, T3, T4> = F extends (a: T1, b: T2, c: T3, d: T4, ...args: infer A) => infer R ? (...args: A) => R : unknown; 
 | 
type CurryFunc = (...arg: any[]) => any 
 | 
  
 | 
function curry<F extends CurryFunc, T1 extends Parameters<F>[0]>(func: F, a: T1): Curry1<F, T1> 
 | 
function curry<F extends CurryFunc, T1 extends Parameters<F>[0], T2 extends Parameters<F>[1]>(func: F, a: T1, b: T2): Curry2<F, T1, T2> 
 | 
function curry<F extends CurryFunc, T1 extends Parameters<F>[0], T2 extends Parameters<F>[1], T3 extends Parameters<F>[2]>(func: F, a: T1, b: T2, c: T3): Curry3<F, T1, T2, T3> 
 | 
function curry<F extends CurryFunc, T1 extends Parameters<F>[0], T2 extends Parameters<F>[1], T3 extends Parameters<F>[2], T4 extends Parameters<F>[3]>(func: F, a: T1, b: T2, c: T3, d: T4): Curry4<F, T1, T2, T3, T4> 
 | 
function curry(func: Function, ...args: any[]): Function { 
 | 
    return function (this: any) { 
 | 
        return func.apply(this, args.concat(nativeSlice.call(arguments))); 
 | 
    }; 
 | 
} 
 | 
export {curry}; 
 | 
/* eslint-enable max-len*/ 
 | 
  
 | 
export function isArray(value: any): value is any[] { 
 | 
    if (Array.isArray) { 
 | 
        return Array.isArray(value); 
 | 
    } 
 | 
    return objToString.call(value) === '[object Array]'; 
 | 
} 
 | 
  
 | 
export function isFunction(value: any): value is Function { 
 | 
    return typeof value === 'function'; 
 | 
} 
 | 
  
 | 
export function isString(value: any): value is string { 
 | 
    // Faster than `objToString.call` several times in chromium and webkit. 
 | 
    // And `new String()` is rarely used. 
 | 
    return typeof value === 'string'; 
 | 
} 
 | 
  
 | 
export function isStringSafe(value: any): value is string { 
 | 
    return objToString.call(value) === '[object String]'; 
 | 
} 
 | 
  
 | 
export function isNumber(value: any): value is number { 
 | 
    // Faster than `objToString.call` several times in chromium and webkit. 
 | 
    // And `new Number()` is rarely used. 
 | 
    return typeof value === 'number'; 
 | 
} 
 | 
  
 | 
// Usage: `isObject(xxx)` or `isObject(SomeType)(xxx)` 
 | 
// Generic T can be used to avoid "ts type gruards" casting the `value` from its original 
 | 
// type `Object` implicitly so that loose its original type info in the subsequent code. 
 | 
export function isObject<T = unknown>(value: T): value is (object & T) { 
 | 
    // Avoid a V8 JIT bug in Chrome 19-20. 
 | 
    // See https://code.google.com/p/v8/issues/detail?id=2291 for more details. 
 | 
    const type = typeof value; 
 | 
    return type === 'function' || (!!value && type === 'object'); 
 | 
} 
 | 
  
 | 
export function isBuiltInObject(value: any): boolean { 
 | 
    return !!BUILTIN_OBJECT[objToString.call(value)]; 
 | 
} 
 | 
  
 | 
export function isTypedArray(value: any): boolean { 
 | 
    return !!TYPED_ARRAY[objToString.call(value)]; 
 | 
} 
 | 
  
 | 
export function isDom(value: any): value is HTMLElement { 
 | 
    return typeof value === 'object' 
 | 
        && typeof value.nodeType === 'number' 
 | 
        && typeof value.ownerDocument === 'object'; 
 | 
} 
 | 
  
 | 
export function isGradientObject(value: any): value is GradientObject { 
 | 
    return (value as GradientObject).colorStops != null; 
 | 
} 
 | 
  
 | 
export function isImagePatternObject(value: any): value is ImagePatternObject { 
 | 
    return (value as ImagePatternObject).image != null; 
 | 
} 
 | 
  
 | 
export function isRegExp(value: unknown): value is RegExp { 
 | 
    return objToString.call(value) === '[object RegExp]'; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * Whether is exactly NaN. Notice isNaN('a') returns true. 
 | 
 */ 
 | 
export function eqNaN(value: any): boolean { 
 | 
    /* eslint-disable-next-line no-self-compare */ 
 | 
    return value !== value; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * If value1 is not null, then return value1, otherwise judget rest of values. 
 | 
 * Low performance. 
 | 
 * @return Final value 
 | 
 */ 
 | 
export function retrieve<T>(...args: T[]): T { 
 | 
    for (let i = 0, len = args.length; i < len; i++) { 
 | 
        if (args[i] != null) { 
 | 
            return args[i]; 
 | 
        } 
 | 
    } 
 | 
} 
 | 
  
 | 
export function retrieve2<T, R>(value0: T, value1: R): T | R { 
 | 
    return value0 != null 
 | 
        ? value0 
 | 
        : value1; 
 | 
} 
 | 
  
 | 
export function retrieve3<T, R, W>(value0: T, value1: R, value2: W): T | R | W { 
 | 
    return value0 != null 
 | 
        ? value0 
 | 
        : value1 != null 
 | 
        ? value1 
 | 
        : value2; 
 | 
} 
 | 
  
 | 
type SliceParams = Parameters<typeof nativeSlice>; 
 | 
export function slice<T>(arr: ArrayLike<T>, ...args: SliceParams): T[] { 
 | 
    return nativeSlice.apply(arr, args as any[]); 
 | 
} 
 | 
  
 | 
/** 
 | 
 * Normalize css liked array configuration 
 | 
 * e.g. 
 | 
 *  3 => [3, 3, 3, 3] 
 | 
 *  [4, 2] => [4, 2, 4, 2] 
 | 
 *  [4, 3, 2] => [4, 3, 2, 3] 
 | 
 */ 
 | 
export function normalizeCssArray(val: number | number[]) { 
 | 
    if (typeof (val) === 'number') { 
 | 
        return [val, val, val, val]; 
 | 
    } 
 | 
    const len = val.length; 
 | 
    if (len === 2) { 
 | 
        // vertical | horizontal 
 | 
        return [val[0], val[1], val[0], val[1]]; 
 | 
    } 
 | 
    else if (len === 3) { 
 | 
        // top | horizontal | bottom 
 | 
        return [val[0], val[1], val[2], val[1]]; 
 | 
    } 
 | 
    return val; 
 | 
} 
 | 
  
 | 
export function assert(condition: any, message?: string) { 
 | 
    if (!condition) { 
 | 
        throw new Error(message); 
 | 
    } 
 | 
} 
 | 
  
 | 
/** 
 | 
 * @param str string to be trimmed 
 | 
 * @return trimmed string 
 | 
 */ 
 | 
export function trim(str: string): string { 
 | 
    if (str == null) { 
 | 
        return null; 
 | 
    } 
 | 
    else if (typeof str.trim === 'function') { 
 | 
        return str.trim(); 
 | 
    } 
 | 
    else { 
 | 
        return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); 
 | 
    } 
 | 
} 
 | 
  
 | 
const primitiveKey = '__ec_primitive__'; 
 | 
/** 
 | 
 * Set an object as primitive to be ignored traversing children in clone or merge 
 | 
 */ 
 | 
export function setAsPrimitive(obj: any) { 
 | 
    obj[primitiveKey] = true; 
 | 
} 
 | 
  
 | 
export function isPrimitive(obj: any): boolean { 
 | 
    return obj[primitiveKey]; 
 | 
} 
 | 
  
 | 
interface MapInterface<T, KEY extends string | number = string | number> { 
 | 
    delete(key: KEY): boolean; 
 | 
    has(key: KEY): boolean; 
 | 
    get(key: KEY): T | undefined; 
 | 
    set(key: KEY, value: T): this; 
 | 
    keys(): KEY[]; 
 | 
    forEach(callback: (value: T, key: KEY) => void): void; 
 | 
} 
 | 
  
 | 
class MapPolyfill<T, KEY extends string | number = string | number> implements MapInterface<T, KEY> { 
 | 
    private data: Record<KEY, T> = {} as Record<KEY, T>; 
 | 
  
 | 
    delete(key: KEY): boolean { 
 | 
        const existed = this.has(key); 
 | 
        if (existed) { 
 | 
            delete this.data[key]; 
 | 
        } 
 | 
        return existed; 
 | 
    } 
 | 
    has(key: KEY): boolean { 
 | 
        return this.data.hasOwnProperty(key); 
 | 
    } 
 | 
    get(key: KEY): T | undefined { 
 | 
        return this.data[key]; 
 | 
    } 
 | 
    set(key: KEY, value: T): this { 
 | 
        this.data[key] = value; 
 | 
        return this; 
 | 
    } 
 | 
    keys(): KEY[] { 
 | 
        return keys(this.data); 
 | 
    } 
 | 
    forEach(callback: (value: T, key: KEY) => void): void { 
 | 
        // This is a potential performance bottleneck, see details in 
 | 
        // https://github.com/ecomfe/zrender/issues/965, however it is now 
 | 
        // less likely to occur as we default to native maps when possible. 
 | 
        const data = this.data; 
 | 
        for (const key in data) { 
 | 
            if (data.hasOwnProperty(key)) { 
 | 
                callback(data[key], key); 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
} 
 | 
  
 | 
// We want to use native Map if it is available, but we do not want to polyfill the global scope 
 | 
// in case users ship their own polyfills or patch the native map object in any way. 
 | 
const isNativeMapSupported = typeof Map === 'function'; 
 | 
function maybeNativeMap<T, KEY extends string | number = string | number>(): MapInterface<T, KEY> { 
 | 
    // Map may be a native class if we are running in an ES6 compatible environment. 
 | 
    // eslint-disable-next-line 
 | 
    return (isNativeMapSupported ? new Map<KEY, T>() : new MapPolyfill<T, KEY>()) as MapInterface<T, KEY>; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * @constructor 
 | 
 * @param {Object} obj 
 | 
 */ 
 | 
export class HashMap<T, KEY extends string | number = string | number> { 
 | 
    data: MapInterface<T, KEY> 
 | 
  
 | 
    constructor(obj?: HashMap<T, KEY> | { [key in KEY]?: T } | KEY[]) { 
 | 
        const isArr = isArray(obj); 
 | 
        // Key should not be set on this, otherwise 
 | 
        // methods get/set/... may be overridden. 
 | 
        this.data = maybeNativeMap<T, KEY>(); 
 | 
        const thisMap = this; 
 | 
  
 | 
        (obj instanceof HashMap) 
 | 
            ? obj.each(visit) 
 | 
            : (obj && each(obj, visit)); 
 | 
  
 | 
        function visit(value: any, key: any) { 
 | 
            isArr ? thisMap.set(value, key) : thisMap.set(key, value); 
 | 
        } 
 | 
    } 
 | 
  
 | 
    // `hasKey` instead of `has` for potential misleading. 
 | 
    hasKey(key: KEY): boolean { 
 | 
        return this.data.has(key); 
 | 
    } 
 | 
    get(key: KEY): T { 
 | 
        return this.data.get(key); 
 | 
    } 
 | 
    set(key: KEY, value: T): T { 
 | 
        // Comparing with invocation chaining, `return value` is more commonly 
 | 
        // used in this case: `const someVal = map.set('a', genVal());` 
 | 
        this.data.set(key, value); 
 | 
        return value; 
 | 
    } 
 | 
    // Although util.each can be performed on this hashMap directly, user 
 | 
    // should not use the exposed keys, who are prefixed. 
 | 
    each<Context>( 
 | 
        cb: (this: Context, value?: T, key?: KEY) => void, 
 | 
        context?: Context 
 | 
    ) { 
 | 
        this.data.forEach((value, key) => { 
 | 
            cb.call(context, value, key); 
 | 
        }); 
 | 
    } 
 | 
    keys(): KEY[] { 
 | 
        const keys = this.data.keys(); 
 | 
        return isNativeMapSupported 
 | 
            // Native map returns an iterator so we need to convert it to an array 
 | 
            ? Array.from(keys) 
 | 
            : keys; 
 | 
    } 
 | 
    // Do not use this method if performance sensitive. 
 | 
    removeKey(key: KEY): void { 
 | 
        this.data.delete(key); 
 | 
    } 
 | 
} 
 | 
  
 | 
export function createHashMap<T, KEY extends string | number = string | number>( 
 | 
    obj?: HashMap<T, KEY> | { [key in KEY]?: T } | KEY[] 
 | 
) { 
 | 
    return new HashMap<T, KEY>(obj); 
 | 
} 
 | 
  
 | 
export function concatArray<T, R>(a: ArrayLike<T>, b: ArrayLike<R>): ArrayLike<T | R> { 
 | 
    const newArray = new (a as any).constructor(a.length + b.length); 
 | 
    for (let i = 0; i < a.length; i++) { 
 | 
        newArray[i] = a[i]; 
 | 
    } 
 | 
    const offset = a.length; 
 | 
    for (let i = 0; i < b.length; i++) { 
 | 
        newArray[i + offset] = b[i]; 
 | 
    } 
 | 
    return newArray; 
 | 
} 
 | 
  
 | 
export function createObject<T>(proto?: object, properties?: T): T { 
 | 
    // Performance of Object.create 
 | 
    // https://jsperf.com/style-strategy-proto-or-others 
 | 
    let obj: T; 
 | 
    if (Object.create) { 
 | 
        obj = Object.create(proto); 
 | 
    } 
 | 
    else { 
 | 
        const StyleCtor = function () {}; 
 | 
        StyleCtor.prototype = proto; 
 | 
        obj = new (StyleCtor as any)(); 
 | 
    } 
 | 
    if (properties) { 
 | 
        extend(obj, properties); 
 | 
    } 
 | 
  
 | 
    return obj; 
 | 
} 
 | 
  
 | 
  
 | 
export function disableUserSelect(dom: HTMLElement) { 
 | 
    const domStyle = dom.style; 
 | 
    domStyle.webkitUserSelect = 'none'; 
 | 
    domStyle.userSelect = 'none'; 
 | 
    // @ts-ignore 
 | 
    domStyle.webkitTapHighlightColor = 'rgba(0,0,0,0)'; 
 | 
    (domStyle as any)['-webkit-touch-callout'] = 'none'; 
 | 
} 
 | 
  
 | 
export function hasOwn(own: object, prop: string): boolean { 
 | 
    return own.hasOwnProperty(prop); 
 | 
} 
 | 
  
 | 
export function noop() {} 
 | 
  
 | 
export const RADIAN_TO_DEGREE = 180 / Math.PI; 
 |