@caperjs/core 0.2.4 → 0.3.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.
- package/build/index.mjs +3 -1
- package/build/internal/discovery.mjs +1 -1
- package/build/internal/manifest.mjs +8 -3
- package/build/internal/validate.mjs +3 -1
- package/build/plugins/assetTypes.mjs +4 -4
- package/build/plugins/caperConfig.mjs +12 -5
- package/build/plugins/lists.mjs +29 -9
- package/cli/create.mjs +57 -6
- package/cli/create.test.mjs +36 -0
- package/cli/update.mjs +1 -1
- package/lib/{CaptionsPlugin-WPAcfAkx.js → CaptionsPlugin-BLbLl3WB.js} +3 -3
- package/lib/{CaptionsPlugin-WPAcfAkx.js.map → CaptionsPlugin-BLbLl3WB.js.map} +1 -1
- package/lib/{DataAdapter-DS-twowe.js → DataAdapter-aHSNM_26.js} +12 -7
- package/lib/DataAdapter-aHSNM_26.js.map +1 -0
- package/lib/{DebugRenderer-D1ryrRYx.js → DebugRenderer-8LjHtBzA.js} +2 -2
- package/lib/{DebugRenderer-D1ryrRYx.js.map → DebugRenderer-8LjHtBzA.js.map} +1 -1
- package/lib/{DevToolsPlugin-DH7zX7HU.js → DevToolsPlugin-Vrk6aOtS.js} +2 -2
- package/lib/{DevToolsPlugin-DH7zX7HU.js.map → DevToolsPlugin-Vrk6aOtS.js.map} +1 -1
- package/lib/{GSAPPlugin-BBtRjUqe.js → GSAPPlugin-Hm3F4zdg.js} +3 -3
- package/lib/{GSAPPlugin-BBtRjUqe.js.map → GSAPPlugin-Hm3F4zdg.js.map} +1 -1
- package/lib/{LayoutPlugin-Co6vcoV4.js → LayoutPlugin-Dpgeawfw.js} +4 -4
- package/lib/{LayoutPlugin-Co6vcoV4.js.map → LayoutPlugin-Dpgeawfw.js.map} +1 -1
- package/lib/{SpinePlugin-uN641Hmk.js → SpinePlugin-B08yAw1S.js} +690 -793
- package/lib/SpinePlugin-B08yAw1S.js.map +1 -0
- package/lib/{StatsPlugin-vZQRm6wh.js → StatsPlugin-D2Xa5j97.js} +5 -2
- package/lib/StatsPlugin-D2Xa5j97.js.map +1 -0
- package/lib/{VoiceOverPlugin-KlZdGj1G.js → VoiceOverPlugin-qtFlqpRn.js} +6 -5
- package/lib/VoiceOverPlugin-qtFlqpRn.js.map +1 -0
- package/lib/caper.mjs +23 -10
- package/lib/caper.mjs.map +1 -1
- package/lib/core/Application.d.ts +8 -3
- package/lib/core/Application.d.ts.map +1 -1
- package/lib/core/Application.test.d.ts +2 -0
- package/lib/core/Application.test.d.ts.map +1 -0
- package/lib/core/config.d.ts +6 -1
- package/lib/core/config.d.ts.map +1 -1
- package/lib/core/config.test.d.ts +2 -0
- package/lib/core/config.test.d.ts.map +1 -0
- package/lib/core/interfaces/IApplication.d.ts +6 -6
- package/lib/core/interfaces/IApplication.d.ts.map +1 -1
- package/lib/core/interfaces/ICoreSignals.d.ts +2 -1
- package/lib/core/interfaces/ICoreSignals.d.ts.map +1 -1
- package/lib/display/Camera.test.d.ts +2 -0
- package/lib/display/Camera.test.d.ts.map +1 -0
- package/lib/display/Entity.d.ts +2 -2
- package/lib/display/SceneTransition.test.d.ts +1 -0
- package/lib/display/SceneTransition.test.d.ts.map +1 -0
- package/lib/display/SpineAnimation.d.ts +3 -3
- package/lib/mixins/factory/methods.d.ts.map +1 -1
- package/lib/plugins/AssetsPlugin.d.ts.map +1 -1
- package/lib/plugins/AssetsPlugin.test.d.ts +2 -0
- package/lib/plugins/AssetsPlugin.test.d.ts.map +1 -0
- package/lib/plugins/DataAdapter.d.ts.map +1 -1
- package/lib/plugins/DataAdapter.test.d.ts +2 -0
- package/lib/plugins/DataAdapter.test.d.ts.map +1 -0
- package/lib/plugins/FullScreenPlugin.d.ts.map +1 -1
- package/lib/plugins/FullScreenPlugin.test.d.ts +2 -0
- package/lib/plugins/FullScreenPlugin.test.d.ts.map +1 -0
- package/lib/plugins/GSAPPlugin.d.ts +0 -2
- package/lib/plugins/GSAPPlugin.d.ts.map +1 -1
- package/lib/plugins/GSAPPlugin.test.d.ts +2 -0
- package/lib/plugins/GSAPPlugin.test.d.ts.map +1 -0
- package/lib/plugins/LookupPlugin.d.ts +6 -1
- package/lib/plugins/LookupPlugin.d.ts.map +1 -1
- package/lib/plugins/LookupPlugin.test.d.ts +2 -0
- package/lib/plugins/LookupPlugin.test.d.ts.map +1 -0
- package/lib/plugins/PopupManagerPlugin.d.ts.map +1 -1
- package/lib/plugins/PopupManagerPlugin.test.d.ts +1 -0
- package/lib/plugins/PopupManagerPlugin.test.d.ts.map +1 -0
- package/lib/plugins/SceneManagerPlugin.d.ts +1 -0
- package/lib/plugins/SceneManagerPlugin.d.ts.map +1 -1
- package/lib/plugins/SceneManagerPlugin.test.d.ts +2 -0
- package/lib/plugins/SceneManagerPlugin.test.d.ts.map +1 -0
- package/lib/plugins/StatsPlugin.d.ts +1 -0
- package/lib/plugins/StatsPlugin.d.ts.map +1 -1
- package/lib/plugins/WebEventsPlugin.d.ts.map +1 -1
- package/lib/plugins/actions/ActionsPlugin.test.d.ts +2 -0
- package/lib/plugins/actions/ActionsPlugin.test.d.ts.map +1 -0
- package/lib/plugins/audio/AudioChannel.d.ts.map +1 -1
- package/lib/plugins/audio/AudioManagerPlugin.d.ts.map +1 -1
- package/lib/plugins/audio/AudioManagerPlugin.test.d.ts +2 -0
- package/lib/plugins/audio/AudioManagerPlugin.test.d.ts.map +1 -0
- package/lib/plugins/audio/VoiceOverPlugin.d.ts.map +1 -1
- package/lib/plugins/captions/CaptionsPlugin.test.d.ts +2 -0
- package/lib/plugins/captions/CaptionsPlugin.test.d.ts.map +1 -0
- package/lib/plugins/focus/FocusManagerPlugin.d.ts.map +1 -1
- package/lib/plugins/focus/FocusManagerPlugin.test.d.ts +2 -0
- package/lib/plugins/focus/FocusManagerPlugin.test.d.ts.map +1 -0
- package/lib/plugins/input/AbstractControls.d.ts +5 -0
- package/lib/plugins/input/AbstractControls.d.ts.map +1 -1
- package/lib/plugins/input/Controls.test.d.ts +2 -0
- package/lib/plugins/input/Controls.test.d.ts.map +1 -0
- package/lib/plugins/input/keyboard/KeyboardContols.d.ts +5 -0
- package/lib/plugins/input/keyboard/KeyboardContols.d.ts.map +1 -1
- package/lib/plugins/input/touch/VirtualControls.d.ts +6 -0
- package/lib/plugins/input/touch/VirtualControls.d.ts.map +1 -1
- package/lib/plugins/input/touch/VirtualControls.test.d.ts +2 -0
- package/lib/plugins/input/touch/VirtualControls.test.d.ts.map +1 -0
- package/lib/{registries-BLrxALZ0.js → registries-m2vKTIih.js} +156 -101
- package/lib/registries-m2vKTIih.js.map +1 -0
- package/lib/store/Store.d.ts.map +1 -1
- package/lib/ui/Button.d.ts.map +1 -1
- package/lib/ui/UICanvas.d.ts +10 -1
- package/lib/ui/UICanvas.d.ts.map +1 -1
- package/lib/ui/UICanvas.test.d.ts +0 -1
- package/lib/ui/UICanvas.test.d.ts.map +1 -1
- package/lib/utils/color.d.ts.map +1 -1
- package/lib/utils/color.test.d.ts +2 -0
- package/lib/utils/color.test.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/core/Application.test.ts +168 -0
- package/src/core/Application.ts +37 -27
- package/src/core/config.test.ts +79 -0
- package/src/core/config.ts +28 -6
- package/src/core/interfaces/IApplication.ts +6 -6
- package/src/core/interfaces/ICoreSignals.ts +2 -0
- package/src/display/Camera.test.ts +40 -0
- package/src/display/Camera.ts +3 -3
- package/src/display/Entity.ts +2 -2
- package/src/display/SceneTransition.test.ts +58 -0
- package/src/display/SceneTransition.ts +1 -1
- package/src/display/SpineAnimation.ts +4 -4
- package/src/mixins/factory/methods.ts +0 -1
- package/src/plugins/AssetsPlugin.test.ts +48 -0
- package/src/plugins/AssetsPlugin.ts +3 -1
- package/src/plugins/DataAdapter.test.ts +32 -0
- package/src/plugins/DataAdapter.ts +5 -1
- package/src/plugins/FullScreenPlugin.test.ts +48 -0
- package/src/plugins/FullScreenPlugin.ts +5 -1
- package/src/plugins/GSAPPlugin.test.ts +31 -0
- package/src/plugins/GSAPPlugin.ts +1 -4
- package/src/plugins/LookupPlugin.test.ts +42 -0
- package/src/plugins/LookupPlugin.ts +15 -3
- package/src/plugins/PopupManagerPlugin.test.ts +53 -0
- package/src/plugins/PopupManagerPlugin.ts +5 -3
- package/src/plugins/SceneManagerPlugin.test.ts +76 -0
- package/src/plugins/SceneManagerPlugin.ts +21 -11
- package/src/plugins/StatsPlugin.ts +8 -0
- package/src/plugins/WebEventsPlugin.test.ts +32 -0
- package/src/plugins/WebEventsPlugin.ts +2 -0
- package/src/plugins/actions/ActionsPlugin.test.ts +68 -0
- package/src/plugins/actions/ActionsPlugin.ts +5 -5
- package/src/plugins/audio/AudioChannel.test.ts +19 -0
- package/src/plugins/audio/AudioChannel.ts +8 -1
- package/src/plugins/audio/AudioManagerPlugin.test.ts +41 -0
- package/src/plugins/audio/AudioManagerPlugin.ts +3 -2
- package/src/plugins/audio/VoiceOverPlugin.ts +1 -0
- package/src/plugins/captions/CaptionsPlugin.test.ts +40 -0
- package/src/plugins/captions/CaptionsPlugin.ts +1 -1
- package/src/plugins/focus/FocusManagerPlugin.test.ts +71 -0
- package/src/plugins/focus/FocusManagerPlugin.ts +7 -0
- package/src/plugins/input/AbstractControls.ts +6 -0
- package/src/plugins/input/Controls.test.ts +149 -0
- package/src/plugins/input/keyboard/KeyboardContols.ts +29 -5
- package/src/plugins/input/touch/VirtualControls.test.ts +38 -0
- package/src/plugins/input/touch/VirtualControls.ts +44 -10
- package/src/plugins/spine/SpinePlugin.ts +2 -2
- package/src/store/Store.test.ts +15 -0
- package/src/store/Store.ts +4 -1
- package/src/ui/Button.test.ts +12 -0
- package/src/ui/Button.ts +1 -0
- package/src/ui/UICanvas.test.ts +109 -6
- package/src/ui/UICanvas.ts +41 -3
- package/src/utils/color.test.ts +16 -0
- package/src/utils/color.ts +2 -1
- package/src/utils/platform.ts +1 -1
- package/src/version.ts +1 -1
- package/lib/DataAdapter-DS-twowe.js.map +0 -1
- package/lib/SpinePlugin-uN641Hmk.js.map +0 -1
- package/lib/StatsPlugin-vZQRm6wh.js.map +0 -1
- package/lib/VoiceOverPlugin-KlZdGj1G.js.map +0 -1
- package/lib/registries-BLrxALZ0.js.map +0 -1
package/src/display/Camera.ts
CHANGED
|
@@ -85,7 +85,7 @@ export class Camera extends Container implements ICamera {
|
|
|
85
85
|
this.minY = config.minY;
|
|
86
86
|
}
|
|
87
87
|
this.viewportWidth = config.viewportWidth ?? this.app.size.width;
|
|
88
|
-
this.viewportHeight = config.viewportHeight ?? this.app.size.
|
|
88
|
+
this.viewportHeight = config.viewportHeight ?? this.app.size.height;
|
|
89
89
|
this.worldWidth = config.worldWidth ?? this.viewportWidth;
|
|
90
90
|
this.worldHeight = config.worldHeight ?? this.viewportHeight;
|
|
91
91
|
this.maxX = config.maxX ?? this.worldWidth - this.viewportWidth;
|
|
@@ -125,7 +125,7 @@ export class Camera extends Container implements ICamera {
|
|
|
125
125
|
protected _targetScale: Point = new Point(1, 1);
|
|
126
126
|
|
|
127
127
|
get targetScale(): Point {
|
|
128
|
-
return this.
|
|
128
|
+
return this._targetScale;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
private _lerp: number = 0;
|
|
@@ -135,7 +135,7 @@ export class Camera extends Container implements ICamera {
|
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
set lerp(value: number) {
|
|
138
|
-
// if the value is less than 0 or greater than 1,
|
|
138
|
+
// if the value is less than 0 or greater than 1, throw an error
|
|
139
139
|
if (value < 0 || value > 1) {
|
|
140
140
|
throw new Error('Camera lerp value must be in the range [0, 1]');
|
|
141
141
|
}
|
package/src/display/Entity.ts
CHANGED
|
@@ -9,8 +9,8 @@ import { Container } from './Container';
|
|
|
9
9
|
*
|
|
10
10
|
* **Lifecycle** (inherited from Container):
|
|
11
11
|
*
|
|
12
|
-
* 1. `constructor(props)` —
|
|
13
|
-
*
|
|
12
|
+
* 1. `constructor(props)` — Container's constructor runs first, then props
|
|
13
|
+
* are stashed on `this.props`. Don't reference `this.app` yet.
|
|
14
14
|
* 2. **addChild** — the factory auto-adds the instance to the calling
|
|
15
15
|
* Container; Pixi emits an `added` event.
|
|
16
16
|
* 3. `added()` — override this to build the display tree using
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
// Registers the factory method table `Factory()` (part of Container's mixin
|
|
3
|
+
// chain) reads from — must be imported before any display/ui class is
|
|
4
|
+
// constructed. See importOrder.containerFirst.test.ts.
|
|
5
|
+
import '../mixins/factory/const';
|
|
6
|
+
import { Signal } from '../signals';
|
|
7
|
+
import { SceneTransition } from './SceneTransition';
|
|
8
|
+
|
|
9
|
+
describe('SceneTransition asset signal wiring', () => {
|
|
10
|
+
let app: {
|
|
11
|
+
assets: {
|
|
12
|
+
onLoadStart: Signal<() => void>;
|
|
13
|
+
onLoadProgress: Signal<(progress: number) => void>;
|
|
14
|
+
onLoadComplete: Signal<() => void>;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
let originalAppDescriptor: PropertyDescriptor | undefined;
|
|
18
|
+
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
app = {
|
|
21
|
+
assets: {
|
|
22
|
+
onLoadStart: new Signal<() => void>(),
|
|
23
|
+
onLoadProgress: new Signal<(progress: number) => void>(),
|
|
24
|
+
onLoadComplete: new Signal<() => void>(),
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
// Container.app is a getter; override it on the prototype so the
|
|
28
|
+
// constructor picks up the fake app before the instance even exists.
|
|
29
|
+
originalAppDescriptor = Object.getOwnPropertyDescriptor(SceneTransition.prototype, 'app');
|
|
30
|
+
Object.defineProperty(SceneTransition.prototype, 'app', { get: () => app, configurable: true });
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
afterEach(() => {
|
|
34
|
+
if (originalAppDescriptor) {
|
|
35
|
+
Object.defineProperty(SceneTransition.prototype, 'app', originalAppDescriptor);
|
|
36
|
+
} else {
|
|
37
|
+
delete (SceneTransition.prototype as any).app;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('routes progress ticks to handleLoadProgress and completion to handleLoadComplete', () => {
|
|
42
|
+
const progressSpy = vi.spyOn(SceneTransition.prototype as any, 'handleLoadProgress');
|
|
43
|
+
const completeSpy = vi.spyOn(SceneTransition.prototype as any, 'handleLoadComplete');
|
|
44
|
+
|
|
45
|
+
const transition = new SceneTransition();
|
|
46
|
+
void transition;
|
|
47
|
+
|
|
48
|
+
app.assets.onLoadProgress.emit(0.5);
|
|
49
|
+
|
|
50
|
+
expect(progressSpy).toHaveBeenCalledTimes(1);
|
|
51
|
+
expect(completeSpy).not.toHaveBeenCalled();
|
|
52
|
+
|
|
53
|
+
app.assets.onLoadComplete.emit();
|
|
54
|
+
|
|
55
|
+
expect(completeSpy).toHaveBeenCalledTimes(1);
|
|
56
|
+
expect(progressSpy).toHaveBeenCalledTimes(1);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -46,7 +46,7 @@ export class SceneTransition extends Container {
|
|
|
46
46
|
this.addSignalConnection(
|
|
47
47
|
this.app.assets.onLoadStart.connect(this.handleLoadStart),
|
|
48
48
|
this.app.assets.onLoadProgress.connect(this.handleLoadProgress),
|
|
49
|
-
this.app.assets.
|
|
49
|
+
this.app.assets.onLoadComplete.connect(this.handleLoadComplete),
|
|
50
50
|
);
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -20,8 +20,8 @@ export interface ISpineAnimation<ANames extends string = string> extends Instanc
|
|
|
20
20
|
readonly onAnimationEvent: Signal<(entry: TrackEntry, event: Event) => void>;
|
|
21
21
|
readonly onPaused: Signal<(entry: TrackEntry | null) => void>;
|
|
22
22
|
readonly onResumed: Signal<(entry: TrackEntry | null) => void>;
|
|
23
|
-
setAnimation(name: ANames, loop?: boolean,
|
|
24
|
-
getCurrentAnimation(
|
|
23
|
+
setAnimation(name: ANames, loop?: boolean, trackIndex?: number): void;
|
|
24
|
+
getCurrentAnimation(trackIndex?: number): ANames;
|
|
25
25
|
pause(): void;
|
|
26
26
|
resume(): void;
|
|
27
27
|
togglePause(): void;
|
|
@@ -113,8 +113,8 @@ export class SpineAnimation<ANames extends string = string> extends _SpineAnimat
|
|
|
113
113
|
return (this.spine.state.getCurrent(trackIndex)?.animation?.name as ANames) || ('' as ANames);
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
setAnimation(name: ANames, loop = false,
|
|
117
|
-
this.spine.state.setAnimation(
|
|
116
|
+
setAnimation(name: ANames, loop = false, trackIndex: number = 0) {
|
|
117
|
+
this.spine.state.setAnimation(trackIndex, name, loop);
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
pause() {
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
// AssetsPlugin extends Plugin, which transitively imports Application → Pixi
|
|
4
|
+
// display graph. Stub it the same way Plugin.test.ts does.
|
|
5
|
+
vi.mock('../core', () => ({
|
|
6
|
+
coreFunctionRegistry: {},
|
|
7
|
+
coreSignalRegistry: {},
|
|
8
|
+
}));
|
|
9
|
+
vi.mock('../core/Application', () => ({
|
|
10
|
+
Application: { getInstance: () => ({ canvas: { dispatchEvent: vi.fn() } }) },
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
vi.mock('pixi.js', async (importOriginal) => {
|
|
14
|
+
const actual = await importOriginal<typeof import('pixi.js')>();
|
|
15
|
+
return {
|
|
16
|
+
...actual,
|
|
17
|
+
Assets: {
|
|
18
|
+
load: vi.fn(async () => undefined),
|
|
19
|
+
loadBundle: vi.fn(async () => undefined),
|
|
20
|
+
setPreferences: vi.fn(),
|
|
21
|
+
backgroundLoad: vi.fn(async () => undefined),
|
|
22
|
+
backgroundLoadBundle: vi.fn(async () => undefined),
|
|
23
|
+
unload: vi.fn(async () => undefined),
|
|
24
|
+
unloadBundle: vi.fn(async () => undefined),
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
import { Assets } from 'pixi.js';
|
|
30
|
+
import { AssetsPlugin } from './AssetsPlugin';
|
|
31
|
+
|
|
32
|
+
const loadBundle = vi.mocked(Assets.loadBundle);
|
|
33
|
+
|
|
34
|
+
describe('AssetsPlugin', () => {
|
|
35
|
+
it('loadRequired only requests bundles that are not already loaded', async () => {
|
|
36
|
+
const plugin = new AssetsPlugin();
|
|
37
|
+
|
|
38
|
+
// Mark bundle 'a' as already loaded.
|
|
39
|
+
await plugin.loadBundles('a');
|
|
40
|
+
loadBundle.mockClear();
|
|
41
|
+
|
|
42
|
+
plugin.initialize({ preload: { bundles: ['a', 'b'] } });
|
|
43
|
+
await plugin.loadRequired();
|
|
44
|
+
|
|
45
|
+
expect(loadBundle).toHaveBeenCalledTimes(1);
|
|
46
|
+
expect(loadBundle).toHaveBeenCalledWith(['b'], expect.anything());
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -187,7 +187,9 @@ export class AssetsPlugin extends Plugin<AssetLoadingOptions> implements IAssets
|
|
|
187
187
|
bundles = [bundles];
|
|
188
188
|
}
|
|
189
189
|
bundles = bundles.filter((bundle) => !this._isBundleLoaded(bundle));
|
|
190
|
-
|
|
190
|
+
if (bundles.length) {
|
|
191
|
+
await Assets.loadBundle(bundles, this._handleLoadProgress);
|
|
192
|
+
}
|
|
191
193
|
}
|
|
192
194
|
}
|
|
193
195
|
this._handleLoadComplete();
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
// DataAdapter extends Plugin, which transitively imports Application → Pixi display graph. Stub it.
|
|
4
|
+
vi.mock('../core', () => ({
|
|
5
|
+
coreFunctionRegistry: {},
|
|
6
|
+
coreSignalRegistry: {},
|
|
7
|
+
}));
|
|
8
|
+
vi.mock('../core/Application', () => ({
|
|
9
|
+
Application: { getInstance: () => ({}) },
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
import { vi } from 'vitest';
|
|
13
|
+
import { DataAdapter } from './DataAdapter';
|
|
14
|
+
|
|
15
|
+
describe('DataAdapter', () => {
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
localStorage.clear();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('clear() with no key removes only this namespace\'s keys, leaving unrelated keys intact', () => {
|
|
21
|
+
localStorage.setItem('myns-foo', '"bar"');
|
|
22
|
+
localStorage.setItem('other-bar', '"baz"');
|
|
23
|
+
|
|
24
|
+
const adapter = new DataAdapter();
|
|
25
|
+
adapter.initialize({ namespace: 'myns', initial: { foo: 'bar' } });
|
|
26
|
+
|
|
27
|
+
adapter.clear();
|
|
28
|
+
|
|
29
|
+
expect(localStorage.getItem('myns-foo')).toBeNull();
|
|
30
|
+
expect(localStorage.getItem('other-bar')).toBe('"baz"');
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -103,6 +103,7 @@ export class DataAdapter extends Plugin<Partial<IDataAdapterOptions>> implements
|
|
|
103
103
|
|
|
104
104
|
destroy() {
|
|
105
105
|
this.data = {} as DataAdapterType;
|
|
106
|
+
super.destroy();
|
|
106
107
|
}
|
|
107
108
|
|
|
108
109
|
public initialize(options?: Partial<IDataAdapterOptions>): void {
|
|
@@ -205,7 +206,10 @@ export class DataAdapter extends Plugin<Partial<IDataAdapterOptions>> implements
|
|
|
205
206
|
clear<K extends keyof DataAdapterType>(key?: K): void {
|
|
206
207
|
if (key === undefined) {
|
|
207
208
|
this.data = {} as DataAdapterType;
|
|
208
|
-
|
|
209
|
+
const prefix = `${this.namespace}-`;
|
|
210
|
+
for (const storageKey of Object.keys(localStorage).filter((k) => k.startsWith(prefix))) {
|
|
211
|
+
localStorage.removeItem(storageKey);
|
|
212
|
+
}
|
|
209
213
|
this.onDataChange.emit({ key, clear: true });
|
|
210
214
|
} else {
|
|
211
215
|
delete this.data[key];
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
// FullScreenPlugin transitively imports Application → Pixi display graph. Stub it.
|
|
4
|
+
vi.mock('../core', () => ({
|
|
5
|
+
coreFunctionRegistry: {},
|
|
6
|
+
coreSignalRegistry: {},
|
|
7
|
+
}));
|
|
8
|
+
vi.mock('../core/Application', () => ({
|
|
9
|
+
Application: { getInstance: () => ({}), containerElement: null },
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
import { FullScreenPlugin } from './FullScreenPlugin';
|
|
13
|
+
|
|
14
|
+
describe('FullScreenPlugin fullscreenchange handling', () => {
|
|
15
|
+
let plugin: FullScreenPlugin;
|
|
16
|
+
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
plugin = new FullScreenPlugin();
|
|
19
|
+
plugin.initialize();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
plugin.destroy();
|
|
24
|
+
delete (document as any).webkitFullscreenElement;
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('clears the cached state when the browser leaves fullscreen', () => {
|
|
28
|
+
(plugin as any)._isFullScreen = true;
|
|
29
|
+
const spy = vi.fn();
|
|
30
|
+
plugin.onFullScreenChange.connect(spy);
|
|
31
|
+
|
|
32
|
+
document.dispatchEvent(new Event('fullscreenchange'));
|
|
33
|
+
|
|
34
|
+
expect(plugin.isFullScreen).toBe(false);
|
|
35
|
+
expect(spy).toHaveBeenCalledWith(false);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('sets the cached state from vendor-prefixed fullscreen elements', () => {
|
|
39
|
+
(document as any).webkitFullscreenElement = document.body;
|
|
40
|
+
const spy = vi.fn();
|
|
41
|
+
plugin.onFullScreenChange.connect(spy);
|
|
42
|
+
|
|
43
|
+
document.dispatchEvent(new Event('webkitfullscreenchange'));
|
|
44
|
+
|
|
45
|
+
expect(plugin.isFullScreen).toBe(true);
|
|
46
|
+
expect(spy).toHaveBeenCalledWith(true);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -160,6 +160,7 @@ export class FullScreenPlugin extends Plugin implements IFullScreenPlugin {
|
|
|
160
160
|
document.removeEventListener('mozfullscreenchange', this._onFullScreenChange);
|
|
161
161
|
document.removeEventListener('msfullscreenchange', this._onFullScreenChange);
|
|
162
162
|
document.removeEventListener('fullscreenchange', this._onFullScreenChange);
|
|
163
|
+
super.destroy();
|
|
163
164
|
}
|
|
164
165
|
|
|
165
166
|
/**
|
|
@@ -354,6 +355,9 @@ export class FullScreenPlugin extends Plugin implements IFullScreenPlugin {
|
|
|
354
355
|
* @private
|
|
355
356
|
*/
|
|
356
357
|
private _onFullScreenChange(): void {
|
|
357
|
-
|
|
358
|
+
// the user can leave fullscreen without us (Esc / browser chrome), so re-read
|
|
359
|
+
// the real state instead of trusting the cached flag
|
|
360
|
+
this._isFullScreen = this.isFullscreen;
|
|
361
|
+
this.onFullScreenChange.emit(this._isFullScreen);
|
|
358
362
|
}
|
|
359
363
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
// GSAPPlugin.ts extends Plugin.ts, which transitively imports Application → Pixi display graph. Stub it.
|
|
4
|
+
vi.mock('../core', () => ({
|
|
5
|
+
coreFunctionRegistry: {},
|
|
6
|
+
coreSignalRegistry: {},
|
|
7
|
+
}));
|
|
8
|
+
vi.mock('../core/Application', () => ({
|
|
9
|
+
Application: { getInstance: () => ({}) },
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
import { GSAPPlugin } from './GSAPPlugin';
|
|
13
|
+
|
|
14
|
+
class TestGSAPPlugin extends GSAPPlugin {
|
|
15
|
+
public createGlobalContextPublic(): void {
|
|
16
|
+
this.createGlobalContext();
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe('GSAPPlugin', () => {
|
|
21
|
+
it('clearGlobal() does not throw and empties the global context', () => {
|
|
22
|
+
const plugin = new TestGSAPPlugin();
|
|
23
|
+
plugin.createGlobalContextPublic();
|
|
24
|
+
|
|
25
|
+
plugin.addAnimation({} as never);
|
|
26
|
+
expect(plugin.getContext('__caper_global')?.size).toBe(1);
|
|
27
|
+
|
|
28
|
+
expect(() => plugin.clearGlobal()).not.toThrow();
|
|
29
|
+
expect(plugin.getContext('__caper_global')?.size).toBe(0);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -348,9 +348,6 @@ export class GSAPPlugin extends Plugin implements IGSAPPlugin {
|
|
|
348
348
|
(detail: { animation: gsap.core.Tween | gsap.core.Timeline; contextId: string }) => void
|
|
349
349
|
>();
|
|
350
350
|
|
|
351
|
-
/** Global animation context reference for quick access */
|
|
352
|
-
private _globalContext: AnimationContext;
|
|
353
|
-
|
|
354
351
|
/** Map of all custom animation contexts by their string identifiers */
|
|
355
352
|
private _animationContexts: Map<string, AnimationContext> = new Map();
|
|
356
353
|
|
|
@@ -804,7 +801,7 @@ export class GSAPPlugin extends Plugin implements IGSAPPlugin {
|
|
|
804
801
|
if (kill) {
|
|
805
802
|
this.killGlobal();
|
|
806
803
|
}
|
|
807
|
-
this.
|
|
804
|
+
this._animationContexts.get(GSAPPlugin.GLOBAL_CONTEXT_ID)?.clear();
|
|
808
805
|
|
|
809
806
|
this.onClearGlobal.emit(kill);
|
|
810
807
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
// LookupPlugin -> Plugin.ts transitively imports Application → Pixi display graph. Stub it.
|
|
4
|
+
vi.mock('../core', () => ({
|
|
5
|
+
coreFunctionRegistry: {} as Record<string, unknown>,
|
|
6
|
+
coreSignalRegistry: {} as Record<string, unknown>,
|
|
7
|
+
}));
|
|
8
|
+
vi.mock('../core/Application', () => ({
|
|
9
|
+
Application: { getInstance: () => ({}) },
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
import { coreFunctionRegistry } from '../core';
|
|
13
|
+
import { Container } from '../display/Container';
|
|
14
|
+
import { LookupPlugin } from './LookupPlugin';
|
|
15
|
+
|
|
16
|
+
describe('LookupPlugin', () => {
|
|
17
|
+
it('registers getChildAtPath (and its siblings) in the core function registry', () => {
|
|
18
|
+
const plugin = new LookupPlugin();
|
|
19
|
+
plugin.registerCoreFunctions();
|
|
20
|
+
|
|
21
|
+
expect((coreFunctionRegistry as Record<string, unknown>).getChildAtPath).toBe(plugin.getChildAtPath);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('disconnects its global container subscriptions on destroy', async () => {
|
|
25
|
+
const plugin = new LookupPlugin();
|
|
26
|
+
const added = vi.spyOn(plugin as any, 'onChildAdded');
|
|
27
|
+
const removed = vi.spyOn(plugin as any, 'onChildRemoved');
|
|
28
|
+
await plugin.initialize();
|
|
29
|
+
|
|
30
|
+
const child = { label: 'Thing', children: [] } as any;
|
|
31
|
+
Container.onGlobalChildAdded.emit(child);
|
|
32
|
+
Container.onGlobalChildRemoved.emit(child);
|
|
33
|
+
expect(added).toHaveBeenCalledTimes(1);
|
|
34
|
+
expect(removed).toHaveBeenCalledTimes(1);
|
|
35
|
+
|
|
36
|
+
plugin.destroy();
|
|
37
|
+
Container.onGlobalChildAdded.emit(child);
|
|
38
|
+
Container.onGlobalChildRemoved.emit(child);
|
|
39
|
+
expect(added).toHaveBeenCalledTimes(1);
|
|
40
|
+
expect(removed).toHaveBeenCalledTimes(1);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -97,7 +97,7 @@ export class LookupPlugin extends Plugin implements ILookupPlugin {
|
|
|
97
97
|
/**
|
|
98
98
|
* Core function names exposed by this plugin for framework integration.
|
|
99
99
|
*/
|
|
100
|
-
|
|
100
|
+
protected getCoreFunctions(): string[] {
|
|
101
101
|
return ['getChildAtPath', 'getPathForChild', 'getChildrenAtPaths', 'getPathsForChildren', 'getAllPaths'];
|
|
102
102
|
}
|
|
103
103
|
|
|
@@ -106,8 +106,20 @@ export class LookupPlugin extends Plugin implements ILookupPlugin {
|
|
|
106
106
|
* Sets up automatic tracking of container hierarchy changes.
|
|
107
107
|
*/
|
|
108
108
|
public async initialize() {
|
|
109
|
-
|
|
110
|
-
|
|
109
|
+
this.addSignalConnection(
|
|
110
|
+
Container.onGlobalChildAdded.connect(this.onChildAdded),
|
|
111
|
+
Container.onGlobalChildRemoved.connect(this.onChildRemoved),
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Disconnects the global container subscriptions and drops the lookup maps,
|
|
117
|
+
* so a destroyed plugin stops tracking (and holding on to) containers.
|
|
118
|
+
*/
|
|
119
|
+
public destroy(): void {
|
|
120
|
+
this.pathToContainer.clear();
|
|
121
|
+
this.containerToPath.clear();
|
|
122
|
+
super.destroy();
|
|
111
123
|
}
|
|
112
124
|
|
|
113
125
|
/**
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
const mockApp = vi.hoisted(() => ({
|
|
4
|
+
ticker: { addOnce: (cb: () => void) => cb() },
|
|
5
|
+
}));
|
|
6
|
+
|
|
7
|
+
// PopupManagerPlugin transitively imports Application → Pixi display graph. Stub it.
|
|
8
|
+
vi.mock('../core', () => ({
|
|
9
|
+
coreFunctionRegistry: {},
|
|
10
|
+
coreSignalRegistry: {},
|
|
11
|
+
}));
|
|
12
|
+
vi.mock('../core/Application', () => ({
|
|
13
|
+
Application: { getInstance: () => mockApp },
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
// Evaluating the table registers it into mixins/factory/defaults, which the
|
|
17
|
+
// plugin's `view` Container reads lazily on construction.
|
|
18
|
+
import '../mixins/factory/const';
|
|
19
|
+
import { PopupManagerPlugin } from './PopupManagerPlugin';
|
|
20
|
+
|
|
21
|
+
function makeFakePopup() {
|
|
22
|
+
return {
|
|
23
|
+
beforeHide: vi.fn(),
|
|
24
|
+
hide: vi.fn().mockResolvedValue(undefined),
|
|
25
|
+
end: vi.fn(),
|
|
26
|
+
restoreActionContext: vi.fn(),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
describe('PopupManagerPlugin removeAllPopups', () => {
|
|
31
|
+
let plugin: PopupManagerPlugin;
|
|
32
|
+
|
|
33
|
+
beforeEach(() => {
|
|
34
|
+
plugin = new PopupManagerPlugin();
|
|
35
|
+
vi.spyOn(plugin.view, 'removeChild').mockReturnValue(undefined as never);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('clears active popups and the current id when animating', async () => {
|
|
39
|
+
const first = makeFakePopup();
|
|
40
|
+
const second = makeFakePopup();
|
|
41
|
+
(plugin as any)._activePopups.set('first', first);
|
|
42
|
+
(plugin as any)._activePopups.set('second', second);
|
|
43
|
+
(plugin as any)._currentPopupId = 'second';
|
|
44
|
+
|
|
45
|
+
plugin.removeAllPopups(true);
|
|
46
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
47
|
+
|
|
48
|
+
expect(first.hide).toHaveBeenCalledTimes(1);
|
|
49
|
+
expect(second.hide).toHaveBeenCalledTimes(1);
|
|
50
|
+
expect(plugin.hasActivePopups).toBe(false);
|
|
51
|
+
expect(plugin.currentPopupId).toBeUndefined();
|
|
52
|
+
});
|
|
53
|
+
});
|
|
@@ -179,9 +179,11 @@ export class PopupManagerPlugin extends Plugin implements IPopupManagerPlugin {
|
|
|
179
179
|
*/
|
|
180
180
|
removeAllPopups(animate: boolean = false): void {
|
|
181
181
|
if (animate) {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
182
|
+
// reuse the single-popup hide path so the view, the active map and the
|
|
183
|
+
// current id get cleaned up once each hide animation resolves
|
|
184
|
+
for (const id of [...this._activePopups.keys()]) {
|
|
185
|
+
void this.hidePopup(id);
|
|
186
|
+
}
|
|
185
187
|
} else {
|
|
186
188
|
this._activePopups.clear();
|
|
187
189
|
this.view.removeChildren();
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { Signal } from '../signals';
|
|
4
|
+
|
|
5
|
+
const mockApp = vi.hoisted(() => ({
|
|
6
|
+
config: {} as Record<string, unknown>,
|
|
7
|
+
onPause: null as any,
|
|
8
|
+
onResume: null as any,
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
// SceneManagerPlugin transitively imports Application → Pixi display graph. Stub it.
|
|
12
|
+
vi.mock('../core', () => ({
|
|
13
|
+
coreFunctionRegistry: {},
|
|
14
|
+
coreSignalRegistry: {},
|
|
15
|
+
}));
|
|
16
|
+
vi.mock('../core/Application', () => ({
|
|
17
|
+
Application: { getInstance: () => mockApp, containerElement: null },
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
import { SceneManagerPlugin } from './SceneManagerPlugin';
|
|
21
|
+
|
|
22
|
+
describe('SceneManagerPlugin destroy', () => {
|
|
23
|
+
let plugin: SceneManagerPlugin;
|
|
24
|
+
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
mockApp.config = { showSceneDebugMenu: false, useHash: true };
|
|
27
|
+
mockApp.onPause = new Signal<(config: any) => void>();
|
|
28
|
+
mockApp.onResume = new Signal<(config: any) => void>();
|
|
29
|
+
(globalThis as any).Caper = { get: () => [] };
|
|
30
|
+
plugin = new SceneManagerPlugin();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
afterEach(() => {
|
|
34
|
+
delete (globalThis as any).Caper;
|
|
35
|
+
window.location.hash = '';
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('stops responding to hashchange after destroy', async () => {
|
|
39
|
+
await plugin.initialize({}, mockApp as never);
|
|
40
|
+
const spy = vi.spyOn(plugin, 'getSceneFromHash').mockReturnValue(null);
|
|
41
|
+
|
|
42
|
+
window.dispatchEvent(new Event('hashchange'));
|
|
43
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
44
|
+
|
|
45
|
+
plugin.destroy();
|
|
46
|
+
window.dispatchEvent(new Event('hashchange'));
|
|
47
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('stops forwarding pause/resume to the current scene after destroy', async () => {
|
|
51
|
+
await plugin.initialize({}, mockApp as never);
|
|
52
|
+
const onPause = vi.fn();
|
|
53
|
+
const onResume = vi.fn();
|
|
54
|
+
plugin.currentScene = { onPause, onResume } as never;
|
|
55
|
+
|
|
56
|
+
mockApp.onPause.emit({});
|
|
57
|
+
mockApp.onResume.emit({});
|
|
58
|
+
expect(onPause).toHaveBeenCalledTimes(1);
|
|
59
|
+
expect(onResume).toHaveBeenCalledTimes(1);
|
|
60
|
+
|
|
61
|
+
plugin.destroy();
|
|
62
|
+
mockApp.onPause.emit({});
|
|
63
|
+
mockApp.onResume.emit({});
|
|
64
|
+
expect(onPause).toHaveBeenCalledTimes(1);
|
|
65
|
+
expect(onResume).toHaveBeenCalledTimes(1);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('removes the debug menu from the DOM after destroy', async () => {
|
|
69
|
+
mockApp.config = { showSceneDebugMenu: true };
|
|
70
|
+
await plugin.initialize({}, mockApp as never);
|
|
71
|
+
expect(document.getElementById('scene-debug')).not.toBeNull();
|
|
72
|
+
|
|
73
|
+
plugin.destroy();
|
|
74
|
+
expect(document.getElementById('scene-debug')).toBeNull();
|
|
75
|
+
});
|
|
76
|
+
});
|
|
@@ -139,7 +139,11 @@ export class SceneManagerPlugin extends Plugin implements ISceneManagerPlugin {
|
|
|
139
139
|
this._defaultLoadMethod = method;
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
public destroy(): void {
|
|
142
|
+
public destroy(): void {
|
|
143
|
+
window.removeEventListener('hashchange', this._onHashChange);
|
|
144
|
+
this._debugMenu?.parentElement?.removeChild(this._debugMenu);
|
|
145
|
+
super.destroy();
|
|
146
|
+
}
|
|
143
147
|
|
|
144
148
|
public async initialize(_options: any, app: IApplication): Promise<void> {
|
|
145
149
|
this._debugVisible =
|
|
@@ -179,8 +183,10 @@ export class SceneManagerPlugin extends Plugin implements ISceneManagerPlugin {
|
|
|
179
183
|
this._listenForHashChange();
|
|
180
184
|
}
|
|
181
185
|
|
|
182
|
-
this.
|
|
183
|
-
|
|
186
|
+
this.addSignalConnection(
|
|
187
|
+
this.app.onPause.connect(this._onPause, 'highest'),
|
|
188
|
+
this.app.onResume.connect(this._onResume, 'highest'),
|
|
189
|
+
);
|
|
184
190
|
|
|
185
191
|
return Promise.resolve(undefined);
|
|
186
192
|
}
|
|
@@ -368,12 +374,14 @@ export class SceneManagerPlugin extends Plugin implements ISceneManagerPlugin {
|
|
|
368
374
|
}
|
|
369
375
|
|
|
370
376
|
private _listenForHashChange() {
|
|
371
|
-
window.addEventListener('hashchange',
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
+
window.addEventListener('hashchange', this._onHashChange);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
private _onHashChange() {
|
|
381
|
+
const sceneId = this.getSceneFromHash();
|
|
382
|
+
if (sceneId) {
|
|
383
|
+
void this.loadScene(sceneId);
|
|
384
|
+
}
|
|
377
385
|
}
|
|
378
386
|
|
|
379
387
|
private async _createCurrentScene() {
|
|
@@ -655,8 +663,10 @@ export class SceneManagerPlugin extends Plugin implements ISceneManagerPlugin {
|
|
|
655
663
|
}
|
|
656
664
|
});
|
|
657
665
|
|
|
658
|
-
this.
|
|
659
|
-
|
|
666
|
+
this.addSignalConnection(
|
|
667
|
+
this.onSceneChangeStart.connect(this._disableDebugMenu),
|
|
668
|
+
this.onSceneChangeComplete.connect(this._enableDebugMenu),
|
|
669
|
+
);
|
|
660
670
|
}
|
|
661
671
|
|
|
662
672
|
private _enableDebugMenu() {
|
|
@@ -21,4 +21,12 @@ export class StatsPlugin extends Plugin {
|
|
|
21
21
|
|
|
22
22
|
Ticker.shared.add(this.stats.update, this.stats, UPDATE_PRIORITY.UTILITY);
|
|
23
23
|
}
|
|
24
|
+
|
|
25
|
+
public destroy(): void {
|
|
26
|
+
if (this.stats) {
|
|
27
|
+
Ticker.shared.remove(this.stats.update, this.stats);
|
|
28
|
+
this.stats.dom.remove();
|
|
29
|
+
}
|
|
30
|
+
super.destroy();
|
|
31
|
+
}
|
|
24
32
|
}
|