clickgo 3.0.0-dev

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +75 -0
  3. package/dist/app/demo/config.json +106 -0
  4. package/dist/app/demo/form/control/block/block.css +1 -0
  5. package/dist/app/demo/form/control/block/block.scss +17 -0
  6. package/dist/app/demo/form/control/block/block.xml +7 -0
  7. package/dist/app/demo/form/control/button/button.css +1 -0
  8. package/dist/app/demo/form/control/button/button.js +27 -0
  9. package/dist/app/demo/form/control/button/button.scss +18 -0
  10. package/dist/app/demo/form/control/button/button.xml +126 -0
  11. package/dist/app/demo/form/control/check/check.js +12 -0
  12. package/dist/app/demo/form/control/check/check.xml +13 -0
  13. package/dist/app/demo/form/control/dialog/dialog.js +8 -0
  14. package/dist/app/demo/form/control/dialog/dialog.xml +26 -0
  15. package/dist/app/demo/form/control/file/file.js +23 -0
  16. package/dist/app/demo/form/control/file/file.xml +25 -0
  17. package/dist/app/demo/form/control/form/form.css +1 -0
  18. package/dist/app/demo/form/control/form/form.js +38 -0
  19. package/dist/app/demo/form/control/form/form.scss +9 -0
  20. package/dist/app/demo/form/control/form/form.xml +28 -0
  21. package/dist/app/demo/form/control/greatview/greatview.css +1 -0
  22. package/dist/app/demo/form/control/greatview/greatview.js +92 -0
  23. package/dist/app/demo/form/control/greatview/greatview.scss +22 -0
  24. package/dist/app/demo/form/control/greatview/greatview.xml +89 -0
  25. package/dist/app/demo/form/control/img/img.xml +16 -0
  26. package/dist/app/demo/form/control/label/label.xml +11 -0
  27. package/dist/app/demo/form/control/list/list.css +1 -0
  28. package/dist/app/demo/form/control/list/list.js +194 -0
  29. package/dist/app/demo/form/control/list/list.scss +7 -0
  30. package/dist/app/demo/form/control/list/list.xml +91 -0
  31. package/dist/app/demo/form/control/loading/loading.xml +8 -0
  32. package/dist/app/demo/form/control/marquee/marquee.js +30 -0
  33. package/dist/app/demo/form/control/marquee/marquee.xml +36 -0
  34. package/dist/app/demo/form/control/menu/menu.js +8 -0
  35. package/dist/app/demo/form/control/menu/menu.xml +122 -0
  36. package/dist/app/demo/form/control/monaco/monaco.js +113 -0
  37. package/dist/app/demo/form/control/monaco/monaco.xml +27 -0
  38. package/dist/app/demo/form/control/overflow/overflow.css +1 -0
  39. package/dist/app/demo/form/control/overflow/overflow.js +70 -0
  40. package/dist/app/demo/form/control/overflow/overflow.scss +18 -0
  41. package/dist/app/demo/form/control/overflow/overflow.xml +98 -0
  42. package/dist/app/demo/form/control/property/property.js +129 -0
  43. package/dist/app/demo/form/control/property/property.xml +6 -0
  44. package/dist/app/demo/form/control/radio/radio.js +7 -0
  45. package/dist/app/demo/form/control/radio/radio.xml +12 -0
  46. package/dist/app/demo/form/control/scroll/scroll.js +14 -0
  47. package/dist/app/demo/form/control/scroll/scroll.xml +35 -0
  48. package/dist/app/demo/form/control/select/select.js +91 -0
  49. package/dist/app/demo/form/control/select/select.xml +74 -0
  50. package/dist/app/demo/form/control/tab/tab.js +75 -0
  51. package/dist/app/demo/form/control/tab/tab.xml +22 -0
  52. package/dist/app/demo/form/control/text/text.js +53 -0
  53. package/dist/app/demo/form/control/text/text.xml +37 -0
  54. package/dist/app/demo/form/control/view/view.css +1 -0
  55. package/dist/app/demo/form/control/view/view.js +73 -0
  56. package/dist/app/demo/form/control/view/view.scss +18 -0
  57. package/dist/app/demo/form/control/view/view.xml +94 -0
  58. package/dist/app/demo/form/event/form/form.css +1 -0
  59. package/dist/app/demo/form/event/form/form.js +129 -0
  60. package/dist/app/demo/form/event/form/form.scss +24 -0
  61. package/dist/app/demo/form/event/form/form.xml +16 -0
  62. package/dist/app/demo/form/event/screen/screen.js +51 -0
  63. package/dist/app/demo/form/event/screen/screen.xml +9 -0
  64. package/dist/app/demo/form/event/task/task.js +78 -0
  65. package/dist/app/demo/form/event/task/task.xml +20 -0
  66. package/dist/app/demo/form/main.css +1 -0
  67. package/dist/app/demo/form/main.js +25 -0
  68. package/dist/app/demo/form/main.scss +9 -0
  69. package/dist/app/demo/form/main.xml +49 -0
  70. package/dist/app/demo/form/method/core/core.js +25 -0
  71. package/dist/app/demo/form/method/core/core.xml +7 -0
  72. package/dist/app/demo/form/method/dom/dom.css +1 -0
  73. package/dist/app/demo/form/method/dom/dom.js +163 -0
  74. package/dist/app/demo/form/method/dom/dom.scss +10 -0
  75. package/dist/app/demo/form/method/dom/dom.xml +55 -0
  76. package/dist/app/demo/form/method/form/form.css +1 -0
  77. package/dist/app/demo/form/method/form/form.js +217 -0
  78. package/dist/app/demo/form/method/form/form.scss +3 -0
  79. package/dist/app/demo/form/method/form/form.xml +56 -0
  80. package/dist/app/demo/form/method/form/test.xml +5 -0
  81. package/dist/app/demo/form/method/fs/fs.js +88 -0
  82. package/dist/app/demo/form/method/fs/fs.xml +8 -0
  83. package/dist/app/demo/form/method/fs/text.js +15 -0
  84. package/dist/app/demo/form/method/fs/text.xml +3 -0
  85. package/dist/app/demo/form/method/task/locale1.json +3 -0
  86. package/dist/app/demo/form/method/task/locale2.json +3 -0
  87. package/dist/app/demo/form/method/task/task.js +153 -0
  88. package/dist/app/demo/form/method/task/task.xml +57 -0
  89. package/dist/app/demo/form/method/theme/theme.js +74 -0
  90. package/dist/app/demo/form/method/theme/theme.xml +9 -0
  91. package/dist/app/demo/form/method/tool/tool.js +64 -0
  92. package/dist/app/demo/form/method/tool/tool.xml +26 -0
  93. package/dist/app/demo/form/method/zip/zip.js +99 -0
  94. package/dist/app/demo/form/method/zip/zip.xml +12 -0
  95. package/dist/app/demo/global.css +3 -0
  96. package/dist/app/demo/res/icon.svg +1 -0
  97. package/dist/app/demo/res/img.jpg +0 -0
  98. package/dist/app/demo/res/r-1.svg +1 -0
  99. package/dist/app/demo/res/r-2.svg +1 -0
  100. package/dist/app/demo/res/sql.svg +1 -0
  101. package/dist/app/demo/res/txt.svg +1 -0
  102. package/dist/app/demo/res/zip.svg +1 -0
  103. package/dist/app/task/config.json +29 -0
  104. package/dist/app/task/form/bar/bar.js +299 -0
  105. package/dist/app/task/form/bar/bar.xml +47 -0
  106. package/dist/app/task/form/desktop/desktop.xml +1 -0
  107. package/dist/app/task/locale/en.json +11 -0
  108. package/dist/app/task/locale/ja.json +11 -0
  109. package/dist/app/task/locale/sc.json +11 -0
  110. package/dist/app/task/locale/tc.json +11 -0
  111. package/dist/clickgo.js +41 -0
  112. package/dist/clickgo.ts +51 -0
  113. package/dist/control/common.cgc +0 -0
  114. package/dist/control/form.cgc +0 -0
  115. package/dist/control/monaco.cgc +0 -0
  116. package/dist/control/property.cgc +0 -0
  117. package/dist/control/task.cgc +0 -0
  118. package/dist/global.css +1 -0
  119. package/dist/icon.png +0 -0
  120. package/dist/index.js +88 -0
  121. package/dist/index.ts +92 -0
  122. package/dist/lib/control.js +365 -0
  123. package/dist/lib/control.ts +428 -0
  124. package/dist/lib/core.js +668 -0
  125. package/dist/lib/core.ts +732 -0
  126. package/dist/lib/dom.js +1471 -0
  127. package/dist/lib/dom.ts +1785 -0
  128. package/dist/lib/form.js +2101 -0
  129. package/dist/lib/form.ts +2496 -0
  130. package/dist/lib/fs.js +849 -0
  131. package/dist/lib/fs.ts +995 -0
  132. package/dist/lib/native.js +138 -0
  133. package/dist/lib/native.ts +219 -0
  134. package/dist/lib/task.js +686 -0
  135. package/dist/lib/task.ts +842 -0
  136. package/dist/lib/theme.js +159 -0
  137. package/dist/lib/theme.ts +196 -0
  138. package/dist/lib/tool.js +501 -0
  139. package/dist/lib/tool.ts +620 -0
  140. package/dist/lib/zip.js +352 -0
  141. package/dist/lib/zip.ts +434 -0
  142. package/dist/theme/familiar.cgt +0 -0
  143. package/package.json +27 -0
  144. package/types/dev.d.ts +30 -0
  145. package/types/index.d.ts +673 -0
@@ -0,0 +1,620 @@
1
+ /**
2
+ * Copyright 2022 Han Guoshuai <zohegs@gmail.com>
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * https://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import * as types from '../../types';
17
+ import * as task from './task';
18
+
19
+ /**
20
+ * --- 将 blob 对象转换为 ArrayBuffer ---
21
+ * @param blob 对象
22
+ */
23
+ export function blob2ArrayBuffer(blob: Blob): Promise<ArrayBuffer> {
24
+ return new Promise(function(resove) {
25
+ const fr = new FileReader();
26
+ fr.addEventListener('load', function() {
27
+ resove(fr.result as ArrayBuffer);
28
+ });
29
+ fr.readAsArrayBuffer(blob);
30
+ });
31
+ }
32
+
33
+ /**
34
+ * --- 完整的克隆一份数组/对象 ---
35
+ * @param obj 要克隆的对象
36
+ */
37
+ export function clone(obj: Record<string, any> | any[]): any[] | any {
38
+ let newObj: any = {};
39
+ if (obj instanceof Array) {
40
+ newObj = [];
41
+ for (let i = 0; i < obj.length; ++i) {
42
+ newObj[i] = typeof obj[i] === 'object' ? clone(obj[i]) : obj[i];
43
+ }
44
+ }
45
+ else {
46
+ for (const key in obj) {
47
+ newObj[key] = typeof obj[key] === 'object' ? clone(obj[key]) : obj[key];
48
+ }
49
+ }
50
+ return newObj;
51
+ }
52
+
53
+ /**
54
+ * --- 等待毫秒 ---
55
+ * @param ms 等待的毫秒,默认 0,最大 3 秒
56
+ */
57
+ export function sleep(ms: number = 0): Promise<boolean> {
58
+ return new Promise(function(resolve) {
59
+ if (ms > 1000 * 3) {
60
+ resolve(false);
61
+ return;
62
+ }
63
+ window.setTimeout(function() {
64
+ resolve(true);
65
+ }, ms);
66
+ });
67
+ }
68
+
69
+ /**
70
+ * --- 去除 html 的空白符、换行以及注释 ---
71
+ * @param text 要纯净的字符串
72
+ */
73
+ export function purify(text: string): string {
74
+ text = '>' + text + '<';
75
+ text = text.replace(/<!--([\s\S]*?)-->/g, '').replace(/>([\s\S]*?)</g, function(t: string, t1: string) {
76
+ return '>' + t1.replace(/\t|\r\n| {2}/g, '').replace(/\n|\r/g, '') + '<';
77
+ });
78
+ return text.slice(1, -1);
79
+ }
80
+
81
+ /**
82
+ * --- 将 style 中的 url 转换成 base64 data url ---
83
+ * @param path 路径基准或以文件的路径为基准
84
+ * @param style 样式表
85
+ * @param files 在此文件列表中查找
86
+ */
87
+ export async function styleUrl2DataUrl(
88
+ path: string,
89
+ style: string,
90
+ files: Record<string, Blob | string>
91
+ ): Promise<string> {
92
+ const reg = /url\(["']{0,1}(.+?)["']{0,1}\)/ig;
93
+ let match: RegExpExecArray | null = null;
94
+ while ((match = reg.exec(style))) {
95
+ let realPath = urlResolve(path, match[1]);
96
+ if (realPath.startsWith('/package/')) {
97
+ // --- 处理 form 里面的路径 ---
98
+ realPath = realPath.slice(8);
99
+ }
100
+ if (!files[realPath]) {
101
+ continue;
102
+ }
103
+ if (typeof files[realPath] !== 'string') {
104
+ style = style.replace(match[0], `url('${await blob2DataUrl(files[realPath] as Blob)}')`);
105
+ }
106
+ }
107
+ return style;
108
+ }
109
+
110
+ /**
111
+ * --- 给标签增加 tag-tagname 的 class,同时给标签增加 cg- 前导(仅字符串,不是操作真实 dom) ---
112
+ * @param layout layout
113
+ * @param retagname 是否更改 tagname 为 cg-tagname
114
+ */
115
+ export function layoutAddTagClassAndReTagName(layout: string, retagname: boolean): string {
116
+ // --- "" '' 引号中的内容先替换为 placeholder 排除掉干扰 ---
117
+ const list: string[] = [];
118
+ layout = layout.replace(/(\S+)=(".+?"|'.+?')/g, function(t, t1): string {
119
+ // --- t1 不是标签名,而是 attr 名,例如 class="xxx"、style="xxx" ---
120
+ if (t1 === 'class') {
121
+ return t;
122
+ }
123
+ list.push(t);
124
+ return '"CG-PLACEHOLDER"';
125
+ });
126
+ // --- 开始添加 class tag ---
127
+ layout = layout.replace(/<(\/{0,1})([\w-]+)([\s\S]*?>)/g, function(t, t1, t2: string, t3: string): string {
128
+ // --- t1 是 /,t2 是 tagname,t3 是标签其他内容 ---
129
+ if (['template', 'slot', 'teleport'].includes(t2)) {
130
+ return t;
131
+ }
132
+ else {
133
+ // --- 需要给 class 添加 cg-xxx ---
134
+ if (t1 === '/') {
135
+ if (t2 === 'block') {
136
+ return '</div' + t3;
137
+ }
138
+ else {
139
+ return retagname ? ('</cg-' + t2 + t3) : t;
140
+ }
141
+ }
142
+ if (t3.toLowerCase().includes(' class')) {
143
+ // --- 有 class,前置增加 ---
144
+ t3 = t3.replace(/ class=(["']{0,1})/i, ' class=$1tag-' + t2 + ' ');
145
+ }
146
+ else {
147
+ // --- 无 class 的 attr,增加 attr ---
148
+ t3 = ` class="tag-${t2}"` + t3;
149
+ }
150
+ if (t2 === 'block') {
151
+ return '<div' + t3;
152
+ }
153
+ else {
154
+ return retagname ? ('<cg-' + t2 + t3) : ('<' + t2 + t3);
155
+ }
156
+ }
157
+ });
158
+ // --- 恢复 placeholder ---
159
+ let i = -1;
160
+ return layout.replace(/"CG-PLACEHOLDER"/g, function() {
161
+ return list[++i];
162
+ });
163
+ }
164
+
165
+ /**
166
+ * --- 给标签追加 attr,即使 attr 存在也会追加上一个新的(非真实 DOM 操作,仅仅是对字符串进行处理) ---
167
+ * @param layout 被追加
168
+ * @param insert 要追加
169
+ * @param opt 选项, ignore 忽略的标签,include 包含的标签
170
+ */
171
+ export function layoutInsertAttr(layout: string, insert: string, opt: { 'ignore'?: RegExp[]; 'include'?: RegExp[]; } = {}): string {
172
+ return layout.replace(/<([\w-]+)[\s\S]*?>/g, function(t, t1): string {
173
+ if (opt.ignore) {
174
+ for (const item of opt.ignore) {
175
+ if (item.test(t1)) {
176
+ return t;
177
+ }
178
+ }
179
+ }
180
+ if (opt.include) {
181
+ let found = false;
182
+ for (const item of opt.include) {
183
+ if (item.test(t1)) {
184
+ found = true;
185
+ break;
186
+ }
187
+ }
188
+ if (!found) {
189
+ return t;
190
+ }
191
+ }
192
+ return t.replace(/<[\w-]+/, function(t) {
193
+ return t + ' ' + insert;
194
+ });
195
+ });
196
+ }
197
+
198
+ /**
199
+ * --- 对 :class 的对象增加实时 css 的前缀 ---
200
+ * @param object :class 中的 {'xxx': xxx, [yyy]: yyy} 字符串
201
+ */
202
+ function layoutClassPrependObject(object: string): string {
203
+ object = object.slice(1, -1).trim();
204
+ return '{' + object.replace(/(.+?):(.+?)(,|$)/g, function(t, t1: string, t2: string, t3: string) {
205
+ // --- t1 是 'xxx', t2 是 xxx,t3 是结尾或者 , 分隔符 ---
206
+ t1 = t1.trim();
207
+ if (t1.startsWith('[')) {
208
+ t1 = '[cgClassPrepend(' + t1.slice(1, -1) + ')]';
209
+ }
210
+ else {
211
+ let sp = '';
212
+ if (t1.startsWith('\'') || t1.startsWith('"')) {
213
+ sp = t1[0];
214
+ t1 = t1.slice(1, -1);
215
+ }
216
+ t1 = `[cgClassPrepend(${sp}${t1}${sp})]`;
217
+ }
218
+ return t1 + ':' + t2 + t3;
219
+ }) + '}';
220
+ }
221
+ /**
222
+ * --- 给 class 增加 scope 的随机前缀 ---
223
+ * @param layout layout
224
+ * @param preps 前置标识符列表,特殊字符串 scope 会被替换为随机前缀
225
+ */
226
+ export function layoutClassPrepend(layout: string, preps: string[]): string {
227
+ return layout.replace(/ class=["'](.+?)["']/gi, function(t, t1: string) {
228
+ // --- t1 为 xxx yyy zzz 这样的 ----
229
+ t1 = t1.trim();
230
+ const classList = t1.split(' ');
231
+ const resultList: string[] = [];
232
+ for (const item of classList) {
233
+ for (const prep of preps) {
234
+ resultList.push(prep + item);
235
+ }
236
+ }
237
+ return ` class='${resultList.join(' ')}'`;
238
+ }).replace(/ :class=(["']).+?>/gi, function(t, sp) {
239
+ return t.replace(new RegExp(` :class=${sp}(.+?)${sp}`, 'gi'), function(t, t1: string) {
240
+ // --- t1 为 [] 或 {} ---
241
+ t1 = t1.trim();
242
+ if (t1.startsWith('[')) {
243
+ // --- ['xxx', yyy && 'yyy'] ---
244
+ t1 = t1.slice(1, -1);
245
+ /** --- 'xxx', yyy && 'yyy' 的数组 --- */
246
+ const t1a = t1.split(',');
247
+ for (let i = 0; i < t1a.length; ++i) {
248
+ t1a[i] = t1a[i].trim();
249
+ if (t1a[i].startsWith('{')) {
250
+ t1a[i] = layoutClassPrependObject(t1a[i]);
251
+ }
252
+ else {
253
+ t1a[i] = 'cgClassPrepend(' + t1a[i] + ')';
254
+ }
255
+ }
256
+ t1 = '[' + t1a.join(',') + ']';
257
+ }
258
+ else {
259
+ t1 = layoutClassPrependObject(t1);
260
+ }
261
+ return ` :class="${t1}"`;
262
+ });
263
+ });
264
+ }
265
+
266
+ /**
267
+ * --- 对 layout 的 @events 事件进行包裹 ---
268
+ * @param layout 要包裹的 layout
269
+ */
270
+ export function eventsAttrWrap(layout: string): string {
271
+ const events = ['click', 'dblclick', 'mousedown', 'mouseenter', 'mouseleave', 'mouseup', 'touchstart', 'touchmove', 'touchend', 'keydown', 'keypress', 'keyup', 'contextmenu'];
272
+ const reg = new RegExp(`@(${events.join('|')})="(.+?)"`, 'g');
273
+ return layout.replace(reg, function(t, t1, t2): string {
274
+ if (/^[\w]+$/.test(t2)) {
275
+ return `@${t1}="cgAllowEvent($event) && ${t2}($event)"`;
276
+ }
277
+ return `@${t1}=";if(cgAllowEvent($event)){${t2}}"`;
278
+ });
279
+ }
280
+
281
+ /**
282
+ * --- 给 class 前部增加唯一标识符 ---
283
+ * @param style 样式内容
284
+ * @param prep 给 class、font 等增加前置
285
+ */
286
+ export function stylePrepend(style: string, prep: string = ''): { 'style': string; 'prep': string; } {
287
+ if (prep === '') {
288
+ prep = 'cg-scope' + Math.round(Math.random() * 1000000000000000).toString() + '_';
289
+ }
290
+ style = style.replace(/([\s\S]+?){([\s\S]+?)}/g, function(t, t1: string, t2: string) {
291
+ // --- xxx { xxx; } ---
292
+ // --- 将 element 模式的 css 变为 class 模式,如 div 变为 .tag-div ---
293
+ // --- 这里面遇到了一个 bug, @keyframe 也被转换了,这是不对的,因此在下面修复 ---
294
+ t1 = t1.replace(/(^|[ >,\r\n])([a-zA-Z-_][a-zA-Z0-9-_]*)/g,
295
+ function(t: string, t1: string, t2: string) {
296
+ if (t2 === 'global') {
297
+ return '[CGTMP-GLOBAL]';
298
+ }
299
+ return t1 + '.tag-' + t2;
300
+ }
301
+ );
302
+ t1 = t1.replace(/keyframes \.tag-([a-zA-Z0-9-_]+)/g, function(t: string, t1: string) {
303
+ return 'keyframes ' + t1;
304
+ });
305
+ // --- 给 style 的 class 前添加 scope ---
306
+ t1 = t1.replace(/([.#])([a-zA-Z0-9-_]+)/g, function(t: string, t1: string, t2: string) {
307
+ /*
308
+ if (t2.startsWith('cg-')) {
309
+ return t;
310
+ }
311
+ */
312
+ return t1 + prep + t2;
313
+ }) + '{' + t2 + '}';
314
+ return t1;
315
+ });
316
+ // --- 自定义 font 名添加 scope ---
317
+ const fontList: string[] = [];
318
+ style = style.replace(/(@font-face[\s\S]+?font-family\s*:\s*["']{0,1})(.+?)(["']{0,1}\s*[;\r\n }])/gi,
319
+ function(t, t1: string, t2: string, t3: string) {
320
+ fontList.push(t2);
321
+ return t1 + prep + t2 + t3;
322
+ }
323
+ );
324
+ // --- 对自定义 font 进行更名 ---
325
+ for (const font of fontList) {
326
+ const reg = new RegExp(`(font.+?[: "'])(${font})`, 'gi');
327
+ style = style.replace(reg, function(t, t1: string, t2: string) {
328
+ return t1 + prep + t2;
329
+ });
330
+ }
331
+ // --- keyframes ---
332
+ const keyframeList: string[] = [];
333
+ style = style.replace(/([-@]keyframes *["']{0,1})([\w-]+)(["']{0,1}\s*?\{)/gi,
334
+ function(t, t1: string, t2: string, t3: string) {
335
+ if (!keyframeList.includes(t2)) {
336
+ keyframeList.push(t2);
337
+ }
338
+ return t1 + prep + t2 + t3;
339
+ }
340
+ );
341
+ // --- 对自定义 keyframe 进行更名 ---
342
+ for (const keyframe of keyframeList) {
343
+ const reg = new RegExp(`(animation[ :\\r\\n]+)(${keyframe})([ ;}\\r\\n])`, 'gi');
344
+ style = style.replace(reg, function(t, t1: string, t2: string, t3: string) {
345
+ return t1 + prep + t2 + t3;
346
+ });
347
+ }
348
+ return {
349
+ 'prep': prep,
350
+ 'style': style
351
+ };
352
+ }
353
+
354
+ /**
355
+ * --- 根据后缀、文件名或路径获取 mime 类型(简单版,完整版请使用 @litert/mime.js) ---
356
+ * @param path 后缀、文件名或路径
357
+ */
358
+ export function getMimeByPath(path: string): { 'mime': string; 'ext': string; } {
359
+ const lio = path.lastIndexOf('.');
360
+ const ext: string = (lio === -1 ? path : path.slice(lio + 1)).toLowerCase();
361
+ const exts: Record<string, string> = {
362
+ 'eot': 'application/vnd.ms-fontobject',
363
+ 'woff': 'font/woff',
364
+ 'ttf': 'font/ttf',
365
+ 'svg': 'image/svg+xml',
366
+ 'jpg': 'image/jpeg',
367
+ 'jpeg': 'image/jpeg',
368
+ 'gif': 'image/gif',
369
+ 'png': 'image/png'
370
+ };
371
+ const mime: string = exts[ext] ?? 'application/octet-stream';
372
+ return {
373
+ 'mime': mime,
374
+ 'ext': ext
375
+ };
376
+ }
377
+
378
+ /** --- 已创建的 object url 列表 --- */
379
+ const objectURLs: string[] = [];
380
+
381
+ /**
382
+ * --- 通过 blob 创建 object url ---
383
+ * @param object blob 对象
384
+ * @param taskId 要记录到 task 里面,待 task 结束后则清除相关 object url
385
+ */
386
+ export function createObjectURL(object: Blob, taskId: number = 0): string {
387
+ let t: types.ITask | null = null;
388
+ if (taskId > 0) {
389
+ t = task.list[taskId];
390
+ if (!t) {
391
+ return '';
392
+ }
393
+ }
394
+ const url = URL.createObjectURL(object);
395
+ objectURLs.push(url);
396
+ if (t) {
397
+ t.objectURLs.push(url);
398
+ }
399
+ return url;
400
+ }
401
+
402
+ /**
403
+ * --- 移除已创建的 object url ---
404
+ * @param url 已创建的 url
405
+ * @param 是否是 task 里面的,若不是,则无法被移除
406
+ */
407
+ export function revokeObjectURL(url: string, taskId: number = 0): void {
408
+ const oio = objectURLs.indexOf(url);
409
+ if (oio === -1) {
410
+ return;
411
+ }
412
+ if (taskId > 0) {
413
+ const t = task.list[taskId];
414
+ if (!t) {
415
+ return;
416
+ }
417
+ const io = t.objectURLs.indexOf(url);
418
+ if (io === -1) {
419
+ return;
420
+ }
421
+ t.objectURLs.splice(io, 1);
422
+ }
423
+ objectURLs.splice(oio, 1);
424
+ URL.revokeObjectURL(url);
425
+ }
426
+
427
+ /**
428
+ * --- 获取已创建的 object url 列表,App 模式下无效 ---
429
+ */
430
+ export function getObjectURLs(): string[] {
431
+ return objectURLs;
432
+ }
433
+
434
+ /**
435
+ * --- 生成范围内的随机数 ---
436
+ * @param min 最新范围
437
+ * @param max 最大范围
438
+ */
439
+ export function rand(min: number, max: number): number {
440
+ if (min > max) {
441
+ [min, max] = [max, min];
442
+ }
443
+ return min + Math.round(Math.random() * (max - min));
444
+ }
445
+
446
+ export const RANDOM_N = '0123456789';
447
+ export const RANDOM_U = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
448
+ export const RANDOM_L = 'abcdefghijklmnopqrstuvwxyz';
449
+
450
+ export const RANDOM_UN = RANDOM_U + RANDOM_N;
451
+ export const RANDOM_LN = RANDOM_L + RANDOM_N;
452
+ export const RANDOM_LU = RANDOM_L + RANDOM_U;
453
+ export const RANDOM_LUN = RANDOM_L + RANDOM_U + RANDOM_N;
454
+ export const RANDOM_V = 'ACEFGHJKLMNPRSTWXY34567';
455
+ export const RANDOM_LUNS = RANDOM_LUN + '()`~!@#$%^&*-+=_|{}[]:;\'<>,.?/]';
456
+ export function random(length: number = 8, source: string = RANDOM_LN, block: string = ''): string {
457
+ // --- 剔除 block 字符 ---
458
+ let len = block.length;
459
+ if (len > 0) {
460
+ for (let i = 0; i < len; ++i) {
461
+ source = source.replace(block[i], '');
462
+ }
463
+ }
464
+ len = source.length;
465
+ if (len === 0) {
466
+ return '';
467
+ }
468
+ let temp = '';
469
+ for (let i = 0; i < length; ++i) {
470
+ temp += source[rand(0, len - 1)];
471
+ }
472
+ return temp;
473
+ }
474
+
475
+ /**
476
+ * --- 根据参数获取最终的布尔值 ---
477
+ * @param param 参数
478
+ */
479
+ export function getBoolean(param: boolean | string | number): boolean {
480
+ const t = typeof param;
481
+ if (t === 'boolean') {
482
+ return param as boolean;
483
+ }
484
+ else if (t === 'string') {
485
+ return param === 'false' ? false : true;
486
+ }
487
+ else {
488
+ return param ? true : false;
489
+ }
490
+ }
491
+
492
+ /**
493
+ * --- 转义 HTML ---
494
+ * @param html HTML 字符
495
+ */
496
+ export function escapeHTML(html: string): string {
497
+ return html.replace(/</g, '&lt;').replace(/>/g, '&gt;');
498
+ }
499
+
500
+ /**
501
+ * --- 发起一个网络请求 ---
502
+ * @param url 网址
503
+ * @param opt 选项
504
+ */
505
+ export function request(url: string, opt: types.IRequestOptions): Promise<null | any> {
506
+ return new Promise(function(resove) {
507
+ const xhr = new XMLHttpRequest();
508
+ xhr.upload.onloadstart = function(e: ProgressEvent): void {
509
+ const r = opt.uploadStart?.(e.total);
510
+ if (r && (r instanceof Promise)) {
511
+ r.catch(function(e) {
512
+ console.log(e);
513
+ });
514
+ }
515
+ };
516
+ xhr.upload.onprogress = function(e: ProgressEvent): void {
517
+ const r = opt.uploadProgress?.(e.loaded, e.total);
518
+ if (r && (r instanceof Promise)) {
519
+ r.catch(function(e) {
520
+ console.log(e);
521
+ });
522
+ }
523
+ };
524
+ xhr.upload.onloadend = function(): void {
525
+ const r = opt.uploadEnd?.();
526
+ if (r && (r instanceof Promise)) {
527
+ r.catch(function(e) {
528
+ console.log(e);
529
+ });
530
+ }
531
+ };
532
+ xhr.onloadstart = function(e: ProgressEvent): void {
533
+ const r = opt.start?.(e.total);
534
+ if (r && (r instanceof Promise)) {
535
+ r.catch(function(e) {
536
+ console.log(e);
537
+ });
538
+ }
539
+ };
540
+ xhr.onprogress = function(e: ProgressEvent): void {
541
+ const r = opt.progress?.(e.loaded, e.total);
542
+ if (r && (r instanceof Promise)) {
543
+ r.catch(function(e) {
544
+ console.log(e);
545
+ });
546
+ }
547
+ };
548
+ xhr.onloadend = function(): void {
549
+ const r = opt.end?.();
550
+ if (r && (r instanceof Promise)) {
551
+ r.catch(function(e) {
552
+ console.log(e);
553
+ });
554
+ }
555
+ };
556
+ xhr.onload = function(): void {
557
+ let res = this.response;
558
+ if (this.getResponseHeader('content-type')?.includes('json')) {
559
+ try {
560
+ res = JSON.parse(res);
561
+ }
562
+ catch {
563
+ res = this.response;
564
+ }
565
+ }
566
+ const r = opt.load?.(res);
567
+ if (r && (r instanceof Promise)) {
568
+ r.catch(function(e) {
569
+ console.log(e);
570
+ });
571
+ }
572
+ resove(res);
573
+ };
574
+ xhr.onerror = function(): void {
575
+ const r = opt.error?.();
576
+ if (r && (r instanceof Promise)) {
577
+ r.catch(function(e) {
578
+ console.log(e);
579
+ });
580
+ }
581
+ resove(null);
582
+ };
583
+ if (opt.responseType) {
584
+ xhr.responseType = opt.responseType;
585
+ }
586
+ if (opt.timeout) {
587
+ xhr.timeout = opt.timeout;
588
+ }
589
+ if (opt.headers) {
590
+ for (const k in opt.headers) {
591
+ xhr.setRequestHeader(k, (opt.headers as any)[k]);
592
+ }
593
+ }
594
+ xhr.open(opt.method ?? 'GET', url, true);
595
+ xhr.send(opt.body);
596
+ });
597
+ }
598
+
599
+ export function parseUrl(url: string): ILoaderUrl {
600
+ return loader.parseUrl(url);
601
+ }
602
+
603
+ export function urlResolve(from: string, to: string): string {
604
+ return loader.urlResolve(from, to);
605
+ }
606
+
607
+ export function blob2Text(blob: Blob): Promise<string> {
608
+ return loader.blob2Text(blob);
609
+ }
610
+
611
+ export function blob2DataUrl(blob: Blob): Promise<string> {
612
+ return loader.blob2DataUrl(blob);
613
+ }
614
+
615
+ export function execCommand(ac: string): void {
616
+ if (!['copy', 'cut'].includes(ac)) {
617
+ return;
618
+ }
619
+ document.execCommand(ac);
620
+ }