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,732 @@
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 clickgo from '../clickgo';
18
+ import * as fs from './fs';
19
+ import * as form from './form';
20
+ import * as task from './task';
21
+ import * as tool from './tool';
22
+ import * as zip from './zip';
23
+
24
+ const configOrigin: types.IConfig = {
25
+ 'locale': 'en',
26
+ 'task.position': 'bottom',
27
+ 'task.pin': {},
28
+ 'desktop.icon.storage': true,
29
+ 'desktop.icon.recycler': true,
30
+ 'desktop.wallpaper': null,
31
+ 'desktop.path': null
32
+ };
33
+ export const config: types.IConfig = Vue.reactive({
34
+ 'locale': 'en',
35
+ 'task.position': 'bottom',
36
+ 'task.pin': {},
37
+ 'desktop.icon.storage': true,
38
+ 'desktop.icon.recycler': true,
39
+ 'desktop.wallpaper': null,
40
+ 'desktop.path': null
41
+ });
42
+
43
+ Vue.watch(config, function() {
44
+ // --- 检测有没有缺少的 config key ---
45
+ for (const key in configOrigin) {
46
+ if ((config as any)[key] !== undefined) {
47
+ continue;
48
+ }
49
+ form.notify({
50
+ 'title': 'Warning',
51
+ 'content': 'There is a software that maliciously removed the system config item.\nKey: ' + key,
52
+ 'type': 'warning'
53
+ });
54
+ (config as any)[key] = (configOrigin as any)[key];
55
+ }
56
+ for (const key in config) {
57
+ if (!Object.keys(configOrigin).includes(key)) {
58
+ form.notify({
59
+ 'title': 'Warning',
60
+ 'content': 'There is a software that maliciously modifies the system config.\nKey: ' + key,
61
+ 'type': 'warning'
62
+ });
63
+ delete (config as any)[key];
64
+ continue;
65
+ }
66
+ if (key === 'task.pin') {
67
+ // --- 如果是 pin,要检查老的和新的的 path 是否相等 ---
68
+ const paths = Object.keys(config['task.pin']).sort().toString();
69
+ const originPaths = Object.keys(configOrigin['task.pin']).sort().toString();
70
+ if (paths === originPaths) {
71
+ continue;
72
+ }
73
+ configOrigin['task.pin'] = {};
74
+ for (const path in config['task.pin']) {
75
+ configOrigin['task.pin'][path] = config['task.pin'][path];
76
+ }
77
+ trigger('configChanged', 'task.pin', config['task.pin']);
78
+ }
79
+ else {
80
+ // --- 别的要判断值是否和比对组一样 ---
81
+ if ((config as any)[key] === (configOrigin as any)[key]) {
82
+ continue;
83
+ }
84
+ (configOrigin as any)[key] = (config as any)[key];
85
+ if (key === 'task.position') {
86
+ task.refreshSystemPosition();
87
+ }
88
+ trigger('configChanged', key, (config as any)[key]);
89
+ }
90
+ }
91
+ }, {
92
+ 'deep': true
93
+ });
94
+
95
+ /** --- module 列表 --- */
96
+ const modules: Record<string, { func: () => any | Promise<any>; 'obj': null | any; 'loading': boolean; }> = {
97
+ 'monaco': {
98
+ func: async function() {
99
+ return new Promise(function(resolve, reject) {
100
+ fetch(clickgo.getCdn() + '/npm/monaco-editor@0.33.0/min/vs/loader.js').then(function(r) {
101
+ return r.blob();
102
+ }).then(function(b) {
103
+ return tool.blob2DataUrl(b);
104
+ }).then(function(d) {
105
+ resolve(d);
106
+ }).catch(function(e) {
107
+ reject(e);
108
+ });
109
+ });
110
+ },
111
+ 'obj': null,
112
+ 'loading': false
113
+ }
114
+ };
115
+
116
+ /**
117
+ * --- 注册新的外接模块 ---
118
+ * @param name 模块名
119
+ * @param func 执行加载函数
120
+ */
121
+ export function regModule(name: string, func: () => any | Promise<any>): boolean {
122
+ if (modules[name]) {
123
+ return false;
124
+ }
125
+ modules[name] = {
126
+ func: func,
127
+ 'obj': null,
128
+ 'loading': false
129
+ };
130
+ return true;
131
+ }
132
+
133
+ /**
134
+ * --- 外接模块需要 init 后才能使用 ---
135
+ * @param names 要加载的模块名
136
+ */
137
+ export function initModules(names: string | string[]): Promise<number> {
138
+ return new Promise(function(resolve) {
139
+ if (typeof names === 'string') {
140
+ names = [names];
141
+ }
142
+ if (names.length === 0) {
143
+ resolve(0);
144
+ return;
145
+ }
146
+ let loaded = 0;
147
+ let successful = 0;
148
+ for (const name of names) {
149
+ if (!modules[name]) {
150
+ ++loaded;
151
+ if (loaded === names.length) {
152
+ resolve(successful);
153
+ return;
154
+ }
155
+ continue;
156
+ }
157
+ if (modules[name].obj) {
158
+ ++loaded;
159
+ ++successful;
160
+ if (loaded === names.length) {
161
+ resolve(successful);
162
+ return;
163
+ }
164
+ continue;
165
+ }
166
+ if (modules[name].loading) {
167
+ ++loaded;
168
+ if (loaded === names.length) {
169
+ resolve(successful);
170
+ return;
171
+ }
172
+ continue;
173
+ }
174
+ // --- 正式开始加载 init ---
175
+ modules[name].loading = true;
176
+ const rtn = modules[name].func();
177
+ if (rtn instanceof Promise) {
178
+ rtn.then(function(obj) {
179
+ modules[name].obj = obj;
180
+ modules[name].loading = false;
181
+ ++loaded;
182
+ ++successful;
183
+ if (loaded === names.length) {
184
+ resolve(successful);
185
+ return;
186
+ }
187
+ }).catch(function() {
188
+ modules[name].loading = false;
189
+ ++loaded;
190
+ if (loaded === names.length) {
191
+ resolve(successful);
192
+ }
193
+ });
194
+ }
195
+ else {
196
+ modules[name].obj = rtn;
197
+ modules[name].loading = false;
198
+ ++loaded;
199
+ ++successful;
200
+ if (loaded === names.length) {
201
+ resolve(successful);
202
+ }
203
+ }
204
+ }
205
+ });
206
+ }
207
+
208
+ /**
209
+ * --- 获取外接模块 ---
210
+ * @param name 模块名
211
+ */
212
+ export function getModule(name: string): null | any {
213
+ if (!modules[name]) {
214
+ return null;
215
+ }
216
+ return modules[name].obj;
217
+ }
218
+
219
+ /** --- 全局响应事件 --- */
220
+ export const globalEvents: types.IGlobalEvents = {
221
+ errorHandler: null,
222
+ screenResizeHandler: function(): void {
223
+ form.refreshMaxPosition();
224
+ },
225
+ configChangedHandler: null,
226
+ formCreatedHandler: null,
227
+ formRemovedHandler: function(taskId: number, formId: number): void {
228
+ if (!form.simpleSystemTaskRoot.forms[formId]) {
229
+ return;
230
+ }
231
+ delete form.simpleSystemTaskRoot.forms[formId];
232
+ },
233
+ formTitleChangedHandler: function(taskId: number, formId: number, title: string): void {
234
+ if (!form.simpleSystemTaskRoot.forms[formId]) {
235
+ return;
236
+ }
237
+ form.simpleSystemTaskRoot.forms[formId].title = title;
238
+ },
239
+ formIconChangedHandler: function(taskId: number, formId: number, icon: string): void {
240
+ if (!form.simpleSystemTaskRoot.forms[formId]) {
241
+ return;
242
+ }
243
+ form.simpleSystemTaskRoot.forms[formId].icon = icon;
244
+ },
245
+ formStateMinChangedHandler: function(taskId: number, formId: number, state: boolean): void {
246
+ if (task.systemTaskInfo.taskId > 0) {
247
+ return;
248
+ }
249
+ if (state) {
250
+ const item = form.get(formId);
251
+ if (!item) {
252
+ return;
253
+ }
254
+ form.simpleSystemTaskRoot.forms[formId] = {
255
+ 'title': item.title,
256
+ 'icon': item.icon
257
+ };
258
+ }
259
+ else {
260
+ if (!form.simpleSystemTaskRoot.forms[formId]) {
261
+ return;
262
+ }
263
+ delete form.simpleSystemTaskRoot.forms[formId];
264
+ }
265
+ },
266
+ formStateMaxChangedHandler: null,
267
+ formShowChangedHandler: null,
268
+ formFocusedHandler: null,
269
+ formBlurredHandler: null,
270
+ formFlashHandler: null,
271
+ taskStartedHandler: null,
272
+ taskEndedHandler: null
273
+ };
274
+
275
+ /**
276
+ * --- 设置系统事件监听,一个窗体只能设置一个监听 ---
277
+ * @param name 系统事件名
278
+ * @param func 回调函数
279
+ * @param formId 窗体 id,app 模式下留空为当前窗体
280
+ * @param taskId 任务 id,app 模式下无效
281
+ */
282
+ export function setSystemEventListener(
283
+ name: types.TGlobalEvent,
284
+ func: (...any: any) => void | Promise<void>,
285
+ formId?: number,
286
+ taskId?: number
287
+ ): void {
288
+ if (!taskId) {
289
+ return;
290
+ }
291
+ const t = task.list[taskId];
292
+ if (!t) {
293
+ return;
294
+ }
295
+ if (!formId) {
296
+ return;
297
+ }
298
+ const f = t.forms[formId];
299
+ if (!f) {
300
+ return;
301
+ }
302
+ f.events[name] = func;
303
+ }
304
+
305
+ /**
306
+ * --- 移除系统事件监听,一个窗体只能设置一个监听 ---
307
+ * @param name name 系统事件名
308
+ * @param formId 窗体 id,app 默认为当前窗体
309
+ * @param taskId 任务 id,app 模式下无效
310
+ */
311
+ export function removeSystemEventListener(
312
+ name: types.TGlobalEvent,
313
+ formId?: number,
314
+ taskId?: number
315
+ ): void {
316
+ if (!taskId) {
317
+ return;
318
+ }
319
+ const t = task.list[taskId];
320
+ if (!t) {
321
+ return;
322
+ }
323
+ if (!formId) {
324
+ return;
325
+ }
326
+ const f = t.forms[formId];
327
+ if (!f) {
328
+ return;
329
+ }
330
+ delete f.events[name];
331
+ }
332
+
333
+ /**
334
+ * --- 主动触发系统级事件 ---
335
+ */
336
+ export function trigger(name: types.TGlobalEvent, taskId: number | string = 0, formId: number | string | boolean | Record<string, any> | null = 0, param1: boolean | Error | string = '', param2: string = ''): void {
337
+ switch (name) {
338
+ case 'error': {
339
+ if (typeof taskId !== 'number' || typeof formId !== 'number') {
340
+ break;
341
+ }
342
+ const r = globalEvents.errorHandler?.(taskId, formId, param1 as Error, param2);
343
+ if (r && (r instanceof Promise)) {
344
+ r.catch(function(e) {
345
+ console.log(e);
346
+ });
347
+ }
348
+ for (const tid in task.list) {
349
+ const t = task.list[tid];
350
+ for (const fid in t.forms) {
351
+ const r = t.forms[fid].events[name]?.(taskId, formId, param1, param2);
352
+ if (r instanceof Promise) {
353
+ r.catch(function(e) {
354
+ console.log(e);
355
+ });
356
+ }
357
+ }
358
+ }
359
+ break;
360
+ }
361
+ case 'screenResize': {
362
+ const r = globalEvents.screenResizeHandler?.();
363
+ if (r && (r instanceof Promise)) {
364
+ r.catch(function(e) {
365
+ console.log(e);
366
+ });
367
+ }
368
+ for (const tid in task.list) {
369
+ const t = task.list[tid];
370
+ for (const fid in t.forms) {
371
+ const r = t.forms[fid].events[name]?.();
372
+ if (r instanceof Promise) {
373
+ r.catch(function(e) {
374
+ console.log(e);
375
+ });
376
+ }
377
+ }
378
+ }
379
+ break;
380
+ }
381
+ case 'configChanged': {
382
+ if ((typeof taskId !== 'string') || (typeof formId === 'number')) {
383
+ break;
384
+ }
385
+ const r = globalEvents.configChangedHandler?.(taskId as types.TConfigName, formId);
386
+ if (r && (r instanceof Promise)) {
387
+ r.catch(function(e) {
388
+ console.log(e);
389
+ });
390
+ }
391
+ for (const tid in task.list) {
392
+ const t = task.list[tid];
393
+ for (const fid in t.forms) {
394
+ const r = t.forms[fid].events[name]?.(taskId, formId);
395
+ if (r instanceof Promise) {
396
+ r.catch(function(e) {
397
+ console.log(e);
398
+ });
399
+ }
400
+ }
401
+ }
402
+ break;
403
+ }
404
+ case 'formCreated':
405
+ case 'formRemoved': {
406
+ (globalEvents as any)[name + 'Handler']?.(taskId, formId, param1, param2);
407
+ for (const tid in task.list) {
408
+ const t = task.list[tid];
409
+ for (const fid in t.forms) {
410
+ const r = t.forms[fid].events[name]?.(taskId, formId, param1, param2);
411
+ if (r instanceof Promise) {
412
+ r.catch(function(e) {
413
+ console.log(e);
414
+ });
415
+ }
416
+ }
417
+ }
418
+ break;
419
+ }
420
+ case 'formTitleChanged':
421
+ case 'formIconChanged': {
422
+ (globalEvents as any)[name + 'Handler']?.(taskId, formId, param1);
423
+ for (const tid in task.list) {
424
+ const t = task.list[tid];
425
+ for (const fid in t.forms) {
426
+ const r = t.forms[fid].events[name]?.(taskId, formId, param1);
427
+ if (r instanceof Promise) {
428
+ r.catch(function(e) {
429
+ console.log(e);
430
+ });
431
+ }
432
+ }
433
+ }
434
+ break;
435
+ }
436
+ case 'formStateMinChanged':
437
+ case 'formStateMaxChanged':
438
+ case 'formShowChanged': {
439
+ (globalEvents as any)[name + 'Handler']?.(taskId, formId, param1);
440
+ for (const tid in task.list) {
441
+ const t = task.list[tid];
442
+ for (const fid in t.forms) {
443
+ const r = t.forms[fid].events[name]?.(taskId, formId, param1);
444
+ if (r instanceof Promise) {
445
+ r.catch(function(e) {
446
+ console.log(e);
447
+ });
448
+ }
449
+ }
450
+ }
451
+ break;
452
+ }
453
+ case 'formFocused':
454
+ case 'formBlurred':
455
+ case 'formFlash': {
456
+ (globalEvents as any)[name + 'Handler']?.(taskId, formId);
457
+ for (const tid in task.list) {
458
+ const t = task.list[tid];
459
+ for (const fid in t.forms) {
460
+ const r = t.forms[fid].events[name]?.(taskId, formId);
461
+ if (r instanceof Promise) {
462
+ r.catch(function(e) {
463
+ console.log(e);
464
+ });
465
+ }
466
+ }
467
+ }
468
+ break;
469
+ }
470
+ case 'taskStarted':
471
+ case 'taskEnded': {
472
+ (globalEvents as any)[name + 'Handler']?.(taskId, formId);
473
+ for (const tid in task.list) {
474
+ const t = task.list[tid];
475
+ for (const fid in t.forms) {
476
+ const r = t.forms[fid].events[name]?.(taskId);
477
+ if (r instanceof Promise) {
478
+ r.catch(function(e) {
479
+ console.log(e);
480
+ });
481
+ }
482
+ }
483
+ }
484
+ break;
485
+ }
486
+ }
487
+ }
488
+
489
+ /**
490
+ * --- cga 文件 blob 转 IApp 对象 ---
491
+ * @param blob blob 对象
492
+ */
493
+ export async function readApp(blob: Blob): Promise<false | types.IApp> {
494
+ const iconLength = parseInt(await blob.slice(0, 7).text());
495
+ const icon = await tool.blob2DataUrl(blob.slice(7, 7 + iconLength));
496
+ const z = await zip.get(blob.slice(7 + iconLength));
497
+ if (!z) {
498
+ return false;
499
+ }
500
+ // --- 开始读取文件 ---
501
+ const files: Record<string, Blob | string> = {};
502
+ /** --- 配置文件 --- */
503
+ const configContent = await z.getContent('/config.json');
504
+ if (!configContent) {
505
+ return false;
506
+ }
507
+ const config: types.IAppConfig = JSON.parse(configContent);
508
+ for (const file of config.files) {
509
+ const mime = tool.getMimeByPath(file);
510
+ if (['txt', 'json', 'js', 'css', 'xml', 'html'].includes(mime.ext)) {
511
+ const fab = await z.getContent(file, 'string');
512
+ if (!fab) {
513
+ continue;
514
+ }
515
+ files[file] = fab.replace(/^\ufeff/, '');
516
+ }
517
+ else {
518
+ const fab = await z.getContent(file, 'arraybuffer');
519
+ if (!fab) {
520
+ continue;
521
+ }
522
+ files[file] = new Blob([fab], {
523
+ 'type': mime.mime
524
+ });
525
+ }
526
+ }
527
+ if (!config) {
528
+ return false;
529
+ }
530
+ return {
531
+ 'type': 'app',
532
+ 'icon': icon,
533
+ 'config': config,
534
+ 'files': files
535
+ };
536
+ }
537
+
538
+ /**
539
+ * --- 从网址下载应用,App 模式下本方法不可用 ---
540
+ * @param url 对于当前网页的相对、绝对路径,以 / 结尾的目录或 .cga 结尾的文件 ---
541
+ * @param opt,notifyId:显示进度条的 notify id,current:设置则以设置的为准,否则以 location 为准 ---
542
+ */
543
+ export async function fetchApp(url: string, opt: types.ICoreFetchAppOptions = {}): Promise<null | types.IApp> {
544
+ /** --- 若是 cga 文件,则是 cga 的文件名,含 .cga --- */
545
+ let cga: string = '';
546
+ if (!url.endsWith('/')) {
547
+ const lio = url.lastIndexOf('/');
548
+ cga = lio === -1 ? url : url.slice(lio + 1);
549
+ if (!cga.endsWith('.cga')) {
550
+ return null;
551
+ }
552
+ }
553
+
554
+ let current = '';
555
+ if (opt.current) {
556
+ if (!opt.current.endsWith('/')) {
557
+ return null;
558
+ }
559
+ current = opt.current.slice(0, -1);
560
+ if (!url.startsWith('/')) {
561
+ url = '/current/' + url;
562
+ }
563
+ }
564
+ else {
565
+ if (!url.startsWith('/clickgo/') && !url.startsWith('/storage/') && !url.startsWith('/mounted/')) {
566
+ current = tool.urlResolve(window.location.href, url);
567
+ if (cga) {
568
+ current = current.slice(0, -cga.length - 1);
569
+ url = '/current/' + cga;
570
+ }
571
+ else {
572
+ current = current.slice(0, -1);
573
+ url = '/current/';
574
+ }
575
+ }
576
+ }
577
+
578
+ // --- 如果是 cga 文件,直接读取并交给 readApp 函数处理 ---
579
+ if (cga) {
580
+ if (opt.notifyId) {
581
+ const blob = await fs.getContent(url, {
582
+ 'current': current,
583
+ progress: (loaded, total): void => {
584
+ form.notifyProgress(opt.notifyId!, loaded / total);
585
+ }
586
+ });
587
+ if ((blob === null) || (typeof blob === 'string')) {
588
+ return null;
589
+ }
590
+ form.notifyProgress(opt.notifyId, 1);
591
+ return await readApp(blob) || null;
592
+ }
593
+ else {
594
+ try {
595
+ const blob = await fs.getContent(url, {
596
+ 'current': current
597
+ });
598
+ if ((blob === null) || typeof blob === 'string') {
599
+ return null;
600
+ }
601
+ return await readApp(blob) || null;
602
+ }
603
+ catch {
604
+ return null;
605
+ }
606
+ }
607
+ }
608
+ // --- 加载目录 ---
609
+ // --- 加载 json 文件,并创建 control 信息对象 ---
610
+ let config: types.IAppConfig;
611
+ // --- 已加载的 files ---
612
+ const files: Record<string, Blob | string> = {};
613
+ try {
614
+ const blob = await fs.getContent(url + 'config.json', {
615
+ 'current': current
616
+ });
617
+ if (blob === null || typeof blob === 'string') {
618
+ return null;
619
+ }
620
+ config = JSON.parse(await tool.blob2Text(blob));
621
+ await new Promise<void>(function(resolve) {
622
+ const total = config.files.length;
623
+ let loaded = 0;
624
+ for (const file of config.files) {
625
+ fs.getContent(url + file.slice(1), {
626
+ 'current': current
627
+ }).then(async function(blob) {
628
+ if (blob === null || typeof blob === 'string') {
629
+ clickgo.form.notify({
630
+ 'title': 'File not found',
631
+ 'content': url + file.slice(1),
632
+ 'type': 'danger'
633
+ });
634
+ return;
635
+ }
636
+ const mime = tool.getMimeByPath(file);
637
+ if (['txt', 'json', 'js', 'css', 'xml', 'html'].includes(mime.ext)) {
638
+ files[file] = (await tool.blob2Text(blob)).replace(/^\ufeff/, '');
639
+ }
640
+ else {
641
+ files[file] = blob;
642
+ }
643
+ ++loaded;
644
+ if (opt.notifyId) {
645
+ form.notifyProgress(opt.notifyId, loaded / total);
646
+ }
647
+ if (loaded < total) {
648
+ return;
649
+ }
650
+ resolve();
651
+ }).catch(function() {
652
+ ++loaded;
653
+ if (opt.notifyId) {
654
+ form.notifyProgress(opt.notifyId, loaded / total);
655
+ }
656
+ if (loaded < total) {
657
+ return;
658
+ }
659
+ resolve();
660
+ });
661
+ }
662
+ });
663
+ }
664
+ catch {
665
+ return null;
666
+ }
667
+ let icon = '/clickgo/icon.png';
668
+ if (config.icon && (files[config.icon] instanceof Blob)) {
669
+ icon = await tool.blob2DataUrl(files[config.icon] as Blob);
670
+ }
671
+ return {
672
+ 'type': 'app',
673
+ 'icon': icon,
674
+ 'config': config,
675
+ 'files': files
676
+ };
677
+ }
678
+
679
+ /**
680
+ * --- 获取屏幕可用区域 ---
681
+ */
682
+ export function getAvailArea(): types.IAvailArea {
683
+ if (Object.keys(form.simpleSystemTaskRoot.forms).length > 0) {
684
+ return {
685
+ 'left': 0,
686
+ 'top': 0,
687
+ 'width': document.body.clientWidth,
688
+ 'height': document.body.clientHeight - 46
689
+ };
690
+ }
691
+ else {
692
+ let left: number = 0;
693
+ let top: number = 0;
694
+ let width: number = 0;
695
+ let height: number = 0;
696
+ switch (config['task.position']) {
697
+ case 'left': {
698
+ left = task.systemTaskInfo.length;
699
+ top = 0;
700
+ width = document.body.clientWidth - task.systemTaskInfo.length;
701
+ height = document.body.clientHeight;
702
+ break;
703
+ }
704
+ case 'right': {
705
+ left = 0;
706
+ top = 0;
707
+ width = document.body.clientWidth - task.systemTaskInfo.length;
708
+ height = document.body.clientHeight;
709
+ break;
710
+ }
711
+ case 'top': {
712
+ left = 0;
713
+ top = task.systemTaskInfo.length;
714
+ width = document.body.clientWidth;
715
+ height = document.body.clientHeight - task.systemTaskInfo.length;
716
+ break;
717
+ }
718
+ case 'bottom': {
719
+ left = 0;
720
+ top = 0;
721
+ width = document.body.clientWidth;
722
+ height = document.body.clientHeight - task.systemTaskInfo.length;
723
+ }
724
+ }
725
+ return {
726
+ 'left': left,
727
+ 'top': top,
728
+ 'width': width,
729
+ 'height': height
730
+ };
731
+ }
732
+ }