clickgo 6.8.2 → 6.10.0

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 (58) hide show
  1. package/AGENTS.md +16 -0
  2. package/README.md +4 -0
  3. package/dist/app/demo.cga +0 -0
  4. package/dist/app/task.cga +0 -0
  5. package/dist/clickgo.d.ts +8 -1
  6. package/dist/control/common.cgc +0 -0
  7. package/dist/control/form.cgc +0 -0
  8. package/dist/control/novnc.cgc +0 -0
  9. package/dist/control/task.cgc +0 -0
  10. package/dist/global.css +1 -1
  11. package/dist/index.js +1 -1
  12. package/dist/lib/command/native.d.ts +18 -0
  13. package/dist/lib/command/palette.d.ts +10 -0
  14. package/dist/lib/command/schema.d.ts +31 -0
  15. package/dist/lib/command.d.ts +218 -0
  16. package/dist/lib/core.d.ts +7 -1
  17. package/dist/lib/form.d.ts +15 -2
  18. package/dist/lib/native.d.ts +12 -0
  19. package/dist/lib/task.d.ts +2 -0
  20. package/dist/theme/admin.cgt +0 -0
  21. package/dist/theme/classic.cgt +0 -0
  22. package/dist/theme/cyber.cgt +0 -0
  23. package/dist/theme/dark.cgt +0 -0
  24. package/dist/theme/light.cgt +0 -0
  25. package/dist/theme/modern-light.cgt +0 -0
  26. package/dist/theme/modern.cgt +0 -0
  27. package/dist/theme/software-dark.cgt +0 -0
  28. package/dist/theme/software-light.cgt +0 -0
  29. package/doc/clickgo-rag.md +2735 -711
  30. package/package.json +11 -4
  31. package/test/core-module-loading.mjs +0 -169
  32. package/test/desktop.mjs +0 -715
  33. package/test/dock-lifecycle.mjs +0 -280
  34. package/test/dom-style.mjs +0 -261
  35. package/test/form-launcher.mjs +0 -102
  36. package/test/form-modal-layer.mjs +0 -75
  37. package/test/form-native-regions.mjs +0 -115
  38. package/test/form-native-style.mjs +0 -80
  39. package/test/form-native.mjs +0 -68
  40. package/test/form-notify.mjs +0 -90
  41. package/test/form-viewport.mjs +0 -127
  42. package/test/iconview.mjs +0 -70
  43. package/test/jodit-lifecycle.mjs +0 -212
  44. package/test/locale-integrity.mjs +0 -189
  45. package/test/pop-max-size.mjs +0 -66
  46. package/test/rtl-archive.mjs +0 -428
  47. package/test/rtl-direction.mjs +0 -193
  48. package/test/scroll.mjs +0 -145
  49. package/test/select.mjs +0 -885
  50. package/test/sidebar-toggle.mjs +0 -75
  51. package/test/task-package-loading.mjs +0 -94
  52. package/test/task-tray.mjs +0 -331
  53. package/test/tool-assets.mjs +0 -64
  54. package/test/tool-clone.mjs +0 -42
  55. package/test/toolbox.mjs +0 -88
  56. package/test/tuieditor-locale.mjs +0 -175
  57. package/test/vflow.mjs +0 -122
  58. package/test/web-drag-mask.mjs +0 -96
package/test/toolbox.mjs DELETED
@@ -1,88 +0,0 @@
1
- // Run after TypeScript compilation: node --experimental-vm-modules test/toolbox.mjs
2
- import assert from 'node:assert/strict';
3
- import { readFile } from 'node:fs/promises';
4
- import { SourceTextModule, SyntheticModule } from 'node:vm';
5
- import { JSDOM } from 'jsdom';
6
-
7
- const dom = new JSDOM('<div id="tools"></div>');
8
- globalThis.HTMLElement = dom.window.HTMLElement;
9
- const tools = dom.window.document.getElementById('tools');
10
- class AbstractControl {
11
- constructor() {
12
- this.refs = { tools };
13
- this.emitted = [];
14
- }
15
- emit(name, value) {
16
- this.emitted.push([name, value]);
17
- }
18
- propNumber(name) {
19
- return Number(this.props[name]);
20
- }
21
- watch(name, callback, options) {
22
- if (options?.immediate) {
23
- callback();
24
- }
25
- }
26
- }
27
- const clickgo = new SyntheticModule(['control'], function() {
28
- this.setExport('control', { AbstractControl });
29
- });
30
- const mod = new SourceTextModule(await readFile(new URL('../dist/sources/control/toolbox/code.js', import.meta.url), 'utf8'));
31
- await mod.link(() => clickgo);
32
- await mod.evaluate();
33
- const Toolbox = mod.namespace.default;
34
- const toolbox = new Toolbox();
35
- toolbox.props.columns = '2';
36
- toolbox.onMounted();
37
- assert.equal(toolbox.columnsData, 2);
38
- toolbox.toggleColumns();
39
- assert.equal(toolbox.columnsData, 1);
40
- assert.deepEqual(toolbox.emitted, [['update:columns', 1]]);
41
-
42
- function button(x, y, { disabled = false, hidden = false } = {}) {
43
- const item = dom.window.document.createElement('div');
44
- item.setAttribute('data-cg-control', 'button');
45
- item.setAttribute('role', 'button');
46
- item.setAttribute('tabindex', '0');
47
- if (disabled) {
48
- item.setAttribute('data-cg-disabled', '');
49
- }
50
- Object.defineProperty(item, 'offsetHeight', { 'value': hidden ? 0 : 24 });
51
- item.getBoundingClientRect = () => ({ 'left': x, 'top': y, 'width': 24, 'height': 24 });
52
- tools.append(item);
53
- return item;
54
- }
55
- // A one-tool group precedes a two-column group; DOM index arithmetic would pick the wrong row.
56
- const browse = button(0, 0);
57
- const paint = button(0, 40);
58
- const erase = button(30, 40);
59
- button(0, 80, { 'disabled': true });
60
- button(30, 80, { 'hidden': true });
61
- const select = button(0, 120);
62
-
63
- function move(from, key, expected) {
64
- from.focus();
65
- let prevented = false;
66
- toolbox.keydown({ 'target': from, key, 'preventDefault': () => { prevented = true; } });
67
- assert.equal(prevented, true);
68
- assert.equal(dom.window.document.activeElement, expected, key);
69
- }
70
- move(browse, 'ArrowDown', paint);
71
- move(paint, 'ArrowRight', erase);
72
- move(erase, 'ArrowLeft', paint);
73
- move(paint, 'ArrowDown', select);
74
- move(select, 'Home', browse);
75
- move(browse, 'End', select);
76
- move(browse, 'ArrowUp', browse);
77
-
78
- // RTL changes the rendered physical positions; navigation must follow those positions.
79
- paint.getBoundingClientRect = () => ({ 'left': 30, 'top': 40, 'width': 24, 'height': 24 });
80
- erase.getBoundingClientRect = () => ({ 'left': 0, 'top': 40, 'width': 24, 'height': 24 });
81
- move(paint, 'ArrowLeft', erase);
82
- move(erase, 'ArrowRight', paint);
83
-
84
- const outside = dom.window.document.createElement('div');
85
- outside.setAttribute('data-cg-control', 'button');
86
- outside.setAttribute('role', 'button');
87
- toolbox.keydown({ 'target': outside, 'key': 'ArrowDown', 'preventDefault': () => assert.fail('header/outside buttons are not tools') });
88
- console.log('Toolbox column binding and keyboard navigation across groups, hidden/disabled tools and RTL passed.');
@@ -1,175 +0,0 @@
1
- import assert from 'node:assert/strict';
2
- import { readFile } from 'node:fs/promises';
3
- import vm from 'node:vm';
4
- import { setImmediate } from 'node:timers/promises';
5
- import ts from 'typescript';
6
- import { JSDOM } from 'jsdom';
7
-
8
- const source = await readFile(new URL('../dist/sources/control/tuieditor/code.ts', import.meta.url), 'utf8');
9
- const compiled = ts.transpileModule(source, {
10
- compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022, esModuleInterop: false },
11
- }).outputText;
12
- const dom = new JSDOM('<!doctype html><html><body></body></html>');
13
- const { document } = dom.window;
14
- const requests = [];
15
- const editors = [];
16
- function deferred() {
17
- let resolve;
18
- let reject;
19
- const promise = new Promise((yes, no) => { resolve = yes; reject = no; });
20
- return { promise, resolve, reject };
21
- }
22
- class AbstractControl {
23
- get locale() { return this.testLocale; }
24
- propBoolean(name) { return this.props[name] === true; }
25
- watch(name, callback, options) {
26
- this.watches.set(name, callback);
27
- if (options?.immediate) callback();
28
- }
29
- emit() {}
30
- }
31
- class Editor {
32
- constructor(options) {
33
- this.options = options;
34
- this.i18n = { get: key => key };
35
- options.el.appendChild(document.createElement('div'));
36
- editors.push(this);
37
- }
38
- insertToolbarItem() {}
39
- }
40
- let main;
41
- const clickgo = {
42
- modules: {},
43
- getCdn: () => 'https://cdn.example',
44
- getDirname: () => 'https://app.example/clickgo',
45
- core: { getModule: () => main },
46
- control: { AbstractControl },
47
- tool: {
48
- loadScript(url) {
49
- const pending = deferred();
50
- requests.push({ url, ...pending });
51
- return pending.promise;
52
- },
53
- },
54
- dom: { createElement: name => document.createElement(name), watchStyle() {} },
55
- };
56
- // Build the actual shared module once, then evaluate controls independently as different tasks do.
57
- const coreSource = await readFile(new URL('../dist/lib/core.ts', import.meta.url), 'utf8');
58
- const tree = ts.createSourceFile('core.ts', coreSource, ts.ScriptTarget.ES2022, true);
59
- const names = new Set(['getModule', 'loadModule']);
60
- const selected = tree.statements.filter(node =>
61
- (ts.isVariableStatement(node) && node.declarationList.declarations.some(item => item.name.getText(tree) === 'modules')) ||
62
- (ts.isFunctionDeclaration(node) && node.body && names.has(node.name?.text)));
63
- const core = {};
64
- vm.runInNewContext(ts.transpileModule(selected.map(node => node.getText(tree)).join('\n'), {
65
- compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022, esModuleInterop: false },
66
- }).outputText, {
67
- exports: core, window: { toastui: { Editor } }, clickgo,
68
- lTool: { ...clickgo.tool, loadAssets: async () => true, loadStyle() {}, urlResolve: (base, path) => new URL(path, base).href },
69
- });
70
- const shared = await core.getModule('@toast-ui/editor');
71
- main = Promise.resolve(shared);
72
- function loadControl() {
73
- const exports = {};
74
- vm.runInNewContext(compiled, { exports, require: () => clickgo });
75
- return exports.default;
76
- }
77
- let Control = loadControl();
78
- function create(locale) {
79
- const control = new Control();
80
- control.testLocale = locale;
81
- control.watches = new Map();
82
- control.element = document.createElement('div');
83
- const content = document.createElement('div');
84
- const pop = document.createElement('div');
85
- control.element.append(content, pop);
86
- control.refs = { content, pop };
87
- document.body.appendChild(control.element);
88
- return control;
89
- }
90
- await create('en').onMounted();
91
- await create('ar').onMounted();
92
- assert.equal(requests.length, 0, 'English and unsupported languages need no locale scripts');
93
- assert.equal(editors.at(-1).options.language, 'en');
94
- assert.equal(editors.at(-1).options.el.dir, 'rtl');
95
-
96
- const base = deferred();
97
- main = base.promise;
98
- const first = create('sc');
99
- Control = loadControl();
100
- const second = create('sc');
101
- const a = first.onMounted();
102
- const b = second.onMounted();
103
- assert.equal(requests.length, 0, 'locale registration waits for the main library');
104
- base.resolve(shared);
105
- await setImmediate();
106
- assert.equal(requests.length, 1, 'independent task control modules share one pending language request');
107
- assert.ok(requests[0].url.endsWith('/i18n/zh-cn.min.js'));
108
- assert.equal(editors.length, 2, 'instances wait until their locale is registered');
109
- requests[0].resolve(true);
110
- await Promise.all([a, b]);
111
- assert.equal(first.access.tuieditor.options.language, 'zh-CN');
112
- assert.equal(second.access.tuieditor.options.language, 'zh-CN');
113
- assert.notEqual(first.access.tuieditor, second.access.tuieditor);
114
- await create('sc').onMounted();
115
- assert.equal(requests.length, 1, 'ready locale scripts are reused across task control modules');
116
-
117
- Control = loadControl();
118
- await create('sc').onMounted();
119
- assert.equal(requests.length, 1, 'a later task reuses the same completed language request');
120
-
121
- first.testLocale = 'ja';
122
- first.watches.get('locale')();
123
- assert.equal(requests.length, 1, 'changing task locale does not reload an existing editor');
124
- assert.equal(first.access.tuieditor.options.language, 'zh-CN');
125
- main = Promise.resolve(shared);
126
- const japanese = create('ja');
127
- const c = japanese.onMounted();
128
- await setImmediate();
129
- assert.ok(requests.at(-1).url.endsWith('/i18n/ja-jp.min.js'));
130
- requests.at(-1).resolve(true);
131
- await c;
132
- assert.equal(japanese.access.tuieditor.options.language, 'ja-JP', 'new editors use the current task language');
133
-
134
- const french = create('fr');
135
- const d = french.onMounted();
136
- await setImmediate();
137
- requests.at(-1).resolve(false);
138
- await d;
139
- assert.equal(french.access.tuieditor.options.language, 'en', 'locale failure falls back without disabling editing');
140
- const beforeRetry = requests.length;
141
- const retry = create('fr').onMounted();
142
- await setImmediate();
143
- assert.equal(requests.length, beforeRetry + 1, 'failed locale requests can be retried');
144
- requests.at(-1).resolve(true);
145
- await retry;
146
- assert.equal(editors.at(-1).options.language, 'fr-FR');
147
-
148
- const rejected = shared.loadLanguage('de-DE');
149
- const rejectedWaiter = shared.loadLanguage('de-de');
150
- requests.at(-1).reject(new Error('external loader rejected'));
151
- assert.equal(await rejected, false);
152
- assert.equal(await rejectedWaiter, false, 'external loader errors release all waiters');
153
- const rejectedRetry = shared.loadLanguage('de-DE');
154
- requests.at(-1).resolve(true);
155
- assert.equal(await rejectedRetry, true, 'external errors do not poison the shared cache');
156
-
157
- const removed = create('tc');
158
- const e = removed.onMounted();
159
- await setImmediate();
160
- assert.ok(requests.at(-1).url.endsWith('/i18n/zh-tw.min.js'));
161
- removed.element.remove();
162
- const count = editors.length;
163
- requests.at(-1).resolve(true);
164
- await e;
165
- assert.equal(editors.length, count, 'removed hosts do not receive late editor instances');
166
-
167
- Control = loadControl();
168
- main = Promise.resolve(null);
169
- const missing = create('sc');
170
- const beforeMissing = requests.length;
171
- await missing.onMounted();
172
- assert.equal(missing.notInit, true);
173
- assert.equal(missing.isLoading, false);
174
- assert.equal(requests.length, beforeMissing, 'a missing main library triggers no locale request');
175
- console.log('Toast UI cross-task locale requests, reuse, fallback, retries and initialization language passed.');
package/test/vflow.mjs DELETED
@@ -1,122 +0,0 @@
1
- import assert from 'node:assert/strict';
2
- import { readFile } from 'node:fs/promises';
3
- import vm from 'node:vm';
4
- import { JSDOM } from 'jsdom';
5
- import ts from 'typescript';
6
- import { getClassPrototype, lang } from '../dist/lib/tool.js';
7
-
8
- const dom = new JSDOM('<!doctype html><html><body></body></html>');
9
- for (const name of ['window', 'document', 'Element', 'HTMLElement', 'SVGElement', 'Node']) {
10
- Object.defineProperty(globalThis, name, { configurable: true, value: dom.window[name] });
11
- }
12
- Object.defineProperties(HTMLElement.prototype, {
13
- offsetWidth: { configurable: true, get: () => 80 },
14
- offsetHeight: { configurable: true, get: () => 20 },
15
- });
16
- const { createApp, reactive, nextTick, h } = await import('vue');
17
- const state = reactive({ locale: 'en', direction: 'h', count: 2 });
18
- const errors = [];
19
- const watched = [];
20
- process.on('unhandledRejection', error => { errors.push(error); });
21
-
22
- // Keep the framework's computed $el access and component-root mounting order.
23
- class AbstractControl {
24
- get element() { return this.$el; }
25
- get locale() { return state.locale; }
26
- get localeDirection() { return lang.getDirection(this.locale); }
27
- nextTick() { return this.$nextTick(); }
28
- watch(...args) { return this.$watch(...args); }
29
- emit(...args) { this.$emit(...args); }
30
- propInt(name) { return parseInt(this.props[name]); }
31
- }
32
- const clickgo = {
33
- control: { AbstractControl },
34
- dom: {
35
- isRtl: el => el instanceof Element && dom.window.getComputedStyle(el).direction === 'rtl',
36
- watchStyle: (el, names, callback, immediate) => {
37
- assert.ok(el instanceof HTMLElement && el.isConnected, 'style watcher needs a mounted element');
38
- watched.push(el);
39
- if (immediate) {
40
- for (const name of names) callback(name, name === 'font' ? '13px sans-serif' : '0px');
41
- }
42
- },
43
- watchSize() {},
44
- unwatchSize() {},
45
- },
46
- };
47
- const source = await readFile(new URL('../dist/sources/control/vflow/code.ts', import.meta.url), 'utf8');
48
- const exports = {};
49
- vm.runInNewContext(ts.transpileModule(source, {
50
- compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022 },
51
- }).outputText, { exports, require: () => clickgo });
52
- const instance = new exports.default();
53
- const prototype = getClassPrototype(instance);
54
- const layout = await readFile(new URL('../dist/sources/control/vflow/layout.html', import.meta.url), 'utf8');
55
- const Flow = {
56
- template: '<div><slot /></div>',
57
- mounted() { this.$emit('clientwidth', 200); this.$emit('clientheight', 100); },
58
- };
59
- const Vflow = {
60
- template: layout,
61
- components: { 'cg-flow': Flow },
62
- props: Object.fromEntries(Object.entries(instance.props).map(([name, value]) => [name, {
63
- default: () => structuredClone(value),
64
- }])),
65
- emits: instance.emits,
66
- data: () => Object.fromEntries(Object.entries(instance).filter(([name]) => !['props', 'emits'].includes(name))),
67
- computed: prototype.access,
68
- methods: prototype.method,
69
- created() { this.props = this.$props; },
70
- async mounted() { await this.$nextTick(); this.onMounted(); },
71
- };
72
- const host = document.createElement('div');
73
- document.body.append(host);
74
- const app = createApp({
75
- render() {
76
- return h('div', { dir: lang.getDirection(state.locale) }, [
77
- h(Vflow, { ref: 'flow', data: state.count, direction: state.direction }, {
78
- default: item => h('span', `Line ${item.row}`),
79
- }),
80
- ]);
81
- },
82
- });
83
- app.config.errorHandler = error => { errors.push(error); };
84
- const root = app.mount(host);
85
-
86
- /** --- Drain Vue render, mount and deferred item-size work. --- */
87
- async function flush() {
88
- for (let i = 0; i < 10; ++i) await nextTick();
89
- await new Promise(resolve => setImmediate(resolve));
90
- assert.deepEqual(errors, [], 'Vflow must mount/update without runtime errors');
91
- }
92
- await flush();
93
- assert.equal(watched.length, 1);
94
- assert.equal(root.$refs.flow.element, host.querySelector('[direction]'));
95
- assert.equal(root.$refs.flow.length, 160);
96
- assert.equal(root.$refs.flow.isRtl, false);
97
- assert.equal(host.querySelector('.item').style.left, '0px');
98
- state.count = 102;
99
- await flush();
100
- assert.equal(root.$refs.flow.pos.length, 102);
101
- assert.ok(host.querySelectorAll('.item').length < 102, 'only visible items are rendered');
102
- state.locale = 'ar';
103
- await flush();
104
- assert.equal(root.$refs.flow.isRtl, true);
105
- assert.equal(host.querySelector('.item').style.left, '');
106
- assert.equal(host.querySelector('.item').style.right, '0px');
107
- state.direction = 'v';
108
- await flush();
109
- assert.equal(root.$refs.flow.isRtl, false);
110
- assert.equal(root.$refs.flow.length, 2040);
111
- state.count = 0;
112
- await flush();
113
- assert.equal(root.$refs.flow.pos.length, 0);
114
- state.count = 2;
115
- state.direction = 'h';
116
- state.locale = 'en';
117
- await flush();
118
- assert.equal(root.$refs.flow.length, 160);
119
- assert.equal(host.querySelector('.item').style.left, '0px');
120
- app.unmount();
121
- await flush();
122
- console.log('Vflow real Vue mount, virtualization, data/direction updates and live RTL switching passed.');
@@ -1,96 +0,0 @@
1
- // Run: node --experimental-vm-modules test/web-drag-mask.mjs
2
- import assert from 'node:assert/strict';
3
- import { readFile } from 'node:fs/promises';
4
- import { SourceTextModule, SyntheticModule } from 'node:vm';
5
- import { JSDOM } from 'jsdom';
6
- import ts from 'typescript';
7
-
8
- const dom = new JSDOM('<!doctype html><html><head></head><body></body></html>');
9
- for (const name of ['window', 'document', 'Element', 'SVGElement', 'Node', 'HTMLElement']) {
10
- globalThis[name] = dom.window[name];
11
- }
12
- globalThis.getComputedStyle = dom.window.getComputedStyle.bind(dom.window);
13
- const { createApp, compile, reactive, nextTick } = await import('vue');
14
- const pointer = await import('@litert/pointer');
15
- const state = reactive({ move: false });
16
- const clickgo = new SyntheticModule(['control', 'dom'], function() {
17
- this.setExport('control', { AbstractControl: class {} });
18
- this.setExport('dom', { is: state });
19
- });
20
- const source = await readFile(new URL('../dist/sources/control/web/code.ts', import.meta.url), 'utf8');
21
- const web = new SourceTextModule(ts.transpileModule(source, {
22
- compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.ESNext },
23
- }).outputText);
24
- await web.link(() => clickgo);
25
- await web.evaluate();
26
- const control = new web.namespace.default();
27
-
28
- // Exercise the actual launcher hooks with the installed Pointer library.
29
- const launcher = await readFile(new URL('../dist/clickgo.ts', import.meta.url), 'utf8');
30
- const hookSource = launcher.match(/ modules\.pointer\.addMoveHook\('down',[\s\S]*? modules\.pointer\.addMoveHook\('up',[\s\S]*? \}\);/)[0];
31
- const hooks = new SourceTextModule(`import { pointer, is } from 'bridge';
32
- const modules = { pointer }; const lDom = { is }; ${hookSource}`);
33
- const bridge = new SyntheticModule(['pointer', 'is'], function() {
34
- this.setExport('pointer', pointer);
35
- this.setExport('is', state);
36
- });
37
- await hooks.link(() => bridge);
38
- await hooks.evaluate();
39
-
40
- const template = await readFile(new URL('../dist/sources/control/web/layout.html', import.meta.url), 'utf8');
41
- const host = document.createElement('div');
42
- document.body.append(host);
43
- const app = createApp({
44
- render: compile(template),
45
- data: () => ({ src: 'about:blank' }),
46
- computed: { showMask: () => control.showMask },
47
- });
48
- app.mount(host);
49
- const iframe = host.querySelector('iframe');
50
- const mask = host.querySelector('.mask');
51
- assert.equal(iframe.getAttribute('src'), 'about:blank');
52
- assert.equal(mask.style.display, 'none');
53
-
54
- function event(type, x, y) {
55
- const e = new dom.window.Event(type, { bubbles: true, cancelable: true });
56
- Object.assign(e, { clientX: x, clientY: y, pointerId: 1, pointerType: 'mouse', button: 0 });
57
- return e;
58
- }
59
-
60
- for (const operation of ['move', 'resize']) {
61
- for (const finish of ['pointerup', 'pointercancel']) {
62
- let updates = 0;
63
- const handle = document.createElement('div');
64
- document.body.append(handle);
65
- handle.addEventListener('pointerdown', e => {
66
- if (operation === 'resize') {
67
- pointer.resize(e, {
68
- border: 'rb', objectLeft: 0, objectTop: 0,
69
- objectWidth: 400, objectHeight: 300,
70
- move: () => { ++updates; },
71
- });
72
- }
73
- else {
74
- pointer.move(e, { move: () => { ++updates; } });
75
- }
76
- });
77
- handle.dispatchEvent(event('pointerdown', 400, 300));
78
- await nextTick();
79
- assert.equal(state.move, true);
80
- assert.notEqual(mask.style.display, 'none');
81
- mask.dispatchEvent(event('pointermove', 350, 250));
82
- mask.dispatchEvent(event('pointermove', 320, 230));
83
- assert.equal(updates, 2);
84
- mask.dispatchEvent(event(finish, 320, 230));
85
- await nextTick();
86
- assert.equal(state.move, false);
87
- assert.equal(mask.style.display, 'none');
88
- mask.dispatchEvent(event('pointermove', 300, 200));
89
- assert.equal(updates, 2);
90
- assert.equal(host.querySelector('iframe'), iframe);
91
- handle.remove();
92
- }
93
- }
94
- app.unmount();
95
- dom.window.close();
96
- console.log('WEB drag/resize mask, pointerup/cancel cleanup and iframe preservation checks passed.');