import { Obj, Type } from '@ephox/katamari'; /** * Array utility class. * * @private * @class tinymce.util.Arr */ export type ArrayCallback = (this: any, x: T, i: number, xs: ArrayLike) => R; export type ObjCallback = (this: any, value: T, key: string, obj: Record) => R; const isArrayLike = (o: Record | ArrayLike): o is ArrayLike => o.length !== undefined; const isArray = Array.isArray; const toArray = (obj: ArrayLike): T[] => { if (!isArray(obj)) { const array: T[] = []; for (let i = 0, l = obj.length; i < l; i++) { array[i] = obj[i]; } return array; } else { return obj; } }; const each: { (arr: ArrayLike | null | undefined, cb: ArrayCallback, scope?: any): boolean; (obj: Record | null | undefined, cb: ObjCallback, scope?: any): boolean; } = (o: ArrayLike | Record | null | undefined, cb: ArrayCallback | ObjCallback, s?: any): boolean => { if (!o) { return false; } s = s || o; if (isArrayLike(o)) { // Indexed arrays, needed for Safari for (let n = 0, l = o.length; n < l; n++) { if ((cb as ArrayCallback).call(s, o[n], n, o) === false) { return false; } } } else { // Hashtables for (const n in o) { if (Obj.has(o, n)) { if ((cb as ObjCallback).call(s, o[n], n, o) === false) { return false; } } } } return true; }; const map: { (arr: ArrayLike | null | undefined, cb: ArrayCallback): R[]; (obj: Record | null | undefined, cb: ObjCallback): R[]; } = (array: any, callback: any): R[] => { const out: R[] = []; each(array, (item, index) => { out.push(callback(item, index, array)); }); return out; }; const filter: { (arr: ArrayLike | null | undefined, f?: ArrayCallback): T[]; (obj: Record | null | undefined, f?: ObjCallback): T[]; } = (a: any, f?: any): T[] => { const o: T[] = []; each(a, (v, index) => { if (!f || f(v, index, a)) { o.push(v); } }); return o; }; const indexOf = (a: ArrayLike, v: T): number => { if (a) { for (let i = 0, l = a.length; i < l; i++) { if (a[i] === v) { return i; } } } return -1; }; const reduce: { (collection: ArrayLike, iteratee: (acc: R, item: T, index: number) => R, accumulator: R, thisArg?: any): R; (collection: ArrayLike, iteratee: (acc: T, item: T, index: number) => T, accumulator?: undefined, thisArg?: any): T; } = (collection: ArrayLike, iteratee: (acc: R, item: R, index: number) => R, accumulator?: R | undefined, thisArg?: any): R => { let acc: R = Type.isUndefined(accumulator) ? collection[0] : accumulator; for (let i = 0; i < collection.length; i++) { acc = iteratee.call(thisArg, acc, collection[i], i); } return acc; }; const findIndex = (array: ArrayLike, predicate: ArrayCallback, thisArg?: any): number => { for (let i = 0, l = array.length; i < l; i++) { if (predicate.call(thisArg, array[i], i, array)) { return i; } } return -1; }; const find = (array: ArrayLike, predicate: ArrayCallback, thisArg?: any): T | undefined => { const idx = findIndex(array, predicate, thisArg); if (idx !== -1) { return array[idx]; } return undefined; }; const last = (collection: ArrayLike): T | undefined => collection[collection.length - 1]; export { isArrayLike, isArray, toArray, each, map, filter, indexOf, reduce, findIndex, find, last };