rx-util

rxliuli 在浏览器上使用的 js 工具集

当前为 2019-08-17 提交的版本,查看 最新版本

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.cn-greasyfork.org/scripts/382120/725892/rx-util.js

  1. (function (global, factory) {
  2. typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
  3. typeof define === 'function' && define.amd ? define(['exports'], factory) :
  4. (global = global || self, factory(global.rx = {}));
  5. }(this, function (exports) { 'use strict';
  6.  
  7. /**
  8. * 在浏览器上下载二进制资源
  9. * @param blob 要下载的二进制资源
  10. * @param filename 文件名
  11. */
  12. function download(blob, filename = 'unknown') {
  13. // 创建隐藏的可下载链接
  14. const eleLink = document.createElement('a');
  15. eleLink.download = filename;
  16. eleLink.style.display = 'none';
  17. // 为 link 赋值
  18. eleLink.href = URL.createObjectURL(blob);
  19. // 触发点击
  20. document.body.appendChild(eleLink);
  21. eleLink.click();
  22. // 然后移除
  23. document.body.removeChild(eleLink);
  24. }
  25.  
  26. /*! *****************************************************************************
  27. Copyright (c) Microsoft Corporation. All rights reserved.
  28. Licensed under the Apache License, Version 2.0 (the "License"); you may not use
  29. this file except in compliance with the License. You may obtain a copy of the
  30. License at http://www.apache.org/licenses/LICENSE-2.0
  31.  
  32. THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  33. KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
  34. WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
  35. MERCHANTABLITY OR NON-INFRINGEMENT.
  36.  
  37. See the Apache Version 2.0 License for specific language governing permissions
  38. and limitations under the License.
  39. ***************************************************************************** */
  40.  
  41. function __awaiter(thisArg, _arguments, P, generator) {
  42. return new (P || (P = Promise))(function (resolve, reject) {
  43. function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  44. function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  45. function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
  46. step((generator = generator.apply(thisArg, _arguments || [])).next());
  47. });
  48. }
  49.  
  50. /**
  51. * 将字符串转化为 Blob 类型
  52. * @param str 字符串
  53. * @returns Blob 数据
  54. */
  55. function strToBlob(str) {
  56. return new Blob([str], {
  57. type: 'text/plain',
  58. });
  59. }
  60.  
  61. /**
  62. * 在浏览器上下载文本内容
  63. * @param str 字符串内容
  64. * @param filename 下载文件名,没有则默认为链接中的文件名
  65. */
  66. function downloadString(str, filename = 'unknown.txt') {
  67. return __awaiter(this, void 0, void 0, function* () {
  68. download(strToBlob(str), filename);
  69. });
  70. }
  71.  
  72. /**
  73. * 根据 url 下载二进制资源
  74. * @param url 下载请求信息
  75. * @param filename 下载文件名,没有则默认为链接中的文件名
  76. */
  77. function downloadUrl(url, filename = url.substr(url.lastIndexOf('/'))) {
  78. return __awaiter(this, void 0, void 0, function* () {
  79. try {
  80. const res = yield fetch(url);
  81. const blob = yield res.blob();
  82. download(blob, filename);
  83. }
  84. catch (error) {
  85. throw error;
  86. }
  87. });
  88. }
  89.  
  90. /**
  91. * 获取 cookie 键值映射 Map
  92. * @returns cookie 键值映射 Map
  93. */
  94. function getCookies() {
  95. return document.cookie.split(';').reduce((res, str) => {
  96. const [k, v] = str.split('=');
  97. res.set(k, v);
  98. return res;
  99. }, new Map());
  100. }
  101.  
  102. /**
  103. * 将 url 中的内容加载到元素上
  104. * 注:domSelector 必须有 src 属性用以将加载完成的资源赋值给其,加载默认是异步的
  105. * @param url url 资源
  106. * @param dom dom 元素
  107. * @param init 初始化参数, 实为 fetch() 的参数以及一些自定义的参数,默认 {}
  108. * 关于 fetch 具体可以参考 <https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch>
  109. */
  110. function loadResource(url, dom, init = {}) {
  111. return __awaiter(this, void 0, void 0, function* () {
  112. const res = yield fetch(url, init);
  113. const blob = yield res.blob();
  114. // 生成一个本地的 url 并赋值给 src 属性
  115. dom.src = window.URL.createObjectURL(blob);
  116. });
  117. }
  118.  
  119. /**
  120. * 协议与默认端口映射表
  121. */
  122. const protocolPortMap = new Map()
  123. .set('http', 80)
  124. .set('https', 443)
  125. .set('ssh', 22)
  126. .set('ftp', 21);
  127. /**
  128. * 解析 url 字符串
  129. * @param url url 字符串,不能为空
  130. * @returns url 对象
  131. */
  132. function parseUrl(url) {
  133. if (!url) {
  134. throw new Error('Url cannot be empty');
  135. }
  136. const regexp = new RegExp('^((\\w+)://([\\w\\.]*)(:(\\d+))?)(.*)');
  137. const temps = regexp.exec(url);
  138. if (temps === null) {
  139. return null;
  140. }
  141. const website = temps[1];
  142. const protocol = temps[2];
  143. const domain = temps[3];
  144. const portStr = temps[5];
  145. const href = temps[6];
  146. // 截取域名之后的内容
  147. const temp = url.substr(website.length);
  148. const markIndex = temp.indexOf('?');
  149. // 如果没有携带参数则直接返回
  150. if (markIndex === -1) {
  151. const accessPath = temp;
  152. return {
  153. url,
  154. website,
  155. protocol,
  156. domain,
  157. // tslint:disable-next-line:radix
  158. port: parseInt(portStr),
  159. href,
  160. accessPath,
  161. params: new Map(),
  162. };
  163. }
  164. let accessPath = temp.substr(0, markIndex);
  165. if (accessPath.endsWith('/')) {
  166. accessPath = accessPath.substring(0, accessPath.length - 1);
  167. }
  168. const port = portStr || protocolPortMap.get(protocol) || 0;
  169. // 解析参数列表
  170. const params = temp
  171. .substr(markIndex + 1)
  172. .split('&')
  173. .map(str => str.split('='))
  174. .filter(arr => arr[0] !== '')
  175. .reduce((params, arr) => {
  176. const k = decodeURIComponent(arr[0]);
  177. const v = decodeURIComponent(arr.length === 1 ? '' : arr[1]);
  178. // 如果已经存在了就认为是数组参数
  179. const vs = params.get(k);
  180. if (vs === undefined) {
  181. params.set(k, v);
  182. }
  183. else {
  184. if (!Array.isArray(vs)) {
  185. params.set(k, [vs]);
  186. }
  187. if (params.get(k).length !== undefined) {
  188. params.get(k).push(v);
  189. }
  190. }
  191. return params;
  192. }, new Map());
  193. return {
  194. url,
  195. website,
  196. protocol,
  197. domain,
  198. port,
  199. href,
  200. accessPath,
  201. params,
  202. };
  203. }
  204.  
  205. /**
  206. * 读取文件类型
  207. */
  208. var ReadType;
  209. (function (ReadType) {
  210. /**
  211. * 以 data url 读取
  212. */
  213. ReadType["DataURL"] = "readAsDataURL";
  214. /**
  215. * 以文本读取
  216. */
  217. ReadType["Text"] = "readAsText";
  218. /**
  219. * 以二进制文件读取
  220. */
  221. ReadType["BinaryString"] = "readAsBinaryString";
  222. /**
  223. * 以 ArrayBuffer 读取
  224. */
  225. ReadType["ArrayBuffer"] = "readAsArrayBuffer";
  226. })(ReadType || (ReadType = {}));
  227. /**
  228. * 读取本地浏览器选择的文件
  229. * @param file 选择的文件
  230. * @param option 可选项参数
  231. * @param option.type 读取的类型,默认按照二进制 url 读取
  232. * @param option.encoding 读取的编码格式,默认为 UTF-8
  233. * @returns 返回了读取到的内容(异步)
  234. */
  235. function readLocal(file, { type = readLocal.DataURL, encoding = 'UTF-8', } = {}) {
  236. return new Promise((resolve, reject) => {
  237. if (!file) {
  238. reject(new Error('file not exists'));
  239. }
  240. const fr = new FileReader();
  241. fr.onload = () => {
  242. resolve(fr.result);
  243. };
  244. fr.onerror = error => {
  245. reject(error);
  246. };
  247. new Map()
  248. .set(ReadType.DataURL, () => fr.readAsDataURL(file))
  249. .set(ReadType.Text, () => fr.readAsText(file, encoding))
  250. .set(ReadType.BinaryString, () => fr.readAsBinaryString(file))
  251. .set(ReadType.ArrayBuffer, () => fr.readAsArrayBuffer(file))
  252. .get(type)();
  253. });
  254. }
  255. /**
  256. * 以 data url 读取
  257. * @deprecated 已废弃,请使用枚举类 ReadType
  258. */
  259. readLocal.DataURL = ReadType.DataURL;
  260. /**
  261. * 以文本读取
  262. * @deprecated 已废弃,请使用枚举类 ReadType
  263. */
  264. readLocal.Text = ReadType.Text;
  265. /**
  266. * 以二进制文件读取
  267. * @deprecated 已废弃,请使用枚举类 ReadType
  268. */
  269. readLocal.BinaryString = ReadType.BinaryString;
  270. /**
  271. * 以 ArrayBuffer 读取
  272. * @deprecated 已废弃,请使用枚举类 ReadType
  273. */
  274. readLocal.ArrayBuffer = ReadType.ArrayBuffer;
  275.  
  276. /**
  277. * 为 js 中的 Date 对象原型添加 format 格式化方法
  278. * @param date 要进行格式化的日期
  279. * @param fmt 日期的格式
  280. * @returns 格式化得到的结果
  281. */
  282. function dateFormat(date, fmt) {
  283. const o = {
  284. 'y+': date.getFullYear(),
  285. 'M+': date.getMonth() + 1,
  286. 'd+': date.getDate(),
  287. 'h+': date.getHours(),
  288. 'm+': date.getMinutes(),
  289. 's+': date.getSeconds(),
  290. 'q+': Math.floor((date.getMonth() + 3) / 3),
  291. 'S+': date.getMilliseconds(),
  292. };
  293. for (const k in o) {
  294. if (!new RegExp('(' + k + ')').test(fmt)) {
  295. continue;
  296. }
  297. if (k === 'y+') {
  298. fmt = fmt.replace(RegExp.$1, ('' + o[k]).substr(4 - RegExp.$1.length));
  299. }
  300. else if (k === 'S+') {
  301. let lens = RegExp.$1.length;
  302. lens = lens === 1 ? 3 : lens;
  303. fmt = fmt.replace(RegExp.$1, ('00' + o[k]).substr(('' + o[k]).length - 1, lens));
  304. }
  305. else {
  306. const v = Reflect.get(o, k);
  307. fmt = fmt.replace(RegExp.$1, RegExp.$1.length === 1 ? v : ('00' + v).substr(('' + v).length));
  308. }
  309. }
  310. return fmt;
  311. }
  312.  
  313. /**
  314. * 默认的日期格式
  315. * 不加 Z 为本地日期时间,避免出现时区的问题
  316. */
  317. const deteFormatter = 'yyyy-MM-ddThh:mm:ss.SSS';
  318. /**
  319. * 将参数 key 与 value 进行 url 编码
  320. * @param k 参数的名字
  321. * @param v 参数的值
  322. * @returns 编码后的字符串
  323. */
  324. const encode = (k, v) => encodeURIComponent(k) + '=' + encodeURIComponent(v);
  325. /**
  326. * 拼接参数字符串
  327. * @param params 参数对象
  328. * @returns 拼接后的字符串
  329. */
  330. function spliceParams(params = {}) {
  331. return Array.from(Object.entries(params)).reduce((res, [k, v]) => {
  332. if (v === undefined || v === null) {
  333. return res;
  334. }
  335. else if (v instanceof Date) {
  336. res += encode(k, dateFormat(v, deteFormatter));
  337. }
  338. else if (v instanceof Array) {
  339. res += v
  340. .map(item => encode(k, item instanceof Date ? dateFormat(item, deteFormatter) : item))
  341. .join('&');
  342. }
  343. else {
  344. res += encode(k, v);
  345. }
  346. return (res += '&');
  347. }, '');
  348. }
  349.  
  350. /**
  351. * 等待指定的时间/等待指定表达式成立
  352. * 如果未指定等待条件则立刻执行
  353. * 注: 此实现在 nodejs 10- 会存在宏任务与微任务的问题,切记 async-await 本质上还是 Promise 的语法糖,实际上并非真正的同步函数!!!即便在浏览器,也不要依赖于这种特性。
  354. * @param param 等待时间/等待条件
  355. * @returns Promise 对象
  356. */
  357. function wait(param) {
  358. return new Promise(resolve => {
  359. if (typeof param === 'number') {
  360. setTimeout(resolve, param);
  361. }
  362. else if (typeof param === 'function') {
  363. const timer = setInterval(() => {
  364. if (param()) {
  365. clearInterval(timer);
  366. resolve();
  367. }
  368. }, 100);
  369. }
  370. else {
  371. resolve();
  372. }
  373. });
  374. }
  375.  
  376. /**
  377. * 为 fetch 请求添加超时选项
  378. * 注:超时选项并非真正意义上的超时即取消请求,请求依旧正常执行完成,但会提前返回 reject 结果
  379. * @param fetchPromise fetch 请求的 Promise
  380. * @param timeout 超时时间
  381. * @returns 如果超时就提前返回 reject, 否则正常返回 fetch 结果
  382. */
  383. function fetchTimeout(fetchPromise, timeout) {
  384. return Promise.race([
  385. fetchPromise,
  386. wait(timeout).then(() => {
  387. throw new Error('timeout');
  388. }),
  389. ]);
  390. }
  391.  
  392. /**
  393. * 将字符串转为字符流
  394. * @param str 字符串
  395. * @returns 字符流对象
  396. */
  397. function strToArrayBuffer(str) {
  398. const buf = new ArrayBuffer(str.length);
  399. const view = new Uint8Array(buf);
  400. for (let i = 0; i < str.length; ++i) {
  401. view[i] = str.charCodeAt(i) & 0xff;
  402. }
  403. return buf;
  404. }
  405.  
  406. /**
  407. * 限制并发请求数量的 fetch 封装
  408. * @class FetchLimiting
  409. * @example
  410. * const fetchLimiting = new FetchLimiting()
  411. * fetchLimiting._fetch('/')
  412. * .then(res => res.json())
  413. * .then(json => console.log(json))
  414. */
  415. class FetchLimiting {
  416. /**
  417. * 构造函数
  418. * @param option 可选配置项
  419. * @param option.timeout 超时毫秒数
  420. * @param option.limit 最大并发数限制
  421. */
  422. constructor({ timeout = 10000, limit = 10, } = {}) {
  423. /**
  424. * @field timeout 超时毫秒数
  425. */
  426. this.timeout = timeout;
  427. /**
  428. * @field limit 最大并发数限制
  429. */
  430. this.limit = limit;
  431. /**
  432. * @field execCount 当前正在执行异步的数量
  433. */
  434. this.execCount = 0;
  435. /**
  436. * @field waitArr 等待的队列
  437. * @type {Array.<IArguments>}
  438. */
  439. this.waitArr = [];
  440. }
  441. /**
  442. * 执行一个请求
  443. * 如果到达最大并发限制时就进行等待
  444. * @param url 请求 url 信息
  445. * @param init 请求的其他可选项,默认为 undefined
  446. * @returns 如果超时就提前返回 reject, 否则正常返回 fetch 结果
  447. */
  448. fetch(input, init) {
  449. return __awaiter(this, void 0, void 0, function* () {
  450. const _innerFetch = () => __awaiter(this, void 0, void 0, function* () {
  451. this.execCount++;
  452. const args = this.waitArr.shift();
  453. try {
  454. // 这里的 args 实际上就是 arguments 对象,即上面的 url 和 init
  455. return yield fetchTimeout(fetch(args[0], args[1]), this.timeout);
  456. }
  457. finally {
  458. this.execCount--;
  459. }
  460. });
  461. this.waitArr.push([input, init]);
  462. yield wait(() => this.execCount < this.limit);
  463. // 尝试启动等待队列
  464. return _innerFetch();
  465. });
  466. }
  467. }
  468.  
  469. /**
  470. * 将一个 Iterator 迭代器转换为一个 Array
  471. * @param iterator Iterator 迭代器
  472. * @return Iterator 中每一项元素转换而得到的 Array
  473. * @deprecated 已废弃,请使用 ES6 原生函数 {@see Array.from} 替代
  474. */
  475. function asIterator(iterator) {
  476. const arr = [];
  477. while (true) {
  478. const next = iterator.next();
  479. if (next.done) {
  480. break;
  481. }
  482. arr.push(next.value);
  483. }
  484. return arr;
  485. }
  486.  
  487. /**
  488. * 将数组异步压平一层
  489. * @param arr 数组
  490. * @param fn 映射函数,将一个元素映射为一个数组
  491. * @returns 压平一层的数组
  492. * @deprecated 已废弃,请使用更强大的异步数组 {@link AsyncArray}
  493. */
  494. function asyncFlatMap(arr, fn) {
  495. return __awaiter(this, void 0, void 0, function* () {
  496. const res = [];
  497. for (let i = 0; i < arr.length; i++) {
  498. res.push(...(yield fn(arr[i], i, arr)));
  499. }
  500. return res;
  501. });
  502. }
  503.  
  504. /**
  505. * 自行实现 flatMap,将数组压平一层
  506. * @param arr 数组
  507. * @param fn 映射方法,将一个元素映射为一个数组
  508. * @returns 压平一层的数组
  509. */
  510. function flatMap(arr, fn = v => Array.from(v)) {
  511. return arr.reduce((res, v, i, arr) => {
  512. res.push(...fn(v, i, arr));
  513. return res;
  514. }, new Array());
  515. }
  516.  
  517. function groupBy(arr, kFn,
  518. /**
  519. * 默认的值处理函数
  520. * @param res 最终 V 集合
  521. * @param item 当前迭代的元素
  522. * @returns 将当前元素合并后的最终 V 集合
  523. */
  524. vFn = ((res, item) => {
  525. res.push(item);
  526. return res;
  527. }), init = () => []) {
  528. // 将元素按照分组条件进行分组得到一个 条件 -> 数组 的对象
  529. return arr.reduce((res, item, index, arr) => {
  530. const k = kFn(item, index, arr);
  531. // 如果已经有这个键了就直接追加, 否则先将之初始化再追加元素
  532. if (!res.has(k)) {
  533. res.set(k, init());
  534. }
  535. res.set(k, vFn(res.get(k), item, index, arr));
  536. return res;
  537. }, new Map());
  538. }
  539.  
  540. /**
  541. * 创建一个等差数列数组
  542. * @param start 开始(包含)
  543. * @param end 结束(不包含)
  544. * @param sep 步长,默认为 1
  545. * @returns 等差数列数组
  546. */
  547. function range(start, end, sep = 1) {
  548. const arr = [];
  549. for (let i = start; i < end; i += sep) {
  550. arr.push(i);
  551. }
  552. return arr;
  553. }
  554.  
  555. /**
  556. * 返回第一个参数的函数
  557. * 注: 一般可以当作返回参数自身的函数,如果你只关注第一个参数的话
  558. * @param obj 任何对象
  559. * @typeparam T 传入参数的类型
  560. * @typeparam R 返回结果的类型,默认为 T,只是为了兼容该函数当参数被传递时可能出现需要类型不一致的问题
  561. * @returns 传入的第一个参数
  562. */
  563. function returnItself(obj) {
  564. return obj;
  565. }
  566.  
  567. /**
  568. * 将数组转化为一个 Object 对象
  569. * @deprecated 已废弃,请使用更好的 {@link arrayToMap} 替代
  570. * @param arr 需要进行转换的数组
  571. * @param kFn 生成对象属性名的函数
  572. * @param vFn 生成对象属性值的函数,默认为数组中的迭代元素
  573. * @returns 转化得到的对象
  574. */
  575. function toObject(arr, kFn, vFn = returnItself) {
  576. return arr.reduce((res, item, i, arr) => {
  577. const k = kFn(item, i, arr);
  578. if (!Reflect.has(res, k)) {
  579. Reflect.set(res, k, vFn(item, i, arr));
  580. }
  581. return res;
  582. }, {});
  583. }
  584.  
  585. /**
  586. * js 的数组去重方法
  587. * @param arr 要进行去重的数组
  588. * @param kFn 唯一标识元素的方法,默认使用 {@link returnItself}
  589. * @returns 进行去重操作之后得到的新的数组 (原数组并未改变)
  590. */
  591. function uniqueBy(arr, kFn = returnItself) {
  592. const set = new Set();
  593. return arr.filter((v, ...args) => {
  594. const k = kFn(v, ...args);
  595. if (set.has(k)) {
  596. return false;
  597. }
  598. set.add(k);
  599. return true;
  600. });
  601. }
  602.  
  603. /**
  604. * 将数组映射为 Map
  605. * @param arr 数组
  606. * @param k 产生 Map 元素唯一标识的函数,或者对象元素中的一个属性名
  607. * @param v 产生 Map 值的函数,默认为返回数组的元素,或者对象元素中的一个属性名
  608. * @returns 映射产生的 map 集合
  609. */
  610. function arrayToMap(arr, k, v = returnItself) {
  611. const kFn = k instanceof Function ? k : (item) => Reflect.get(item, k);
  612. const vFn = v instanceof Function ? v : (item) => Reflect.get(item, v);
  613. return arr.reduce((res, item, index, arr) => res.set(kFn(item, index, arr), vFn(item, index, arr)), new Map());
  614. }
  615.  
  616. /**
  617. * 填充字符串到指定长度
  618. * @param item 填充的字符串
  619. * @param len 填充的长度
  620. * @returns 填充完成的字符串
  621. * @deprecated 已废弃,请使用 ES6 {@link String.prototype.repeat} 函数
  622. * 具体请参考 MDN {@url(https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/repeat)}
  623. */
  624. function fill(item, len) {
  625. if (len <= 0) {
  626. return '';
  627. }
  628. return item + fill(item, len - 1);
  629. }
  630.  
  631. /**
  632. * 日期格式化类
  633. */
  634. class DateFormat {
  635. /**
  636. * 构造函数
  637. * @param name 日期格式的名称
  638. * @param format 日期的格式值
  639. * @param value 格式化得到的值
  640. * @param index 需要替换位置的索引
  641. */
  642. constructor(name, format, value, index) {
  643. this.name = name;
  644. this.format = format;
  645. this.value = value;
  646. this.index = index;
  647. }
  648. }
  649. /**
  650. * 日期时间的正则表达式
  651. */
  652. const dateFormats = new Map()
  653. .set('year', 'y{4}|y{2}')
  654. .set('month', 'M{1,2}')
  655. .set('day', 'd{1,2}')
  656. .set('hour', 'h{1,2}')
  657. .set('minute', 'm{1,2}')
  658. .set('second', 's{1,2}')
  659. .set('millieSecond', 'S{1,3}');
  660. /**
  661. * 如果没有格式化某项的话则设置为默认时间
  662. */
  663. const defaultDateValues = new Map()
  664. .set('month', '01')
  665. .set('day', '01')
  666. .set('hour', '00')
  667. .set('minute', '00')
  668. .set('second', '00')
  669. .set('millieSecond', '000');
  670. /**
  671. * 解析字符串为 Date 对象
  672. * @param str 日期字符串
  673. * @param fmt 日期字符串的格式,目前仅支持使用 y(年),M(月),d(日),h(时),m(分),s(秒),S(毫秒)
  674. * @returns 解析得到的 Date 对象
  675. */
  676. function dateParse(str, fmt) {
  677. const now = new Date();
  678. defaultDateValues.set('year', now.getFullYear().toString());
  679. // 保存对传入的日期字符串进行格式化的全部信息数组列表
  680. const dateUnits = [];
  681. for (const [fmtName, regex] of dateFormats) {
  682. const regExp = new RegExp(regex);
  683. if (regExp.test(fmt)) {
  684. const matchStr = regExp.exec(fmt)[0];
  685. const regexStr = fill('`', matchStr.length);
  686. const index = fmt.indexOf(matchStr);
  687. fmt = fmt.replace(matchStr, regexStr);
  688. dateUnits.push(new DateFormat(fmtName, fill('\\d', matchStr.length), null, index));
  689. }
  690. else {
  691. dateUnits.push(new DateFormat(fmtName, null, defaultDateValues.get(fmtName), -1));
  692. }
  693. }
  694. // 进行验证是否真的是符合传入格式的字符串
  695. fmt = fmt.replace(new RegExp('`', 'g'), '\\d');
  696. if (!new RegExp(`^${fmt}$`).test(str)) {
  697. return null;
  698. }
  699. // 进行一次排序, 依次对字符串进行截取
  700. dateUnits
  701. // 过滤掉没有得到格式化的对象
  702. .filter(({ format }) => format)
  703. // 按照字符串中日期片段的索引进行排序
  704. .sort(function (a, b) {
  705. return a.index - b.index;
  706. })
  707. // 获取到匹配的日期片段的值
  708. .map(format => {
  709. const matchDateUnit = new RegExp(format.format).exec(str);
  710. if (matchDateUnit !== null && matchDateUnit.length > 0) {
  711. str = str.replace(matchDateUnit[0], '');
  712. format.value = matchDateUnit[0];
  713. }
  714. return format;
  715. })
  716. // 覆写到 dateStr 上面
  717. .forEach(({ format }, i) => {
  718. const matchDateUnit = new RegExp(format).exec(str);
  719. if (matchDateUnit !== null && matchDateUnit.length > 0) {
  720. str = str.replace(matchDateUnit[0], '');
  721. dateUnits[i].value = matchDateUnit[0];
  722. }
  723. });
  724. // 将截取完成的信息封装成对象并格式化标准的日期字符串
  725. const map = arrayToMap(dateUnits, item => item.name, item => item.value);
  726. if (map.get('year').length === 2) {
  727. map.set('year', defaultDateValues
  728. .get('year')
  729. .substr(0, 2)
  730. .concat(map.get('year')));
  731. }
  732. // 注意:此处使用的是本地时间而非 UTC 时间
  733. const date = `${map.get('year')}-${map.get('month')}-${map.get('day')}T${map.get('hour')}:${map.get('minute')}:${map.get('second')}.${map.get('millieSecond')}`;
  734. return new Date(date);
  735. }
  736.  
  737. /**
  738. * 解析字符串为 Date 对象
  739. * @deprecated 已弃用,请使用可读性更好的 {@link dateParse} 代替
  740. * @param dateStr 日期字符串
  741. * @param fmt 日期字符串的格式
  742. * 目前仅支持使用 y(年),M(月),d(日),h(时),m(分),s(秒),S(毫秒)
  743. * @returns 解析得到的 Date 对象
  744. */
  745. function strToDate(dateStr, fmt) {
  746. return dateParse(dateStr, fmt);
  747. }
  748.  
  749. /**
  750. * 复制一段文本内容
  751. * @param text 要进行复制的文本
  752. * @returns 是否复制成功
  753. */
  754. function copyText(text) {
  755. const input = document.createElement('input');
  756. document.body.appendChild(input);
  757. input.setAttribute('value', text);
  758. input.select();
  759. const res = document.execCommand('copy');
  760. document.body.removeChild(input);
  761. return res;
  762. }
  763.  
  764. /**
  765. * 根据 html 字符串创建 Element 元素
  766. * @param str html 字符串
  767. * @returns 创建的 Element 元素
  768. */
  769. function createElByString(str) {
  770. const root = document.createElement('div');
  771. root.innerHTML = str;
  772. return root.querySelector('*');
  773. }
  774.  
  775. /**
  776. * 获取输入框中光标所在位置
  777. * @param {HTMLFormElement} el 需要获取的输入框元素
  778. * @returns 光标所在位置的下标
  779. */
  780. function getCusorPostion(el) {
  781. return el.selectionStart;
  782. }
  783.  
  784. /**
  785. * 设置输入框中选中的文本/光标所在位置
  786. * @param el 需要设置的输入框元素
  787. * @param start 光标所在位置的下标
  788. * @param end 结束位置,默认为输入框结束
  789. */
  790. function setCusorPostion(el, start, end = start) {
  791. el.focus();
  792. el.setSelectionRange(start, end);
  793. }
  794.  
  795. /**
  796. * 在指定位置后插入文本
  797. * @param el 需要设置的输入框元素
  798. * @param text 要插入的值
  799. * @param start 开始位置,默认为当前光标处
  800. */
  801. function insertText(el, text, start = getCusorPostion(el)) {
  802. const value = el.value;
  803. el.value = value.substr(0, start) + text + value.substr(start);
  804. setCusorPostion(el, start + text.length);
  805. }
  806.  
  807. /**
  808. * 判断一个对象是否是无效的
  809. * 无效的值仅包含 null/undefined
  810. * @param object 任何一个对象
  811. * @returns 是否无效的值
  812. */
  813. function isNullOrUndefined(object) {
  814. return object === undefined || object === null;
  815. }
  816.  
  817. /**
  818. * 字符串安全的转换为小写
  819. * @param str 字符串
  820. * @returns 转换后得到的全小写字符串
  821. */
  822. function toLowerCase(str) {
  823. if (isNullOrUndefined(str) || typeof str !== 'string') {
  824. return str;
  825. }
  826. return str.toLowerCase();
  827. }
  828.  
  829. /**
  830. * 判断指定元素是否是可编辑元素
  831. * 注:可编辑元素并不一定能够进行编辑,例如只读的 input 元素
  832. * @param el 需要进行判断的元素
  833. * @returns 是否为可编辑元素
  834. */
  835. function isEditable(el) {
  836. const inputEls = ['input', 'date', 'datetime', 'select', 'textarea'];
  837. return (
  838. // 此处需要判断是否存在属性 isContentEditable
  839. // @ts-ignore
  840. el && (el.isContentEditable || inputEls.includes(toLowerCase(el.tagName))));
  841. }
  842.  
  843. let lastFocusEl;
  844. document.addEventListener('focus', event => {
  845. lastFocusEl = event.target;
  846. }, true);
  847. document.addEventListener('blur', () => {
  848. lastFocusEl = null;
  849. }, true);
  850. /**
  851. * 获取到最后一个获得焦点的元素
  852. * @returns 最后一个获取到焦点的元素
  853. */
  854. function lastFocus() {
  855. return lastFocusEl;
  856. }
  857.  
  858. /**
  859. * 直接删除指定元素
  860. * @param el 需要删除的元素
  861. * @returns 返回被删除的元素
  862. */
  863. function removeEl(el) {
  864. const parent = el.parentElement;
  865. if (parent == null) {
  866. return null;
  867. }
  868. return parent.removeChild(el);
  869. }
  870.  
  871. /**
  872. * 在指定范围内删除文本
  873. * @param el 需要设置的输入框元素
  874. * @param start 开始位置,默认为当前选中开始位置
  875. * @param end 结束位置,默认为当前选中结束位置
  876. */
  877. function removeText(el, start = el.selectionStart, end = el.selectionEnd) {
  878. // 删除之前必须要 [记住] 当前光标的位置
  879. const index = getCusorPostion(el);
  880. const value = el.value;
  881. el.value = value.substr(0, start) + value.substr(end, value.length);
  882. setCusorPostion(el, index);
  883. }
  884.  
  885. /**
  886. * 用来保存监听到的事件信息
  887. */
  888. class Event {
  889. constructor(el, type, listener, options) {
  890. this.el = el;
  891. this.type = type;
  892. this.listener = listener;
  893. this.options = options;
  894. }
  895. }
  896. /**
  897. * 监听 event 的添加
  898. * 注:必须及早添加
  899. */
  900. function watchEventListener() {
  901. /**
  902. * 监听所有的 addEventListener, removeEventListener 事件
  903. */
  904. const documentAddEventListener = document.addEventListener;
  905. const eventTargetAddEventListener = EventTarget.prototype.addEventListener;
  906. const documentRemoveEventListener = document.removeEventListener;
  907. const eventTargetRemoveEventListener = EventTarget.prototype.removeEventListener;
  908. let events = [];
  909. /**
  910. * 自定义的添加事件监听函数
  911. * @param type 事件类型
  912. * @param listener 事件监听函数
  913. * @param useCapture 是否需要捕获事件冒泡,默认为 false
  914. */
  915. function addEventListener(type, listener, useCapture) {
  916. // @ts-ignore
  917. const _this = this;
  918. const $addEventListener = _this === document
  919. ? documentAddEventListener
  920. : eventTargetAddEventListener;
  921. events.push(new Event(_this, type, listener, useCapture));
  922. // @ts-ignore
  923. $addEventListener.apply(this, type, listener, useCapture);
  924. }
  925. /**
  926. * 自定义的根据类型删除事件函数
  927. * 该方法会删除这个类型下面全部的监听函数,不管数量
  928. * @param type 事件类型
  929. */
  930. // @ts-ignore
  931. function removeEventListenerByType(type) {
  932. // @ts-ignore
  933. const _this = this;
  934. const $removeEventListener = _this === document
  935. ? documentRemoveEventListener
  936. : eventTargetRemoveEventListener;
  937. const map = groupBy(events, e => e.el === _this && e.type === type);
  938. const removeArr = map.get(true);
  939. removeArr.forEach(e => {
  940. // @ts-ignore
  941. $removeEventListener.apply(e.el, [e.type, e.listener, e.useCapture]);
  942. });
  943. // @ts-ignore
  944. events = map.get(false);
  945. }
  946. // @ts-ignore
  947. document.addEventListener = EventTarget.prototype.addEventListener = addEventListener;
  948. // 此处是为了新增函数 removeEventListenerByType
  949. // @ts-ignore
  950. document.removeEventListenerByType = EventTarget.prototype.removeEventListenerByType = removeEventListenerByType;
  951. }
  952.  
  953. /**
  954. * 将任意对象转换为 String
  955. * 主要避免原生 Object toString 遇到某些空值的时候抛异常的问题
  956. * @param object 任意对象
  957. * @returns 字符串
  958. */
  959. function toString(object) {
  960. if (isNullOrUndefined(object)) {
  961. return '';
  962. }
  963. if (object instanceof Date) {
  964. return object.toISOString();
  965. }
  966. return object.toString();
  967. }
  968.  
  969. /**
  970. * FormData 批量添加方法
  971. * 注:该方法不会覆盖掉原本的属性
  972. * @param fd FormData 对象
  973. * @param obj 键值对对象
  974. * @returns 添加完成后的 FormData 对象
  975. */
  976. function appends(fd, obj) {
  977. for (const k in obj) {
  978. const v = obj[k];
  979. fd.append(k, toString(v));
  980. }
  981. return fd;
  982. }
  983.  
  984. /**
  985. * FormData 批量删除方法
  986. * @param fd FormData 对象
  987. * @param keys 删除的 key 列表
  988. * @returns 返回删除后的 FormData 对象
  989. */
  990. function deletes(fd, keys) {
  991. keys.forEach(key => fd.delete(key));
  992. return fd;
  993. }
  994.  
  995. /**
  996. * FormData 批量设置方法
  997. * 注:该方法会覆盖掉原本的属性
  998. * @param fd 表单对象
  999. * @param obj 键值对对象
  1000. * @returns 设置完成后的 FormData 对象
  1001. */
  1002. function sets(fd, obj) {
  1003. for (const k in obj) {
  1004. fd.set(k, obj[k]);
  1005. }
  1006. return fd;
  1007. }
  1008.  
  1009. /**
  1010. * FormData 转换为包含所有键值数组的二维数组函数
  1011. *
  1012. * @param fd 需要转换的 FormData 对象
  1013. * @returns 转换后的数组
  1014. * @deprecated 已被原生函数 Array.from 取代
  1015. */
  1016. function formDataToArray(fd) {
  1017. // @ts-ignore
  1018. return Array.from(fd);
  1019. }
  1020.  
  1021. /**
  1022. * 将参数对象转换为 FormData,只转换一层
  1023. * @param data 参数对象
  1024. * @return {FormData} 转换后的表单对象
  1025. */
  1026. function objToFormData(data) {
  1027. return Object.entries(data).reduce((res, [k, v]) => {
  1028. if (v instanceof Blob) {
  1029. res.append(k, v);
  1030. }
  1031. else {
  1032. res.append(k, v && v.toString());
  1033. }
  1034. return res;
  1035. }, new FormData());
  1036. }
  1037.  
  1038. /**
  1039. * 函数去抖
  1040. * 去抖 (debounce) 去抖就是对于一定时间段的连续的函数调用,只让其执行一次
  1041. * 注: 包装后的函数如果两次操作间隔小于 delay 则不会被执行, 如果一直在操作就会一直不执行, 直到操作停止的时间大于 delay 最小间隔时间才会执行一次, 不管任何时间调用都需要停止操作等待最小延迟时间
  1042. * 应用场景主要在那些连续的操作, 例如页面滚动监听, 包装后的函数只会执行最后一次
  1043. * 注: 该函数第一次调用一定不会执行,第一次一定拿不到缓存值,后面的连续调用都会拿到上一次的缓存值。如果需要在第一次调用获取到的缓存值,则需要传入第三个参数 {@param init},默认为 {@code undefined} 的可选参数
  1044. * 注: 返回函数结果的高阶函数需要使用 {@see Proxy} 实现,以避免原函数原型链上的信息丢失
  1045. *
  1046. * @param delay 最小延迟时间,单位为 ms
  1047. * @param action 真正需要执行的操作
  1048. * @param init 初始的缓存值,不填默认为 {@see undefined}
  1049. * @return 包装后有去抖功能的函数。该函数是异步的,与需要包装的函数 {@see action} 是否异步没有太大关联
  1050. */
  1051. function debounce(delay, action, init = null) {
  1052. let flag;
  1053. let result = init;
  1054. return new Proxy(action, {
  1055. apply(_, _this, args) {
  1056. return new Promise(resolve => {
  1057. if (flag)
  1058. clearTimeout(flag);
  1059. flag = setTimeout(() => resolve((result = Reflect.apply(_, _this, args))), delay);
  1060. setTimeout(() => resolve(result), delay);
  1061. });
  1062. },
  1063. });
  1064. }
  1065.  
  1066. /**
  1067. * 安全执行某个函数
  1068. * @param fn 需要执行的函数
  1069. * @param defaultVal 发生异常后的默认返回值,默认为 null
  1070. * @param args 可选的函数参数
  1071. * @returns 函数执行的结果,或者其默认值
  1072. */
  1073. function safeExec(fn, defaultVal = null, ...args) {
  1074. try {
  1075. return fn(...args);
  1076. }
  1077. catch (err) {
  1078. return defaultVal;
  1079. }
  1080. }
  1081.  
  1082. /**
  1083. * 使用 Proxy 实现通用的单例模式
  1084. * @param clazz 需要包装为单例的类型
  1085. * @returns 包装后的单例模式类,使用 {@code new} 创建将只在第一次有效
  1086. */
  1087. function singleModel(clazz) {
  1088. let instance;
  1089. return new Proxy(clazz, {
  1090. construct(target, args, newTarget) {
  1091. if (instance === undefined) {
  1092. instance = Reflect.construct(target, args, newTarget);
  1093. }
  1094. return instance;
  1095. },
  1096. });
  1097. }
  1098.  
  1099. /**
  1100. * 状态机
  1101. * 用于避免使用 if-else 的一种方式
  1102. * @typeparam K 状态的类型,默认为 any
  1103. * @typeparam V 构造函数返回值的类型,一般为实现子类的基类,默认为 any
  1104. */
  1105. class StateMachine {
  1106. constructor() {
  1107. this.classMap = new Map();
  1108. }
  1109. /**
  1110. * 获取到一个状态工厂
  1111. * @deprecated 已废弃,请直接创建一个 StateMachine 实例
  1112. */
  1113. static getFactory() {
  1114. /**
  1115. * 状态注册器
  1116. * 更好的有限状态机,分离子类与构建的关系,无论子类如何增删该都不影响基类及工厂类
  1117. */
  1118. return new StateMachine();
  1119. }
  1120. /**
  1121. * 注册一个 class,创建子类时调用,用于记录每一个 [状态 => 子类] 对应
  1122. * 注: 此处不再默认使用单例模式,如果需要,请自行对 class 进行包装
  1123. * @param state 作为键的状态
  1124. * @param clazz 对应的子类型
  1125. * @returns 返回 clazz 本身
  1126. */
  1127. register(state, clazz) {
  1128. this.classMap.set(state, clazz);
  1129. return clazz;
  1130. }
  1131. /**
  1132. * 获取一个标签子类对象
  1133. * @param state 状态索引
  1134. * @param args 构造函数的参数
  1135. * @returns 子类对象
  1136. */
  1137. getInstance(state, ...args) {
  1138. const Class = this.classMap.get(state);
  1139. if (!Class) {
  1140. return null;
  1141. }
  1142. // 构造函数的参数
  1143. return new Class(...args);
  1144. }
  1145. /**
  1146. * 允许使用 for-of 遍历整个状态机
  1147. */
  1148. *[Symbol.iterator]() {
  1149. for (const kv of this.classMap.entries()) {
  1150. yield kv;
  1151. }
  1152. }
  1153. }
  1154.  
  1155. /**
  1156. * 函数节流
  1157. * 节流 (throttle) 让一个函数不要执行的太频繁,减少执行过快的调用,叫节流
  1158. * 类似于上面而又不同于上面的函数去抖, 包装后函数在上一次操作执行过去了最小间隔时间后会直接执行, 否则会忽略该次操作
  1159. * 与上面函数去抖的明显区别在连续操作时会按照最小间隔时间循环执行操作, 而非仅执行最后一次操作
  1160. * 注: 该函数第一次调用一定会执行,不需要担心第一次拿不到缓存值,后面的连续调用都会拿到上一次的缓存值
  1161. * 注: 返回函数结果的高阶函数需要使用 {@see Proxy} 实现,以避免原函数原型链上的信息丢失
  1162. *
  1163. * @param delay 最小间隔时间,单位为 ms
  1164. * @param action 真正需要执行的操作
  1165. * @return {Function} 包装后有节流功能的函数。该函数是异步的,与需要包装的函数 {@link action} 是否异步没有太大关联
  1166. */
  1167. function throttle(delay, action) {
  1168. let last = 0;
  1169. let result;
  1170. return new Proxy(action, {
  1171. apply(target, thisArg, args) {
  1172. return new Promise(resolve => {
  1173. const curr = Date.now();
  1174. if (curr - last > delay) {
  1175. result = Reflect.apply(target, thisArg, args);
  1176. last = curr;
  1177. resolve(result);
  1178. return;
  1179. }
  1180. resolve(result);
  1181. });
  1182. },
  1183. });
  1184. }
  1185.  
  1186. /**
  1187. * 兼容异步函数的返回值
  1188. * @param res 返回值
  1189. * @param callback 同步/异步结果的回调函数
  1190. * @typeparam T 处理参数的类型,如果是 Promise 类型,则取出其泛型类型
  1191. * @typeparam Param 处理参数具体的类型,如果是 Promise 类型,则指定为原类型
  1192. * @typeparam R 返回值具体的类型,如果是 Promise 类型,则指定为 Promise 类型,否则为原类型
  1193. * @returns 处理后的结果,如果是同步的,则返回结果是同步的,否则为异步的
  1194. */
  1195. function compatibleAsync(res, callback) {
  1196. return (res instanceof Promise
  1197. ? res.then(callback)
  1198. : callback(res));
  1199. }
  1200.  
  1201. /**
  1202. * 测试函数的执行时间
  1203. * 注:如果函数返回 Promise,则该函数也会返回 Promise,否则直接返回执行时间
  1204. * @param fn 需要测试的函数
  1205. * @returns 执行的毫秒数
  1206. */
  1207. function timing(fn) {
  1208. const begin = performance.now();
  1209. const res = fn();
  1210. return compatibleAsync(res, () => performance.now() - begin);
  1211. }
  1212.  
  1213. /**
  1214. * 轮询等待指定资源加载完毕再执行操作
  1215. * 使用 Promises 实现,可以使用 ES7 的 {@see async} 和 {@see await} 调用
  1216. * @param fn 判断必须的资源是否存在的方法
  1217. * @param option 可配置项
  1218. * @returns Promise 对象
  1219. */
  1220. function waitResource(fn, { interval = 100, max = 10 } = {}) {
  1221. let current = 0;
  1222. return new Promise((resolve, reject) => {
  1223. const timer = setInterval(() => {
  1224. if (fn()) {
  1225. clearInterval(timer);
  1226. resolve();
  1227. }
  1228. current++;
  1229. if (current >= max) {
  1230. clearInterval(timer);
  1231. reject(new Error('waitResource call timeout'));
  1232. }
  1233. }, interval);
  1234. });
  1235. }
  1236.  
  1237. /**
  1238. * 监视指定函数返回值的变化
  1239. * @param fn 需要监视的函数
  1240. * @param callback 回调函数
  1241. * @param interval 每次检查的间隔时间,默认为 100ms
  1242. * @returns 关闭这个监视函数
  1243. */
  1244. function watch(fn, callback, interval = 100) {
  1245. let oldVal = fn();
  1246. const timer = setInterval(() => {
  1247. const newVal = fn();
  1248. if (oldVal !== newVal) {
  1249. callback(newVal, oldVal);
  1250. oldVal = newVal;
  1251. }
  1252. }, interval);
  1253. return () => clearInterval(timer);
  1254. }
  1255.  
  1256. /**
  1257. * 深度监听指定对象属性的变化
  1258. * 注:指定对象不能是原始类型,即不可变类型,而且对象本身的引用不能改变,最好使用 const 进行声明
  1259. * @param object 需要监视的对象
  1260. * @param callback 当代理对象发生改变时的回调函数,回调函数有三个参数,分别是对象,修改的 key,修改的 v
  1261. * @returns 返回源对象的一个代理
  1262. */
  1263. function watchObject(object, callback) {
  1264. const handler = {
  1265. get(target, k) {
  1266. try {
  1267. // 注意: 这里很关键,它为对象的字段也添加了代理
  1268. return new Proxy(Reflect.get(target, k), handler);
  1269. }
  1270. catch (err) {
  1271. return Reflect.get(target, k);
  1272. }
  1273. },
  1274. set(target, k, v) {
  1275. callback(target, k, v);
  1276. return Reflect.set(target, k, v);
  1277. },
  1278. };
  1279. return new Proxy(object, handler);
  1280. }
  1281.  
  1282. /**
  1283. * 字符串格式化
  1284. *
  1285. * @param str 要进行格式化的值
  1286. * @param args 格式化参数值,替换字符串中的 {} 的值
  1287. * @returns 替换完成的字符串
  1288. * @deprecated 已废弃,请使用 ES6 模板字符串 {@url(https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/template_strings)}
  1289. */
  1290. function format(str, args) {
  1291. return Object.keys(args).reduce((res, k) => res.replace(new RegExp(`{${k}}`, 'g'), toString(args[k])), str);
  1292. }
  1293.  
  1294. /**
  1295. * 判断数字是否在指定区间之中
  1296. * @param num 指定数字
  1297. * @param min 最小值
  1298. * @param max 最大值(不包含)
  1299. */
  1300. function isRange(num, min, max) {
  1301. return num >= min && num < max;
  1302. }
  1303.  
  1304. /**
  1305. * 判断是否为小数的正则表达式
  1306. */
  1307. const FloatRule = /^(-?\d+)(.\d+)?$/;
  1308. /**
  1309. * 判断是否为整数的正则表达式
  1310. */
  1311. const IntegerRule = /^-?\d+$/;
  1312. /**
  1313. * 判断是否为邮箱的正则表达式
  1314. */
  1315. const EmailRule = /^\w+((-\w+)|(\.\w+))*@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z]+$/;
  1316. /**
  1317. * 判断是否为 ipv4 地址的正则表达式
  1318. */
  1319. const Ipv4Rule = /^((25[0-5]|2[0-4]\d|1?\d?\d)\.){3}(25[0-5]|2[0-4]\d|1?\d?\d)$/;
  1320. /**
  1321. * 判断是否为固定电话的正则表达式
  1322. */
  1323. const TelephoneRule = /^0[1-9][0-9]{1,2}-[2-8][0-9]{6,7}$/;
  1324. /**
  1325. * 判断是否为移动电话的正则表达式
  1326. */
  1327. const MobileRule = /^(((13[0-9]{1})|15[0-9]{1}|18[0-9]{1}|)+\d{8})$/;
  1328. /**
  1329. * 判断是否为域名的正则表达式
  1330. */
  1331. const DomainRule = /^(?=^.{3,255}$)[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+$/;
  1332. /**
  1333. * 判断是否为邮政编码的正则表达式
  1334. */
  1335. const PostcodeRule = /^\d{6}$/;
  1336. /**
  1337. * 字符串校验
  1338. * TODO 使用 any 可能是个严重的错误。。。
  1339. */
  1340. class StringValidator {
  1341. /**
  1342. * 判断一个字符串是否为空字符串
  1343. * @param str 字符串
  1344. * @returns 是否为空字符串
  1345. */
  1346. static isEmpty(str) {
  1347. return isNullOrUndefined(str) || str === '';
  1348. }
  1349. /**
  1350. * 判断一个字符串是否为空白的字符串
  1351. * @param str 字符串
  1352. * @returns 是否为空字符串
  1353. */
  1354. static isBlank(str) {
  1355. return StringValidator.isEmpty(str) || str.trim() === '';
  1356. }
  1357. /**
  1358. * 判断字符串是否位小数
  1359. * @param str 需要进行判断的字符串
  1360. * @returns 是否为小数
  1361. */
  1362. static isFloat(str) {
  1363. if (isNullOrUndefined(str)) {
  1364. return false;
  1365. }
  1366. return FloatRule.test(str);
  1367. }
  1368. /**
  1369. * 判断字符串是否位整数
  1370. * @param str 需要进行判断的字符串
  1371. * @returns 是否为小数
  1372. */
  1373. static isInteger(str) {
  1374. return !isNullOrUndefined(str) && IntegerRule.test(str);
  1375. }
  1376. /**
  1377. * 判断邮箱的格式是否正确
  1378. * @param str 邮箱字符串
  1379. * @returns 是否是邮箱
  1380. */
  1381. static isEmail(str) {
  1382. return !isNullOrUndefined(str) && EmailRule.test(str);
  1383. }
  1384. /**
  1385. * 判断 ipv4 地址的格式是否正确
  1386. * @param str ipv4 字符串
  1387. * @returns 是否是 ipv4 地址
  1388. */
  1389. static isIpv4(str) {
  1390. return !isNullOrUndefined(str) && Ipv4Rule.test(str);
  1391. }
  1392. /**
  1393. * 判断字符串是否为正确的端口号
  1394. * 正确的端口号是 1-65535
  1395. * @param str 字符串
  1396. * @returns 是否为端口号
  1397. */
  1398. static isPort(str) {
  1399. // tslint:disable-next-line:radix
  1400. return StringValidator.isInteger(str) && isRange(parseInt(str), 1, 65535);
  1401. }
  1402. /**
  1403. * 判断是否为固定电话
  1404. * @param str 字符串
  1405. * @returns 是否为固定电话
  1406. */
  1407. static isTelephone(str) {
  1408. return !isNullOrUndefined(str) && TelephoneRule.test(str);
  1409. }
  1410. /**
  1411. * 判断是否为移动电话
  1412. * @param str 字符串
  1413. * @returns 是否为移动电话
  1414. */
  1415. static isMobile(str) {
  1416. return !isNullOrUndefined(str) && MobileRule.test(str);
  1417. }
  1418. /**
  1419. * 判断是否为域名
  1420. * @param str 字符串
  1421. * @returns 是否为域名
  1422. */
  1423. static isDomain(str) {
  1424. return !isNullOrUndefined(str) && DomainRule.test(str);
  1425. }
  1426. /**
  1427. * 判断是否为邮政编码
  1428. * @param str 字符串
  1429. * @returns 是否为邮政编码
  1430. */
  1431. static isPostcode(str) {
  1432. return !isNullOrUndefined(str) && PostcodeRule.test(str);
  1433. }
  1434. }
  1435. /**
  1436. * 导出一个字符串校验的对象
  1437. * @deprecated 已废弃,请直接使用类的静态函数
  1438. */
  1439. const stringValidator = StringValidator;
  1440.  
  1441. /**
  1442. * 判断字符串是否位小数
  1443. * @param str 需要进行判断的字符串
  1444. * @returns 是否为小数
  1445. * @deprecated 已废弃,请使用 {@link stringValidator#isFloat}
  1446. */
  1447. function isFloat(str) {
  1448. return stringValidator.isFloat(str);
  1449. }
  1450.  
  1451. /**
  1452. * 判断字符串是否位整数
  1453. * @param str 需要进行判断的字符串
  1454. * @returns 是否为小数
  1455. * @deprecated 已废弃,请使用 {@link stringValidator#isInteger}
  1456. */
  1457. function isNumber(str) {
  1458. return stringValidator.isInteger(str);
  1459. }
  1460.  
  1461. /**
  1462. * 字符串安全的转换为大写
  1463. * @param str 字符串
  1464. * @returns 转换后得到的全大写字符串
  1465. */
  1466. function toUpperCase(str) {
  1467. if (isNullOrUndefined(str) || typeof str !== 'string') {
  1468. return str;
  1469. }
  1470. return str.toUpperCase();
  1471. }
  1472.  
  1473. /**
  1474. * 将空白字符串转换为 null
  1475. *
  1476. * @param str 将空字符串转换为 {@code null}
  1477. * @returns 可能为 {@code null}
  1478. */
  1479. function blankToNull(str) {
  1480. return stringValidator.isBlank(str) ? null : str;
  1481. }
  1482.  
  1483. /**
  1484. * 置空对象所有空白的属性
  1485. * @param obj 对象
  1486. * @returns 将所有的空白属性全部转换为 null 的新对象
  1487. */
  1488. function blankToNullField(obj) {
  1489. return Reflect.ownKeys(obj).reduce((res, k) => {
  1490. const v = Reflect.get(obj, k);
  1491. Reflect.set(res, k, typeof v === 'string' ? blankToNull(v) : v);
  1492. return res;
  1493. }, {});
  1494. }
  1495.  
  1496. /**
  1497. * 将对象的所有属性置空
  1498. * @param obj 需要置空属性的对象
  1499. * @returns 返回一个新的对象
  1500. */
  1501. function emptyAllField(obj) {
  1502. return Reflect.ownKeys(obj).reduce((res, k) => {
  1503. Reflect.set(res, k, null);
  1504. return res;
  1505. }, {});
  1506. }
  1507.  
  1508. /**
  1509. * 排除对象中的指定字段
  1510. * 注: 此处将获得一个浅拷贝对象
  1511. * @param obj 排除对象
  1512. * @param fields 要排除的多个字段
  1513. * @returns 排除完指定字段得到的新的对象
  1514. */
  1515. function excludeFields(obj, ...fields) {
  1516. const set = new Set(fields);
  1517. return Reflect.ownKeys(obj).reduce((res, k) => {
  1518. if (!set.has(k)) {
  1519. Reflect.set(res, k, Reflect.get(obj, k));
  1520. }
  1521. return res;
  1522. }, {});
  1523. }
  1524.  
  1525. /**
  1526. * 将 Map 转换为 Object 对象
  1527. * @param map Map 键值表
  1528. * @returns 转换得到的 Object 对象
  1529. */
  1530. function mapToObject(map) {
  1531. const res = {};
  1532. for (const [k, v] of map) {
  1533. Reflect.set(res, k, v);
  1534. }
  1535. return res;
  1536. }
  1537.  
  1538. /**
  1539. * 生成一个随机的数字
  1540. * 如果没有参数,则会抛出异常
  1541. * @param num1 第一个参数,如果只有一个参数,则认为是最大值,最小值为 0
  1542. * @param num2 第二个参数,如果该参数存在,则认为第二个是最大值,忽略剩余的参数
  1543. * @returns 生成的随机整数
  1544. */
  1545. function randomInt(num1, num2) {
  1546. const min = num2 ? num1 : 0;
  1547. const max = num2 ? num2 : num1;
  1548. return min + Math.floor(Math.random() * (max - min));
  1549. }
  1550.  
  1551. /**
  1552. * 日期固定时间点
  1553. */
  1554. class DateConstants {
  1555. /**
  1556. * 获取指定日期一天的开始时间
  1557. * @param date 指定的时间,默认为当前日期
  1558. * @returns 一天的开始时间
  1559. */
  1560. static dayStart(date = new Date()) {
  1561. return new Date(`${dateFormat(date, 'yyyy-MM-dd')}T00:00:00.000`);
  1562. }
  1563. /**
  1564. * 获取指定日期一天的结束时间
  1565. * @param date 指定的时间,默认为当前日期
  1566. * @returns 一天的结束时间
  1567. */
  1568. static dayEnd(date = new Date()) {
  1569. return new Date(`${dateFormat(date, 'yyyy-MM-dd')}T23:59:59.999`);
  1570. }
  1571. /**
  1572. * 获取指定日期所在年份的新年开始时间
  1573. * @param date 指定的时间,默认为当前日期
  1574. * @returns 新年开始时间
  1575. */
  1576. static yearStart(date = new Date()) {
  1577. return new Date(`${date.getFullYear()}-01-01T00:00:00.000`);
  1578. }
  1579. /**
  1580. * 获取指定日期所在年份的旧年结束时间
  1581. * @param date 指定的时间,默认为当前日期
  1582. * @returns 旧年结束时间
  1583. */
  1584. static yearEnd(date = new Date()) {
  1585. return new Date(`${date.getFullYear()}-12-31T23:59:59.999`);
  1586. }
  1587. }
  1588. /**
  1589. * 导出一个日期固定时间点的对象
  1590. * @deprecated 已废弃,请直接使用类的静态函数
  1591. */
  1592. const dateConstants = DateConstants;
  1593.  
  1594. /**
  1595. * 一天标准的毫秒数
  1596. */
  1597. const DAY_UNIT_TIME = 1000 * 60 * 60 * 24;
  1598. /**
  1599. * 日期增强
  1600. */
  1601. class DateEnhance {
  1602. /**
  1603. * 构造函数
  1604. * @param date 要增强的日期
  1605. */
  1606. constructor(date) {
  1607. this.date = date;
  1608. }
  1609. /**
  1610. * 获取到年份
  1611. * @returns
  1612. */
  1613. year() {
  1614. return this.date.getFullYear();
  1615. }
  1616. /**
  1617. * 获取月份
  1618. * @returns
  1619. * @deprecated 已废弃,请使用 {@link this#monthOfYear} 函数
  1620. */
  1621. month() {
  1622. return this.date.getMonth();
  1623. }
  1624. /**
  1625. * 获取今年的第几个月份
  1626. * 和 {@link this#month} 不同的是不再从 0 计算月份
  1627. */
  1628. monthOfYear() {
  1629. return this.date.getMonth() + 1;
  1630. }
  1631. /**
  1632. * 获取一年内的第多少天
  1633. * 注: 这个天数指定的在第几天而非过去了多少天,例如 2018-01-10 的结果会是 10
  1634. * @returns
  1635. */
  1636. dayOfYear() {
  1637. return Math.ceil((this.date.getTime() - dateConstants.yearStart(this.date).getTime()) /
  1638. DAY_UNIT_TIME);
  1639. }
  1640. /**
  1641. * 获取一个月内的第多少天
  1642. * 注: 这个天数指的是在第几天而非过去了多少天,例如 2018-01-10 的结果会是 10
  1643. * @returns
  1644. */
  1645. dayOfMonth() {
  1646. return this.date.getDate();
  1647. }
  1648. /**
  1649. * 获取一个星期内的第多少天
  1650. * @returns
  1651. */
  1652. dayOfWeek() {
  1653. return this.date.getDay();
  1654. }
  1655. /**
  1656. * 获取一年内的第多少星期
  1657. * 注: 这个星期指定的在第几天而非过去了多少天,例如 2018-01-10 的结果会是 10
  1658. * @returns
  1659. */
  1660. weekOfYear() {
  1661. return Math.ceil(this.dayOfYear() / 7);
  1662. }
  1663. /**
  1664. * 获取一个月内的第多少星期
  1665. * @returns
  1666. */
  1667. weekOfMonth() {
  1668. return Math.ceil(this.dayOfMonth() / 7);
  1669. }
  1670. /**
  1671. * 获取季度
  1672. * @returns
  1673. */
  1674. quarter() {
  1675. const month = this.month();
  1676. if (isRange(month, 0, 3)) {
  1677. return 1;
  1678. }
  1679. else if (isRange(month, 3, 6)) {
  1680. return 2;
  1681. }
  1682. else if (isRange(month, 6, 9)) {
  1683. return 3;
  1684. }
  1685. else {
  1686. return 4;
  1687. }
  1688. }
  1689. /**
  1690. * 获取小时
  1691. * @returns
  1692. */
  1693. hour() {
  1694. return this.date.getHours();
  1695. }
  1696. /**
  1697. * 获取分钟
  1698. * @returns
  1699. */
  1700. minute() {
  1701. return this.date.getMinutes();
  1702. }
  1703. /**
  1704. * 获取秒
  1705. * @returns
  1706. */
  1707. second() {
  1708. return this.date.getSeconds();
  1709. }
  1710. /**
  1711. * 获取毫秒
  1712. * @returns
  1713. */
  1714. milliSecond() {
  1715. return this.date.getMilliseconds();
  1716. }
  1717. }
  1718. /**
  1719. * 获取一个增强的日期
  1720. * @param date 要增强的日期
  1721. * @returns 增强日期
  1722. */
  1723. function dateEnhance(date) {
  1724. return new DateEnhance(date);
  1725. }
  1726.  
  1727. /**
  1728. * 获取一年内的第多少星期
  1729. * @param date 日期
  1730. * @returns 这个日期第多少个星期
  1731. * @deprecated 不推荐使用,请使用 {@see dateEnhance} 代替
  1732. */
  1733. function getYearWeek(date) {
  1734. return dateEnhance(date).weekOfYear();
  1735. }
  1736.  
  1737. /**
  1738. * 时间日期间隔
  1739. */
  1740. class DateBetween {
  1741. /**
  1742. * 构造函数
  1743. * @param start 开始时间
  1744. * @param end 结束时间
  1745. */
  1746. constructor(start, end) {
  1747. this.start = start;
  1748. this.end = end;
  1749. }
  1750. /**
  1751. * 获取毫秒差值
  1752. * @returns 毫秒差值
  1753. */
  1754. milliSecond() {
  1755. return this.end.getTime() - this.start.getTime();
  1756. }
  1757. /**
  1758. * 获取秒差值
  1759. * @returns 秒差值
  1760. */
  1761. second() {
  1762. return Math.floor(this.milliSecond() / 1000);
  1763. }
  1764. /**
  1765. * 获取分钟差值
  1766. * @returns 分钟差值
  1767. */
  1768. minute() {
  1769. return Math.floor(this.second() / 60);
  1770. }
  1771. /**
  1772. * 获取小时差值
  1773. * @returns 小时差值
  1774. */
  1775. hour() {
  1776. return Math.floor(this.minute() / 60);
  1777. }
  1778. /**
  1779. * 获取天数差值
  1780. * @returns 天数差值
  1781. */
  1782. day() {
  1783. return Math.floor(this.hour() / 24);
  1784. }
  1785. /**
  1786. * 获取月份差值
  1787. * 注: 此处获取的差值是按月计算的,即 2018-12-31 => 2019-01-01 也被认为相差一个月
  1788. * @returns 月份差值
  1789. */
  1790. month() {
  1791. const year = this.year();
  1792. const month = this.end.getMonth() - this.start.getMonth();
  1793. return year * 12 + month;
  1794. }
  1795. /**
  1796. * 获取年份差值
  1797. * 注: 此处获取的差值是按年计算的,即 2018-12-31 => 2019-01-01 也被认为相差一年
  1798. * @returns 年份差值
  1799. */
  1800. year() {
  1801. return this.end.getFullYear() - this.start.getFullYear();
  1802. }
  1803. }
  1804. /**
  1805. * 获取两个时间的差值
  1806. * @param start 开始时间
  1807. * @param end 结束时间
  1808. * @returns 差值对象
  1809. */
  1810. function dateBetween(start, end) {
  1811. return new DateBetween(start, end);
  1812. }
  1813.  
  1814. /**
  1815. * 返回合理参数本身的函数
  1816. * 1. 如果没有参数则返回 undefined
  1817. * 2. 如果只有一个参数则返回参数本身
  1818. * 3. 如果有两个以上的参数则返回参数列表
  1819. * @param args 任何对象
  1820. * @returns 传入的参数
  1821. */
  1822. function returnReasonableItself(...args) {
  1823. const len = args.length;
  1824. if (len === 0) {
  1825. return null;
  1826. }
  1827. if (len === 1) {
  1828. return args[0];
  1829. }
  1830. return args;
  1831. }
  1832.  
  1833. /**
  1834. * 从数组中移除指定的元素
  1835. * 注: 时间复杂度为 1~3On
  1836. * @param arr 需要被过滤的数组
  1837. * @param deleteItems 要过滤的元素数组
  1838. * @param kFn 每个元素的唯一键函数
  1839. */
  1840. function filterItems(arr, deleteItems, kFn = returnItself) {
  1841. const kSet = new Set(deleteItems.map(kFn));
  1842. return arr.filter((v, i, arr) => !kSet.has(kFn(v, i, arr)));
  1843. }
  1844.  
  1845. /**
  1846. * 比较两个数组的差异
  1847. * @param left 第一个数组
  1848. * @param right 第二个数组
  1849. * @param kFn 每个元素的唯一标识产生函数
  1850. * @returns 比较的差异结果
  1851. */
  1852. function arrayDiffBy(left, right, kFn = returnItself) {
  1853. // 首先得到两个 kSet 集合用于过滤
  1854. const kThanSet = new Set(left.map(kFn));
  1855. const kThatSet = new Set(right.map(kFn));
  1856. const leftUnique = left.filter((v, ...args) => !kThatSet.has(kFn(v, ...args)));
  1857. const rightUnique = right.filter((v, ...args) => !kThanSet.has(kFn(v, ...args)));
  1858. const kLeftSet = new Set(leftUnique.map(kFn));
  1859. const common = left.filter((v, ...args) => !kLeftSet.has(kFn(v, ...args)));
  1860. return { left: leftUnique, right: rightUnique, common };
  1861. }
  1862.  
  1863. /**
  1864. * 使用 Generator 实现一个从 0 开始的无限自增序列
  1865. */
  1866. function* autoIncrementGenerator() {
  1867. for (let i = 0;; i++) {
  1868. /**
  1869. * @returns 每次获取都返回循环中的当前迭代变量,然后暂停于此处
  1870. */
  1871. yield i;
  1872. }
  1873. }
  1874. /**
  1875. * 生成器对象
  1876. */
  1877. const generator = autoIncrementGenerator();
  1878. /**
  1879. * 获取自增长序列的最新值
  1880. * @returns 最新值
  1881. */
  1882. function autoIncrement() {
  1883. return generator.next().value;
  1884. }
  1885.  
  1886. /**
  1887. * 转换接口
  1888. * @interface
  1889. */
  1890. class IConverter {
  1891. /**
  1892. * 将字符串解析为字符串列表
  1893. *
  1894. * @param str 字符串
  1895. * @return {Array.<String>} 字符串列表
  1896. * @abstract
  1897. */
  1898. from(str) {
  1899. throw new Error('子类必须重写 from 函数');
  1900. }
  1901. /**
  1902. * 将字符串列表构造为字符串
  1903. *
  1904. * @param list 字符串列表
  1905. * @return {String} 字符串
  1906. * @abstract
  1907. */
  1908. to(list) {
  1909. throw new Error('子类必须重写 to 函数');
  1910. }
  1911. }
  1912.  
  1913. /**
  1914. * 驼峰风格解析
  1915. */
  1916. class CamelOrPascalFrom extends IConverter {
  1917. /**
  1918. * 将字符串解析为字符串列表
  1919. *
  1920. * @param str 字符串
  1921. * @return {Array.<String>} 字符串列表
  1922. * @override
  1923. */
  1924. from(str) {
  1925. const result = [];
  1926. const len = str.length;
  1927. let old = 0;
  1928. for (let i = 0; i < len; i++) {
  1929. const c = str.charAt(i);
  1930. if (c >= 'A' && c <= 'Z') {
  1931. if (i !== 0) {
  1932. result.push(str.substring(old, i));
  1933. }
  1934. old = i;
  1935. }
  1936. }
  1937. if (old !== str.length) {
  1938. result.push(str.substring(old, str.length));
  1939. }
  1940. return result;
  1941. }
  1942. }
  1943.  
  1944. /**
  1945. * 小写开头的驼峰转换器
  1946. *
  1947. */
  1948. class CamelConverter extends CamelOrPascalFrom {
  1949. /**
  1950. * 将字符串列表构造为字符串
  1951. *
  1952. * @param list 字符串列表
  1953. * @return {String} 字符串
  1954. * @override
  1955. */
  1956. to(list) {
  1957. return list.reduce((res, s, i) => {
  1958. const str = toLowerCase(s);
  1959. return (res +=
  1960. (i === 0 ? toLowerCase : toUpperCase)(str.substring(0, 1)) +
  1961. str.substring(1));
  1962. }, '');
  1963. }
  1964. }
  1965.  
  1966. /**
  1967. * 大写开头的驼峰转换器
  1968. */
  1969. class PascalConverter extends CamelOrPascalFrom {
  1970. /**
  1971. * 将字符串列表构造为字符串
  1972. *
  1973. * @param list 字符串列表
  1974. * @return {String} 字符串
  1975. * @override
  1976. */
  1977. to(list) {
  1978. return list.reduce((res, s) => {
  1979. const str = toLowerCase(s);
  1980. return (res += toUpperCase(str.substring(0, 1)) + str.substring(1));
  1981. }, '');
  1982. }
  1983. }
  1984.  
  1985. /**
  1986. * 下划线风格解析
  1987. */
  1988. class SnakeOrScreamingSnakeFrom extends IConverter {
  1989. /**
  1990. * 将字符串解析为字符串列表
  1991. *
  1992. * @param str 字符串
  1993. * @return {Array.<String>} 字符串列表
  1994. * @override
  1995. */
  1996. from(str) {
  1997. return str.split('_');
  1998. }
  1999. }
  2000.  
  2001. /**
  2002. * 小写下划线的转换器
  2003. */
  2004. class SnakeConverter extends SnakeOrScreamingSnakeFrom {
  2005. /**
  2006. * 将字符串列表构造为字符串
  2007. *
  2008. * @param list 字符串列表
  2009. * @return {String} 字符串
  2010. * @override
  2011. */
  2012. to(list) {
  2013. return list.map(toLowerCase).join('_');
  2014. }
  2015. }
  2016.  
  2017. /**
  2018. * 大写下划线的转换器
  2019. */
  2020. class ScreamingSnakeConverter extends SnakeOrScreamingSnakeFrom {
  2021. /**
  2022. * 将字符串列表构造为字符串
  2023. *
  2024. * @param list 字符串列表
  2025. * @return {String} 字符串
  2026. * @override
  2027. */
  2028. to(list) {
  2029. return list.map(toUpperCase).join('_');
  2030. }
  2031. }
  2032.  
  2033. /**
  2034. * @enum {Symbol} 字符串风格常量对象
  2035. */
  2036. (function (StringStyleType) {
  2037. /**
  2038. * 小写驼峰
  2039. */
  2040. StringStyleType[StringStyleType["Camel"] = 1] = "Camel";
  2041. /**
  2042. * 大写驼峰
  2043. */
  2044. StringStyleType[StringStyleType["Pascal"] = 2] = "Pascal";
  2045. /**
  2046. * 小写下划线
  2047. */
  2048. StringStyleType[StringStyleType["Snake"] = 3] = "Snake";
  2049. /**
  2050. * 大写下划线
  2051. */
  2052. StringStyleType[StringStyleType["ScreamingSnake"] = 4] = "ScreamingSnake";
  2053. })(exports.StringStyleType || (exports.StringStyleType = {}));
  2054.  
  2055. /**
  2056. * 转换器工厂
  2057. */
  2058. class ConverterFactory {
  2059. /**
  2060. * 获取一个转换器实例
  2061. *
  2062. * @param styleType 转换风格,使用了 {@link stringStyleType} 定义的常量对象
  2063. * @return {IConverter} 转换器对象
  2064. * @throws 如果获取未定义过的转换器,则会抛出异常
  2065. */
  2066. static getInstance(styleType) {
  2067. switch (styleType) {
  2068. case exports.StringStyleType.Camel:
  2069. return new CamelConverter();
  2070. case exports.StringStyleType.Pascal:
  2071. return new PascalConverter();
  2072. case exports.StringStyleType.Snake:
  2073. return new SnakeConverter();
  2074. case exports.StringStyleType.ScreamingSnake:
  2075. return new ScreamingSnakeConverter();
  2076. default:
  2077. throw new Error('No corresponding converter found');
  2078. }
  2079. }
  2080. }
  2081.  
  2082. /**
  2083. * 字符串风格转换器
  2084. * 请不要直接使用构造函数创建,而是用 {@link StringStyleUtil.getConverter} 来获得一个转换器
  2085. * @private
  2086. */
  2087. class StringStyleConverter {
  2088. /**
  2089. * 构造一个字符串任意风格转换器
  2090. * @param from 转换字符串的风格
  2091. * @param to 需要转换的风格
  2092. * @private
  2093. */
  2094. constructor(from, to) {
  2095. /**
  2096. * @field 解析字符串风格的转换器
  2097. * @type {IConverter}
  2098. * @private
  2099. */
  2100. this.fromConverter = ConverterFactory.getInstance(from);
  2101. /**
  2102. * @field 构造字符串风格的转换器
  2103. * @type {IConverter}
  2104. * @private
  2105. */
  2106. this.toConverter = ConverterFactory.getInstance(to);
  2107. }
  2108. /**
  2109. * 转换字符串的风格
  2110. *
  2111. * @param str 要转换的字符串
  2112. * @return {String} 转换得到的字符串
  2113. */
  2114. convert(str) {
  2115. if (stringValidator.isEmpty(str)) {
  2116. return str;
  2117. }
  2118. return this.toConverter.to(this.fromConverter.from(str));
  2119. }
  2120. }
  2121.  
  2122. /**
  2123. * 包装一个函数为指定参数只执行一次的函数
  2124. * @param fn 需要包装的函数
  2125. * @param identity 参数转换的函数,参数为需要包装函数的参数
  2126. * @returns 需要被包装的函数
  2127. */
  2128. function onceOfSameParam(fn, identity = fn.toString()) {
  2129. const generateKey = (args) => `onceOfSameParam-${identity}-${JSON.stringify(args)}`;
  2130. const cacheMap = new Map();
  2131. return new Proxy(fn, {
  2132. apply(_, _this, args) {
  2133. const key = generateKey(args);
  2134. const old = cacheMap.get(key);
  2135. if (old !== undefined) {
  2136. return old;
  2137. }
  2138. const res = Reflect.apply(_, _this, args);
  2139. return compatibleAsync(res, res => {
  2140. cacheMap.set(key, res);
  2141. return res;
  2142. });
  2143. },
  2144. });
  2145. }
  2146.  
  2147. /**
  2148. * 包装获取字符串风格转换器
  2149. * 此处采用了单例模式,每种转换器只会有一个
  2150. *
  2151. * @param from 解析风格
  2152. * @param to 转换风格
  2153. * @return {StringStyleConverter} 转换器的实例
  2154. */
  2155. const _getConverter = onceOfSameParam(
  2156. /**
  2157. * @param from 解析风格
  2158. * @param to 转换风格
  2159. * @return {StringStyleConverter} 转换器的实例
  2160. */
  2161. (from, to) => new StringStyleConverter(from, to));
  2162. /**
  2163. * 字符串风格转换工具类
  2164. */
  2165. class StringStyleUtil {
  2166. /**
  2167. * 获取一个转换器的实例
  2168. * 该函数获取的转换器可以任意复用,请优先使用函数
  2169. * @param from 解析风格
  2170. * @param to 转换风格
  2171. * @return {StringStyleConverter} 转换器的实例
  2172. */
  2173. static getConverter(from, to) {
  2174. return _getConverter(from, to);
  2175. }
  2176. /**
  2177. * 直接转换字符串的风格
  2178. * 请优先使用可以复用的 {@link StringStyleUtil.getConverter} 函数
  2179. * @param from 解析风格
  2180. * @param to 转换风格
  2181. * @param str 要转换的字符串
  2182. * @return {String} 转换得到的字符串
  2183. */
  2184. static convert(from, to, str) {
  2185. return StringStyleUtil.getConverter(from, to).convert(str);
  2186. }
  2187. }
  2188.  
  2189. /**
  2190. * 获取对象中所有的属性,包括 ES6 新增的 Symbol 类型的属性
  2191. * @param obj 任何对象
  2192. * @returns 属性数组
  2193. * @deprecated 已废弃,请使用 ES6 {@see Reflect.ownKeys} 代替
  2194. * 具体参考 {@url(https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect/ownKeys)}
  2195. */
  2196. function getObjectKeys(obj) {
  2197. if (isNullOrUndefined(obj)) {
  2198. return [];
  2199. }
  2200. return Reflect.ownKeys(obj);
  2201. }
  2202.  
  2203. /**
  2204. * 获取对象中所有的属性值,包括 ES6 新增的 Symbol 类型的属性
  2205. * @param obj 任何对象
  2206. * @returns 属性值数组
  2207. */
  2208. function getObjectValues(obj) {
  2209. return getObjectKeys(obj).map(k => Reflect.get(obj, k));
  2210. }
  2211.  
  2212. /**
  2213. * 递归使对象不可变
  2214. * @param obj 任何非空对象
  2215. * @returns 新的不可变对象
  2216. */
  2217. function deepFreeze(obj) {
  2218. // 数组和对象分别处理
  2219. if (obj instanceof Array) {
  2220. obj.forEach(v => {
  2221. if (typeof v === 'object') {
  2222. deepFreeze(v);
  2223. }
  2224. });
  2225. }
  2226. else if (obj instanceof Object) {
  2227. getObjectValues(obj).forEach(v => {
  2228. if (typeof v === 'object') {
  2229. deepFreeze(v);
  2230. }
  2231. });
  2232. }
  2233. return Object.freeze(obj);
  2234. }
  2235.  
  2236. /**
  2237. * 包装对象,使其成为可以任意深度调用而不会出现 undefined 调用的问题
  2238. * 注意: 该函数不能进行递归调用({@link JSON.stringfy}),一定会造成堆栈溢出的问题(RangeError: Maximum call stack size exceeded)
  2239. * @param obj 任意一个 Object 对象
  2240. * @returns 包装后的对象
  2241. */
  2242. function deepProxy(obj) {
  2243. const handler = {
  2244. get(target, k) {
  2245. Reflect.set(target, k, Reflect.has(target, k) ? Reflect.get(target, k) : {});
  2246. const v = Reflect.get(target, k);
  2247. if (typeof v === 'object') {
  2248. return new Proxy(v, handler);
  2249. }
  2250. return v;
  2251. },
  2252. };
  2253. return new Proxy(obj, handler);
  2254. }
  2255.  
  2256. /**
  2257. * 将函数包装为柯里化函数
  2258. * 注: 该函数模仿了 Lodash 的 curry 函数
  2259. * @param fn 需要包装的函数
  2260. * @param {...any} args 应用的部分参数
  2261. * @returns 包装后的函数
  2262. */
  2263. function curry(fn, ...args) {
  2264. const realArgs = args.filter(arg => arg !== curry._);
  2265. // 如果函数参数足够则调用传入的函数
  2266. if (realArgs.length >= fn.length) {
  2267. return fn(...realArgs);
  2268. }
  2269. /**
  2270. * 最终返回的函数
  2271. * @param otherArgs 接受任意参数
  2272. * @returns 返回一个函数,或者函数调用完成返回结果
  2273. */
  2274. function innerFn(...otherArgs) {
  2275. // 记录需要移除补到前面的参数
  2276. const removeIndexSet = new Set();
  2277. let i = 0;
  2278. const newArgs = args.map(arg => {
  2279. if (arg !== curry._ ||
  2280. otherArgs[i] === undefined ||
  2281. otherArgs[i] === curry._) {
  2282. return arg;
  2283. }
  2284. removeIndexSet.add(i);
  2285. // 每次补偿前面的 curry._ 参数计数器 +1
  2286. return otherArgs[i++];
  2287. });
  2288. const newOtherArgs = otherArgs.filter((_v, i) => !removeIndexSet.has(i));
  2289. return curry(fn, ...newArgs, ...newOtherArgs);
  2290. }
  2291. // 定义柯里化函数的剩余参数长度,便于在其他地方进行部分参数应用
  2292. // 注: 不使用 length 属性的原因是 length 属性
  2293. innerFn._length = fn.length - args.filter(arg => arg !== curry._).length;
  2294. // 自定义 toString 函数便于调试
  2295. innerFn.toString = () => `name: ${fn.name}, args: [${args.map(o => o.toString()).join(', ')}]`;
  2296. innerFn._curry = true;
  2297. return innerFn;
  2298. }
  2299. /**
  2300. * 柯里化的占位符,需要应用后面的参数时使用
  2301. * 例如 {@link curry(fn)(curry._, 1)} 意味着函数 fn 的第二个参数将被确定为 1
  2302. */
  2303. curry._ = Symbol('_');
  2304.  
  2305. /**
  2306. * 快速根据指定函数对数组进行排序
  2307. * 注: 使用递归实现,对于超大数组(其实前端的数组不可能特别大吧?#笑)可能造成堆栈溢出
  2308. * @param arr 需要排序的数组
  2309. * @param kFn 对数组中每个元素都产生可比较的值的函数,默认返回自身进行比较
  2310. * @returns 排序后的新数组
  2311. */
  2312. function sortBy(arr, kFn = returnItself) {
  2313. const newArr = arr.map((v, i) => [v, i]);
  2314. function _sort(arr, fn) {
  2315. // 边界条件,如果传入数组的值
  2316. if (arr.length <= 1) {
  2317. return arr;
  2318. }
  2319. // 根据中间值对数组分治为两个数组
  2320. const medianIndex = Math.floor(arr.length / 2);
  2321. const medianValue = arr[medianIndex];
  2322. const left = [];
  2323. const right = [];
  2324. for (let i = 0, len = arr.length; i < len; i++) {
  2325. if (i === medianIndex) {
  2326. continue;
  2327. }
  2328. const v = arr[i];
  2329. if (fn(v, medianValue) <= 0) {
  2330. left.push(v);
  2331. }
  2332. else {
  2333. right.push(v);
  2334. }
  2335. }
  2336. return _sort(left, fn)
  2337. .concat([medianValue])
  2338. .concat(_sort(right, fn));
  2339. }
  2340. return _sort(newArr, ([t1, i1], [t2, i2]) => {
  2341. const k1 = kFn(t1, i1, arr);
  2342. const k2 = kFn(t2, i2, arr);
  2343. if (k1 === k2) {
  2344. return 0;
  2345. }
  2346. else if (k1 < k2) {
  2347. return -1;
  2348. }
  2349. else {
  2350. return 1;
  2351. }
  2352. }).map(([_v, i]) => arr[i]);
  2353. }
  2354.  
  2355. /**
  2356. * 日期格式化器
  2357. * 包含格式化为字符串和解析字符串为日期的函数
  2358. */
  2359. class DateFormatter {
  2360. /**
  2361. * 构造函数
  2362. * @param fmt 日期时间格式
  2363. */
  2364. constructor(fmt) {
  2365. this.fmt = fmt;
  2366. }
  2367. /**
  2368. * 格式化
  2369. * @param date 需要格式化的日期
  2370. * @returns 格式化的字符串
  2371. */
  2372. format(date) {
  2373. if (isNullOrUndefined(date)) {
  2374. return '';
  2375. }
  2376. return dateFormat(date, this.fmt);
  2377. }
  2378. /**
  2379. * 解析字符串为日期对象
  2380. * @param str 字符串
  2381. * @returns 解析得到的日期
  2382. */
  2383. parse(str) {
  2384. if (stringValidator.isEmpty(str)) {
  2385. return null;
  2386. }
  2387. return dateParse(str, this.fmt);
  2388. }
  2389. /**
  2390. * 将日期时间字符串转换为前端指定格式的字符串
  2391. * 主要适用场景是前端接收到后端的日期时间一般是一个字符串,然而需要自定义格式的时候还必须先创建 {@link Date} 对象才能格式化,略微繁琐,故使用该函数
  2392. * @param str 字符串
  2393. * @param parseFmt 解析的日期时间格式。默认直接使用 {@link new Date()} 创建
  2394. * @returns 转换后得到的字符串
  2395. */
  2396. strFormat(str, parseFmt) {
  2397. if (stringValidator.isEmpty(str)) {
  2398. return '';
  2399. }
  2400. const date = parseFmt ? dateParse(str, parseFmt) : new Date(str);
  2401. return dateFormat(date, this.fmt);
  2402. }
  2403. }
  2404. /**
  2405. * 日期格式化器
  2406. */
  2407. DateFormatter.dateFormatter = new DateFormatter('yyyy-MM-dd');
  2408. /**
  2409. * 时间格式化器
  2410. */
  2411. DateFormatter.timeFormatter = new DateFormatter('hh:mm:ss');
  2412. /**
  2413. * 日期时间格式化器
  2414. */
  2415. DateFormatter.dateTimeFormatter = new DateFormatter('yyyy-MM-dd hh:mm:ss');
  2416.  
  2417. /**
  2418. * 查询符合条件的元素的下标
  2419. * @param arr 查询的数组
  2420. * @param fn 谓词
  2421. * @param num 查询的第几个符合条件的元素,默认为 1,和默认的 findIndex 行为保持一致
  2422. * @returns 符合条件的元素的下标,如果没有则返回 -1
  2423. */
  2424. function findIndex(arr, fn, num = 1) {
  2425. let k = 0;
  2426. for (let i = 0, len = arr.length; i < len; i++) {
  2427. if (fn.call(arr, arr[i], i, arr) && ++k >= num) {
  2428. return i;
  2429. }
  2430. }
  2431. return -1;
  2432. }
  2433.  
  2434. /**
  2435. * 连接两个函数并自动柯里化
  2436. * 注: 该函数依赖于 length,所以不支持默认参数以及不定参数
  2437. * @param fn1 第一个函数
  2438. * @param fn2 第二个函数
  2439. * @returns 连接后的函数
  2440. */
  2441. const _compose = (fn1, fn2) => {
  2442. return function (...args) {
  2443. const i = findIndex(args, v => v !== curry._, fn1._length || fn1.length);
  2444. const res = curry(fn1, ...args);
  2445. // 如果这个函数的参数不足,则返回它
  2446. if (i === -1) {
  2447. return _compose(res, fn2);
  2448. }
  2449. // 否则将结果以及多余的参数应用到下一个函数上
  2450. return curry(fn2, res, ...args.slice(i + 1));
  2451. };
  2452. };
  2453. /**
  2454. * 将多个函数组合起来
  2455. * 前面函数的返回值将变成后面函数的第一个参数,如果到了最后一个函数执行完成,则直接返回
  2456. * 注: 该函数是自动柯里化,将对所有传入的函数进行柯里化处理
  2457. * 注: 该函数支持一次调用传入全部函数的参数
  2458. * @param fns 多个需要连接函数
  2459. * @returns 连接后的柯里化函数
  2460. * TODO 这里需要进行类型优化
  2461. */
  2462. function compose(...fns) {
  2463. return fns.reduceRight((fn1, fn2) => _compose(fn2, fn1));
  2464. }
  2465.  
  2466. /**
  2467. * 递归排除对象中的指定字段
  2468. * @param obj 需要排除的对象
  2469. * @param {...obj} fields 需要排除的字段
  2470. */
  2471. function excludeFieldsDeep(obj, ...fields) {
  2472. return Reflect.ownKeys(obj).reduce((res, k) => {
  2473. const v = Reflect.get(res, k);
  2474. if (v instanceof Object) {
  2475. Reflect.set(obj, k, excludeFieldsDeep(v, ...fields));
  2476. }
  2477. return res;
  2478. }, obj instanceof Array ? obj : excludeFields(obj, ...fields));
  2479. }
  2480.  
  2481. /**
  2482. * 缓存的值
  2483. */
  2484. class CacheVal {
  2485. /**
  2486. * 构造函数
  2487. * @param options 缓存值对象
  2488. * @param options.key 缓存的键原始值
  2489. * @param options.val 缓存的值
  2490. * @param options.cacheOption 缓存的选项
  2491. */
  2492. constructor(options = {}) {
  2493. Object.assign(this, options);
  2494. }
  2495. }
  2496.  
  2497. /**
  2498. * 无限的超时时间
  2499. * TODO 此处暂时使用字符串作为一种折衷方法,因为 Symbol 无法被序列化为 JSON,反向序列化也是不可能的
  2500. */
  2501. const TimeoutInfinite = 'TimoutInfinite';
  2502.  
  2503. /**
  2504. * 合并多个对象的属性
  2505. * 1. 该合并的方式为浅层合并,只会合并一层的对象
  2506. * 2. 默认忽略值为 undefined/null 的属性
  2507. * @param {...Object} sources 任意数量的对象
  2508. * @returns 合并后的对象
  2509. */
  2510. function assign(target, ...sources) {
  2511. return [target, ...sources].reduce((res, source) => {
  2512. if (isNullOrUndefined(source)) {
  2513. return res;
  2514. }
  2515. return Reflect.ownKeys(source).reduce((res, k) => {
  2516. const v = Reflect.get(source, k);
  2517. if (isNullOrUndefined(v)) {
  2518. return res;
  2519. }
  2520. Reflect.set(res, k, v);
  2521. return res;
  2522. }, res);
  2523. }, {});
  2524. }
  2525.  
  2526. /**
  2527. * 使用 LocalStorage 实现的缓存
  2528. */
  2529. class LocalStorageCache {
  2530. /**
  2531. * 构造函数
  2532. * @param cacheOption 全局缓存选项
  2533. */
  2534. constructor({ timeout = TimeoutInfinite, serialize = JSON.stringify, deserialize = JSON.parse, } = {}) {
  2535. this.cacheOption = {
  2536. timeout,
  2537. serialize,
  2538. deserialize,
  2539. };
  2540. /**
  2541. * 缓存对象,默认使用 localStorage
  2542. */
  2543. this.localStorage = window.localStorage;
  2544. // 创建后将异步清空所有过期的缓存
  2545. this.clearExpired();
  2546. }
  2547. /**
  2548. * 清空所有过期的 key
  2549. * 注: 该函数是异步执行的
  2550. */
  2551. clearExpired() {
  2552. return __awaiter(this, void 0, void 0, function* () {
  2553. const local = this.localStorage;
  2554. const len = local.length;
  2555. const delKeys = [];
  2556. for (let i = 0; i < len; i++) {
  2557. yield wait(0);
  2558. const key = local.key(i);
  2559. const str = local.getItem(key);
  2560. const cacheVal = safeExec(JSON.parse, null, str);
  2561. if (cacheVal === null) {
  2562. continue;
  2563. }
  2564. const { timeStart, timeout } = cacheVal.cacheOption;
  2565. // 如果超时则删除并返回 null
  2566. // console.log(i, cacheVal, Date.now(), Date.now() - timeStart > timeout)
  2567. if (timeout !== TimeoutInfinite && Date.now() - timeStart > timeout) {
  2568. delKeys.push(key);
  2569. }
  2570. // console.log(i, key, local.getItem(key))
  2571. }
  2572. yield delKeys.forEach((key) => __awaiter(this, void 0, void 0, function* () { return local.removeItem(key); }));
  2573. });
  2574. }
  2575. /**
  2576. * 根据 key + value 添加
  2577. * 如果不存在则添加,否则忽略
  2578. * @param key 缓存的 key
  2579. * @param val 缓存的 value
  2580. * @param cacheOption 缓存的选项,默认为无限时间
  2581. * @override
  2582. */
  2583. add(key, val, cacheOption = {}) {
  2584. const result = this.get(key, cacheOption);
  2585. if (result !== null) {
  2586. return;
  2587. }
  2588. this.set(key, val, cacheOption);
  2589. }
  2590. /**
  2591. * 根据指定的 key 删除
  2592. * 如果存在则删除,否则忽略
  2593. * @param key 删除的 key
  2594. * @override
  2595. */
  2596. del(key) {
  2597. this.localStorage.removeItem(key);
  2598. }
  2599. /**
  2600. * 根据指定的 key 修改
  2601. * 不管是否存在都会设置
  2602. * @param key 修改的 key
  2603. * @param val 修改的 value
  2604. * @param cacheOption 修改的选项
  2605. * @override
  2606. */
  2607. set(key, val, cacheOption = {}) {
  2608. const option = assign(this.cacheOption, cacheOption);
  2609. this.localStorage.setItem(key, JSON.stringify(new CacheVal({
  2610. key,
  2611. val: option.serialize(val),
  2612. cacheOption: Object.assign({}, option, { timeStart: option.timeStart || Date.now() }),
  2613. })));
  2614. }
  2615. /**
  2616. * 根据 key 获取
  2617. * 如果存在则获取,否则忽略
  2618. * @param key 指定的 key
  2619. * @param cacheOption 获取的选项
  2620. * @returns 获取到的缓存值
  2621. * @override
  2622. */
  2623. get(key, cacheOption = {}) {
  2624. const str = this.localStorage.getItem(key);
  2625. const cacheVal = safeExec(JSON.parse, null, str);
  2626. if (cacheVal === null) {
  2627. return null;
  2628. }
  2629. const { timeStart, timeout, deserialize } = assign(this.cacheOption, cacheVal.cacheOption, cacheOption);
  2630. // 如果超时则删除并返回 null
  2631. if (timeout !== TimeoutInfinite && Date.now() - timeStart > timeout) {
  2632. this.del(key);
  2633. return null;
  2634. }
  2635. try {
  2636. return deserialize(cacheVal.val);
  2637. }
  2638. catch (e) {
  2639. this.del(key);
  2640. return null;
  2641. }
  2642. }
  2643. /**
  2644. * 根据 key 获取并刷新超时时间
  2645. * @param key 指定的 key
  2646. * @param cacheOption 获取的选项
  2647. * @returns 获取到的缓存值
  2648. * @override
  2649. */
  2650. touch(key, cacheOption = {}) {
  2651. const str = this.localStorage.getItem(key);
  2652. /**
  2653. * @type {CacheVal}
  2654. */
  2655. const cacheVal = safeExec(JSON.parse, null, str);
  2656. if (cacheVal === null) {
  2657. return null;
  2658. }
  2659. /**
  2660. * @type {ICacheOption}
  2661. */
  2662. const option = assign(this.cacheOption, cacheVal.cacheOption, cacheOption);
  2663. const { timeStart, timeout, deserialize } = option;
  2664. // 如果超时则删除并返回 null
  2665. if (timeout !== TimeoutInfinite && Date.now() - timeStart > timeout) {
  2666. this.del(key);
  2667. return null;
  2668. }
  2669. try {
  2670. const result = deserialize(cacheVal.val);
  2671. this.set(key, result, assign(option, { timeStart: Date.now() }));
  2672. return result;
  2673. }
  2674. catch (e) {
  2675. this.del(key);
  2676. return null;
  2677. }
  2678. }
  2679. }
  2680.  
  2681. /**
  2682. * 默认使用的 {@link ICache} 接口的缓存实现
  2683. */
  2684. const cache = new LocalStorageCache();
  2685. /**
  2686. * 缓存工具类
  2687. * 主要实现缓存高阶函数的封装
  2688. */
  2689. class CacheUtil {
  2690. /**
  2691. * 将指定函数包装为只调用一次为缓存函数
  2692. * @param fn 需要包装的函数
  2693. * @returns 包装后的函数
  2694. */
  2695. static once(fn, { identity = fn.toString(), timeout = TimeoutInfinite, } = {}) {
  2696. const generateKey = () => `CacheUtil.onceOfSameParam-${identity}`;
  2697. const innerFn = new Proxy(fn, {
  2698. apply(_, _this, args) {
  2699. const key = generateKey();
  2700. const cacheOption = { timeout };
  2701. const val = cache.get(key);
  2702. if (val !== null) {
  2703. return val;
  2704. }
  2705. return compatibleAsync(Reflect.apply(_, _this, args), res => {
  2706. cache.set(key, res, cacheOption);
  2707. return res;
  2708. });
  2709. },
  2710. });
  2711. return Object.assign(innerFn, {
  2712. origin: fn,
  2713. clear() {
  2714. cache.del(generateKey());
  2715. },
  2716. });
  2717. }
  2718. /**
  2719. * 包裹函数为缓存函数
  2720. * @param fn 一个接受一些参数并返回结果的函数
  2721. * @param options 缓存选项对象。可选项
  2722. * @param options.identity 缓存标识。默认为函数 {@link toString},但有时候不太可行(继承自基类的函数)
  2723. * @param options.timeout 缓存时间。默认为无限
  2724. * @returns 带有缓存功能的函数
  2725. */
  2726. static onceOfSameParam(fn, { identity = fn.toString(), timeout = TimeoutInfinite, } = {}) {
  2727. const generateKey = (args) => `CacheUtil.onceOfSameParam-${identity}-${JSON.stringify(args)}`;
  2728. const innerFn = new Proxy(fn, {
  2729. apply(_, _this, args) {
  2730. const key = generateKey(args);
  2731. const cacheOption = { timeout };
  2732. const val = cache.get(key);
  2733. if (val !== null) {
  2734. return val;
  2735. }
  2736. return compatibleAsync(Reflect.apply(_, _this, args), res => {
  2737. cache.set(key, res, cacheOption);
  2738. return res;
  2739. });
  2740. },
  2741. });
  2742. return Object.assign(innerFn, {
  2743. origin: fn,
  2744. clear(...args) {
  2745. cache.del(generateKey(args));
  2746. },
  2747. });
  2748. }
  2749. }
  2750. /**
  2751. * 导出一个默认的缓存工具对象
  2752. * @deprecated 已废弃,请直接使用类的静态函数
  2753. */
  2754. const cacheUtil = CacheUtil;
  2755.  
  2756. /**
  2757. * 一个空的函数
  2758. * @param args 接受任何参数
  2759. */
  2760. function emptyFunc(...args) { }
  2761.  
  2762. /**
  2763. * 禁止他人调试网站相关方法的集合对象
  2764. */
  2765. class AntiDebug {
  2766. /**
  2767. * 不停循环 debugger 防止有人调试代码
  2768. * @returns 取消函数
  2769. */
  2770. static cyclingDebugger() {
  2771. const res = setInterval(() => {
  2772. debugger;
  2773. }, 100);
  2774. return () => clearInterval(res);
  2775. }
  2776. /**
  2777. * 检查是否正在 debugger 并调用回调函数
  2778. * @param fn 回调函数,默认为重载页面
  2779. * @returns 取消函数
  2780. */
  2781. static checkDebug(fn = () => window.location.reload()) {
  2782. const res = setInterval(() => {
  2783. const diff = timing(() => {
  2784. for (let i = 0; i < 1000; i++) {
  2785. console.log(i);
  2786. console.clear();
  2787. }
  2788. });
  2789. if (diff > 500) {
  2790. console.log(diff);
  2791. fn();
  2792. }
  2793. }, 1000);
  2794. return () => clearInterval(res);
  2795. }
  2796. /**
  2797. * 禁用控制台调试输出
  2798. * @returns 取消函数
  2799. */
  2800. static disableConsoleOutput() {
  2801. if (!window.console) {
  2802. return emptyFunc;
  2803. }
  2804. const map = arrayToMap(Object.keys(console), returnItself, k => {
  2805. // @ts-ignore
  2806. const temp = console[k];
  2807. // @ts-ignore
  2808. console[k] = emptyFunc;
  2809. return temp;
  2810. });
  2811. return () => {
  2812. for (const [k, v] of map) {
  2813. // @ts-ignore
  2814. console[k] = v;
  2815. }
  2816. };
  2817. }
  2818. }
  2819. /**
  2820. * 禁止他人调试网站相关方法的集合对象
  2821. * @deprecated 已废弃,请直接使用类的静态函数
  2822. */
  2823. const antiDebug = AntiDebug;
  2824.  
  2825. /**
  2826. * 判断一个字符串是否为空白的字符串
  2827. * @param str 字符串
  2828. * @returns 是否为空字符串
  2829. * @deprecated 已废弃,请使用 {@link stringValidator#isBlank}
  2830. */
  2831. function isBlank(str) {
  2832. return stringValidator.isBlank(str);
  2833. }
  2834.  
  2835. /**
  2836. * 判断一个字符串是否为空字符串
  2837. * @param str 字符串
  2838. * @returns 是否为空字符串
  2839. * @deprecated 已废弃,请使用 {@link stringValidator#isEmpty}
  2840. */
  2841. function isEmpty(str) {
  2842. return stringValidator.isEmpty(str);
  2843. }
  2844.  
  2845. /**
  2846. * 加载一个远程脚本文件
  2847. * @param src 远程脚本路径
  2848. * @returns 等待异步加载脚本完成
  2849. */
  2850. function loadScript(src) {
  2851. return new Promise((resolve, reject) => {
  2852. const script = document.createElement('script');
  2853. script.src = src;
  2854. script.addEventListener('load', () => resolve());
  2855. script.addEventListener('error', reject);
  2856. document.body.appendChild(script);
  2857. });
  2858. }
  2859.  
  2860. /**
  2861. * 内部使用的函数
  2862. * 注: 如果谓词中包含任意一个异步(返回 Promise)函数,则整个返回结果将变成异步的,否则默认为同步操作.
  2863. * @param fns 谓词数组
  2864. * @param args 谓词应用的参数列表
  2865. * @param condition 临界条件
  2866. * @returns 返回结果
  2867. */
  2868. function _inner(fns, args, condition) {
  2869. const fn = fns[0];
  2870. const res = fn(...args);
  2871. function _call(res) {
  2872. if (condition(res)) {
  2873. return res;
  2874. }
  2875. const others = fns.slice(1);
  2876. if (others.length === 0) {
  2877. return res;
  2878. }
  2879. return _inner(others, args, condition);
  2880. }
  2881. return compatibleAsync(res, _call);
  2882. }
  2883. /**
  2884. * 连接谓词函数
  2885. */
  2886. class CombinedPredicate {
  2887. /**
  2888. * 使用 && 进行连接
  2889. * @param fns 连接任意多个谓词
  2890. * @returns 连接后的新谓词
  2891. */
  2892. static and(...fns) {
  2893. return function (...args) {
  2894. return _inner(fns, args, res => !res);
  2895. };
  2896. }
  2897. /**
  2898. * 使用 || 进行连接
  2899. * @param fns 连接任意多个谓词
  2900. * @returns 连接后的新谓词
  2901. */
  2902. static or(...fns) {
  2903. return function (...args) {
  2904. return _inner(fns, args, res => res);
  2905. };
  2906. }
  2907. /**
  2908. * 对谓词进行取反
  2909. * @param fn 谓词
  2910. * @returns 取反后的谓词
  2911. */
  2912. static not(fn) {
  2913. return new Proxy(fn, {
  2914. apply(_, _this, args) {
  2915. return compatibleAsync(Reflect.apply(_, this, args), res => !res);
  2916. },
  2917. });
  2918. }
  2919. }
  2920.  
  2921. /**
  2922. * 将一个谓词函数取反
  2923. * 如果是同步函数,则返回的函数也是同步的,否则返回的是取反后的异步函数
  2924. * @param fn 要取反的函数
  2925. * @returns 取反得到的函数
  2926. * @deprecated 已废弃,请使用 {@link CombinedPredicate.not} 进行为此取反
  2927. */
  2928. function deny(fn) {
  2929. return CombinedPredicate.not(fn);
  2930. }
  2931.  
  2932. /**
  2933. * 数组校验器
  2934. */
  2935. class ArrayValidator {
  2936. /**
  2937. * 是否为空数组
  2938. * @param array 空数组
  2939. * @returns 是否为空数组
  2940. */
  2941. static isEmpty(array) {
  2942. return (isNullOrUndefined(array) ||
  2943. !(array instanceof Array) ||
  2944. array.length === 0);
  2945. }
  2946. }
  2947. /**
  2948. * 导出一个默认的数组校验对象
  2949. * @deprecated 已废弃,请直接使用类的静态函数
  2950. */
  2951. const arrayValidator = ArrayValidator;
  2952.  
  2953. /**
  2954. * 路径工具
  2955. */
  2956. class PathUtil {
  2957. /**
  2958. * 拼接多个路径
  2959. *
  2960. * @param paths 路径数组
  2961. * @return {String} 拼接完成的路径
  2962. */
  2963. static join(...paths) {
  2964. return paths.reduce(PathUtil._join);
  2965. }
  2966. /**
  2967. * 拼接两个路径
  2968. *
  2969. * @param pathStart 开始路径
  2970. * @param pathEnd 结束路径
  2971. * @return {String} 拼接完成的两个路径
  2972. */
  2973. static _join(pathStart, pathEnd) {
  2974. if (pathStart.endsWith(PathUtil.Separator)) {
  2975. return (pathStart + pathEnd).replace(PathUtil.Separator + PathUtil.Separator, PathUtil.Separator);
  2976. }
  2977. if (pathEnd.startsWith(PathUtil.Separator)) {
  2978. return pathStart + pathEnd;
  2979. }
  2980. return pathStart + PathUtil.Separator + pathEnd;
  2981. }
  2982. }
  2983. /**
  2984. * 路径分隔符
  2985. */
  2986. PathUtil.Separator = '/';
  2987. /**
  2988. * 导出一个路径工具类
  2989. * @deprecated 已废弃,请直接使用类的静态函数
  2990. */
  2991. const pathUtil = PathUtil;
  2992.  
  2993. /**
  2994. * 自定义的日志类
  2995. * 主要便于在开发环境下正常显示调试信息,在生产环境则默认关闭它
  2996. */
  2997. class Logger {
  2998. /**
  2999. * 构造函数
  3000. * @param options 可选项
  3001. * @param options.enable 是否开启日志
  3002. */
  3003. constructor({ enable = true } = {}) {
  3004. this.debug = console.debug;
  3005. this.error = console.error;
  3006. this.info = console.info;
  3007. this.log = console.log;
  3008. this.warn = console.warn;
  3009. this.dir = console.dir;
  3010. this.dirxml = console.dirxml;
  3011. this.table = console.table;
  3012. this.trace = console.trace;
  3013. this.group = console.group;
  3014. this.groupCollapsed = console.groupCollapsed;
  3015. this.groupEnd = console.groupEnd;
  3016. this.clear = console.clear;
  3017. this.count = console.count;
  3018. this.assert = console.assert;
  3019. this.profile = console.profile;
  3020. this.profileEnd = console.profileEnd;
  3021. this.time = console.time;
  3022. this.timeEnd = console.timeEnd;
  3023. this.timeStamp = console.timeStamp;
  3024. this._enable = enable;
  3025. }
  3026. /**
  3027. * 设置 enable 的 setter 属性,在改变时合并对应的子类对象实现
  3028. * @param enable 是否开启
  3029. */
  3030. set enable(enable) {
  3031. /**
  3032. * @field 是否开启全局控制台,该属性只写
  3033. */
  3034. this._enable = enable;
  3035. Object.keys(console).forEach(
  3036. // @ts-ignore
  3037. k => (this[k] = enable ? console[k] : emptyFunc));
  3038. }
  3039. }
  3040. /**
  3041. * 导出一个全局可用的 Logger 对象
  3042. * 使用 enable 属性控制是否开启日志输出,默认为 true
  3043. */
  3044. const logger = new Logger();
  3045.  
  3046. /**
  3047. * 将 Object 对象 转换为 Map
  3048. * @param obj Object 对象
  3049. * @returns 转换得到的 Map 键值表
  3050. */
  3051. function objectToMap(obj) {
  3052. return Reflect.ownKeys(obj).reduce((map, k) => map.set(k, Reflect.get(obj, k)), new Map());
  3053. }
  3054.  
  3055. /**
  3056. * 将列表转换为树节点
  3057. * 注: 该函数默认树的根节点只有一个,如果有多个,则返回一个数组
  3058. * @param list 树节点列表
  3059. * @param options 其他选项
  3060. * @returns 树节点,或是树节点列表
  3061. */
  3062. function listToTree(list, { bridge = returnItself, isRoot = node => !node.parentId, } = {}) {
  3063. const arr = [];
  3064. const res = list.reduce((root, _sub) => {
  3065. const sub = bridge(_sub);
  3066. if (isRoot(sub)) {
  3067. root.push(sub);
  3068. return root;
  3069. }
  3070. for (const _parent of list) {
  3071. const parent = bridge(_parent);
  3072. if (sub.parentId === parent.id) {
  3073. parent.child = parent.child || [];
  3074. parent.child.push(sub);
  3075. return root;
  3076. }
  3077. }
  3078. return root;
  3079. }, arr);
  3080. // 根据顶级节点的数量决定如何返回
  3081. const len = res.length;
  3082. if (len === 0)
  3083. return {};
  3084. if (len === 1)
  3085. return res[0];
  3086. return res;
  3087. }
  3088.  
  3089. /**
  3090. * 桥接对象不存在的字段
  3091. * @param map 代理的字段映射 Map
  3092. * @returns 转换一个对象为代理对象
  3093. */
  3094. function bridge(map) {
  3095. /**
  3096. * 为对象添加代理的函数
  3097. * @param obj 任何对象
  3098. * @returns 代理后的对象
  3099. */
  3100. return function (obj) {
  3101. return new Proxy(obj, {
  3102. get(_, k) {
  3103. if (Reflect.has(map, k)) {
  3104. return Reflect.get(_, Reflect.get(map, k));
  3105. }
  3106. return Reflect.get(_, k);
  3107. },
  3108. set(_, k, v) {
  3109. if (Reflect.has(map, k)) {
  3110. Reflect.set(_, Reflect.get(map, k), v);
  3111. return true;
  3112. }
  3113. Reflect.set(_, k, v);
  3114. return true;
  3115. },
  3116. });
  3117. };
  3118. }
  3119.  
  3120. /**
  3121. * 遍历并映射一棵树的每个节点
  3122. * @param root 树节点
  3123. * @param options 其他选项
  3124. * @returns 递归遍历后的树节点
  3125. */
  3126. function treeMapping(root, { before = returnItself, after = returnItself, paramFn = (node, ...args) => [], } = {}) {
  3127. /**
  3128. * 遍历一颗完整的树
  3129. * @param node 要遍历的树节点
  3130. * @param args 每次递归遍历时的参数
  3131. */
  3132. function _treeMapping(node, ...args) {
  3133. // 之前的操作
  3134. const _node = before(node, ...args);
  3135. const _child = _node.child;
  3136. if (!arrayValidator.isEmpty(_child)) {
  3137. _node.child = _child.map(v =>
  3138. // 产生一个参数
  3139. _treeMapping(v, ...paramFn(_node, ...args)));
  3140. }
  3141. // 之后的操作
  3142. return after(_node, ...args);
  3143. }
  3144. return _treeMapping(root);
  3145. }
  3146.  
  3147. /**
  3148. * 将树节点转为树节点列表
  3149. * @param root 树节点
  3150. * @param options 其他选项
  3151. * @returns 树节点列表
  3152. */
  3153. function treeToList(root, { calcPath = false, bridge = returnItself, } = {}) {
  3154. const res = [];
  3155. // @ts-ignore
  3156. treeMapping(root, {
  3157. before(_node, parentPath) {
  3158. const node = bridge(_node);
  3159. // 是否计算全路径
  3160. if (calcPath) {
  3161. node.path = (parentPath ? parentPath + ',' : '') + node.id;
  3162. }
  3163. // 此时追加到数组中
  3164. res.push(node);
  3165. return node;
  3166. },
  3167. paramFn: node => (calcPath ? [node.path] : []),
  3168. });
  3169. return res;
  3170. }
  3171.  
  3172. /**
  3173. * 树节点桥接工具类
  3174. * 主要实现了桥接 {@field bridge} {@field bridgeTree} 和 {@field bridgeList} 三个函数,事实上桥接之后再转换相当于做了两遍,但就目前而言暂且只能如此了
  3175. */
  3176. class NodeBridgeUtil {
  3177. /**
  3178. * 桥接对象为标准的树结构
  3179. * @param nodeBridge 桥接对象
  3180. * @returns 代理函数
  3181. */
  3182. static bridge({ id = 'id', parentId = 'parentId', child = 'child', path = 'path', } = {}) {
  3183. return bridge({
  3184. id,
  3185. parentId,
  3186. child,
  3187. path,
  3188. });
  3189. }
  3190. /**
  3191. * 桥接一棵完整的树
  3192. * @param tree 树节点
  3193. * @param nodeBridge 桥接对象
  3194. * @returns 代理后的树对象
  3195. */
  3196. static bridgeTree(tree, nodeBridge) {
  3197. return treeMapping(tree, {
  3198. before: this.bridge(nodeBridge),
  3199. });
  3200. }
  3201. /**
  3202. * 桥接一个树节点列表
  3203. * @param list 树节点列表
  3204. * @param nodeBridge 桥接对象
  3205. * @returns 代理后的树节点列表
  3206. */
  3207. static bridgeList(list, nodeBridge) {
  3208. return list.map(this.bridge(nodeBridge));
  3209. }
  3210. }
  3211. /**
  3212. * 导出一个 NodeBridgeUtil 的实例
  3213. * @deprecated 已废弃,请直接使用类的静态函数
  3214. */
  3215. const nodeBridgeUtil = NodeBridgeUtil;
  3216.  
  3217. /**
  3218. * 获取对象中所有的属性及对应的值,包括 ES6 新增的 Symbol 类型的属性
  3219. * @param obj 任何对象
  3220. * @returns 属性及其对应值的二维数组
  3221. */
  3222. function getObjectEntries(obj) {
  3223. const mFn = k => [
  3224. k,
  3225. Reflect.get(obj, k),
  3226. ];
  3227. return Reflect.ownKeys(obj).map(mFn);
  3228. }
  3229.  
  3230. /**
  3231. * 比较两个浮点数是否相等
  3232. * 具体实现采用差值取绝对值并与 {@link Number.EPSILON} 比较的方式,如果小于浮点数最小差值,则认为它们是 [相等] 的
  3233. * @param num1 第一个浮点数
  3234. * @param num2 第二个浮点数
  3235. * @returns 两数是否相等
  3236. */
  3237. function floatEquals(num1, num2) {
  3238. return Math.abs(num1 - num2) < Number.EPSILON;
  3239. }
  3240.  
  3241. /**
  3242. * 根据不同的源对象获取不同的正则匹配,代表不需要拷贝的属性
  3243. * @param source 源对象
  3244. * @returns 匹配内部属性的正则表达式
  3245. */
  3246. function getInnerFieldRule(source) {
  3247. if (source instanceof Function) {
  3248. return /^(?:constructor|prototype|arguments|caller|name|bind|call|apply|toString|length)$/;
  3249. }
  3250. else {
  3251. return /^(?:toString|length)$/;
  3252. }
  3253. }
  3254. /**
  3255. * 拷贝对象的属性到目标对象上
  3256. * @param target 目标对象
  3257. * @param source 源对象
  3258. * @returns 返回 {@param target} 目标对象
  3259. */
  3260. function _copyProps(target, source) {
  3261. const innerField = getInnerFieldRule(source);
  3262. Reflect.ownKeys(source).forEach(prop => {
  3263. if (typeof prop === 'string' && innerField.test(prop)) {
  3264. return;
  3265. }
  3266. Reflect.set(target, prop, Reflect.get(source, prop));
  3267. });
  3268. return target;
  3269. }
  3270. /**
  3271. * 混合多个类
  3272. * @param {...Class} mixins 需要混合的多个类及其构造函数参数映射函数的 Map 集合
  3273. * @returns 返回一个混合后的类,构造函数将的参数
  3274. */
  3275. function aggregation(mixins) {
  3276. const arr = Array.from(mixins);
  3277. class Aggregate {
  3278. /**
  3279. * @param args 任意数量的参数
  3280. */
  3281. constructor(...args) {
  3282. arr.forEach(([Mixin, fn = returnItself]) => _copyProps(this, new Mixin(...fn(args))));
  3283. }
  3284. }
  3285. arr.forEach(([Mixin]) => {
  3286. _copyProps(Aggregate.prototype, Mixin.prototype);
  3287. _copyProps(Aggregate, Mixin);
  3288. });
  3289. return Aggregate;
  3290. }
  3291.  
  3292. /**
  3293. * 包装一个异步函数为有限制并发功能的函数
  3294. * @param fn 异步函数
  3295. * @param options 可选参数
  3296. * @param options.limit 并发限制数量,默认为 1
  3297. * @returns 返回被包装后的限制并发功能的函数
  3298. */
  3299. function asyncLimiting(fn, { limit = 1 } = {}) {
  3300. // 当前正在执行异步的数量
  3301. let execCount = 0;
  3302. // waitArr 等待的队列
  3303. const takeQueue = [];
  3304. // 是否增加了 execCount 的标记
  3305. let flag = false;
  3306. return new Proxy(fn, {
  3307. apply(_, _this, args) {
  3308. return __awaiter(this, void 0, void 0, function* () {
  3309. const _takeRun = () => __awaiter(this, void 0, void 0, function* () {
  3310. if (!flag) {
  3311. execCount++;
  3312. flag = false;
  3313. }
  3314. const tempArgs = takeQueue.shift();
  3315. try {
  3316. return yield Reflect.apply(_, _this, tempArgs);
  3317. }
  3318. finally {
  3319. execCount--;
  3320. }
  3321. });
  3322. takeQueue.push(args);
  3323. yield wait(() => {
  3324. const result = execCount < limit;
  3325. // 如果等待结束则必须立刻增加 execCount,避免微任务与宏任务调度可能产生错误
  3326. if (result) {
  3327. flag = true;
  3328. execCount++;
  3329. }
  3330. return result;
  3331. });
  3332. return _takeRun();
  3333. });
  3334. },
  3335. });
  3336. }
  3337.  
  3338. /**
  3339. * 默认的超时时间,可以认为是无限
  3340. */
  3341. const TimeoutInfinity = () => false;
  3342. /**
  3343. * 创建一个 Lock 对象,用于锁住当前的当前的异步流程
  3344. */
  3345. class Locker {
  3346. /**
  3347. * @param options 可选项
  3348. * @param options.limit 限制并发数量,默认为 1
  3349. * @param options.timeout 超时时间,默认为无限
  3350. */
  3351. constructor({ limit = 1, timeout } = {}) {
  3352. this.limit = limit;
  3353. this.timeout = timeout || TimeoutInfinity;
  3354. }
  3355. /**
  3356. * 当前是否锁住了
  3357. * @returns 是否锁住了
  3358. */
  3359. isLocked() {
  3360. return this.limit <= 0;
  3361. }
  3362. /**
  3363. * 添加异步锁
  3364. * @param timeout 超时时间,默认为全局 timeout
  3365. * @returns 进行等待
  3366. */
  3367. lock(timeout = this.timeout) {
  3368. return __awaiter(this, void 0, void 0, function* () {
  3369. if (this.isLocked()) {
  3370. /**
  3371. * @type {Number|Function}
  3372. */
  3373. yield Promise.race([
  3374. wait(() => {
  3375. const result = !this.isLocked();
  3376. if (result) {
  3377. this.limit--;
  3378. }
  3379. return result;
  3380. }),
  3381. wait(timeout),
  3382. ]);
  3383. }
  3384. else {
  3385. this.limit--;
  3386. }
  3387. });
  3388. }
  3389. /**
  3390. * 删除异步锁
  3391. */
  3392. unlock() {
  3393. this.limit++;
  3394. }
  3395. }
  3396.  
  3397. /**
  3398. * 包装一个函数为有错误重试功能的函数
  3399. * 注: 如果发生错误,最终将抛出最后一次调用的异常
  3400. * @param fn 需要被包装的函数
  3401. * @param num 调用的次数。默认为 1
  3402. * @param errorCheck 检查返回结果是否需要重试的函数。默认只要 resolve() 就返回 true
  3403. * @returns 包装后的有错误重试功能的函数
  3404. */
  3405. function trySometime(fn, num = 1, errorCheck = res => true) {
  3406. return new Proxy(fn, {
  3407. apply(target, thisArg, args) {
  3408. return __awaiter(this, void 0, void 0, function* () {
  3409. let err;
  3410. for (let i = 0; i < num; i++) {
  3411. try {
  3412. // 等待结果出来
  3413. const res = yield Reflect.apply(target, thisArg, args);
  3414. // 如果没问题就直接返回了
  3415. if (errorCheck(res) === true) {
  3416. return res;
  3417. }
  3418. // 否则抛出异常以进行下一次重试
  3419. throw res;
  3420. }
  3421. catch (error) {
  3422. err = error;
  3423. }
  3424. }
  3425. throw err;
  3426. });
  3427. },
  3428. });
  3429. }
  3430.  
  3431. /**
  3432. * 包装一个函数为有错误重试功能的函数
  3433. * 注意: 该函数是并行运行,所以一旦调用,就会同时调用 n 次,不管之前有没有失败。。。此函数不适合包装有副作用的操作,例如修改用户信息,请使用 {@link trySometime} 替代
  3434. * @param fn 需要被包装的函数
  3435. * @param num 调用的次数。默认为 1
  3436. * @param errorCheck 检查返回结果是否需要重试的函数。默认只要 resolve() 就返回 true
  3437. * @returns 包装后的有错误重试功能的函数
  3438. */
  3439. function trySometimeParallel(fn, num = 1, errorCheck = res => true) {
  3440. return new Proxy(fn, {
  3441. apply(target, thisArg, args) {
  3442. return __awaiter(this, void 0, void 0, function* () {
  3443. return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
  3444. let err;
  3445. try {
  3446. yield Promise.all(range(0, num).map(() => __awaiter(this, void 0, void 0, function* () {
  3447. try {
  3448. const res = yield Reflect.apply(target, thisArg, args);
  3449. if (errorCheck(res) === true) {
  3450. resolve(res);
  3451. }
  3452. throw res;
  3453. }
  3454. catch (error) {
  3455. err = error;
  3456. }
  3457. })));
  3458. }
  3459. catch (error) {
  3460. console.log(error);
  3461. }
  3462. reject(err);
  3463. }));
  3464. });
  3465. },
  3466. });
  3467. }
  3468.  
  3469. /**
  3470. * 深度比较两个对象是否相等
  3471. * @param x 任何对象
  3472. * @param y 任何对象
  3473. * @returns 是否相等
  3474. */
  3475. function compare(x, y) {
  3476. if ((typeof x === 'number' || x instanceof Number) &&
  3477. (typeof y === 'number' || y instanceof Number)) {
  3478. const _x = +x;
  3479. const _y = +y;
  3480. // 如果都是 NaN 则直接返回 true
  3481. if (isNaN(_x) && isNaN(_y)) {
  3482. return true;
  3483. }
  3484. // 如果是 -0/+0 则返回 false
  3485. if (_x === _y) {
  3486. return 1 / _x === 1 / _y;
  3487. }
  3488. // 如果均为数字且两数之差的绝对值小于浮点数的最小精度(此举主要是为了避免浮点数的精度丢失)
  3489. if (Math.abs(_x - _y) < Number.EPSILON) {
  3490. return true;
  3491. }
  3492. }
  3493. // 如果恒等表达式成立则直接返回 true
  3494. if (x === y) {
  3495. return true;
  3496. }
  3497. // 比较正则和字符串
  3498. if ((x instanceof RegExp && y instanceof RegExp) ||
  3499. ((typeof x === 'string' || x instanceof String) &&
  3500. (typeof y === 'string' || y instanceof String))) {
  3501. return x.toString() === y.toString();
  3502. }
  3503. // 如果都是时间则比较它们的时间戳
  3504. if (x instanceof Date && y instanceof Date) {
  3505. return x.getTime() === y.getTime();
  3506. }
  3507. // 如果两者有一个不是 Object 类型的话则直接返回 false
  3508. // 注: 此处直接返回 false 是因为特殊原生类型的都在上面处理过了
  3509. // 注: Array 可以按照 Object 的逻辑进行处理
  3510. if (!(x instanceof Object && y instanceof Object)) {
  3511. return false;
  3512. }
  3513. // 比较它们的原型
  3514. if (x.prototype !== y.prototype) {
  3515. return false;
  3516. }
  3517. // 比较构造函数
  3518. if (x.constructor !== y.constructor) {
  3519. return false;
  3520. }
  3521. // 比较 y 中的属性是否全部都在 x 中
  3522. for (const p of Reflect.ownKeys(y)) {
  3523. if (!Reflect.has(x, p)) {
  3524. return false;
  3525. }
  3526. }
  3527. // 比较 x 中的属性是否全部都在 y 中
  3528. for (const p of Reflect.ownKeys(x)) {
  3529. if (!Reflect.has(y, p)) {
  3530. return false;
  3531. }
  3532. // 比较每个元素的类型,如果不同则直接返回 false
  3533. if (typeof y[p] !== typeof x[p]) {
  3534. return false;
  3535. }
  3536. // 递归比较每个元素
  3537. if (!compare(x[p], y[p])) {
  3538. return false;
  3539. }
  3540. }
  3541. // 全部比较完成仍然没有结果就返回 true
  3542. return true;
  3543. }
  3544.  
  3545. /**
  3546. * 阻塞主线程指定时间
  3547. * 注: 和 {@see wait} 不同,该函数会真的阻塞住主线程,即这段时间内其他的代码都无法执行,例如用户的点击事件!
  3548. * @param time 阻塞毫秒数
  3549. */
  3550. function sleep(time) {
  3551. const end = performance.now() + time;
  3552. while (performance.now() <= end) { }
  3553. }
  3554.  
  3555. /**
  3556. * 操作类型
  3557. */
  3558. var ActionType;
  3559. (function (ActionType) {
  3560. ActionType["forEach"] = "forEach";
  3561. ActionType["filter"] = "filter";
  3562. ActionType["map"] = "map";
  3563. ActionType["flatMap"] = "flatMap";
  3564. ActionType["sort"] = "sort";
  3565. ActionType["reduce"] = "reduce";
  3566. ActionType["reduceRight"] = "reduceRight";
  3567. ActionType["findIndex"] = "findIndex";
  3568. ActionType["find"] = "find";
  3569. ActionType["every"] = "every";
  3570. ActionType["some"] = "some";
  3571. ActionType["parallel"] = "parallel";
  3572. ActionType["serial"] = "serial";
  3573. })(ActionType || (ActionType = {}));
  3574. /**
  3575. * 保存高阶函数传入的异步操作
  3576. * @field 异步操作的类型
  3577. * @field 异步操作
  3578. */
  3579. class Action {
  3580. constructor(type, args) {
  3581. this.type = type;
  3582. this.args = args;
  3583. this.type = type;
  3584. this.args = args;
  3585. }
  3586. }
  3587. Action.Type = ActionType;
  3588. /**
  3589. * 抽象异步数组,实现了一些公共的函数
  3590. */
  3591. class InnerBaseAsyncArray {
  3592. /**
  3593. * 构造函数
  3594. * @param args 数组初始元素
  3595. */
  3596. constructor(args = []) {
  3597. this._arr = args;
  3598. }
  3599. /**
  3600. * 将整个数组排序
  3601. * @param fn 比较函数
  3602. * @returns 排序后的数组
  3603. */
  3604. sort(fn) {
  3605. return __awaiter(this, void 0, void 0, function* () {
  3606. if (fn === undefined) {
  3607. return new InnerAsyncArray(this._arr.sort());
  3608. }
  3609. const arr = this._arr.map((v, i) => [v, i]);
  3610. function _sort(arr, fn) {
  3611. return __awaiter(this, void 0, void 0, function* () {
  3612. // 边界条件,如果传入数组的值
  3613. if (arr.length <= 1) {
  3614. return arr;
  3615. }
  3616. // 根据中间值对数组分治为两个数组
  3617. const medianIndex = Math.floor(arr.length / 2);
  3618. const medianValue = arr[medianIndex];
  3619. const left = [];
  3620. const right = [];
  3621. for (let i = 0, len = arr.length; i < len; i++) {
  3622. if (i === medianIndex) {
  3623. continue;
  3624. }
  3625. const v = arr[i];
  3626. if ((yield fn(v, medianValue)) <= 0) {
  3627. left.push(v);
  3628. }
  3629. else {
  3630. right.push(v);
  3631. }
  3632. }
  3633. return (yield _sort(left, fn))
  3634. .concat([medianValue])
  3635. .concat(yield _sort(right, fn));
  3636. });
  3637. }
  3638. return new InnerAsyncArray(yield (yield _sort(arr, ([t1], [t2]) => fn(t1, t2))).map(([_v, i]) => this._arr[i]));
  3639. });
  3640. }
  3641. /**
  3642. * 异步的 find
  3643. * @param fn 异步查询函数
  3644. * @returns 查询到的第一个值
  3645. */
  3646. find(fn) {
  3647. return __awaiter(this, void 0, void 0, function* () {
  3648. const i = yield this.findIndex(fn);
  3649. return i === -1 ? null : this._arr[i];
  3650. });
  3651. }
  3652. /**
  3653. * 异步的 every
  3654. * @param fn 异步匹配函数
  3655. * @returns 是否全部匹配
  3656. */
  3657. every(fn) {
  3658. return __awaiter(this, void 0, void 0, function* () {
  3659. return (yield this.findIndex(CombinedPredicate.not(fn))) === -1;
  3660. });
  3661. }
  3662. /**
  3663. * 异步的 some
  3664. * @param fn 异步匹配函数
  3665. * @returns 是否有任意一个匹配
  3666. */
  3667. some(fn) {
  3668. return __awaiter(this, void 0, void 0, function* () {
  3669. return (yield this.findIndex(fn)) !== -1;
  3670. });
  3671. }
  3672. /**
  3673. * 转换为并发异步数组
  3674. */
  3675. parallel() {
  3676. return new InnerAsyncArrayParallel(this._arr);
  3677. }
  3678. /**
  3679. * 转换为顺序异步数组
  3680. */
  3681. serial() {
  3682. return new InnerAsyncArray(this._arr);
  3683. }
  3684. /**
  3685. * 获取内部数组的值,将返回一个浅复制的数组
  3686. */
  3687. value() {
  3688. return this._arr.slice();
  3689. }
  3690. }
  3691. /**
  3692. * 串行的异步数组
  3693. */
  3694. class InnerAsyncArray extends InnerBaseAsyncArray {
  3695. constructor(args) {
  3696. super(args);
  3697. }
  3698. forEach(fn) {
  3699. return __awaiter(this, void 0, void 0, function* () {
  3700. for (let i = 0, len = this._arr.length; i < len; i++) {
  3701. yield fn.call(this, this._arr[i], i, this);
  3702. }
  3703. });
  3704. }
  3705. filter(fn) {
  3706. return __awaiter(this, void 0, void 0, function* () {
  3707. const res = new InnerAsyncArray();
  3708. for (let i = 0, len = this._arr.length; i < len; i++) {
  3709. if (yield fn.call(this, this._arr[i], i, this)) {
  3710. res._arr.push(this._arr[i]);
  3711. }
  3712. }
  3713. return res;
  3714. });
  3715. }
  3716. map(fn) {
  3717. return __awaiter(this, void 0, void 0, function* () {
  3718. const res = new InnerAsyncArray();
  3719. for (let i = 0, len = this._arr.length; i < len; i++) {
  3720. res._arr.push(yield fn.call(this, this._arr[i], i, this));
  3721. }
  3722. return res;
  3723. });
  3724. }
  3725. flatMap(fn) {
  3726. return __awaiter(this, void 0, void 0, function* () {
  3727. const res = new InnerAsyncArray();
  3728. for (let i = 0, len = this._arr.length; i < len; i++) {
  3729. res._arr.push(...(yield fn.call(this, this._arr[i], i, this)));
  3730. }
  3731. return res;
  3732. });
  3733. }
  3734. reduce(fn, res) {
  3735. return __awaiter(this, void 0, void 0, function* () {
  3736. for (let i = 0, len = this._arr.length; i < len; i++) {
  3737. if (res) {
  3738. res = yield fn.call(this, res, this._arr[i], i, this);
  3739. }
  3740. else {
  3741. res = this._arr[i];
  3742. }
  3743. }
  3744. return res;
  3745. });
  3746. }
  3747. reduceRight(fn, res) {
  3748. return __awaiter(this, void 0, void 0, function* () {
  3749. for (let i = this._arr.length - 1; i >= 0; i--) {
  3750. if (res) {
  3751. res = yield fn.apply(this, [res, this._arr[i], i, this]);
  3752. }
  3753. else {
  3754. res = this._arr[i];
  3755. }
  3756. }
  3757. return res;
  3758. });
  3759. }
  3760. findIndex(fn) {
  3761. return __awaiter(this, void 0, void 0, function* () {
  3762. for (let i = 0, len = this._arr.length; i < len; i++) {
  3763. const res = yield fn.call(this, this._arr[i], i, this);
  3764. if (res) {
  3765. return i;
  3766. }
  3767. }
  3768. return -1;
  3769. });
  3770. }
  3771. }
  3772. /**
  3773. * 并发的异步数组
  3774. */
  3775. class InnerAsyncArrayParallel extends InnerBaseAsyncArray {
  3776. constructor(args) {
  3777. super(args);
  3778. }
  3779. forEach(fn) {
  3780. return __awaiter(this, void 0, void 0, function* () {
  3781. yield this._all(fn);
  3782. });
  3783. }
  3784. filter(fn) {
  3785. return __awaiter(this, void 0, void 0, function* () {
  3786. const res = yield this._all(fn);
  3787. const result = new InnerAsyncArrayParallel();
  3788. for (let i = 0, len = res.length; i < len; i++) {
  3789. if (res[i]) {
  3790. result._arr.push(this._arr[i]);
  3791. }
  3792. }
  3793. return result;
  3794. });
  3795. }
  3796. map(fn) {
  3797. return __awaiter(this, void 0, void 0, function* () {
  3798. return new InnerAsyncArrayParallel(yield this._all(fn));
  3799. });
  3800. }
  3801. flatMap(fn) {
  3802. return __awaiter(this, void 0, void 0, function* () {
  3803. const res = yield this._all(fn);
  3804. return new InnerAsyncArrayParallel(res.flat());
  3805. });
  3806. }
  3807. sort(fn) {
  3808. throw new Error('Method not implemented.');
  3809. }
  3810. reduce(fn, res) {
  3811. return __awaiter(this, void 0, void 0, function* () {
  3812. for (let i = 0, len = this._arr.length; i < len; i++) {
  3813. if (res) {
  3814. res = yield fn.call(this, res, this._arr[i], i, this);
  3815. }
  3816. else {
  3817. res = this._arr[i];
  3818. }
  3819. }
  3820. return res;
  3821. });
  3822. }
  3823. reduceRight(fn, res) {
  3824. return __awaiter(this, void 0, void 0, function* () {
  3825. for (let i = this._arr.length - 1; i >= 0; i--) {
  3826. if (res) {
  3827. res = yield fn.apply(this, [res, this._arr[i], i, this]);
  3828. }
  3829. else {
  3830. res = this._arr[i];
  3831. }
  3832. }
  3833. return res;
  3834. });
  3835. }
  3836. findIndex(fn) {
  3837. return __awaiter(this, void 0, void 0, function* () {
  3838. return (yield this._all(fn)).findIndex(returnItself);
  3839. });
  3840. }
  3841. _all(fn) {
  3842. return __awaiter(this, void 0, void 0, function* () {
  3843. return yield Promise.all(this._arr.map((v, i) => fn.apply(this, [v, i, this])));
  3844. });
  3845. }
  3846. }
  3847. /**
  3848. * 异步数组
  3849. */
  3850. class AsyncArray {
  3851. /**
  3852. * 构造函数
  3853. * @param args 任意个参数
  3854. */
  3855. constructor(...args) {
  3856. /**
  3857. * 内部数组的长度,用于让 {@link AsyncArray} 的实例能作为 {@link Array.from} 的参数
  3858. */
  3859. this.length = 0;
  3860. this._arr = Array.from(args);
  3861. /**
  3862. * @field 保存异步任务
  3863. * @type {Action[]}
  3864. */
  3865. this._tasks = [];
  3866. }
  3867. /**
  3868. * 为内置数组赋值
  3869. * 此处自动重新计算 length 属性
  3870. */
  3871. set _arr(arr) {
  3872. this.__arr = arr;
  3873. this.length = this.__arr.length;
  3874. }
  3875. get _arr() {
  3876. return this.__arr;
  3877. }
  3878. /**
  3879. * 提供一个函数方便根据已有的数组或类数组(Set/Map)创建 {@link AsyncArray}
  3880. * @param arr 一个可迭代元素
  3881. * @returns 创建一个新的异步数组包装
  3882. */
  3883. static from(arr) {
  3884. const reuslt = new AsyncArray();
  3885. if (isNullOrUndefined(arr)) {
  3886. return reuslt;
  3887. }
  3888. reuslt._arr = Array.from(arr);
  3889. return reuslt;
  3890. }
  3891. filter(fn) {
  3892. return this._addTask(new Action(Action.Type.filter, [fn]));
  3893. }
  3894. map(fn) {
  3895. return this._addTask(new Action(Action.Type.map, [fn]));
  3896. }
  3897. flatMap(fn) {
  3898. return this._addTask(new Action(Action.Type.flatMap, [fn]));
  3899. }
  3900. sort(fn) {
  3901. return this._addTask(new Action(Action.Type.sort, [fn]));
  3902. }
  3903. parallel() {
  3904. return this._addTask(new Action(Action.Type.parallel, []));
  3905. }
  3906. serial() {
  3907. return this._addTask(new Action(Action.Type.serial, []));
  3908. }
  3909. forEach(fn) {
  3910. return this._addTask(new Action(Action.Type.forEach, [fn])).then();
  3911. }
  3912. some(fn) {
  3913. return this._addTask(new Action(Action.Type.some, [fn])).then();
  3914. }
  3915. every(fn) {
  3916. return this._addTask(new Action(Action.Type.every, [fn])).then();
  3917. }
  3918. find(fn) {
  3919. return this._addTask(new Action(Action.Type.find, [fn])).then();
  3920. }
  3921. findIndex(fn) {
  3922. return this._addTask(new Action(Action.Type.findIndex, [fn])).then();
  3923. }
  3924. reduce(fn, res) {
  3925. return this._addTask(new Action(Action.Type.reduce, [fn, res])).then();
  3926. }
  3927. reduceRight(fn, res) {
  3928. return this._addTask(new Action(Action.Type.reduceRight, [fn, res])).then();
  3929. }
  3930. /**
  3931. * 终结整个链式操作并返回结果,可以使用 await 等待当前实例开始计算
  3932. */
  3933. then(onfulfilled, onrejected) {
  3934. return __awaiter(this, void 0, void 0, function* () {
  3935. try {
  3936. let asyncArray = new InnerAsyncArray(this._arr);
  3937. let result = this._arr;
  3938. for (const task of this._tasks) {
  3939. asyncArray = yield Reflect.apply(Reflect.get(asyncArray, task.type), asyncArray, task.args);
  3940. if (asyncArray instanceof InnerBaseAsyncArray) {
  3941. result = asyncArray.value();
  3942. }
  3943. else {
  3944. if (!isNullOrUndefined(onfulfilled)) {
  3945. onfulfilled(result);
  3946. }
  3947. return asyncArray;
  3948. }
  3949. }
  3950. if (!isNullOrUndefined(onfulfilled)) {
  3951. onfulfilled(result);
  3952. }
  3953. return result;
  3954. }
  3955. catch (err) {
  3956. if (!isNullOrUndefined(onrejected)) {
  3957. onrejected(err);
  3958. }
  3959. }
  3960. });
  3961. }
  3962. /**
  3963. * @deprecated 已废弃,请直接使用 await 进行等待获取结果值即可
  3964. */
  3965. value() {
  3966. return this.then();
  3967. }
  3968. /**
  3969. * 允许使用 for-of 遍历内部的 _arr
  3970. */
  3971. *[Symbol.iterator]() {
  3972. for (const kv of this._arr) {
  3973. yield kv;
  3974. }
  3975. }
  3976. _addTask(task) {
  3977. const result = new AsyncArray(...this._arr);
  3978. result._tasks = [...this._tasks, task];
  3979. return result;
  3980. }
  3981. }
  3982.  
  3983. /**
  3984. * 包装一个函数为异步函数
  3985. * @param fn 任意一个函数
  3986. * @typeparam R 原函数函数返回值类型
  3987. * @returns 返回的异步结果 Promise 对象
  3988. */
  3989. function async(fn) {
  3990. return new Proxy(fn, {
  3991. apply(_, _this, args) {
  3992. return __awaiter(this, void 0, void 0, function* () {
  3993. return yield Reflect.apply(_, _this, args);
  3994. });
  3995. },
  3996. });
  3997. }
  3998.  
  3999. /**
  4000. * 将一个异步函数包装为具有时序的异步函数
  4001. * 注: 该函数会按照调用顺序依次返回结果,后面的调用的结果需要等待前面的,所以如果不关心过时的结果,请使用 {@link switchMap} 函数
  4002. * @param fn 一个普通的异步函数
  4003. * @returns 包装后的函数
  4004. */
  4005. function mergeMap(fn) {
  4006. // 当前执行的异步操作 id
  4007. let id = 0;
  4008. // 所执行的异步操作 id 列表
  4009. const ids = new Set();
  4010. return new Proxy(fn, {
  4011. apply(_, _this, args) {
  4012. return __awaiter(this, void 0, void 0, function* () {
  4013. const prom = Reflect.apply(_, _this, args);
  4014. const temp = id;
  4015. ids.add(temp);
  4016. id++;
  4017. yield wait(() => !ids.has(temp - 1));
  4018. ids.delete(temp);
  4019. return yield prom;
  4020. });
  4021. },
  4022. });
  4023. }
  4024.  
  4025. /**
  4026. * 将一个异步函数包装为具有时序的异步函数
  4027. * 注: 该函数会丢弃过期的异步操作结果,这样的话性能会稍稍提高(主要是响应比较快的结果会立刻生效而不必等待前面的响应结果)
  4028. * @param fn 一个普通的异步函数
  4029. * @returns 包装后的函数
  4030. */
  4031. function switchMap(fn) {
  4032. // 当前执行的异步操作 id
  4033. let id = 0;
  4034. // 最后一次异步操作的 id,小于这个的操作结果会被丢弃
  4035. let last = 0;
  4036. // 缓存最后一次异步操作的结果
  4037. let cache;
  4038. return new Proxy(fn, {
  4039. apply(_, _this, args) {
  4040. return __awaiter(this, void 0, void 0, function* () {
  4041. const temp = id;
  4042. id++;
  4043. const res = yield Reflect.apply(_, _this, args);
  4044. if (temp < last) {
  4045. return cache;
  4046. }
  4047. cache = res;
  4048. last = temp;
  4049. return res;
  4050. });
  4051. },
  4052. });
  4053. }
  4054.  
  4055. /**
  4056. * 将指定函数包装为只调用一次
  4057. * @param fn 需要包装的函数
  4058. * @returns 包装后的函数
  4059. */
  4060. function once(fn) {
  4061. let flag = true;
  4062. let cache;
  4063. return new Proxy(fn, {
  4064. apply(target, thisArg, args) {
  4065. if (flag === false) {
  4066. return cache;
  4067. }
  4068. flag = false;
  4069. // 如果是异步函数则返回异步的结果
  4070. return compatibleAsync(Reflect.apply(target, thisArg, args), res => {
  4071. cache = res;
  4072. return cache;
  4073. });
  4074. },
  4075. });
  4076. }
  4077.  
  4078. /**
  4079. * 将一个异步函数包装为具有时序的异步函数
  4080. * 注: 该函数会按照调用顺序依次返回结果,后面的执行的调用(不是调用结果)需要等待前面的,此函数适用于异步函数的内里执行也必须保证顺序时使用,否则请使用 {@link mergeMap} 函数
  4081. * 注: 该函数其实相当于调用 {@code asyncLimiting(fn, {limit: 1})} 函数
  4082. * 例如即时保存文档到服务器,当然要等待上一次的请求结束才能请求下一次,不然数据库保存的数据就存在谬误了
  4083. * @param fn 一个普通的异步函数
  4084. * @returns 包装后的函数
  4085. */
  4086. function concatMap(fn) {
  4087. // 当前执行的异步操作 id
  4088. let id = 0;
  4089. // 所执行的异步操作 id 列表
  4090. const ids = new Set();
  4091. return new Proxy(fn, {
  4092. apply(_, _this, args) {
  4093. return __awaiter(this, void 0, void 0, function* () {
  4094. const temp = id;
  4095. ids.add(temp);
  4096. id++;
  4097. yield wait(() => !ids.has(temp - 1));
  4098. const prom = Reflect.apply(_, _this, args);
  4099. ids.delete(temp);
  4100. return yield prom;
  4101. });
  4102. },
  4103. });
  4104. }
  4105.  
  4106. exports.AntiDebug = AntiDebug;
  4107. exports.ArrayValidator = ArrayValidator;
  4108. exports.AsyncArray = AsyncArray;
  4109. exports.CacheUtil = CacheUtil;
  4110. exports.CombinedPredicate = CombinedPredicate;
  4111. exports.DateConstants = DateConstants;
  4112. exports.DateFormatter = DateFormatter;
  4113. exports.FetchLimiting = FetchLimiting;
  4114. exports.LocalStorageCache = LocalStorageCache;
  4115. exports.Locker = Locker;
  4116. exports.Logger = Logger;
  4117. exports.NodeBridgeUtil = NodeBridgeUtil;
  4118. exports.PathUtil = PathUtil;
  4119. exports.StateMachine = StateMachine;
  4120. exports.StringStyleUtil = StringStyleUtil;
  4121. exports.StringValidator = StringValidator;
  4122. exports.aggregation = aggregation;
  4123. exports.antiDebug = antiDebug;
  4124. exports.appends = appends;
  4125. exports.arrayDiffBy = arrayDiffBy;
  4126. exports.arrayToMap = arrayToMap;
  4127. exports.arrayValidator = arrayValidator;
  4128. exports.asIterator = asIterator;
  4129. exports.assign = assign;
  4130. exports.async = async;
  4131. exports.asyncFlatMap = asyncFlatMap;
  4132. exports.asyncLimiting = asyncLimiting;
  4133. exports.autoIncrement = autoIncrement;
  4134. exports.blankToNull = blankToNull;
  4135. exports.blankToNullField = blankToNullField;
  4136. exports.bridge = bridge;
  4137. exports.cacheUtil = cacheUtil;
  4138. exports.compare = compare;
  4139. exports.compose = compose;
  4140. exports.concatMap = concatMap;
  4141. exports.copyText = copyText;
  4142. exports.createElByString = createElByString;
  4143. exports.curry = curry;
  4144. exports.dateBetween = dateBetween;
  4145. exports.dateConstants = dateConstants;
  4146. exports.dateEnhance = dateEnhance;
  4147. exports.dateFormat = dateFormat;
  4148. exports.dateParse = dateParse;
  4149. exports.debounce = debounce;
  4150. exports.deepFreeze = deepFreeze;
  4151. exports.deepProxy = deepProxy;
  4152. exports.deletes = deletes;
  4153. exports.deny = deny;
  4154. exports.download = download;
  4155. exports.downloadString = downloadString;
  4156. exports.downloadUrl = downloadUrl;
  4157. exports.emptyAllField = emptyAllField;
  4158. exports.emptyFunc = emptyFunc;
  4159. exports.excludeFields = excludeFields;
  4160. exports.excludeFieldsDeep = excludeFieldsDeep;
  4161. exports.fetchTimeout = fetchTimeout;
  4162. exports.fill = fill;
  4163. exports.filterItems = filterItems;
  4164. exports.findIndex = findIndex;
  4165. exports.flatMap = flatMap;
  4166. exports.floatEquals = floatEquals;
  4167. exports.formDataToArray = formDataToArray;
  4168. exports.format = format;
  4169. exports.getCookies = getCookies;
  4170. exports.getCusorPostion = getCusorPostion;
  4171. exports.getObjectEntries = getObjectEntries;
  4172. exports.getObjectKeys = getObjectKeys;
  4173. exports.getYearWeek = getYearWeek;
  4174. exports.groupBy = groupBy;
  4175. exports.insertText = insertText;
  4176. exports.isBlank = isBlank;
  4177. exports.isEditable = isEditable;
  4178. exports.isEmpty = isEmpty;
  4179. exports.isFloat = isFloat;
  4180. exports.isNullOrUndefined = isNullOrUndefined;
  4181. exports.isNumber = isNumber;
  4182. exports.isRange = isRange;
  4183. exports.lastFocus = lastFocus;
  4184. exports.listToTree = listToTree;
  4185. exports.loadResource = loadResource;
  4186. exports.loadScript = loadScript;
  4187. exports.logger = logger;
  4188. exports.mapToObject = mapToObject;
  4189. exports.mergeMap = mergeMap;
  4190. exports.nodeBridgeUtil = nodeBridgeUtil;
  4191. exports.objToFormData = objToFormData;
  4192. exports.objectToMap = objectToMap;
  4193. exports.once = once;
  4194. exports.onceOfSameParam = onceOfSameParam;
  4195. exports.parseUrl = parseUrl;
  4196. exports.pathUtil = pathUtil;
  4197. exports.randomInt = randomInt;
  4198. exports.range = range;
  4199. exports.readLocal = readLocal;
  4200. exports.removeEl = removeEl;
  4201. exports.removeText = removeText;
  4202. exports.returnItself = returnItself;
  4203. exports.returnReasonableItself = returnReasonableItself;
  4204. exports.safeExec = safeExec;
  4205. exports.setCusorPostion = setCusorPostion;
  4206. exports.sets = sets;
  4207. exports.singleModel = singleModel;
  4208. exports.sleep = sleep;
  4209. exports.sortBy = sortBy;
  4210. exports.spliceParams = spliceParams;
  4211. exports.strToArrayBuffer = strToArrayBuffer;
  4212. exports.strToDate = strToDate;
  4213. exports.stringValidator = stringValidator;
  4214. exports.switchMap = switchMap;
  4215. exports.throttle = throttle;
  4216. exports.timing = timing;
  4217. exports.toLowerCase = toLowerCase;
  4218. exports.toObject = toObject;
  4219. exports.toString = toString;
  4220. exports.toUpperCase = toUpperCase;
  4221. exports.treeMapping = treeMapping;
  4222. exports.treeToList = treeToList;
  4223. exports.trySometime = trySometime;
  4224. exports.trySometimeParallel = trySometimeParallel;
  4225. exports.uniqueBy = uniqueBy;
  4226. exports.wait = wait;
  4227. exports.waitResource = waitResource;
  4228. exports.watch = watch;
  4229. exports.watchEventListener = watchEventListener;
  4230. exports.watchObject = watchObject;
  4231.  
  4232. Object.defineProperty(exports, '__esModule', { value: true });
  4233.  
  4234. }));
  4235. //# sourceMappingURL=rx-util.js.map