//#region ******************************** 类型声明 ******************************** export type Callback = (err: Error | null, data: T) => void; export type Constructor = new (...args: any[]) => T; export type ObjectKeyAsValue = { readonly [K in keyof T]: K; } export type KeyArrayAsValue = { readonly [V in T]: V; } //#endregion /** * 销毁接口 */ export interface IDispose { Dispose(): void; } /** 自动销毁附着对象 */ export interface IDisposeAttachTarget { AutoDispose(toDispose: IDispose): void; } //#region ******************************** 辅助函数 ******************************** export function CreateObjectKeyAsValueMap(obj: T): ObjectKeyAsValue { let keys = Object.keys(obj); let ret: ObjectKeyAsValue = Object.create(null); keys.forEach(key => { ret[key] = key; }) keys = undefined; return ret; } export function CreateIntEnum(states: Array, stateVal: number = 1): { [V in T]: number } { let ret = Object.create(null); states.forEach(state => { ret[state] = stateVal; stateVal = stateVal + 1; }) return ret; } export function MaxValueOf(intEnum: { [k: string]: number }) { let init = false; let maxVal = 0; for (let k in intEnum) { if (!init) { maxVal = intEnum[k]; } else { maxVal = (intEnum[k] > maxVal) ? intEnum[k] : maxVal; } } return maxVal; } export function onceCall(cb: () => void, cnt: number = 1) { return () => { if (cnt <= 0) { return; }; --cnt; if (cnt === 0) { cb() } }; } export function onceArgCall(cb: (p: T) => void, cnt: number) { return (p: T) => { if (cnt <= 0) { return; }; --cnt; if (cnt === 0) { cb(p) } } } //#endregion /** * 尾插入排序(从小到大) * @param list * @param compare * @returns */ export function insertionSort(list: T[], compare: (a: T, b: T) => number) { for (let length = list.length, i = 1; i < length; i++) { let j, value: T = list[i] for (j = i - 1; j >= 0; j--) { if (compare(list[j], value) <= 0) break list[j + 1] = list[j] } list[j + 1] = value } return list } /** * 已排好序(从小到大)的数组,获得 value 应该排在哪个位置(有相同值的情况下,排最左边) * @param list * @param value * @param compare * @returns */ export function sortedIndex(list: T[], value: V, compare: (a: T, b: V) => number): number { let low = 0, high = list.length while (low < high) { let mid = (low + high) >>> 1 if (compare(list[mid], value) < 0) low = mid + 1 else high = mid } return low } /** * 已排好序(从小到大)的数组,获得 value 应该排在哪个位置(有相同值的情况下,排最右边) * @param list * @param value * @param compare * @returns */ export function sortedLastIndex(list: T[], value: T, compare: (a: T, b: T) => number): number { let low = 0, high = list.length while (low < high) { let mid = (low + high) >>> 1 if (compare(list[mid], value) <= 0) low = mid + 1 else high = mid } return high } /** * 快速删除数组元素(不保证删除后的顺序) * @param arr * @param idx */ export function fastRemoveIndex(arr: any[], idx: number) { if (idx >= 0 && idx < arr.length) { arr[idx] = arr[arr.length - 1]; arr.length--; } } /** * 拷贝内容到剪切板 * @param copyStr 内容 */ export function copyToClipboardForWeb(copyStr: string, callback: (err?: Error) => void): void { const el = document.createElement('textarea'); el.value = copyStr; // Prevent keyboard from showing on mobile el.setAttribute('readonly', ''); //el.style.contain = 'strict'; el.style.position = 'absolute'; el.style.left = '-9999px'; el.style.fontSize = '12pt'; // Prevent zooming on iOS const selection = getSelection()!; let originalRange; if (selection.rangeCount > 0) { originalRange = selection.getRangeAt(0); } document.body.appendChild(el); el.select(); // Explicit selection workaround for iOS el.selectionStart = 0; el.selectionEnd = copyStr.length; let isSuccess = false; try { isSuccess = document.execCommand('copy'); } catch (err) { isSuccess = false; } finally { document.body.removeChild(el); if (originalRange) { selection.removeAllRanges(); selection.addRange(originalRange); } } /** 返回结果 */ if (isSuccess) { callback(); } else { callback(new Error("执行剪切板拷贝命令出错")) } }