clickgo 3.0.7-dev8 → 3.1.0-dev9

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/dist/lib/core.ts CHANGED
@@ -19,8 +19,12 @@ import * as fs from './fs';
19
19
  import * as form from './form';
20
20
  import * as task from './task';
21
21
  import * as tool from './tool';
22
+ import * as control from './control';
23
+ import * as theme from './theme';
22
24
  import * as zip from './zip';
25
+ import * as dom from './dom';
23
26
 
27
+ /** --- Config 原始参考对象 --- */
24
28
  const configOrigin: types.IConfig = {
25
29
  'locale': 'en',
26
30
  'task.position': 'bottom',
@@ -31,6 +35,8 @@ const configOrigin: types.IConfig = {
31
35
  'desktop.path': null,
32
36
  'launcher.list': []
33
37
  };
38
+
39
+ /** --- Config 配置对象 --- */
34
40
  export const config: types.IConfig = clickgo.vue.reactive({
35
41
  'locale': 'en',
36
42
  'task.position': 'bottom',
@@ -42,7 +48,296 @@ export const config: types.IConfig = clickgo.vue.reactive({
42
48
  'launcher.list': []
43
49
  });
44
50
 
45
- export const cdn = '';
51
+ /** --- App 抽象类 --- */
52
+ export abstract class AbstractApp {
53
+
54
+ /** --- 当前 js 文件在包内的完整路径 --- */
55
+ public get filename(): string {
56
+ return '';
57
+ }
58
+
59
+ /** --- invoke 时系统会自动设置本项 --- */
60
+ public get taskId(): number {
61
+ return 0;
62
+ }
63
+
64
+ public set taskId(v: number) {
65
+ form.notify({
66
+ 'title': 'Error',
67
+ 'content': `The software tries to modify the system variable "taskId" to "${v}".\nPath: ${this.filename}`,
68
+ 'type': 'danger'
69
+ });
70
+ }
71
+
72
+ /** --- App 的入口文件 --- */
73
+ public abstract main(): Promise<void>;
74
+
75
+ /**
76
+ * --- 先设置 App 的配置信息 ---
77
+ * @param config 配置对象
78
+ */
79
+ public async config(config: types.IAppConfig): Promise<boolean> {
80
+ const t = task.list[this.taskId];
81
+ if (!t) {
82
+ return false;
83
+ }
84
+ t.config = config;
85
+ // --- 加载余下的 xml 等静态包内资源,仅限 net 的 app 模式 ---
86
+ if (t.app.net) {
87
+ if (!t.config.files) {
88
+ return false;
89
+ }
90
+ const files = t.config.files;
91
+ const net = t.app.net;
92
+ await new Promise<void>(function(resolve) {
93
+ let loaded = 0;
94
+ const total = files.length;
95
+ const beforeTotal = Object.keys(t.app.files).length;
96
+ net.progress?.(loaded + beforeTotal, total + beforeTotal) as unknown;
97
+ for (const file of files) {
98
+ fs.getContent(net.url + file.slice(1), {
99
+ 'current': net.current
100
+ }).then(async function(blob) {
101
+ if (blob === null || typeof blob === 'string') {
102
+ clickgo.form.notify({
103
+ 'title': 'File not found',
104
+ 'content': net.url + file.slice(1),
105
+ 'type': 'danger'
106
+ });
107
+ return;
108
+ }
109
+ const mime = tool.getMimeByPath(file);
110
+ if (['txt', 'json', 'js', 'css', 'xml', 'html'].includes(mime.ext)) {
111
+ t.app.files[file] = (await tool.blob2Text(blob)).replace(/^\ufeff/, '');
112
+ }
113
+ else {
114
+ t.app.files[file] = blob;
115
+ }
116
+ ++loaded;
117
+ net.progress?.(loaded + beforeTotal, total + beforeTotal) as unknown;
118
+ if (net.notify) {
119
+ form.notifyProgress(net.notify, (loaded / total) / 2 + 0.5);
120
+ }
121
+ if (loaded < total) {
122
+ return;
123
+ }
124
+ resolve();
125
+ }).catch(function() {
126
+ ++loaded;
127
+ net.progress?.(loaded + beforeTotal, total + beforeTotal) as unknown;
128
+ if (net.notify) {
129
+ form.notifyProgress(net.notify, (loaded / total) / 2 + 0.5);
130
+ }
131
+ if (loaded < total) {
132
+ return;
133
+ }
134
+ resolve();
135
+ });
136
+ }
137
+ });
138
+ if (net.notify) {
139
+ setTimeout(function(): void {
140
+ form.hideNotify(net.notify!);
141
+ }, 2000);
142
+ }
143
+ }
144
+ // --- 要加载 control ---
145
+ if (!await control.init(this.taskId)) {
146
+ return false;
147
+ }
148
+ // --- theme ---
149
+ if (config.themes?.length) {
150
+ for (let path of config.themes) {
151
+ path += '.cgt';
152
+ path = tool.urlResolve('/', path);
153
+ const file = await fs.getContent(path, {
154
+ 'files': t.app.files,
155
+ 'current': t.current
156
+ });
157
+ if (file && typeof file !== 'string') {
158
+ const th = await theme.read(file);
159
+ if (th) {
160
+ await theme.load(th, t.id);
161
+ }
162
+ }
163
+ }
164
+ }
165
+ else {
166
+ // --- 加载全局主题 ---
167
+ if (theme.global) {
168
+ await theme.load(undefined, this.taskId);
169
+ }
170
+ }
171
+ // --- locale ---
172
+ if (config.locales) {
173
+ for (let path in config.locales) {
174
+ const locale = config.locales[path];
175
+ if (!path.endsWith('.json')) {
176
+ path += '.json';
177
+ }
178
+ const lcontent = await fs.getContent(path, {
179
+ 'encoding': 'utf8',
180
+ 'files': t.app.files,
181
+ 'current': t.current
182
+ });
183
+ if (!lcontent) {
184
+ continue;
185
+ }
186
+ try {
187
+ const data = JSON.parse(lcontent);
188
+ task.loadLocaleData(locale, data, '', t.id);
189
+ }
190
+ catch {
191
+ // --- 无所谓 ---
192
+ }
193
+ }
194
+ }
195
+ // --- 加载任务级全局样式 ---
196
+ if (config.style) {
197
+ const style = await fs.getContent(config.style + '.css', {
198
+ 'encoding': 'utf8',
199
+ 'files': t.app.files,
200
+ 'current': t.current
201
+ });
202
+ if (style) {
203
+ const r = tool.stylePrepend(style, 'cg-task' + this.taskId.toString() + '_');
204
+ dom.pushStyle(this.taskId, await tool.styleUrl2DataUrl(config.style, r.style, t.app.files));
205
+ }
206
+ }
207
+ // --- 加载图标 ---
208
+ if (config.icon) {
209
+ const icon = await fs.getContent(config.icon, {
210
+ 'files': t.app.files,
211
+ 'current': t.current
212
+ });
213
+ if (icon && typeof icon !== 'string') {
214
+ t.app.icon = await tool.blob2DataUrl(icon);
215
+ }
216
+ }
217
+ // --- 全部成功,设置 t.class 为自己 ---
218
+ t.class = this;
219
+ return true;
220
+ }
221
+
222
+ /**
223
+ * --- 以某个窗体进行正式启动这个 app(入口 form),不启动则任务也启动失败 ---
224
+ * @param form 窗体对象
225
+ */
226
+ public run(form: form.AbstractForm | number): void {
227
+ if (typeof form === 'number') {
228
+ // --- 报错 ---
229
+ const msg = 'Application run error, Form creation failed (' + form.toString() + ').';
230
+ trigger('error', this.taskId, 0, new Error(msg), msg);
231
+ return;
232
+ }
233
+ form.show();
234
+ }
235
+
236
+ /** --- 全局错误事件 --- */
237
+ public onError(taskId: number, formId: number, error: Error, info: string): void | Promise<void>;
238
+ public onError(): void {
239
+ return;
240
+ }
241
+
242
+ /** --- 屏幕大小改变事件 --- */
243
+ public onScreenResize(): void | Promise<void>;
244
+ public onScreenResize(): void {
245
+ return;
246
+ }
247
+
248
+ /** --- 系统配置变更事件 --- */
249
+ public onConfigChanged<T extends types.IConfig, TK extends keyof T>(n: TK, v: T[TK]): void | Promise<void>;
250
+ public onConfigChanged(): void {
251
+ return;
252
+ }
253
+
254
+ /** --- 窗体创建事件 --- */
255
+ public onFormCreated(taskId: number, formId: number, title: string, icon: string): void | Promise<void>;
256
+ public onFormCreated(): void {
257
+ return;
258
+ }
259
+
260
+ /** --- 窗体销毁事件 */
261
+ public onFormRemoved(taskId: number, formId: number, title: string, icon: string): void | Promise<void>;
262
+ public onFormRemoved(): void {
263
+ return;
264
+ }
265
+
266
+ /** --- 窗体标题改变事件 */
267
+ public onFormTitleChanged(taskId: number, formId: number, title: string): void | Promise<void>;
268
+ public onFormTitleChanged(): void | Promise<void> {
269
+ return;
270
+ }
271
+
272
+ /** --- 窗体图标改变事件 --- */
273
+ public onFormIconChanged(taskId: number, formId: number, icon: string): void | Promise<void>;
274
+ public onFormIconChanged(): void | Promise<void> {
275
+ return;
276
+ }
277
+
278
+ /** --- 窗体最小化状态改变事件 --- */
279
+ public onFormStateMinChanged(taskId: number, formId: number, state: boolean): void | Promise<void>;
280
+ public onFormStateMinChanged(): void {
281
+ return;
282
+ }
283
+
284
+ /** --- 窗体最大化状态改变事件 --- */
285
+ public onFormStateMaxChanged(taskId: number, formId: number, state: boolean): void | Promise<void>;
286
+ public onFormStateMaxChanged(): void {
287
+ return;
288
+ }
289
+
290
+ /** --- 窗体显示状态改变事件 --- */
291
+ public onFormShowChanged(taskId: number, formId: number, state: boolean): void | Promise<void>;
292
+ public onFormShowChanged(): void {
293
+ return;
294
+ }
295
+
296
+ /** --- 窗体获得焦点事件 --- */
297
+ public onFormFocused(taskId: number, formId: number): void | Promise<void>;
298
+ public onFormFocused(): void {
299
+ return;
300
+ }
301
+
302
+ /** --- 窗体丢失焦点事件 --- */
303
+ public onFormBlurred(taskId: number, formId: number): void | Promise<void>;
304
+ public onFormBlurred(): void {
305
+ return;
306
+ }
307
+
308
+ /** --- 窗体闪烁事件 --- */
309
+ public onFormFlash(taskId: number, formId: number): void | Promise<void>;
310
+ public onFormFlash(): void {
311
+ return;
312
+ }
313
+
314
+ /** --- 任务开始事件 --- */
315
+ public onTaskStarted(taskId: number): void | Promise<void>;
316
+ public onTaskStarted(): void | Promise<void> {
317
+ return;
318
+ }
319
+
320
+ /** --- 任务结束事件 --- */
321
+ public onTaskEnded(taskId: number): void | Promise<void>;
322
+ public onTaskEnded(): void | Promise<void> {
323
+ return;
324
+ }
325
+
326
+ /** --- launcher 文件夹名称修改事件 --- */
327
+ public onLauncherFolderNameChanged(id: string, name: string): void | Promise<void>;
328
+ public onLauncherFolderNameChanged(): void {
329
+ return;
330
+ }
331
+
332
+ }
333
+
334
+ /** --- CDN 地址 --- */
335
+ export function getCdn(): string {
336
+ return loader.cdn;
337
+ }
338
+
339
+ /** --- boot 类 --- */
340
+ export let boot: import('../index').AbstractBoot;
46
341
 
47
342
  clickgo.vue.watch(config, function() {
48
343
  // --- 检测有没有缺少的 config key ---
@@ -101,7 +396,7 @@ const modules: Record<string, { func: () => any | Promise<any>; 'obj': null | an
101
396
  'monaco': {
102
397
  func: async function() {
103
398
  return new Promise(function(resolve, reject) {
104
- fetch(loader.cdn + '/npm/monaco-editor@0.33.0/min/vs/loader.js').then(function(r) {
399
+ fetch(loader.cdn + '/npm/monaco-editor@0.34.1/min/vs/loader.js').then(function(r) {
105
400
  return r.blob();
106
401
  }).then(function(b) {
107
402
  return tool.blob2DataUrl(b);
@@ -118,7 +413,7 @@ const modules: Record<string, { func: () => any | Promise<any>; 'obj': null | an
118
413
  };
119
414
 
120
415
  /**
121
- * --- 注册新的外接模块 ---
416
+ * --- 注册新的外接模块,App 模式下无效 ---
122
417
  * @param name 模块名
123
418
  * @param func 执行加载函数
124
419
  */
@@ -220,33 +515,30 @@ export function getModule(name: string): null | any {
220
515
  return modules[name].obj;
221
516
  }
222
517
 
223
- /** --- 全局响应事件 --- */
224
- export const globalEvents: types.IGlobalEvents = {
225
- errorHandler: null,
226
- screenResizeHandler: function(): void {
518
+ /** --- 系统要处理的全局响应事件 --- */
519
+ const globalEvents = {
520
+ screenResize: function(): void {
227
521
  form.refreshMaxPosition();
228
522
  },
229
- configChangedHandler: null,
230
- formCreatedHandler: null,
231
- formRemovedHandler: function(taskId: number, formId: number): void {
523
+ formRemoved: function(taskId: number, formId: number): void {
232
524
  if (!form.simpleSystemTaskRoot.forms[formId]) {
233
525
  return;
234
526
  }
235
527
  delete form.simpleSystemTaskRoot.forms[formId];
236
528
  },
237
- formTitleChangedHandler: function(taskId: number, formId: number, title: string): void {
529
+ formTitleChanged: function(taskId: number, formId: number, title: string): void {
238
530
  if (!form.simpleSystemTaskRoot.forms[formId]) {
239
531
  return;
240
532
  }
241
533
  form.simpleSystemTaskRoot.forms[formId].title = title;
242
534
  },
243
- formIconChangedHandler: function(taskId: number, formId: number, icon: string): void {
535
+ formIconChanged: function(taskId: number, formId: number, icon: string): void {
244
536
  if (!form.simpleSystemTaskRoot.forms[formId]) {
245
537
  return;
246
538
  }
247
539
  form.simpleSystemTaskRoot.forms[formId].icon = icon;
248
540
  },
249
- formStateMinChangedHandler: function(taskId: number, formId: number, state: boolean): void {
541
+ formStateMinChanged: function(taskId: number, formId: number, state: boolean): void {
250
542
  if (task.systemTaskInfo.taskId > 0) {
251
543
  return;
252
544
  }
@@ -266,119 +558,37 @@ export const globalEvents: types.IGlobalEvents = {
266
558
  }
267
559
  delete form.simpleSystemTaskRoot.forms[formId];
268
560
  }
269
- },
270
- formStateMaxChangedHandler: null,
271
- formShowChangedHandler: null,
272
- formFocusedHandler: null,
273
- formBlurredHandler: null,
274
- formFlashHandler: null,
275
- taskStartedHandler: null,
276
- taskEndedHandler: null,
277
- launcherFolderNameChangedHandler: null
278
- };
279
-
280
- /**
281
- * --- 设置系统事件监听,一个窗体只能设置一个监听 ---
282
- * @param name 系统事件名
283
- * @param func 回调函数
284
- * @param formId 窗体 id,app 模式下留空为当前窗体
285
- * @param taskId 任务 id,app 模式下无效
286
- */
287
- export function setSystemEventListener(
288
- name: types.TGlobalEvent,
289
- func: (...any: any) => void | Promise<void>,
290
- formId?: number,
291
- taskId?: number
292
- ): void {
293
- if (!taskId) {
294
- return;
295
- }
296
- const t = task.list[taskId];
297
- if (!t) {
298
- return;
299
- }
300
- if (!formId) {
301
- return;
302
- }
303
- const f = t.forms[formId];
304
- if (!f) {
305
- return;
306
- }
307
- f.events[name] = func;
308
- }
309
-
310
- /**
311
- * --- 移除系统事件监听,一个窗体只能设置一个监听 ---
312
- * @param name name 系统事件名
313
- * @param formId 窗体 id,app 默认为当前窗体
314
- * @param taskId 任务 id,app 模式下无效
315
- */
316
- export function removeSystemEventListener(
317
- name: types.TGlobalEvent,
318
- formId?: number,
319
- taskId?: number
320
- ): void {
321
- if (!taskId) {
322
- return;
323
561
  }
324
- const t = task.list[taskId];
325
- if (!t) {
326
- return;
327
- }
328
- if (!formId) {
329
- return;
330
- }
331
- const f = t.forms[formId];
332
- if (!f) {
333
- return;
334
- }
335
- delete f.events[name];
336
- }
562
+ };
337
563
 
338
564
  /**
339
- * --- 主动触发系统级事件 ---
565
+ * --- 主动触发系统级事件,App 中无效,用 this.trigger 替代 ---
340
566
  */
341
- 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 {
567
+ export function trigger(name: types.TGlobalEvent, taskId: number | string | boolean = 0, formId: number | string | boolean | Record<string, any> | null = 0, param1: boolean | Error | string = '', param2: string = ''): void {
568
+ const eventName = 'on' + name[0].toUpperCase() + name.slice(1);
342
569
  switch (name) {
343
570
  case 'error': {
344
571
  if (typeof taskId !== 'number' || typeof formId !== 'number') {
345
572
  break;
346
573
  }
347
- const r = globalEvents.errorHandler?.(taskId, formId, param1 as Error, param2);
348
- if (r && (r instanceof Promise)) {
349
- r.catch(function(e) {
350
- console.log(e);
351
- });
352
- }
574
+ (boot as any)[eventName](taskId, formId, param1, param2);
353
575
  for (const tid in task.list) {
354
576
  const t = task.list[tid];
577
+ (t.class as any)?.[eventName](taskId, formId, param1, param2);
355
578
  for (const fid in t.forms) {
356
- const r = t.forms[fid].events[name]?.(taskId, formId, param1, param2);
357
- if (r instanceof Promise) {
358
- r.catch(function(e) {
359
- console.log(e);
360
- });
361
- }
579
+ t.forms[fid].vroot[eventName]?.(taskId, formId, param1, param2);
362
580
  }
363
581
  }
364
582
  break;
365
583
  }
366
584
  case 'screenResize': {
367
- const r = globalEvents.screenResizeHandler?.();
368
- if (r && (r instanceof Promise)) {
369
- r.catch(function(e) {
370
- console.log(e);
371
- });
372
- }
585
+ globalEvents.screenResize();
586
+ (boot as any)[eventName]();
373
587
  for (const tid in task.list) {
374
588
  const t = task.list[tid];
589
+ (t.class as any)?.[eventName]();
375
590
  for (const fid in t.forms) {
376
- const r = t.forms[fid].events[name]?.();
377
- if (r instanceof Promise) {
378
- r.catch(function(e) {
379
- console.log(e);
380
- });
381
- }
591
+ t.forms[fid].vroot[eventName]?.();
382
592
  }
383
593
  }
384
594
  break;
@@ -387,53 +597,38 @@ export function trigger(name: types.TGlobalEvent, taskId: number | string = 0, f
387
597
  if ((typeof taskId !== 'string') || (typeof formId === 'number')) {
388
598
  break;
389
599
  }
390
- const r = globalEvents.configChangedHandler?.(taskId as types.TConfigName, formId);
391
- if (r && (r instanceof Promise)) {
392
- r.catch(function(e) {
393
- console.log(e);
394
- });
395
- }
600
+ (boot as any)[eventName]();
396
601
  for (const tid in task.list) {
397
602
  const t = task.list[tid];
603
+ (t.class as any)?.[eventName](taskId, formId);
398
604
  for (const fid in t.forms) {
399
- const r = t.forms[fid].events[name]?.(taskId, formId);
400
- if (r instanceof Promise) {
401
- r.catch(function(e) {
402
- console.log(e);
403
- });
404
- }
605
+ t.forms[fid].vroot[eventName]?.(taskId, formId);
405
606
  }
406
607
  }
407
608
  break;
408
609
  }
409
610
  case 'formCreated':
410
611
  case 'formRemoved': {
411
- (globalEvents as any)[name + 'Handler']?.(taskId, formId, param1, param2);
612
+ (globalEvents as any)[name]?.(taskId, formId, param1, param2);
613
+ (boot as any)[eventName](taskId, formId, param1, param2);
412
614
  for (const tid in task.list) {
413
615
  const t = task.list[tid];
616
+ (t.class as any)?.[eventName](taskId, formId, param1, param2);
414
617
  for (const fid in t.forms) {
415
- const r = t.forms[fid].events[name]?.(taskId, formId, param1, param2);
416
- if (r instanceof Promise) {
417
- r.catch(function(e) {
418
- console.log(e);
419
- });
420
- }
618
+ t.forms[fid].vroot[eventName]?.(taskId, formId, param1, param2);
421
619
  }
422
620
  }
423
621
  break;
424
622
  }
425
623
  case 'formTitleChanged':
426
624
  case 'formIconChanged': {
427
- (globalEvents as any)[name + 'Handler']?.(taskId, formId, param1);
625
+ (globalEvents as any)[name]?.(taskId, formId, param1);
626
+ (boot as any)[eventName](taskId, formId, param1);
428
627
  for (const tid in task.list) {
429
628
  const t = task.list[tid];
629
+ (t.class as any)?.[eventName](taskId, formId, param1);
430
630
  for (const fid in t.forms) {
431
- const r = t.forms[fid].events[name]?.(taskId, formId, param1);
432
- if (r instanceof Promise) {
433
- r.catch(function(e) {
434
- console.log(e);
435
- });
436
- }
631
+ t.forms[fid].vroot[eventName]?.(taskId, formId, param1);
437
632
  }
438
633
  }
439
634
  break;
@@ -441,16 +636,13 @@ export function trigger(name: types.TGlobalEvent, taskId: number | string = 0, f
441
636
  case 'formStateMinChanged':
442
637
  case 'formStateMaxChanged':
443
638
  case 'formShowChanged': {
444
- (globalEvents as any)[name + 'Handler']?.(taskId, formId, param1);
639
+ (globalEvents as any)[name]?.(taskId, formId, param1);
640
+ (boot as any)[eventName](taskId, formId, param1);
445
641
  for (const tid in task.list) {
446
642
  const t = task.list[tid];
643
+ (t.class as any)?.[eventName](taskId, formId, param1);
447
644
  for (const fid in t.forms) {
448
- const r = t.forms[fid].events[name]?.(taskId, formId, param1);
449
- if (r instanceof Promise) {
450
- r.catch(function(e) {
451
- console.log(e);
452
- });
453
- }
645
+ t.forms[fid].vroot[eventName]?.(taskId, formId, param1);
454
646
  }
455
647
  }
456
648
  break;
@@ -458,32 +650,26 @@ export function trigger(name: types.TGlobalEvent, taskId: number | string = 0, f
458
650
  case 'formFocused':
459
651
  case 'formBlurred':
460
652
  case 'formFlash': {
461
- (globalEvents as any)[name + 'Handler']?.(taskId, formId);
653
+ (globalEvents as any)[name]?.(taskId, formId);
654
+ (boot as any)[eventName](taskId, formId);
462
655
  for (const tid in task.list) {
463
656
  const t = task.list[tid];
657
+ (t.class as any)?.[eventName](taskId, formId);
464
658
  for (const fid in t.forms) {
465
- const r = t.forms[fid].events[name]?.(taskId, formId);
466
- if (r instanceof Promise) {
467
- r.catch(function(e) {
468
- console.log(e);
469
- });
470
- }
659
+ t.forms[fid].vroot[eventName]?.(taskId, formId);
471
660
  }
472
661
  }
473
662
  break;
474
663
  }
475
664
  case 'taskStarted':
476
665
  case 'taskEnded': {
477
- (globalEvents as any)[name + 'Handler']?.(taskId, formId);
666
+ (globalEvents as any)[name]?.(taskId, formId);
667
+ (boot as any)[eventName](taskId, formId);
478
668
  for (const tid in task.list) {
479
669
  const t = task.list[tid];
670
+ (t.class as any)?.[eventName](taskId);
480
671
  for (const fid in t.forms) {
481
- const r = t.forms[fid].events[name]?.(taskId);
482
- if (r instanceof Promise) {
483
- r.catch(function(e) {
484
- console.log(e);
485
- });
486
- }
672
+ t.forms[fid].vroot[eventName]?.(taskId);
487
673
  }
488
674
  }
489
675
  break;
@@ -495,21 +681,12 @@ export function trigger(name: types.TGlobalEvent, taskId: number | string = 0, f
495
681
  if (typeof taskId === 'number') {
496
682
  taskId = taskId.toString();
497
683
  }
498
- const r = globalEvents.launcherFolderNameChangedHandler?.(taskId, formId);
499
- if (r && (r instanceof Promise)) {
500
- r.catch(function(e) {
501
- console.log(e);
502
- });
503
- }
684
+ (boot as any)[eventName](taskId, formId);
504
685
  for (const tid in task.list) {
505
686
  const t = task.list[tid];
687
+ (t.class as any)?.[eventName](taskId, formId);
506
688
  for (const fid in t.forms) {
507
- const r = t.forms[fid].events[name]?.(taskId, formId);
508
- if (r instanceof Promise) {
509
- r.catch(function(e) {
510
- console.log(e);
511
- });
512
- }
689
+ t.forms[fid].vroot[eventName]?.(taskId, formId);
513
690
  }
514
691
  }
515
692
  break;
@@ -518,7 +695,7 @@ export function trigger(name: types.TGlobalEvent, taskId: number | string = 0, f
518
695
  }
519
696
 
520
697
  /**
521
- * --- cga 文件 blob IApp 对象 ---
698
+ * --- cga blob 文件解包 ---
522
699
  * @param blob blob 对象
523
700
  */
524
701
  export async function readApp(blob: Blob): Promise<false | types.IApp> {
@@ -530,38 +707,30 @@ export async function readApp(blob: Blob): Promise<false | types.IApp> {
530
707
  }
531
708
  // --- 开始读取文件 ---
532
709
  const files: Record<string, Blob | string> = {};
533
- /** --- 配置文件 --- */
534
- const configContent = await z.getContent('/config.json');
535
- if (!configContent) {
536
- return false;
537
- }
538
- const config: types.IAppConfig = JSON.parse(configContent);
539
- for (const file of config.files) {
540
- const mime = tool.getMimeByPath(file);
710
+ const list = z.readDir('/', {
711
+ 'hasChildren': true
712
+ });
713
+ for (const file of list) {
714
+ const mime = tool.getMimeByPath(file.name);
541
715
  if (['txt', 'json', 'js', 'css', 'xml', 'html'].includes(mime.ext)) {
542
- const fab = await z.getContent(file, 'string');
716
+ const fab = await z.getContent(file.path + file.name, 'string');
543
717
  if (!fab) {
544
718
  continue;
545
719
  }
546
- files[file] = fab.replace(/^\ufeff/, '');
720
+ files[file.path + file.name] = fab.replace(/^\ufeff/, '');
547
721
  }
548
722
  else {
549
- const fab = await z.getContent(file, 'arraybuffer');
723
+ const fab = await z.getContent(file.path + file.name, 'arraybuffer');
550
724
  if (!fab) {
551
725
  continue;
552
726
  }
553
- files[file] = new Blob([fab], {
727
+ files[file.path + file.name] = new Blob([fab], {
554
728
  'type': mime.mime
555
729
  });
556
730
  }
557
731
  }
558
- if (!config) {
559
- return false;
560
- }
561
732
  return {
562
- 'type': 'app',
563
733
  'icon': icon,
564
- 'config': config,
565
734
  'files': files
566
735
  };
567
736
  }
@@ -569,9 +738,12 @@ export async function readApp(blob: Blob): Promise<false | types.IApp> {
569
738
  /**
570
739
  * --- 从网址下载应用,App 模式下本方法不可用 ---
571
740
  * @param url 对于当前网页的相对、绝对路径,以 / 结尾的目录或 .cga 结尾的文件 ---
572
- * @param opt,notifyId:显示进度条的 notify id,current:设置则以设置的为准,以 / 结尾,否则以 location 为准 ---
741
+ * @param opt,notifyId:显示进度条的 notify id,current:设置则以设置的为准,不以 / 结尾,否则以 location 为准 ---
573
742
  */
574
- export async function fetchApp(url: string, opt: types.ICoreFetchAppOptions = {}): Promise<null | types.IApp> {
743
+ export async function fetchApp(
744
+ url: string,
745
+ opt: types.ICoreFetchAppOptions = {}
746
+ ): Promise<null | types.IApp> {
575
747
  /** --- 若是 cga 文件,则是 cga 的文件名,含 .cga --- */
576
748
  let cga: string = '';
577
749
  if (!url.endsWith('/')) {
@@ -584,18 +756,14 @@ export async function fetchApp(url: string, opt: types.ICoreFetchAppOptions = {}
584
756
 
585
757
  let current = '';
586
758
  if (opt.current) {
587
- if (!opt.current.endsWith('/')) {
588
- return null;
589
- }
590
- if (!url.startsWith('/')) {
591
- url = '/current/' + url;
592
- }
759
+ current = opt.current.endsWith('/') ? opt.current.slice(0, -1) : opt.current;
760
+ url = tool.urlResolve('/current/', url);
593
761
  }
594
762
  else {
595
763
  if (!url.startsWith('/clickgo/') && !url.startsWith('/storage/') && !url.startsWith('/mounted/')) {
596
764
  current = tool.urlResolve(window.location.href, url);
597
765
  if (cga) {
598
- current = current.slice(0, -cga.length);
766
+ current = current.slice(0, -cga.length - 1);
599
767
  url = '/current/' + cga;
600
768
  }
601
769
  else {
@@ -630,75 +798,52 @@ export async function fetchApp(url: string, opt: types.ICoreFetchAppOptions = {}
630
798
  return null;
631
799
  }
632
800
  }
633
- // --- 加载目录 ---
634
- // --- 加载 json 文件,并创建 control 信息对象 ---
635
- let config: types.IAppConfig;
636
- // --- 已加载的 files ---
637
- const files: Record<string, Blob | string> = {};
638
- try {
639
- const blob = await fs.getContent(url + 'config.json', {
640
- 'current': current
641
- });
642
- if (blob === null || typeof blob === 'string') {
643
- return null;
644
- }
645
- config = JSON.parse(await tool.blob2Text(blob));
646
- await new Promise<void>(function(resolve) {
647
- const total = config.files.length;
648
- let loaded = 0;
649
- for (const file of config.files) {
650
- fs.getContent(url + file.slice(1), {
651
- 'current': current
652
- }).then(async function(blob) {
653
- if (blob === null || typeof blob === 'string') {
654
- clickgo.form.notify({
655
- 'title': 'File not found',
656
- 'content': url + file.slice(1),
657
- 'type': 'danger'
658
- });
659
- return;
660
- }
661
- const mime = tool.getMimeByPath(file);
662
- if (['txt', 'json', 'js', 'css', 'xml', 'html'].includes(mime.ext)) {
663
- files[file] = (await tool.blob2Text(blob)).replace(/^\ufeff/, '');
664
- }
665
- else {
666
- files[file] = blob;
667
- }
668
- ++loaded;
669
- opt.progress?.(loaded, total) as unknown;
670
- if (opt.notifyId) {
671
- form.notifyProgress(opt.notifyId, loaded / total);
672
- }
673
- if (loaded < total) {
674
- return;
675
- }
676
- resolve();
677
- }).catch(function() {
678
- ++loaded;
679
- opt.progress?.(loaded, total) as unknown;
680
- if (opt.notifyId) {
681
- form.notifyProgress(opt.notifyId, loaded / total);
682
- }
683
- if (loaded < total) {
684
- return;
685
- }
686
- resolve();
687
- });
801
+ // --- 从网络嗅探 ---
802
+ let loaded = 0;
803
+ let total = 30;
804
+ // --- 网络嗅探,不知道文件总数,先暂定 30,不过超出 30 文件的建议打包为 cga ---
805
+ const files = await loader.sniffFiles(url + 'app.js', {
806
+ 'dir': '/',
807
+ adapter: async (url) => {
808
+ const r = await fs.getContent(url, {
809
+ 'encoding': 'utf8',
810
+ 'current': current
811
+ });
812
+ return r;
813
+ },
814
+ 'loaded': () => {
815
+ ++loaded;
816
+ if (loaded === total) {
817
+ ++total;
688
818
  }
689
- });
819
+ if (opt.notifyId) {
820
+ form.notifyProgress(opt.notifyId, (loaded / total) / 2);
821
+ }
822
+ if (opt.progress) {
823
+ opt.progress(loaded, total) as unknown;
824
+ }
825
+ }
826
+ });
827
+ // --- net 模式此处加载完只算到 50%,因为还有 app 类当中 config 中的静态部分,暂定预留 50% ---
828
+ if (opt.notifyId) {
829
+ form.notifyProgress(opt.notifyId, 0.5);
690
830
  }
691
- catch {
831
+ if (Object.keys(files).length === 0) {
692
832
  return null;
693
833
  }
694
- let icon = '/clickgo/icon.png';
695
- if (config.icon && (files[config.icon] instanceof Blob)) {
696
- icon = await tool.blob2DataUrl(files[config.icon] as Blob);
834
+ const ul = url.length - 1;
835
+ for (const fn in files) {
836
+ files[fn.slice(ul)] = files[fn];
837
+ delete files[fn];
697
838
  }
698
839
  return {
699
- 'type': 'app',
700
- 'icon': icon,
701
- 'config': config,
840
+ 'net': {
841
+ 'current': current,
842
+ 'notify': opt.notifyId,
843
+ 'url': url,
844
+ 'progress': opt.progress
845
+ },
846
+ 'icon': '',
702
847
  'files': files
703
848
  };
704
849
  }