123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822 |
- /* 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; i < keysArr.length; 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;
|