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.
- package/LICENSE +201 -0
- package/README.md +75 -0
- package/dist/app/demo/config.json +106 -0
- package/dist/app/demo/form/control/block/block.css +1 -0
- package/dist/app/demo/form/control/block/block.scss +17 -0
- package/dist/app/demo/form/control/block/block.xml +7 -0
- package/dist/app/demo/form/control/button/button.css +1 -0
- package/dist/app/demo/form/control/button/button.js +27 -0
- package/dist/app/demo/form/control/button/button.scss +18 -0
- package/dist/app/demo/form/control/button/button.xml +126 -0
- package/dist/app/demo/form/control/check/check.js +12 -0
- package/dist/app/demo/form/control/check/check.xml +13 -0
- package/dist/app/demo/form/control/dialog/dialog.js +8 -0
- package/dist/app/demo/form/control/dialog/dialog.xml +26 -0
- package/dist/app/demo/form/control/file/file.js +23 -0
- package/dist/app/demo/form/control/file/file.xml +25 -0
- package/dist/app/demo/form/control/form/form.css +1 -0
- package/dist/app/demo/form/control/form/form.js +38 -0
- package/dist/app/demo/form/control/form/form.scss +9 -0
- package/dist/app/demo/form/control/form/form.xml +28 -0
- package/dist/app/demo/form/control/greatview/greatview.css +1 -0
- package/dist/app/demo/form/control/greatview/greatview.js +92 -0
- package/dist/app/demo/form/control/greatview/greatview.scss +22 -0
- package/dist/app/demo/form/control/greatview/greatview.xml +89 -0
- package/dist/app/demo/form/control/img/img.xml +16 -0
- package/dist/app/demo/form/control/label/label.xml +11 -0
- package/dist/app/demo/form/control/list/list.css +1 -0
- package/dist/app/demo/form/control/list/list.js +194 -0
- package/dist/app/demo/form/control/list/list.scss +7 -0
- package/dist/app/demo/form/control/list/list.xml +91 -0
- package/dist/app/demo/form/control/loading/loading.xml +8 -0
- package/dist/app/demo/form/control/marquee/marquee.js +30 -0
- package/dist/app/demo/form/control/marquee/marquee.xml +36 -0
- package/dist/app/demo/form/control/menu/menu.js +8 -0
- package/dist/app/demo/form/control/menu/menu.xml +122 -0
- package/dist/app/demo/form/control/monaco/monaco.js +113 -0
- package/dist/app/demo/form/control/monaco/monaco.xml +27 -0
- package/dist/app/demo/form/control/overflow/overflow.css +1 -0
- package/dist/app/demo/form/control/overflow/overflow.js +70 -0
- package/dist/app/demo/form/control/overflow/overflow.scss +18 -0
- package/dist/app/demo/form/control/overflow/overflow.xml +98 -0
- package/dist/app/demo/form/control/property/property.js +129 -0
- package/dist/app/demo/form/control/property/property.xml +6 -0
- package/dist/app/demo/form/control/radio/radio.js +7 -0
- package/dist/app/demo/form/control/radio/radio.xml +12 -0
- package/dist/app/demo/form/control/scroll/scroll.js +14 -0
- package/dist/app/demo/form/control/scroll/scroll.xml +35 -0
- package/dist/app/demo/form/control/select/select.js +91 -0
- package/dist/app/demo/form/control/select/select.xml +74 -0
- package/dist/app/demo/form/control/tab/tab.js +75 -0
- package/dist/app/demo/form/control/tab/tab.xml +22 -0
- package/dist/app/demo/form/control/text/text.js +53 -0
- package/dist/app/demo/form/control/text/text.xml +37 -0
- package/dist/app/demo/form/control/view/view.css +1 -0
- package/dist/app/demo/form/control/view/view.js +73 -0
- package/dist/app/demo/form/control/view/view.scss +18 -0
- package/dist/app/demo/form/control/view/view.xml +94 -0
- package/dist/app/demo/form/event/form/form.css +1 -0
- package/dist/app/demo/form/event/form/form.js +129 -0
- package/dist/app/demo/form/event/form/form.scss +24 -0
- package/dist/app/demo/form/event/form/form.xml +16 -0
- package/dist/app/demo/form/event/screen/screen.js +51 -0
- package/dist/app/demo/form/event/screen/screen.xml +9 -0
- package/dist/app/demo/form/event/task/task.js +78 -0
- package/dist/app/demo/form/event/task/task.xml +20 -0
- package/dist/app/demo/form/main.css +1 -0
- package/dist/app/demo/form/main.js +25 -0
- package/dist/app/demo/form/main.scss +9 -0
- package/dist/app/demo/form/main.xml +49 -0
- package/dist/app/demo/form/method/core/core.js +25 -0
- package/dist/app/demo/form/method/core/core.xml +7 -0
- package/dist/app/demo/form/method/dom/dom.css +1 -0
- package/dist/app/demo/form/method/dom/dom.js +163 -0
- package/dist/app/demo/form/method/dom/dom.scss +10 -0
- package/dist/app/demo/form/method/dom/dom.xml +55 -0
- package/dist/app/demo/form/method/form/form.css +1 -0
- package/dist/app/demo/form/method/form/form.js +217 -0
- package/dist/app/demo/form/method/form/form.scss +3 -0
- package/dist/app/demo/form/method/form/form.xml +56 -0
- package/dist/app/demo/form/method/form/test.xml +5 -0
- package/dist/app/demo/form/method/fs/fs.js +88 -0
- package/dist/app/demo/form/method/fs/fs.xml +8 -0
- package/dist/app/demo/form/method/fs/text.js +15 -0
- package/dist/app/demo/form/method/fs/text.xml +3 -0
- package/dist/app/demo/form/method/task/locale1.json +3 -0
- package/dist/app/demo/form/method/task/locale2.json +3 -0
- package/dist/app/demo/form/method/task/task.js +153 -0
- package/dist/app/demo/form/method/task/task.xml +57 -0
- package/dist/app/demo/form/method/theme/theme.js +74 -0
- package/dist/app/demo/form/method/theme/theme.xml +9 -0
- package/dist/app/demo/form/method/tool/tool.js +64 -0
- package/dist/app/demo/form/method/tool/tool.xml +26 -0
- package/dist/app/demo/form/method/zip/zip.js +99 -0
- package/dist/app/demo/form/method/zip/zip.xml +12 -0
- package/dist/app/demo/global.css +3 -0
- package/dist/app/demo/res/icon.svg +1 -0
- package/dist/app/demo/res/img.jpg +0 -0
- package/dist/app/demo/res/r-1.svg +1 -0
- package/dist/app/demo/res/r-2.svg +1 -0
- package/dist/app/demo/res/sql.svg +1 -0
- package/dist/app/demo/res/txt.svg +1 -0
- package/dist/app/demo/res/zip.svg +1 -0
- package/dist/app/task/config.json +29 -0
- package/dist/app/task/form/bar/bar.js +299 -0
- package/dist/app/task/form/bar/bar.xml +47 -0
- package/dist/app/task/form/desktop/desktop.xml +1 -0
- package/dist/app/task/locale/en.json +11 -0
- package/dist/app/task/locale/ja.json +11 -0
- package/dist/app/task/locale/sc.json +11 -0
- package/dist/app/task/locale/tc.json +11 -0
- package/dist/clickgo.js +41 -0
- package/dist/clickgo.ts +51 -0
- package/dist/control/common.cgc +0 -0
- package/dist/control/form.cgc +0 -0
- package/dist/control/monaco.cgc +0 -0
- package/dist/control/property.cgc +0 -0
- package/dist/control/task.cgc +0 -0
- package/dist/global.css +1 -0
- package/dist/icon.png +0 -0
- package/dist/index.js +88 -0
- package/dist/index.ts +92 -0
- package/dist/lib/control.js +365 -0
- package/dist/lib/control.ts +428 -0
- package/dist/lib/core.js +668 -0
- package/dist/lib/core.ts +732 -0
- package/dist/lib/dom.js +1471 -0
- package/dist/lib/dom.ts +1785 -0
- package/dist/lib/form.js +2101 -0
- package/dist/lib/form.ts +2496 -0
- package/dist/lib/fs.js +849 -0
- package/dist/lib/fs.ts +995 -0
- package/dist/lib/native.js +138 -0
- package/dist/lib/native.ts +219 -0
- package/dist/lib/task.js +686 -0
- package/dist/lib/task.ts +842 -0
- package/dist/lib/theme.js +159 -0
- package/dist/lib/theme.ts +196 -0
- package/dist/lib/tool.js +501 -0
- package/dist/lib/tool.ts +620 -0
- package/dist/lib/zip.js +352 -0
- package/dist/lib/zip.ts +434 -0
- package/dist/theme/familiar.cgt +0 -0
- package/package.json +27 -0
- package/types/dev.d.ts +30 -0
- package/types/index.d.ts +673 -0
package/dist/lib/tool.ts
ADDED
|
@@ -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, '<').replace(/>/g, '>');
|
|
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
|
+
}
|