safe.ts
565 lines
| 17.6 KiB
| video/mp2t
|
TypeScriptLexer
|
|
r154 | import { ICancellable, Constructor, IDestroyable, ICancellation, IRemovable } from "./interfaces"; | ||
|
|
r71 | |||
|
|
r49 | let _nextOid = 0; | ||
| const _oid = typeof Symbol === "function" ? | ||||
| Symbol("__implab__oid__") : | ||||
| "__implab__oid__"; | ||||
|
|
r159 | function _noop() { } | ||
|
|
r135 | export function oid(instance: null | undefined): undefined; | ||
| export function oid(instance: NonNullable<any>): string; | ||||
|
|
r114 | export function oid(instance: any): string | undefined { | ||
|
|
r49 | if (isNull(instance)) | ||
|
|
r114 | return undefined; | ||
|
|
r49 | |||
| if (_oid in instance) | ||||
| return instance[_oid]; | ||||
| else | ||||
| return (instance[_oid] = "oid_" + (++_nextOid)); | ||||
| } | ||||
|
|
r150 | const cancellationNone: ICancellation = { | ||
| isSupported(): boolean { | ||||
| return false; | ||||
| }, | ||||
| throwIfRequested(): void { | ||||
| }, | ||||
| isRequested(): boolean { | ||||
| return false; | ||||
| }, | ||||
| register(_cb: (e: any) => void): IDestroyable { | ||||
| return destroyed; | ||||
| } | ||||
| }; | ||||
|
|
r114 | export function keys<T>(arg: T): (Extract<keyof T, string>)[] { | ||
| return isObject(arg) && arg ? Object.keys(arg) as (Extract<keyof T, string>)[] : []; | ||||
| } | ||||
|
|
r115 | export function isKeyof<T>(k: string, target: T): k is Extract<keyof T, string> { | ||
| return target && typeof target === "object" && k in target; | ||||
| } | ||||
|
|
r75 | export function argumentNotNull(arg: any, name: string) { | ||
|
|
r49 | if (arg === null || arg === undefined) | ||
| throw new Error("The argument " + name + " can't be null or undefined"); | ||||
| } | ||||
|
|
r75 | export function argumentNotEmptyString(arg: any, name: string) { | ||
|
|
r49 | if (typeof (arg) !== "string" || !arg.length) | ||
| throw new Error("The argument '" + name + "' must be a not empty string"); | ||||
| } | ||||
|
|
r75 | export function argumentNotEmptyArray(arg: any, name: string) { | ||
|
|
r49 | if (!(arg instanceof Array) || !arg.length) | ||
| throw new Error("The argument '" + name + "' must be a not empty array"); | ||||
| } | ||||
|
|
r75 | export function argumentOfType(arg: any, type: Constructor<{}>, name: string) { | ||
|
|
r49 | if (!(arg instanceof type)) | ||
| throw new Error("The argument '" + name + "' type doesn't match"); | ||||
| } | ||||
|
|
r115 | export function isNull(val: any): val is null | undefined { | ||
|
|
r75 | return (val === null || val === undefined); | ||
|
|
r49 | } | ||
|
|
r115 | export type primitive = symbol | string | number | boolean | undefined | null; | ||
| export function isPrimitive(val: any): val is primitive { | ||||
|
|
r75 | return (val === null || val === undefined || typeof (val) === "string" || | ||
| typeof (val) === "number" || typeof (val) === "boolean"); | ||||
|
|
r49 | } | ||
|
|
r154 | export function isObject<T>(value: T): value is Exclude<T & object, primitive> { | ||
| return !!(value && typeof value === "object"); | ||||
| } | ||||
|
|
r75 | export function isInteger(val: any): val is number { | ||
| return parseInt(val, 10) === val; | ||||
|
|
r49 | } | ||
|
|
r75 | export function isNumber(val: any): val is number { | ||
| return parseFloat(val) === val; | ||||
|
|
r49 | } | ||
|
|
r75 | export function isString(val: any): val is string { | ||
|
|
r154 | return typeof (val) === "string"; | ||
|
|
r49 | } | ||
|
|
r115 | export function isPromise<T = any>(val: any): val is PromiseLike<T> { | ||
|
|
r154 | return !!(val && typeof val.then === "function"); | ||
|
|
r49 | } | ||
|
|
r75 | export function isCancellable(val: any): val is ICancellable { | ||
|
|
r154 | return !!(val && typeof val.cancel === "function"); | ||
|
|
r71 | } | ||
|
|
r115 | export function isNullOrEmptyString(val: any): val is ("" | null | undefined) { | ||
|
|
r114 | return (val === null || val === undefined || | ||
|
|
r115 | ((typeof (val) === "string" || val instanceof String) && val.length === 0)); | ||
|
|
r49 | } | ||
|
|
r115 | export function isNotEmptyArray<T = any>(arg: any): arg is T[] { | ||
|
|
r49 | return (arg instanceof Array && arg.length > 0); | ||
| } | ||||
|
|
r114 | function _isStrictMode(this: any) { | ||
|
|
r65 | return !this; | ||
| } | ||||
|
|
r114 | function _getNonStrictGlobal(this: any) { | ||
|
|
r65 | return this; | ||
| } | ||||
|
|
r49 | export function getGlobal() { | ||
|
|
r65 | // in es3 we can't use indirect call to eval, since it will | ||
| // be executed in the current call context. | ||||
| if (!_isStrictMode()) { | ||||
| return _getNonStrictGlobal(); | ||||
| } else { | ||||
| // tslint:disable-next-line:no-eval | ||||
| return eval.call(null, "this"); | ||||
| } | ||||
|
|
r49 | } | ||
| export function get(member: string, context?: object) { | ||||
| argumentNotEmptyString(member, "member"); | ||||
| let that = context || getGlobal(); | ||||
| const parts = member.split("."); | ||||
| for (const m of parts) { | ||||
| if (!m) | ||||
| continue; | ||||
| if (isNull(that = that[m])) | ||||
| break; | ||||
| } | ||||
| return that; | ||||
| } | ||||
| /** | ||||
| * Выполняет метод для каждого элемента массива, останавливается, когда | ||||
| * либо достигнут конец массива, либо функция <c>cb</c> вернула | ||||
| * значение. | ||||
| * | ||||
| * @param {Array | Object} obj массив элементов для просмотра | ||||
| * @param {Function} cb функция, вызываемая для каждого элемента | ||||
| * @param {Object} thisArg значение, которое будет передано в качестве | ||||
| * <c>this</c> в <c>cb</c>. | ||||
|
|
r134 | * @returns {void} | ||
|
|
r49 | */ | ||
|
|
r152 | export function each<T>(obj: T, cb: <X extends Extract<keyof T, string>>(v: NonNullable<T[X]>, k: X) => void): void; | ||
|
|
r115 | export function each<T>(array: T[], cb: (v: T, i: number) => void): void; | ||
|
|
r114 | export function each(obj: any, cb: any, thisArg?: any): any; | ||
| export function each(obj: any, cb: any, thisArg?: any) { | ||||
|
|
r49 | argumentNotNull(cb, "cb"); | ||
| if (obj instanceof Array) { | ||||
|
|
r134 | let v: any; | ||
|
|
r49 | for (let i = 0; i < obj.length; i++) { | ||
|
|
r134 | v = obj[i]; | ||
| if (v !== undefined) | ||||
| cb.call(thisArg, v, i); | ||||
|
|
r49 | } | ||
| } else { | ||||
|
|
r134 | Object.keys(obj).forEach(k => obj[k] !== undefined && cb.call(thisArg, obj[k], k)); | ||
|
|
r49 | } | ||
| } | ||||
| /** Copies property values from a source object to the destination and returns | ||||
|
|
r142 | * the destination object. | ||
|
|
r49 | * | ||
| * @param dest The destination object into which properties from the source | ||||
| * object will be copied. | ||||
| * @param source The source of values which will be copied to the destination | ||||
| * object. | ||||
| * @param template An optional parameter specifies which properties should be | ||||
| * copied from the source and how to map them to the destination. If the | ||||
| * template is an array it contains the list of property names to copy from the | ||||
| * source to the destination. In case of object the templates contains the map | ||||
| * where keys are property names in the source and the values are property | ||||
| * names in the destination object. If the template isn't specified then the | ||||
| * own properties of the source are entirely copied to the destination. | ||||
| * | ||||
| */ | ||||
|
|
r140 | export function mixin<T extends object, S extends object>(dest: T, source: S, template?: (keyof S)[]): T & S; | ||
|
|
r115 | export function mixin<T extends object, S extends object, R extends object = T>(dest: T, source: S, template: { [p in keyof S]?: keyof R; }): T & R; | ||
| export function mixin<T extends object, S extends object>(dest: T, source: S, template?: any): any { | ||||
| argumentNotNull(dest, "dest"); | ||||
| const _res: any = dest as any; | ||||
|
|
r49 | |||
|
|
r65 | if (isPrimitive(source)) | ||
| return _res; | ||||
|
|
r49 | if (template instanceof Array) { | ||
|
|
r115 | template.forEach(p => { | ||
| if (isKeyof(p, source)) | ||||
|
|
r49 | _res[p] = source[p]; | ||
|
|
r115 | }); | ||
|
|
r49 | } else if (template) { | ||
|
|
r115 | keys(source).forEach(p => { | ||
| if (isKeyof(p, template)) | ||||
|
|
r49 | _res[template[p]] = source[p]; | ||
|
|
r115 | }); | ||
|
|
r49 | } else { | ||
|
|
r115 | keys(source).forEach(p => _res[p] = source[p]); | ||
|
|
r49 | } | ||
| return _res; | ||||
| } | ||||
| /** Wraps the specified function to emulate an asynchronous execution. | ||||
| * @param{Object} thisArg [Optional] Object which will be passed as 'this' to the function. | ||||
| * @param{Function|String} fn [Required] Function wich will be wrapped. | ||||
| */ | ||||
|
|
r115 | export function async<T, F extends (...args: any[]) => T | PromiseLike<T>>( | ||
| fn: F, | ||||
| thisArg?: ThisParameterType<F> | ||||
| ): (...args: Parameters<F>) => PromiseLike<T>; | ||||
| export function async<T, M extends string, O extends { [m in M]?: (...args: any[]) => T | PromiseLike<T> }>( | ||||
| fn: M, | ||||
| thisArg: O | ||||
| ): (...args: Parameters<NonNullable<O[M]>>) => PromiseLike<T>; | ||||
| export function async(_fn: any, thisArg: any): (...args: any[]) => PromiseLike<any> { | ||||
|
|
r49 | let fn = _fn; | ||
| if (arguments.length === 2 && !(fn instanceof Function)) | ||||
| fn = thisArg[fn]; | ||||
| if (fn == null) | ||||
| throw new Error("The function must be specified"); | ||||
|
|
r115 | function wrapresult(x: any, e?: any): PromiseLike<any> { | ||
|
|
r49 | if (e) { | ||
| return { | ||||
| then(cb, eb) { | ||||
| try { | ||||
| return eb ? wrapresult(eb(e)) : this; | ||||
| } catch (e2) { | ||||
| return wrapresult(null, e2); | ||||
| } | ||||
| } | ||||
| }; | ||||
| } else { | ||||
| if (x && x.then) | ||||
| return x; | ||||
| return { | ||||
| then(cb) { | ||||
| try { | ||||
| return cb ? wrapresult(cb(x)) : this; | ||||
| } catch (e2) { | ||||
| return wrapresult(e2); | ||||
| } | ||||
| } | ||||
| }; | ||||
| } | ||||
| } | ||||
| return (...args) => { | ||||
| try { | ||||
| return wrapresult(fn.apply(thisArg, args)); | ||||
| } catch (e) { | ||||
| return wrapresult(null, e); | ||||
| } | ||||
| }; | ||||
| } | ||||
|
|
r115 | export function delegate<T extends object, F extends (this: T, ...args: any[]) => any>( | ||
| target: T, | ||||
| method: F | ||||
| ): OmitThisParameter<F>; | ||||
| export function delegate<M extends string, T extends { [m in M]?: (...args: any[]) => any; }>( | ||||
| target: T, | ||||
| method: M | ||||
| ): OmitThisParameter<T[M]>; | ||||
| export function delegate(target: any, _method: any): (...args: any[]) => any { | ||||
| let method: any; | ||||
|
|
r49 | if (!(_method instanceof Function)) { | ||
| argumentNotNull(target, "target"); | ||||
| method = target[_method]; | ||||
| if (!(method instanceof Function)) | ||||
| throw new Error("'method' argument must be a Function or a method name"); | ||||
| } else { | ||||
| method = _method; | ||||
| } | ||||
| return (...args) => { | ||||
| return method.apply(target, args); | ||||
| }; | ||||
| } | ||||
|
|
r159 | /** Returns promise which will be resolved after the specified amount of time. | ||
| * | ||||
| * @param timeMs The delay before the promise will be resolved in milliseconds. | ||||
| * @param ct Optional. A cancellation token for the operation. | ||||
| */ | ||||
|
|
r150 | export function delay(timeMs: number, ct = cancellationNone) { | ||
|
|
r77 | ct.throwIfRequested(); | ||
|
|
r159 | return new Promise<void>((resolve, reject) => { | ||
|
|
r77 | const h = ct.register(e => { | ||
| clearTimeout(id); | ||||
| reject(e); | ||||
| // we don't nedd to unregister h, since ct is already disposed | ||||
| }); | ||||
| const id = setTimeout(() => { | ||||
| h.destroy(); | ||||
| resolve(); | ||||
| }, timeMs); | ||||
|
|
r76 | }); | ||
| } | ||||
|
|
r159 | export function debounce<T extends any[], R, This>(func: (this: This, ...args: T) => R | PromiseLike<R>, wait: number) { | ||
| let cancel: (e?: any) => void = _noop; | ||||
| const fn = function executedFunction(this: This, ...args: T) { | ||||
| return new Promise<R>((resolve, reject) => { | ||||
| // used to cleanup currently allocated resources | ||||
| const _cleanup = () => { | ||||
| cancel = _noop; | ||||
| clearTimeout(handle); | ||||
| }; | ||||
| // used in case of cancellation of the current operation | ||||
| const _cancel = (e: any) => { | ||||
| _cleanup(); | ||||
| reject(e); | ||||
| }; | ||||
| // performs actual work | ||||
| const _later = () => { | ||||
| _cleanup(); | ||||
| resolve(func.apply(this, args)); | ||||
| }; | ||||
| // cancel previously queued operation | ||||
| if (cancel !== _noop) | ||||
| cancel(new Error("Operation cancelled due to debouncing")); | ||||
| cancel = _cancel; | ||||
| const handle = setTimeout(_later, wait); | ||||
| }); | ||||
| }; | ||||
| fn.cancel = (e?: any) => cancel(e); | ||||
| return fn; | ||||
| } | ||||
|
|
r115 | /** Returns resolved promise, awaiting this method will cause the asynchronous | ||
| * completion of the rest of the code. | ||||
| */ | ||||
| export function fork() { | ||||
| return Promise.resolve(); | ||||
| } | ||||
| /** Always throws Error, can be used as a stub for the methods which should be | ||||
| * assigned later and are required to be not null. | ||||
| */ | ||||
| export function notImplemented(): never { | ||||
|
|
r154 | throw new Error("Not implemented"); | ||
|
|
r115 | } | ||
|
|
r49 | /** | ||
|
|
r80 | * Iterates over the specified array of items and calls the callback `cb`, if | ||
| * the result of the callback is a promise the next item from the array will be | ||||
| * proceeded after the promise is resolved. | ||||
|
|
r49 | * | ||
| */ | ||||
|
|
r80 | export function pmap<T, T2>( | ||
| items: ArrayLike<T> | PromiseLike<ArrayLike<T>>, | ||||
| cb: (item: T, i: number) => T2 | PromiseLike<T2> | ||||
| ): T2[] | PromiseLike<T2[]> { | ||||
|
|
r49 | argumentNotNull(cb, "cb"); | ||
|
|
r80 | if (isPromise(items)) { | ||
|
|
r49 | return items.then(data => pmap(data, cb)); | ||
|
|
r80 | } else { | ||
|
|
r49 | |||
|
|
r80 | if (isNull(items) || !items.length) | ||
| return []; | ||||
|
|
r49 | |||
|
|
r80 | let i = 0; | ||
| const result = new Array<T2>(); | ||||
|
|
r49 | |||
|
|
r114 | const next = (): any => { | ||
|
|
r80 | while (i < items.length) { | ||
| const r = cb(items[i], i); | ||||
| const ri = i; | ||||
| i++; | ||||
| if (isPromise(r)) { | ||||
| return r.then(x => { | ||||
| result[ri] = x; | ||||
| return next(); | ||||
| }); | ||||
| } else { | ||||
| result[ri] = r; | ||||
| } | ||||
| } | ||||
| return result; | ||||
| }; | ||||
|
|
r49 | |||
|
|
r80 | return next(); | ||
|
|
r49 | } | ||
| } | ||||
|
|
r80 | export function pfor<T>( | ||
| items: ArrayLike<T> | PromiseLike<ArrayLike<T>>, | ||||
| cb: (item: T, i: number) => any | ||||
| ): void | PromiseLike<void> { | ||||
|
|
r65 | argumentNotNull(cb, "cb"); | ||
|
|
r80 | if (isPromise(items)) { | ||
| return items.then(data => pfor(data, cb)); | ||||
| } else { | ||||
| if (isNull(items) || !items.length) | ||||
| return; | ||||
|
|
r65 | |||
|
|
r80 | let i = 0; | ||
|
|
r65 | |||
|
|
r114 | const next = (): any => { | ||
|
|
r80 | while (i < items.length) { | ||
| const r = cb(items[i], i); | ||||
| i++; | ||||
| if (isPromise(r)) | ||||
| return r.then(next); | ||||
| } | ||||
| }; | ||||
| return next(); | ||||
|
|
r65 | } | ||
| } | ||||
|
|
r75 | export function first<T>(sequence: ArrayLike<T>): T; | ||
| export function first<T>(sequence: PromiseLike<ArrayLike<T>>): PromiseLike<T>; | ||||
| export function first<T>( | ||||
| sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>, | ||||
|
|
r115 | cb?: (x: T) => void, | ||
|
|
r75 | err?: (x: Error) => void | ||
| ): void; | ||||
|
|
r49 | /** | ||
| * Выбирает первый элемент из последовательности, или обещания, если в | ||||
| * качестве параметра используется обещание, оно должно вернуть массив. | ||||
| * | ||||
| * @param {Function} cb обработчик результата, ему будет передан первый | ||||
| * элемент последовательности в случае успеха | ||||
| * @param {Function} err обработчик исключения, если массив пустой, либо | ||||
| * не массив | ||||
| * | ||||
| * @remarks Если не указаны ни cb ни err, тогда функция вернет либо | ||||
| * обещание, либо первый элемент. | ||||
| * @async | ||||
| */ | ||||
|
|
r75 | export function first<T>( | ||
| sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>, | ||||
| cb?: (x: T) => void, | ||||
| err?: (x: Error) => void | ||||
| ) { | ||||
| if (isPromise(sequence)) { | ||||
|
|
r115 | return sequence.then(res => first(res, cb as any /* force to pass undefined cb */, err)); | ||
|
|
r75 | } else if (sequence && "length" in sequence) { | ||
| if (sequence.length === 0) { | ||||
| if (err) | ||||
| return err(new Error("The sequence is empty")); | ||||
| else | ||||
| throw new Error("The sequence is empty"); | ||||
| } else if (cb) { | ||||
|
|
r80 | return cb(sequence[0]); | ||
|
|
r75 | } else { | ||
| return sequence[0]; | ||||
|
|
r49 | } | ||
|
|
r75 | } else { | ||
| if (err) | ||||
|
|
r80 | return err(new Error("The sequence is required")); | ||
|
|
r75 | else | ||
| throw new Error("The sequence is required"); | ||||
|
|
r49 | } | ||
| } | ||||
|
|
r75 | export function firstWhere<T>( | ||
| sequence: ArrayLike<T>, | ||||
| predicate: (x: T) => boolean | ||||
| ): T; | ||||
| export function firstWhere<T>( | ||||
| sequence: PromiseLike<ArrayLike<T>>, | ||||
| predicate: (x: T) => boolean | ||||
| ): PromiseLike<T>; | ||||
| export function firstWhere<T>( | ||||
| sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>, | ||||
| predicate: (x: T) => boolean, | ||||
| cb: (x: T) => void, | ||||
| err?: (x: Error) => void | ||||
| ): void; | ||||
| export function firstWhere<T>( | ||||
| sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>, | ||||
| predicate?: (x: T) => boolean, | ||||
|
|
r76 | cb?: (x: T) => any, | ||
| err?: (x: Error) => any | ||||
|
|
r75 | ) { | ||
| if (isPromise(sequence)) { | ||||
|
|
r115 | return sequence.then(res => firstWhere( | ||
| res, | ||||
| predicate as any /* force to pass undefined predicate */, | ||||
| cb as any /* force to pass undefined cb */, | ||||
| err) | ||||
| ); | ||||
|
|
r75 | } else if (sequence && "length" in sequence) { | ||
| if (sequence.length === 0) { | ||||
| if (err) | ||||
| err(new Error("The sequence is empty")); | ||||
| else | ||||
| throw new Error("The sequence is empty"); | ||||
| } else { | ||||
| if (!predicate) { | ||||
|
|
r76 | return cb ? cb(sequence[0]) && void (0) : sequence[0]; | ||
|
|
r75 | } else { | ||
| for (let i = 0; i < sequence.length; i++) { | ||||
| const v = sequence[i]; | ||||
| if (predicate(v)) | ||||
| return cb ? cb(v) : v; | ||||
| } | ||||
| if (err) | ||||
| err(new Error("The sequence doesn't contain matching items")); | ||||
| else | ||||
| throw new Error("The sequence doesn't contain matching items"); | ||||
| } | ||||
| } | ||||
| } else { | ||||
| if (err) | ||||
| err(new Error("The sequence is required")); | ||||
| else | ||||
| throw new Error("The sequence is required"); | ||||
| } | ||||
| } | ||||
|
|
r129 | export function isDestroyable(d: any): d is IDestroyable { | ||
|
|
r154 | return !!(d && typeof d.destroy === "function"); | ||
| } | ||||
| export function isRemovable(value: any): value is IRemovable { | ||||
| return !!(value && typeof value.remove === "function"); | ||||
|
|
r129 | } | ||
|
|
r75 | export function destroy(d: any) { | ||
|
|
r154 | if (isDestroyable(d)) | ||
|
|
r49 | d.destroy(); | ||
| } | ||||
|
|
r75 | |||
| /** | ||||
| * Used to mark that the async operation isn't awaited intentionally. | ||||
| * @param p The promise which represents the async operation. | ||||
| */ | ||||
| export function nowait(p: Promise<any>) { | ||||
| } | ||||
|
|
r76 | |||
| /** represents already destroyed object. | ||||
| */ | ||||
| export const destroyed = { | ||||
| /** Calling to this method doesn't affect anything, noop. | ||||
| */ | ||||
| destroy() { | ||||
| } | ||||
| }; | ||||
