import 'array-flat-polyfill';import {default as clone_} from 'clone';import deepEqual_ from 'fast-deep-equal';import stableStringify from 'fast-json-stable-stringify';import {hasOwnProperty, isNumber, isString, splitAccessPath, stringValue, writeConfig} from 'vega-util';import {isLogicalAnd, isLogicalNot, isLogicalOr, LogicalComposition} from './logical';
export const deepEqual = deepEqual_;export const duplicate = clone_;
export function pick<T extends object, K extends keyof T>(obj: T, props: readonly K[]): Pick<T, K> { const copy: any = {}; for (const prop of props) { if (hasOwnProperty(obj, prop)) { copy[prop] = obj[prop]; } } return copy;}
export function omit<T extends object, K extends keyof T>(obj: T, props: readonly K[]): Omit<T, K> { const copy = {...(obj as any)}; for (const prop of props) { delete copy[prop]; } return copy;}
Set.prototype['toJSON'] = function () { return `Set(${[...this].map(x => stableStringify(x)).join(',')})`;};
export const stringify = stableStringify;
export function hash(a: any): string | number { if (isNumber(a)) { return a; }
const str = isString(a) ? a : stableStringify(a);
if (str.length < 250) { return str; }
let h = 0; for (let i = 0; i < str.length; i++) { const char = str.charCodeAt(i); h = (h << 5) - h + char; h = h & h; } return h;}
export function isNullOrFalse(x: any): x is false | null { return x === false || x === null;}
export function contains<T>(array: readonly T[], item: T) { return array.indexOf(item) > -1;}
export function some<T>(arr: readonly T[], f: (d: T, k?: any, i?: any) => boolean) { let i = 0; for (const [k, a] of arr.entries()) { if (f(a, k, i++)) { return true; } } return false;}
export function every<T>(arr: readonly T[], f: (d: T, k?: any, i?: any) => boolean) { let i = 0; for (const [k, a] of arr.entries()) { if (!f(a, k, i++)) { return false; } } return true;}
export type DeepPartial<T> = {[P in keyof T]?: DeepPartial<T[P]>};
export function mergeDeep<T>(dest: T, ...src: readonly DeepPartial<T>[]): T { for (const s of src) { deepMerge_(dest, s ?? {}); } return dest;}
function deepMerge_(dest: any, src: any) { for (const property of keys(src)) { writeConfig(dest, property, src[property], true); }}
export function unique<T>(values: readonly T[], f: (item: T) => string | number): T[] { const results: T[] = []; const u = {}; let v: string | number; for (const val of values) { v = f(val); if (v in u) { continue; } u[v] = 1; results.push(val); } return results;}
export type Dict<T> = Record<string, T>;
export function isEqual<T>(dict: Dict<T>, other: Dict<T>) { const dictKeys = keys(dict); const otherKeys = keys(other); if (dictKeys.length !== otherKeys.length) { return false; } for (const key of dictKeys) { if (dict[key] !== other[key]) { return false; } } return true;}
export function setEqual<T>(a: Set<T>, b: Set<T>) { if (a.size !== b.size) { return false; } for (const e of a) { if (!b.has(e)) { return false; } } return true;}
export function hasIntersection<T>(a: ReadonlySet<T>, b: ReadonlySet<T>) { for (const key of a) { if (b.has(key)) { return true; } } return false;}
export function prefixGenerator(a: ReadonlySet<string>): ReadonlySet<string> { const prefixes = new Set<string>(); for (const x of a) { const splitField = splitAccessPath(x); const wrappedWithAccessors = splitField.map((y, i) => (i === 0 ? y : `[${y}]`)); const computedPrefixes = wrappedWithAccessors.map((_, i) => wrappedWithAccessors.slice(0, i + 1).join('')); for (const y of computedPrefixes) { prefixes.add(y); } } return prefixes;}
export function fieldIntersection(a: ReadonlySet<string>, b: ReadonlySet<string>): boolean { if (a === undefined || b === undefined) { return true; } return hasIntersection(prefixGenerator(a), prefixGenerator(b));}
export function isEmpty(obj: object) { return keys(obj).length === 0;}
export const keys = Object.keys as <T>(o: T) => Extract<keyof T, string>[];
export const vals = Object.values;
export const entries = Object.entries;
export type Flag<S extends string> = {[K in S]: 1};
export function isBoolean(b: any): b is boolean { return b === true || b === false;}
export function varName(s: string): string { const alphanumericS = s.replace(/\W/g, '_');
return (s.match(/^\d+/) ? '_' : '') + alphanumericS;}
export function logicalExpr<T>(op: LogicalComposition<T>, cb: (...args: readonly any[]) => string): string { if (isLogicalNot(op)) { return '!(' + logicalExpr(op.not, cb) + ')'; } else if (isLogicalAnd(op)) { return '(' + op.and.map((and: LogicalComposition<T>) => logicalExpr(and, cb)).join(') && (') + ')'; } else if (isLogicalOr(op)) { return '(' + op.or.map((or: LogicalComposition<T>) => logicalExpr(or, cb)).join(') || (') + ')'; } else { return cb(op); }}
export function deleteNestedProperty(obj: any, orderedProps: string[]) { if (orderedProps.length === 0) { return true; } const prop = orderedProps.shift()!; if (prop in obj && deleteNestedProperty(obj[prop], orderedProps)) { delete obj[prop]; } return isEmpty(obj);}
export function titleCase(s: string) { return s.charAt(0).toUpperCase() + s.substr(1);}
export function accessPathWithDatum(path: string, datum = 'datum') { const pieces = splitAccessPath(path); const prefixes = []; for (let i = 1; i <= pieces.length; i++) { const prefix = `[${pieces.slice(0, i).map(stringValue).join('][')}]`; prefixes.push(`${datum}${prefix}`); } return prefixes.join(' && ');}
export function flatAccessWithDatum(path: string, datum: 'datum' | 'parent' | 'datum.datum' = 'datum') { return `${datum}[${stringValue(splitAccessPath(path).join('.'))}]`;}
function escapePathAccess(string: string) { return string.replace(/(\[|\]|\.|'|")/g, '\\$1');}
export function replacePathInField(path: string) { return `${splitAccessPath(path).map(escapePathAccess).join('\\.')}`;}
export function replaceAll(string: string, find: string, replacement: string) { return string.replace(new RegExp(find.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'), 'g'), replacement);}
export function removePathFromField(path: string) { return `${splitAccessPath(path).join('.')}`;}
export function accessPathDepth(path: string) { if (!path) { return 0; } return splitAccessPath(path).length;}
export function getFirstDefined<T>(...args: readonly T[]): T | undefined { for (const arg of args) { if (arg !== undefined) { return arg; } } return undefined;}
let idCounter = 42;
export function uniqueId(prefix?: string) { const id = ++idCounter; return prefix ? String(prefix) + id : id;}
export function resetIdCounter() { idCounter = 42;}
export function internalField(name: string) { return isInternalField(name) ? name : `__${name}`;}
export function isInternalField(name: string) { return name.indexOf('__') === 0;}
export function normalizeAngle(angle: number) { if (angle === undefined) { return undefined; } return ((angle % 360) + 360) % 360;}
export function isNumeric(value: number | string): boolean { if (isNumber(value)) { return true; } return !isNaN(value as any) && !isNaN(parseFloat(value));}