Fundation.ts 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. //#region ******************************** 类型声明 ********************************
  2. export type Callback<T = any> = (err: Error | null, data: T) => void;
  3. export type Constructor<T = unknown> = new (...args: any[]) => T;
  4. export type ObjectKeyAsValue<T> = { readonly [K in keyof T]: K; }
  5. export type KeyArrayAsValue<T extends string> = { readonly [V in T]: V; }
  6. //#endregion
  7. /**
  8. * 销毁接口
  9. */
  10. export interface IDispose {
  11. Dispose(): void;
  12. }
  13. /** 自动销毁附着对象 */
  14. export interface IDisposeAttachTarget {
  15. AutoDispose(toDispose: IDispose): void;
  16. }
  17. //#region ******************************** 辅助函数 ********************************
  18. export function CreateObjectKeyAsValueMap<T extends Object>(obj: T): ObjectKeyAsValue<T> {
  19. let keys = Object.keys(obj);
  20. let ret: ObjectKeyAsValue<T> = Object.create(null);
  21. keys.forEach(key => {
  22. ret[key] = key;
  23. })
  24. keys = undefined;
  25. return ret;
  26. }
  27. export function CreateIntEnum<T extends string>(states: Array<T>, stateVal: number = 1): { [V in T]: number } {
  28. let ret = Object.create(null);
  29. states.forEach(state => {
  30. ret[state] = stateVal;
  31. stateVal = stateVal + 1;
  32. })
  33. return ret;
  34. }
  35. export function MaxValueOf<T extends string>(intEnum: { [k: string]: number }) {
  36. let init = false;
  37. let maxVal = 0;
  38. for (let k in intEnum) {
  39. if (!init) {
  40. maxVal = intEnum[k];
  41. } else {
  42. maxVal = (intEnum[k] > maxVal) ? intEnum[k] : maxVal;
  43. }
  44. }
  45. return maxVal;
  46. }
  47. export function onceCall(cb: () => void, cnt: number = 1) {
  48. return () => {
  49. if (cnt <= 0) {
  50. return;
  51. };
  52. --cnt;
  53. if (cnt === 0) {
  54. cb()
  55. }
  56. };
  57. }
  58. export function onceArgCall<T>(cb: (p: T) => void, cnt: number) {
  59. return (p: T) => {
  60. if (cnt <= 0) {
  61. return;
  62. };
  63. --cnt;
  64. if (cnt === 0) {
  65. cb(p)
  66. }
  67. }
  68. }
  69. //#endregion
  70. /**
  71. * 尾插入排序(从小到大)
  72. * @param list
  73. * @param compare
  74. * @returns
  75. */
  76. export function insertionSort<T>(list: T[], compare: (a: T, b: T) => number) {
  77. for (let length = list.length, i = 1; i < length; i++) {
  78. let j, value: T = list[i]
  79. for (j = i - 1; j >= 0; j--) {
  80. if (compare(list[j], value) <= 0) break
  81. list[j + 1] = list[j]
  82. }
  83. list[j + 1] = value
  84. }
  85. return list
  86. }
  87. /**
  88. * 已排好序(从小到大)的数组,获得 value 应该排在哪个位置(有相同值的情况下,排最左边)
  89. * @param list
  90. * @param value
  91. * @param compare
  92. * @returns
  93. */
  94. export function sortedIndex<T, V>(list: T[], value: V, compare: (a: T, b: V) => number): number {
  95. let low = 0, high = list.length
  96. while (low < high) {
  97. let mid = (low + high) >>> 1
  98. if (compare(list[mid], value) < 0) low = mid + 1
  99. else high = mid
  100. }
  101. return low
  102. }
  103. /**
  104. * 已排好序(从小到大)的数组,获得 value 应该排在哪个位置(有相同值的情况下,排最右边)
  105. * @param list
  106. * @param value
  107. * @param compare
  108. * @returns
  109. */
  110. export function sortedLastIndex<T>(list: T[], value: T, compare: (a: T, b: T) => number): number {
  111. let low = 0, high = list.length
  112. while (low < high) {
  113. let mid = (low + high) >>> 1
  114. if (compare(list[mid], value) <= 0) low = mid + 1
  115. else high = mid
  116. }
  117. return high
  118. }
  119. /**
  120. * 快速删除数组元素(不保证删除后的顺序)
  121. * @param arr
  122. * @param idx
  123. */
  124. export function fastRemoveIndex(arr: any[], idx: number) {
  125. if (idx >= 0 && idx < arr.length) {
  126. arr[idx] = arr[arr.length - 1];
  127. arr.length--;
  128. }
  129. }
  130. /**
  131. * 拷贝内容到剪切板
  132. * @param copyStr 内容
  133. */
  134. export function copyToClipboardForWeb(copyStr: string, callback: (err?: Error) => void): void {
  135. const el = document.createElement('textarea');
  136. el.value = copyStr;
  137. // Prevent keyboard from showing on mobile
  138. el.setAttribute('readonly', '');
  139. //el.style.contain = 'strict';
  140. el.style.position = 'absolute';
  141. el.style.left = '-9999px';
  142. el.style.fontSize = '12pt'; // Prevent zooming on iOS
  143. const selection = getSelection()!;
  144. let originalRange;
  145. if (selection.rangeCount > 0) {
  146. originalRange = selection.getRangeAt(0);
  147. }
  148. document.body.appendChild(el);
  149. el.select();
  150. // Explicit selection workaround for iOS
  151. el.selectionStart = 0;
  152. el.selectionEnd = copyStr.length;
  153. let isSuccess = false;
  154. try {
  155. isSuccess = document.execCommand('copy');
  156. } catch (err) {
  157. isSuccess = false;
  158. } finally {
  159. document.body.removeChild(el);
  160. if (originalRange) {
  161. selection.removeAllRanges();
  162. selection.addRange(originalRange);
  163. }
  164. }
  165. /** 返回结果 */
  166. if (isSuccess) {
  167. callback();
  168. } else {
  169. callback(new Error("执行剪切板拷贝命令出错"))
  170. }
  171. }