@swf2es/player 0.1.0 → 0.1.1
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/package.json +7 -7
- package/dist/audio.d.ts +0 -22
- package/dist/audio.d.ts.map +0 -1
- package/dist/audio.js +0 -106
- package/dist/audio.js.map +0 -1
- package/dist/bitmap-filters.d.ts +0 -29
- package/dist/bitmap-filters.d.ts.map +0 -1
- package/dist/bitmap-filters.js +0 -906
- package/dist/bitmap-filters.js.map +0 -1
- package/dist/bitmap-ops.d.ts +0 -113
- package/dist/bitmap-ops.d.ts.map +0 -1
- package/dist/bitmap-ops.js +0 -725
- package/dist/bitmap-ops.js.map +0 -1
- package/dist/bitmap.d.ts +0 -98
- package/dist/bitmap.d.ts.map +0 -1
- package/dist/bitmap.js +0 -314
- package/dist/bitmap.js.map +0 -1
- package/dist/bounds.d.ts +0 -26
- package/dist/bounds.d.ts.map +0 -1
- package/dist/bounds.js +0 -300
- package/dist/bounds.js.map +0 -1
- package/dist/color.d.ts +0 -7
- package/dist/color.d.ts.map +0 -1
- package/dist/color.js +0 -42
- package/dist/color.js.map +0 -1
- package/dist/display.d.ts +0 -425
- package/dist/display.d.ts.map +0 -1
- package/dist/display.js +0 -1274
- package/dist/display.js.map +0 -1
- package/dist/drawing.d.ts +0 -52
- package/dist/drawing.d.ts.map +0 -1
- package/dist/drawing.js +0 -204
- package/dist/drawing.js.map +0 -1
- package/dist/filters.d.ts +0 -54
- package/dist/filters.d.ts.map +0 -1
- package/dist/filters.js +0 -155
- package/dist/filters.js.map +0 -1
- package/dist/fonts.d.ts +0 -23
- package/dist/fonts.d.ts.map +0 -1
- package/dist/fonts.js +0 -91
- package/dist/fonts.js.map +0 -1
- package/dist/geometry.d.ts +0 -21
- package/dist/geometry.d.ts.map +0 -1
- package/dist/geometry.js +0 -76
- package/dist/geometry.js.map +0 -1
- package/dist/gradients.d.ts +0 -29
- package/dist/gradients.d.ts.map +0 -1
- package/dist/gradients.js +0 -116
- package/dist/gradients.js.map +0 -1
- package/dist/images.d.ts +0 -22
- package/dist/images.d.ts.map +0 -1
- package/dist/images.js +0 -147
- package/dist/images.js.map +0 -1
- package/dist/input.d.ts +0 -79
- package/dist/input.d.ts.map +0 -1
- package/dist/input.js +0 -366
- package/dist/input.js.map +0 -1
- package/dist/keyboard.d.ts +0 -97
- package/dist/keyboard.d.ts.map +0 -1
- package/dist/keyboard.js +0 -583
- package/dist/keyboard.js.map +0 -1
- package/dist/morph.d.ts +0 -17
- package/dist/morph.d.ts.map +0 -1
- package/dist/morph.js +0 -218
- package/dist/morph.js.map +0 -1
- package/dist/pixi-blend.d.ts +0 -7
- package/dist/pixi-blend.d.ts.map +0 -1
- package/dist/pixi-blend.js +0 -253
- package/dist/pixi-blend.js.map +0 -1
- package/dist/pixi-color.d.ts +0 -54
- package/dist/pixi-color.d.ts.map +0 -1
- package/dist/pixi-color.js +0 -336
- package/dist/pixi-color.js.map +0 -1
- package/dist/pixi-filters.d.ts +0 -40
- package/dist/pixi-filters.d.ts.map +0 -1
- package/dist/pixi-filters.js +0 -953
- package/dist/pixi-filters.js.map +0 -1
- package/dist/pixi-resolve.d.ts +0 -4
- package/dist/pixi-resolve.d.ts.map +0 -1
- package/dist/pixi-resolve.js +0 -76
- package/dist/pixi-resolve.js.map +0 -1
- package/dist/pixi.d.ts +0 -219
- package/dist/pixi.d.ts.map +0 -1
- package/dist/pixi.js +0 -2169
- package/dist/pixi.js.map +0 -1
- package/dist/playerglobal/flash/filters/filters.d.ts +0 -26
- package/dist/playerglobal/flash/filters/filters.d.ts.map +0 -1
- package/dist/playerglobal/flash/filters/filters.js +0 -287
- package/dist/playerglobal/flash/filters/filters.js.map +0 -1
- package/dist/playerglobal/flash/system/FSCommand.d.ts +0 -4
- package/dist/playerglobal/flash/system/FSCommand.d.ts.map +0 -1
- package/dist/playerglobal/flash/system/FSCommand.js +0 -18
- package/dist/playerglobal/flash/system/FSCommand.js.map +0 -1
- package/dist/playerglobal/natives.d.ts +0 -10
- package/dist/playerglobal/natives.d.ts.map +0 -1
- package/dist/playerglobal/natives.js +0 -53
- package/dist/playerglobal/natives.js.map +0 -1
- package/dist/png.d.ts +0 -4
- package/dist/png.d.ts.map +0 -1
- package/dist/png.js +0 -140
- package/dist/png.js.map +0 -1
- package/dist/render/art.d.ts +0 -148
- package/dist/render/art.d.ts.map +0 -1
- package/dist/render/art.js +0 -575
- package/dist/render/art.js.map +0 -1
- package/dist/render/batchers.d.ts +0 -23
- package/dist/render/batchers.d.ts.map +0 -1
- package/dist/render/batchers.js +0 -52
- package/dist/render/batchers.js.map +0 -1
- package/dist/render/bitmaps.d.ts +0 -117
- package/dist/render/bitmaps.d.ts.map +0 -1
- package/dist/render/bitmaps.js +0 -422
- package/dist/render/bitmaps.js.map +0 -1
- package/dist/render/blend.d.ts +0 -37
- package/dist/render/blend.d.ts.map +0 -1
- package/dist/render/blend.js +0 -195
- package/dist/render/blend.js.map +0 -1
- package/dist/render/color.d.ts +0 -111
- package/dist/render/color.d.ts.map +0 -1
- package/dist/render/color.js +0 -533
- package/dist/render/color.js.map +0 -1
- package/dist/render/compositing.d.ts +0 -77
- package/dist/render/compositing.d.ts.map +0 -1
- package/dist/render/compositing.js +0 -188
- package/dist/render/compositing.js.map +0 -1
- package/dist/render/filters.d.ts +0 -98
- package/dist/render/filters.d.ts.map +0 -1
- package/dist/render/filters.js +0 -1326
- package/dist/render/filters.js.map +0 -1
- package/dist/render/hooks.d.ts +0 -4
- package/dist/render/hooks.d.ts.map +0 -1
- package/dist/render/hooks.js +0 -48
- package/dist/render/hooks.js.map +0 -1
- package/dist/render/node.d.ts +0 -139
- package/dist/render/node.d.ts.map +0 -1
- package/dist/render/node.js +0 -59
- package/dist/render/node.js.map +0 -1
- package/dist/render/patches.d.ts +0 -54
- package/dist/render/patches.d.ts.map +0 -1
- package/dist/render/patches.js +0 -154
- package/dist/render/patches.js.map +0 -1
- package/dist/render/planes.d.ts +0 -74
- package/dist/render/planes.d.ts.map +0 -1
- package/dist/render/planes.js +0 -267
- package/dist/render/planes.js.map +0 -1
- package/dist/render/pointer.d.ts +0 -5
- package/dist/render/pointer.d.ts.map +0 -1
- package/dist/render/pointer.js +0 -279
- package/dist/render/pointer.js.map +0 -1
- package/dist/render/pools.d.ts +0 -122
- package/dist/render/pools.d.ts.map +0 -1
- package/dist/render/pools.js +0 -238
- package/dist/render/pools.js.map +0 -1
- package/dist/render/resolve.d.ts +0 -4
- package/dist/render/resolve.d.ts.map +0 -1
- package/dist/render/resolve.js +0 -76
- package/dist/render/resolve.js.map +0 -1
- package/dist/render/saffron.d.ts +0 -135
- package/dist/render/saffron.d.ts.map +0 -1
- package/dist/render/saffron.js +0 -725
- package/dist/render/saffron.js.map +0 -1
- package/dist/render/settle.d.ts +0 -60
- package/dist/render/settle.d.ts.map +0 -1
- package/dist/render/settle.js +0 -128
- package/dist/render/settle.js.map +0 -1
- package/dist/render/shared.d.ts +0 -25
- package/dist/render/shared.d.ts.map +0 -1
- package/dist/render/shared.js +0 -33
- package/dist/render/shared.js.map +0 -1
- package/dist/render/snapshot.d.ts +0 -21
- package/dist/render/snapshot.d.ts.map +0 -1
- package/dist/render/snapshot.js +0 -91
- package/dist/render/snapshot.js.map +0 -1
- package/dist/render/spares.d.ts +0 -56
- package/dist/render/spares.d.ts.map +0 -1
- package/dist/render/spares.js +0 -160
- package/dist/render/spares.js.map +0 -1
- package/dist/render/strokes.d.ts +0 -107
- package/dist/render/strokes.d.ts.map +0 -1
- package/dist/render/strokes.js +0 -448
- package/dist/render/strokes.js.map +0 -1
- package/dist/render/table.d.ts +0 -213
- package/dist/render/table.d.ts.map +0 -1
- package/dist/render/table.js +0 -795
- package/dist/render/table.js.map +0 -1
- package/dist/render/tessellate.d.ts +0 -89
- package/dist/render/tessellate.d.ts.map +0 -1
- package/dist/render/tessellate.js +0 -386
- package/dist/render/tessellate.js.map +0 -1
- package/dist/render/text.d.ts +0 -40
- package/dist/render/text.d.ts.map +0 -1
- package/dist/render/text.js +0 -309
- package/dist/render/text.js.map +0 -1
- package/dist/render/three.d.ts +0 -83
- package/dist/render/three.d.ts.map +0 -1
- package/dist/render/three.js +0 -511
- package/dist/render/three.js.map +0 -1
- package/dist/render/triangles.d.ts +0 -28
- package/dist/render/triangles.d.ts.map +0 -1
- package/dist/render/triangles.js +0 -189
- package/dist/render/triangles.js.map +0 -1
- package/dist/render/view.d.ts +0 -238
- package/dist/render/view.d.ts.map +0 -1
- package/dist/render/view.js +0 -1173
- package/dist/render/view.js.map +0 -1
- package/dist/render/worker/bitmaps.d.ts +0 -43
- package/dist/render/worker/bitmaps.d.ts.map +0 -1
- package/dist/render/worker/bitmaps.js +0 -151
- package/dist/render/worker/bitmaps.js.map +0 -1
- package/dist/render/worker/drawing.d.ts +0 -21
- package/dist/render/worker/drawing.d.ts.map +0 -1
- package/dist/render/worker/drawing.js +0 -356
- package/dist/render/worker/drawing.js.map +0 -1
- package/dist/render/worker/filters.d.ts +0 -7
- package/dist/render/worker/filters.d.ts.map +0 -1
- package/dist/render/worker/filters.js +0 -125
- package/dist/render/worker/filters.js.map +0 -1
- package/dist/render/worker/frames.d.ts +0 -32
- package/dist/render/worker/frames.d.ts.map +0 -1
- package/dist/render/worker/frames.js +0 -124
- package/dist/render/worker/frames.js.map +0 -1
- package/dist/render/worker/host.d.ts +0 -60
- package/dist/render/worker/host.d.ts.map +0 -1
- package/dist/render/worker/host.js +0 -297
- package/dist/render/worker/host.js.map +0 -1
- package/dist/render/worker/libraries.d.ts +0 -36
- package/dist/render/worker/libraries.d.ts.map +0 -1
- package/dist/render/worker/libraries.js +0 -153
- package/dist/render/worker/libraries.js.map +0 -1
- package/dist/render/worker/presentation.d.ts +0 -18
- package/dist/render/worker/presentation.d.ts.map +0 -1
- package/dist/render/worker/presentation.js +0 -47
- package/dist/render/worker/presentation.js.map +0 -1
- package/dist/render/worker/renderer.d.ts +0 -4
- package/dist/render/worker/renderer.d.ts.map +0 -1
- package/dist/render/worker/renderer.js +0 -21
- package/dist/render/worker/renderer.js.map +0 -1
- package/dist/render/worker/scene.d.ts +0 -32
- package/dist/render/worker/scene.d.ts.map +0 -1
- package/dist/render/worker/scene.js +0 -611
- package/dist/render/worker/scene.js.map +0 -1
- package/dist/render/worker/text.d.ts +0 -47
- package/dist/render/worker/text.d.ts.map +0 -1
- package/dist/render/worker/text.js +0 -261
- package/dist/render/worker/text.js.map +0 -1
- package/dist/render/worker/thread.d.ts +0 -23
- package/dist/render/worker/thread.d.ts.map +0 -1
- package/dist/render/worker/thread.js +0 -107
- package/dist/render/worker/thread.js.map +0 -1
- package/dist/render/worker/values.d.ts +0 -18
- package/dist/render/worker/values.d.ts.map +0 -1
- package/dist/render/worker/values.js +0 -64
- package/dist/render/worker/values.js.map +0 -1
- package/dist/sha256.d.ts +0 -5
- package/dist/sha256.d.ts.map +0 -1
- package/dist/sha256.js +0 -96
- package/dist/sha256.js.map +0 -1
- package/dist/shapes.d.ts +0 -71
- package/dist/shapes.d.ts.map +0 -1
- package/dist/shapes.js +0 -340
- package/dist/shapes.js.map +0 -1
- package/dist/static-text.d.ts +0 -33
- package/dist/static-text.d.ts.map +0 -1
- package/dist/static-text.js +0 -105
- package/dist/static-text.js.map +0 -1
- package/dist/text-layout.d.ts +0 -66
- package/dist/text-layout.d.ts.map +0 -1
- package/dist/text-layout.js +0 -315
- package/dist/text-layout.js.map +0 -1
- package/dist/text.d.ts +0 -67
- package/dist/text.d.ts.map +0 -1
- package/dist/text.js +0 -383
- package/dist/text.js.map +0 -1
- package/dist/timeline.d.ts +0 -150
- package/dist/timeline.d.ts.map +0 -1
- package/dist/timeline.js +0 -266
- package/dist/timeline.js.map +0 -1
package/dist/display.js
DELETED
|
@@ -1,1274 +0,0 @@
|
|
|
1
|
-
// The display list the player owns: display objects, containers and
|
|
2
|
-
// movie clips, as Flash has them. A renderer mirrors it (see pixi.ts) and
|
|
3
|
-
// never decides its order. A container keeps its children in render order,
|
|
4
|
-
// which is their index in AS3, and apart from it the children the timeline
|
|
5
|
-
// placed, by depth, which is how SWF tags address them; a child the
|
|
6
|
-
// timeline places goes before the first child of a greater depth.
|
|
7
|
-
import { BUTTON_DOWN, BUTTON_HIT_TEST, BUTTON_OVER, BUTTON_UP, IDENTITY, readFilters, } from "@swf2es/format";
|
|
8
|
-
import { BitmapStore } from "./bitmap.js";
|
|
9
|
-
import { filterOfSwf } from "./filters.js";
|
|
10
|
-
import { shifted } from "./geometry.js";
|
|
11
|
-
import { morphAt } from "./morph.js";
|
|
12
|
-
import { placeGlyphs } from "./static-text.js";
|
|
13
|
-
import { TextModel } from "./text.js";
|
|
14
|
-
import { GUTTER, layoutText } from "./text-layout.js";
|
|
15
|
-
import { INVALID_PIXELS, } from "./timeline.js";
|
|
16
|
-
/** Nothing changed since the renderer last looked, or what did. */
|
|
17
|
-
export const CLEAN = 0;
|
|
18
|
-
export const TRANSFORM = 1;
|
|
19
|
-
export const CHILDREN = 2;
|
|
20
|
-
export const CONTENT = 4;
|
|
21
|
-
/** A Bitmap's pixels changed in place: the texture uploads again, nothing is rebuilt. */
|
|
22
|
-
export const PIXELS = 8;
|
|
23
|
-
const NO_FILTERS = Object.freeze([]);
|
|
24
|
-
const NO_FILTER_BYTES = new Uint8Array([0]);
|
|
25
|
-
/** The blend modes by the number PlaceObject3 gives them: 0 and 1 are normal. */
|
|
26
|
-
export const BLEND_MODES = [
|
|
27
|
-
"normal",
|
|
28
|
-
"normal",
|
|
29
|
-
"layer",
|
|
30
|
-
"multiply",
|
|
31
|
-
"screen",
|
|
32
|
-
"lighten",
|
|
33
|
-
"darken",
|
|
34
|
-
"difference",
|
|
35
|
-
"add",
|
|
36
|
-
"subtract",
|
|
37
|
-
"invert",
|
|
38
|
-
"alpha",
|
|
39
|
-
"erase",
|
|
40
|
-
"overlay",
|
|
41
|
-
"hardlight",
|
|
42
|
-
];
|
|
43
|
-
/** Display objects are numbered as they are made: Flash runs orphans' scripts newest first. */
|
|
44
|
-
let made = 0;
|
|
45
|
-
const DEGREES = 180 / Math.PI;
|
|
46
|
-
/** A matrix column of scale `from` stretched to scale `to`; zeroed for a NaN, and null where there is no proportion to keep. */
|
|
47
|
-
function scaled(to, from, x, y) {
|
|
48
|
-
if (Number.isNaN(to)) {
|
|
49
|
-
return [0, 0];
|
|
50
|
-
}
|
|
51
|
-
const f = to / from;
|
|
52
|
-
return Number.isFinite(f) ? [x * f, y * f] : null;
|
|
53
|
-
}
|
|
54
|
-
/** The sine and cosine of `degrees`, exact at the quarter turns, where Flash's matrix has 0s and 1s and not the doubles' rounding (`replaces`). */
|
|
55
|
-
function sinCos(degrees) {
|
|
56
|
-
const quarters = degrees / 90;
|
|
57
|
-
if (Number.isInteger(quarters)) {
|
|
58
|
-
const k = ((quarters % 4) + 4) % 4;
|
|
59
|
-
return [[0, 1, 0, -1][k], [1, 0, -1, 0][k]];
|
|
60
|
-
}
|
|
61
|
-
const r = degrees / DEGREES;
|
|
62
|
-
return [Math.sin(r), Math.cos(r)];
|
|
63
|
-
}
|
|
64
|
-
/** Degrees into Flash's range, -180 to 180 with both ends kept as given; a NaN stays NaN. */
|
|
65
|
-
export function normalizeDegrees(degrees) {
|
|
66
|
-
let d = degrees % 360;
|
|
67
|
-
if (d > 180) {
|
|
68
|
-
d -= 360;
|
|
69
|
-
}
|
|
70
|
-
else if (d < -180) {
|
|
71
|
-
d += 360;
|
|
72
|
-
}
|
|
73
|
-
return d;
|
|
74
|
-
}
|
|
75
|
-
export class DisplayObject {
|
|
76
|
-
serial = made++;
|
|
77
|
-
parent = null;
|
|
78
|
-
/** The timeline depth it was placed at, or null for one a script added. */
|
|
79
|
-
depth = null;
|
|
80
|
-
/** The frame of its parent's timeline that placed it, 1 the first; 0 for one a script added. */
|
|
81
|
-
placeFrame = 0;
|
|
82
|
-
/**
|
|
83
|
-
* The ratio its placements gave, 0 for none: a rewind keeps the child
|
|
84
|
-
* only where the frames replayed give it the same (gotoFrame).
|
|
85
|
-
*/
|
|
86
|
-
ratio = 0;
|
|
87
|
-
name = "";
|
|
88
|
-
/** Whether its name is one the timeline gave it, which its parent has a property of; not a default instanceN. */
|
|
89
|
-
timelineNamed = false;
|
|
90
|
-
/**
|
|
91
|
-
* Its transform in its parent, translation in pixels: made from the
|
|
92
|
-
* scales, rotation and skew below, which are the object's own, as Flash
|
|
93
|
-
* keeps them apart from the matrix; set whole, it is taken apart into them.
|
|
94
|
-
*/
|
|
95
|
-
matrix = { ...IDENTITY };
|
|
96
|
-
scaleX = 1;
|
|
97
|
-
scaleY = 1;
|
|
98
|
-
/** In degrees, -180 to 180, as Flash reports it. */
|
|
99
|
-
rotation = 0;
|
|
100
|
-
/** The second column's turn beyond the first's, in degrees: 0 but for a matrix that skews. */
|
|
101
|
-
skew = 0;
|
|
102
|
-
colorTransform = null;
|
|
103
|
-
visible = true;
|
|
104
|
-
/**
|
|
105
|
-
* Where it has the keyboard's focus, how to drop it: Flash takes focus
|
|
106
|
-
* from an object taken off its parent or hidden (keyboard.ts).
|
|
107
|
-
*/
|
|
108
|
-
focusDrop = null;
|
|
109
|
-
/** The character it was made from, or null. */
|
|
110
|
-
character = null;
|
|
111
|
-
/** Whether a script set a property of it; from then on the timeline swaps no shape under it, as Flash's does not. */
|
|
112
|
-
scripted = false;
|
|
113
|
-
/**
|
|
114
|
-
* Whether a script set its transform or another property a place gives:
|
|
115
|
-
* from then on the timeline's places, moves and a rewind's alike, leave
|
|
116
|
-
* its matrix, colour, ratio, visibility, blend mode and filters as they
|
|
117
|
-
* are, all of them whichever was set, as Flash's do (`scripted-moves`).
|
|
118
|
-
* Setting visible, mask or cacheAsBitmap is no such touch.
|
|
119
|
-
*/
|
|
120
|
-
transformed = false;
|
|
121
|
-
/** Its other face, the AS3 object a script sees; null in an AVM1 movie. */
|
|
122
|
-
object = null;
|
|
123
|
-
/** The root of an AVM1 movie an AS3 Loader loaded: its other face, an AVM1Movie, is no InteractiveObject. */
|
|
124
|
-
avm1Root = false;
|
|
125
|
-
/** The LoaderInfo of the SWF this is the root of: set on the main root and on each loaded SWF's; null below. */
|
|
126
|
-
loaderInfo = null;
|
|
127
|
-
/** What its Graphics drew, for a Shape or Sprite a script draws in; null until one does. */
|
|
128
|
-
drawing = null;
|
|
129
|
-
/** What changed since the renderer last synced it: TRANSFORM, CHILDREN, CONTENT. */
|
|
130
|
-
dirty = TRANSFORM | CONTENT;
|
|
131
|
-
/** This object, weakly, as a store it shows or fills with holds it. */
|
|
132
|
-
ref = new WeakRef(this);
|
|
133
|
-
/**
|
|
134
|
-
* The last depth this clips, as a timeline's mask, or 0: a mask is not
|
|
135
|
-
* drawn, and clips the children after it until one placed deeper.
|
|
136
|
-
*/
|
|
137
|
-
clipDepth = 0;
|
|
138
|
-
/** The object `mask` set clips this one to; and the one it clips, for such a mask. */
|
|
139
|
-
mask = null;
|
|
140
|
-
maskOf = null;
|
|
141
|
-
/**
|
|
142
|
-
* scrollRect as set, its edges whole pixels, and as the last render took
|
|
143
|
-
* it, which the drawing, bounds and points go by: Flash's takes effect
|
|
144
|
-
* when it next draws.
|
|
145
|
-
*/
|
|
146
|
-
scrollRect = null;
|
|
147
|
-
scroll = null;
|
|
148
|
-
/** Its blend mode, as BlendMode names it. */
|
|
149
|
-
blendMode = "normal";
|
|
150
|
-
/** Its filters' values, which its `filters` reads copies of. */
|
|
151
|
-
filters = NO_FILTERS;
|
|
152
|
-
/** A store it shows or fills with changed its pixels, or was disposed. */
|
|
153
|
-
pixelsChanged(disposed) {
|
|
154
|
-
this.invalidate(disposed ? CONTENT : PIXELS);
|
|
155
|
-
}
|
|
156
|
-
/** Mark a change, and that its ancestors have a changed descendant. */
|
|
157
|
-
invalidate(what) {
|
|
158
|
-
this.dirty |= what;
|
|
159
|
-
for (let p = this.parent; p && !p.descendantsDirty; p = p.parent) {
|
|
160
|
-
p.descendantsDirty = true;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
/** Its matrix after its scroll's shift: from what it draws to its parent's space. */
|
|
164
|
-
get placed() {
|
|
165
|
-
return this.scroll ? shifted(this.matrix, this.scroll) : this.matrix;
|
|
166
|
-
}
|
|
167
|
-
/** Whether `o` is this or under it. */
|
|
168
|
-
encloses(o) {
|
|
169
|
-
for (let p = o; p; p = p.parent) {
|
|
170
|
-
if (p === this) {
|
|
171
|
-
return true;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
return false;
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* Clip this to `mask`'s fills, or to nothing for null. A mask clips one
|
|
178
|
-
* object: set on another it leaves the one before, as Flash's does.
|
|
179
|
-
*/
|
|
180
|
-
setMask(mask) {
|
|
181
|
-
if (this.mask === mask) {
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
if (this.mask) {
|
|
185
|
-
this.mask.maskOf = null;
|
|
186
|
-
this.mask.invalidate(TRANSFORM);
|
|
187
|
-
}
|
|
188
|
-
if (mask?.maskOf) {
|
|
189
|
-
mask.maskOf.mask = null;
|
|
190
|
-
mask.maskOf.invalidate(TRANSFORM);
|
|
191
|
-
}
|
|
192
|
-
this.mask = mask;
|
|
193
|
-
if (mask) {
|
|
194
|
-
mask.maskOf = this;
|
|
195
|
-
mask.invalidate(TRANSFORM);
|
|
196
|
-
}
|
|
197
|
-
this.invalidate(TRANSFORM);
|
|
198
|
-
}
|
|
199
|
-
/** A script set a property a place gives (`transformed`), which is also a touch (`scripted`). */
|
|
200
|
-
touch() {
|
|
201
|
-
this.scripted = true;
|
|
202
|
-
this.transformed = true;
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* The matrix set whole, and taken apart: the scales are its columns'
|
|
206
|
-
* lengths, the rotation the first column's angle, the skew the second's
|
|
207
|
-
* beyond that. A negative scale set as such is not recovered: a half
|
|
208
|
-
* turn is what the matrix says.
|
|
209
|
-
*/
|
|
210
|
-
setMatrix(m) {
|
|
211
|
-
this.matrix = { ...m };
|
|
212
|
-
this.scaleX = Math.hypot(m.a, m.b);
|
|
213
|
-
this.scaleY = Math.hypot(m.c, m.d);
|
|
214
|
-
this.rotation = Math.atan2(m.b, m.a) * DEGREES;
|
|
215
|
-
this.skew = normalizeDegrees(Math.atan2(-m.c, m.d) * DEGREES - this.rotation);
|
|
216
|
-
this.invalidate(TRANSFORM);
|
|
217
|
-
}
|
|
218
|
-
/**
|
|
219
|
-
* A scale set stretches its column of the matrix in proportion, so the
|
|
220
|
-
* other column stays exactly as it was, as Flash's does (the corpus's
|
|
221
|
-
* `displayobject_invalid_floats`); from a scale of 0 or NaN, where there
|
|
222
|
-
* is no proportion, the column is made anew from the angles. A NaN is
|
|
223
|
-
* reported back as set, as Flash reports it: a NaN scale zeroes its
|
|
224
|
-
* column, a NaN rotation leaves the matrix as it was and counts as none
|
|
225
|
-
* from then on.
|
|
226
|
-
*/
|
|
227
|
-
setScaleX(v) {
|
|
228
|
-
const column = scaled(v, this.scaleX, this.matrix.a, this.matrix.b);
|
|
229
|
-
this.scaleX = v;
|
|
230
|
-
if (column) {
|
|
231
|
-
this.matrix = { ...this.matrix, a: column[0], b: column[1] };
|
|
232
|
-
this.invalidate(TRANSFORM);
|
|
233
|
-
}
|
|
234
|
-
else {
|
|
235
|
-
this.remake();
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
setScaleY(v) {
|
|
239
|
-
const column = scaled(v, this.scaleY, this.matrix.c, this.matrix.d);
|
|
240
|
-
this.scaleY = v;
|
|
241
|
-
if (column) {
|
|
242
|
-
this.matrix = { ...this.matrix, c: column[0], d: column[1] };
|
|
243
|
-
this.invalidate(TRANSFORM);
|
|
244
|
-
}
|
|
245
|
-
else {
|
|
246
|
-
this.remake();
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
setRotation(degrees) {
|
|
250
|
-
this.rotation = normalizeDegrees(degrees);
|
|
251
|
-
if (!Number.isNaN(degrees)) {
|
|
252
|
-
this.remake();
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
remake() {
|
|
256
|
-
const [sinR, cosR] = sinCos(this.rotation || 0);
|
|
257
|
-
const [sinQ, cosQ] = sinCos((this.rotation || 0) + this.skew);
|
|
258
|
-
this.matrix = {
|
|
259
|
-
a: (this.scaleX || 0) * cosR,
|
|
260
|
-
b: (this.scaleX || 0) * sinR,
|
|
261
|
-
c: -(this.scaleY || 0) * sinQ,
|
|
262
|
-
d: (this.scaleY || 0) * cosQ,
|
|
263
|
-
tx: this.matrix.tx,
|
|
264
|
-
ty: this.matrix.ty,
|
|
265
|
-
};
|
|
266
|
-
this.invalidate(TRANSFORM);
|
|
267
|
-
}
|
|
268
|
-
/** Apply a place's transform, colour, name and visibility; to one a script transformed, its name and clip depth only. */
|
|
269
|
-
applyPlace(place) {
|
|
270
|
-
if (place.name !== null) {
|
|
271
|
-
this.name = place.name;
|
|
272
|
-
this.timelineNamed = true;
|
|
273
|
-
}
|
|
274
|
-
if (this.transformed) {
|
|
275
|
-
if (place.clipDepth !== null) {
|
|
276
|
-
this.applyRare({ ...place, blendMode: null, filters: null });
|
|
277
|
-
}
|
|
278
|
-
return;
|
|
279
|
-
}
|
|
280
|
-
if (place.matrix) {
|
|
281
|
-
const m = place.matrix;
|
|
282
|
-
this.setMatrix({ a: m.a, b: m.b, c: m.c, d: m.d, tx: m.tx / 20, ty: m.ty / 20 });
|
|
283
|
-
}
|
|
284
|
-
if (place.colorTransform) {
|
|
285
|
-
this.colorTransform = place.colorTransform;
|
|
286
|
-
this.invalidate(TRANSFORM);
|
|
287
|
-
}
|
|
288
|
-
if (place.ratio !== null) {
|
|
289
|
-
this.ratio = place.ratio;
|
|
290
|
-
}
|
|
291
|
-
if (place.visible !== null) {
|
|
292
|
-
this.visible = place.visible;
|
|
293
|
-
this.invalidate(TRANSFORM);
|
|
294
|
-
if (!place.visible) {
|
|
295
|
-
this.focusDrop?.(this);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
// Apart, and once tested: applyPlace runs for each move of each frame, and grown it is no longer inlined.
|
|
299
|
-
if (place.clipDepth !== null || place.blendMode !== null || place.filters !== null) {
|
|
300
|
-
this.applyRare(place);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
/** What a place sets that few do: a clip depth, a blend mode, filters. */
|
|
304
|
-
applyRare(place) {
|
|
305
|
-
if (place.filters !== null) {
|
|
306
|
-
this.filters = readFilters(place.filters).map(filterOfSwf);
|
|
307
|
-
this.invalidate(TRANSFORM);
|
|
308
|
-
}
|
|
309
|
-
if (place.clipDepth !== null && place.clipDepth !== this.clipDepth) {
|
|
310
|
-
this.clipDepth = place.clipDepth;
|
|
311
|
-
this.invalidate(TRANSFORM);
|
|
312
|
-
this.parent?.invalidate(CHILDREN);
|
|
313
|
-
}
|
|
314
|
-
if (place.blendMode !== null) {
|
|
315
|
-
this.setBlendMode(BLEND_MODES[place.blendMode] ?? "normal");
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
/** How it is composited with what is below it (BLEND_MODES). */
|
|
319
|
-
setBlendMode(mode) {
|
|
320
|
-
if (mode !== this.blendMode) {
|
|
321
|
-
this.blendMode = mode;
|
|
322
|
-
this.invalidate(TRANSFORM);
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
export class ShapeObject extends DisplayObject {
|
|
327
|
-
/** The shape it draws, or last drew; null for a Shape a script made, which draws nothing yet. */
|
|
328
|
-
shape;
|
|
329
|
-
/** A MorphShape's morph, which `shape` is a blend of. */
|
|
330
|
-
morph = null;
|
|
331
|
-
/**
|
|
332
|
-
* The blend drawn() last made, and its morph and ratio: it stands while
|
|
333
|
-
* `shape` is still it, which a swap to a shape is not.
|
|
334
|
-
*/
|
|
335
|
-
blended = null;
|
|
336
|
-
constructor(shape) {
|
|
337
|
-
super();
|
|
338
|
-
this.shape = shape;
|
|
339
|
-
this.character = shape;
|
|
340
|
-
}
|
|
341
|
-
/** A MorphShape, its morph's start until it is drawn at another ratio. */
|
|
342
|
-
static ofMorph(morph) {
|
|
343
|
-
const shape = morphAt(morph, 0);
|
|
344
|
-
const object = new ShapeObject(shape);
|
|
345
|
-
object.morph = morph;
|
|
346
|
-
object.blended = { morph, ratio: 0, shape };
|
|
347
|
-
object.character = morph;
|
|
348
|
-
return object;
|
|
349
|
-
}
|
|
350
|
-
/**
|
|
351
|
-
* A MorphShape takes on a new ratio when it is next drawn (drawn): until
|
|
352
|
-
* then its bounds and hit tests are the last drawn blend's, as Flash's
|
|
353
|
-
* are (the corpus's hittest_morph).
|
|
354
|
-
*/
|
|
355
|
-
applyPlace(place) {
|
|
356
|
-
const ratio = this.ratio;
|
|
357
|
-
super.applyPlace(place);
|
|
358
|
-
if (this.morph && this.ratio !== ratio) {
|
|
359
|
-
this.invalidate(CONTENT);
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
/** The shape to draw now: a MorphShape's blend at its ratio, which it keeps from here. */
|
|
363
|
-
drawn() {
|
|
364
|
-
// The blend it has is kept while its morph and ratio are: asking the
|
|
365
|
-
// morph again would turn over its few latest blends for nothing.
|
|
366
|
-
const b = this.blended;
|
|
367
|
-
if (this.morph &&
|
|
368
|
-
(b?.morph !== this.morph || b.ratio !== this.ratio || b.shape !== this.shape)) {
|
|
369
|
-
this.shape = morphAt(this.morph, this.ratio);
|
|
370
|
-
this.blended = { morph: this.morph, ratio: this.ratio, shape: this.shape };
|
|
371
|
-
}
|
|
372
|
-
return this.shape;
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
/** A StaticText: DefineText's glyphs, which only a timeline places; its bounds the tag's. */
|
|
376
|
-
export class StaticTextObject extends DisplayObject {
|
|
377
|
-
definition;
|
|
378
|
-
characters;
|
|
379
|
-
laidGlyphs = null;
|
|
380
|
-
constructor(definition, characters) {
|
|
381
|
-
super();
|
|
382
|
-
this.definition = definition;
|
|
383
|
-
this.characters = characters;
|
|
384
|
-
this.character = definition;
|
|
385
|
-
}
|
|
386
|
-
/**
|
|
387
|
-
* Its glyphs as placed, and its text, found the first time they are
|
|
388
|
-
* asked for: by then its SWF's fonts are all read, those defined after it
|
|
389
|
-
* too.
|
|
390
|
-
*/
|
|
391
|
-
get glyphs() {
|
|
392
|
-
this.laidGlyphs ??= placeGlyphs(this.definition, this.characters);
|
|
393
|
-
return this.laidGlyphs;
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
/**
|
|
397
|
-
* A TextField: placed by DefineEditText, or made by a script. Its text and
|
|
398
|
-
* formats are a TextModel; a timeline's starts with its tag's text, read
|
|
399
|
-
* as HTML if the tag says so, in the format the tag gives.
|
|
400
|
-
*/
|
|
401
|
-
export class TextObject extends DisplayObject {
|
|
402
|
-
definition;
|
|
403
|
-
model = new TextModel();
|
|
404
|
-
/** The embedded fonts its text may be in: its SWF's. */
|
|
405
|
-
fonts = null;
|
|
406
|
-
laid = null;
|
|
407
|
-
/** The field's rectangle, in its own pixels: from (left, top), width by height. */
|
|
408
|
-
left = 0;
|
|
409
|
-
top = 0;
|
|
410
|
-
width = 100;
|
|
411
|
-
height = 100;
|
|
412
|
-
border = false;
|
|
413
|
-
borderColor = 0;
|
|
414
|
-
background = false;
|
|
415
|
-
backgroundColor = 0xffffff;
|
|
416
|
-
multiline = false;
|
|
417
|
-
wordWrap = false;
|
|
418
|
-
type = "dynamic";
|
|
419
|
-
embedFonts = false;
|
|
420
|
-
autoSize = "none";
|
|
421
|
-
selectable = true;
|
|
422
|
-
maxChars = 0;
|
|
423
|
-
displayAsPassword = false;
|
|
424
|
-
condenseWhite = false;
|
|
425
|
-
antiAliasType = "normal";
|
|
426
|
-
gridFitType = "pixel";
|
|
427
|
-
sharpness = 0;
|
|
428
|
-
thickness = 0;
|
|
429
|
-
restrict = null;
|
|
430
|
-
mouseWheelEnabled = true;
|
|
431
|
-
alwaysShowSelection = false;
|
|
432
|
-
useRichTextClipboard = false;
|
|
433
|
-
scrollH = 0;
|
|
434
|
-
scrollV = 1;
|
|
435
|
-
/** The selection, from the end it was started at to the caret, as text indices; empty, a caret alone. */
|
|
436
|
-
anchorAt = 0;
|
|
437
|
-
caretAt = 0;
|
|
438
|
-
/** Whether it has the keyboard's focus, an input field then showing its caret. */
|
|
439
|
-
focused = false;
|
|
440
|
-
constructor(definition, initialText = true) {
|
|
441
|
-
super();
|
|
442
|
-
this.definition = definition;
|
|
443
|
-
this.character = definition;
|
|
444
|
-
const edit = definition?.definition;
|
|
445
|
-
if (!edit) {
|
|
446
|
-
return;
|
|
447
|
-
}
|
|
448
|
-
this.left = edit.bounds.xMin / 20;
|
|
449
|
-
this.top = edit.bounds.yMin / 20;
|
|
450
|
-
this.width = (edit.bounds.xMax - edit.bounds.xMin) / 20;
|
|
451
|
-
this.height = (edit.bounds.yMax - edit.bounds.yMin) / 20;
|
|
452
|
-
this.multiline = edit.multiline;
|
|
453
|
-
this.wordWrap = edit.wordWrap;
|
|
454
|
-
this.type = edit.readOnly ? "dynamic" : "input";
|
|
455
|
-
this.displayAsPassword = edit.password;
|
|
456
|
-
this.autoSize = edit.autoSize ? "left" : "none";
|
|
457
|
-
this.selectable = edit.selectable;
|
|
458
|
-
// A border in the tag is Flash's border and white background together.
|
|
459
|
-
this.border = edit.border;
|
|
460
|
-
this.background = edit.border;
|
|
461
|
-
this.maxChars = edit.maxLength ?? 0;
|
|
462
|
-
this.embedFonts = edit.useOutlines;
|
|
463
|
-
const font = definition?.font;
|
|
464
|
-
const color = edit.color;
|
|
465
|
-
this.model.defaultFormat = {
|
|
466
|
-
...this.model.defaultFormat,
|
|
467
|
-
font: font?.name ?? edit.fontClass ?? this.model.defaultFormat.font,
|
|
468
|
-
bold: font?.bold ?? false,
|
|
469
|
-
italic: font?.italic ?? false,
|
|
470
|
-
size: edit.fontHeight === null ? 12 : edit.fontHeight / 20,
|
|
471
|
-
// RGBA as the tag stores it.
|
|
472
|
-
color: color === null ? 0 : ((color & 0xff) << 16) | (color & 0xff00) | ((color >>> 16) & 0xff),
|
|
473
|
-
align: ["left", "right", "center", "justify"][edit.align] ?? "left",
|
|
474
|
-
leftMargin: edit.leftMargin / 20,
|
|
475
|
-
rightMargin: edit.rightMargin / 20,
|
|
476
|
-
indent: edit.indent / 20,
|
|
477
|
-
leading: edit.leading / 20,
|
|
478
|
-
};
|
|
479
|
-
if (!initialText) {
|
|
480
|
-
this.model.setText("");
|
|
481
|
-
}
|
|
482
|
-
else if (edit.html) {
|
|
483
|
-
this.model.setHtml(edit.text, edit.multiline);
|
|
484
|
-
}
|
|
485
|
-
else {
|
|
486
|
-
this.model.setText(edit.text);
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
/** Its text laid out, as of now: made again only when the text, its formats or the field changed. */
|
|
490
|
-
get layout() {
|
|
491
|
-
const key = `${this.model.revision}|${this.width}|${this.wordWrap}|${this.embedFonts}|${this.displayAsPassword}`;
|
|
492
|
-
if (this.laid?.key !== key) {
|
|
493
|
-
const text = this.model.text;
|
|
494
|
-
this.laid = {
|
|
495
|
-
key,
|
|
496
|
-
layout: layoutText({
|
|
497
|
-
// A password's characters are laid out, and drawn, as asterisks; its lines stay.
|
|
498
|
-
text: this.displayAsPassword ? text.replace(/[^\r]/g, "*") : text,
|
|
499
|
-
formats: this.model.formats,
|
|
500
|
-
defaultFormat: this.model.defaultFormat,
|
|
501
|
-
width: this.width,
|
|
502
|
-
wordWrap: this.wordWrap,
|
|
503
|
-
embedFonts: this.embedFonts,
|
|
504
|
-
fonts: this.fonts,
|
|
505
|
-
authored: this.definition !== null,
|
|
506
|
-
}),
|
|
507
|
-
};
|
|
508
|
-
}
|
|
509
|
-
return this.laid.layout;
|
|
510
|
-
}
|
|
511
|
-
/**
|
|
512
|
-
* Fit the field to its text, as autoSize does: its height to the text's
|
|
513
|
-
* and the gutters, and its width too unless it wraps, keeping its left,
|
|
514
|
-
* centre or right edge where it was.
|
|
515
|
-
*/
|
|
516
|
-
fit() {
|
|
517
|
-
if (this.autoSize === "none") {
|
|
518
|
-
return;
|
|
519
|
-
}
|
|
520
|
-
const layout = this.layout;
|
|
521
|
-
const height = (layout.height + 2 * GUTTER) / 20;
|
|
522
|
-
if (!this.wordWrap) {
|
|
523
|
-
const width = (layout.width + 2 * GUTTER) / 20;
|
|
524
|
-
const moved = this.autoSize === "center"
|
|
525
|
-
? (this.width - width) / 2
|
|
526
|
-
: this.autoSize === "right"
|
|
527
|
-
? this.width - width
|
|
528
|
-
: 0;
|
|
529
|
-
if (moved !== 0) {
|
|
530
|
-
this.matrix = { ...this.matrix, tx: Math.round((this.matrix.tx + moved) * 20) / 20 };
|
|
531
|
-
this.invalidate(TRANSFORM);
|
|
532
|
-
}
|
|
533
|
-
this.width = width;
|
|
534
|
-
}
|
|
535
|
-
this.height = height;
|
|
536
|
-
this.invalidate(CONTENT);
|
|
537
|
-
}
|
|
538
|
-
/** The text, \r between its lines. */
|
|
539
|
-
get text() {
|
|
540
|
-
return this.model.text;
|
|
541
|
-
}
|
|
542
|
-
/** The alignment of its first paragraph. */
|
|
543
|
-
get align() {
|
|
544
|
-
return (this.model.formats[0] ?? this.model.defaultFormat).align;
|
|
545
|
-
}
|
|
546
|
-
/** The end the selection was started at, within the text as it is now, which a script may have shortened. */
|
|
547
|
-
get anchor() {
|
|
548
|
-
return Math.min(this.anchorAt, this.model.text.length);
|
|
549
|
-
}
|
|
550
|
-
/** The caret, within the text as it is now. */
|
|
551
|
-
get caret() {
|
|
552
|
-
return Math.min(this.caretAt, this.model.text.length);
|
|
553
|
-
}
|
|
554
|
-
/** The selection's first and last index, in order. */
|
|
555
|
-
get selection() {
|
|
556
|
-
const [anchor, caret] = [this.anchor, this.caret];
|
|
557
|
-
return anchor <= caret ? [anchor, caret] : [caret, anchor];
|
|
558
|
-
}
|
|
559
|
-
/** Select from `anchor` to `caret`, each kept within the text; a focused field shows its caret there. */
|
|
560
|
-
select(anchor, caret) {
|
|
561
|
-
const length = this.model.text.length;
|
|
562
|
-
const [a, c] = [Math.max(0, Math.min(length, anchor)), Math.max(0, Math.min(length, caret))];
|
|
563
|
-
// A drag selects on every move, mostly what it had: redraw only for a change.
|
|
564
|
-
if (a === this.anchor && c === this.caret) {
|
|
565
|
-
return;
|
|
566
|
-
}
|
|
567
|
-
this.anchorAt = a;
|
|
568
|
-
this.caretAt = c;
|
|
569
|
-
this.invalidate(CONTENT);
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
/**
|
|
573
|
-
* Another character placed in a child's stead, with the move flag or where
|
|
574
|
-
* a rewind keeps the child: Flash makes no new object, and only a Shape or
|
|
575
|
-
* MorphShape no script has touched takes the new shape's or morph's
|
|
576
|
-
* graphic; a clip, a Shape a script set a property of, and a Shape a
|
|
577
|
-
* sprite is placed over all stay as they are (`replaces`).
|
|
578
|
-
*/
|
|
579
|
-
function swap(existing, character) {
|
|
580
|
-
if (!(existing instanceof ShapeObject) || existing.scripted || existing.character === character) {
|
|
581
|
-
return;
|
|
582
|
-
}
|
|
583
|
-
// A morph is blended when it is next drawn, at the ratio the place gives.
|
|
584
|
-
if (character.type === "morph") {
|
|
585
|
-
existing.morph = character;
|
|
586
|
-
}
|
|
587
|
-
else if (character.type === "shape") {
|
|
588
|
-
existing.morph = null;
|
|
589
|
-
existing.shape = character;
|
|
590
|
-
}
|
|
591
|
-
else {
|
|
592
|
-
return;
|
|
593
|
-
}
|
|
594
|
-
existing.character = character;
|
|
595
|
-
existing.invalidate(CONTENT);
|
|
596
|
-
}
|
|
597
|
-
/** A Bitmap: a display object that shows a BitmapData's pixels, its bounds the data's size. */
|
|
598
|
-
export class BitmapObject extends DisplayObject {
|
|
599
|
-
shown = null;
|
|
600
|
-
smoothing = false;
|
|
601
|
-
pixelSnapping = "auto";
|
|
602
|
-
/** The SWF's bitmap it shows a copy of, placed by a timeline or made by its class; null for one a script made. */
|
|
603
|
-
character = null;
|
|
604
|
-
/** PlaceObject3's HasImage where a timeline placed it: Flash then takes a bound class for its data's. */
|
|
605
|
-
hasImage = false;
|
|
606
|
-
constructor(store) {
|
|
607
|
-
super();
|
|
608
|
-
this.store = store;
|
|
609
|
-
}
|
|
610
|
-
/** The store shown; the object watches it, so a pixel set marks the object for the renderer. */
|
|
611
|
-
get store() {
|
|
612
|
-
return this.shown;
|
|
613
|
-
}
|
|
614
|
-
set store(store) {
|
|
615
|
-
this.shown?.views.delete(this.ref);
|
|
616
|
-
this.shown = store;
|
|
617
|
-
store?.views.add(this.ref);
|
|
618
|
-
this.invalidate(CONTENT);
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
export class Container extends DisplayObject {
|
|
622
|
-
/** The library whose timeline this is, for what it tells of children going; a script's container has none. */
|
|
623
|
-
library = null;
|
|
624
|
-
/** Its children in render order: index 0 is drawn first, below the rest. */
|
|
625
|
-
children = [];
|
|
626
|
-
/** The children the timeline placed, by depth. */
|
|
627
|
-
depths = new Map();
|
|
628
|
-
/** Whether a descendant changed since the renderer last synced. */
|
|
629
|
-
descendantsDirty = true;
|
|
630
|
-
/** Place `child` at timeline depth `depth`: before the first child of a greater depth. */
|
|
631
|
-
placeAtDepth(child, depth) {
|
|
632
|
-
this.removeAtDepth(depth);
|
|
633
|
-
let index = this.children.length;
|
|
634
|
-
for (let i = 0; i < this.children.length; i++) {
|
|
635
|
-
const d = this.children[i].depth;
|
|
636
|
-
if (d !== null && d > depth) {
|
|
637
|
-
index = i;
|
|
638
|
-
break;
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
child.depth = depth;
|
|
642
|
-
child.parent = this;
|
|
643
|
-
this.children.splice(index, 0, child);
|
|
644
|
-
this.depths.set(depth, child);
|
|
645
|
-
this.invalidate(CHILDREN);
|
|
646
|
-
}
|
|
647
|
-
removeAtDepth(depth) {
|
|
648
|
-
const child = this.depths.get(depth);
|
|
649
|
-
if (!child) {
|
|
650
|
-
return null;
|
|
651
|
-
}
|
|
652
|
-
// The timeline's removal: a script's goes through the natives, which tell of it themselves.
|
|
653
|
-
this.library?.removing?.(child, true);
|
|
654
|
-
this.removeChild(child);
|
|
655
|
-
return child;
|
|
656
|
-
}
|
|
657
|
-
/** Take `child` out, wherever it is in the list, and off the timeline's depths. */
|
|
658
|
-
removeChild(child) {
|
|
659
|
-
if (child.depth !== null) {
|
|
660
|
-
this.depths.delete(child.depth);
|
|
661
|
-
child.depth = null;
|
|
662
|
-
}
|
|
663
|
-
this.children.splice(this.children.indexOf(child), 1);
|
|
664
|
-
child.parent = null;
|
|
665
|
-
child.focusDrop?.(child);
|
|
666
|
-
this.invalidate(CHILDREN);
|
|
667
|
-
}
|
|
668
|
-
/**
|
|
669
|
-
* Put `child` at `index` in render order, as a script's addChildAt does:
|
|
670
|
-
* out of its parent first, and off the timeline's depths, which no
|
|
671
|
-
* longer place it.
|
|
672
|
-
*/
|
|
673
|
-
addChildAt(child, index) {
|
|
674
|
-
const same = child.parent === this;
|
|
675
|
-
child.parent?.removeChild(child);
|
|
676
|
-
child.parent = this;
|
|
677
|
-
this.children.splice(same ? Math.min(index, this.children.length) : index, 0, child);
|
|
678
|
-
this.invalidate(CHILDREN);
|
|
679
|
-
}
|
|
680
|
-
swapChildren(a, b) {
|
|
681
|
-
const i = this.children.indexOf(a);
|
|
682
|
-
const j = this.children.indexOf(b);
|
|
683
|
-
this.children[i] = b;
|
|
684
|
-
this.children[j] = a;
|
|
685
|
-
this.invalidate(CHILDREN);
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
/**
|
|
689
|
-
* A button (SimpleButton): four states, each a display object, of which it
|
|
690
|
-
* shows the up, over or down one, as the pointer has it, and hit tests with
|
|
691
|
-
* the fourth. The state it shows is its one child, which it is drawn and
|
|
692
|
-
* bounded by; the others are its own, off the display list, but each has
|
|
693
|
-
* its frames as Flash's do (`frameChildren`). Not a container to a script.
|
|
694
|
-
*/
|
|
695
|
-
export class ButtonObject extends Container {
|
|
696
|
-
upState = null;
|
|
697
|
-
overState = null;
|
|
698
|
-
downState = null;
|
|
699
|
-
hitTestState = null;
|
|
700
|
-
state = "up";
|
|
701
|
-
/** Its states' next frame scripts run up, over, down, hit: the first, in a SWF after 9 (Scripting.construct). */
|
|
702
|
-
firstScripts = false;
|
|
703
|
-
enabled = true;
|
|
704
|
-
useHandCursor = true;
|
|
705
|
-
trackAsMenu = false;
|
|
706
|
-
/** The display object of state `state`. */
|
|
707
|
-
stateObject(state) {
|
|
708
|
-
return state === "up" ? this.upState : state === "over" ? this.overState : this.downState;
|
|
709
|
-
}
|
|
710
|
-
/** Show state `state`. */
|
|
711
|
-
setState(state) {
|
|
712
|
-
this.state = state;
|
|
713
|
-
this.show();
|
|
714
|
-
}
|
|
715
|
-
/**
|
|
716
|
-
* Have the state it is in be its one child, as it is now. The states it
|
|
717
|
-
* does not show are parented to nothing, as Flash has them once made.
|
|
718
|
-
*/
|
|
719
|
-
show() {
|
|
720
|
-
const current = this.stateObject(this.state);
|
|
721
|
-
for (const child of [...this.children]) {
|
|
722
|
-
if (child !== current) {
|
|
723
|
-
this.removeChild(child);
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
for (const state of [this.upState, this.overState, this.downState, this.hitTestState]) {
|
|
727
|
-
if (state && state !== current && state.parent === this && !this.children.includes(state)) {
|
|
728
|
-
state.parent = null;
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
if (current && !this.children.includes(current)) {
|
|
732
|
-
if (current.parent !== this) {
|
|
733
|
-
current.parent?.removeChild(current);
|
|
734
|
-
}
|
|
735
|
-
this.children.splice(0, 0, current);
|
|
736
|
-
current.parent = this;
|
|
737
|
-
this.invalidate(CHILDREN);
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
/**
|
|
742
|
-
* Make a button's states from its records, as Flash does when the button
|
|
743
|
-
* is made, up, over, down, then hit test: each state the one character it
|
|
744
|
-
* shows, else a container of its characters by depth, one for none too. A
|
|
745
|
-
* character in several states is made once for each. `made` brings a
|
|
746
|
-
* character to life once placed, and `holderMade` each container, once all
|
|
747
|
-
* four states' characters are, as Flash names them after those; the hit
|
|
748
|
-
* test state takes only the records' transforms.
|
|
749
|
-
*/
|
|
750
|
-
export function buttonStates(button, character, library, made, holderMade) {
|
|
751
|
-
const holders = [];
|
|
752
|
-
const state = (flag) => {
|
|
753
|
-
const parts = [];
|
|
754
|
-
for (const record of character.records) {
|
|
755
|
-
const c = record.states & flag ? library.characters.get(record.character) : undefined;
|
|
756
|
-
if (c &&
|
|
757
|
-
c.type !== "binary" &&
|
|
758
|
-
c.type !== "font" &&
|
|
759
|
-
c.type !== "fontCff" &&
|
|
760
|
-
c.type !== "sound") {
|
|
761
|
-
const display = displayFor(c, library);
|
|
762
|
-
display.applyPlace(recordPlace(record, flag !== BUTTON_HIT_TEST));
|
|
763
|
-
parts.push({ display, character: c, record });
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
// Made as the button's, under it, though in no container's list: a
|
|
767
|
-
// script sees the stage from them, and no parent above them.
|
|
768
|
-
if (parts.length === 1) {
|
|
769
|
-
parts[0].display.parent = button;
|
|
770
|
-
made(parts[0].display, parts[0].character);
|
|
771
|
-
return parts[0].display;
|
|
772
|
-
}
|
|
773
|
-
const holder = new Container();
|
|
774
|
-
holder.parent = button;
|
|
775
|
-
for (const part of parts) {
|
|
776
|
-
holder.placeAtDepth(part.display, part.record.depth);
|
|
777
|
-
made(part.display, part.character);
|
|
778
|
-
}
|
|
779
|
-
holders.push(holder);
|
|
780
|
-
return holder;
|
|
781
|
-
};
|
|
782
|
-
button.upState = state(BUTTON_UP);
|
|
783
|
-
button.overState = state(BUTTON_OVER);
|
|
784
|
-
button.downState = state(BUTTON_DOWN);
|
|
785
|
-
button.hitTestState = state(BUTTON_HIT_TEST);
|
|
786
|
-
for (const holder of holders) {
|
|
787
|
-
holderMade(holder);
|
|
788
|
-
}
|
|
789
|
-
button.show();
|
|
790
|
-
}
|
|
791
|
-
/** A button record as a place of its character: its transform, and, but in the hit test state, its look. */
|
|
792
|
-
function recordPlace(record, look) {
|
|
793
|
-
return {
|
|
794
|
-
depth: record.depth,
|
|
795
|
-
move: false,
|
|
796
|
-
character: record.character,
|
|
797
|
-
matrix: record.matrix,
|
|
798
|
-
colorTransform: look ? record.colorTransform : null,
|
|
799
|
-
ratio: null,
|
|
800
|
-
name: null,
|
|
801
|
-
clipDepth: null,
|
|
802
|
-
className: null,
|
|
803
|
-
hasImage: false,
|
|
804
|
-
blendMode: look ? record.blendMode : null,
|
|
805
|
-
cacheAsBitmap: null,
|
|
806
|
-
visible: null,
|
|
807
|
-
opaqueBackground: null,
|
|
808
|
-
filters: look ? record.filters : null,
|
|
809
|
-
clipActions: null,
|
|
810
|
-
};
|
|
811
|
-
}
|
|
812
|
-
/**
|
|
813
|
-
* The display objects whose frame scripts a display object's run: those
|
|
814
|
-
* whose frames its frames run, but a button's first, in a SWF after 9, up,
|
|
815
|
-
* over, down, then hit test, as Flash runs them while the button is made.
|
|
816
|
-
*/
|
|
817
|
-
export function scriptChildren(d) {
|
|
818
|
-
if (d instanceof ButtonObject && d.firstScripts) {
|
|
819
|
-
d.firstScripts = false;
|
|
820
|
-
return [d.upState, d.overState, d.downState, d.hitTestState].filter((o, i, all) => o !== null && all.indexOf(o) === i);
|
|
821
|
-
}
|
|
822
|
-
return frameChildren(d);
|
|
823
|
-
}
|
|
824
|
-
/**
|
|
825
|
-
* The display objects whose frames a display object's frames run: a
|
|
826
|
-
* container's children, and all a button's states, as Ruffle orders them,
|
|
827
|
-
* the hit test state first, whichever it shows.
|
|
828
|
-
*/
|
|
829
|
-
export function frameChildren(d) {
|
|
830
|
-
if (d instanceof ButtonObject) {
|
|
831
|
-
return [d.hitTestState, d.upState, d.downState, d.overState].filter((o, i, all) => o !== null && all.indexOf(o) === i);
|
|
832
|
-
}
|
|
833
|
-
return d instanceof Container ? d.children : [];
|
|
834
|
-
}
|
|
835
|
-
/**
|
|
836
|
-
* A walk of a container's children in render order, for the timeline
|
|
837
|
-
* masks that clip each: a mask clips the children after it until one
|
|
838
|
-
* placed deeper than its clip depth; one a script put among them has no
|
|
839
|
-
* depth and ends nothing. A class, not a generator: the renderer walks
|
|
840
|
-
* every child each time a container's children change.
|
|
841
|
-
*/
|
|
842
|
-
export class Clips {
|
|
843
|
-
/** The masks open, outermost first. */
|
|
844
|
-
masks = [];
|
|
845
|
-
/** The next child: how many of `masks`, from the first, clip it; it clips those after it, if a mask. */
|
|
846
|
-
enter(child) {
|
|
847
|
-
const depth = child.depth;
|
|
848
|
-
const masks = this.masks;
|
|
849
|
-
while (depth !== null && masks.length > 0 && masks[masks.length - 1].clipDepth < depth) {
|
|
850
|
-
masks.pop();
|
|
851
|
-
}
|
|
852
|
-
const n = masks.length;
|
|
853
|
-
if (child.clipDepth > 0) {
|
|
854
|
-
masks.push(child);
|
|
855
|
-
}
|
|
856
|
-
return n;
|
|
857
|
-
}
|
|
858
|
-
}
|
|
859
|
-
export class MovieClip extends Container {
|
|
860
|
-
timeline;
|
|
861
|
-
/** The frame it shows, 1 the first; 0 before its first frame is entered. */
|
|
862
|
-
currentFrame = 0;
|
|
863
|
-
playing = true;
|
|
864
|
-
/** Made by a script with `new`: Flash has such a clip sit out the next frame's advance. */
|
|
865
|
-
fresh = false;
|
|
866
|
-
/** The scripts addFrameScript registered, by frame, 1 the first. */
|
|
867
|
-
frameScripts = new Map();
|
|
868
|
-
/** The frame whose script last ran, so that entering a frame runs its script once. */
|
|
869
|
-
scriptedFrame = 0;
|
|
870
|
-
/** A goto a frame script asked for, taken when the script returns, as Flash defers it; null for none. */
|
|
871
|
-
queuedGoto = null;
|
|
872
|
-
/** Whether that goto plays or stops the clip, as it happens, not as it is asked for. */
|
|
873
|
-
queuedPlay = false;
|
|
874
|
-
/**
|
|
875
|
-
* The frame count (Scripting.frames) at a script's goto in a SWF of
|
|
876
|
-
* version 9 or earlier, a goto to the frame it is on among them: the next
|
|
877
|
-
* frame it sits out, with all in it, as Flash has such a clip
|
|
878
|
-
* (`goto-children`). A stamp, not a flag: one a skipped parent took along,
|
|
879
|
-
* or a clip no frame reached, is past by the frame after and never skips.
|
|
880
|
-
* From version 10 a goto leaves its frames as they were.
|
|
881
|
-
*/
|
|
882
|
-
skipsAfter = -1;
|
|
883
|
-
constructor(timeline, library) {
|
|
884
|
-
super();
|
|
885
|
-
this.timeline = timeline;
|
|
886
|
-
this.library = library;
|
|
887
|
-
}
|
|
888
|
-
get totalFrames() {
|
|
889
|
-
return this.timeline.frames.length;
|
|
890
|
-
}
|
|
891
|
-
/**
|
|
892
|
-
* First-frame children placed but not yet made alive: placed before the
|
|
893
|
-
* clip's class constructor runs, made by Sprite's constructChildren in its
|
|
894
|
-
* super(), as Flash has a constructor find them (`instantiation_on_enter_frame`).
|
|
895
|
-
*/
|
|
896
|
-
held = [];
|
|
897
|
-
/**
|
|
898
|
-
* Run frame `frame`'s commands as the playhead reaches it: place, move and
|
|
899
|
-
* remove the timeline's children. A place without the move flag at a depth
|
|
900
|
-
* already taken is let be, as Flash lets it (`same-depth`).
|
|
901
|
-
* A new child is made alive at once, or `later`: by the frame's construct
|
|
902
|
-
* phase, after ENTER_FRAME, for a frame played on; by constructChildren,
|
|
903
|
-
* held, for a clip's first frame.
|
|
904
|
-
*/
|
|
905
|
-
runFrame(frame, later = null) {
|
|
906
|
-
for (const command of this.timeline.frames[frame - 1] ?? []) {
|
|
907
|
-
if (command.type === "remove") {
|
|
908
|
-
this.removeAtDepth(command.depth);
|
|
909
|
-
continue;
|
|
910
|
-
}
|
|
911
|
-
const place = command.place;
|
|
912
|
-
const existing = this.depths.get(place.depth);
|
|
913
|
-
if (!place.move && existing) {
|
|
914
|
-
continue;
|
|
915
|
-
}
|
|
916
|
-
if (place.move && existing && place.character === null) {
|
|
917
|
-
existing.applyPlace(place);
|
|
918
|
-
continue;
|
|
919
|
-
}
|
|
920
|
-
// A new character, or one that replaces what is at the depth.
|
|
921
|
-
// Data is no display object: Flash places nothing for it.
|
|
922
|
-
const character = place.character === null ? null : this.library.characters.get(place.character);
|
|
923
|
-
if (!character ||
|
|
924
|
-
character.type === "binary" ||
|
|
925
|
-
character.type === "font" ||
|
|
926
|
-
character.type === "fontCff" ||
|
|
927
|
-
character.type === "sound") {
|
|
928
|
-
existing?.applyPlace(place);
|
|
929
|
-
continue;
|
|
930
|
-
}
|
|
931
|
-
// With the move flag the child stays, another character or not; and
|
|
932
|
-
// where there is none, the move places nothing (`rewind-first`).
|
|
933
|
-
if (place.move) {
|
|
934
|
-
if (existing) {
|
|
935
|
-
swap(existing, character);
|
|
936
|
-
existing.applyPlace(place);
|
|
937
|
-
}
|
|
938
|
-
continue;
|
|
939
|
-
}
|
|
940
|
-
const child = displayFor(character, this.library, place.hasImage);
|
|
941
|
-
child.applyPlace(place);
|
|
942
|
-
child.placeFrame = frame;
|
|
943
|
-
this.placeAtDepth(child, place.depth);
|
|
944
|
-
if (later === "held" && this.library.construct) {
|
|
945
|
-
this.held.push({ display: child, character });
|
|
946
|
-
}
|
|
947
|
-
else if (later === "frame" && this.library.constructLater) {
|
|
948
|
-
this.library.constructLater(child, character);
|
|
949
|
-
}
|
|
950
|
-
else {
|
|
951
|
-
construct(child, character, this.library);
|
|
952
|
-
}
|
|
953
|
-
}
|
|
954
|
-
}
|
|
955
|
-
/**
|
|
956
|
-
* Jump to frame `frame`, as Flash does rather than by running the frames
|
|
957
|
-
* between: the frames up to it (from the first, for a rewind) are
|
|
958
|
-
* replayed into one jump per depth. On a rewind the children the timeline
|
|
959
|
-
* placed after it go, but one at a depth the frames up to it end on a
|
|
960
|
-
* place without the move flag at, which stays and takes the place; and
|
|
961
|
-
* any child whose ratio is not the one the replayed frames give goes,
|
|
962
|
-
* whenever it was placed. Each jump then changes the child still at its
|
|
963
|
-
* depth, or makes one where the frames placed one anew, or where a
|
|
964
|
-
* rewind ends on another character or ratio. The result is what playing
|
|
965
|
-
* the frames would leave, except that a child placed before the frame and
|
|
966
|
-
* untouched since keeps playing, and its identity; on a rewind a place
|
|
967
|
-
* puts back what it leaves unsaid too, so it looks as it did when first
|
|
968
|
-
* placed. A clip's own loop (`looping`) makes the children it places
|
|
969
|
-
* alive in the frame's construct phase, as playing on does (`loop-ratio`);
|
|
970
|
-
* a script's goto, at once.
|
|
971
|
-
*/
|
|
972
|
-
gotoFrame(frame, looping = false) {
|
|
973
|
-
const target = Math.max(1, Math.min(frame, this.totalFrames));
|
|
974
|
-
const rewind = target < this.currentFrame;
|
|
975
|
-
const jumps = new Map();
|
|
976
|
-
for (let f = (rewind ? 0 : this.currentFrame) + 1; f <= target; f++) {
|
|
977
|
-
for (const command of this.timeline.frames[f - 1] ?? []) {
|
|
978
|
-
if (command.type === "remove") {
|
|
979
|
-
jumps.delete(command.depth);
|
|
980
|
-
// Going forward, a removal between the frames takes effect.
|
|
981
|
-
if (!rewind) {
|
|
982
|
-
this.removeAtDepth(command.depth);
|
|
983
|
-
}
|
|
984
|
-
continue;
|
|
985
|
-
}
|
|
986
|
-
const place = command.place;
|
|
987
|
-
const jump = jumps.get(place.depth) ?? {
|
|
988
|
-
before: null,
|
|
989
|
-
place: null,
|
|
990
|
-
frame: f,
|
|
991
|
-
placed: false,
|
|
992
|
-
};
|
|
993
|
-
// A rewind replays from an empty display list, so what the frames do
|
|
994
|
-
// at a depth nothing has placed yet is known: a change does nothing,
|
|
995
|
-
// and a place in a child's stead places anew.
|
|
996
|
-
if (place.character === null) {
|
|
997
|
-
if (jump.place) {
|
|
998
|
-
jump.place = mergePlace(jump.place, place);
|
|
999
|
-
}
|
|
1000
|
-
else if (!rewind) {
|
|
1001
|
-
jump.before = jump.before ? mergePlace(jump.before, place) : place;
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
else if (!place.move && (jump.place || (!rewind && this.depths.has(place.depth)))) {
|
|
1005
|
-
// At a depth taken by then, as frame by frame: let be.
|
|
1006
|
-
continue;
|
|
1007
|
-
}
|
|
1008
|
-
else if (!place.move) {
|
|
1009
|
-
// Anew: the child starts from nothing, and from here.
|
|
1010
|
-
jump.before = null;
|
|
1011
|
-
jump.place = rewind ? asFirstPlaced(place) : place;
|
|
1012
|
-
jump.frame = f;
|
|
1013
|
-
jump.placed = true;
|
|
1014
|
-
}
|
|
1015
|
-
else if (jump.place) {
|
|
1016
|
-
jump.place = mergePlace(jump.place, place);
|
|
1017
|
-
}
|
|
1018
|
-
else if (!rewind && this.depths.has(place.depth)) {
|
|
1019
|
-
jump.place = place;
|
|
1020
|
-
jump.frame = f;
|
|
1021
|
-
}
|
|
1022
|
-
else {
|
|
1023
|
-
// Another character with the move flag where nothing is: as frame
|
|
1024
|
-
// by frame, nothing is placed (`rewind-first`).
|
|
1025
|
-
continue;
|
|
1026
|
-
}
|
|
1027
|
-
jumps.set(place.depth, jump);
|
|
1028
|
-
}
|
|
1029
|
-
}
|
|
1030
|
-
// On a rewind, what the timeline placed after the target goes, but where
|
|
1031
|
-
// the frames replayed end on a place without the move flag: that child
|
|
1032
|
-
// stays and takes the place, as Flash keeps it (`same-depth` at the
|
|
1033
|
-
// loop, `rewind-first`), a clip its character and a shape the place's
|
|
1034
|
-
// (swap, `morph-shapes`). Replayed from the first frame, a depth the
|
|
1035
|
-
// frames left empty is empty. Whenever it was placed, a child whose
|
|
1036
|
-
// ratio is not the one the frames replayed give goes too, to be made
|
|
1037
|
-
// anew: authoring tools give each placement a ratio of its own, and
|
|
1038
|
-
// Flash takes another ratio for another object, of every kind
|
|
1039
|
-
// (`rewind-ratio`, `rewind-kinds`), where Ruffle compares it only for
|
|
1040
|
-
// children placed after the target, and for morphs. In render order, as
|
|
1041
|
-
// Ruffle removes.
|
|
1042
|
-
const kept = new Set();
|
|
1043
|
-
if (rewind) {
|
|
1044
|
-
for (const child of [...this.children]) {
|
|
1045
|
-
const depth = child.depth;
|
|
1046
|
-
if (depth === null || child.placeFrame <= 0) {
|
|
1047
|
-
continue;
|
|
1048
|
-
}
|
|
1049
|
-
const jump = jumps.get(depth);
|
|
1050
|
-
if (!jump || (jump.place && jump.place.ratio !== child.ratio)) {
|
|
1051
|
-
this.removeAtDepth(depth);
|
|
1052
|
-
}
|
|
1053
|
-
else if (child.placeFrame > target && jump.placed) {
|
|
1054
|
-
kept.add(depth);
|
|
1055
|
-
}
|
|
1056
|
-
else if (child.placeFrame > target) {
|
|
1057
|
-
this.removeAtDepth(depth);
|
|
1058
|
-
}
|
|
1059
|
-
}
|
|
1060
|
-
}
|
|
1061
|
-
// On the frame before its children are made: one constructed now sees it there, as in Flash.
|
|
1062
|
-
this.currentFrame = target;
|
|
1063
|
-
for (const [depth, jump] of jumps) {
|
|
1064
|
-
const existing = this.depths.get(depth);
|
|
1065
|
-
if (existing && kept.has(depth) && jump.place) {
|
|
1066
|
-
const id = jump.place.character;
|
|
1067
|
-
const character = id === null ? undefined : this.library.characters.get(id);
|
|
1068
|
-
if (character) {
|
|
1069
|
-
swap(existing, character);
|
|
1070
|
-
}
|
|
1071
|
-
existing.applyPlace(jump.place);
|
|
1072
|
-
continue;
|
|
1073
|
-
}
|
|
1074
|
-
if (existing && jump.before) {
|
|
1075
|
-
existing.applyPlace(jump.before);
|
|
1076
|
-
}
|
|
1077
|
-
if (!jump.place) {
|
|
1078
|
-
continue;
|
|
1079
|
-
}
|
|
1080
|
-
const character = jump.place.character === null ? null : this.library.characters.get(jump.place.character);
|
|
1081
|
-
if (!character ||
|
|
1082
|
-
character.type === "binary" ||
|
|
1083
|
-
character.type === "font" ||
|
|
1084
|
-
character.type === "fontCff" ||
|
|
1085
|
-
character.type === "sound") {
|
|
1086
|
-
existing?.applyPlace(jump.place);
|
|
1087
|
-
continue;
|
|
1088
|
-
}
|
|
1089
|
-
// Forward, the child stays for a place with the move flag, whatever
|
|
1090
|
-
// character it names, as frame by frame; on a rewind it stays only
|
|
1091
|
-
// for its own character, and another, however the frames between went,
|
|
1092
|
-
// makes a new child (the corpus's `place_object_replace_2`). Placed
|
|
1093
|
-
// anew it is a new child.
|
|
1094
|
-
const same = existing?.character === character;
|
|
1095
|
-
if (existing && (rewind ? same : jump.place.move)) {
|
|
1096
|
-
swap(existing, character);
|
|
1097
|
-
existing.applyPlace(jump.place);
|
|
1098
|
-
continue;
|
|
1099
|
-
}
|
|
1100
|
-
const child = displayFor(character, this.library, jump.place.hasImage);
|
|
1101
|
-
child.applyPlace(jump.place);
|
|
1102
|
-
child.placeFrame = jump.frame;
|
|
1103
|
-
this.placeAtDepth(child, depth);
|
|
1104
|
-
if (looping && this.library.constructLater) {
|
|
1105
|
-
this.library.constructLater(child, character);
|
|
1106
|
-
}
|
|
1107
|
-
else if (this.library.uncaught) {
|
|
1108
|
-
// A constructor that throws is reported, and the children after it
|
|
1109
|
-
// are still made; a first frame's, made in its parent's super(),
|
|
1110
|
-
// still throw into the parent's constructor, as in Flash.
|
|
1111
|
-
try {
|
|
1112
|
-
construct(child, character, this.library);
|
|
1113
|
-
}
|
|
1114
|
-
catch (error) {
|
|
1115
|
-
this.library.uncaught(error);
|
|
1116
|
-
}
|
|
1117
|
-
}
|
|
1118
|
-
else {
|
|
1119
|
-
construct(child, character, this.library);
|
|
1120
|
-
}
|
|
1121
|
-
}
|
|
1122
|
-
}
|
|
1123
|
-
/** The first frame, as a clip runs it when it is made, its children placed and made alive; once. */
|
|
1124
|
-
enterFirstFrame() {
|
|
1125
|
-
this.placeFirstFrame();
|
|
1126
|
-
// One at a time: a constructor that throws stops here, as its error stops the frame.
|
|
1127
|
-
for (let next = this.held.shift(); next; next = this.held.shift()) {
|
|
1128
|
-
if (!next.display.object && next.display.parent === this) {
|
|
1129
|
-
construct(next.display, next.character, this.library);
|
|
1130
|
-
}
|
|
1131
|
-
}
|
|
1132
|
-
}
|
|
1133
|
-
/** The first frame's children placed, to be made alive by enterFirstFrame; once. */
|
|
1134
|
-
placeFirstFrame() {
|
|
1135
|
-
if (this.currentFrame !== 0) {
|
|
1136
|
-
return;
|
|
1137
|
-
}
|
|
1138
|
-
this.currentFrame = 1;
|
|
1139
|
-
this.runFrame(1, "held");
|
|
1140
|
-
}
|
|
1141
|
-
/**
|
|
1142
|
-
* On to the next frame, if it plays and has one. From the last frame it
|
|
1143
|
-
* loops to the first as a goto, so what the first frame placed stays.
|
|
1144
|
-
*/
|
|
1145
|
-
advance() {
|
|
1146
|
-
if (this.fresh) {
|
|
1147
|
-
this.fresh = false;
|
|
1148
|
-
return;
|
|
1149
|
-
}
|
|
1150
|
-
if (!this.playing || this.totalFrames <= 1) {
|
|
1151
|
-
return;
|
|
1152
|
-
}
|
|
1153
|
-
if (this.currentFrame >= this.totalFrames) {
|
|
1154
|
-
this.gotoFrame(1, true);
|
|
1155
|
-
return;
|
|
1156
|
-
}
|
|
1157
|
-
this.currentFrame++;
|
|
1158
|
-
this.runFrame(this.currentFrame, "frame");
|
|
1159
|
-
}
|
|
1160
|
-
}
|
|
1161
|
-
/**
|
|
1162
|
-
* A rewind's place with what it leaves unsaid said: the transform, colour,
|
|
1163
|
-
* ratio and the rest as a character first placed has them, as Ruffle's
|
|
1164
|
-
* goto fills them in. The name and visibility are not among them: they
|
|
1165
|
-
* stay as they were.
|
|
1166
|
-
*/
|
|
1167
|
-
function asFirstPlaced(place) {
|
|
1168
|
-
return {
|
|
1169
|
-
...place,
|
|
1170
|
-
matrix: place.matrix ?? IDENTITY,
|
|
1171
|
-
colorTransform: place.colorTransform ?? IDENTITY_COLOR,
|
|
1172
|
-
ratio: place.ratio ?? 0,
|
|
1173
|
-
blendMode: place.blendMode ?? 0,
|
|
1174
|
-
// An empty FILTERLIST: a rewind clears what the frames after set.
|
|
1175
|
-
filters: place.filters ?? NO_FILTER_BYTES,
|
|
1176
|
-
cacheAsBitmap: place.cacheAsBitmap ?? false,
|
|
1177
|
-
};
|
|
1178
|
-
}
|
|
1179
|
-
const IDENTITY_COLOR = {
|
|
1180
|
-
rMul: 1,
|
|
1181
|
-
gMul: 1,
|
|
1182
|
-
bMul: 1,
|
|
1183
|
-
aMul: 1,
|
|
1184
|
-
rAdd: 0,
|
|
1185
|
-
gAdd: 0,
|
|
1186
|
-
bAdd: 0,
|
|
1187
|
-
aAdd: 0,
|
|
1188
|
-
};
|
|
1189
|
-
/** `next` over `previous`: what the later place sets, and the rest as it was. */
|
|
1190
|
-
function mergePlace(previous, next) {
|
|
1191
|
-
return {
|
|
1192
|
-
...previous,
|
|
1193
|
-
character: next.character ?? previous.character,
|
|
1194
|
-
matrix: next.matrix ?? previous.matrix,
|
|
1195
|
-
colorTransform: next.colorTransform ?? previous.colorTransform,
|
|
1196
|
-
ratio: next.ratio ?? previous.ratio,
|
|
1197
|
-
name: next.name ?? previous.name,
|
|
1198
|
-
clipDepth: next.clipDepth ?? previous.clipDepth,
|
|
1199
|
-
className: next.className ?? previous.className,
|
|
1200
|
-
blendMode: next.blendMode ?? previous.blendMode,
|
|
1201
|
-
cacheAsBitmap: next.cacheAsBitmap ?? previous.cacheAsBitmap,
|
|
1202
|
-
visible: next.visible ?? previous.visible,
|
|
1203
|
-
opaqueBackground: next.opaqueBackground ?? previous.opaqueBackground,
|
|
1204
|
-
filters: next.filters ?? previous.filters,
|
|
1205
|
-
clipActions: next.clipActions ?? previous.clipActions,
|
|
1206
|
-
};
|
|
1207
|
-
}
|
|
1208
|
-
/** A display object for a character, before its first frame: a shape, or a clip. */
|
|
1209
|
-
/** `hasImage` is PlaceObject3's flag where a timeline places it, which a bitmap's construction reads. */
|
|
1210
|
-
export function displayFor(character, library, hasImage = false) {
|
|
1211
|
-
if (character.type === "shape") {
|
|
1212
|
-
return new ShapeObject(character);
|
|
1213
|
-
}
|
|
1214
|
-
if (character.type === "morph") {
|
|
1215
|
-
return ShapeObject.ofMorph(character);
|
|
1216
|
-
}
|
|
1217
|
-
if (character.type === "bitmap") {
|
|
1218
|
-
// With scripts, its Bitmap's constructor gives it its data; without, a copy of the pixels.
|
|
1219
|
-
const bitmap = new BitmapObject(library.construct ? null : BitmapStore.of(character.pixels ?? INVALID_PIXELS));
|
|
1220
|
-
bitmap.character = character;
|
|
1221
|
-
bitmap.hasImage = hasImage;
|
|
1222
|
-
return bitmap;
|
|
1223
|
-
}
|
|
1224
|
-
if (character.type === "static") {
|
|
1225
|
-
return new StaticTextObject(character, library.characters);
|
|
1226
|
-
}
|
|
1227
|
-
if (character.type === "text") {
|
|
1228
|
-
const text = new TextObject(character);
|
|
1229
|
-
text.fonts = library.fonts;
|
|
1230
|
-
return text;
|
|
1231
|
-
}
|
|
1232
|
-
// Its states are made where it is constructed (Scripting.construct).
|
|
1233
|
-
if (character.type === "button") {
|
|
1234
|
-
const button = new ButtonObject();
|
|
1235
|
-
button.character = character;
|
|
1236
|
-
button.trackAsMenu = character.trackAsMenu;
|
|
1237
|
-
return button;
|
|
1238
|
-
}
|
|
1239
|
-
const clip = new MovieClip(character.timeline, library);
|
|
1240
|
-
clip.character = character;
|
|
1241
|
-
return clip;
|
|
1242
|
-
}
|
|
1243
|
-
/**
|
|
1244
|
-
* Bring a display object the timeline placed to life: its AS3 object
|
|
1245
|
-
* constructed where the SWF has scripts, which enters a clip's first frame
|
|
1246
|
-
* and names it on its parent, else a clip's first frame entered. After the
|
|
1247
|
-
* placement, as Flash has it, so that the parent and the name are there.
|
|
1248
|
-
*/
|
|
1249
|
-
function construct(display, character, library) {
|
|
1250
|
-
if (library.construct) {
|
|
1251
|
-
library.construct(display, character);
|
|
1252
|
-
}
|
|
1253
|
-
else if (display instanceof MovieClip) {
|
|
1254
|
-
display.enterFirstFrame();
|
|
1255
|
-
}
|
|
1256
|
-
else if (display instanceof ButtonObject && character.type === "button") {
|
|
1257
|
-
buttonStates(display, character, library, (d, c) => construct(d, c, library), () => { });
|
|
1258
|
-
}
|
|
1259
|
-
}
|
|
1260
|
-
/** A display object for a character as the timeline would place it, alive, but in no container. */
|
|
1261
|
-
export function instantiate(character, library) {
|
|
1262
|
-
const display = displayFor(character, library);
|
|
1263
|
-
construct(display, character, library);
|
|
1264
|
-
return display;
|
|
1265
|
-
}
|
|
1266
|
-
/** A timeline of one empty frame: a clip a script makes. */
|
|
1267
|
-
export const EMPTY_TIMELINE = {
|
|
1268
|
-
frames: [[]],
|
|
1269
|
-
labels: [],
|
|
1270
|
-
gotoLabels: [],
|
|
1271
|
-
frameLabels: new Map(),
|
|
1272
|
-
scenes: [{ name: "", frame: 1 }],
|
|
1273
|
-
};
|
|
1274
|
-
//# sourceMappingURL=display.js.map
|